diff --git a/.fossa.yml b/.fossa.yml index 69f579717200..6301cc302700 100644 --- a/.fossa.yml +++ b/.fossa.yml @@ -4,6 +4,9 @@ targets: only: # only scanning the modules which are published # (as opposed to internal testing modules + - type: gradle + path: ./ + target: ':declarative-config-bridge' - type: gradle path: ./ target: ':instrumentation-annotations' @@ -88,9 +91,6 @@ targets: - type: gradle path: ./ target: ':instrumentation:camel-2.20:javaagent' - - type: gradle - path: ./ - target: ':instrumentation:clickhouse-client-0.5:javaagent' - type: gradle path: ./ target: ':instrumentation:executors:bootstrap' @@ -346,6 +346,12 @@ targets: - type: gradle path: ./ target: ':instrumentation:aws-lambda:aws-lambda-events-2.2:library' + - type: gradle + path: ./ + target: ':instrumentation:aws-lambda:aws-lambda-events-3.11:library' + - type: gradle + path: ./ + target: ':instrumentation:aws-lambda:aws-lambda-events-common-2.2:library' - type: gradle path: ./ target: ':instrumentation:aws-sdk:aws-sdk-1.11:javaagent' @@ -385,6 +391,15 @@ targets: - type: gradle path: ./ target: ':instrumentation:cassandra:cassandra-4.4:library' + - type: gradle + path: ./ + target: ':instrumentation:clickhouse:clickhouse-client-common:javaagent' + - type: gradle + path: ./ + target: ':instrumentation:clickhouse:clickhouse-client-v1-0.5:javaagent' + - type: gradle + path: ./ + target: ':instrumentation:clickhouse:clickhouse-client-v2-0.8:javaagent' - type: gradle path: ./ target: ':instrumentation:couchbase:couchbase-2-common:javaagent' diff --git a/.github/workflows/auto-license-report.yml b/.github/workflows/auto-license-report.yml index 6374f588ae78..165675a964c4 100644 --- a/.github/workflows/auto-license-report.yml +++ b/.github/workflows/auto-license-report.yml @@ -16,13 +16,13 @@ jobs: check: runs-on: ubuntu-latest steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - name: Free disk space run: .github/scripts/gha-free-disk-space.sh - name: Set up JDK for running Gradle - uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1 + uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.0 with: distribution: temurin java-version-file: .java-version diff --git a/.github/workflows/auto-spotless.yml b/.github/workflows/auto-spotless.yml index d621d37f98be..ae9d5f14f1eb 100644 --- a/.github/workflows/auto-spotless.yml +++ b/.github/workflows/auto-spotless.yml @@ -16,13 +16,13 @@ jobs: check: runs-on: ubuntu-latest steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - name: Free disk space run: .github/scripts/gha-free-disk-space.sh - name: Set up JDK for running Gradle - uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1 + uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.0 with: distribution: temurin java-version-file: .java-version diff --git a/.github/workflows/auto-update-otel-sdk.yml b/.github/workflows/auto-update-otel-sdk.yml index 3e84b21863c0..1999c84c3fe9 100644 --- a/.github/workflows/auto-update-otel-sdk.yml +++ b/.github/workflows/auto-update-otel-sdk.yml @@ -17,7 +17,7 @@ jobs: latest-version: ${{ steps.check-versions.outputs.latest-version }} already-opened: ${{ steps.check-versions.outputs.already-opened }} steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - id: check-versions name: Check versions @@ -55,7 +55,7 @@ jobs: needs: - check-versions steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - name: Update version env: @@ -66,7 +66,7 @@ jobs: run: .github/scripts/gha-free-disk-space.sh - name: Set up JDK for running Gradle - uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1 + uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.0 with: distribution: temurin java-version-file: .java-version @@ -84,7 +84,7 @@ jobs: - name: Use CLA approved bot run: .github/scripts/use-cla-approved-bot.sh - - uses: actions/create-github-app-token@0f859bf9e69e887678d5bbfbee594437cb440ffe # v2.1.0 + - uses: actions/create-github-app-token@a8d616148505b5069dccd32f177bb87d7f39123b # v2.1.1 id: otelbot-token with: app-id: ${{ vars.OTELBOT_APP_ID }} diff --git a/.github/workflows/auto-update-pull-request.yml b/.github/workflows/auto-update-pull-request.yml index 20cd95aa1987..a72c15e11b72 100644 --- a/.github/workflows/auto-update-pull-request.yml +++ b/.github/workflows/auto-update-pull-request.yml @@ -33,14 +33,14 @@ jobs: echo "exists=true" >> $GITHUB_OUTPUT fi - - uses: actions/create-github-app-token@0f859bf9e69e887678d5bbfbee594437cb440ffe # v2.1.0 + - uses: actions/create-github-app-token@a8d616148505b5069dccd32f177bb87d7f39123b # v2.1.1 if: steps.unzip-patch.outputs.exists == 'true' id: otelbot-token with: app-id: 1295839 private-key: ${{ secrets.OTELBOT_JAVA_INSTRUMENTATION_PRIVATE_KEY }} - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 if: steps.unzip-patch.outputs.exists == 'true' with: repository: "${{ github.event.workflow_run.head_repository.full_name }}" diff --git a/.github/workflows/backport.yml b/.github/workflows/backport.yml index 905390e7ac3a..5e661d42c877 100644 --- a/.github/workflows/backport.yml +++ b/.github/workflows/backport.yml @@ -21,7 +21,7 @@ jobs: exit 1 fi - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: # history is needed to run git cherry-pick below fetch-depth: 0 @@ -29,7 +29,7 @@ jobs: - name: Use CLA approved bot run: .github/scripts/use-cla-approved-bot.sh - - uses: actions/create-github-app-token@0f859bf9e69e887678d5bbfbee594437cb440ffe # v2.1.0 + - uses: actions/create-github-app-token@a8d616148505b5069dccd32f177bb87d7f39123b # v2.1.1 id: otelbot-token with: app-id: ${{ vars.OTELBOT_APP_ID }} diff --git a/.github/workflows/build-common.yml b/.github/workflows/build-common.yml index 621aa3fdae8e..ff72f2ddecea 100644 --- a/.github/workflows/build-common.yml +++ b/.github/workflows/build-common.yml @@ -9,9 +9,6 @@ on: no-build-cache: type: boolean required: false - max-test-retries: - type: string - required: false skip-openj9-tests: type: boolean required: false @@ -29,13 +26,13 @@ jobs: spotless: runs-on: ubuntu-latest steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - name: Free disk space run: .github/scripts/gha-free-disk-space.sh - name: Set up JDK for running Gradle - uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1 + uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.0 with: distribution: temurin java-version-file: .java-version @@ -51,13 +48,13 @@ jobs: license-check: runs-on: ubuntu-latest steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - name: Free disk space run: .github/scripts/gha-free-disk-space.sh - name: Set up JDK for running Gradle - uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1 + uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.0 with: distribution: temurin java-version-file: .java-version @@ -90,13 +87,13 @@ jobs: fossa-configuration-check: runs-on: ubuntu-latest steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - name: Free disk space run: .github/scripts/gha-free-disk-space.sh - name: Set up JDK for running Gradle - uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1 + uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.0 with: distribution: temurin java-version-file: .java-version @@ -129,13 +126,14 @@ jobs: extra-dependency-management-enforcement: runs-on: ubuntu-latest steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - name: Dependency check run: | set +e grep '^ implementation(".*:.*:[0-9].*")\|^ api(".*:.*:[0-9].*")' \ --include=\*.kts \ + --exclude-dir=quarkus\*-plugin \ -r instrumentation \ | grep -v testing/build.gradle.kts \ | grep -v com.azure:azure-core-tracing-opentelemetry \ @@ -152,34 +150,34 @@ jobs: check-javaagent-suppression-keys: runs-on: ubuntu-latest steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - run: .github/scripts/check-javaagent-suppression-keys.sh check-latest-dep-test-overrides: runs-on: ubuntu-latest steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - run: .github/scripts/check-latest-dep-test-overrides.sh check-package-names: runs-on: ubuntu-latest steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - run: .github/scripts/check-package-names.sh build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - name: Free disk space run: .github/scripts/gha-free-disk-space.sh - name: Set up JDK for running Gradle - uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1 + uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.0 with: distribution: temurin java-version-file: .java-version @@ -264,28 +262,28 @@ jobs: vm: openj9 fail-fast: false steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - name: Free disk space run: .github/scripts/gha-free-disk-space.sh - id: setup-test-java name: Set up JDK ${{ matrix.test-java-version }}-${{ matrix.vm }} for running tests - uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1 + uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.0 with: # using zulu because new releases get published quickly distribution: ${{ matrix.vm == 'hotspot' && 'zulu' || 'adopt-openj9'}} java-version: ${{ matrix.test-java-version }} - name: Set up JDK for running Gradle - uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1 + uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.0 with: distribution: temurin java-version-file: .java-version # vaadin 14 tests fail with node 18 - name: Set up Node - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 + uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0 with: node-version: 16 @@ -338,7 +336,7 @@ jobs: - name: Get current job url id: jobs if: ${{ !cancelled() }} - uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 env: matrix: ${{ toJson(matrix) }} with: @@ -419,13 +417,13 @@ jobs: run: git config --system core.longpaths true if: matrix.os == 'windows-latest' - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - name: Free disk space run: .github/scripts/gha-free-disk-space.sh - name: Set up JDK for running Gradle - uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1 + uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.0 with: distribution: temurin java-version-file: .java-version @@ -471,13 +469,13 @@ jobs: gradle-plugins: runs-on: ubuntu-latest steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - name: Free disk space run: .github/scripts/gha-free-disk-space.sh - name: Set up JDK for running Gradle - uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1 + uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.0 with: distribution: temurin java-version-file: .java-version @@ -494,13 +492,13 @@ jobs: examples: runs-on: ubuntu-latest steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - name: Free disk space run: .github/scripts/gha-free-disk-space.sh - name: Set up JDK for running Gradle - uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1 + uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.0 with: distribution: temurin java-version-file: .java-version diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index bdd87d4b53d9..5d67c3761760 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -64,13 +64,13 @@ jobs: # skipping release branches because the versions in those branches are not snapshots if: github.ref_name == 'main' && github.repository == 'open-telemetry/opentelemetry-java-instrumentation' steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - name: Free disk space run: .github/scripts/gha-free-disk-space.sh - name: Set up JDK for running Gradle - uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1 + uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.0 with: distribution: temurin java-version-file: .java-version diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 550155ca936b..55d38cfbfe6d 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -34,18 +34,18 @@ jobs: - language: java runs-on: oracle-8cpu-32gb-x86-64 steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 # don't need to free disk space (which takes time) since running on larger machine - name: Set up JDK 11 - uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1 + uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.0 with: distribution: temurin java-version: 11 - name: Set up JDK for running Gradle - uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1 + uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.0 with: distribution: temurin java-version-file: .java-version @@ -63,22 +63,27 @@ jobs: cache-read-only: ${{ github.event_name == 'pull_request' }} - name: Initialize CodeQL - uses: github/codeql-action/init@76621b61decf072c1cee8dd1ce2d2a82d33c17ed # v3.29.8 + uses: github/codeql-action/init@f1f6e5f6af878fb37288ce1c627459e94dbf7d01 # v3.30.1 with: languages: ${{ matrix.language }} - # using "latest" helps to keep up with the latest Kotlin support + # using "linked" helps to keep up with the linked Kotlin support # see https://github.com/github/codeql-action/issues/1555#issuecomment-1452228433 - tools: latest + tools: linked - name: Assemble if: matrix.language == 'java' # --no-build-cache is required for codeql to analyze all modules # --no-daemon is required for codeql to observe the compilation # (see https://docs.github.com/en/code-security/codeql-cli/getting-started-with-the-codeql-cli/preparing-your-code-for-codeql-analysis#specifying-build-commands) - # quarkus tasks are disabled because they often cause the build to fail (see https://github.com/open-telemetry/opentelemetry-java-instrumentation/issues/13284) - run: ./gradlew assemble -x javadoc -x :instrumentation:quarkus-resteasy-reactive:quarkus3-testing:quarkusGenerateCodeDev -x :instrumentation:quarkus-resteasy-reactive:quarkus2-testing:quarkusGenerateCodeDev --no-build-cache --no-daemon + # collectReachabilityMetadata tasks are disabled because they often cause the build to fail + run: > + ./gradlew assemble -x javadoc + -x :smoke-tests-otel-starter:spring-boot-3:collectReachabilityMetadata + -x :smoke-tests-otel-starter:spring-boot-3.2:collectReachabilityMetadata + -x :smoke-tests-otel-starter:spring-boot-reactive-3:collectReachabilityMetadata + --no-build-cache --no-daemon - name: Perform CodeQL analysis - uses: github/codeql-action/analyze@76621b61decf072c1cee8dd1ce2d2a82d33c17ed # v3.29.8 + uses: github/codeql-action/analyze@f1f6e5f6af878fb37288ce1c627459e94dbf7d01 # v3.30.1 with: category: "/language:${{matrix.language}}" diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml index b85021ab000d..c0f2e6e00926 100644 --- a/.github/workflows/dependency-review.yml +++ b/.github/workflows/dependency-review.yml @@ -17,6 +17,6 @@ jobs: runs-on: ubuntu-latest steps: - name: 'Checkout Repository' - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - name: 'Dependency Review' - uses: actions/dependency-review-action@da24556b548a50705dd671f47852072ea4c105d9 # v4.7.1 + uses: actions/dependency-review-action@595b5aeba73380359d98a5e087f648dbb0edce1b # v4.7.3 diff --git a/.github/workflows/documentation-disable-list-audit.yml b/.github/workflows/documentation-disable-list-audit.yml index 77f862af5c62..30ad3e8bb9bf 100644 --- a/.github/workflows/documentation-disable-list-audit.yml +++ b/.github/workflows/documentation-disable-list-audit.yml @@ -12,9 +12,9 @@ jobs: crawl: runs-on: ubuntu-latest steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - - uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1 + - uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.0 with: distribution: temurin java-version: 17 diff --git a/.github/workflows/fossa.yml b/.github/workflows/fossa.yml index ddc524464ed2..4f2c7d5d6fd4 100644 --- a/.github/workflows/fossa.yml +++ b/.github/workflows/fossa.yml @@ -12,7 +12,7 @@ jobs: fossa: runs-on: ubuntu-latest steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - uses: fossas/fossa-action@3ebcea1862c6ffbd5cf1b4d0bd6b3fe7bd6f2cac # v1.7.0 with: diff --git a/.github/workflows/gradle-wrapper-validation.yml b/.github/workflows/gradle-wrapper-validation.yml index 3601282d427c..e18b208f66b7 100644 --- a/.github/workflows/gradle-wrapper-validation.yml +++ b/.github/workflows/gradle-wrapper-validation.yml @@ -11,7 +11,7 @@ jobs: gradle-wrapper-validation: runs-on: ubuntu-latest steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 # this needs to be in its own workflow in order to make OSSF scorecard happy - uses: gradle/actions/wrapper-validation@017a9effdb900e5b5b2fddfb590a105619dca3c3 # v4.4.2 diff --git a/.github/workflows/issue-management-feedback-label.yml b/.github/workflows/issue-management-feedback-label.yml index fb4a037576c2..49db5efb55d5 100644 --- a/.github/workflows/issue-management-feedback-label.yml +++ b/.github/workflows/issue-management-feedback-label.yml @@ -18,7 +18,7 @@ jobs: github.event.comment.user.login == github.event.issue.user.login runs-on: ubuntu-latest steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - name: Remove labels env: diff --git a/.github/workflows/issue-management-stale-action.yml b/.github/workflows/issue-management-stale-action.yml index 483df9b15a76..7282d3fcd6f1 100644 --- a/.github/workflows/issue-management-stale-action.yml +++ b/.github/workflows/issue-management-stale-action.yml @@ -16,7 +16,7 @@ jobs: pull-requests: write # for actions/stale to close stale PRs runs-on: ubuntu-latest steps: - - uses: actions/stale@5bef64f19d7facfb25b37b414482c7164d639639 # v9.1.0 + - uses: actions/stale@3a9db7e6a41a89f618792c92c0e97cc736e1b13f # v10.0.0 with: repo-token: ${{ secrets.GITHUB_TOKEN }} days-before-stale: 7 diff --git a/.github/workflows/label.yml b/.github/workflows/label.yml index 8f64d9b34d98..b05a9b2f942c 100644 --- a/.github/workflows/label.yml +++ b/.github/workflows/label.yml @@ -11,6 +11,6 @@ jobs: contents: read pull-requests: write steps: - - uses: actions/labeler@8558fd74291d67161a8a78ce36a881fa63b766a9 # v5.0.0 + - uses: actions/labeler@634933edcd8ababfe52f92936142cc22ac488b1b # v6.0.1 with: repo-token: "${{ secrets.GITHUB_TOKEN }}" diff --git a/.github/workflows/metadata-update.yml b/.github/workflows/metadata-update.yml index 98caade39fca..8147236985ca 100644 --- a/.github/workflows/metadata-update.yml +++ b/.github/workflows/metadata-update.yml @@ -21,13 +21,13 @@ jobs: pull-requests: write # for adding label and assignee to PR steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - name: Free disk space run: .github/scripts/gha-free-disk-space.sh - name: Set up JDK for running Gradle - uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1 + uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.0 with: distribution: temurin java-version-file: .java-version @@ -55,7 +55,7 @@ jobs: if: steps.diffcheck.outputs.has_diff == 'true' run: .github/scripts/use-cla-approved-bot.sh - - uses: actions/create-github-app-token@0f859bf9e69e887678d5bbfbee594437cb440ffe # v2.1.0 + - uses: actions/create-github-app-token@a8d616148505b5069dccd32f177bb87d7f39123b # v2.1.1 if: steps.diffcheck.outputs.has_diff == 'true' id: otelbot-token with: @@ -68,7 +68,7 @@ jobs: env: GH_TOKEN: ${{ steps.otelbot-token.outputs.token }} run: | - BRANCH_NAME="metadata-update-main" + BRANCH_NAME="otelbot/metadata-update-main" echo "branch=$BRANCH_NAME" >> $GITHUB_OUTPUT if git ls-remote --exit-code --heads origin "$BRANCH_NAME"; then git fetch origin "$BRANCH_NAME" diff --git a/.github/workflows/ossf-scorecard.yml b/.github/workflows/ossf-scorecard.yml index 4be05df86a17..0581c4fd7cf2 100644 --- a/.github/workflows/ossf-scorecard.yml +++ b/.github/workflows/ossf-scorecard.yml @@ -19,7 +19,7 @@ jobs: # Needed for GitHub OIDC token if publish_results is true id-token: write steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: persist-credentials: false @@ -42,6 +42,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard (optional). # Commenting out will disable upload of results to your repo's Code Scanning dashboard - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@76621b61decf072c1cee8dd1ce2d2a82d33c17ed # v3.29.8 + uses: github/codeql-action/upload-sarif@f1f6e5f6af878fb37288ce1c627459e94dbf7d01 # v3.30.1 with: sarif_file: results.sarif diff --git a/.github/workflows/overhead-benchmark-daily.yml b/.github/workflows/overhead-benchmark-daily.yml index 3c5c508c0cb0..e1e280441ff7 100644 --- a/.github/workflows/overhead-benchmark-daily.yml +++ b/.github/workflows/overhead-benchmark-daily.yml @@ -14,9 +14,9 @@ jobs: contents: write # for git push to gh-pages branch runs-on: ubuntu-latest steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: ref: gh-pages path: gh-pages diff --git a/.github/workflows/owasp-dependency-check-daily.yml b/.github/workflows/owasp-dependency-check-daily.yml index 96e2458f4545..b0a8395b4d85 100644 --- a/.github/workflows/owasp-dependency-check-daily.yml +++ b/.github/workflows/owasp-dependency-check-daily.yml @@ -15,13 +15,13 @@ jobs: analyze: runs-on: ubuntu-latest steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - name: Free disk space run: .github/scripts/gha-free-disk-space.sh - name: Set up JDK for running Gradle - uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1 + uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.0 with: distribution: temurin java-version-file: .java-version diff --git a/.github/workflows/pr-smoke-test-early-jdk8-images.yml b/.github/workflows/pr-smoke-test-early-jdk8-images.yml index 929cfefe571b..a81cac5400ab 100644 --- a/.github/workflows/pr-smoke-test-early-jdk8-images.yml +++ b/.github/workflows/pr-smoke-test-early-jdk8-images.yml @@ -13,13 +13,13 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - name: Free disk space run: .github/scripts/gha-free-disk-space.sh - name: Set up JDK for running Gradle - uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1 + uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.0 with: distribution: temurin java-version-file: .java-version diff --git a/.github/workflows/pr-smoke-test-fake-backend-images.yml b/.github/workflows/pr-smoke-test-fake-backend-images.yml index e5647800b96b..98eb4acbc093 100644 --- a/.github/workflows/pr-smoke-test-fake-backend-images.yml +++ b/.github/workflows/pr-smoke-test-fake-backend-images.yml @@ -13,13 +13,13 @@ jobs: buildLinux: runs-on: ubuntu-latest steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - name: Free disk space run: .github/scripts/gha-free-disk-space.sh - name: Set up JDK for running Gradle - uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1 + uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.0 with: distribution: temurin java-version-file: .java-version @@ -41,10 +41,10 @@ jobs: - name: Support long paths run: git config --system core.longpaths true - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - name: Set up JDK for running Gradle - uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1 + uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.0 with: distribution: temurin java-version-file: .java-version diff --git a/.github/workflows/pr-smoke-test-servlet-images.yml b/.github/workflows/pr-smoke-test-servlet-images.yml index 1533476e706e..979784fef2eb 100644 --- a/.github/workflows/pr-smoke-test-servlet-images.yml +++ b/.github/workflows/pr-smoke-test-servlet-images.yml @@ -34,13 +34,13 @@ jobs: run: git config --system core.longpaths true if: matrix.os == 'windows-latest' - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - name: Free disk space run: .github/scripts/gha-free-disk-space.sh - name: Set up JDK for running Gradle - uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1 + uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.0 with: distribution: temurin java-version-file: .java-version diff --git a/.github/workflows/prepare-patch-release.yml b/.github/workflows/prepare-patch-release.yml index 7f6dac305415..1fb62fd6fba6 100644 --- a/.github/workflows/prepare-patch-release.yml +++ b/.github/workflows/prepare-patch-release.yml @@ -11,7 +11,7 @@ jobs: contents: write # for git push to PR branch runs-on: ubuntu-latest steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - run: | if [[ ! $GITHUB_REF_NAME =~ ^release/v[0-9]+\.[0-9]+\.x$ ]]; then @@ -51,7 +51,7 @@ jobs: - name: Use CLA approved bot run: .github/scripts/use-cla-approved-bot.sh - - uses: actions/create-github-app-token@0f859bf9e69e887678d5bbfbee594437cb440ffe # v2.1.0 + - uses: actions/create-github-app-token@a8d616148505b5069dccd32f177bb87d7f39123b # v2.1.1 id: otelbot-token with: app-id: ${{ vars.OTELBOT_APP_ID }} diff --git a/.github/workflows/prepare-release-branch.yml b/.github/workflows/prepare-release-branch.yml index bc967c67520a..e7afaeb036e9 100644 --- a/.github/workflows/prepare-release-branch.yml +++ b/.github/workflows/prepare-release-branch.yml @@ -9,7 +9,7 @@ jobs: prereqs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - name: Verify prerequisites run: | @@ -30,7 +30,7 @@ jobs: needs: - prereqs steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - name: Create release branch run: | @@ -63,7 +63,7 @@ jobs: - name: Use CLA approved bot run: .github/scripts/use-cla-approved-bot.sh - - uses: actions/create-github-app-token@0f859bf9e69e887678d5bbfbee594437cb440ffe # v2.1.0 + - uses: actions/create-github-app-token@a8d616148505b5069dccd32f177bb87d7f39123b # v2.1.1 id: otelbot-token with: app-id: ${{ vars.OTELBOT_APP_ID }} @@ -91,7 +91,7 @@ jobs: needs: - prereqs steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - name: Set environment variables run: | @@ -120,7 +120,7 @@ jobs: - name: Use CLA approved bot run: .github/scripts/use-cla-approved-bot.sh - - uses: actions/create-github-app-token@0f859bf9e69e887678d5bbfbee594437cb440ffe # v2.1.0 + - uses: actions/create-github-app-token@a8d616148505b5069dccd32f177bb87d7f39123b # v2.1.1 id: otelbot-token with: app-id: ${{ vars.OTELBOT_APP_ID }} diff --git a/.github/workflows/publish-petclinic-benchmark-image.yml b/.github/workflows/publish-petclinic-benchmark-image.yml index f1c0db36041f..bbf785c60e48 100644 --- a/.github/workflows/publish-petclinic-benchmark-image.yml +++ b/.github/workflows/publish-petclinic-benchmark-image.yml @@ -17,7 +17,7 @@ jobs: contents: read packages: write steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1 diff --git a/.github/workflows/publish-smoke-test-early-jdk8-images.yml b/.github/workflows/publish-smoke-test-early-jdk8-images.yml index 596c78aad4df..fff3630db68d 100644 --- a/.github/workflows/publish-smoke-test-early-jdk8-images.yml +++ b/.github/workflows/publish-smoke-test-early-jdk8-images.yml @@ -19,13 +19,13 @@ jobs: packages: write runs-on: ubuntu-latest steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - name: Free disk space run: .github/scripts/gha-free-disk-space.sh - name: Set up JDK for running Gradle - uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1 + uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.0 with: distribution: temurin java-version-file: .java-version diff --git a/.github/workflows/publish-smoke-test-fake-backend-images.yml b/.github/workflows/publish-smoke-test-fake-backend-images.yml index 6df4f92805e9..604517b3dd31 100644 --- a/.github/workflows/publish-smoke-test-fake-backend-images.yml +++ b/.github/workflows/publish-smoke-test-fake-backend-images.yml @@ -19,13 +19,13 @@ jobs: packages: write runs-on: ubuntu-latest steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - name: Free disk space run: .github/scripts/gha-free-disk-space.sh - name: Set up JDK for running Gradle - uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1 + uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.0 with: distribution: temurin java-version-file: .java-version @@ -58,10 +58,10 @@ jobs: - name: Support long paths run: git config --system core.longpaths true - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - name: Set up JDK for running Gradle - uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1 + uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.0 with: distribution: temurin java-version-file: .java-version diff --git a/.github/workflows/publish-smoke-test-servlet-images.yml b/.github/workflows/publish-smoke-test-servlet-images.yml index dc0995cd76a4..a9c99381254d 100644 --- a/.github/workflows/publish-smoke-test-servlet-images.yml +++ b/.github/workflows/publish-smoke-test-servlet-images.yml @@ -53,14 +53,14 @@ jobs: run: git config --system core.longpaths true if: matrix.os == 'windows-latest' - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - name: Free disk space if: matrix.os != 'windows-latest' run: .github/scripts/gha-free-disk-space.sh - name: Set up JDK for running Gradle - uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1 + uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.0 with: distribution: temurin java-version-file: .java-version diff --git a/.github/workflows/release-update-cloudfoundry-index.yml b/.github/workflows/release-update-cloudfoundry-index.yml index 856c5f17d38b..e4e7ff33176e 100644 --- a/.github/workflows/release-update-cloudfoundry-index.yml +++ b/.github/workflows/release-update-cloudfoundry-index.yml @@ -17,14 +17,14 @@ jobs: contents: write # for git push to PR branch runs-on: ubuntu-latest steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 # need to run this script before we switch branches # since the script doesn't exist on the cloudfoundry branch - name: Use CLA approved github bot run: .github/scripts/use-cla-approved-bot.sh - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: ref: 'cloudfoundry' @@ -44,7 +44,7 @@ jobs: - name: display changes run: git diff - - uses: actions/create-github-app-token@0f859bf9e69e887678d5bbfbee594437cb440ffe # v2.1.0 + - uses: actions/create-github-app-token@a8d616148505b5069dccd32f177bb87d7f39123b # v2.1.1 id: otelbot-token with: app-id: ${{ vars.OTELBOT_APP_ID }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b420a95d93bb..8b0d04764231 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -34,7 +34,7 @@ jobs: exit 1 fi - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - name: Set environment variables run: | @@ -63,7 +63,7 @@ jobs: # check out main branch to verify there won't be problems with merging the change log # at the end of this workflow - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: ref: main @@ -78,7 +78,7 @@ jobs: fi # back to the release branch - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: # tags are needed for the generate-release-contributors.sh script fetch-depth: 0 @@ -86,7 +86,7 @@ jobs: - name: Free disk space run: .github/scripts/gha-free-disk-space.sh - - uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1 + - uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.0 with: distribution: temurin java-version-file: .java-version @@ -189,7 +189,7 @@ jobs: echo "version=$VERSION" >> $GITHUB_OUTPUT echo "prior-version=$PRIOR_VERSION" >> $GITHUB_OUTPUT - update-apidiff-baseline-to-released-version: + post-release-updates: permissions: contents: write # for git push to PR branch runs-on: ubuntu-latest @@ -198,7 +198,7 @@ jobs: steps: # add change log sync (if any) into this PR since the apidiff update # is required before any other PR can be merged anyway - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - name: Copy change log section from release branch env: @@ -207,7 +207,7 @@ jobs: sed -n "0,/^## Version $VERSION /d;/^## Version /q;p" CHANGELOG.md \ > /tmp/changelog-section.md - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: ref: main @@ -244,7 +244,7 @@ jobs: - name: Use CLA approved bot run: .github/scripts/use-cla-approved-bot.sh - - uses: actions/create-github-app-token@0f859bf9e69e887678d5bbfbee594437cb440ffe # v2.1.0 + - uses: actions/create-github-app-token@a8d616148505b5069dccd32f177bb87d7f39123b # v2.1.1 id: otelbot-token with: app-id: ${{ vars.OTELBOT_APP_ID }} @@ -256,8 +256,8 @@ jobs: # not using secrets.GITHUB_TOKEN since pull requests from that token do not run workflows GH_TOKEN: ${{ steps.otelbot-token.outputs.token }} run: | - message="Update apidiff baseline to released version $VERSION" - body="Update apidiff baseline to released version \`$VERSION\`." + message="Post-release updates for $VERSION" + body="Post-release updates for `$VERSION`." branch="otelbot/update-apidiff-baseline-to-released-version-${VERSION}" git checkout -b $branch diff --git a/.github/workflows/reusable-link-check.yml b/.github/workflows/reusable-link-check.yml index 044e85e1f9a5..77e3a95802fe 100644 --- a/.github/workflows/reusable-link-check.yml +++ b/.github/workflows/reusable-link-check.yml @@ -10,7 +10,7 @@ jobs: link-check: runs-on: ubuntu-latest steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - name: Link check env: diff --git a/.github/workflows/reusable-markdown-lint-check.yml b/.github/workflows/reusable-markdown-lint-check.yml index 9940d33465cc..5b45c0ca516e 100644 --- a/.github/workflows/reusable-markdown-lint-check.yml +++ b/.github/workflows/reusable-markdown-lint-check.yml @@ -10,7 +10,7 @@ jobs: markdown-lint-check: runs-on: ubuntu-latest steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - name: Run markdownlint run: | diff --git a/.github/workflows/reusable-misspell-check.yml b/.github/workflows/reusable-misspell-check.yml index 2d56ee6ed725..bef3f1171803 100644 --- a/.github/workflows/reusable-misspell-check.yml +++ b/.github/workflows/reusable-misspell-check.yml @@ -10,7 +10,7 @@ jobs: misspell-check: runs-on: ubuntu-latest steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - name: Install misspell run: | diff --git a/.github/workflows/reusable-muzzle.yml b/.github/workflows/reusable-muzzle.yml index 233fb16664ee..ca08fd469212 100644 --- a/.github/workflows/reusable-muzzle.yml +++ b/.github/workflows/reusable-muzzle.yml @@ -22,13 +22,13 @@ jobs: - ":instrumentation:muzzle4" fail-fast: false steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - name: Free disk space run: .github/scripts/gha-free-disk-space.sh - name: Set up JDK for running Gradle - uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1 + uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.0 with: distribution: temurin java-version-file: .java-version diff --git a/.github/workflows/reusable-native-tests.yml b/.github/workflows/reusable-native-tests.yml index e83635556ab1..96c9538037fb 100644 --- a/.github/workflows/reusable-native-tests.yml +++ b/.github/workflows/reusable-native-tests.yml @@ -24,10 +24,10 @@ jobs: - 23 - 24 steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - id: read-java run: echo "version=$(cat .java-version)" >> "$GITHUB_OUTPUT" - - uses: graalvm/setup-graalvm@7f488cf82a3629ee755e4e97342c01d6bed318fa # v1.3.5.1 + - uses: graalvm/setup-graalvm@7a1da54cb7fdef4ea19f6ecdfa9ecf59dc5a48fe # v1.3.6.1 with: version: "latest" java-version: ${{ matrix.test-java-version }} diff --git a/.github/workflows/reusable-pr-smoke-test-images.yml b/.github/workflows/reusable-pr-smoke-test-images.yml index a6f30dcc51cd..b2ae82c2a770 100644 --- a/.github/workflows/reusable-pr-smoke-test-images.yml +++ b/.github/workflows/reusable-pr-smoke-test-images.yml @@ -36,13 +36,13 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - name: Free disk space run: .github/scripts/gha-free-disk-space.sh - name: Set up JDK for running Gradle - uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1 + uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.0 with: distribution: temurin java-version-file: .java-version diff --git a/.github/workflows/reusable-publish-smoke-test-images.yml b/.github/workflows/reusable-publish-smoke-test-images.yml index 28b4cd5b9bd2..eb29b3a96c28 100644 --- a/.github/workflows/reusable-publish-smoke-test-images.yml +++ b/.github/workflows/reusable-publish-smoke-test-images.yml @@ -39,13 +39,13 @@ jobs: contents: read packages: write steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - name: Free disk space run: .github/scripts/gha-free-disk-space.sh - name: Set up JDK for running Gradle - uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1 + uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.0 with: distribution: temurin java-version-file: .java-version diff --git a/.github/workflows/reusable-shell-script-check.yml b/.github/workflows/reusable-shell-script-check.yml index 4e5f51e0fe35..b2e403110019 100644 --- a/.github/workflows/reusable-shell-script-check.yml +++ b/.github/workflows/reusable-shell-script-check.yml @@ -10,7 +10,7 @@ jobs: shell-script-check: runs-on: ubuntu-latest steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - name: Install shell check run: wget -qO- "https://github.com/koalaman/shellcheck/releases/download/stable/shellcheck-stable.linux.x86_64.tar.xz" | tar -xJv diff --git a/.github/workflows/reusable-test-latest-deps.yml b/.github/workflows/reusable-test-latest-deps.yml index d0b0933d0f77..97270dfb514f 100644 --- a/.github/workflows/reusable-test-latest-deps.yml +++ b/.github/workflows/reusable-test-latest-deps.yml @@ -29,13 +29,13 @@ jobs: - 3 fail-fast: false steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - name: Free disk space run: .github/scripts/gha-free-disk-space.sh - name: Set up JDK for running Gradle - uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1 + uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.0 with: distribution: temurin java-version-file: .java-version @@ -85,7 +85,7 @@ jobs: - name: Get current job url id: jobs if: ${{ !cancelled() }} - uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 env: matrix: ${{ toJson(matrix) }} with: diff --git a/.github/workflows/reusable-workflow-notification.yml b/.github/workflows/reusable-workflow-notification.yml index 701f90f5a084..61e8d6267ccb 100644 --- a/.github/workflows/reusable-workflow-notification.yml +++ b/.github/workflows/reusable-workflow-notification.yml @@ -19,7 +19,7 @@ jobs: issues: write runs-on: ubuntu-latest steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - name: Open issue or add comment if issue already open env: diff --git a/.github/workflows/survey-on-merged-pr.yml b/.github/workflows/survey-on-merged-pr.yml new file mode 100644 index 000000000000..4d9ebcdee5ff --- /dev/null +++ b/.github/workflows/survey-on-merged-pr.yml @@ -0,0 +1,48 @@ +name: Survey on Merged PR by Non-Member + +on: + pull_request_target: + types: [closed] + +permissions: + contents: read + +env: + PR_NUM: ${{ github.event.pull_request.number }} + SURVEY_URL: https://docs.google.com/forms/d/e/1FAIpQLSf2FfCsW-DimeWzdQgfl0KDzT2UEAqu69_f7F2BVPSxVae1cQ/viewform?entry.1540511742=${{ github.repository }} + +jobs: + comment-on-pr: + name: Add survey to PR if author is not a member + runs-on: ubuntu-latest + if: github.event.pull_request.merged == true + steps: + - uses: actions/create-github-app-token@a8d616148505b5069dccd32f177bb87d7f39123b # v2.1.1 + id: otelbot-token + with: + app-id: ${{ vars.OTELBOT_APP_ID }} + private-key: ${{ secrets.OTELBOT_PRIVATE_KEY }} + + - name: Add survey comment if author is not a member or bot + run: | + USERNAME="${{ github.event.pull_request.user.login }}" + USER_TYPE="${{ github.event.pull_request.user.type }}" + ORG="${{ github.repository_owner }}" + + # Skip if user is a bot + if [[ "$USER_TYPE" == "Bot" ]]; then + echo "Skipping survey for bot user: $USERNAME" + exit 0 + fi + + # Skip if user is an org member + if gh api "orgs/$ORG/members/$USERNAME" --silent; then + echo "Skipping survey for org member: $USERNAME" + exit 0 + fi + + # Add survey comment for external contributor + echo "Adding survey comment for external contributor: $USERNAME" + gh pr comment ${PR_NUM} --repo ${{ github.repository }} --body "Thank you for your contribution @${USERNAME}! 🎉 We would like to hear from you about your experience contributing to OpenTelemetry by taking a few minutes to fill out this [survey](${SURVEY_URL})." + env: + GH_TOKEN: ${{ steps.otelbot-token.outputs.token }} diff --git a/CHANGELOG.md b/CHANGELOG.md index 3a1b7588ad2b..e63aa29ac32a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,64 @@ # Changelog -## Unreleased +## Version 2.20.0 (2025-09-13) + +### Migration notes + +- The configuration option used to enable traces generated by the `dropwizard-views` instrumentation has changed from `otel.instrumentation.common.experimental.controller-telemetry.enabled` to `otel.instrumentation.common.experimental.view-telemetry.enabled` + ([#14475](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/14475)) +- Deprecated (and `-alpha`) SpanNames class was removed + ([#14582](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/14582)) + +### 🌟 New javaagent instrumentation + +- Add ClickHouse client v2 instrumentation + ([#14501](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/14501)) + +### 📈 Enhancements + +- Add code attributes for log4j1 + ([#13947](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/13947)) +- Add headers capture feature to Kafka 2.6 interceptors + ([#14290](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/14290)) +- Support custom exception handling from logger instrumentation + ([#14493](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/14493)) +- Add call depth check to executor instrumentation + ([#14546](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/14546)) +- Add Spring starter thread details support + ([#14449](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/14449)) +- Let AWS Lambda SQS handlers report partial batch failures + ([#14468](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/14468)) +- Instrument instrumentation suppression API + ([#14565](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/14565)) +- Declarative config: update file version support + ([#14593](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/14593)) +- Add `url.template` support to Spring 6 RestTemplate + ([#14612](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/14612)) +- Declarative config: add missing resource providers + ([#14222](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/14222)) +- Declarative config: map common-enabled property + ([#14589](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/14589)) + +### 🛠️ Bug fixes + +- Fix non-lowercase messaging headers capture + ([#14479](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/14479)) +- Fix JUL logger methods parameter mismatch + ([#14531](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/14531)) +- Fix a test failure with latest Mongo release + ([#14642](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/14642)) + +### 🧰 Tooling + +- Add constructor to AgentClassLoader for custom parent ClassLoader + ([#14480](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/14480)) +- Declarative config: make bridge usable by Spring starter and contrib + ([#14497](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/14497), + [#14548](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/14548)) +- Reduce log level for main jar detection + ([#14528](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/14528)) + +## Version 2.19.0 (2025-08-17) ### 📈 Enhancements diff --git a/README.md b/README.md index c9ac848e0a47..fd20606aef4c 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ If you are looking for documentation on using those. ## Getting Started Download -the [latest version](https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/latest/download/opentelemetry-javaagent.jar). +the [latest version](https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/download/v2.20.0/opentelemetry-javaagent.jar). This package includes the instrumentation agent as well as instrumentations for all supported libraries and all available data exporters. diff --git a/benchmark-overhead-jmh/build.gradle.kts b/benchmark-overhead-jmh/build.gradle.kts index 00484af35862..00d3ddbb5725 100644 --- a/benchmark-overhead-jmh/build.gradle.kts +++ b/benchmark-overhead-jmh/build.gradle.kts @@ -13,7 +13,7 @@ otelJava { } dependencies { - jmhImplementation("org.springframework.boot:spring-boot-starter-web:3.5.4") + jmhImplementation("org.springframework.boot:spring-boot-starter-web:3.5.5") } tasks { diff --git a/benchmark-overhead/Dockerfile.petclinic b/benchmark-overhead/Dockerfile.petclinic index 8cc71ceaee6a..eacc3df2562f 100644 --- a/benchmark-overhead/Dockerfile.petclinic +++ b/benchmark-overhead/Dockerfile.petclinic @@ -1,4 +1,4 @@ -FROM eclipse-temurin:11.0.28_6-jdk@sha256:63060204d77c7b003f3e1bdac280dd905c9c33a8280bc32309d4e641ede5d39e as app-build +FROM eclipse-temurin:11.0.28_6-jdk@sha256:e9f66c0fb6833112e436fa5f82fc6f1e43d0624736f639ebe5e9ba3c7635faf5 as app-build # This is the base image that will contain a built version of the spring-petclinic-rest # application. Installing the dependencies and maven compiling the application is time diff --git a/benchmark-overhead/README.md b/benchmark-overhead/README.md index 5aaef1af19bd..f69bd8c1805e 100644 --- a/benchmark-overhead/README.md +++ b/benchmark-overhead/README.md @@ -50,7 +50,7 @@ relative overhead. | Thread switch rate | # / s | Max observed thread context switch rate | | GC time | ms | Total amount of time spent paused for garbage collection | | Request mean | ms | Average time to handle a single web request (measured at the caller) | -| Request p95 | ms | 95th percentile time to handle a single web requ4st (measured at the caller) | +| Request p95 | ms | 95th percentile time to handle a single web request (measured at the caller) | | Iteration mean | ms | average time to do a single pass through the k6 test script | | Iteration p95 | ms | 95th percentile time to do a single pass through the k6 test script | | Peak threads | # | Highest number of running threads in the VM, including agent threads | diff --git a/benchmark-overhead/gradle/wrapper/gradle-wrapper.jar b/benchmark-overhead/gradle/wrapper/gradle-wrapper.jar index 1b33c55baabb..8bdaf60c75ab 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 78cb6e16a49f..3e781fbad9c7 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=bd71102213493060956ec229d946beee57158dbd89d0e62b91bca0fa2c5f3531 -distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip +distributionSha256Sum=8fad3d78296ca518113f3d29016617c7f9367dc005f932bd9d93bf45ba46072b +distributionUrl=https\://services.gradle.org/distributions/gradle-9.0.0-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/benchmark-overhead/gradlew b/benchmark-overhead/gradlew index 23d15a936707..ef07e0162b18 100755 --- a/benchmark-overhead/gradlew +++ b/benchmark-overhead/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/conventions/build.gradle.kts b/conventions/build.gradle.kts index fac54d233cd9..017e41522dbf 100644 --- a/conventions/build.gradle.kts +++ b/conventions/build.gradle.kts @@ -56,19 +56,18 @@ dependencies { // When updating, update above in plugins too implementation("com.diffplug.spotless:spotless-plugin-gradle:7.2.1") implementation("com.google.guava:guava:33.4.8-jre") - implementation("gradle.plugin.com.google.protobuf:protobuf-gradle-plugin:0.8.18") - implementation("com.gradleup.shadow:shadow-gradle-plugin:8.3.9") + implementation("com.gradleup.shadow:shadow-gradle-plugin:9.1.0") implementation("org.apache.httpcomponents:httpclient:4.5.14") - implementation("com.gradle.develocity:com.gradle.develocity.gradle.plugin:4.1") + implementation("com.gradle.develocity:com.gradle.develocity.gradle.plugin:4.1.1") implementation("org.owasp:dependency-check-gradle:12.1.3") implementation("ru.vyarus:gradle-animalsniffer-plugin:2.0.1") implementation("org.spdx:spdx-gradle-plugin:0.9.0") // When updating, also update dependencyManagement/build.gradle.kts - implementation("net.bytebuddy:byte-buddy-gradle-plugin:1.17.6") + implementation("net.bytebuddy:byte-buddy-gradle-plugin:1.17.7") implementation("gradle.plugin.io.morethan.jmhreport:gradle-jmh-report:0.9.6") implementation("me.champeau.jmh:jmh-gradle-plugin:0.7.3") implementation("net.ltgt.gradle:gradle-errorprone-plugin:4.3.0") - implementation("net.ltgt.gradle:gradle-nullaway-plugin:2.2.0") + implementation("net.ltgt.gradle:gradle-nullaway-plugin:2.3.0") implementation("me.champeau.gradle:japicmp-gradle-plugin:0.4.6") testImplementation(enforcedPlatform("org.junit:junit-bom:5.13.4")) diff --git a/conventions/src/main/kotlin/io.opentelemetry.instrumentation.javaagent-shadowing.gradle.kts b/conventions/src/main/kotlin/io.opentelemetry.instrumentation.javaagent-shadowing.gradle.kts index 5c04a66377c4..f658edd1dea6 100644 --- a/conventions/src/main/kotlin/io.opentelemetry.instrumentation.javaagent-shadowing.gradle.kts +++ b/conventions/src/main/kotlin/io.opentelemetry.instrumentation.javaagent-shadowing.gradle.kts @@ -8,9 +8,17 @@ plugins { // io.opentelemetry.instrumentation.muzzle-check.gradle.kts tasks.withType().configureEach { mergeServiceFiles() + // mergeServiceFiles requires that duplicate strategy is set to include + filesMatching("META-INF/services/**") { + duplicatesStrategy = DuplicatesStrategy.INCLUDE + } // Merge any AWS SDK service files that may be present (too bad they didn't just use normal // service loader...) mergeServiceFiles("software/amazon/awssdk/global/handlers") + // mergeServiceFiles requires that duplicate strategy is set to include + filesMatching("software/amazon/awssdk/global/handlers/**") { + duplicatesStrategy = DuplicatesStrategy.INCLUDE + } exclude("**/module-info.class") diff --git a/conventions/src/main/kotlin/io.opentelemetry.instrumentation.javaagent-testing.gradle.kts b/conventions/src/main/kotlin/io.opentelemetry.instrumentation.javaagent-testing.gradle.kts index b00652f7d59c..bfeb0ccbd4f7 100644 --- a/conventions/src/main/kotlin/io.opentelemetry.instrumentation.javaagent-testing.gradle.kts +++ b/conventions/src/main/kotlin/io.opentelemetry.instrumentation.javaagent-testing.gradle.kts @@ -152,11 +152,13 @@ afterEvaluate { // shadowJar is only used for creating a jar for testing, but the shadow plugin automatically adds // it to a project's published Java component. Skip it if publishing is configured for this // project. -plugins.withId("maven-publish") { - configure { - (components["java"] as AdhocComponentWithVariants).run { - withVariantsFromConfiguration(configurations["shadowRuntimeElements"]) { - skip() +afterEvaluate { + plugins.withId("maven-publish") { + configure { + (components["java"] as AdhocComponentWithVariants).run { + withVariantsFromConfiguration(configurations["shadowRuntimeElements"]) { + skip() + } } } } diff --git a/conventions/src/main/kotlin/otel.java-conventions.gradle.kts b/conventions/src/main/kotlin/otel.java-conventions.gradle.kts index 22f6bf59a98a..e48b53444a93 100644 --- a/conventions/src/main/kotlin/otel.java-conventions.gradle.kts +++ b/conventions/src/main/kotlin/otel.java-conventions.gradle.kts @@ -143,7 +143,7 @@ abstract class NettyAlignmentRule : ComponentMetadataRule { with(ctx.details) { if (id.group == "io.netty" && id.name != "netty") { if (id.version.startsWith("4.1.")) { - belongsTo("io.netty:netty-bom:4.1.124.Final", false) + belongsTo("io.netty:netty-bom:4.1.127.Final", false) } else if (id.version.startsWith("4.0.")) { belongsTo("io.netty:netty-bom:4.0.56.Final", false) } @@ -339,6 +339,14 @@ val resourceClassesCsv = resourceNames.joinToString(",") { "io.opentelemetry.sdk tasks.withType().configureEach { useJUnitPlatform() + // work around jvm crash on openJ9 8 after updating armeria to 1.33.1 + val testJavaVersion = gradle.startParameter.projectProperties["testJavaVersion"]?.let(JavaVersion::toVersion) + val useJ9 = gradle.startParameter.projectProperties["testJavaVM"]?.run { this == "openj9" } + ?: false + if (useJ9 && testJavaVersion != null && testJavaVersion.isJava8) { + jvmArgs("-Xjit:exclude={io/opentelemetry/testing/internal/io/netty/buffer/HeapByteBufUtil.*},exclude={io/opentelemetry/testing/internal/io/netty/buffer/UnpooledHeapByteBuf.*},exclude={io/opentelemetry/testing/internal/io/netty/buffer/AbstractByteBuf.*}") + } + // There's no real harm in setting this for all tests even if any happen to not be using context // propagation. jvmArgs("-Dio.opentelemetry.context.enableStrictContext=${rootProject.findProperty("enableStrictContext") ?: true}") @@ -431,7 +439,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 = "11.0.0" + toolVersion = "11.0.1" maxWarnings = 0 } diff --git a/declarative-config-bridge/README.md b/declarative-config-bridge/README.md new file mode 100644 index 000000000000..4938649ae891 --- /dev/null +++ b/declarative-config-bridge/README.md @@ -0,0 +1,84 @@ +# Declarative Config Bridge + +Declarative Config Bridge allows instrumentation authors to access configuration in a uniform way, +regardless of the configuration source. + +The bridge allows you to read configuration using the system property style when dealing with +declarative configuration. + +## Example + +As an example, let's look at the inferred spans configuration. +First, there is a configuration method that reads the properties and is unaware of the source of the +configuration: + +```java +class InferredSpansConfig { + static SpanProcessor create(ConfigProperties properties) { + // read properties here + boolean backupDiagnosticFiles = + properties.getBoolean("otel.inferred.spans.backup.diagnostic.files", false); + } +} +``` + +The auto configuration **without declarative config** passes the provided properties directly: + +```java + +@AutoService(AutoConfigurationCustomizerProvider.class) +public class InferredSpansAutoConfig implements AutoConfigurationCustomizerProvider { + + @Override + public void customize(AutoConfigurationCustomizer config) { + config.addTracerProviderCustomizer( + (providerBuilder, properties) -> { + providerBuilder.addSpanProcessor(InferredSpansConfig.create(properties)); + return providerBuilder; + }); + } +} +``` + +The auto configuration **with declarative config** uses the Declarative Config Bridge to be able to +use common configuration method: + +Let's first look at the yaml file that is used to configure the inferred spans processor: + +```yaml +file_format: 1.0-rc.1 +tracer_provider: + processors: + - inferred_spans: + backup: + diagnostic: + files: true +``` + +And now the component provider that uses the Declarative Config Bridge: + +```java + +@AutoService(ComponentProvider.class) +public class InferredSpansComponentProvider implements ComponentProvider { + + @Override + public String getName() { + return "inferred_spans"; + } + + @Override + public SpanProcessor create(DeclarativeConfigProperties config) { + return InferredSpansConfig.create( + new DeclarativeConfigPropertiesBridgeBuilder() + // crop the prefix, because the properties are under the "inferred_spans" processor + .addMapping("otel.inferred.spans.", "") + .build(config)); + } + + @Override + public Class getType() { + return SpanProcessor.class; + } +} +``` diff --git a/declarative-config-bridge/build.gradle.kts b/declarative-config-bridge/build.gradle.kts new file mode 100644 index 000000000000..b5ac9ec9c1c3 --- /dev/null +++ b/declarative-config-bridge/build.gradle.kts @@ -0,0 +1,19 @@ +plugins { + id("otel.java-conventions") + id("otel.publish-conventions") + id("otel.nullaway-conventions") +} + +group = "io.opentelemetry.instrumentation" + +dependencies { + compileOnly("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure") + implementation("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure-spi") + implementation("io.opentelemetry:opentelemetry-api-incubator") + + testImplementation("io.opentelemetry:opentelemetry-sdk-extension-incubator") + testImplementation("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure") + + compileOnly("com.google.code.findbugs:annotations") + testCompileOnly("com.google.code.findbugs:annotations") +} diff --git a/declarative-config-bridge/src/main/java/io/opentelemetry/instrumentation/config/bridge/ConfigPropertiesUtil.java b/declarative-config-bridge/src/main/java/io/opentelemetry/instrumentation/config/bridge/ConfigPropertiesUtil.java new file mode 100644 index 000000000000..cc7fb351ea3c --- /dev/null +++ b/declarative-config-bridge/src/main/java/io/opentelemetry/instrumentation/config/bridge/ConfigPropertiesUtil.java @@ -0,0 +1,23 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.config.bridge; + +public final class ConfigPropertiesUtil { + private ConfigPropertiesUtil() {} + + public static String propertyYamlPath(String propertyName) { + return yamlPath(propertyName); + } + + static String yamlPath(String property) { + String[] segments = DeclarativeConfigPropertiesBridge.getSegments(property); + if (segments.length == 0) { + throw new IllegalArgumentException("Invalid property: " + property); + } + + return "'instrumentation/development' / 'java' / '" + String.join("' / '", segments) + "'"; + } +} diff --git a/javaagent-extension-api/src/main/java/io/opentelemetry/javaagent/extension/internal/DeclarativeConfigPropertiesBridge.java b/declarative-config-bridge/src/main/java/io/opentelemetry/instrumentation/config/bridge/DeclarativeConfigPropertiesBridge.java similarity index 98% rename from javaagent-extension-api/src/main/java/io/opentelemetry/javaagent/extension/internal/DeclarativeConfigPropertiesBridge.java rename to declarative-config-bridge/src/main/java/io/opentelemetry/instrumentation/config/bridge/DeclarativeConfigPropertiesBridge.java index e2e782443361..928a17f5bd0e 100644 --- a/javaagent-extension-api/src/main/java/io/opentelemetry/javaagent/extension/internal/DeclarativeConfigPropertiesBridge.java +++ b/declarative-config-bridge/src/main/java/io/opentelemetry/instrumentation/config/bridge/DeclarativeConfigPropertiesBridge.java @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.javaagent.extension.internal; +package io.opentelemetry.instrumentation.config.bridge; import static io.opentelemetry.api.incubator.config.DeclarativeConfigProperties.empty; @@ -169,7 +169,7 @@ private T getPropertyValue( return extractor.apply(target, lastPart); } - private static String[] getSegments(String property) { + static String[] getSegments(String property) { if (property.startsWith(OTEL_INSTRUMENTATION_PREFIX)) { property = property.substring(OTEL_INSTRUMENTATION_PREFIX.length()); } diff --git a/javaagent-extension-api/src/main/java/io/opentelemetry/javaagent/extension/internal/DeclarativeConfigPropertiesBridgeBuilder.java b/declarative-config-bridge/src/main/java/io/opentelemetry/instrumentation/config/bridge/DeclarativeConfigPropertiesBridgeBuilder.java similarity index 87% rename from javaagent-extension-api/src/main/java/io/opentelemetry/javaagent/extension/internal/DeclarativeConfigPropertiesBridgeBuilder.java rename to declarative-config-bridge/src/main/java/io/opentelemetry/instrumentation/config/bridge/DeclarativeConfigPropertiesBridgeBuilder.java index 05da62862dec..3c70dc8b6100 100644 --- a/javaagent-extension-api/src/main/java/io/opentelemetry/javaagent/extension/internal/DeclarativeConfigPropertiesBridgeBuilder.java +++ b/declarative-config-bridge/src/main/java/io/opentelemetry/instrumentation/config/bridge/DeclarativeConfigPropertiesBridgeBuilder.java @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.javaagent.extension.internal; +package io.opentelemetry.instrumentation.config.bridge; import static io.opentelemetry.api.incubator.config.DeclarativeConfigProperties.empty; @@ -21,9 +21,6 @@ /** * A builder for {@link DeclarativeConfigPropertiesBridge} that allows adding translations and fixed * values for properties. - * - *

This class is internal and is hence not for public use. Its APIs are unstable and can change - * at any time. */ public class DeclarativeConfigPropertiesBridgeBuilder { /** @@ -82,6 +79,17 @@ public ConfigProperties build(AutoConfiguredOpenTelemetrySdk autoConfiguredOpenT "AutoConfiguredOpenTelemetrySdk does not have ConfigProperties or DeclarativeConfigProperties. This is likely a programming error in opentelemetry-java"); } + /** + * Build {@link ConfigProperties} from the provided {@link DeclarativeConfigProperties} node. + * + * @param node the declarative config properties to build from + * @return a new instance of {@link ConfigProperties} + */ + public ConfigProperties build(@Nullable DeclarativeConfigProperties node) { + return new DeclarativeConfigPropertiesBridge( + node == null ? empty() : node, mappings, overrideValues); + } + /** * Build {@link ConfigProperties} from the {@link DeclarativeConfigProperties} provided by the * instrumentation configuration. @@ -94,12 +102,7 @@ public ConfigProperties build(AutoConfiguredOpenTelemetrySdk autoConfiguredOpenT */ public ConfigProperties buildFromInstrumentationConfig( @Nullable DeclarativeConfigProperties instrumentationConfig) { - // leave the name "build" for a future method that builds from a DeclarativeConfigProperties - // instance that doesn't come from the top-level instrumentation config - if (instrumentationConfig == null) { - instrumentationConfig = DeclarativeConfigProperties.empty(); - } - return new DeclarativeConfigPropertiesBridge( - instrumentationConfig.getStructured("java", empty()), mappings, overrideValues); + return build( + instrumentationConfig == null ? null : instrumentationConfig.getStructured("java")); } } diff --git a/declarative-config-bridge/src/test/java/io/opentelemetry/instrumentation/config/bridge/ConfigPropertiesUtilTest.java b/declarative-config-bridge/src/test/java/io/opentelemetry/instrumentation/config/bridge/ConfigPropertiesUtilTest.java new file mode 100644 index 000000000000..db62912fd572 --- /dev/null +++ b/declarative-config-bridge/src/test/java/io/opentelemetry/instrumentation/config/bridge/ConfigPropertiesUtilTest.java @@ -0,0 +1,19 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.config.bridge; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.Test; + +class ConfigPropertiesUtilTest { + @Test + void propertyYamlPath() { + assertThat(ConfigPropertiesUtil.propertyYamlPath("google.otel.auth.target.signals")) + .isEqualTo( + "'instrumentation/development' / 'java' / 'google' / 'otel' / 'auth' / 'target' / 'signals'"); + } +} diff --git a/javaagent-extension-api/src/test/java/io/opentelemetry/javaagent/extension/internal/DeclarativeConfigPropertiesBridgeBuilderTest.java b/declarative-config-bridge/src/test/java/io/opentelemetry/instrumentation/config/bridge/DeclarativeConfigPropertiesBridgeBuilderTest.java similarity index 94% rename from javaagent-extension-api/src/test/java/io/opentelemetry/javaagent/extension/internal/DeclarativeConfigPropertiesBridgeBuilderTest.java rename to declarative-config-bridge/src/test/java/io/opentelemetry/instrumentation/config/bridge/DeclarativeConfigPropertiesBridgeBuilderTest.java index dd41958eebcb..d3058e0b888f 100644 --- a/javaagent-extension-api/src/test/java/io/opentelemetry/javaagent/extension/internal/DeclarativeConfigPropertiesBridgeBuilderTest.java +++ b/declarative-config-bridge/src/test/java/io/opentelemetry/instrumentation/config/bridge/DeclarativeConfigPropertiesBridgeBuilderTest.java @@ -3,10 +3,9 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.javaagent.extension.internal; +package io.opentelemetry.instrumentation.config.bridge; import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -47,7 +46,7 @@ void shouldUseConfigProviderForDeclarativeConfiguration() { when(javaNodeMock.getString(propertyName)).thenReturn(expectedValue); DeclarativeConfigProperties instrumentationConfigMock = mock(DeclarativeConfigProperties.class); - when(instrumentationConfigMock.getStructured(eq("java"), any())).thenReturn(javaNodeMock); + when(instrumentationConfigMock.getStructured(eq("java"))).thenReturn(javaNodeMock); ConfigProvider configProviderMock = mock(ConfigProvider.class); when(configProviderMock.getInstrumentationConfig()).thenReturn(instrumentationConfigMock); diff --git a/javaagent-extension-api/src/test/java/io/opentelemetry/javaagent/extension/internal/DeclarativeConfigPropertiesBridgeTest.java b/declarative-config-bridge/src/test/java/io/opentelemetry/instrumentation/config/bridge/DeclarativeConfigPropertiesBridgeTest.java similarity index 99% rename from javaagent-extension-api/src/test/java/io/opentelemetry/javaagent/extension/internal/DeclarativeConfigPropertiesBridgeTest.java rename to declarative-config-bridge/src/test/java/io/opentelemetry/instrumentation/config/bridge/DeclarativeConfigPropertiesBridgeTest.java index deeb424b8abe..29f9d424cd9d 100644 --- a/javaagent-extension-api/src/test/java/io/opentelemetry/javaagent/extension/internal/DeclarativeConfigPropertiesBridgeTest.java +++ b/declarative-config-bridge/src/test/java/io/opentelemetry/instrumentation/config/bridge/DeclarativeConfigPropertiesBridgeTest.java @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.javaagent.extension.internal; +package io.opentelemetry.instrumentation.config.bridge; import static org.assertj.core.api.Assertions.assertThat; diff --git a/javaagent-extension-api/src/test/resources/config.yaml b/declarative-config-bridge/src/test/resources/config.yaml similarity index 95% rename from javaagent-extension-api/src/test/resources/config.yaml rename to declarative-config-bridge/src/test/resources/config.yaml index e0f2020bda37..5f8bbd1943b0 100644 --- a/javaagent-extension-api/src/test/resources/config.yaml +++ b/declarative-config-bridge/src/test/resources/config.yaml @@ -1,4 +1,4 @@ -file_format: 0.4 +file_format: 1.0-rc.1 instrumentation/development: java: acme: diff --git a/dependencyManagement/build.gradle.kts b/dependencyManagement/build.gradle.kts index 8a41dae8fa65..316f824b305c 100644 --- a/dependencyManagement/build.gradle.kts +++ b/dependencyManagement/build.gradle.kts @@ -5,8 +5,8 @@ plugins { data class DependencySet(val group: String, val version: String, val modules: List) // this line is managed by .github/scripts/update-sdk-version.sh -val otelSdkVersion = "1.53.0" -val otelContribVersion = "1.48.0-alpha" +val otelSdkVersion = "1.54.0" +val otelContribVersion = "1.49.0-alpha" val otelSdkAlphaVersion = otelSdkVersion.replaceFirst("(-SNAPSHOT)?$".toRegex(), "-alpha$1") // Need both BOM and groovy jars @@ -27,7 +27,7 @@ val DEPENDENCY_BOMS = listOf( // for some reason boms show up as runtime dependencies in license and vulnerability scans // even if they are only used by test dependencies, so not using junit bom since it is LGPL - "com.fasterxml.jackson:jackson-bom:2.19.2", + "com.fasterxml.jackson:jackson-bom:2.20.0", "com.google.guava:guava-bom:33.4.8-jre", "org.apache.groovy:groovy-bom:${groovyVersion}", "io.opentelemetry:opentelemetry-bom:${otelSdkVersion}", @@ -38,12 +38,12 @@ val DEPENDENCY_BOMS = listOf( val autoServiceVersion = "1.1.1" val autoValueVersion = "1.11.0" val errorProneVersion = "2.41.0" -val byteBuddyVersion = "1.17.6" +val byteBuddyVersion = "1.17.7" val asmVersion = "9.8" val jmhVersion = "1.37" val mockitoVersion = "4.11.0" val slf4jVersion = "2.0.17" -val semConvVersion = "1.34.0" +val semConvVersion = "1.37.0" val semConvAlphaVersion = semConvVersion.replaceFirst("(-rc.*)?$".toRegex(), "-alpha$1") val CORE_DEPENDENCIES = listOf( @@ -89,7 +89,7 @@ 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.12.8", + "com.uber.nullaway:nullaway:0.12.9", "commons-beanutils:commons-beanutils:1.11.0", "commons-cli:commons-cli:1.10.0", "commons-codec:commons-codec:1.19.0", @@ -107,7 +107,7 @@ val DEPENDENCIES = listOf( "io.opentelemetry.contrib:opentelemetry-gcp-resources:${otelContribVersion}", "io.opentelemetry.contrib:opentelemetry-cloudfoundry-resources:${otelContribVersion}", "io.opentelemetry.contrib:opentelemetry-baggage-processor:${otelContribVersion}", - "io.opentelemetry.proto:opentelemetry-proto:1.7.0-alpha", + "io.opentelemetry.proto:opentelemetry-proto:1.8.0-alpha", "io.opentelemetry:opentelemetry-extension-annotations:1.18.0", // deprecated, no longer part of bom "org.assertj:assertj-core:3.27.4", "org.awaitility:awaitility:4.3.0", diff --git a/docs/apidiffs/2.19.0_vs_2.18.0/opentelemetry-instrumentation-annotations.txt b/docs/apidiffs/2.19.0_vs_2.18.0/opentelemetry-instrumentation-annotations.txt new file mode 100644 index 000000000000..abff67fa5b32 --- /dev/null +++ b/docs/apidiffs/2.19.0_vs_2.18.0/opentelemetry-instrumentation-annotations.txt @@ -0,0 +1,2 @@ +Comparing source compatibility of opentelemetry-instrumentation-annotations-2.19.0.jar against opentelemetry-instrumentation-annotations-2.18.0.jar +No changes. \ No newline at end of file diff --git a/docs/apidiffs/2.19.0_vs_2.18.0/opentelemetry-instrumentation-api.txt b/docs/apidiffs/2.19.0_vs_2.18.0/opentelemetry-instrumentation-api.txt new file mode 100644 index 000000000000..2eeedee5ab24 --- /dev/null +++ b/docs/apidiffs/2.19.0_vs_2.18.0/opentelemetry-instrumentation-api.txt @@ -0,0 +1,2 @@ +Comparing source compatibility of opentelemetry-instrumentation-api-2.19.0.jar against opentelemetry-instrumentation-api-2.18.0.jar +No changes. \ No newline at end of file diff --git a/docs/apidiffs/2.19.0_vs_2.18.0/opentelemetry-spring-boot-autoconfigure.txt b/docs/apidiffs/2.19.0_vs_2.18.0/opentelemetry-spring-boot-autoconfigure.txt new file mode 100644 index 000000000000..7fb820dd8945 --- /dev/null +++ b/docs/apidiffs/2.19.0_vs_2.18.0/opentelemetry-spring-boot-autoconfigure.txt @@ -0,0 +1,2 @@ +Comparing source compatibility of opentelemetry-spring-boot-autoconfigure-2.19.0.jar against opentelemetry-spring-boot-autoconfigure-2.18.0.jar +No changes. \ No newline at end of file diff --git a/docs/apidiffs/2.19.0_vs_2.18.0/opentelemetry-spring-boot-starter.txt b/docs/apidiffs/2.19.0_vs_2.18.0/opentelemetry-spring-boot-starter.txt new file mode 100644 index 000000000000..f244273b1cab --- /dev/null +++ b/docs/apidiffs/2.19.0_vs_2.18.0/opentelemetry-spring-boot-starter.txt @@ -0,0 +1,2 @@ +Comparing source compatibility of opentelemetry-spring-boot-starter-2.19.0.jar against opentelemetry-spring-boot-starter-2.18.0.jar +No changes. \ No newline at end of file diff --git a/docs/apidiffs/current_vs_latest/opentelemetry-instrumentation-annotations.txt b/docs/apidiffs/current_vs_latest/opentelemetry-instrumentation-annotations.txt index e1d0a8a76701..40665bc6db73 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.19.0-SNAPSHOT.jar against opentelemetry-instrumentation-annotations-2.18.1.jar +Comparing source compatibility of opentelemetry-instrumentation-annotations-2.20.0.jar against opentelemetry-instrumentation-annotations-2.19.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 7f6c2469d18e..eb51d0dbf537 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.19.0-SNAPSHOT.jar against opentelemetry-instrumentation-api-2.18.1.jar +Comparing source compatibility of opentelemetry-instrumentation-api-2.20.0.jar against opentelemetry-instrumentation-api-2.19.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 1bd91e0f9c39..b0f1eba2718f 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.19.0-SNAPSHOT.jar against opentelemetry-spring-boot-autoconfigure-2.18.1.jar +Comparing source compatibility of opentelemetry-spring-boot-autoconfigure-2.20.0.jar against opentelemetry-spring-boot-autoconfigure-2.19.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 85aa35209a8a..7945cc3cf0d1 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.19.0-SNAPSHOT.jar against opentelemetry-spring-boot-starter-2.18.1.jar +Comparing source compatibility of opentelemetry-spring-boot-starter-2.20.0.jar against opentelemetry-spring-boot-starter-2.19.0.jar No changes. \ No newline at end of file diff --git a/docs/contributing/documenting-instrumentation.md b/docs/contributing/documenting-instrumentation.md new file mode 100644 index 000000000000..b64a6bc2e7ba --- /dev/null +++ b/docs/contributing/documenting-instrumentation.md @@ -0,0 +1,198 @@ +# Documenting Instrumentation + +Due to the large number of instrumentations supported in this project, it is important to maintain a +consistent documentation approach. We use structured metadata files and README.md files along with +tooling and automation to both generate and audit documentation. + +This document outlines the documentation aspirations for this project. Not all instrumentations +will meet all of these guidelines or already be documented in this way. + +## README.md Files for Library Instrumentations + +Every library instrumentation module must have a README.md file in the library directory root +(`instrumentation/{some instrumentation}/library/README.md`) that follows this pattern: + +```markdown +# Library Instrumentation for [Technology] version [X.Y] and higher + +Provides OpenTelemetry instrumentation for [Technology link]. + +[Any other relevant context about the instrumentation] + +## Quickstart + +### Add these dependencies to your project + +Replace `OPENTELEMETRY_VERSION` with the [latest release](maven-central-link). + +For Maven, add to your `pom.xml` dependencies: + + ```xml + + + io.opentelemetry.instrumentation + {library artifact name} + OPENTELEMETRY_VERSION + + + ``` + +For Gradle, add to your dependencies: + + ```kotlin + implementation("io.opentelemetry.instrumentation:{library artifact name}:OPENTELEMETRY_VERSION") + ``` + +### Usage + +[Code examples showing integration] +``` + +Following these sections, you can include any other relevant information. + +**If there is a difference in functionality between the library and javaagent instrumentation, it is +important to document these differences.** + +## README.md Files for Javaagent Instrumentations + +Every javaagent instrumentation module should have a README.md file in the javaagent directory root +(`instrumentation/{some instrumentation}/javaagent/README.md`) that follows this pattern: + +```markdown +# [Technology] Instrumentation + +[Brief description of what the instrumentation does and what versions it applies to] + +## Settings +| System property | Type | Default | Description | +|-----------------|------|---------|-------------| +| `property.name` | Type | Default | Description | +``` + + +**Note:** At some point we will likely automate the generation of this javaagent README.md file +using the metadata.yaml file described below. + + +## Metadata.yaml Files + +Each instrumentation should have a `metadata.yaml` file in the root of the instrumentation directory +(`instrumentation/{some instrumentation}/metadata.yaml`) that contains structured metadata about the +instrumentation. + +Example: + +```yaml +description: "This instrumentation enables..." +disabled_by_default: true +classification: library +library_link: https://github.com/... +configurations: + - name: otel.instrumentation.common.db-statement-sanitizer.enabled + description: Enables statement sanitization for database queries. + type: boolean + default: true +``` + +### Description (required) + +At a minimum, every instrumentation metadata file should include a `description`. + +Some example descriptions: + +* This instrumentation enables HTTP server spans and HTTP server metrics for the ActiveJ HTTP server. +* This instrumentation provides context propagation for Akka actors, it does not emit any telemetry + on its own. +* The Alibaba Druid instrumentation generates database connection pool metrics for druid data sources. +* The Apache Dubbo instrumentation provides RPC client spans and RPC server spans for Apache Dubbo + RPC calls. Each call produces a span named after the Dubbo method, enriched with standard RPC + attributes (system, service, method), network attributes, and error details if an exception + occurs. + +Some notes when writing descriptions: + +* You don't always need to explicitly name the instrumentation, and you can start with "This + instrumentation..." +* Prefer the convention of using the word "enables" when describing what the instrumentation does, + "This instrumentation **enables** HTTP server spans and HTTP server metrics for the ActiveJ" instead + of something like "This instrumentation **provides** HTTP server spans and HTTP server metrics for the ActiveJ". +* Explicitly state whether the instrumentation generates new telemetry (spans, metrics, logs). + * If it doesn't generate new telemetry, clearly explain what it's purpose is, for example whether it + augments or enriches existing telemetry produced by other instrumentations (e.g., by adding + attributes or ensuring context propagation). +* When describing the functionality of the instrumentation and the telemetry, specify using + [semantic convention categories](https://opentelemetry.io/docs/specs/semconv/) when possible + (e.g., "database client spans", "RPC server metrics", "consumer messaging spans"). +* Do not include specific method names, class names, or other low-level implementation details in + the description unless they are essential to understanding the purpose of the instrumentation. +* It is not usually necessary to include specific library or framework version numbers in the + description, unless that context is significant in some way. + +### Library Link + +For library instrumentations, include a `library_link` field with a URL to the library or framework's +main website or documentation, or if those don't exist, the GitHub repository. + +### Configurations + +If an instrumentation module has configuration options, they should be documented in the +`configurations` section of the `metadata.yaml` file. + +Each configuration should include: + +* `name`: The full configuration property name, for example `otel.instrumentation.common.db-statement-sanitizer.enabled`. +* `description`: A brief description of what the configuration does. +* `type`: The data type of the configuration value. Supported types are: `boolean`, `string`, `list`, and `map`. +* `default`: The default value for the configuration. + + +If a configuration enables experimental attributes, list them, for example: + +> Enables experimental span attributes `couchbase.operation_id` and `couchbase.local.address`. + + +### Classification (optional) + +If an instrumentation module does not specify a `classification`, it is assumed to be `library`. + +There are currently three supported classifications: + +* `library`: An instrumentation that provides automatic instrumentation for a specific library or + framework. This is the default classification. +* `internal`: An instrumentation that is used internally by other instrumentations or the OpenTelemetry + SDK itself, and is not intended for direct use by end users. +* `custom`: An instrumentation that is intended for custom or user-defined use cases, and may not + fit into the standard library or internal categories. + +The primary way this `classification` is used is to group and filter instrumentations by their +utility in tooling and documentation. If you are unsure which classification to use, you can omit +this field, and it will default to `library`. + + +### Disabled by Default (optional) + +If an instrumentation is disabled by default, set `disabled_by_default: true`. This indicates that +the instrumentation will not be active unless explicitly enabled by the user. If this field is omitted, +it defaults to `false`, meaning the instrumentation is enabled by default. + +## Instrumentation List (docs/instrumentation-list.md) + +The contents of the `metadata.yaml` files are combined with other information about the instrumentation +to generate a complete catalog of instrumentations in `docs/instrumentation-list.md`. This file +is generated via a gradle task and should not be edited directly (see +[this readme](../../instrumentation-docs/readme.md) for more information on this process). + +**If you are submitting new instrumentation or updating existing instrumentation, you do not need to +update this file unless you want to, as it can take a significant amount of time to run** (40+ +minutes). Each night a [GitHub Action](../../.github/workflows/metadata-update.yml) runs to +regenerate this file based on the current state of the repository, so all changes will be reflected +within 24 hours. + +## opentelemetry.io + +All of our instrumentation modules are listed on the opentelemetry.io website in the context of how +to [suppress specific instrumentation](https://opentelemetry.io/docs/zero-code/java/agent/disable/#suppressing-specific-agent-instrumentation). + +All new instrumentations should be added to this list. There is a +[Github action](../../.github/workflows/documentation-disable-list-audit.yml) that runs nightly to check +for any missing instrumentations, and will open an issue if any are found. diff --git a/docs/contributing/style-guideline.md b/docs/contributing/style-guideline.md index d8249cb657aa..cd4cb7459fb1 100644 --- a/docs/contributing/style-guideline.md +++ b/docs/contributing/style-guideline.md @@ -6,8 +6,8 @@ We follow the [Google Java Style Guide](https://google.github.io/styleguide/java The build will fail if the source code is not formatted according to the google java style. -The main goal is to avoid extensive reformatting caused by different IDEs having different opinion -about how things should be formatted by establishing. +The main goal is to avoid extensive reformatting caused by different IDEs having different opinions +about how things should be formatted by establishing a consistent standard. Running @@ -23,7 +23,7 @@ Running ./gradlew spotlessCheck ``` -runs formatting verify task only. +runs the formatting verification task only. ### Pre-commit hook @@ -37,7 +37,7 @@ git config core.hooksPath .githooks ### Editorconfig -As additional convenience for IntelliJ users, we provide `.editorconfig` +As additional convenience for IntelliJ users, we provide an `.editorconfig` file. IntelliJ will automatically use it to adjust its code formatting settings. It does not support all required rules, so you still have to run `spotlessApply` from time to time. @@ -55,8 +55,8 @@ To run these checks locally: ## Static imports -We leverage static imports for many common types of operations. However, not all static methods or -constants are necessarily good candidates for a static import. The following list is a very +We use static imports for many common types of operations. However, not all static methods or +constants are necessarily good candidates for a static import. The following list is a rough guideline of what are commonly accepted static imports: - Test assertions (JUnit and AssertJ) @@ -70,8 +70,8 @@ rough guideline of what are commonly accepted static imports: Some of these are enforced by checkstyle rules: -- look for `RegexpSinglelineJava` in `checkstyle.xml` -- use `@SuppressWarnings("checkstyle:RegexpSinglelineJava")` to suppress the checkstyle warning +- Look for `RegexpSinglelineJava` in `checkstyle.xml` +- Use `@SuppressWarnings("checkstyle:RegexpSinglelineJava")` to suppress the checkstyle warning ## Ordering of class contents @@ -84,7 +84,7 @@ The following order is preferred: - Nested classes If methods call each other, it's nice if the calling method is ordered (somewhere) above -the method that it calls. So, for one example, a private method would be ordered (somewhere) below +the method that it calls. For example, a private method would be ordered (somewhere) below the non-private methods that use it. In static utility classes (where all members are static), the private constructor @@ -102,7 +102,7 @@ Method parameters and local variables should never be declared `final`. ## `@Nullable` annotation usage -[Note: this section is aspirational, as opposed to a reflection of the current codebase] +**Note: this section is aspirational, as opposed to a reflection of the current codebase** All parameters and fields which can be `null` should be annotated with `@Nullable` (specifically `javax.annotation.Nullable`, which is included by the @@ -125,7 +125,7 @@ plugins { ## java.util.Optional usage Following the reasoning from [Writing a Java library with better experience (slide 12)](https://speakerdeck.com/trustin/writing-a-java-library-with-better-experience?slide=12), -usage of `java.util.Optional` is kept at a minimum in this project. +usage of `java.util.Optional` is kept to a minimum in this project. It is ok to use `Optional` in places where it does not leak into public API signatures. @@ -137,9 +137,9 @@ itself uses it. Avoid allocations whenever possible on the hot path (instrumentation code). This includes `Iterator` allocations from collections; note that `for(SomeType t : plainJavaArray)` does not allocate an iterator object. -Non-allocating stream api usage on the hot path is acceptable but may not -fit the surrounding code style; this is a judgement call. Note that -some stream apis make it much more difficult to allocate efficiently +Non-allocating stream API usage on the hot path is acceptable but may not +fit the surrounding code style; this is a judgment call. Note that +some stream APIs make it much more difficult to allocate efficiently (e.g., `collect` with presized sink data structures involves convoluted `Supplier` code, or lambdas passed to `forEach` might be capturing/allocating lambdas). diff --git a/docs/contributing/writing-instrumentation.md b/docs/contributing/writing-instrumentation.md index 152de64794b9..336704118773 100644 --- a/docs/contributing/writing-instrumentation.md +++ b/docs/contributing/writing-instrumentation.md @@ -69,15 +69,6 @@ include(":instrumentation:yarpc-1.0:library") include(":instrumentation:yarpc-1.0:testing") ``` -### Instrumentation metadata.yaml (Experimental) - -Each module can contain a `metadata.yaml` file that describes the instrumentation. This information -is then used when generating the [instrumentation-list.yaml](../instrumentation-list.yaml) file. -The schema for `metadata.yaml` is still in development and may change in the future. See the -[instrumentation-docs readme](../../instrumentation-docs/readme.md) for more information and the -latest schema. - - ### Instrumentation Submodules When writing instrumentation that requires submodules for different versions, the name of each @@ -344,6 +335,11 @@ code, see [this section](#writing-java-agent-unit-tests). ## Additional considerations regarding instrumentations +### Documentation + +All new instrumentation modules should include relevant documentation. See our docs on +[Documenting Instrumentation](documenting-instrumentation.md) for more details. + ### Instrumenting code that is not available as a Maven dependency If an instrumented server or library jar isn't available in any public Maven repository you can diff --git a/docs/instrumentation-list.yaml b/docs/instrumentation-list.yaml index c3726961b362..860f402f27cc 100644 --- a/docs/instrumentation-list.yaml +++ b/docs/instrumentation-list.yaml @@ -5,8 +5,10 @@ libraries: activej: - name: activej-http-6.0 - description: This instrumentation enables SERVER spans and metrics for the ActiveJ - HTTP server. + display_name: ActiveJ + description: This instrumentation enables HTTP server spans and HTTP server metrics + for the ActiveJ HTTP server. + library_link: https://activej.io/ source_path: instrumentation/activej-http-6.0 minimum_java_version: 17 scope: @@ -59,8 +61,10 @@ libraries: type: STRING akka: - name: akka-actor-2.3 + display_name: Akka Actors description: This instrumentation provides context propagation for Akka actors, it does not emit any telemetry on its own. + library_link: https://doc.akka.io/libraries/akka-core/current/typed/index.html source_path: instrumentation/akka/akka-actor-2.3 scope: name: io.opentelemetry.akka-actor-2.3 @@ -70,8 +74,10 @@ libraries: - com.typesafe.akka:akka-actor_2.12:[2.3,) - com.typesafe.akka:akka-actor_2.13:[2.3,) - name: akka-actor-fork-join-2.5 + display_name: Akka Actors description: This instrumentation provides context propagation for the Akka Fork-Join Pool, it does not emit any telemetry on its own. + library_link: https://doc.akka.io/libraries/akka-core/current/typed/index.html source_path: instrumentation/akka/akka-actor-fork-join-2.5 scope: name: io.opentelemetry.akka-actor-fork-join-2.5 @@ -81,8 +87,10 @@ libraries: - com.typesafe.akka:akka-actor_2.13:[2.5.23,2.6) - com.typesafe.akka:akka-actor_2.11:[2.5,) - name: akka-http-10.0 - description: This instrumentation enables CLIENT and SERVER spans and metrics - for the Akka HTTP client and server. + display_name: Akka HTTP + description: | + This instrumentation enables HTTP client spans and metrics for the Akka HTTP client, and HTTP server spans and metrics for the Akka HTTP server. + library_link: https://doc.akka.io/docs/akka-http/current/index.html source_path: instrumentation/akka/akka-http-10.0 scope: name: io.opentelemetry.akka-http-10.0 @@ -171,6 +179,7 @@ libraries: - name: alibaba-druid-1.0 description: | The Alibaba Druid instrumentation generates database connection pool metrics for druid data sources. + library_link: https://github.com/alibaba/druid source_path: instrumentation/alibaba-druid-1.0 scope: name: io.opentelemetry.alibaba-druid-1.0 @@ -266,6 +275,7 @@ libraries: description: | This instrumentation enables database connection pools metrics for Apache DBCP. The instrumentation uses `MBeanRegistration` methods for lifecycle detection, therefore it only activates if the `BasicDataSource` is registered to an `MBeanServer`. If using Spring Boot, this happens automatically as all Spring beans that support JMX registration are automatically registered by default. + library_link: https://commons.apache.org/proper/commons-dbcp/ source_path: instrumentation/apache-dbcp-2.0 scope: name: io.opentelemetry.apache-dbcp-2.0 @@ -342,10 +352,11 @@ libraries: - name: db.client.connection.pool.name type: STRING - name: apache-dubbo-2.7 - description: The Apache Dubbo instrumentation provides client and server spans - for Apache Dubbo RPC calls. Each call produces a span named after the Dubbo - method, enriched with standard RPC attributes (system, service, method), network - attributes, and error details if an exception occurs. + description: The Apache Dubbo instrumentation provides RPC client spans and RPC + server spans for Apache Dubbo RPC calls. Each call produces a span named after + the Dubbo method, enriched with standard RPC attributes (system, service, method), + network attributes, and error details if an exception occurs. + library_link: https://github.com/apache/dubbo/ source_path: instrumentation/apache-dubbo-2.7 scope: name: io.opentelemetry.apache-dubbo-2.7 @@ -386,8 +397,9 @@ libraries: - name: rpc.system type: STRING - name: apache-httpasyncclient-4.1 - description: This instrumentation enables CLIENT spans and metrics for the Apache - HttpAsyncClient. + description: This instrumentation enables HTTP client spans and HTTP client metrics + for the Apache HttpAsyncClient. + library_link: https://hc.apache.org/index.html source_path: instrumentation/apache-httpasyncclient-4.1 scope: name: io.opentelemetry.apache-httpasyncclient-4.1 @@ -432,8 +444,9 @@ libraries: - name: url.full type: STRING - name: apache-httpclient-2.0 - description: This instrumentation enables CLIENT spans and metrics for versions - 2 and 3 of the Apache HttpClient. + description: This instrumentation enables HTTP client spans and HTTP client metrics + for versions 2 and 3 of the Apache HttpClient. + library_link: https://hc.apache.org/index.html source_path: instrumentation/apache-httpclient/apache-httpclient-2.0 scope: name: io.opentelemetry.apache-httpclient-2.0 @@ -476,8 +489,9 @@ libraries: - name: url.full type: STRING - name: apache-httpclient-4.0 - description: This instrumentation enables CLIENT spans and metrics for version - 4 of the Apache HttpClient. + description: This instrumentation enables HTTP client spans and HTTP client metrics + for version 4 of the Apache HttpClient. + library_link: https://hc.apache.org/index.html source_path: instrumentation/apache-httpclient/apache-httpclient-4.0 scope: name: io.opentelemetry.apache-httpclient-4.0 @@ -524,7 +538,8 @@ libraries: type: STRING - name: apache-httpclient-4.3 description: This instrumentation provides a library integration that enables - CLIENT spans and metrics for the Apache HttpClient. + HTTP client spans and HTTP client metrics for the Apache HttpClient. + library_link: https://hc.apache.org/index.html source_path: instrumentation/apache-httpclient/apache-httpclient-4.3 scope: name: io.opentelemetry.apache-httpclient-4.3 @@ -571,8 +586,9 @@ libraries: - name: url.full type: STRING - name: apache-httpclient-5.0 - description: This instrumentation enables CLIENT spans and metrics for version - 5 of the Apache HttpClient. + description: This instrumentation enables HTTP client spans and HTTP client metrics + for version 5 of the Apache HttpClient. + library_link: https://hc.apache.org/index.html source_path: instrumentation/apache-httpclient/apache-httpclient-5.0 scope: name: io.opentelemetry.apache-httpclient-5.0 @@ -618,7 +634,8 @@ libraries: type: STRING - name: apache-httpclient-5.2 description: This instrumentation provides a library integration that enables - CLIENT spans and metrics for the Apache HttpClient. + HTTP client spans and HTTP client metrics for the Apache HttpClient. + library_link: https://hc.apache.org/index.html source_path: instrumentation/apache-httpclient/apache-httpclient-5.2 scope: name: io.opentelemetry.apache-httpclient-5.2 @@ -666,7 +683,8 @@ libraries: type: STRING - name: apache-shenyu-2.4 description: | - This instrumentation does not emit telemetry on its own. Instead, it augments existing SERVER spans and HTTP server metrics with the HTTP route and Shenyu specific attributes. + This instrumentation does not emit telemetry on its own. Instead, it augments existing HTTP server spans and HTTP server metrics with the HTTP route and Shenyu specific attributes. + library_link: https://shenyu.apache.org/ source_path: instrumentation/apache-shenyu-2.4 scope: name: io.opentelemetry.apache-shenyu-2.4 @@ -681,8 +699,9 @@ libraries: default: false armeria: - name: armeria-1.3 - description: This instrumentation enables CLIENT and SERVER spans and metrics - for the Armeria HTTP client and server. + description: | + This instrumentation enables HTTP client spans and metrics for the Armeria HTTP client, and HTTP server spans and metrics for the Armeria HTTP server. + library_link: https://armeria.dev/ source_path: instrumentation/armeria/armeria-1.3 scope: name: io.opentelemetry.armeria-1.3 @@ -776,8 +795,9 @@ libraries: - name: user_agent.original type: STRING - name: armeria-grpc-1.14 - description: This instrumentation enables CLIENT and SERVER spans and metrics - for the Armeria gRPC client and server. + description: | + This instrumentation enables RPC client spans and metrics for the Armeria gRPC client, and RPC server spans and metrics for the Armeria gRPC server. + library_link: https://armeria.dev/ source_path: instrumentation/armeria/armeria-grpc-1.14 scope: name: io.opentelemetry.armeria-grpc-1.14 @@ -817,8 +837,9 @@ libraries: type: LONG async: - name: async-http-client-1.9 - description: This instrumentation enables CLIENT spans and metrics for version - 1 of the AsyncHttpClient (AHC) HTTP client. + description: This instrumentation enables HTTP client spans and HTTP client metrics + for version 1 of the AsyncHttpClient (AHC) HTTP client. + library_link: https://github.com/AsyncHttpClient/async-http-client source_path: instrumentation/async-http-client/async-http-client-1.9 scope: name: io.opentelemetry.async-http-client-1.9 @@ -859,8 +880,9 @@ libraries: - name: url.full type: STRING - name: async-http-client-2.0 - description: This instrumentation enables CLIENT spans and metrics for version - 2 of the AsyncHttpClient (AHC) HTTP client. + description: This instrumentation enables HTTP client spans and HTTP client metrics + for version 2 of the AsyncHttpClient (AHC) HTTP client. + library_link: https://github.com/AsyncHttpClient/async-http-client source_path: instrumentation/async-http-client/async-http-client-2.0 scope: name: io.opentelemetry.async-http-client-2.0 @@ -911,7 +933,8 @@ libraries: avaje: - name: avaje-jex-3.0 description: | - This instrumentation does not emit telemetry on its own. Instead, it hooks into the Avaje Jex Context to extract the HTTP route and attach it to existing SERVER spans and HTTP server metrics. + This instrumentation does not emit telemetry on its own. Instead, it hooks into the Avaje Jex Context to extract the HTTP route and attach it to existing HTTP server spans and HTTP server metrics. + library_link: https://avaje.io/jex/ source_path: instrumentation/avaje-jex-3.0 minimum_java_version: 21 scope: @@ -922,7 +945,9 @@ libraries: aws: - name: aws-lambda-core-1.0 description: | - Provides lightweight instrumentation of the Lambda core library, supporting all versions. It generates FaaS SERVER spans with the `faas.invocation_id` attribute. Use this package if you only use `RequestStreamHandler` or know you don't use any event classes from `aws-lambda-java-events`. This also includes when you are using `aws-serverless-java-container` to run e.g., a Spring Boot application on Lambda. + Provides lightweight instrumentation of the Lambda core library, supporting all versions. It generates FaaS server spans with the `faas.invocation_id` attribute. Use this package if you only use `RequestStreamHandler` or know you don't use any event classes from `aws-lambda-java-events`. This also includes when you are using `aws-serverless-java-container` to run e.g., a Spring Boot application on Lambda. + For custom wrappers when using library instrumentation, you can configure the `OTEL_INSTRUMENTATION_AWS_LAMBDA_HANDLER` environment variable to contain your lambda handler method (in the format `package.ClassName::methodName`) and use one of wrappers as your lambda `Handler`. + library_link: https://docs.aws.amazon.com/lambda/latest/dg/java-handler.html source_path: instrumentation/aws-lambda/aws-lambda-core-1.0 scope: name: io.opentelemetry.aws-lambda-core-1.0 @@ -945,7 +970,8 @@ libraries: type: STRING - name: aws-lambda-events-2.2 description: | - Provides full instrumentation of the Lambda library, including standard and custom event types, from `aws-lambda-java-events` 2.2+. + This version of the library instrumentation is deprecated, please use the `aws-lambda-events-3.11` library instrumentation instead. This instrumentation builds on top of the `aws-lambda-core-1.0` instrumentation, expanding support to cover the Lambda library, including standard and custom event types. + library_link: https://docs.aws.amazon.com/lambda/latest/dg/java-handler.html source_path: instrumentation/aws-lambda/aws-lambda-events-2.2 scope: name: io.opentelemetry.aws-lambda-events-2.2 @@ -956,6 +982,50 @@ libraries: - com.amazonaws:aws-lambda-java-events:2.2.1 - com.amazonaws:aws-lambda-java-core:1.0.0 configurations: + - name: otel.instrumentation.aws-lambda.flush-timeout + description: Flush timeout in milliseconds. + type: int + default: 10000 + - name: otel.instrumentation.http.known-methods + description: | + Configures the instrumentation to recognize an alternative set of HTTP request methods. All other methods will be treated as `_OTHER`. + type: list + default: CONNECT,DELETE,GET,HEAD,OPTIONS,PATCH,POST,PUT,TRACE + telemetry: + - when: default + spans: + - span_kind: CONSUMER + attributes: + - name: messaging.operation + type: STRING + - name: messaging.system + type: STRING + - span_kind: SERVER + attributes: + - name: faas.invocation_id + type: STRING + - name: faas.trigger + type: STRING + - name: http.request.method + type: STRING + - name: http.response.status_code + type: LONG + - name: url.full + type: STRING + - name: user_agent.original + type: STRING + - name: aws-lambda-events-3.11 + description: | + This instrumentation builds on top of the `aws-lambda-core-1.0` instrumentation, expanding support to cover the Lambda library, including standard and custom event types. + library_link: https://docs.aws.amazon.com/lambda/latest/dg/java-handler.html + source_path: instrumentation/aws-lambda/aws-lambda-events-3.11 + scope: + name: io.opentelemetry.aws-lambda-events-3.11 + target_versions: + library: + - com.amazonaws:aws-lambda-java-events:3.11.0 + - com.amazonaws:aws-lambda-java-core:1.0.0 + configurations: - name: otel.instrumentation.aws-lambda.flush-timeout description: Flush timeout in milliseconds. type: int @@ -965,12 +1035,20 @@ libraries: spans: - span_kind: CONSUMER attributes: + - name: messaging.destination.name + type: STRING + - name: messaging.message.id + type: STRING - name: messaging.operation type: STRING - name: messaging.system type: STRING - span_kind: SERVER attributes: + - name: cloud.account.id + type: STRING + - name: cloud.resource_id + type: STRING - name: faas.invocation_id type: STRING - name: faas.trigger @@ -986,6 +1064,7 @@ libraries: - name: aws-sdk-1.11 description: | This instrumentation covers the AWS SDK 1.11+ client library, enabling messaging and client spans and metrics for calls to AWS services including DynamoDB, EC2, Kinesis, Lambda, RDS, S3, secrets manager, SNS/SQS and step functions. + library_link: https://aws.amazon.com/sdk-for-java/ source_path: instrumentation/aws-sdk/aws-sdk-1.11 scope: name: io.opentelemetry.aws-sdk-1.11 @@ -1213,6 +1292,7 @@ libraries: - name: aws-sdk-2.2 description: | This instrumentation covers the AWS SDK 2.2+ client library, enabling messaging and client spans and metrics for calls to AWS services including DynamoDB, EC2, Kinesis, Lambda, RDS, S3, SNS/SQS and Bedrock. + library_link: https://aws.amazon.com/sdk-for-java/ source_path: instrumentation/aws-sdk/aws-sdk-2.2 scope: name: io.opentelemetry.aws-sdk-2.2 @@ -1269,9 +1349,9 @@ libraries: attributes: - name: gen_ai.operation.name type: STRING - - name: gen_ai.request.model + - name: gen_ai.provider.name type: STRING - - name: gen_ai.system + - name: gen_ai.request.model type: STRING - name: gen_ai.client.token.usage description: Measures number of input and output tokens used. @@ -1280,9 +1360,9 @@ libraries: attributes: - name: gen_ai.operation.name type: STRING - - name: gen_ai.request.model + - name: gen_ai.provider.name type: STRING - - name: gen_ai.system + - name: gen_ai.request.model type: STRING - name: gen_ai.token.type type: STRING @@ -1333,6 +1413,8 @@ libraries: type: STRING - name: gen_ai.operation.name type: STRING + - name: gen_ai.provider.name + type: STRING - name: gen_ai.request.max_tokens type: LONG - name: gen_ai.request.model @@ -1345,8 +1427,6 @@ libraries: type: DOUBLE - name: gen_ai.response.finish_reasons type: STRING_ARRAY - - name: gen_ai.system - type: STRING - name: gen_ai.usage.input_tokens type: LONG - name: gen_ai.usage.output_tokens @@ -1571,6 +1651,7 @@ libraries: - name: azure-core-1.14 description: This instrumentation enables context propagation for the Azure Core library, it does not emit any telemetry on its own. + library_link: https://learn.microsoft.com/en-us/java/api/overview/azure/core-readme?view=azure-java-stable source_path: instrumentation/azure-core/azure-core-1.14 scope: name: io.opentelemetry.azure-core-1.14 @@ -1580,6 +1661,7 @@ libraries: - name: azure-core-1.19 description: This instrumentation enables context propagation for the Azure Core library, it does not emit any telemetry on its own. + library_link: https://learn.microsoft.com/en-us/java/api/overview/azure/core-readme?view=azure-java-stable source_path: instrumentation/azure-core/azure-core-1.19 scope: name: io.opentelemetry.azure-core-1.19 @@ -1589,6 +1671,7 @@ libraries: - name: azure-core-1.36 description: This instrumentation enables context propagation for the Azure Core library, it does not emit any telemetry on its own. + library_link: https://learn.microsoft.com/en-us/java/api/overview/azure/core-readme?view=azure-java-stable source_path: instrumentation/azure-core/azure-core-1.36 scope: name: io.opentelemetry.azure-core-1.36 @@ -1599,6 +1682,7 @@ libraries: - name: c3p0-0.9 description: The c3p0 instrumentation provides connection pool metrics for c3p0 data sources. + library_link: https://github.com/swaldman/c3p0 source_path: instrumentation/c3p0-0.9 scope: name: io.opentelemetry.c3p0-0.9 @@ -1651,6 +1735,7 @@ libraries: - name: camel-2.20 description: | This instrumentation enables tracing for Apache Camel 2.x applications by generating spans for each route execution. For Camel versions 3.5 and newer, users should instead use the native 'camel-opentelemetry' component provided directly by the Camel project. + library_link: https://camel.apache.org/ source_path: instrumentation/camel-2.20 scope: name: io.opentelemetry.camel-2.20 @@ -1776,6 +1861,7 @@ libraries: - name: cassandra-3.0 description: | Instruments the Cassandra database client, providing database client spans and metrics for Cassandra queries. + library_link: https://github.com/apache/cassandra-java-driver source_path: instrumentation/cassandra/cassandra-3.0 scope: name: io.opentelemetry.cassandra-3.0 @@ -1839,6 +1925,7 @@ libraries: - name: cassandra-4.0 description: | Instruments the Cassandra database client, providing database client spans and metrics for Cassandra queries. + library_link: https://github.com/apache/cassandra-java-driver source_path: instrumentation/cassandra/cassandra-4.0 scope: name: io.opentelemetry.cassandra-4.0 @@ -1926,6 +2013,7 @@ libraries: - name: cassandra-4.4 description: | Instruments the Cassandra database client, providing database client spans and metrics for Cassandra queries. + library_link: https://github.com/apache/cassandra-java-driver source_path: instrumentation/cassandra/cassandra-4.4 scope: name: io.opentelemetry.cassandra-4.4 @@ -2013,12 +2101,13 @@ libraries: - name: server.port type: LONG clickhouse: - - name: clickhouse-client-0.5 - description: Instruments the V1 ClickHouseClient, providing database client spans - and metrics. - source_path: instrumentation/clickhouse-client-0.5 + - name: clickhouse-client-v1-0.5 + description: This instrumentation enables database client spans and metrics for + the V1 ClickHouse client. + library_link: https://github.com/ClickHouse/clickhouse-java + source_path: instrumentation/clickhouse/clickhouse-client-v1-0.5 scope: - name: io.opentelemetry.clickhouse-client-0.5 + name: io.opentelemetry.clickhouse-client-v1-0.5 target_versions: javaagent: - com.clickhouse.client:clickhouse-client:[0.5.0,) @@ -2080,21 +2169,124 @@ libraries: type: STRING - name: server.port type: LONG + - name: clickhouse-client-v2-0.8 + description: This instrumentation enables database client spans and metrics for + the V2 ClickHouse client. + library_link: https://github.com/ClickHouse/clickhouse-java + source_path: instrumentation/clickhouse/clickhouse-client-v2-0.8 + scope: + name: io.opentelemetry.clickhouse-client-v2-0.8 + target_versions: + javaagent: + - com.clickhouse:client-v2:[0.6.4,) + configurations: + - name: otel.instrumentation.common.db-statement-sanitizer.enabled + description: Enables statement sanitization for database queries. + type: boolean + default: true + telemetry: + - when: default + spans: + - span_kind: CLIENT + attributes: + - name: db.name + type: STRING + - name: db.operation + type: STRING + - name: db.statement + type: STRING + - name: db.system + type: STRING + - name: server.address + type: STRING + - name: server.port + type: LONG + - when: otel.semconv-stability.opt-in=database + metrics: + - name: db.client.operation.duration + description: Duration of database client operations. + type: HISTOGRAM + unit: s + attributes: + - name: db.namespace + type: STRING + - name: db.operation.name + type: STRING + - name: db.system.name + type: STRING + - name: server.address + type: STRING + - name: server.port + type: LONG + spans: + - span_kind: CLIENT + attributes: + - name: db.namespace + type: STRING + - name: db.operation.name + type: STRING + - name: db.query.text + type: STRING + - name: db.response.status_code + type: STRING + - name: db.system.name + type: STRING + - name: error.type + type: STRING + - name: server.address + type: STRING + - name: server.port + type: LONG couchbase: - name: couchbase-2.0 + description: | + This instrumentation enables database client spans and database client metrics for Couchbase 2.0 operations. It automatically traces key-value operations (get, upsert, replace, remove), view queries, N1QL queries, and cluster management operations. + library_link: https://github.com/couchbase/couchbase-java-client source_path: instrumentation/couchbase/couchbase-2.0 scope: name: io.opentelemetry.couchbase-2.0 target_versions: javaagent: - com.couchbase.client:java-client:[2,3) - configurations: - - name: otel.instrumentation.couchbase.experimental-span-attributes - description: Enables experimental span attributes `couchbase.operation_id` and - `couchbase.local.address` - type: boolean - default: false + telemetry: + - when: default + spans: + - span_kind: CLIENT + attributes: + - name: db.name + type: STRING + - name: db.operation + type: STRING + - name: db.statement + type: STRING + - name: db.system + type: STRING + - when: otel.semconv-stability.opt-in=database + metrics: + - name: db.client.operation.duration + description: Duration of database client operations. + type: HISTOGRAM + unit: s + attributes: + - name: db.operation.name + type: STRING + - name: db.system.name + type: STRING + spans: + - span_kind: CLIENT + attributes: + - name: db.namespace + type: STRING + - name: db.operation.name + type: STRING + - name: db.query.text + type: STRING + - name: db.system.name + type: STRING - name: couchbase-2.6 + description: | + This instrumentation enables database client spans and database client metrics for Couchbase 2.6 operations. It automatically traces key-value operations (get, upsert, replace, remove), view queries, N1QL queries, and cluster management operations. + library_link: https://github.com/couchbase/couchbase-java-client source_path: instrumentation/couchbase/couchbase-2.6 scope: name: io.opentelemetry.couchbase-2.6 @@ -2103,13 +2295,87 @@ libraries: - com.couchbase.client:java-client:[2.6.0,3) configurations: - name: otel.instrumentation.couchbase.experimental-span-attributes - description: Enables experimental span attributes couchbase.operation_id and - couchbase.local.address + description: | + Enables experimental span attributes `couchbase.operation_id` and `couchbase.local.address`. Different operation types receive different experimental attributes. type: boolean default: false + telemetry: + - when: default + spans: + - span_kind: CLIENT + attributes: + - name: db.name + type: STRING + - name: db.operation + type: STRING + - name: db.statement + type: STRING + - name: db.system + type: STRING + - name: network.peer.address + type: STRING + - name: network.peer.port + type: LONG + - name: network.type + type: STRING + - when: otel.instrumentation.couchbase.experimental-span-attributes=true + spans: + - span_kind: CLIENT + attributes: + - name: couchbase.local.address + type: STRING + - name: couchbase.operation_id + type: STRING + - name: db.name + type: STRING + - name: db.operation + type: STRING + - name: db.statement + type: STRING + - name: db.system + type: STRING + - name: network.peer.address + type: STRING + - name: network.peer.port + type: LONG + - name: network.type + type: STRING + - when: otel.semconv-stability.opt-in=database + metrics: + - name: db.client.operation.duration + description: Duration of database client operations. + type: HISTOGRAM + unit: s + attributes: + - name: db.operation.name + type: STRING + - name: db.system.name + type: STRING + - name: network.peer.address + type: STRING + - name: network.peer.port + type: LONG + spans: + - span_kind: CLIENT + attributes: + - name: db.namespace + type: STRING + - name: db.operation.name + type: STRING + - name: db.query.text + type: STRING + - name: db.system.name + type: STRING + - name: network.peer.address + type: STRING + - name: network.peer.port + type: LONG + - name: network.type + type: STRING - name: couchbase-3.1 description: | - Couchbase instrumentation is owned by the Couchbase project. This instrumentation automatically configures the instrumentation provided by the Couchbase library. + Couchbase instrumentation is owned by the Couchbase project for versions 3+. This instrumentation automatically configures the instrumentation provided by the Couchbase library. + library_link: https://github.com/couchbase/couchbase-java-client source_path: instrumentation/couchbase/couchbase-3.1 scope: name: io.opentelemetry.couchbase-3.1 @@ -2118,7 +2384,8 @@ libraries: - com.couchbase.client:java-client:[3.1,3.1.6) - name: couchbase-3.1.6 description: | - Couchbase instrumentation is owned by the Couchbase project. This instrumentation automatically configures the instrumentation provided by the Couchbase library. + Couchbase instrumentation is owned by the Couchbase project for versions 3+. This instrumentation automatically configures the instrumentation provided by the Couchbase library. + library_link: https://github.com/couchbase/couchbase-java-client source_path: instrumentation/couchbase/couchbase-3.1.6 scope: name: io.opentelemetry.couchbase-3.1.6 @@ -2127,7 +2394,8 @@ libraries: - com.couchbase.client:java-client:[3.1.6,3.2.0) - name: couchbase-3.2 description: | - Couchbase instrumentation is owned by the Couchbase project. This instrumentation automatically configures the instrumentation provided by the Couchbase library. + Couchbase instrumentation is owned by the Couchbase project for versions 3+. This instrumentation automatically configures the instrumentation provided by the Couchbase library. + library_link: https://github.com/couchbase/couchbase-java-client source_path: instrumentation/couchbase/couchbase-3.2 scope: name: io.opentelemetry.couchbase-3.2 @@ -2139,6 +2407,7 @@ libraries: description: | The dropwizard-metrics instrumentation for the dropwizard/codahale metrics library produces OpenTelemetry compliant versions of the metrics generated by the Dropwizard MetricRegistry. The Dropwizard metrics API does not have a concept of metric labels/tags/attributes, thus the data produced by this integration might be of very low quality, depending on how the API is used in the instrumented application. + library_link: https://metrics.dropwizard.io/4.2.0/ disabled_by_default: true source_path: instrumentation/dropwizard/dropwizard-metrics-4.0 scope: @@ -2152,6 +2421,9 @@ libraries: type: boolean default: false - name: dropwizard-views-0.7 + description: This instrumentation enables the creation of spans for Dropwizard + views. + library_link: https://www.dropwizard.io/en/latest/manual/views.html source_path: instrumentation/dropwizard/dropwizard-views-0.7 scope: name: io.opentelemetry.dropwizard-views-0.7 @@ -2163,15 +2435,16 @@ libraries: description: Enables the creation of experimental view (INTERNAL) spans. type: boolean default: false - - name: otel.instrumentation.common.experimental.controller-telemetry.enabled - description: Enables the creation of experimental controller (INTERNAL) spans. - type: boolean - default: false + telemetry: + - when: otel.instrumentation.common.experimental.view-telemetry.enabled=true + spans: + - span_kind: INTERNAL + attributes: [] elasticsearch: - name: elasticsearch-api-client-7.16 - description: This instrumentation enables client spans for Elasticsearch API client - requests for version 7 of the client. Versions 8.10 and later have native support - for OpenTelemetry. + description: | + This instrumentation extends the elasticsearch-rest-7.0 instrumentation by adding additional `db.elasticsearch.path_parts.id` and `db.elasticsearch.path_parts.index` attributes to Elasticsearch database client spans. Versions 8.10 and later of the client have native support for OpenTelemetry. + library_link: https://www.elastic.co/docs/reference/elasticsearch/clients/java source_path: instrumentation/elasticsearch/elasticsearch-api-client-7.16 scope: name: io.opentelemetry.elasticsearch-api-client-7.16 @@ -2179,27 +2452,17 @@ libraries: javaagent: - co.elastic.clients:elasticsearch-java:[7.16,7.17.20) - co.elastic.clients:elasticsearch-java:[8.0.0,8.10) - - name: elasticsearch-rest-5.0 - description: This instrumentation enables tracing for Elasticsearch REST clients. - source_path: instrumentation/elasticsearch/elasticsearch-rest-5.0 - scope: - name: io.opentelemetry.elasticsearch-rest-5.0 - target_versions: - javaagent: - - org.elasticsearch.client:rest:[5.0,6.4) - - org.elasticsearch.client:elasticsearch-rest-client:[5.0,6.4) - configurations: - - name: otel.instrumentation.elasticsearch.capture-search-query - description: | - Enable the capture of search query bodies. It is important to note that Elasticsearch queries - may contain personal or sensitive information. - type: boolean - default: false telemetry: - when: default spans: - span_kind: CLIENT attributes: + - name: db.elasticsearch.path_parts.id + type: STRING + - name: db.elasticsearch.path_parts.index + type: STRING + - name: db.operation + type: STRING - name: db.system type: STRING - name: http.request.method @@ -2210,20 +2473,124 @@ libraries: type: LONG - name: url.full type: STRING - - name: elasticsearch-rest-6.4 - description: This instrumentation enables tracing for Elasticsearch REST clients. - source_path: instrumentation/elasticsearch/elasticsearch-rest-6.4 - scope: - name: io.opentelemetry.elasticsearch-rest-6.4 - target_versions: - javaagent: - - org.elasticsearch.client:elasticsearch-rest-client:[6.4,7.0) - configurations: + - when: otel.semconv-stability.opt-in=database + metrics: + - name: db.client.operation.duration + description: Duration of database client operations. + type: HISTOGRAM + unit: s + attributes: + - name: db.operation.name + type: STRING + - name: db.system.name + type: STRING + - name: server.address + type: STRING + - name: server.port + type: LONG + spans: + - span_kind: CLIENT + attributes: + - name: db.elasticsearch.path_parts.id + type: STRING + - name: db.elasticsearch.path_parts.index + type: STRING + - name: db.operation.name + type: STRING + - name: db.system.name + type: STRING + - name: http.request.method + type: STRING + - name: server.address + type: STRING + - name: server.port + type: LONG + - name: url.full + type: STRING + - name: elasticsearch-rest-5.0 + description: This instrumentation enables database client spans and database client + metrics for Elasticsearch REST clients. + library_link: https://www.elastic.co/guide/en/elasticsearch/client/java-rest + source_path: instrumentation/elasticsearch/elasticsearch-rest-5.0 + scope: + name: io.opentelemetry.elasticsearch-rest-5.0 + target_versions: + javaagent: + - org.elasticsearch.client:rest:[5.0,6.4) + - org.elasticsearch.client:elasticsearch-rest-client:[5.0,6.4) + configurations: + - name: otel.instrumentation.elasticsearch.capture-search-query + description: | + Enable the capture of search query bodies. It is important to note that Elasticsearch queries may contain personal or sensitive information. + type: boolean + default: false + - name: otel.instrumentation.http.known-methods + description: | + Configures the instrumentation to recognize an alternative set of HTTP request methods. All other methods will be treated as `_OTHER`. + type: list + default: CONNECT,DELETE,GET,HEAD,OPTIONS,PATCH,POST,PUT,TRACE + telemetry: + - when: default + spans: + - span_kind: CLIENT + attributes: + - name: db.system + type: STRING + - name: http.request.method + type: STRING + - name: server.address + type: STRING + - name: server.port + type: LONG + - name: url.full + type: STRING + - when: otel.semconv-stability.opt-in=database + metrics: + - name: db.client.operation.duration + description: Duration of database client operations. + type: HISTOGRAM + unit: s + attributes: + - name: db.system.name + type: STRING + - name: server.address + type: STRING + - name: server.port + type: LONG + spans: + - span_kind: CLIENT + attributes: + - name: db.system.name + type: STRING + - name: http.request.method + type: STRING + - name: server.address + type: STRING + - name: server.port + type: LONG + - name: url.full + type: STRING + - name: elasticsearch-rest-6.4 + description: This instrumentation enables database client spans and database client + metrics for Elasticsearch REST clients. + library_link: https://www.elastic.co/guide/en/elasticsearch/client/java-rest + source_path: instrumentation/elasticsearch/elasticsearch-rest-6.4 + scope: + name: io.opentelemetry.elasticsearch-rest-6.4 + target_versions: + javaagent: + - org.elasticsearch.client:elasticsearch-rest-client:[6.4,7.0) + configurations: - name: otel.instrumentation.elasticsearch.capture-search-query description: | Enable the capture of search query bodies. It is important to note that Elasticsearch queries may contain personal or sensitive information. type: boolean default: false + - name: otel.instrumentation.http.known-methods + description: | + Configures the instrumentation to recognize an alternative set of HTTP request methods. All other methods will be treated as `_OTHER`. + type: list + default: CONNECT,DELETE,GET,HEAD,OPTIONS,PATCH,POST,PUT,TRACE telemetry: - when: default spans: @@ -2239,8 +2606,36 @@ libraries: type: LONG - name: url.full type: STRING + - when: otel.semconv-stability.opt-in=database + metrics: + - name: db.client.operation.duration + description: Duration of database client operations. + type: HISTOGRAM + unit: s + attributes: + - name: db.system.name + type: STRING + - name: server.address + type: STRING + - name: server.port + type: LONG + spans: + - span_kind: CLIENT + attributes: + - name: db.system.name + type: STRING + - name: http.request.method + type: STRING + - name: server.address + type: STRING + - name: server.port + type: LONG + - name: url.full + type: STRING - name: elasticsearch-rest-7.0 - description: This instrumentation enables tracing for Elasticsearch REST clients. + description: This instrumentation enables database client spans and database client + metrics for Elasticsearch REST clients. + library_link: https://www.elastic.co/guide/en/elasticsearch/client/java-rest source_path: instrumentation/elasticsearch/elasticsearch-rest-7.0 scope: name: io.opentelemetry.elasticsearch-rest-7.0 @@ -2255,6 +2650,11 @@ libraries: Enable the capture of search query bodies. It is important to note that Elasticsearch queries may contain personal or sensitive information. type: boolean default: false + - name: otel.instrumentation.http.known-methods + description: | + Configures the instrumentation to recognize an alternative set of HTTP request methods. All other methods will be treated as `_OTHER`. + type: list + default: CONNECT,DELETE,GET,HEAD,OPTIONS,PATCH,POST,PUT,TRACE telemetry: - when: default spans: @@ -2270,9 +2670,36 @@ libraries: type: LONG - name: url.full type: STRING + - when: otel.semconv-stability.opt-in=database + metrics: + - name: db.client.operation.duration + description: Duration of database client operations. + type: HISTOGRAM + unit: s + attributes: + - name: db.system.name + type: STRING + - name: server.address + type: STRING + - name: server.port + type: LONG + spans: + - span_kind: CLIENT + attributes: + - name: db.system.name + type: STRING + - name: http.request.method + type: STRING + - name: server.address + type: STRING + - name: server.port + type: LONG + - name: url.full + type: STRING - name: elasticsearch-transport-5.0 description: | - This instrumentation enables client spans for Elasticsearch transport client requests. Each call produces a span named after the Elasticsearch action, enriched with transport-specific attributes. + This instrumentation enables database client spans and database client metrics for Elasticsearch transport client requests. Each call produces a span named after the Elasticsearch action, enriched with transport-specific attributes. + library_link: https://www.elastic.co/guide/en/elasticsearch/client/java-api/ source_path: instrumentation/elasticsearch/elasticsearch-transport-5.0 scope: name: io.opentelemetry.elasticsearch-transport-5.0 @@ -2282,9 +2709,15 @@ libraries: - org.elasticsearch:elasticsearch:[5.0.0,5.3.0) configurations: - name: otel.instrumentation.elasticsearch.experimental-span-attributes - description: Enable the capture of experimental span attributes. + description: | + Enable the capture of the experimental span attributes `elasticsearch.action`, `elasticsearch.id`, `elasticsearch.request`, `elasticsearch.request.indices`, `elasticsearch.request.write.routing`, `elasticsearch.request.write.type`, `elasticsearch.response.status`, `elasticsearch.shard.replication.failed`, `elasticsearch.shard.replication.successful`, `elasticsearch.shard.replication.total`, `elasticsearch.type`, and `elasticsearch.version`. type: boolean default: false + - name: otel.instrumentation.http.known-methods + description: | + Configures the instrumentation to recognize an alternative set of HTTP request methods. All other methods will be treated as `_OTHER`. + type: list + default: CONNECT,DELETE,GET,HEAD,OPTIONS,PATCH,POST,PUT,TRACE telemetry: - when: default spans: @@ -2333,6 +2766,16 @@ libraries: - name: network.peer.port type: LONG - when: otel.semconv-stability.opt-in=database + metrics: + - name: db.client.operation.duration + description: Duration of database client operations. + type: HISTOGRAM + unit: s + attributes: + - name: db.operation.name + type: STRING + - name: db.system.name + type: STRING spans: - span_kind: CLIENT attributes: @@ -2348,7 +2791,8 @@ libraries: type: LONG - name: elasticsearch-transport-5.3 description: | - This instrumentation enables client spans for Elasticsearch transport client requests. Each call produces a span named after the Elasticsearch action, enriched with transport-specific attributes. + This instrumentation enables database client spans and database client metrics for Elasticsearch transport client requests. Each call produces a span named after the Elasticsearch action, enriched with transport-specific attributes. + library_link: https://www.elastic.co/guide/en/elasticsearch/client/java-api/ source_path: instrumentation/elasticsearch/elasticsearch-transport-5.3 scope: name: io.opentelemetry.elasticsearch-transport-5.3 @@ -2358,7 +2802,8 @@ libraries: - org.elasticsearch:elasticsearch:[5.3.0,6.0.0) configurations: - name: otel.instrumentation.elasticsearch.experimental-span-attributes - description: Enable the capture of experimental span attributes. + description: | + Enable the capture of `elasticsearch.action`, `elasticsearch.id`, `elasticsearch.request`, `elasticsearch.request.indices`, `elasticsearch.request.search.types`, `elasticsearch.request.write.type`, `elasticsearch.request.write.version`, `elasticsearch.response.status`, `elasticsearch.shard.broadcast.failed`, `elasticsearch.shard.broadcast.successful`, `elasticsearch.shard.broadcast.total`, `elasticsearch.shard.replication.failed`, `elasticsearch.shard.replication.successful`, `elasticsearch.shard.replication.total`, `elasticsearch.type`, and `elasticsearch.version` experimental span attributes. type: boolean default: false telemetry: @@ -2419,6 +2864,16 @@ libraries: - name: network.peer.port type: LONG - when: otel.semconv-stability.opt-in=database + metrics: + - name: db.client.operation.duration + description: Duration of database client operations. + type: HISTOGRAM + unit: s + attributes: + - name: db.operation.name + type: STRING + - name: db.system.name + type: STRING spans: - span_kind: CLIENT attributes: @@ -2434,7 +2889,8 @@ libraries: type: LONG - name: elasticsearch-transport-6.0 description: | - This instrumentation enables client spans for Elasticsearch transport client requests. Each call produces a span named after the Elasticsearch action, enriched with transport-specific attributes. + This instrumentation enables database client spans and database client metrics for Elasticsearch transport client requests. Each call produces a span named after the Elasticsearch action, enriched with transport-specific attributes. + library_link: https://www.elastic.co/guide/en/elasticsearch/client/java-api/ source_path: instrumentation/elasticsearch/elasticsearch-transport-6.0 scope: name: io.opentelemetry.elasticsearch-transport-6.0 @@ -2444,7 +2900,8 @@ libraries: - org.elasticsearch.client:transport:[6.0.0,) configurations: - name: otel.instrumentation.elasticsearch.experimental-span-attributes - description: Enable the capture of experimental span attributes. + description: | + Enable the capture of `elasticsearch.action`, `elasticsearch.id`, `elasticsearch.request`, `elasticsearch.request.indices`, `elasticsearch.request.write.type`, `elasticsearch.request.write.version`, `elasticsearch.response.status`, `elasticsearch.shard.replication.failed`, `elasticsearch.shard.replication.successful`, `elasticsearch.shard.replication.total`, `elasticsearch.type`, and `elasticsearch.version` experimental span attributes. type: boolean default: false telemetry: @@ -2501,6 +2958,16 @@ libraries: - name: network.type type: STRING - when: otel.semconv-stability.opt-in=database + metrics: + - name: db.client.operation.duration + description: Duration of database client operations. + type: HISTOGRAM + unit: s + attributes: + - name: db.operation.name + type: STRING + - name: db.system.name + type: STRING spans: - span_kind: CLIENT attributes: @@ -2520,6 +2987,7 @@ libraries: - name: executors description: | The executor instrumentation ensures that context is automatically propagated when using common Java executors (e.g., ThreadPoolExecutor, ScheduledThreadPoolExecutor, ForkJoinPool). When a task is submitted, the current context is captured and bound to the task. Then, when the task eventually runs, even if it’s on a different thread, the instrumentation reactivates that context, enabling consistent correlation across concurrent and asynchronous workflows. + library_link: https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/Executor.html source_path: instrumentation/executors scope: name: io.opentelemetry.executors @@ -2539,6 +3007,7 @@ libraries: - name: finagle-http-23.11 description: | This instrumentation for Finagle HTTP clients and servers ensures that telemetry is correctly generated by the underlying Netty instrumentation. It augments existing telemetry by bridging the gap between Finagle's abstractions and Netty's pipeline, primarily for context propagation. + library_link: https://github.com/twitter/finagle source_path: instrumentation/finagle-http-23.11 scope: name: io.opentelemetry.finagle-http-23.11 @@ -2551,6 +3020,7 @@ libraries: description: This instrumentation for the Finatra web framework augments the telemetry generated by the underlying Netty instrumentation. It provides more specific, high-level context, such as route information, to the spans generated by Netty. + library_link: https://github.com/twitter/finatra source_path: instrumentation/finatra-2.9 scope: name: io.opentelemetry.finatra-2.9 @@ -2569,20 +3039,96 @@ libraries: type: STRING geode: - name: geode-1.4 + description: This instrumentation enables database client spans and database client + metrics for Apache Geode cache operations. + library_link: https://geode.apache.org/ source_path: instrumentation/geode-1.4 scope: name: io.opentelemetry.geode-1.4 target_versions: javaagent: - org.apache.geode:geode-core:[1.4.0,) + configurations: + - name: otel.instrumentation.common.db-statement-sanitizer.enabled + description: Enables statement sanitization for database queries. + type: boolean + default: true + telemetry: + - when: default + spans: + - span_kind: CLIENT + attributes: + - name: db.name + type: STRING + - name: db.operation + type: STRING + - name: db.statement + type: STRING + - name: db.system + type: STRING + - when: otel.semconv-stability.opt-in=database + metrics: + - name: db.client.operation.duration + description: Duration of database client operations. + type: HISTOGRAM + unit: s + attributes: + - name: db.namespace + type: STRING + - name: db.operation.name + type: STRING + - name: db.system.name + type: STRING + spans: + - span_kind: CLIENT + attributes: + - name: db.namespace + type: STRING + - name: db.operation.name + type: STRING + - name: db.query.text + type: STRING + - name: db.system.name + type: STRING google: - name: google-http-client-1.19 + description: This instrumentation enables HTTP client spans and HTTP client metrics + for Google HTTP Client requests. + library_link: https://github.com/googleapis/google-http-java-client source_path: instrumentation/google-http-client-1.19 scope: name: io.opentelemetry.google-http-client-1.19 target_versions: javaagent: - com.google.http-client:google-http-client:[1.19.0,) + configurations: + - name: otel.instrumentation.http.known-methods + description: | + Configures the instrumentation to recognize an alternative set of HTTP request methods. All other methods will be treated as `_OTHER`. + type: list + default: CONNECT,DELETE,GET,HEAD,OPTIONS,PATCH,POST,PUT,TRACE + - name: otel.instrumentation.http.client.capture-request-headers + description: List of HTTP request headers to capture in HTTP client telemetry. + type: list + default: '' + - name: otel.instrumentation.http.client.capture-response-headers + description: List of HTTP response headers to capture in HTTP client telemetry. + type: list + default: '' + - name: otel.instrumentation.common.peer-service-mapping + description: Used to specify a mapping from host names or IP addresses to peer + services. + type: map + default: '' + - name: otel.instrumentation.http.client.emit-experimental-telemetry + description: | + Enable the capture of experimental HTTP client telemetry. Adds the `http.request.body.size` and `http.response.body.size` attributes to spans, and records `http.client.request.size` and `http.client.response.size` metrics. + type: boolean + default: false + - name: otel.instrumentation.http.client.experimental.redact-query-parameters + description: Redact sensitive URL parameters. See https://opentelemetry.io/docs/specs/semconv/http/http-spans. + type: boolean + default: true telemetry: - when: default metrics: @@ -2616,14 +3162,29 @@ libraries: type: STRING grails: - name: grails-3.0 + description: | + This instrumentation enriches existing HTTP server spans with HTTP route information, and optionally enables experimental controller (INTERNAL) spans for Grails applications. + library_link: https://grails.apache.org/ source_path: instrumentation/grails-3.0 scope: name: io.opentelemetry.grails-3.0 target_versions: javaagent: - org.grails:grails-web-url-mappings:[3.0,) + configurations: + - name: otel.instrumentation.common.experimental.controller-telemetry.enabled + description: Enables the creation of experimental controller (INTERNAL) spans. + type: boolean + default: false + telemetry: + - when: otel.instrumentation.common.experimental.controller-telemetry.enabled=true + spans: + - span_kind: INTERNAL + attributes: [] graphql: - name: graphql-java-12.0 + description: This instrumentation enables spans for GraphQL Java operations. + library_link: https://www.graphql-java.com/ source_path: instrumentation/graphql-java/graphql-java-12.0 scope: name: io.opentelemetry.graphql-java-12.0 @@ -2632,7 +3193,31 @@ libraries: - com.graphql-java:graphql-java:[12,20) library: - com.graphql-java:graphql-java:[12.0,19.+) + configurations: + - name: otel.instrumentation.graphql.query-sanitizer.enabled + description: Enables sanitization of sensitive information from queries so they + aren't added as span attributes. + type: boolean + default: true + - name: otel.instrumentation.graphql.add-operation-name-to-span-name.enabled + description: | + Whether GraphQL operation name is added to the span name. WARNING: The GraphQL operation name is provided by the client and can have high cardinality. Use only when the server is not exposed to malicious clients. + type: boolean + default: false + telemetry: + - when: default + spans: + - span_kind: INTERNAL + attributes: + - name: graphql.document + type: STRING + - name: graphql.operation.name + type: STRING + - name: graphql.operation.type + type: STRING - name: graphql-java-20.0 + description: This instrumentation enables spans for GraphQL Java operations. + library_link: https://www.graphql-java.com/ source_path: instrumentation/graphql-java/graphql-java-20.0 minimum_java_version: 11 scope: @@ -2642,16 +3227,138 @@ libraries: - com.graphql-java:graphql-java:[20,) library: - com.graphql-java:graphql-java:20.0 + configurations: + - name: otel.instrumentation.graphql.query-sanitizer.enabled + description: Enables sanitization of sensitive information from queries so they + aren't added as span attributes. + type: boolean + default: true + - name: otel.instrumentation.graphql.add-operation-name-to-span-name.enabled + description: | + Whether GraphQL operation name is added to the span name. WARNING: The GraphQL operation name is provided by the client and can have high cardinality. Use only when the server is not exposed to malicious clients. + type: boolean + default: false + - name: otel.instrumentation.graphql.data-fetcher.enabled + description: Enables span generation for data fetchers. + type: boolean + default: false + - name: otel.instrumentation.graphql.trivial-data-fetcher.enabled + description: Whether to create spans for trivial data fetchers. A trivial data + fetcher is one that simply maps data from an object to a field. + type: boolean + default: false + telemetry: + - when: default + spans: + - span_kind: INTERNAL + attributes: + - name: graphql.document + type: STRING + - name: graphql.operation.name + type: STRING + - name: graphql.operation.type + type: STRING + - when: otel.instrumentation.graphql.data-fetcher.enabled=true + spans: + - span_kind: INTERNAL + attributes: + - name: graphql.document + type: STRING + - name: graphql.field.name + type: STRING + - name: graphql.field.path + type: STRING + - name: graphql.operation.name + type: STRING + - name: graphql.operation.type + type: STRING grizzly: - name: grizzly-2.3 + description: This instrumentation enables HTTP server spans and HTTP server metrics + for Grizzly applications. + library_link: https://javaee.github.io/grizzly/httpserverframework.html source_path: instrumentation/grizzly-2.3 scope: name: io.opentelemetry.grizzly-2.3 target_versions: javaagent: - org.glassfish.grizzly:grizzly-http:[2.3,) + configurations: + - name: otel.instrumentation.http.known-methods + description: | + Configures the instrumentation to recognize an alternative set of HTTP request methods. All other methods will be treated as `_OTHER`. + type: list + default: CONNECT,DELETE,GET,HEAD,OPTIONS,PATCH,POST,PUT,TRACE + - name: otel.instrumentation.http.server.capture-request-headers + description: List of HTTP request headers to capture in HTTP server telemetry. + type: list + default: '' + - name: otel.instrumentation.http.server.capture-response-headers + description: List of HTTP response headers to capture in HTTP server telemetry. + type: list + default: '' + - name: otel.instrumentation.common.peer-service-mapping + description: Used to specify a mapping from host names or IP addresses to peer + services. + type: map + default: '' + - name: otel.instrumentation.http.server.emit-experimental-telemetry + description: | + Enable the capture of experimental HTTP server telemetry. Adds the `http.request.body.size` and `http.response.body.size` attributes to spans, and records `http.server.request.body.size` and `http.server.response.body.size` metrics. + type: boolean + default: false + telemetry: + - when: default + metrics: + - name: http.server.request.duration + description: Duration of HTTP server requests. + type: HISTOGRAM + unit: s + attributes: + - name: http.request.method + type: STRING + - name: http.response.status_code + type: LONG + - name: network.protocol.version + type: STRING + - name: url.scheme + type: STRING + spans: + - span_kind: SERVER + attributes: + - name: client.address + type: STRING + - name: error.type + type: STRING + - name: http.request.method + type: STRING + - name: http.request.method_original + type: STRING + - name: http.response.status_code + type: LONG + - name: network.peer.address + type: STRING + - name: network.peer.port + type: LONG + - name: network.protocol.version + type: STRING + - name: server.address + type: STRING + - name: server.port + type: LONG + - name: url.path + type: STRING + - name: url.query + type: STRING + - name: url.scheme + type: STRING + - name: user_agent.original + type: STRING grpc: - name: grpc-1.6 + description: This instrumentation enables RPC client spans and metrics, and RPC + server spans and metrics for gRPC version 1.6 and above. + library_link: https://github.com/grpc/grpc-java source_path: instrumentation/grpc-1.6 scope: name: io.opentelemetry.grpc-1.6 @@ -2660,6 +3367,182 @@ libraries: - io.grpc:grpc-core:[1.6.0,) library: - io.grpc:grpc-core:1.6.0 + configurations: + - name: otel.instrumentation.grpc.emit-message-events + description: Determines whether to emit a span event for each individual message + received and sent. + type: boolean + default: true + - name: otel.instrumentation.grpc.experimental-span-attributes + description: | + Enable the capture of experimental span attributes `grpc.received.message_count`, `grpc.sent.message_count` and `grpc.canceled`. + type: boolean + default: false + - name: otel.instrumentation.grpc.capture-metadata.client.request + description: | + A comma-separated list of request metadata keys. gRPC client instrumentation will capture metadata values corresponding to configured keys as span attributes. + type: list + default: '' + - name: otel.instrumentation.grpc.capture-metadata.server.request + description: | + A comma-separated list of request metadata keys. gRPC server instrumentation will capture metadata values corresponding to configured keys as span attributes. + type: list + default: '' + telemetry: + - when: default + metrics: + - name: rpc.client.duration + description: The duration of an outbound RPC invocation. + type: HISTOGRAM + unit: ms + attributes: + - name: rpc.grpc.status_code + type: LONG + - name: rpc.method + type: STRING + - name: rpc.service + type: STRING + - name: rpc.system + type: STRING + - name: server.address + type: STRING + - name: server.port + type: LONG + - name: rpc.server.duration + description: The duration of an inbound RPC invocation. + type: HISTOGRAM + unit: ms + attributes: + - name: network.type + type: STRING + - name: rpc.grpc.status_code + type: LONG + - name: rpc.method + type: STRING + - name: rpc.service + type: STRING + - name: rpc.system + type: STRING + - name: server.address + type: STRING + - name: server.port + type: LONG + spans: + - span_kind: CLIENT + attributes: + - name: rpc.grpc.status_code + type: LONG + - name: rpc.method + type: STRING + - name: rpc.service + type: STRING + - name: rpc.system + type: STRING + - name: server.address + type: STRING + - name: server.port + type: LONG + - span_kind: SERVER + attributes: + - name: network.peer.address + type: STRING + - name: network.peer.port + type: LONG + - name: network.type + type: STRING + - name: rpc.grpc.status_code + type: LONG + - name: rpc.method + type: STRING + - name: rpc.service + type: STRING + - name: rpc.system + type: STRING + - name: server.address + type: STRING + - name: server.port + type: LONG + - when: otel.instrumentation.grpc.experimental-span-attributes=true + metrics: + - name: rpc.client.duration + description: The duration of an outbound RPC invocation. + type: HISTOGRAM + unit: ms + attributes: + - name: rpc.grpc.status_code + type: LONG + - name: rpc.method + type: STRING + - name: rpc.service + type: STRING + - name: rpc.system + type: STRING + - name: server.address + type: STRING + - name: server.port + type: LONG + - name: rpc.server.duration + description: The duration of an inbound RPC invocation. + type: HISTOGRAM + unit: ms + attributes: + - name: network.type + type: STRING + - name: rpc.grpc.status_code + type: LONG + - name: rpc.method + type: STRING + - name: rpc.service + type: STRING + - name: rpc.system + type: STRING + - name: server.address + type: STRING + - name: server.port + type: LONG + spans: + - span_kind: CLIENT + attributes: + - name: grpc.received.message_count + type: LONG + - name: grpc.sent.message_count + type: LONG + - name: rpc.grpc.status_code + type: LONG + - name: rpc.method + type: STRING + - name: rpc.service + type: STRING + - name: rpc.system + type: STRING + - name: server.address + type: STRING + - name: server.port + type: LONG + - span_kind: SERVER + attributes: + - name: grpc.received.message_count + type: LONG + - name: grpc.sent.message_count + type: LONG + - name: network.peer.address + type: STRING + - name: network.peer.port + type: LONG + - name: network.type + type: STRING + - name: rpc.grpc.status_code + type: LONG + - name: rpc.method + type: STRING + - name: rpc.service + type: STRING + - name: rpc.system + type: STRING + - name: server.address + type: STRING + - name: server.port + type: LONG guava: - name: guava-10.0 source_path: instrumentation/guava-10.0 @@ -2672,6 +3555,8 @@ libraries: - com.google.guava:guava:10.0 gwt: - name: gwt-2.0 + description: This instrumentation enables RPC server spans for GWT RPC requests. + library_link: https://www.gwtproject.org/ source_path: instrumentation/gwt-2.0 scope: name: io.opentelemetry.gwt-2.0 @@ -2679,22 +3564,76 @@ libraries: javaagent: - com.google.gwt:gwt-servlet:[2.0.0,) - org.gwtproject:gwt-servlet:[2.10.0,) + telemetry: + - when: default + spans: + - span_kind: SERVER + attributes: + - name: rpc.method + type: STRING + - name: rpc.service + type: STRING + - name: rpc.system + type: STRING hibernate: - name: hibernate-3.3 + description: | + This instrumentation enables the generation of INTERNAL spans for Hibernate operations, including session methods (e.g., `save`, `update`, `delete`), transaction commits, and query executions. + library_link: https://github.com/hibernate/hibernate-orm source_path: instrumentation/hibernate/hibernate-3.3 scope: name: io.opentelemetry.hibernate-3.3 target_versions: javaagent: - org.hibernate:hibernate-core:[3.3.0.GA,4.0.0.Final) + configurations: + - name: otel.instrumentation.hibernate.experimental-span-attributes + description: Enables the addition of the experimental `hibernate.session_id` + span attribute. + type: boolean + default: false + telemetry: + - when: default + spans: + - span_kind: INTERNAL + attributes: [] + - when: otel.instrumentation.hibernate.experimental-span-attributes=true + spans: + - span_kind: INTERNAL + attributes: + - name: hibernate.session_id + type: STRING - name: hibernate-4.0 + description: | + This instrumentation enables the generation of INTERNAL spans for Hibernate operations, including session methods (e.g., `save`, `update`, `delete`), transaction commits, and query executions. + library_link: https://github.com/hibernate/hibernate-orm source_path: instrumentation/hibernate/hibernate-4.0 scope: name: io.opentelemetry.hibernate-4.0 target_versions: javaagent: - org.hibernate:hibernate-core:[4.0.0.Final,6) + configurations: + - name: otel.instrumentation.hibernate.experimental-span-attributes + description: Enables the addition of the experimental `hibernate.session_id` + span attribute. + type: boolean + default: false + telemetry: + - when: default + spans: + - span_kind: INTERNAL + attributes: [] + - when: otel.instrumentation.hibernate.experimental-span-attributes=true + spans: + - span_kind: INTERNAL + attributes: + - name: hibernate.session_id + type: STRING - name: hibernate-6.0 + description: | + This instrumentation enables the generation of INTERNAL spans for Hibernate operations, including session methods (e.g., `save`, `update`, `delete`), transaction commits, and query executions. + library_link: https://github.com/hibernate/hibernate-orm source_path: instrumentation/hibernate/hibernate-6.0 minimum_java_version: 11 scope: @@ -2702,14 +3641,54 @@ libraries: target_versions: javaagent: - org.hibernate:hibernate-core:[6.0.0.Final,) + configurations: + - name: otel.instrumentation.hibernate.experimental-span-attributes + description: Enables the addition of the experimental `hibernate.session_id` + span attribute. + type: boolean + default: false + telemetry: + - when: default + spans: + - span_kind: INTERNAL + attributes: [] + - when: otel.instrumentation.hibernate.experimental-span-attributes=true + spans: + - span_kind: INTERNAL + attributes: + - name: hibernate.session_id + type: STRING - name: hibernate-procedure-call-4.3 + description: This instrumentation enables the generation of INTERNAL spans for + Hibernate stored procedure calls. + library_link: https://github.com/hibernate/hibernate-orm source_path: instrumentation/hibernate/hibernate-procedure-call-4.3 scope: name: io.opentelemetry.hibernate-procedure-call-4.3 target_versions: javaagent: - org.hibernate:hibernate-core:[4.3.0.Final,) + configurations: + - name: otel.instrumentation.hibernate.experimental-span-attributes + description: Enables the addition of the experimental `hibernate.session_id` + span attribute. + type: boolean + default: false + telemetry: + - when: default + spans: + - span_kind: INTERNAL + attributes: [] + - when: otel.instrumentation.hibernate.experimental-span-attributes=true + spans: + - span_kind: INTERNAL + attributes: + - name: hibernate.session_id + type: STRING - name: hibernate-reactive-1.0 + description: | + This instrumentation does not emit any telemetry on its own. Instead, it enables context propagation for Hibernate Reactive asynchronous operations. + library_link: https://hibernate.org/reactive/ source_path: instrumentation/hibernate/hibernate-reactive-1.0 scope: name: io.opentelemetry.hibernate-reactive-1.0 @@ -2718,6 +3697,9 @@ libraries: - org.hibernate.reactive:hibernate-reactive-core:(,) hikaricp: - name: hikaricp-3.0 + description: | + This instrumentation provides database client metrics for HikariCP 3.0+ connection pools. It reports metrics like connection timeouts, creation time, wait time, and usage time, along with connection pool statistics such as the number of active, idle, and pending connections. + library_link: https://github.com/brettwooldridge/HikariCP source_path: instrumentation/hikaricp-3.0 scope: name: io.opentelemetry.hikaricp-3.0 @@ -2856,12 +3838,43 @@ libraries: type: STRING http: - name: http-url-connection + description: | + This instrumentation enables the generation of HTTP client spans and HTTP client metrics for requests made using `java.net.HttpURLConnection`. + library_link: https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/net/HttpURLConnection.html source_path: instrumentation/http-url-connection scope: name: io.opentelemetry.http-url-connection target_versions: javaagent: - Java 8+ + configurations: + - name: otel.instrumentation.http.known-methods + description: | + Configures the instrumentation to recognize an alternative set of HTTP request methods. All other methods will be treated as `_OTHER`. + type: list + default: CONNECT,DELETE,GET,HEAD,OPTIONS,PATCH,POST,PUT,TRACE + - name: otel.instrumentation.http.client.capture-request-headers + description: List of HTTP request headers to capture in HTTP client telemetry. + type: list + default: '' + - name: otel.instrumentation.http.client.capture-response-headers + description: List of HTTP response headers to capture in HTTP client telemetry. + type: list + default: '' + - name: otel.instrumentation.common.peer-service-mapping + description: Used to specify a mapping from host names or IP addresses to peer + services. + type: map + default: '' + - name: otel.instrumentation.http.client.emit-experimental-telemetry + description: | + Enable the capture of experimental HTTP client telemetry. Adds the `http.request.body.size` and `http.response.body.size` attributes to spans, and records `http.client.request.size` and `http.client.response.size` metrics. + type: boolean + default: false + - name: otel.instrumentation.http.client.experimental.redact-query-parameters + description: Redact sensitive URL parameters. See https://opentelemetry.io/docs/specs/semconv/http/http-spans. + type: boolean + default: true telemetry: - when: default metrics: @@ -2899,22 +3912,101 @@ libraries: type: STRING hystrix: - name: hystrix-1.4 + description: This instrumentation enables the generation of INTERNAL spans for + Hystrix command executions and fallbacks. + library_link: https://github.com/Netflix/Hystrix source_path: instrumentation/hystrix-1.4 scope: name: io.opentelemetry.hystrix-1.4 target_versions: javaagent: - com.netflix.hystrix:hystrix-core:[1.4.0,) + configurations: + - name: otel.instrumentation.hystrix.experimental-span-attributes + description: Enables capturing the experimental `hystrix.command`, `hystrix.circuit_open` + and `hystrix.group` span attributes. + type: boolean + default: false + telemetry: + - when: default + spans: + - span_kind: INTERNAL + attributes: [] + - when: otel.instrumentation.hystrix.experimental-span-attributes=true + spans: + - span_kind: INTERNAL + attributes: + - name: hystrix.circuit_open + type: BOOLEAN + - name: hystrix.command + type: STRING + - name: hystrix.group + type: STRING influxdb: - name: influxdb-2.4 + description: This instrumentation enables the generation of database client spans + and metrics for the InfluxDB Java client. + library_link: https://github.com/influxdata/influxdb-java source_path: instrumentation/influxdb-2.4 scope: name: io.opentelemetry.influxdb-2.4 target_versions: javaagent: - org.influxdb:influxdb-java:[2.4,) + telemetry: + - when: default + spans: + - span_kind: CLIENT + attributes: + - name: db.name + type: STRING + - name: db.operation + type: STRING + - name: db.statement + type: STRING + - name: db.system + type: STRING + - name: server.address + type: STRING + - name: server.port + type: LONG + - when: otel.semconv-stability.opt-in=database + metrics: + - name: db.client.operation.duration + description: Duration of database client operations. + type: HISTOGRAM + unit: s + attributes: + - name: db.namespace + type: STRING + - name: db.operation.name + type: STRING + - name: db.system.name + type: STRING + - name: server.address + type: STRING + - name: server.port + type: LONG + spans: + - span_kind: CLIENT + attributes: + - name: db.namespace + type: STRING + - name: db.operation.name + type: STRING + - name: db.query.text + type: STRING + - name: db.system.name + type: STRING + - name: server.address + type: STRING + - name: server.port + type: LONG java: - name: java-http-client + description: This instrumentation enables HTTP client spans and HTTP client metrics + for requests made using the Java HTTP client. + library_link: https://docs.oracle.com/en/java/javase/11/docs/api/java.net.http/java/net/http/package-summary.html source_path: instrumentation/java-http-client minimum_java_version: 11 scope: @@ -2922,6 +4014,34 @@ libraries: target_versions: javaagent: - Java 11+ + configurations: + - name: otel.instrumentation.http.known-methods + description: | + Configures the instrumentation to recognize an alternative set of HTTP request methods. All other methods will be treated as `_OTHER`. + type: list + default: CONNECT,DELETE,GET,HEAD,OPTIONS,PATCH,POST,PUT,TRACE + - name: otel.instrumentation.http.client.capture-request-headers + description: List of HTTP request headers to capture in HTTP client telemetry. + type: list + default: '' + - name: otel.instrumentation.http.client.capture-response-headers + description: List of HTTP response headers to capture in HTTP client telemetry. + type: list + default: '' + - name: otel.instrumentation.common.peer-service-mapping + description: Used to specify a mapping from host names or IP addresses to peer + services. + type: map + default: '' + - name: otel.instrumentation.http.client.emit-experimental-telemetry + description: | + Enable the capture of experimental HTTP client telemetry. Adds the `http.request.body.size` and `http.response.body.size` attributes to spans, and records `http.client.request.size` and `http.client.response.size` metrics. + type: boolean + default: false + - name: otel.instrumentation.http.client.experimental.redact-query-parameters + description: Redact sensitive URL parameters. See https://opentelemetry.io/docs/specs/semconv/http/http-spans. + type: boolean + default: true telemetry: - when: default metrics: @@ -2960,14 +4080,93 @@ libraries: - name: url.full type: STRING - name: java-http-server + description: This instrumentation enables HTTP server spans and HTTP server metrics + for the Java HTTP server. + library_link: https://docs.oracle.com/en/java/javase/21/docs/api/jdk.httpserver/module-summary.html source_path: instrumentation/java-http-server scope: name: io.opentelemetry.java-http-server target_versions: javaagent: - Java 8+ + configurations: + - name: otel.instrumentation.http.known-methods + description: | + Configures the instrumentation to recognize an alternative set of HTTP request methods. All other methods will be treated as `_OTHER`. + type: list + default: CONNECT,DELETE,GET,HEAD,OPTIONS,PATCH,POST,PUT,TRACE + - name: otel.instrumentation.http.server.capture-request-headers + description: List of HTTP request headers to capture in HTTP server telemetry. + type: list + default: '' + - name: otel.instrumentation.http.server.capture-response-headers + description: List of HTTP response headers to capture in HTTP server telemetry. + type: list + default: '' + - name: otel.instrumentation.common.peer-service-mapping + description: Used to specify a mapping from host names or IP addresses to peer + services. + type: map + default: '' + - name: otel.instrumentation.http.server.emit-experimental-telemetry + description: | + Enable the capture of experimental HTTP server telemetry. Adds the `http.request.body.size` and `http.response.body.size` attributes to spans, and records `http.server.request.body.size` and `http.server.response.body.size` metrics. + type: boolean + default: false + telemetry: + - when: default + metrics: + - name: http.server.request.duration + description: Duration of HTTP server requests. + type: HISTOGRAM + unit: s + attributes: + - name: http.request.method + type: STRING + - name: http.response.status_code + type: LONG + - name: http.route + type: STRING + - name: network.protocol.version + type: STRING + - name: url.scheme + type: STRING + spans: + - span_kind: SERVER + attributes: + - name: client.address + type: STRING + - name: error.type + type: STRING + - name: http.request.method + type: STRING + - name: http.response.status_code + type: LONG + - name: http.route + type: STRING + - name: network.peer.address + type: STRING + - name: network.peer.port + type: LONG + - name: network.protocol.version + type: STRING + - name: server.address + type: STRING + - name: server.port + type: LONG + - name: url.path + type: STRING + - name: url.query + type: STRING + - name: url.scheme + type: STRING + - name: user_agent.original + type: STRING javalin: - name: javalin-5.0 + description: This instrumentation enriches existing HTTP server spans with route + information, it does not emit any telemetry on its own. + library_link: https://javalin.io/ source_path: instrumentation/javalin-5.0 minimum_java_version: 11 scope: @@ -3103,6 +4302,7 @@ libraries: description: | The JDBC instrumentation provides database client spans and metrics. Each call produces a span named after the SQL verb, enriched with standard DB client attributes (system, database, operation, sanitized statement, peer address) and error details if an exception occurs. There is also a "jdbc-datasource" instrumentation that creates spans for datasource connections, but is disabled by default due to the volume of telemetry produced. + library_link: https://docs.oracle.com/javase/8/docs/api/java/sql/package-summary.html source_path: instrumentation/jdbc scope: name: io.opentelemetry.jdbc @@ -3387,6 +4587,7 @@ libraries: - name: kafka-clients-0.11 description: | This instrumentation enables messaging spans and metrics for Apache Kafka 0.11 clients. It automatically traces message production and consumption, propagates context, and emits metrics for production and consumption. + library_link: https://kafka.apache.org/ source_path: instrumentation/kafka/kafka-clients/kafka-clients-0.11 scope: name: io.opentelemetry.kafka-clients-0.11 @@ -3414,13 +4615,25 @@ libraries: - name: kafka-clients-2.6 description: | This instrumentation provides a library integration that enables messaging spans and metrics for Apache Kafka 2.6+ clients. + library_link: https://kafka.apache.org/ source_path: instrumentation/kafka/kafka-clients/kafka-clients-2.6 scope: name: io.opentelemetry.kafka-clients-2.6 target_versions: library: - org.apache.kafka:kafka-clients:2.6.0 + configurations: + - name: otel.instrumentation.messaging.experimental.capture-headers + description: A comma-separated list of header names to capture as span attributes. + type: list + default: '' + - name: otel.instrumentation.messaging.experimental.receive-telemetry.enabled + description: | + Enables experimental receive telemetry, which will cause consumers to start a new trace, with only a span link connecting it to the producer trace. + type: boolean + default: false - name: kafka-streams-0.11 + library_link: https://kafka.apache.org/documentation/streams/ source_path: instrumentation/kafka/kafka-streams-0.11 scope: name: io.opentelemetry.kafka-streams-0.11 @@ -4024,6 +5237,7 @@ libraries: - name: openai-java-1.1 description: This instrumentation enables Gen AI client spans and metrics for OpenAI Java SDK 1.1+. + library_link: https://github.com/openai/openai-java source_path: instrumentation/openai/openai-java-1.1 scope: name: io.opentelemetry.openai-java-1.1 @@ -4031,7 +5245,7 @@ libraries: javaagent: - com.openai:openai-java:[1.1.0,3) library: - - com.openai:openai-java:[1.1.0,2.+) + - com.openai:openai-java:1.1.0 configurations: - name: otel.instrumentation.genai.capture-message-content description: | @@ -4048,12 +5262,12 @@ libraries: attributes: - name: gen_ai.operation.name type: STRING + - name: gen_ai.provider.name + type: STRING - name: gen_ai.request.model type: STRING - name: gen_ai.response.model type: STRING - - name: gen_ai.system - type: STRING - name: gen_ai.client.token.usage description: Measures number of input and output tokens used. type: HISTOGRAM @@ -4061,12 +5275,12 @@ libraries: attributes: - name: gen_ai.operation.name type: STRING + - name: gen_ai.provider.name + type: STRING - name: gen_ai.request.model type: STRING - name: gen_ai.response.model type: STRING - - name: gen_ai.system - type: STRING - name: gen_ai.token.type type: STRING spans: @@ -4074,20 +5288,22 @@ libraries: attributes: - name: gen_ai.operation.name type: STRING + - name: gen_ai.provider.name + type: STRING - name: gen_ai.request.encoding_formats type: STRING_ARRAY - name: gen_ai.request.model type: STRING - name: gen_ai.response.model type: STRING - - name: gen_ai.system - type: STRING - name: gen_ai.usage.input_tokens type: LONG - span_kind: INTERNAL attributes: - name: gen_ai.operation.name type: STRING + - name: gen_ai.provider.name + type: STRING - name: gen_ai.request.frequency_penalty type: DOUBLE - name: gen_ai.request.max_tokens @@ -4110,8 +5326,6 @@ libraries: type: STRING - name: gen_ai.response.model type: STRING - - name: gen_ai.system - type: STRING - name: gen_ai.usage.input_tokens type: LONG - name: gen_ai.usage.output_tokens @@ -4137,6 +5351,7 @@ libraries: - name: oracle-ucp-11.2 description: The Oracle Universal Connection Pool (UCP) instrumentation generates connection pool metrics. + library_link: https://docs.oracle.com/database/121/JJUCP/ source_path: instrumentation/oracle-ucp-11.2 scope: name: io.opentelemetry.oracle-ucp-11.2 @@ -4205,6 +5420,7 @@ libraries: description: When the OSHI library is detected on the classpath, this instrumentation will use the system class loader to load classes from the oshi-core jar that are then used to generate system metrics. + library_link: https://github.com/oshi/oshi/ source_path: instrumentation/oshi scope: name: io.opentelemetry.oshi @@ -4922,7 +6138,8 @@ libraries: spark: - name: spark-2.3 description: | - This instrumentation does not emit telemetry on its own. Instead, it extracts the HTTP route and attaches it to SERVER spans and HTTP server metrics. + This instrumentation does not emit telemetry on its own. Instead, it extracts the HTTP route and attaches it to HTTP server spans and HTTP server metrics. + library_link: https://sparkjava.com/ source_path: instrumentation/spark-2.3 scope: name: io.opentelemetry.spark-2.3 @@ -4933,6 +6150,7 @@ libraries: - name: spring-batch-3.0 description: This instrumentation enables INTERNAL spans for jobs run by the Spring Batch framework. + library_link: https://spring.io/projects/spring-batch disabled_by_default: true source_path: instrumentation/spring/spring-batch-3.0 scope: @@ -4965,6 +6183,7 @@ libraries: - name: spring-boot-actuator-autoconfigure-2.0 description: | This instrumentation configures the OpenTelemetry Micrometer bridge to receive metrics from Spring Boot Actuator. It does not produce telemetry on its own. + library_link: https://spring.io/projects/spring-boot disabled_by_default: true source_path: instrumentation/spring/spring-boot-actuator-autoconfigure-2.0 scope: @@ -4984,12 +6203,14 @@ libraries: - Check for application.yml in the current working dir - Check for --spring.application.name program argument (not jvm arg) via ProcessHandle - Check for --spring.application.name program argument via sun.java.command system property + library_link: https://spring.io/projects/spring-boot source_path: instrumentation/spring/spring-boot-resources scope: name: io.opentelemetry.spring-boot-resources - name: spring-cloud-aws-3.0 description: | This instrumentation enhances tracing for Spring Cloud AWS. It augments the existing AWS SDK instrumentation by providing higher-level tracing for SQS operations, capturing details specific to Spring Cloud AWS SQS usage and linking them to the underlying AWS SDK traces. + library_link: https://spring.io/projects/spring-cloud-aws source_path: instrumentation/spring/spring-cloud-aws-3.0 minimum_java_version: 17 scope: @@ -5000,6 +6221,7 @@ libraries: - name: spring-cloud-gateway-2.0 description: | This instrumentation enhances tracing for Spring Cloud Gateway. It does not generate new telemetry on its own, but rather enriches existing traces produced by other instrumentations like Netty and Spring WebFlux with Spring Cloud Gateway-specific attributes. + library_link: https://github.com/spring-cloud/spring-cloud-gateway source_path: instrumentation/spring/spring-cloud-gateway/spring-cloud-gateway-2.0 scope: name: io.opentelemetry.spring-cloud-gateway-2.0 @@ -5015,6 +6237,7 @@ libraries: - name: spring-core-2.0 description: | This instrumentation ensures proper context propagation for asynchronous operations within Spring Core. It modifies how tasks are submitted and executed to ensure that spans created by other instrumentations are correctly linked across thread boundaries, rather than generating any new telemetry itself. + library_link: https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/core/package-summary.html source_path: instrumentation/spring/spring-core-2.0 minimum_java_version: 17 scope: @@ -5025,6 +6248,7 @@ libraries: - name: spring-data-1.8 description: | This instrumentation enhances tracing for Spring Data operations. It works in conjunction with other instrumentations, such as JDBC, to provide additional context and details for database interactions initiated through Spring Data. + library_link: https://spring.io/projects/spring-data source_path: instrumentation/spring/spring-data/spring-data-1.8 scope: name: io.opentelemetry.spring-data-1.8 @@ -5042,8 +6266,9 @@ libraries: - name: code.namespace type: STRING - name: spring-integration-4.1 - description: This instrumentation enables PRODUCER and CONSUMER spans for Spring - Integration. + description: This instrumentation enables producer and consumer messaging spans + for Spring Integration. + library_link: https://spring.io/projects/spring-integration source_path: instrumentation/spring/spring-integration-4.1 scope: name: io.opentelemetry.spring-integration-4.1 @@ -5084,8 +6309,9 @@ libraries: - name: messaging.operation type: STRING - name: spring-jms-2.0 - description: This instrumentation enables the generation of CONSUMER spans for - Spring JMS. + description: This instrumentation enables the generation of consumer messaging + spans for Spring JMS. + library_link: https://docs.spring.io/spring-framework/reference/integration/jms.html source_path: instrumentation/spring/spring-jms/spring-jms-2.0 scope: name: io.opentelemetry.spring-jms-2.0 @@ -5116,8 +6342,9 @@ libraries: - name: messaging.system type: STRING - name: spring-jms-6.0 - description: This instrumentation enables the generation of CONSUMER spans for - Spring JMS. + description: This instrumentation enables the generation of consumer messaging + spans for Spring JMS. + library_link: https://docs.spring.io/spring-framework/reference/integration/jms.html source_path: instrumentation/spring/spring-jms/spring-jms-6.0 minimum_java_version: 17 scope: @@ -5149,7 +6376,9 @@ libraries: - name: messaging.system type: STRING - name: spring-kafka-2.7 - description: This instrumentation enables CONSUMER spans for Spring Kafka listeners. + description: This instrumentation enables consumer messaging spans for Spring + Kafka listeners. + library_link: https://spring.io/projects/spring-kafka source_path: instrumentation/spring/spring-kafka-2.7 scope: name: io.opentelemetry.spring-kafka-2.7 @@ -5222,7 +6451,9 @@ libraries: - name: messaging.system type: STRING - name: spring-pulsar-1.0 - description: This instrumentation enables CONSUMER spans for Spring Pulsar listeners. + description: This instrumentation enables consumer messaging spans for Spring + Pulsar listeners. + library_link: https://spring.io/projects/spring-pulsar source_path: instrumentation/spring/spring-pulsar-1.0 minimum_java_version: 17 scope: @@ -5261,7 +6492,9 @@ libraries: - name: messaging.system type: STRING - name: spring-rabbit-1.0 - description: This instrumentation enables CONSUMER spans for Spring RabbitMQ listeners. + description: This instrumentation enables consumer messaging spans for Spring + RabbitMQ listeners. + library_link: https://spring.io/projects/spring-amqp source_path: instrumentation/spring/spring-rabbit-1.0 scope: name: io.opentelemetry.spring-rabbit-1.0 @@ -5287,8 +6520,9 @@ libraries: - name: messaging.system type: STRING - name: spring-rmi-4.0 - description: This instrumentation enables CLIENT and SERVER spans for Spring RMI - applications. + description: This instrumentation enables RPC client and RPC server spans for + Spring RMI applications. + library_link: https://docs.spring.io/spring-framework/docs/4.0.x/javadoc-api/org/springframework/remoting/rmi/package-summary.html source_path: instrumentation/spring/spring-rmi-4.0 scope: name: io.opentelemetry.spring-rmi-4.0 @@ -5316,6 +6550,7 @@ libraries: type: STRING - name: spring-scheduling-3.1 description: This instrumentation enables tracing for Spring Scheduling tasks. + library_link: https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/scheduling/package-summary.html source_path: instrumentation/spring/spring-scheduling-3.1 scope: name: io.opentelemetry.spring-scheduling-3.1 @@ -5351,6 +6586,7 @@ libraries: description: | This instrumentation does not emit any telemetry on its own. Instead, it captures enduser attributes, and is only enabled when at least one of the `enduser` configurations is enabled. NOTE: The `enduser` attributes have been deprecated and will be removed in 3.0+ of the java agent. + library_link: https://spring.io/projects/spring-security source_path: instrumentation/spring/spring-security-config-6.0 minimum_java_version: 17 scope: @@ -5390,6 +6626,7 @@ libraries: - name: spring-web-3.1 description: | This instrumentation provides a library integration that enables capturing HTTP client spans and metrics for Spring's RestTemplate. The agent integration enriches HTTP server spans and metrics with route information. + library_link: https://github.com/spring-projects/spring-framework source_path: instrumentation/spring/spring-web/spring-web-3.1 scope: name: io.opentelemetry.spring-web-3.1 @@ -5430,7 +6667,9 @@ libraries: - name: spring-web-6.0 description: This instrumentation enriches HTTP server spans and metrics with route information. + library_link: https://github.com/spring-projects/spring-framework source_path: instrumentation/spring/spring-web/spring-web-6.0 + minimum_java_version: 17 scope: name: io.opentelemetry.spring-web-6.0 target_versions: @@ -5439,6 +6678,7 @@ libraries: - name: spring-webflux-5.0 description: | This instrumentation enables HTTP client spans and metrics for Spring WebFlux 5.0. It also optionally enables experimental controller (INTERNAL) spans. + library_link: https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/reactive/package-summary.html source_path: instrumentation/spring/spring-webflux/spring-webflux-5.0 scope: name: io.opentelemetry.spring-webflux-5.0 @@ -5492,6 +6732,7 @@ libraries: - name: spring-webflux-5.3 description: | This instrumentation provides a library integration for the Spring WebFlux WebClient and Webflux server versions 5.3+ that enables HTTP client and server spans and metrics. + library_link: https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/reactive/package-summary.html source_path: instrumentation/spring/spring-webflux/spring-webflux-5.3 scope: name: io.opentelemetry.spring-webflux-5.3 @@ -5573,6 +6814,7 @@ libraries: - name: spring-webmvc-3.1 description: | This instrumentation enables optional Controller and View (INTERNAL) spans for Spring WebMVC 3.1+. + library_link: https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/servlet/mvc/package-summary.html source_path: instrumentation/spring/spring-webmvc/spring-webmvc-3.1 scope: name: io.opentelemetry.spring-webmvc-3.1 @@ -5615,6 +6857,7 @@ libraries: - name: spring-webmvc-5.3 description: | This instrumentation provides a library integration for Spring WebMVC controllers, that enables the creation of HTTP server spans and metrics for requests processed by the Spring servlet container. + library_link: https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/servlet/mvc/package-summary.html source_path: instrumentation/spring/spring-webmvc/spring-webmvc-5.3 scope: name: io.opentelemetry.spring-webmvc-5.3 @@ -5672,6 +6915,7 @@ libraries: - name: spring-webmvc-6.0 description: | This instrumentation enables optional Controller and View (INTERNAL) spans for Spring WebMVC 6.0+. + library_link: https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/servlet/mvc/package-summary.html source_path: instrumentation/spring/spring-webmvc/spring-webmvc-6.0 minimum_java_version: 17 scope: @@ -5713,6 +6957,7 @@ libraries: - name: spring-webmvc.view.type type: STRING - name: spring-ws-2.0 + library_link: https://spring.io/projects/spring-ws disabled_by_default: true source_path: instrumentation/spring/spring-ws-2.0 scope: @@ -6236,6 +7481,7 @@ libraries: - name: vibur-dbcp-11.0 description: Instrumentation for the vibur-dbcp library, which provides connection pool metrics. + library_link: https://www.vibur.org/ source_path: instrumentation/vibur-dbcp-11.0 scope: name: io.opentelemetry.vibur-dbcp-11.0 diff --git a/docs/supported-libraries.md b/docs/supported-libraries.md index cc87ac34c04e..7bd9a6d37f74 100644 --- a/docs/supported-libraries.md +++ b/docs/supported-libraries.md @@ -47,11 +47,12 @@ These are the supported libraries and frameworks: | [Armeria gRPC](https://armeria.dev) | 1.14+ | | [RPC Client Spans], [RPC Client Metrics], [RPC Server Spans], [RPC Server Metrics] | | [AsyncHttpClient](https://github.com/AsyncHttpClient/async-http-client) | 1.9+ | N/A | [HTTP Client Spans], [HTTP Client Metrics] | | [Avaje Jex](https://avaje.io/jex/) | 3.0+ | N/A | Provides `http.route` [2] | -| [AWS Lambda](https://docs.aws.amazon.com/lambda/latest/dg/java-handler.html) | 1.0+ | [opentelemetry-aws-lambda-core-1.0](../instrumentation/aws-lambda/aws-lambda-core-1.0/library),
[opentelemetry-aws-lambda-events-2.2](../instrumentation/aws-lambda/aws-lambda-events-2.2/library) | [FaaS Server Spans] | +| [AWS Lambda](https://docs.aws.amazon.com/lambda/latest/dg/java-handler.html) | 1.0+ | [opentelemetry-aws-lambda-core-1.0](../instrumentation/aws-lambda/aws-lambda-core-1.0/library),
[opentelemetry-aws-lambda-events-3.11](../instrumentation/aws-lambda/aws-lambda-events-3.11/library) | [FaaS Server Spans] | | [AWS SDK](https://aws.amazon.com/sdk-for-java/) | 1.11 - 1.12.583,
2.2+ | [opentelemetry-aws-sdk-1.11](../instrumentation/aws-sdk/aws-sdk-1.11/library),
[opentelemetry-aws-sdk-1.11-autoconfigure](../instrumentation/aws-sdk/aws-sdk-1.11/library-autoconfigure),
[opentelemetry-aws-sdk-2.2](../instrumentation/aws-sdk/aws-sdk-2.2/library),
[opentelemetry-aws-sdk-2.2-autoconfigure](../instrumentation/aws-sdk/aws-sdk-2.2/library-autoconfigure) | [Messaging Spans], [Database Client Spans], [Database Client Metrics] [6], [HTTP Client Spans], [GenAI Client Spans], [GenAI Client Metrics] | | [Azure Core](https://docs.microsoft.com/en-us/java/api/overview/azure/core-readme) | 1.14+ | N/A | Context propagation | | [Cassandra Driver](https://github.com/datastax/java-driver) | 3.0+ | [opentelemetry-cassandra-4.4](../instrumentation/cassandra/cassandra-4.4/library) | [Database Client Spans], [Database Client Metrics] [6] | -| [Clickhouse Client](https://github.com/ClickHouse/clickhouse-java) | 0.5+ | N/A | [Database Client Spans], [Database Client Metrics] [6] | +| [Clickhouse Client V1](https://github.com/ClickHouse/clickhouse-java) | 0.5+ | N/A | [Database Client Spans], [Database Client Metrics] [6] | +| [Clickhouse Client V2](https://github.com/ClickHouse/clickhouse-java) | 0.8+ | N/A | [Database Client Spans], [Database Client Metrics] [6] | | [Couchbase Client](https://github.com/couchbase/couchbase-java-client) | 2.0 - 2.7.x | N/A | [Database Client Spans], [Database Client Metrics] [6] | | [Couchbase Client](https://github.com/couchbase/couchbase-java-client) | 3.1+ | N/A | [Database Client Spans] | | [c3p0](https://github.com/swaldman/c3p0) | 0.9.2+ | [opentelemetry-c3p0-0.9](../instrumentation/c3p0-0.9/library) | [Database Pool Metrics] | @@ -73,7 +74,7 @@ These are the supported libraries and frameworks: | [GraphQL Java](https://www.graphql-java.com/) | 12.0+ | [opentelemetry-graphql-java-12.0](../instrumentation/graphql-java/graphql-java-12.0/library),
[opentelemetry-graphql-java-20.0](../instrumentation/graphql-java/graphql-java-20.0/library) | [GraphQL Server Spans] | | [gRPC](https://github.com/grpc/grpc-java) | 1.6+ | [opentelemetry-grpc-1.6](../instrumentation/grpc-1.6/library) | [RPC Client Spans], [RPC Client Metrics], [RPC Server Spans], [RPC Server Metrics] | | [Guava ListenableFuture](https://guava.dev/releases/snapshot/api/docs/com/google/common/util/concurrent/ListenableFuture.html) | 10.0+ | [opentelemetry-guava-10.0](../instrumentation/guava-10.0/library) | Context propagation | -| [GWT](http://www.gwtproject.org/) | 2.0+ | N/A | [RPC Server Spans] | +| [GWT](https://www.gwtproject.org/) | 2.0+ | N/A | [RPC Server Spans] | | [Hibernate](https://github.com/hibernate/hibernate-orm) | 3.3+ | N/A | none | | [Hibernate Reactive](https://hibernate.org/reactive) | 1.0+ | N/A | none | | [HikariCP](https://github.com/brettwooldridge/HikariCP) | 3.0+ | [opentelemetry-hikaricp-3.0](../instrumentation/hikaricp-3.0/library) | [Database Pool Metrics] | @@ -101,7 +102,7 @@ These are the supported libraries and frameworks: | [Lettuce](https://github.com/lettuce-io/lettuce-core) | 4.0+ | [opentelemetry-lettuce-5.1](../instrumentation/lettuce/lettuce-5.1/library) | [Database Client Spans], [Database Client Metrics] [6] | | [Log4j 1](https://logging.apache.org/log4j/1.2/) | 1.2+ | N/A | none | | [Log4j 2](https://logging.apache.org/log4j/2.x/) | 2.11+ | [opentelemetry-log4j-appender-2.17](../instrumentation/log4j/log4j-appender-2.17/library),
[opentelemetry-log4j-context-data-2.17-autoconfigure](../instrumentation/log4j/log4j-context-data/log4j-context-data-2.17/library-autoconfigure) | none | -| [Logback](http://logback.qos.ch/) | 1.0+ | [opentelemetry-logback-appender-1.0](../instrumentation/logback/logback-appender-1.0/library),
[opentelemetry-logback-mdc-1.0](../instrumentation/logback/logback-mdc-1.0/library) | none | +| [Logback](https://logback.qos.ch/) | 1.0+ | [opentelemetry-logback-appender-1.0](../instrumentation/logback/logback-appender-1.0/library),
[opentelemetry-logback-mdc-1.0](../instrumentation/logback/logback-mdc-1.0/library) | none | | [Micrometer](https://micrometer.io/) | 1.5+ (disabled by default) | [opentelemetry-micrometer-1.5](../instrumentation/micrometer/micrometer-1.5/library) | none | | [MongoDB Driver](https://mongodb.github.io/mongo-java-driver/) | 3.1+ | [opentelemetry-mongo-3.1](../instrumentation/mongo/mongo-3.1/library) | [Database Client Spans], [Database Client Metrics] [6] | | [MyBatis](https://mybatis.org/mybatis-3/) | 3.2+ | N/A | none | @@ -124,7 +125,6 @@ These are the supported libraries and frameworks: | [Rediscala](https://github.com/etaty/rediscala) | 1.8+ | N/A | [Database Client Spans], [Database Client Metrics] [6] | | [Redisson](https://github.com/redisson/redisson) | 3.0+ | N/A | [Database Client Spans], [Database Client Metrics] [6] | | [RESTEasy](https://resteasy.dev/) | 3.0+ (not including 6.0+ yet) | N/A | Provides `http.route` [2], Controller Spans [3] | -| [RESTEasy](https://resteasy.dev/) | 3.0+ (not including 6.0+ yet) | N/A | Provides `http.route` [2], Controller Spans [3] | | [Restlet](https://restlet.github.io/) | 1.0+ | [opentelemetry-restlet-1.1](../instrumentation/restlet/restlet-1.1/library),
[opentelemetry-restlet-2.0](../instrumentation/restlet/restlet-2.0/library) | [HTTP Server Spans], [HTTP Server Metrics] | | [RMI](https://docs.oracle.com/en/java/javase/11/docs/api/java.rmi/java/rmi/package-summary.html) | Java 8+ | | [RPC Client Spans], [RPC Server Spans] | | [RxJava](https://github.com/ReactiveX/RxJava) | 1.0+ | [opentelemetry-rxjava-1.0](../instrumentation/rxjava/rxjava-1.0/library),
[opentelemetry-rxjava-2.0](../instrumentation/rxjava/rxjava-2.0/library),
[opentelemetry-rxjava-3.0](../instrumentation/rxjava/rxjava-3.0/library),
[opentelemetry-rxjava-3.1.1](../instrumentation/rxjava/rxjava-3.1.1/library) | Context propagation | @@ -209,9 +209,9 @@ These are the application servers that the smoke tests are run against: | [Open Liberty](https://openliberty.io/) | 23.0.0.12 | OpenJDK 8, 11, 17, 20
OpenJ9 8, 11, 17, 20 | [`ubuntu-latest`], [`windows-latest`] | | [Payara](https://www.payara.fish/) | 5.2020.6, 5.2021.8 | OpenJDK 8, 11
OpenJ9 8, 11 | [`ubuntu-latest`], [`windows-latest`] | | [Payara](https://www.payara.fish/) | 6.2023.12 | OpenJDK 11, 17
OpenJ9 11, 17, 21, 23 | [`ubuntu-latest`], [`windows-latest`] | -| [Tomcat](http://tomcat.apache.org/) | 7.0.109 | OpenJDK 8
OpenJ9 8 | [`ubuntu-latest`], [`windows-latest`] | -| [Tomcat](http://tomcat.apache.org/) | 8.5.98, 9.0.85 | OpenJDK 8, 11, 17, 21, 23
OpenJ9 8, 11, 17, 21, 23 | [`ubuntu-latest`], [`windows-latest`] | -| [Tomcat](http://tomcat.apache.org/) | 10.1.18 | OpenJDK 11, 17, 21, 23
OpenJ9 11, 17, 21, 23 | [`ubuntu-latest`], [`windows-latest`] | +| [Tomcat](https://tomcat.apache.org/) | 7.0.109 | OpenJDK 8
OpenJ9 8 | [`ubuntu-latest`], [`windows-latest`] | +| [Tomcat](https://tomcat.apache.org/) | 8.5.98, 9.0.85 | OpenJDK 8, 11, 17, 21, 23
OpenJ9 8, 11, 17, 21, 23 | [`ubuntu-latest`], [`windows-latest`] | +| [Tomcat](https://tomcat.apache.org/) | 10.1.18 | OpenJDK 11, 17, 21, 23
OpenJ9 11, 17, 21, 23 | [`ubuntu-latest`], [`windows-latest`] | | [TomEE](https://tomee.apache.org/) | 7.0.9, 7.1.4 | OpenJDK 8
OpenJ9 8 | [`ubuntu-latest`], [`windows-latest`] | | [TomEE](https://tomee.apache.org/) | 8.0.16 | OpenJDK 8, 11, 17, 21, 23
OpenJ9 8, 11, 17, 21, 23 | [`ubuntu-latest`], [`windows-latest`] | | [TomEE](https://tomee.apache.org/) | 9.1.2 | OpenJDK 11, 17, 21, 23
OpenJ9 11, 17, 21, 23 | [`ubuntu-latest`], [`windows-latest`] | diff --git a/examples/distro/agent/build.gradle b/examples/distro/agent/build.gradle index b04e087c3ff6..4e493f757b81 100644 --- a/examples/distro/agent/build.gradle +++ b/examples/distro/agent/build.gradle @@ -41,8 +41,7 @@ CopySpec isolateClasses(Iterable jars) { from(zipTree(it)) { into("inst") rename("^(.*)\\.class\$", "\$1.classdata") - // Rename LICENSE file since it clashes with license dir on non-case sensitive FSs (i.e. Mac) - rename("^LICENSE\$", "LICENSE.renamed") + exclude("^LICENSE\$") exclude("META-INF/INDEX.LIST") exclude("META-INF/*.DSA") exclude("META-INF/*.SF") @@ -64,11 +63,14 @@ tasks { task relocateJavaagentLibs(type: ShadowJar) { configurations = [project.configurations.javaagentLibs] - duplicatesStrategy = DuplicatesStrategy.FAIL - archiveFileName.set("javaagentLibs-relocated.jar") + duplicatesStrategy = DuplicatesStrategy.FAIL mergeServiceFiles() + // mergeServiceFiles requires that duplicate strategy is set to include + filesMatching("META-INF/services/**") { + duplicatesStrategy = DuplicatesStrategy.INCLUDE + } exclude("**/module-info.class") relocatePackages(it) @@ -106,10 +108,12 @@ tasks { archiveClassifier.set("all") duplicatesStrategy = DuplicatesStrategy.EXCLUDE - - mergeServiceFiles { - include("inst/META-INF/services/*") + mergeServiceFiles("inst/META-INF/services") + // mergeServiceFiles requires that duplicate strategy is set to include + filesMatching("inst/META-INF/services/**") { + duplicatesStrategy = DuplicatesStrategy.INCLUDE } + exclude("**/module-info.class") relocatePackages(it) diff --git a/examples/distro/build.gradle b/examples/distro/build.gradle index 2fc63e67766a..cbd5d561abc5 100644 --- a/examples/distro/build.gradle +++ b/examples/distro/build.gradle @@ -13,8 +13,8 @@ buildscript { } dependencies { classpath "com.diffplug.spotless:spotless-plugin-gradle:7.2.1" - classpath "com.gradleup.shadow:shadow-gradle-plugin:8.3.9" - classpath "io.opentelemetry.instrumentation:gradle-plugins:2.19.0-alpha-SNAPSHOT" + classpath "com.gradleup.shadow:shadow-gradle-plugin:9.1.0" + classpath "io.opentelemetry.instrumentation:gradle-plugins:2.20.0-alpha" } } @@ -27,11 +27,11 @@ subprojects { ext { versions = [ // this line is managed by .github/scripts/update-sdk-version.sh - opentelemetrySdk : "1.53.0", + opentelemetrySdk : "1.54.0", // these lines are managed by .github/scripts/update-version.sh - opentelemetryJavaagent : "2.19.0-SNAPSHOT", - opentelemetryJavaagentAlpha: "2.19.0-alpha-SNAPSHOT", + opentelemetryJavaagent : "2.20.0", + opentelemetryJavaagentAlpha: "2.20.0-alpha", autoservice : "1.1.1" ] diff --git a/examples/distro/gradle/instrumentation.gradle b/examples/distro/gradle/instrumentation.gradle index 43337328ad66..ec72ce25daa4 100644 --- a/examples/distro/gradle/instrumentation.gradle +++ b/examples/distro/gradle/instrumentation.gradle @@ -35,7 +35,12 @@ dependencies { shadowJar { configurations = [project.configurations.runtimeClasspath, project.configurations.testInstrumentation] + mergeServiceFiles() + // mergeServiceFiles requires that duplicate strategy is set to include + filesMatching("inst/META-INF/services/**") { + duplicatesStrategy = DuplicatesStrategy.INCLUDE + } archiveFileName = 'agent-testing.jar' @@ -46,13 +51,16 @@ tasks.withType(Test).configureEach { inputs.file(shadowJar.archiveFile) jvmArgs "-Dotel.javaagent.debug=true" - jvmArgs "-javaagent:${configurations.testAgent.files.first().absolutePath}" jvmArgs "-Dotel.javaagent.experimental.initializer.jar=${shadowJar.archiveFile.get().asFile.absolutePath}" jvmArgs "-Dotel.javaagent.testing.additional-library-ignores.enabled=false" jvmArgs "-Dotel.javaagent.testing.fail-on-context-leak=true" // prevent sporadic gradle deadlocks, see SafeLogger for more details jvmArgs "-Dotel.javaagent.testing.transform-safe-logging.enabled=true" + jvmArgumentProviders.add(new JavaagentProvider(project.providers.provider { + configurations.testAgent.files.first() + })) + dependsOn shadowJar dependsOn configurations.testAgent.buildDependencies @@ -65,3 +73,18 @@ tasks.withType(Test).configureEach { return true } } + +class JavaagentProvider implements CommandLineArgumentProvider { + @InputFile + @PathSensitive(PathSensitivity.RELATIVE) + Provider agentJar + + JavaagentProvider(Provider agentJar) { + this.agentJar = agentJar + } + + @Override + Iterable asArguments() { + return ["-javaagent:${agentJar.get().absolutePath}"] + } +} diff --git a/examples/distro/gradle/wrapper/gradle-wrapper.jar b/examples/distro/gradle/wrapper/gradle-wrapper.jar index 1b33c55baabb..8bdaf60c75ab 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 78cb6e16a49f..3e781fbad9c7 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=bd71102213493060956ec229d946beee57158dbd89d0e62b91bca0fa2c5f3531 -distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip +distributionSha256Sum=8fad3d78296ca518113f3d29016617c7f9367dc005f932bd9d93bf45ba46072b +distributionUrl=https\://services.gradle.org/distributions/gradle-9.0.0-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/examples/distro/gradlew b/examples/distro/gradlew index 23d15a936707..ef07e0162b18 100755 --- a/examples/distro/gradlew +++ b/examples/distro/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/examples/distro/smoke-tests/build.gradle b/examples/distro/smoke-tests/build.gradle index 6f6488de3cd1..30a61d696421 100644 --- a/examples/distro/smoke-tests/build.gradle +++ b/examples/distro/smoke-tests/build.gradle @@ -4,10 +4,10 @@ plugins { dependencies { testImplementation("org.testcontainers:testcontainers:1.21.3") - testImplementation("com.fasterxml.jackson.core:jackson-databind:2.19.2") - testImplementation("com.google.protobuf:protobuf-java-util:4.32.0") + testImplementation("com.fasterxml.jackson.core:jackson-databind:2.20.0") + testImplementation("com.google.protobuf:protobuf-java-util:4.32.1") testImplementation("com.squareup.okhttp3:okhttp:5.1.0") - testImplementation("io.opentelemetry.proto:opentelemetry-proto:1.7.0-alpha") + testImplementation("io.opentelemetry.proto:opentelemetry-proto:1.8.0-alpha") testImplementation("io.opentelemetry:opentelemetry-api") testImplementation("ch.qos.logback:logback-classic:1.5.18") diff --git a/examples/distro/testing/agent-for-testing/build.gradle b/examples/distro/testing/agent-for-testing/build.gradle index 9c9ad1af0240..fcf243ef22d5 100644 --- a/examples/distro/testing/agent-for-testing/build.gradle +++ b/examples/distro/testing/agent-for-testing/build.gradle @@ -59,11 +59,15 @@ tasks { task relocateJavaagentLibs(type: ShadowJar) { configurations = [project.configurations.javaagentLibs] - duplicatesStrategy = DuplicatesStrategy.FAIL - archiveFileName.set("javaagentLibs-relocated.jar") + duplicatesStrategy = DuplicatesStrategy.FAIL mergeServiceFiles() + // mergeServiceFiles requires that duplicate strategy is set to include + filesMatching("META-INF/services/**") { + duplicatesStrategy = DuplicatesStrategy.INCLUDE + } + exclude("**/module-info.class") relocatePackages(it) @@ -101,10 +105,12 @@ tasks { archiveClassifier.set("") duplicatesStrategy = DuplicatesStrategy.EXCLUDE - - mergeServiceFiles { - include("inst/META-INF/services/*") + mergeServiceFiles("inst/META-INF/services") + // mergeServiceFiles requires that duplicate strategy is set to include + filesMatching("inst/META-INF/services/**") { + duplicatesStrategy = DuplicatesStrategy.INCLUDE } + exclude("**/module-info.class") relocatePackages(it) diff --git a/examples/extension/build.gradle b/examples/extension/build.gradle index ce856c2b2abb..42e2ebd09b7a 100644 --- a/examples/extension/build.gradle +++ b/examples/extension/build.gradle @@ -10,11 +10,11 @@ plugins { into a single jar. See https://imperceptiblethoughts.com/shadow/ for more details about Shadow plugin. */ - id "com.gradleup.shadow" version "8.3.9" + id "com.gradleup.shadow" version "9.1.0" id "com.diffplug.spotless" version "7.2.1" - id "io.opentelemetry.instrumentation.muzzle-generation" version "2.19.0-alpha-SNAPSHOT" - id "io.opentelemetry.instrumentation.muzzle-check" version "2.19.0-alpha-SNAPSHOT" + id "io.opentelemetry.instrumentation.muzzle-generation" version "2.20.0-alpha" + id "io.opentelemetry.instrumentation.muzzle-check" version "2.20.0-alpha" } group 'io.opentelemetry.example' @@ -23,11 +23,11 @@ version '1.0' ext { versions = [ // this line is managed by .github/scripts/update-sdk-version.sh - opentelemetrySdk : "1.53.0", + opentelemetrySdk : "1.54.0", // these lines are managed by .github/scripts/update-version.sh - opentelemetryJavaagent : "2.19.0-SNAPSHOT", - opentelemetryJavaagentAlpha: "2.19.0-alpha-SNAPSHOT" + opentelemetryJavaagent : "2.20.0", + opentelemetryJavaagentAlpha: "2.20.0-alpha" ] deps = [ @@ -99,11 +99,11 @@ dependencies { //All dependencies below are only for tests testImplementation("org.testcontainers:testcontainers:1.21.3") - testImplementation("com.fasterxml.jackson.core:jackson-databind:2.19.2") - testImplementation("com.google.protobuf:protobuf-java-util:4.32.0") + testImplementation("com.fasterxml.jackson.core:jackson-databind:2.20.0") + testImplementation("com.google.protobuf:protobuf-java-util:4.32.1") testImplementation("com.squareup.okhttp3:okhttp:5.1.0") testImplementation("io.opentelemetry:opentelemetry-api") - testImplementation("io.opentelemetry.proto:opentelemetry-proto:1.7.0-alpha") + testImplementation("io.opentelemetry.proto:opentelemetry-proto:1.8.0-alpha") testImplementation(enforcedPlatform("org.junit:junit-bom:5.13.4")) testImplementation("org.junit.jupiter:junit-jupiter-api") diff --git a/examples/extension/gradle/wrapper/gradle-wrapper.jar b/examples/extension/gradle/wrapper/gradle-wrapper.jar index 1b33c55baabb..8bdaf60c75ab 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 78cb6e16a49f..3e781fbad9c7 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=bd71102213493060956ec229d946beee57158dbd89d0e62b91bca0fa2c5f3531 -distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip +distributionSha256Sum=8fad3d78296ca518113f3d29016617c7f9367dc005f932bd9d93bf45ba46072b +distributionUrl=https\://services.gradle.org/distributions/gradle-9.0.0-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/examples/extension/gradlew b/examples/extension/gradlew index 23d15a936707..ef07e0162b18 100755 --- a/examples/extension/gradlew +++ b/examples/extension/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/gradle-plugins/build.gradle.kts b/gradle-plugins/build.gradle.kts index 1803dd14fed4..911b496369c3 100644 --- a/gradle-plugins/build.gradle.kts +++ b/gradle-plugins/build.gradle.kts @@ -25,7 +25,7 @@ configurations.named("compileOnly") { extendsFrom(bbGradlePlugin) } -val byteBuddyVersion = "1.17.6" +val byteBuddyVersion = "1.17.7" val aetherVersion = "1.1.0" dependencies { @@ -40,7 +40,7 @@ dependencies { implementation("org.eclipse.aether:aether-transport-http:${aetherVersion}") implementation("org.apache.maven:maven-aether-provider:3.3.9") - implementation("com.gradleup.shadow:shadow-gradle-plugin:8.3.9") + implementation("com.gradleup.shadow:shadow-gradle-plugin:9.1.0") testImplementation("org.assertj:assertj-core:3.27.4") diff --git a/gradle-plugins/gradle/wrapper/gradle-wrapper.properties b/gradle-plugins/gradle/wrapper/gradle-wrapper.properties index 78cb6e16a49f..3e781fbad9c7 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=bd71102213493060956ec229d946beee57158dbd89d0e62b91bca0fa2c5f3531 -distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip +distributionSha256Sum=8fad3d78296ca518113f3d29016617c7f9367dc005f932bd9d93bf45ba46072b +distributionUrl=https\://services.gradle.org/distributions/gradle-9.0.0-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 503b824459fa..e28bb5ec96de 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.3.1" + id("com.gradle.plugin-publish") version "2.0.0" id("io.github.gradle-nexus.publish-plugin") version "2.0.0" } } diff --git a/gradle-plugins/src/main/kotlin/io.opentelemetry.instrumentation.muzzle-check.gradle.kts b/gradle-plugins/src/main/kotlin/io.opentelemetry.instrumentation.muzzle-check.gradle.kts index a906434ef600..e93514e28d15 100644 --- a/gradle-plugins/src/main/kotlin/io.opentelemetry.instrumentation.muzzle-check.gradle.kts +++ b/gradle-plugins/src/main/kotlin/io.opentelemetry.instrumentation.muzzle-check.gradle.kts @@ -47,7 +47,7 @@ val muzzleBootstrap: Configuration by configurations.creating { } val shadowModule by tasks.registering(ShadowJar::class) { - from(tasks.jar) + from(zipTree(tasks.jar.get().archiveFile)) configurations = listOf(project.configurations.runtimeClasspath.get()) @@ -75,9 +75,17 @@ val shadowMuzzleBootstrap by tasks.registering(ShadowJar::class) { // avoid publishing io.opentelemetry.instrumentation.javaagent-shadowing publicly tasks.withType().configureEach { mergeServiceFiles() + // mergeServiceFiles requires that duplicate strategy is set to include + filesMatching("META-INF/services/**") { + duplicatesStrategy = DuplicatesStrategy.INCLUDE + } // Merge any AWS SDK service files that may be present (too bad they didn't just use normal // service loader...) mergeServiceFiles("software/amazon/awssdk/global/handlers") + // mergeServiceFiles requires that duplicate strategy is set to include + filesMatching("software/amazon/awssdk/global/handlers/**") { + duplicatesStrategy = DuplicatesStrategy.INCLUDE + } exclude("**/module-info.class") diff --git a/gradle-plugins/src/main/kotlin/io/opentelemetry/javaagent/muzzle/MuzzleDirective.kt b/gradle-plugins/src/main/kotlin/io/opentelemetry/javaagent/muzzle/MuzzleDirective.kt index 155f8a1a7b8d..d2754ed5c41e 100644 --- a/gradle-plugins/src/main/kotlin/io/opentelemetry/javaagent/muzzle/MuzzleDirective.kt +++ b/gradle-plugins/src/main/kotlin/io/opentelemetry/javaagent/muzzle/MuzzleDirective.kt @@ -69,7 +69,7 @@ abstract class MuzzleDirective { excludedInstrumentationNames.add(excludeString) } - fun skip(vararg version: String?) { + fun skip(vararg version: String) { skipVersions.addAll(*version) } diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 1b33c55baabb..8bdaf60c75ab 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 78cb6e16a49f..3e781fbad9c7 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=bd71102213493060956ec229d946beee57158dbd89d0e62b91bca0fa2c5f3531 -distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip +distributionSha256Sum=8fad3d78296ca518113f3d29016617c7f9367dc005f932bd9d93bf45ba46072b +distributionUrl=https\://services.gradle.org/distributions/gradle-9.0.0-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew index 23d15a936707..ef07e0162b18 100755 --- a/gradlew +++ b/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/instrumentation-annotations/build.gradle.kts b/instrumentation-annotations/build.gradle.kts index 321ea11d7f85..7a0e2dac5de6 100644 --- a/instrumentation-annotations/build.gradle.kts +++ b/instrumentation-annotations/build.gradle.kts @@ -11,3 +11,10 @@ group = "io.opentelemetry.instrumentation" dependencies { api("io.opentelemetry:opentelemetry-api") } + +tasks.test { + // This module does not have tests, but has example classes in the test directory. Gradle 9 fails + // the build when there are source files in the test directory but no tests to run so we disable + // the test task. + enabled = false +} diff --git a/instrumentation-api-incubator/build.gradle.kts b/instrumentation-api-incubator/build.gradle.kts index 707fd2217c1b..c25f6aea378d 100644 --- a/instrumentation-api-incubator/build.gradle.kts +++ b/instrumentation-api-incubator/build.gradle.kts @@ -42,15 +42,18 @@ tasks { } val testStableSemconv by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath jvmArgs("-Dotel.semconv-stability.opt-in=database,code") } val testBothSemconv by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath jvmArgs("-Dotel.semconv-stability.opt-in=database/dup,code/dup") } check { - dependsOn(testStableSemconv) - dependsOn(testBothSemconv) + dependsOn(testStableSemconv, testBothSemconv) } } 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 7889308567ea..817f16e11f10 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 @@ -8,11 +8,13 @@ import com.google.errorprone.annotations.CanIgnoreReturnValue; import io.opentelemetry.api.OpenTelemetry; import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.context.Context; import io.opentelemetry.context.propagation.TextMapSetter; import io.opentelemetry.instrumentation.api.incubator.config.internal.CommonConfig; import io.opentelemetry.instrumentation.api.incubator.semconv.http.HttpClientExperimentalAttributesGetter; import io.opentelemetry.instrumentation.api.incubator.semconv.http.HttpClientExperimentalMetrics; import io.opentelemetry.instrumentation.api.incubator.semconv.http.HttpClientPeerServiceAttributesExtractor; +import io.opentelemetry.instrumentation.api.incubator.semconv.http.HttpClientUrlTemplate; import io.opentelemetry.instrumentation.api.incubator.semconv.http.HttpExperimentalAttributesExtractor; import io.opentelemetry.instrumentation.api.incubator.semconv.net.PeerServiceResolver; import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor; @@ -82,7 +84,7 @@ public static DefaultHttpClientInstrumenterBuilder attributesGetter) { - return new DefaultHttpClientInstrumenterBuilder( + return new DefaultHttpClientInstrumenterBuilder<>( instrumentationName, openTelemetry, attributesGetter, null); } @@ -91,7 +93,7 @@ public static DefaultHttpClientInstrumenterBuilder attributesGetter, TextMapSetter headerSetter) { - return new DefaultHttpClientInstrumenterBuilder( + return new DefaultHttpClientInstrumenterBuilder<>( instrumentationName, openTelemetry, attributesGetter, @@ -219,12 +221,20 @@ public DefaultHttpClientInstrumenterBuilder setBuilderCustomi } public Instrumenter build() { - if (emitExperimentalHttpClientTelemetry - && attributesGetter instanceof HttpClientExperimentalAttributesGetter) { - HttpClientExperimentalAttributesGetter experimentalAttributesGetter = - (HttpClientExperimentalAttributesGetter) attributesGetter; + if (emitExperimentalHttpClientTelemetry) { + Function urlTemplateExtractorFunction = unused -> null; + if (attributesGetter instanceof HttpClientExperimentalAttributesGetter) { + HttpClientExperimentalAttributesGetter experimentalAttributesGetter = + (HttpClientExperimentalAttributesGetter) attributesGetter; + urlTemplateExtractorFunction = experimentalAttributesGetter::getUrlTemplate; + } + Function urlTemplateExtractor = urlTemplateExtractorFunction; Experimental.setUrlTemplateExtractor( - httpSpanNameExtractorBuilder, experimentalAttributesGetter::getUrlTemplate); + httpSpanNameExtractorBuilder, + request -> { + String urlTemplate = HttpClientUrlTemplate.get(Context.current()); + return urlTemplate != null ? urlTemplate : urlTemplateExtractor.apply(request); + }); } SpanNameExtractor spanNameExtractor = spanNameExtractorTransformer.apply(httpSpanNameExtractorBuilder.build()); diff --git a/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/DbClientCommonAttributesGetter.java b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/DbClientCommonAttributesGetter.java index b135625bd970..bda95d5c69a7 100644 --- a/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/DbClientCommonAttributesGetter.java +++ b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/DbClientCommonAttributesGetter.java @@ -10,17 +10,7 @@ /** An interface for getting attributes common to database clients. */ public interface DbClientCommonAttributesGetter { - @Deprecated - @Nullable - default String getSystem(REQUEST request) { - return null; - } - - // TODO: make this required to implement - @Nullable - default String getDbSystem(REQUEST request) { - return getSystem(request); - } + String getDbSystem(REQUEST request); @Deprecated @Nullable @@ -28,20 +18,8 @@ default String getUser(REQUEST request) { return null; } - /** - * @deprecated Use {@link #getDbNamespace(Object)} instead. - */ - @Deprecated - @Nullable - default String getName(REQUEST request) { - return null; - } - - // TODO: make this required to implement @Nullable - default String getDbNamespace(REQUEST request) { - return getName(request); - } + String getDbNamespace(REQUEST request); @Deprecated @Nullable diff --git a/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/SqlClientAttributesExtractor.java b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/SqlClientAttributesExtractor.java index 4cb1ae7fc8cc..b30a61505176 100644 --- a/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/SqlClientAttributesExtractor.java +++ b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/SqlClientAttributesExtractor.java @@ -27,8 +27,8 @@ * *

It sets the same set of attributes as {@link DbClientAttributesExtractor} plus an additional * db.sql.table attribute. The raw SQL statements returned by the {@link - * SqlClientAttributesGetter#getRawQueryText(Object)} method are sanitized before use, all statement - * parameters are removed. + * SqlClientAttributesGetter#getRawQueryTexts(Object)} method are sanitized before use, all + * statement parameters are removed. */ public final class SqlClientAttributesExtractor extends DbClientCommonAttributesExtractor< diff --git a/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/SqlClientAttributesGetter.java b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/SqlClientAttributesGetter.java index bdf64e638a1c..3c9af7d35449 100644 --- a/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/SqlClientAttributesGetter.java +++ b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/SqlClientAttributesGetter.java @@ -5,13 +5,9 @@ package io.opentelemetry.instrumentation.api.incubator.semconv.db; -import static java.util.Collections.emptySet; -import static java.util.Collections.singleton; - import java.util.Collection; import java.util.Collections; import java.util.Map; -import javax.annotation.Nullable; /** * An interface for getting SQL database client attributes. @@ -27,30 +23,6 @@ public interface SqlClientAttributesGetter extends DbClientCommonAttributesGetter { - /** - * Get the raw SQL statement. The value returned by this method is later sanitized by the {@link - * SqlClientAttributesExtractor} before being set as span attribute. - * - * @deprecated Use {@link #getRawQueryText(Object)} instead. - */ - @Deprecated - @Nullable - default String getRawStatement(REQUEST request) { - return null; - } - - /** - * Get the raw SQL query text. The value returned by this method is later sanitized by the {@link - * SqlClientAttributesExtractor} before being set as span attribute. - * - * @deprecated Use {@link #getRawQueryTexts(Object)} instead. - */ - @Deprecated - @Nullable - default String getRawQueryText(REQUEST request) { - return getRawStatement(request); - } - /** * Get the raw SQL query texts. The values returned by this method is later sanitized by the * {@link SqlClientAttributesExtractor} before being set as span attribute. @@ -58,11 +30,7 @@ default String getRawQueryText(REQUEST request) { *

If {@code request} is not a batch query, then this method should return a collection with a * single element. */ - // TODO: make this required to implement - default Collection getRawQueryTexts(REQUEST request) { - String rawQueryText = getRawQueryText(request); - return rawQueryText == null ? emptySet() : singleton(rawQueryText); - } + Collection getRawQueryTexts(REQUEST request); // TODO: make this required to implement default Long getBatchSize(REQUEST request) { diff --git a/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/genai/GenAiAttributesExtractor.java b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/genai/GenAiAttributesExtractor.java index 091cd048276f..a282e8ec5dcf 100644 --- a/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/genai/GenAiAttributesExtractor.java +++ b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/genai/GenAiAttributesExtractor.java @@ -52,7 +52,7 @@ public final class GenAiAttributesExtractor stringArrayKey("gen_ai.response.finish_reasons"); private static final AttributeKey GEN_AI_RESPONSE_ID = stringKey("gen_ai.response.id"); static final AttributeKey GEN_AI_RESPONSE_MODEL = stringKey("gen_ai.response.model"); - static final AttributeKey GEN_AI_SYSTEM = stringKey("gen_ai.system"); + static final AttributeKey GEN_AI_PROVIDER_NAME = stringKey("gen_ai.provider.name"); static final AttributeKey GEN_AI_USAGE_INPUT_TOKENS = longKey("gen_ai.usage.input_tokens"); static final AttributeKey GEN_AI_USAGE_OUTPUT_TOKENS = longKey("gen_ai.usage.output_tokens"); @@ -72,7 +72,7 @@ private GenAiAttributesExtractor(GenAiAttributesGetter getter @Override public void onStart(AttributesBuilder attributes, Context parentContext, REQUEST request) { internalSet(attributes, GEN_AI_OPERATION_NAME, getter.getOperationName(request)); - internalSet(attributes, GEN_AI_SYSTEM, getter.getSystem(request)); + internalSet(attributes, GEN_AI_PROVIDER_NAME, getter.getSystem(request)); internalSet(attributes, GEN_AI_REQUEST_MODEL, getter.getRequestModel(request)); internalSet(attributes, GEN_AI_REQUEST_SEED, getter.getRequestSeed(request)); internalSet( diff --git a/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/genai/GenAiMetricsAdvice.java b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/genai/GenAiMetricsAdvice.java index 0efa2b1beb6d..340d6ce3aa71 100644 --- a/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/genai/GenAiMetricsAdvice.java +++ b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/genai/GenAiMetricsAdvice.java @@ -6,9 +6,9 @@ package io.opentelemetry.instrumentation.api.incubator.semconv.genai; import static io.opentelemetry.instrumentation.api.incubator.semconv.genai.GenAiAttributesExtractor.GEN_AI_OPERATION_NAME; +import static io.opentelemetry.instrumentation.api.incubator.semconv.genai.GenAiAttributesExtractor.GEN_AI_PROVIDER_NAME; import static io.opentelemetry.instrumentation.api.incubator.semconv.genai.GenAiAttributesExtractor.GEN_AI_REQUEST_MODEL; import static io.opentelemetry.instrumentation.api.incubator.semconv.genai.GenAiAttributesExtractor.GEN_AI_RESPONSE_MODEL; -import static io.opentelemetry.instrumentation.api.incubator.semconv.genai.GenAiAttributesExtractor.GEN_AI_SYSTEM; import static io.opentelemetry.instrumentation.api.incubator.semconv.genai.GenAiClientMetrics.GEN_AI_TOKEN_TYPE; import static io.opentelemetry.semconv.ServerAttributes.SERVER_ADDRESS; import static io.opentelemetry.semconv.ServerAttributes.SERVER_PORT; @@ -44,7 +44,7 @@ static void applyClientTokenUsageAdvice(LongHistogramBuilder builder) { .setAttributesAdvice( asList( GEN_AI_OPERATION_NAME, - GEN_AI_SYSTEM, + GEN_AI_PROVIDER_NAME, GEN_AI_TOKEN_TYPE, GEN_AI_REQUEST_MODEL, SERVER_PORT, @@ -60,7 +60,7 @@ static void applyClientOperationDurationAdvice(DoubleHistogramBuilder builder) { .setAttributesAdvice( asList( GEN_AI_OPERATION_NAME, - GEN_AI_SYSTEM, + GEN_AI_PROVIDER_NAME, ErrorAttributes.ERROR_TYPE, GEN_AI_REQUEST_MODEL, SERVER_PORT, diff --git a/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/http/HttpClientUrlTemplate.java b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/http/HttpClientUrlTemplate.java new file mode 100644 index 000000000000..8121f18470bb --- /dev/null +++ b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/http/HttpClientUrlTemplate.java @@ -0,0 +1,54 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.api.incubator.semconv.http; + +import io.opentelemetry.context.Context; +import io.opentelemetry.context.ContextKey; +import io.opentelemetry.context.ImplicitContextKeyed; +import io.opentelemetry.context.Scope; +import javax.annotation.Nullable; + +/** A helper class for setting {@code url.template} attribute value for HTTP client calls. */ +public final class HttpClientUrlTemplate { + + /** + * Add url template to context and make the new context current. Http client calls made while the + * context is active will set {@code url.template} attribute to the supplied value. + */ + public static Scope with(Context context, String urlTemplate) { + return context.with(new UrlTemplateState(urlTemplate)).makeCurrent(); + } + + @Nullable + public static String get(Context context) { + UrlTemplateState state = UrlTemplateState.fromContextOrNull(context); + return state != null ? state.urlTemplate : null; + } + + private HttpClientUrlTemplate() {} + + private static class UrlTemplateState implements ImplicitContextKeyed { + + private static final ContextKey KEY = + ContextKey.named("opentelemetry-http-client-url-template-key"); + + private final String urlTemplate; + + @Nullable + static UrlTemplateState fromContextOrNull(Context context) { + return context.get(KEY); + } + + UrlTemplateState(String urlTemplate) { + this.urlTemplate = urlTemplate; + } + + @Override + public Context storeInContext(Context context) { + return context.with(KEY, this); + } + } +} diff --git a/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/http/HttpExperimentalAttributesExtractor.java b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/http/HttpExperimentalAttributesExtractor.java index 8908c8be27a5..4ee1fe09a319 100644 --- a/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/http/HttpExperimentalAttributesExtractor.java +++ b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/http/HttpExperimentalAttributesExtractor.java @@ -65,7 +65,10 @@ public void onEnd( internalSet(attributes, HTTP_RESPONSE_BODY_SIZE, responseBodySize); } - if (getter instanceof HttpClientExperimentalAttributesGetter) { + String urlTemplate = HttpClientUrlTemplate.get(context); + if (urlTemplate != null) { + internalSet(attributes, URL_TEMPLATE, urlTemplate); + } else if (getter instanceof HttpClientExperimentalAttributesGetter) { HttpClientExperimentalAttributesGetter experimentalGetter = (HttpClientExperimentalAttributesGetter) getter; internalSet(attributes, URL_TEMPLATE, experimentalGetter.getUrlTemplate(request)); diff --git a/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/http/HttpExperimentalMetricsAdvice.java b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/http/HttpExperimentalMetricsAdvice.java index fb14ee0f0153..003972013f35 100644 --- a/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/http/HttpExperimentalMetricsAdvice.java +++ b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/http/HttpExperimentalMetricsAdvice.java @@ -5,8 +5,10 @@ package io.opentelemetry.instrumentation.api.incubator.semconv.http; +import static io.opentelemetry.api.common.AttributeKey.stringKey; import static java.util.Arrays.asList; +import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.api.incubator.metrics.ExtendedLongHistogramBuilder; import io.opentelemetry.api.incubator.metrics.ExtendedLongUpDownCounterBuilder; import io.opentelemetry.api.metrics.LongHistogramBuilder; @@ -18,6 +20,8 @@ import io.opentelemetry.semconv.UrlAttributes; final class HttpExperimentalMetricsAdvice { + // copied from UrlIncubatingAttributes + private static final AttributeKey URL_TEMPLATE = stringKey("url.template"); static void applyClientRequestSizeAdvice(LongHistogramBuilder builder) { if (!(builder instanceof ExtendedLongHistogramBuilder)) { @@ -32,7 +36,8 @@ static void applyClientRequestSizeAdvice(LongHistogramBuilder builder) { NetworkAttributes.NETWORK_PROTOCOL_NAME, NetworkAttributes.NETWORK_PROTOCOL_VERSION, ServerAttributes.SERVER_ADDRESS, - ServerAttributes.SERVER_PORT)); + ServerAttributes.SERVER_PORT, + URL_TEMPLATE)); } static void applyServerRequestSizeAdvice(LongHistogramBuilder builder) { diff --git a/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/messaging/CapturedMessageHeadersUtil.java b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/messaging/CapturedMessageHeadersUtil.java index 2a756f5797ab..83f55f7c4663 100644 --- a/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/messaging/CapturedMessageHeadersUtil.java +++ b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/messaging/CapturedMessageHeadersUtil.java @@ -5,31 +5,21 @@ package io.opentelemetry.instrumentation.api.incubator.semconv.messaging; -import static java.util.Collections.unmodifiableList; - import io.opentelemetry.api.common.AttributeKey; import java.util.List; -import java.util.Locale; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; -import java.util.stream.Collectors; final class CapturedMessageHeadersUtil { private static final ConcurrentMap>> attributeKeysCache = new ConcurrentHashMap<>(); - static List lowercase(List names) { - return unmodifiableList( - names.stream().map(s -> s.toLowerCase(Locale.ROOT)).collect(Collectors.toList())); - } - static AttributeKey> attributeKey(String headerName) { return attributeKeysCache.computeIfAbsent(headerName, n -> createKey(n)); } private static AttributeKey> createKey(String headerName) { - // headerName is always lowercase, see MessagingAttributesExtractor String key = "messaging.header." + headerName.replace('-', '_'); return AttributeKey.stringArrayKey(key); } 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 bdf5ab879786..4efce367e1a1 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 @@ -13,6 +13,7 @@ import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor; import io.opentelemetry.instrumentation.api.internal.SpanKey; import io.opentelemetry.instrumentation.api.internal.SpanKeyProvider; +import java.util.ArrayList; import java.util.List; import javax.annotation.Nullable; @@ -85,7 +86,7 @@ public static MessagingAttributesExtractorBuilder capturedHeaders) { this.getter = getter; this.operation = operation; - this.capturedHeaders = CapturedMessageHeadersUtil.lowercase(capturedHeaders); + this.capturedHeaders = new ArrayList<>(capturedHeaders); } @Override diff --git a/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/messaging/MessagingAttributesExtractorBuilder.java b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/messaging/MessagingAttributesExtractorBuilder.java index 769de0862362..61f6c6916549 100644 --- a/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/messaging/MessagingAttributesExtractorBuilder.java +++ b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/messaging/MessagingAttributesExtractorBuilder.java @@ -30,8 +30,8 @@ public final class MessagingAttributesExtractorBuilder { * Configures the messaging headers that will be captured as span attributes. * *

The messaging header values will be captured under the {@code messaging.header.} - * attribute key. The {@code } part in the attribute key is the normalized header name: - * lowercase, with dashes replaced by underscores. + * attribute key. The {@code } part in the attribute key is the header name with dashes + * replaced by underscores. * * @param capturedHeaders A list of messaging header names. */ diff --git a/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/util/SpanNames.java b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/util/SpanNames.java deleted file mode 100644 index 6d9f799436a3..000000000000 --- a/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/util/SpanNames.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.instrumentation.api.incubator.semconv.util; - -import java.lang.reflect.Method; - -/** - * This class has been stabilized and moved to {@link - * io.opentelemetry.instrumentation.api.semconv.util.SpanNames} - */ -@Deprecated -public final class SpanNames { - - /** - * This method is used to generate a span name based on a method. Anonymous classes are named - * based on their parent. - */ - public static String fromMethod(Method method) { - return io.opentelemetry.instrumentation.api.semconv.util.SpanNames.fromMethod(method); - } - - /** - * This method is used to generate a span name based on a method. Anonymous classes are named - * based on their parent. - */ - public static String fromMethod(Class clazz, String methodName) { - return io.opentelemetry.instrumentation.api.semconv.util.SpanNames.fromMethod( - clazz, methodName); - } - - private SpanNames() {} -} diff --git a/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/semconv/http/HttpMetricsAdvice.java b/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/semconv/http/HttpMetricsAdvice.java index c852e9d07f91..25344953e220 100644 --- a/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/semconv/http/HttpMetricsAdvice.java +++ b/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/semconv/http/HttpMetricsAdvice.java @@ -5,9 +5,11 @@ package io.opentelemetry.instrumentation.api.semconv.http; +import static io.opentelemetry.api.common.AttributeKey.stringKey; import static java.util.Arrays.asList; import static java.util.Collections.unmodifiableList; +import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.api.incubator.metrics.ExtendedDoubleHistogramBuilder; import io.opentelemetry.api.metrics.DoubleHistogramBuilder; import io.opentelemetry.semconv.ErrorAttributes; @@ -23,6 +25,9 @@ final class HttpMetricsAdvice { unmodifiableList( asList(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)); + // copied from UrlIncubatingAttributes + private static final AttributeKey URL_TEMPLATE = stringKey("url.template"); + static void applyClientDurationAdvice(DoubleHistogramBuilder builder) { if (!(builder instanceof ExtendedDoubleHistogramBuilder)) { return; @@ -36,7 +41,9 @@ static void applyClientDurationAdvice(DoubleHistogramBuilder builder) { NetworkAttributes.NETWORK_PROTOCOL_NAME, NetworkAttributes.NETWORK_PROTOCOL_VERSION, ServerAttributes.SERVER_ADDRESS, - ServerAttributes.SERVER_PORT)); + ServerAttributes.SERVER_PORT, + // we only add url.template when experimental http client telemetry is enabled + URL_TEMPLATE)); } static void applyServerDurationAdvice(DoubleHistogramBuilder builder) { diff --git a/instrumentation-api/src/test/java/io/opentelemetry/instrumentation/api/internal/InstrumenterContextTest.java b/instrumentation-api/src/test/java/io/opentelemetry/instrumentation/api/internal/InstrumenterContextTest.java index 044935031cc2..7aa8897b7911 100644 --- a/instrumentation-api/src/test/java/io/opentelemetry/instrumentation/api/internal/InstrumenterContextTest.java +++ b/instrumentation-api/src/test/java/io/opentelemetry/instrumentation/api/internal/InstrumenterContextTest.java @@ -34,6 +34,15 @@ void testSqlSanitizer() { String testQuery = "SELECT name FROM test WHERE id = 1"; SqlClientAttributesGetter getter = new SqlClientAttributesGetter() { + @Override + public String getDbSystem(Object o) { + return "testdb"; + } + + @Override + public String getDbNamespace(Object o) { + return null; + } @Override public Collection getRawQueryTexts(Object request) { diff --git a/instrumentation-docs/build.gradle.kts b/instrumentation-docs/build.gradle.kts index 280cf2bf3bed..043e55da0828 100644 --- a/instrumentation-docs/build.gradle.kts +++ b/instrumentation-docs/build.gradle.kts @@ -8,8 +8,8 @@ otelJava { } dependencies { - implementation("org.yaml:snakeyaml:2.4") - implementation("com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.19.2") + implementation("org.yaml:snakeyaml:2.5") + implementation("com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.20.0") implementation("io.opentelemetry:opentelemetry-sdk-common") testImplementation(enforcedPlatform("org.junit:junit-bom:5.13.4")) diff --git a/instrumentation-docs/instrumentations.sh b/instrumentation-docs/instrumentations.sh index 87fd91e0f8a6..5b259ce7e134 100755 --- a/instrumentation-docs/instrumentations.sh +++ b/instrumentation-docs/instrumentations.sh @@ -22,8 +22,10 @@ readonly INSTRUMENTATIONS=( "apache-dubbo-2.7:javaagent:testDubbo" "c3p0-0.9:javaagent:test" "c3p0-0.9:javaagent:testStableSemconv" - "clickhouse-client-0.5:javaagent:test" - "clickhouse-client-0.5:javaagent:testStableSemconv" + "clickhouse:clickhouse-client-v1-0.5:javaagent:test" + "clickhouse:clickhouse-client-v1-0.5:javaagent:testStableSemconv" + "clickhouse:clickhouse-client-v2-0.8:javaagent:test" + "clickhouse:clickhouse-client-v2-0.8:javaagent:testStableSemconv" "aws-sdk:aws-sdk-1.11:javaagent:test" "google-http-client-1.19:javaagent:test" "http-url-connection:javaagent:test" @@ -67,6 +69,7 @@ readonly INSTRUMENTATIONS=( "elasticsearch:elasticsearch-api-client-7.16:javaagent:test" "elasticsearch:elasticsearch-api-client-7.16:javaagent:testStableSemconv" "elasticsearch:elasticsearch-rest-7.0:javaagent:test" + "elasticsearch:elasticsearch-rest-7.0:javaagent:testStableSemconv" "elasticsearch:elasticsearch-transport-5.0:javaagent:test" "elasticsearch:elasticsearch-transport-5.0:javaagent:testStableSemconv" "elasticsearch:elasticsearch-transport-5.0:javaagent:testExperimental" @@ -116,6 +119,7 @@ readonly INSTRUMENTATIONS=( "openai:openai-java-1.1:javaagent:test" "aws-lambda:aws-lambda-core-1.0:javaagent:test" "aws-lambda:aws-lambda-events-2.2:javaagent:test" + "aws-lambda:aws-lambda-events-3.11:library:test" "cassandra:cassandra-3.0:javaagent:test" "cassandra:cassandra-3.0:javaagent:testStableSemconv" "cassandra:cassandra-4.0:javaagent:test" @@ -125,6 +129,35 @@ readonly INSTRUMENTATIONS=( "camel-2.20:javaagent:test" "camel-2.20:javaagent:testStableSemconv" "camel-2.20:javaagent:testExperimental" + "couchbase:couchbase-2.0:javaagent:test" + "couchbase:couchbase-2.0:javaagent:testStableSemconv" + "couchbase:couchbase-2.6:javaagent:test" + "couchbase:couchbase-2.6:javaagent:testStableSemconv" + "couchbase:couchbase-2.6:javaagent:testExperimental" + "dropwizard:dropwizard-views-0.7:javaagent:test" + "geode-1.4:javaagent:test" + "geode-1.4:javaagent:testStableSemconv" + "grails-3.0:javaagent:test" + "grizzly-2.3:javaagent:test" + "gwt-2.0:javaagent:test" + "graphql-java:graphql-java-12.0:javaagent:test" + "graphql-java:graphql-java-20.0:javaagent:test" + "graphql-java:graphql-java-20.0:javaagent:testDataFetcher" + "grpc-1.6:javaagent:test" + "grpc-1.6:javaagent:testExperimental" + "hibernate:hibernate-3.3:javaagent:test" + "hibernate:hibernate-3.3:javaagent:testExperimental" + "hibernate:hibernate-4.0:javaagent:test" + "hibernate:hibernate-4.0:javaagent:testExperimental" + "hibernate:hibernate-6.0:javaagent:test" + "hibernate:hibernate-6.0:javaagent:testExperimental" + "hibernate:hibernate-procedure-call-4.3:javaagent:test" + "hibernate:hibernate-procedure-call-4.3:javaagent:testExperimental" + "hystrix-1.4:javaagent:test" + "hystrix-1.4:javaagent:testExperimental" + "influxdb-2.4:javaagent:test" + "influxdb-2.4:javaagent:testStableSemconv" + "java-http-server:javaagent:test" ) # Some instrumentation test suites don't run ARM, so we use colima to run them in an x86_64 @@ -133,7 +166,9 @@ readonly INSTRUMENTATIONS=( readonly COLIMA_INSTRUMENTATIONS=( "spring:spring-jms:spring-jms-6.0:javaagent:test" "elasticsearch:elasticsearch-rest-6.4:javaagent:test" + "elasticsearch:elasticsearch-rest-6.4:javaagent:testStableSemconv" "elasticsearch:elasticsearch-rest-5.0:javaagent:test" + "elasticsearch:elasticsearch-rest-5.0:javaagent:testStableSemconv" "oracle-ucp-11.2:javaagent:test" "oracle-ucp-11.2:javaagent:testStableSemconv" ) diff --git a/instrumentation-docs/readme.md b/instrumentation-docs/readme.md index 0bb6ba9dfe52..4e389f6f54da 100644 --- a/instrumentation-docs/readme.md +++ b/instrumentation-docs/readme.md @@ -68,7 +68,7 @@ or use the helper script that will run only the currently supported tests (recom ## Instrumentation Hierarchy -An "InstrumentationModule" represents a module that that targets specific code in a +An "InstrumentationModule" represents a module that targets specific code in a framework/library/technology. Each module will have a name, a namespace, and a group. Using these structures as examples: @@ -114,6 +114,9 @@ public class SpringWebInstrumentationModule extends InstrumentationModule * name * Identifier for instrumentation module, used to enable/disable * Configured in `InstrumentationModule` code for each module +* library_link + * URL to the library or framework's main website or documentation, or if those don't exist, the + GitHub repository. * source_path * Path to the source code of the instrumentation module * minimum_java_version @@ -125,7 +128,7 @@ public class SpringWebInstrumentationModule extends InstrumentationModule * List of supported versions by the module, broken down by `library` or `javaagent` support * scope * Name: The scope name of the instrumentation, `io.opentelemetry.{instrumentation name}` -* configurations settings +* configuration settings * List of settings that are available for the instrumentation module * Each setting has a name, description, type, and default value * metrics @@ -145,9 +148,10 @@ additional information about the instrumentation module. As of now, the following fields are supported, all of which are optional: ```yaml -description: "Instruments..." # Description of the instrumentation module -disabled_by_default: true # Defaults to `false` -classification: internal # instrumentation classification: library | internal | custom +description: "This instrumentation enables..." # Description of the instrumentation module +disabled_by_default: true # Defaults to `false` +classification: internal # instrumentation classification: library | internal | custom +library_link: https://... # URL to the library or framework's main website or documentation configurations: - name: otel.instrumentation.common.db-statement-sanitizer.enabled description: Enables statement sanitization for database queries. diff --git a/instrumentation-docs/src/main/java/io/opentelemetry/instrumentation/docs/InstrumentationAnalyzer.java b/instrumentation-docs/src/main/java/io/opentelemetry/instrumentation/docs/InstrumentationAnalyzer.java index ad393200fd6b..a84c88dd95e9 100644 --- a/instrumentation-docs/src/main/java/io/opentelemetry/instrumentation/docs/InstrumentationAnalyzer.java +++ b/instrumentation-docs/src/main/java/io/opentelemetry/instrumentation/docs/InstrumentationAnalyzer.java @@ -8,7 +8,7 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.exc.MismatchedInputException; import com.fasterxml.jackson.databind.exc.ValueInstantiationException; -import io.opentelemetry.instrumentation.docs.internal.InstrumentationMetaData; +import io.opentelemetry.instrumentation.docs.internal.InstrumentationMetadata; import io.opentelemetry.instrumentation.docs.internal.InstrumentationModule; import io.opentelemetry.instrumentation.docs.internal.InstrumentationType; import io.opentelemetry.instrumentation.docs.parsers.GradleParser; @@ -58,7 +58,7 @@ public List analyze() throws IOException { } private void enrichModule(InstrumentationModule module) throws IOException { - InstrumentationMetaData metaData = getMetadata(module); + InstrumentationMetadata metaData = getMetadata(module); if (metaData != null) { module.setMetadata(metaData); } @@ -69,7 +69,7 @@ private void enrichModule(InstrumentationModule module) throws IOException { } @Nullable - private InstrumentationMetaData getMetadata(InstrumentationModule module) + private InstrumentationMetadata getMetadata(InstrumentationModule module) throws JsonProcessingException { String metadataFile = fileManager.getMetaDataFile(module.getSrcPath()); if (metadataFile == null) { diff --git a/instrumentation-docs/src/main/java/io/opentelemetry/instrumentation/docs/internal/InstrumentationMetaData.java b/instrumentation-docs/src/main/java/io/opentelemetry/instrumentation/docs/internal/InstrumentationMetaData.java deleted file mode 100644 index d43a8bd2c748..000000000000 --- a/instrumentation-docs/src/main/java/io/opentelemetry/instrumentation/docs/internal/InstrumentationMetaData.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.instrumentation.docs.internal; - -import com.fasterxml.jackson.annotation.JsonProperty; -import java.util.Collections; -import java.util.List; -import java.util.Objects; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -/** - * Represents the data in a metadata.yaml file. This class is internal and is hence not for public - * use. Its APIs are unstable and can change at any time. - */ -public class InstrumentationMetaData { - @Nullable private String description; - - @JsonProperty("disabled_by_default") - @Nullable - private Boolean disabledByDefault; - - private String classification; - - private List configurations = Collections.emptyList(); - - public InstrumentationMetaData() { - this.classification = InstrumentationClassification.LIBRARY.toString(); - } - - public InstrumentationMetaData( - @Nullable String description, - String classification, - @Nullable Boolean disabledByDefault, - @Nullable List configurations) { - this.classification = classification; - this.disabledByDefault = disabledByDefault; - this.description = description; - this.configurations = Objects.requireNonNullElse(configurations, Collections.emptyList()); - } - - @Nullable - public String getDescription() { - return description; - } - - @Nonnull - public InstrumentationClassification getClassification() { - return Objects.requireNonNullElse( - InstrumentationClassification.fromString(classification), - InstrumentationClassification.LIBRARY); - } - - public Boolean getDisabledByDefault() { - return Objects.requireNonNullElse(disabledByDefault, false); - } - - public void setDescription(@Nullable String description) { - this.description = description; - } - - public void setClassification(String classification) { - this.classification = classification; - } - - public void setDisabledByDefault(@Nullable Boolean disabledByDefault) { - this.disabledByDefault = disabledByDefault; - } - - public List getConfigurations() { - return configurations; - } - - public void setConfigurations(@Nullable List configurations) { - this.configurations = Objects.requireNonNullElse(configurations, Collections.emptyList()); - } -} diff --git a/instrumentation-docs/src/main/java/io/opentelemetry/instrumentation/docs/internal/InstrumentationMetadata.java b/instrumentation-docs/src/main/java/io/opentelemetry/instrumentation/docs/internal/InstrumentationMetadata.java new file mode 100644 index 000000000000..aeb2671ed5a0 --- /dev/null +++ b/instrumentation-docs/src/main/java/io/opentelemetry/instrumentation/docs/internal/InstrumentationMetadata.java @@ -0,0 +1,172 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.docs.internal; + +import static java.util.Collections.emptyList; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.google.errorprone.annotations.CanIgnoreReturnValue; +import java.util.List; +import java.util.Objects; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +/** + * Represents the data in a metadata.yaml file. This class is internal and is hence not for public + * use. Its APIs are unstable and can change at any time. + */ +public class InstrumentationMetadata { + @Nullable private String description; + + @JsonProperty("disabled_by_default") + @Nullable + private Boolean disabledByDefault; + + private String classification; + + @JsonProperty("library_link") + @Nullable + private String libraryLink; + + @JsonProperty("display_name") + @Nullable + private String displayName; + + private List configurations = emptyList(); + + public InstrumentationMetadata() { + this.classification = InstrumentationClassification.LIBRARY.name(); + } + + public InstrumentationMetadata( + @Nullable String description, + @Nullable Boolean disabledByDefault, + String classification, + @Nullable String libraryLink, + @Nullable String displayName, + @Nullable List configurations) { + this.classification = classification; + this.disabledByDefault = disabledByDefault; + this.description = description; + this.libraryLink = libraryLink; + this.displayName = displayName; + this.configurations = Objects.requireNonNullElse(configurations, emptyList()); + } + + @Nullable + public String getDescription() { + return description; + } + + public void setDisplayName(@Nullable String displayName) { + this.displayName = displayName; + } + + @Nullable + public String getDisplayName() { + return displayName; + } + + @Nonnull + public InstrumentationClassification getClassification() { + return Objects.requireNonNullElse( + InstrumentationClassification.fromString(classification), + InstrumentationClassification.LIBRARY); + } + + public Boolean getDisabledByDefault() { + return Objects.requireNonNullElse(disabledByDefault, false); + } + + public void setDescription(@Nullable String description) { + this.description = description; + } + + public void setClassification(String classification) { + this.classification = classification; + } + + public void setDisabledByDefault(@Nullable Boolean disabledByDefault) { + this.disabledByDefault = disabledByDefault; + } + + public List getConfigurations() { + return configurations; + } + + public void setConfigurations(@Nullable List configurations) { + this.configurations = Objects.requireNonNullElse(configurations, emptyList()); + } + + @Nullable + public String getLibraryLink() { + return libraryLink; + } + + public void setLibraryLink(@Nullable String libraryLink) { + this.libraryLink = libraryLink; + } + + /** + * This class is internal and is hence not for public use. Its APIs are unstable and can change at + * any time. + */ + public static class Builder { + + @Nullable private String description; + @Nullable private Boolean disabledByDefault; + @Nullable private String classification; + @Nullable private String libraryLink; + @Nullable private String displayName; + private List configurations = emptyList(); + + @CanIgnoreReturnValue + public Builder description(@Nullable String description) { + this.description = description; + return this; + } + + @CanIgnoreReturnValue + public Builder disabledByDefault(@Nullable Boolean disabledByDefault) { + this.disabledByDefault = disabledByDefault; + return this; + } + + @CanIgnoreReturnValue + public Builder classification(@Nullable String classification) { + this.classification = classification; + return this; + } + + @CanIgnoreReturnValue + public Builder libraryLink(@Nullable String libraryLink) { + this.libraryLink = libraryLink; + return this; + } + + @CanIgnoreReturnValue + public Builder displayName(@Nullable String displayName) { + this.displayName = displayName; + return this; + } + + @CanIgnoreReturnValue + public Builder configurations(@Nullable List configurations) { + this.configurations = Objects.requireNonNullElse(configurations, emptyList()); + return this; + } + + public InstrumentationMetadata build() { + return new InstrumentationMetadata( + description, + disabledByDefault, + classification != null ? classification : InstrumentationClassification.LIBRARY.name(), + libraryLink, + displayName, + configurations); + } + } +} diff --git a/instrumentation-docs/src/main/java/io/opentelemetry/instrumentation/docs/internal/InstrumentationModule.java b/instrumentation-docs/src/main/java/io/opentelemetry/instrumentation/docs/internal/InstrumentationModule.java index 3910e291b890..1b13cb8eb55e 100644 --- a/instrumentation-docs/src/main/java/io/opentelemetry/instrumentation/docs/internal/InstrumentationModule.java +++ b/instrumentation-docs/src/main/java/io/opentelemetry/instrumentation/docs/internal/InstrumentationModule.java @@ -35,7 +35,7 @@ public class InstrumentationModule { @Nullable private Integer minJavaVersion; - @Nullable private InstrumentationMetaData metadata; + @Nullable private InstrumentationMetadata metadata; /** * This class is internal and is hence not for public use. Its APIs are unstable and can change at @@ -79,9 +79,9 @@ public InstrumentationScopeInfo getScopeInfo() { return scopeInfo; } - public InstrumentationMetaData getMetadata() { + public InstrumentationMetadata getMetadata() { if (metadata == null) { - metadata = new InstrumentationMetaData(); + metadata = new InstrumentationMetadata(); } return metadata; @@ -109,7 +109,7 @@ public void setTargetVersions(Map> targetVersio this.targetVersions = targetVersions; } - public void setMetadata(InstrumentationMetaData metadata) { + public void setMetadata(InstrumentationMetadata metadata) { this.metadata = metadata; } @@ -135,7 +135,7 @@ public static class Builder { @Nullable private String namespace; @Nullable private String group; @Nullable private Integer minJavaVersion; - @Nullable private InstrumentationMetaData metadata; + @Nullable private InstrumentationMetadata metadata; @Nullable private Map> targetVersions; @Nullable private Map> metrics; @Nullable private Map> spans; @@ -171,7 +171,7 @@ public Builder group(String group) { } @CanIgnoreReturnValue - public Builder metadata(InstrumentationMetaData metadata) { + public Builder metadata(InstrumentationMetadata metadata) { this.metadata = metadata; return this; } diff --git a/instrumentation-docs/src/main/java/io/opentelemetry/instrumentation/docs/parsers/SpanParser.java b/instrumentation-docs/src/main/java/io/opentelemetry/instrumentation/docs/parsers/SpanParser.java index 4cb5aa32c70d..b35c35a29826 100644 --- a/instrumentation-docs/src/main/java/io/opentelemetry/instrumentation/docs/parsers/SpanParser.java +++ b/instrumentation-docs/src/main/java/io/opentelemetry/instrumentation/docs/parsers/SpanParser.java @@ -25,7 +25,14 @@ public class SpanParser { // We want to ignore test related attributes private static final List EXCLUDED_ATTRIBUTES = - List.of("x-test-", "test-baggage-", "test_message"); + List.of( + "x-test-", + "test-baggage-", + "test_message", + "Test_Message", + "Test-Message", + "some-client-key", + "some-server-key"); /** * Pull spans from the `.telemetry` directory, filter them by scope, and set them in the module. diff --git a/instrumentation-docs/src/main/java/io/opentelemetry/instrumentation/docs/parsers/TelemetryParser.java b/instrumentation-docs/src/main/java/io/opentelemetry/instrumentation/docs/parsers/TelemetryParser.java index a49fa551c87e..c5aaf6934321 100644 --- a/instrumentation-docs/src/main/java/io/opentelemetry/instrumentation/docs/parsers/TelemetryParser.java +++ b/instrumentation-docs/src/main/java/io/opentelemetry/instrumentation/docs/parsers/TelemetryParser.java @@ -12,7 +12,14 @@ class TelemetryParser { // Key is the scope of the module being analyzed, value is a set of additional allowed scopes. private static final Map> scopeAllowList = - Map.of("io.opentelemetry.armeria-grpc-1.14", Set.of("io.opentelemetry.grpc-1.6")); + Map.of( + // armeria-grpc uses grpc-1.6 instrumenter. + "io.opentelemetry.armeria-grpc-1.14", Set.of("io.opentelemetry.grpc-1.6"), + // couchbase-2.6 extends couchbase-2.0 instrumentation with more attributes. + "io.opentelemetry.couchbase-2.6", Set.of("io.opentelemetry.couchbase-2.0"), + // elasticsearch-rest-7.0 extends elasticsearch-api-client-7.16 with more attributes. + "io.opentelemetry.elasticsearch-api-client-7.16", + Set.of("io.opentelemetry.elasticsearch-rest-7.0")); /** * Checks if the given telemetry scope is valid for the specified module scope. diff --git a/instrumentation-docs/src/main/java/io/opentelemetry/instrumentation/docs/utils/YamlHelper.java b/instrumentation-docs/src/main/java/io/opentelemetry/instrumentation/docs/utils/YamlHelper.java index 98ed802d6047..e79d52e7f852 100644 --- a/instrumentation-docs/src/main/java/io/opentelemetry/instrumentation/docs/utils/YamlHelper.java +++ b/instrumentation-docs/src/main/java/io/opentelemetry/instrumentation/docs/utils/YamlHelper.java @@ -13,7 +13,7 @@ import io.opentelemetry.instrumentation.docs.internal.EmittedMetrics; import io.opentelemetry.instrumentation.docs.internal.EmittedSpans; import io.opentelemetry.instrumentation.docs.internal.InstrumentationClassification; -import io.opentelemetry.instrumentation.docs.internal.InstrumentationMetaData; +import io.opentelemetry.instrumentation.docs.internal.InstrumentationMetadata; import io.opentelemetry.instrumentation.docs.internal.InstrumentationModule; import io.opentelemetry.instrumentation.docs.internal.TelemetryAttribute; import java.io.BufferedWriter; @@ -195,9 +195,15 @@ private static Map baseProperties(InstrumentationModule module) private static void addMetadataProperties( InstrumentationModule module, Map moduleMap) { if (module.getMetadata() != null) { + if (module.getMetadata().getDisplayName() != null) { + moduleMap.put("display_name", module.getMetadata().getDisplayName()); + } if (module.getMetadata().getDescription() != null) { moduleMap.put("description", module.getMetadata().getDescription()); } + if (module.getMetadata().getLibraryLink() != null) { + moduleMap.put("library_link", module.getMetadata().getLibraryLink()); + } if (module.getMetadata().getDisabledByDefault()) { moduleMap.put("disabled_by_default", module.getMetadata().getDisabledByDefault()); } @@ -287,9 +293,9 @@ private static Map getSpanMap(EmittedSpans.Span span) { return innerMetricMap; } - public static InstrumentationMetaData metaDataParser(String input) + public static InstrumentationMetadata metaDataParser(String input) throws JsonProcessingException { - return mapper.readValue(input, InstrumentationMetaData.class); + return mapper.readValue(input, InstrumentationMetadata.class); } public static EmittedMetrics emittedMetricsParser(String input) throws JsonProcessingException { diff --git a/instrumentation-docs/src/test/java/io/opentelemetry/instrumentation/docs/utils/YamlHelperTest.java b/instrumentation-docs/src/test/java/io/opentelemetry/instrumentation/docs/utils/YamlHelperTest.java index be63541917f9..a8921128df0c 100644 --- a/instrumentation-docs/src/test/java/io/opentelemetry/instrumentation/docs/utils/YamlHelperTest.java +++ b/instrumentation-docs/src/test/java/io/opentelemetry/instrumentation/docs/utils/YamlHelperTest.java @@ -14,7 +14,7 @@ import io.opentelemetry.instrumentation.docs.internal.EmittedMetrics; import io.opentelemetry.instrumentation.docs.internal.EmittedSpans; import io.opentelemetry.instrumentation.docs.internal.InstrumentationClassification; -import io.opentelemetry.instrumentation.docs.internal.InstrumentationMetaData; +import io.opentelemetry.instrumentation.docs.internal.InstrumentationMetadata; import io.opentelemetry.instrumentation.docs.internal.InstrumentationModule; import io.opentelemetry.instrumentation.docs.internal.InstrumentationType; import io.opentelemetry.instrumentation.docs.internal.TelemetryAttribute; @@ -38,12 +38,13 @@ void testPrintInstrumentationList() throws Exception { InstrumentationType.JAVAAGENT, new HashSet<>(List.of("org.springframework:spring-web:[6.0.0,)"))); - InstrumentationMetaData springMetadata = - new InstrumentationMetaData( - "Spring Web 6.0 instrumentation", - InstrumentationClassification.LIBRARY.toString(), - true, - null); + InstrumentationMetadata springMetadata = + new InstrumentationMetadata.Builder() + .description("Spring Web 6.0 instrumentation") + .displayName("Spring Web") + .classification(InstrumentationClassification.LIBRARY.name()) + .disabledByDefault(true) + .build(); modules.add( new InstrumentationModule.Builder() @@ -81,6 +82,7 @@ void testPrintInstrumentationList() throws Exception { libraries: spring: - name: spring-web-6.0 + display_name: Spring Web description: Spring Web 6.0 instrumentation disabled_by_default: true source_path: instrumentation/spring/spring-web/spring-web-6.0 @@ -109,17 +111,19 @@ void testGenerateInstrumentationYamlSeparatesClassifications() throws Exception Map> springTargetVersions = Map.of(InstrumentationType.JAVAAGENT, Set.of("org.springframework:spring-web:[6.0.0,)")); - InstrumentationMetaData springMetadata = - new InstrumentationMetaData( - "Spring Web 6.0 instrumentation", - InstrumentationClassification.LIBRARY.toString(), - false, - List.of( - new ConfigurationOption( - "otel.instrumentation.spring-web-6.0.enabled", - "Enables or disables Spring Web 6.0 instrumentation.", - "true", - ConfigurationType.BOOLEAN))); + InstrumentationMetadata springMetadata = + new InstrumentationMetadata.Builder() + .description("Spring Web 6.0 instrumentation") + .classification(InstrumentationClassification.LIBRARY.name()) + .disabledByDefault(false) + .configurations( + List.of( + new ConfigurationOption( + "otel.instrumentation.spring-web-6.0.enabled", + "Enables or disables Spring Web 6.0 instrumentation.", + "true", + ConfigurationType.BOOLEAN))) + .build(); modules.add( new InstrumentationModule.Builder() @@ -132,9 +136,10 @@ void testGenerateInstrumentationYamlSeparatesClassifications() throws Exception .minJavaVersion(11) .build()); - InstrumentationMetaData internalMetadata = - new InstrumentationMetaData( - null, InstrumentationClassification.INTERNAL.toString(), null, null); + InstrumentationMetadata internalMetadata = + new InstrumentationMetadata.Builder() + .classification(InstrumentationClassification.INTERNAL.name()) + .build(); modules.add( new InstrumentationModule.Builder() @@ -146,9 +151,10 @@ void testGenerateInstrumentationYamlSeparatesClassifications() throws Exception .targetVersions(new HashMap<>()) .build()); - InstrumentationMetaData customMetadata = - new InstrumentationMetaData( - null, InstrumentationClassification.CUSTOM.toString(), null, null); + InstrumentationMetadata customMetadata = + new InstrumentationMetadata.Builder() + .classification(InstrumentationClassification.CUSTOM.name()) + .build(); Map> externalAnnotationsVersions = Map.of( @@ -214,6 +220,7 @@ void testMetadataParser() throws JsonProcessingException { description: test description classification: internal disabled_by_default: true + library_link: https://example.com/test-library configurations: - name: otel.instrumentation.common.db-statement-sanitizer.enabled description: Enables statement sanitization for database queries. @@ -221,7 +228,7 @@ void testMetadataParser() throws JsonProcessingException { default: true """; - InstrumentationMetaData metadata = YamlHelper.metaDataParser(input); + InstrumentationMetadata metadata = YamlHelper.metaDataParser(input); ConfigurationOption config = metadata.getConfigurations().get(0); assertThat(config.name()) @@ -233,14 +240,27 @@ void testMetadataParser() throws JsonProcessingException { assertThat(metadata.getClassification()).isEqualTo(InstrumentationClassification.INTERNAL); assertThat(metadata.getDescription()).isEqualTo("test description"); assertThat(metadata.getDisabledByDefault()).isEqualTo(true); + assertThat(metadata.getLibraryLink()).isEqualTo("https://example.com/test-library"); } @Test void testMetadataParserWithOnlyLibraryEntry() throws JsonProcessingException { String input = "classification: internal"; - InstrumentationMetaData metadata = YamlHelper.metaDataParser(input); + InstrumentationMetadata metadata = YamlHelper.metaDataParser(input); assertThat(metadata.getClassification()).isEqualTo(InstrumentationClassification.INTERNAL); assertThat(metadata.getDescription()).isNull(); + assertThat(metadata.getLibraryLink()).isNull(); + assertThat(metadata.getDisabledByDefault()).isFalse(); + assertThat(metadata.getConfigurations()).isEmpty(); + } + + @Test + void testMetadataParserWithOnlyLibraryLink() throws JsonProcessingException { + String input = "library_link: https://example.com/only-link"; + InstrumentationMetadata metadata = YamlHelper.metaDataParser(input); + assertThat(metadata.getClassification()).isEqualTo(InstrumentationClassification.LIBRARY); + assertThat(metadata.getDescription()).isNull(); + assertThat(metadata.getLibraryLink()).isEqualTo("https://example.com/only-link"); assertThat(metadata.getDisabledByDefault()).isFalse(); assertThat(metadata.getConfigurations()).isEmpty(); } @@ -248,7 +268,7 @@ void testMetadataParserWithOnlyLibraryEntry() throws JsonProcessingException { @Test void testMetadataParserWithOnlyDescription() throws JsonProcessingException { String input = "description: false"; - InstrumentationMetaData metadata = YamlHelper.metaDataParser(input); + InstrumentationMetadata metadata = YamlHelper.metaDataParser(input); assertThat(metadata.getClassification()).isEqualTo(InstrumentationClassification.LIBRARY); assertThat(metadata.getDisabledByDefault()).isFalse(); assertThat(metadata.getConfigurations()).isEmpty(); @@ -257,7 +277,7 @@ void testMetadataParserWithOnlyDescription() throws JsonProcessingException { @Test void testMetadataParserWithOnlyDisabledByDefault() throws JsonProcessingException { String input = "disabled_by_default: true"; - InstrumentationMetaData metadata = YamlHelper.metaDataParser(input); + InstrumentationMetadata metadata = YamlHelper.metaDataParser(input); assertThat(metadata.getClassification()).isEqualTo(InstrumentationClassification.LIBRARY); assertThat(metadata.getDescription()).isNull(); assertThat(metadata.getDisabledByDefault()).isTrue(); @@ -274,7 +294,7 @@ void testMetadataParserWithOnlyConfigurations() throws JsonProcessingException { type: boolean default: true """; - InstrumentationMetaData metadata = YamlHelper.metaDataParser(input); + InstrumentationMetadata metadata = YamlHelper.metaDataParser(input); ConfigurationOption config = metadata.getConfigurations().get(0); assertThat(metadata.getClassification()).isEqualTo(InstrumentationClassification.LIBRARY); @@ -485,4 +505,79 @@ void testTelemetryGroupsAreSorted() throws Exception { assertThat(yaml1).isEqualTo(yaml2); } + + @Test + void testYamlGenerationWithLibraryLink() throws Exception { + List modules = new ArrayList<>(); + Map> targetVersions = new HashMap<>(); + targetVersions.put( + InstrumentationType.JAVAAGENT, new HashSet<>(List.of("com.example:test-library:[1.0.0,)"))); + + InstrumentationMetadata metadataWithLink = + new InstrumentationMetadata.Builder() + .description("Test library instrumentation with link") + .classification(InstrumentationClassification.LIBRARY.name()) + .disabledByDefault(false) + .libraryLink("https://example.com/test-library-docs") + .build(); + + modules.add( + new InstrumentationModule.Builder() + .srcPath("instrumentation/test-lib/test-lib-1.0") + .instrumentationName("test-lib-1.0") + .namespace("test-lib") + .group("test-lib") + .targetVersions(targetVersions) + .metadata(metadataWithLink) + .build()); + + InstrumentationMetadata metadataWithoutLink = + new InstrumentationMetadata.Builder() + .description("Test library instrumentation without link") + .classification(InstrumentationClassification.LIBRARY.name()) + .disabledByDefault(false) + .build(); + + modules.add( + new InstrumentationModule.Builder() + .srcPath("instrumentation/other-lib/other-lib-1.0") + .instrumentationName("other-lib-1.0") + .namespace("other-lib") + .group("other-lib") + .targetVersions(targetVersions) + .metadata(metadataWithoutLink) + .build()); + + StringWriter stringWriter = new StringWriter(); + BufferedWriter writer = new BufferedWriter(stringWriter); + + YamlHelper.generateInstrumentationYaml(modules, writer); + writer.flush(); + + String expectedYaml = + """ + libraries: + other-lib: + - name: other-lib-1.0 + description: Test library instrumentation without link + source_path: instrumentation/other-lib/other-lib-1.0 + scope: + name: io.opentelemetry.other-lib-1.0 + target_versions: + javaagent: + - com.example:test-library:[1.0.0,) + test-lib: + - name: test-lib-1.0 + description: Test library instrumentation with link + library_link: https://example.com/test-library-docs + source_path: instrumentation/test-lib/test-lib-1.0 + scope: + name: io.opentelemetry.test-lib-1.0 + target_versions: + javaagent: + - com.example:test-library:[1.0.0,) + """; + + assertThat(expectedYaml).isEqualTo(stringWriter.toString()); + } } diff --git a/instrumentation/activej-http-6.0/metadata.yaml b/instrumentation/activej-http-6.0/metadata.yaml index 61aeae5824c5..fe37bcf901fa 100644 --- a/instrumentation/activej-http-6.0/metadata.yaml +++ b/instrumentation/activej-http-6.0/metadata.yaml @@ -1 +1,3 @@ -description: This instrumentation enables SERVER spans and metrics for the ActiveJ HTTP server. +display_name: ActiveJ +description: This instrumentation enables HTTP server spans and HTTP server metrics for the ActiveJ HTTP server. +library_link: https://activej.io/ diff --git a/instrumentation/akka/akka-actor-2.3/metadata.yaml b/instrumentation/akka/akka-actor-2.3/metadata.yaml index c1a83e29bb5f..26427a916ee3 100644 --- a/instrumentation/akka/akka-actor-2.3/metadata.yaml +++ b/instrumentation/akka/akka-actor-2.3/metadata.yaml @@ -1 +1,3 @@ +display_name: Akka Actors description: This instrumentation provides context propagation for Akka actors, it does not emit any telemetry on its own. +library_link: https://doc.akka.io/libraries/akka-core/current/typed/index.html diff --git a/instrumentation/akka/akka-actor-fork-join-2.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/akkaactor/AkkaActorForkJoinInstrumentationModule.java b/instrumentation/akka/akka-actor-fork-join-2.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/akkaforkjoin/AkkaActorForkJoinInstrumentationModule.java similarity index 93% rename from instrumentation/akka/akka-actor-fork-join-2.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/akkaactor/AkkaActorForkJoinInstrumentationModule.java rename to instrumentation/akka/akka-actor-fork-join-2.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/akkaforkjoin/AkkaActorForkJoinInstrumentationModule.java index 4125e841a84b..a5e44c94a8f0 100644 --- a/instrumentation/akka/akka-actor-fork-join-2.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/akkaactor/AkkaActorForkJoinInstrumentationModule.java +++ b/instrumentation/akka/akka-actor-fork-join-2.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/akkaforkjoin/AkkaActorForkJoinInstrumentationModule.java @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.javaagent.instrumentation.akkaactor; +package io.opentelemetry.javaagent.instrumentation.akkaforkjoin; import static java.util.Arrays.asList; diff --git a/instrumentation/akka/akka-actor-fork-join-2.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/akkaactor/AkkaForkJoinPoolInstrumentation.java b/instrumentation/akka/akka-actor-fork-join-2.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/akkaforkjoin/AkkaForkJoinPoolInstrumentation.java similarity index 97% rename from instrumentation/akka/akka-actor-fork-join-2.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/akkaactor/AkkaForkJoinPoolInstrumentation.java rename to instrumentation/akka/akka-actor-fork-join-2.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/akkaforkjoin/AkkaForkJoinPoolInstrumentation.java index 40556c79cb3e..e7532860a3d0 100644 --- a/instrumentation/akka/akka-actor-fork-join-2.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/akkaactor/AkkaForkJoinPoolInstrumentation.java +++ b/instrumentation/akka/akka-actor-fork-join-2.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/akkaforkjoin/AkkaForkJoinPoolInstrumentation.java @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.javaagent.instrumentation.akkaactor; +package io.opentelemetry.javaagent.instrumentation.akkaforkjoin; import static net.bytebuddy.matcher.ElementMatchers.named; import static net.bytebuddy.matcher.ElementMatchers.takesArgument; diff --git a/instrumentation/akka/akka-actor-fork-join-2.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/akkaactor/AkkaForkJoinTaskInstrumentation.java b/instrumentation/akka/akka-actor-fork-join-2.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/akkaforkjoin/AkkaForkJoinTaskInstrumentation.java similarity index 98% rename from instrumentation/akka/akka-actor-fork-join-2.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/akkaactor/AkkaForkJoinTaskInstrumentation.java rename to instrumentation/akka/akka-actor-fork-join-2.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/akkaforkjoin/AkkaForkJoinTaskInstrumentation.java index 66fe65df953a..76c1ed7b5ef9 100644 --- a/instrumentation/akka/akka-actor-fork-join-2.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/akkaactor/AkkaForkJoinTaskInstrumentation.java +++ b/instrumentation/akka/akka-actor-fork-join-2.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/akkaforkjoin/AkkaForkJoinTaskInstrumentation.java @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.javaagent.instrumentation.akkaactor; +package io.opentelemetry.javaagent.instrumentation.akkaforkjoin; import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.extendsClass; import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed; diff --git a/instrumentation/akka/akka-actor-fork-join-2.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/akkaactor/VirtualFields.java b/instrumentation/akka/akka-actor-fork-join-2.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/akkaforkjoin/VirtualFields.java similarity index 93% rename from instrumentation/akka/akka-actor-fork-join-2.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/akkaactor/VirtualFields.java rename to instrumentation/akka/akka-actor-fork-join-2.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/akkaforkjoin/VirtualFields.java index 2ff4df7f029f..aceb5a2d8948 100644 --- a/instrumentation/akka/akka-actor-fork-join-2.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/akkaactor/VirtualFields.java +++ b/instrumentation/akka/akka-actor-fork-join-2.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/akkaforkjoin/VirtualFields.java @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.javaagent.instrumentation.akkaactor; +package io.opentelemetry.javaagent.instrumentation.akkaforkjoin; import akka.dispatch.forkjoin.ForkJoinTask; import io.opentelemetry.instrumentation.api.util.VirtualField; diff --git a/instrumentation/akka/akka-actor-fork-join-2.5/javaagent/src/test/java/io/opentelemetry/instrumentation/akkaactor/AkkaAsyncChild.java b/instrumentation/akka/akka-actor-fork-join-2.5/javaagent/src/test/java/io/opentelemetry/instrumentation/akkaforkjoin/AkkaAsyncChild.java similarity index 96% rename from instrumentation/akka/akka-actor-fork-join-2.5/javaagent/src/test/java/io/opentelemetry/instrumentation/akkaactor/AkkaAsyncChild.java rename to instrumentation/akka/akka-actor-fork-join-2.5/javaagent/src/test/java/io/opentelemetry/instrumentation/akkaforkjoin/AkkaAsyncChild.java index 1fcf2542934c..6d703a369d00 100644 --- a/instrumentation/akka/akka-actor-fork-join-2.5/javaagent/src/test/java/io/opentelemetry/instrumentation/akkaactor/AkkaAsyncChild.java +++ b/instrumentation/akka/akka-actor-fork-join-2.5/javaagent/src/test/java/io/opentelemetry/instrumentation/akkaforkjoin/AkkaAsyncChild.java @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.instrumentation.akkaactor; +package io.opentelemetry.instrumentation.akkaforkjoin; import akka.dispatch.forkjoin.ForkJoinTask; import io.opentelemetry.api.GlobalOpenTelemetry; diff --git a/instrumentation/akka/akka-actor-fork-join-2.5/javaagent/src/test/java/io/opentelemetry/instrumentation/akkaactor/AkkaExecutorInstrumentationTest.java b/instrumentation/akka/akka-actor-fork-join-2.5/javaagent/src/test/java/io/opentelemetry/instrumentation/akkaforkjoin/AkkaExecutorInstrumentationTest.java similarity index 95% rename from instrumentation/akka/akka-actor-fork-join-2.5/javaagent/src/test/java/io/opentelemetry/instrumentation/akkaactor/AkkaExecutorInstrumentationTest.java rename to instrumentation/akka/akka-actor-fork-join-2.5/javaagent/src/test/java/io/opentelemetry/instrumentation/akkaforkjoin/AkkaExecutorInstrumentationTest.java index 75b416906e3e..ff78fe62ed37 100644 --- a/instrumentation/akka/akka-actor-fork-join-2.5/javaagent/src/test/java/io/opentelemetry/instrumentation/akkaactor/AkkaExecutorInstrumentationTest.java +++ b/instrumentation/akka/akka-actor-fork-join-2.5/javaagent/src/test/java/io/opentelemetry/instrumentation/akkaforkjoin/AkkaExecutorInstrumentationTest.java @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.instrumentation.akkaactor; +package io.opentelemetry.instrumentation.akkaforkjoin; import akka.dispatch.forkjoin.ForkJoinPool; import akka.dispatch.forkjoin.ForkJoinTask; diff --git a/instrumentation/akka/akka-actor-fork-join-2.5/metadata.yaml b/instrumentation/akka/akka-actor-fork-join-2.5/metadata.yaml index b9bfd2254016..5b7ffadfaa2a 100644 --- a/instrumentation/akka/akka-actor-fork-join-2.5/metadata.yaml +++ b/instrumentation/akka/akka-actor-fork-join-2.5/metadata.yaml @@ -1 +1,3 @@ +display_name: Akka Actors description: This instrumentation provides context propagation for the Akka Fork-Join Pool, it does not emit any telemetry on its own. +library_link: https://doc.akka.io/libraries/akka-core/current/typed/index.html diff --git a/instrumentation/akka/akka-http-10.0/metadata.yaml b/instrumentation/akka/akka-http-10.0/metadata.yaml index 8f284f8dba6a..9f8a4ae3bcbb 100644 --- a/instrumentation/akka/akka-http-10.0/metadata.yaml +++ b/instrumentation/akka/akka-http-10.0/metadata.yaml @@ -1 +1,5 @@ -description: This instrumentation enables CLIENT and SERVER spans and metrics for the Akka HTTP client and server. +display_name: Akka HTTP +description: > + This instrumentation enables HTTP client spans and metrics for the Akka HTTP client, and HTTP + server spans and metrics for the Akka HTTP server. +library_link: https://doc.akka.io/docs/akka-http/current/index.html diff --git a/instrumentation/alibaba-druid-1.0/javaagent/build.gradle.kts b/instrumentation/alibaba-druid-1.0/javaagent/build.gradle.kts index f0f6141eb0b0..a09f94a642b2 100644 --- a/instrumentation/alibaba-druid-1.0/javaagent/build.gradle.kts +++ b/instrumentation/alibaba-druid-1.0/javaagent/build.gradle.kts @@ -23,8 +23,9 @@ val collectMetadata = findProperty("collectMetadata")?.toString() ?: "false" tasks { val testStableSemconv by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath jvmArgs("-Dotel.semconv-stability.opt-in=database") - systemProperty("collectMetadata", collectMetadata) systemProperty("metadataConfig", "otel.semconv-stability.opt-in=database") } diff --git a/instrumentation/alibaba-druid-1.0/library/README.md b/instrumentation/alibaba-druid-1.0/library/README.md new file mode 100644 index 000000000000..badcfd54ac10 --- /dev/null +++ b/instrumentation/alibaba-druid-1.0/library/README.md @@ -0,0 +1,54 @@ +# Library Instrumentation for Alibaba Druid version 1.0 and higher + +Provides OpenTelemetry instrumentation for [Alibaba Druid](https://github.com/alibaba/druid), +enabling database connection pool metrics for druid data sources. + +## Quickstart + +### Add these dependencies to your project + +Replace `OPENTELEMETRY_VERSION` with the [latest release](https://central.sonatype.com/artifact/io.opentelemetry.instrumentation/opentelemetry-alibaba-druid-1.0). + +For Maven, add to your `pom.xml` dependencies: + +```xml + + + io.opentelemetry.instrumentation + opentelemetry-alibaba-druid-1.0 + OPENTELEMETRY_VERSION + + +``` + +For Gradle, add to your dependencies: + +```kotlin +implementation("io.opentelemetry.instrumentation:opentelemetry-alibaba-druid-1.0:OPENTELEMETRY_VERSION") +``` + +### Usage + +```java +import com.alibaba.druid.pool.DruidDataSource; +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.instrumentation.alibabadruid.v1_0.DruidTelemetry; + +// ... + +// Get an OpenTelemetry instance +OpenTelemetry openTelemetry = ...; + +// Create a DruidTelemetry instance +DruidTelemetry druidTelemetry = DruidTelemetry.create(openTelemetry); + +// Create a DruidDataSource +DruidDataSource dataSource = new DruidDataSource(); +// ... configure the dataSource + +// Register the dataSource for metrics +druidTelemetry.registerMetrics(dataSource, "my-druid-pool"); + +// Unregister the dataSource when it's no longer needed +druidTelemetry.unregisterMetrics(dataSource); +``` diff --git a/instrumentation/alibaba-druid-1.0/library/build.gradle.kts b/instrumentation/alibaba-druid-1.0/library/build.gradle.kts index 6101530383e6..bc6ff46a63dd 100644 --- a/instrumentation/alibaba-druid-1.0/library/build.gradle.kts +++ b/instrumentation/alibaba-druid-1.0/library/build.gradle.kts @@ -11,6 +11,8 @@ dependencies { tasks { val testStableSemconv by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath jvmArgs("-Dotel.semconv-stability.opt-in=database") } diff --git a/instrumentation/alibaba-druid-1.0/metadata.yaml b/instrumentation/alibaba-druid-1.0/metadata.yaml index fea1243befb7..07b26ff1c997 100644 --- a/instrumentation/alibaba-druid-1.0/metadata.yaml +++ b/instrumentation/alibaba-druid-1.0/metadata.yaml @@ -1,3 +1,4 @@ description: > The Alibaba Druid instrumentation generates database connection pool metrics for druid data sources. +library_link: https://github.com/alibaba/druid diff --git a/instrumentation/apache-dbcp-2.0/javaagent/build.gradle.kts b/instrumentation/apache-dbcp-2.0/javaagent/build.gradle.kts index 708d2b7b1ae4..1963fec28b6f 100644 --- a/instrumentation/apache-dbcp-2.0/javaagent/build.gradle.kts +++ b/instrumentation/apache-dbcp-2.0/javaagent/build.gradle.kts @@ -23,8 +23,10 @@ val collectMetadata = findProperty("collectMetadata")?.toString() ?: "false" tasks { val testStableSemconv by registering(Test::class) { - jvmArgs("-Dotel.semconv-stability.opt-in=database") + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath + jvmArgs("-Dotel.semconv-stability.opt-in=database") systemProperty("collectMetadata", collectMetadata) systemProperty("metadataConfig", "otel.semconv-stability.opt-in=database") } diff --git a/instrumentation/apache-dbcp-2.0/library/README.md b/instrumentation/apache-dbcp-2.0/library/README.md index 04c3e98c3764..3a7a0a2808ed 100644 --- a/instrumentation/apache-dbcp-2.0/library/README.md +++ b/instrumentation/apache-dbcp-2.0/library/README.md @@ -7,7 +7,7 @@ Provides OpenTelemetry instrumentation for [Apache DBCP](https://commons.apache. ### Add these dependencies to your project Replace `OPENTELEMETRY_VERSION` with the [latest -release](https://search.maven.org/search?q=g:io.opentelemetry.instrumentation%20AND%20a:opentelemetry-apache-dbcp-2.0). +release](https://central.sonatype.com/artifact/io.opentelemetry.instrumentation/opentelemetry-apache-dbcp-2.0). For Maven, add to your `pom.xml` dependencies: diff --git a/instrumentation/apache-dbcp-2.0/library/build.gradle.kts b/instrumentation/apache-dbcp-2.0/library/build.gradle.kts index a662876a4782..eb7afb1d91c0 100644 --- a/instrumentation/apache-dbcp-2.0/library/build.gradle.kts +++ b/instrumentation/apache-dbcp-2.0/library/build.gradle.kts @@ -11,6 +11,9 @@ dependencies { tasks { val testStableSemconv by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath + jvmArgs("-Dotel.semconv-stability.opt-in=database") } diff --git a/instrumentation/apache-dbcp-2.0/metadata.yaml b/instrumentation/apache-dbcp-2.0/metadata.yaml index b4f5c4be5557..52981169ad5c 100644 --- a/instrumentation/apache-dbcp-2.0/metadata.yaml +++ b/instrumentation/apache-dbcp-2.0/metadata.yaml @@ -5,3 +5,4 @@ description: > only activates if the `BasicDataSource` is registered to an `MBeanServer`. If using Spring Boot, this happens automatically as all Spring beans that support JMX registration are automatically registered by default. +library_link: https://commons.apache.org/proper/commons-dbcp/ diff --git a/instrumentation/apache-dubbo-2.7/metadata.yaml b/instrumentation/apache-dubbo-2.7/metadata.yaml index e8bb5ff3faf4..2deedb82d52e 100644 --- a/instrumentation/apache-dubbo-2.7/metadata.yaml +++ b/instrumentation/apache-dubbo-2.7/metadata.yaml @@ -1,7 +1,8 @@ -description: The Apache Dubbo instrumentation provides client and server spans for Apache Dubbo - RPC calls. Each call produces a span named after the Dubbo method, enriched with standard RPC - attributes (system, service, method), network attributes, and error details if an exception - occurs. +description: The Apache Dubbo instrumentation provides RPC client spans and RPC server spans for + Apache Dubbo RPC calls. Each call produces a span named after the Dubbo method, enriched with + standard RPC attributes (system, service, method), network attributes, and error details if an + exception occurs. +library_link: https://github.com/apache/dubbo/ configurations: - name: otel.instrumentation.common.peer-service-mapping description: Used to specify a mapping from host names or IP addresses to peer services. diff --git a/instrumentation/apache-httpasyncclient-4.1/metadata.yaml b/instrumentation/apache-httpasyncclient-4.1/metadata.yaml index eb4297e91dfb..033d214b4b9c 100644 --- a/instrumentation/apache-httpasyncclient-4.1/metadata.yaml +++ b/instrumentation/apache-httpasyncclient-4.1/metadata.yaml @@ -1 +1,2 @@ -description: This instrumentation enables CLIENT spans and metrics for the Apache HttpAsyncClient. +description: This instrumentation enables HTTP client spans and HTTP client metrics for the Apache HttpAsyncClient. +library_link: https://hc.apache.org/index.html diff --git a/instrumentation/apache-httpclient/apache-httpclient-2.0/metadata.yaml b/instrumentation/apache-httpclient/apache-httpclient-2.0/metadata.yaml index 8947340aaa9d..000fb1800be1 100644 --- a/instrumentation/apache-httpclient/apache-httpclient-2.0/metadata.yaml +++ b/instrumentation/apache-httpclient/apache-httpclient-2.0/metadata.yaml @@ -1 +1,2 @@ -description: This instrumentation enables CLIENT spans and metrics for versions 2 and 3 of the Apache HttpClient. +description: This instrumentation enables HTTP client spans and HTTP client metrics for versions 2 and 3 of the Apache HttpClient. +library_link: https://hc.apache.org/index.html diff --git a/instrumentation/apache-httpclient/apache-httpclient-4.0/metadata.yaml b/instrumentation/apache-httpclient/apache-httpclient-4.0/metadata.yaml index f38a5943cb29..39d3f42f5258 100644 --- a/instrumentation/apache-httpclient/apache-httpclient-4.0/metadata.yaml +++ b/instrumentation/apache-httpclient/apache-httpclient-4.0/metadata.yaml @@ -1 +1,2 @@ -description: This instrumentation enables CLIENT spans and metrics for version 4 of the Apache HttpClient. +description: This instrumentation enables HTTP client spans and HTTP client metrics for version 4 of the Apache HttpClient. +library_link: https://hc.apache.org/index.html diff --git a/instrumentation/apache-httpclient/apache-httpclient-4.3/library/README.md b/instrumentation/apache-httpclient/apache-httpclient-4.3/library/README.md new file mode 100644 index 000000000000..60a728ce8ddb --- /dev/null +++ b/instrumentation/apache-httpclient/apache-httpclient-4.3/library/README.md @@ -0,0 +1,48 @@ +# Library Instrumentation for Apache HttpClient version 4.3 and higher + +Provides OpenTelemetry instrumentation for the [Apache HttpClient](https://hc.apache.org/httpcomponents-client-ga/), enabling HTTP client spans and metrics. + +## Quickstart + +### Add these dependencies to your project + +Replace `OPENTELEMETRY_VERSION` with the [latest release](https://central.sonatype.com/artifact/io.opentelemetry.instrumentation/opentelemetry-apache-httpclient-4.3). + +For Maven, add to your `pom.xml` dependencies: + +```xml + + + io.opentelemetry.instrumentation + opentelemetry-apache-httpclient-4.3 + OPENTELEMETRY_VERSION + + +``` + +For Gradle, add to your dependencies: + +```kotlin +implementation("io.opentelemetry.instrumentation:opentelemetry-apache-httpclient-4.3:OPENTELEMETRY_VERSION") +``` + +### Usage + +```java +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.instrumentation.apachehttpclient.v4_3.ApacheHttpClientTelemetry; +import org.apache.http.impl.client.CloseableHttpClient; + +// ... + +// Get an OpenTelemetry instance +OpenTelemetry openTelemetry = ...; + +// Create an ApacheHttpClientTelemetry instance +ApacheHttpClientTelemetry telemetry = ApacheHttpClientTelemetry.create(openTelemetry); + +// Get a traced HttpClient +CloseableHttpClient httpClient = telemetry.newHttpClient(); + +// ... use the httpClient to make requests +``` diff --git a/instrumentation/apache-httpclient/apache-httpclient-4.3/metadata.yaml b/instrumentation/apache-httpclient/apache-httpclient-4.3/metadata.yaml index 7ca71c47fe24..bd364a6a5ef9 100644 --- a/instrumentation/apache-httpclient/apache-httpclient-4.3/metadata.yaml +++ b/instrumentation/apache-httpclient/apache-httpclient-4.3/metadata.yaml @@ -1 +1,2 @@ -description: This instrumentation provides a library integration that enables CLIENT spans and metrics for the Apache HttpClient. +description: This instrumentation provides a library integration that enables HTTP client spans and HTTP client metrics for the Apache HttpClient. +library_link: https://hc.apache.org/index.html diff --git a/instrumentation/apache-httpclient/apache-httpclient-5.0/metadata.yaml b/instrumentation/apache-httpclient/apache-httpclient-5.0/metadata.yaml index d5abba280e31..6400d25c59f6 100644 --- a/instrumentation/apache-httpclient/apache-httpclient-5.0/metadata.yaml +++ b/instrumentation/apache-httpclient/apache-httpclient-5.0/metadata.yaml @@ -1 +1,2 @@ -description: This instrumentation enables CLIENT spans and metrics for version 5 of the Apache HttpClient. +description: This instrumentation enables HTTP client spans and HTTP client metrics for version 5 of the Apache HttpClient. +library_link: https://hc.apache.org/index.html diff --git a/instrumentation/apache-httpclient/apache-httpclient-5.2/library/README.md b/instrumentation/apache-httpclient/apache-httpclient-5.2/library/README.md index fb9002c5b6f2..4d3c90e82033 100644 --- a/instrumentation/apache-httpclient/apache-httpclient-5.2/library/README.md +++ b/instrumentation/apache-httpclient/apache-httpclient-5.2/library/README.md @@ -7,7 +7,7 @@ Provides OpenTelemetry instrumentation for [Apache Http Client 5.2](https://hc.a ### Add these dependencies to your project Replace `OPENTELEMETRY_VERSION` with the [latest -release](https://search.maven.org/search?q=g:io.opentelemetry.instrumentation%20AND%20a:opentelemetry-apache-httpclient-5.2). +release]( https://central.sonatype.com/artifact/io.opentelemetry.instrumentation/opentelemetry-apache-httpclient-5.2). For Maven, add to your `pom.xml` dependencies: diff --git a/instrumentation/apache-httpclient/apache-httpclient-5.2/library/metadata.yaml b/instrumentation/apache-httpclient/apache-httpclient-5.2/library/metadata.yaml index eb4297e91dfb..44d209456cfa 100644 --- a/instrumentation/apache-httpclient/apache-httpclient-5.2/library/metadata.yaml +++ b/instrumentation/apache-httpclient/apache-httpclient-5.2/library/metadata.yaml @@ -1 +1,2 @@ description: This instrumentation enables CLIENT spans and metrics for the Apache HttpAsyncClient. +library_link: https://hc.apache.org/index.html diff --git a/instrumentation/apache-httpclient/apache-httpclient-5.2/metadata.yaml b/instrumentation/apache-httpclient/apache-httpclient-5.2/metadata.yaml index 7ca71c47fe24..bd364a6a5ef9 100644 --- a/instrumentation/apache-httpclient/apache-httpclient-5.2/metadata.yaml +++ b/instrumentation/apache-httpclient/apache-httpclient-5.2/metadata.yaml @@ -1 +1,2 @@ -description: This instrumentation provides a library integration that enables CLIENT spans and metrics for the Apache HttpClient. +description: This instrumentation provides a library integration that enables HTTP client spans and HTTP client metrics for the Apache HttpClient. +library_link: https://hc.apache.org/index.html diff --git a/instrumentation/apache-shenyu-2.4/metadata.yaml b/instrumentation/apache-shenyu-2.4/metadata.yaml index 54b38ebaf92c..ab95d42e9305 100644 --- a/instrumentation/apache-shenyu-2.4/metadata.yaml +++ b/instrumentation/apache-shenyu-2.4/metadata.yaml @@ -1,6 +1,7 @@ description: > - This instrumentation does not emit telemetry on its own. Instead, it augments existing SERVER + This instrumentation does not emit telemetry on its own. Instead, it augments existing HTTP server spans and HTTP server metrics with the HTTP route and Shenyu specific attributes. +library_link: https://shenyu.apache.org/ configurations: - name: otel.instrumentation.apache-shenyu.experimental-span-attributes description: > diff --git a/instrumentation/armeria/armeria-1.3/library/README.md b/instrumentation/armeria/armeria-1.3/library/README.md new file mode 100644 index 000000000000..71e107994d76 --- /dev/null +++ b/instrumentation/armeria/armeria-1.3/library/README.md @@ -0,0 +1,76 @@ +# Library Instrumentation for Armeria version 1.3 and higher + +Provides OpenTelemetry instrumentation for [Armeria](https://armeria.dev/), enabling HTTP client spans and metrics, +and HTTP server spans and metrics. + +## Quickstart + +### Add these dependencies to your project + +Replace `OPENTELEMETRY_VERSION` with the [latest release](https://central.sonatype.com/artifact/io.opentelemetry.instrumentation/opentelemetry-armeria-1.3). + +For Maven, add to your `pom.xml` dependencies: + +```xml + + + io.opentelemetry.instrumentation + opentelemetry-armeria-1.3 + OPENTELEMETRY_VERSION + + +``` + +For Gradle, add to your dependencies: + +```kotlin +implementation("io.opentelemetry.instrumentation:opentelemetry-armeria-1.3:OPENTELEMETRY_VERSION") +``` + +### Usage + +#### Server + +```java +import com.linecorp.armeria.server.Server; +import com.linecorp.armeria.server.ServerBuilder; +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.instrumentation.armeria.v1_3.ArmeriaServerTelemetry; + +// ... + +// Get an OpenTelemetry instance +OpenTelemetry openTelemetry = ...; + +// Create an ArmeriaServerTelemetry instance +ArmeriaServerTelemetry telemetry = ArmeriaServerTelemetry.create(openTelemetry); + +// Add the decorator to your server builder +Server server = Server.builder() + .decorator(telemetry.newDecorator()) + // ... other server configuration + .build(); +``` + +#### Client + +```java +import com.linecorp.armeria.client.ClientBuilder; +import com.linecorp.armeria.client.WebClient; +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.instrumentation.armeria.v1_3.ArmeriaClientTelemetry; + +// ... + +// Get an OpenTelemetry instance +OpenTelemetry openTelemetry = ...; + +// Create an ArmeriaClientTelemetry instance +ArmeriaClientTelemetry telemetry = ArmeriaClientTelemetry.create(openTelemetry); + +// Add the decorator to your client builder +WebClient client = new ClientBuilder("http://my-service.com") + .decorator(telemetry.newDecorator()) + // ... other client configuration + .build(WebClient.class); +``` diff --git a/instrumentation/armeria/armeria-1.3/metadata.yaml b/instrumentation/armeria/armeria-1.3/metadata.yaml index b58435f956a6..2193e2b895ad 100644 --- a/instrumentation/armeria/armeria-1.3/metadata.yaml +++ b/instrumentation/armeria/armeria-1.3/metadata.yaml @@ -1 +1,4 @@ -description: This instrumentation enables CLIENT and SERVER spans and metrics for the Armeria HTTP client and server. +description: > + This instrumentation enables HTTP client spans and metrics for the Armeria HTTP client, and HTTP + server spans and metrics for the Armeria HTTP server. +library_link: https://armeria.dev/ diff --git a/instrumentation/armeria/armeria-grpc-1.14/javaagent/build.gradle.kts b/instrumentation/armeria/armeria-grpc-1.14/javaagent/build.gradle.kts index 93bcfca7ee48..1b798164b322 100644 --- a/instrumentation/armeria/armeria-grpc-1.14/javaagent/build.gradle.kts +++ b/instrumentation/armeria/armeria-grpc-1.14/javaagent/build.gradle.kts @@ -24,6 +24,11 @@ dependencies { testLibrary("com.linecorp.armeria:armeria-junit5:1.14.0") } +tasks.named("checkstyleTest") { + // exclude generated classes + exclude("**/example/**") +} + val latestDepTest = findProperty("testLatestDeps") as Boolean protobuf { protoc { diff --git a/instrumentation/armeria/armeria-grpc-1.14/metadata.yaml b/instrumentation/armeria/armeria-grpc-1.14/metadata.yaml index 480139208df6..6fb1394a9b1a 100644 --- a/instrumentation/armeria/armeria-grpc-1.14/metadata.yaml +++ b/instrumentation/armeria/armeria-grpc-1.14/metadata.yaml @@ -1 +1,4 @@ -description: This instrumentation enables CLIENT and SERVER spans and metrics for the Armeria gRPC client and server. +description: > + This instrumentation enables RPC client spans and metrics for the Armeria gRPC client, and RPC + server spans and metrics for the Armeria gRPC server. +library_link: https://armeria.dev/ diff --git a/instrumentation/async-http-client/async-http-client-1.9/metadata.yaml b/instrumentation/async-http-client/async-http-client-1.9/metadata.yaml index 2badb74d86d4..1ff1e4f33ad2 100644 --- a/instrumentation/async-http-client/async-http-client-1.9/metadata.yaml +++ b/instrumentation/async-http-client/async-http-client-1.9/metadata.yaml @@ -1 +1,2 @@ -description: This instrumentation enables CLIENT spans and metrics for version 1 of the AsyncHttpClient (AHC) HTTP client. +description: This instrumentation enables HTTP client spans and HTTP client metrics for version 1 of the AsyncHttpClient (AHC) HTTP client. +library_link: https://github.com/AsyncHttpClient/async-http-client diff --git a/instrumentation/async-http-client/async-http-client-2.0/metadata.yaml b/instrumentation/async-http-client/async-http-client-2.0/metadata.yaml index 92cff9602ef9..c965d8494436 100644 --- a/instrumentation/async-http-client/async-http-client-2.0/metadata.yaml +++ b/instrumentation/async-http-client/async-http-client-2.0/metadata.yaml @@ -1 +1,2 @@ -description: This instrumentation enables CLIENT spans and metrics for version 2 of the AsyncHttpClient (AHC) HTTP client. +description: This instrumentation enables HTTP client spans and HTTP client metrics for version 2 of the AsyncHttpClient (AHC) HTTP client. +library_link: https://github.com/AsyncHttpClient/async-http-client diff --git a/instrumentation/avaje-jex-3.0/metadata.yaml b/instrumentation/avaje-jex-3.0/metadata.yaml index 7b96ea51bd23..298a92934ea2 100644 --- a/instrumentation/avaje-jex-3.0/metadata.yaml +++ b/instrumentation/avaje-jex-3.0/metadata.yaml @@ -1,4 +1,5 @@ description: > This instrumentation does not emit telemetry on its own. Instead, it hooks into the Avaje Jex - Context to extract the HTTP route and attach it to existing SERVER spans and HTTP server + Context to extract the HTTP route and attach it to existing HTTP server spans and HTTP server metrics. +library_link: https://avaje.io/jex/ diff --git a/instrumentation/aws-lambda/README.md b/instrumentation/aws-lambda/README.md index 9173db38f84b..83d3a7f98bc2 100644 --- a/instrumentation/aws-lambda/README.md +++ b/instrumentation/aws-lambda/README.md @@ -7,5 +7,8 @@ We provide two packages for instrumenting AWS lambda functions. `aws-lambda-java-events`. This also includes when you are using `aws-serverless-java-container` to run e.g., a Spring Boot application on Lambda. -- [aws-lambda-events-2.2](./aws-lambda-events-2.2/library) provides full instrumentation of the Lambda library, including standard - and custom event types, from `aws-lambda-java-events` 2.2+. +- [aws-lambda-events-2.2](./aws-lambda-events-2.2/library) (Library instrumentation is deprecated) provides instrumentation of the Lambda library, + including standard and custom event types, from `aws-lambda-java-events` 2.2+. + +- [aws-lambda-events-3.11](./aws-lambda-events-2.2/library) provides instrumentation of the Lambda library, including standard and custom event + types, from `aws-lambda-java-events` 3.11+. diff --git a/instrumentation/aws-lambda/aws-lambda-core-1.0/library/README.md b/instrumentation/aws-lambda/aws-lambda-core-1.0/library/README.md index 0af6179dd320..c0ee14cedcd2 100644 --- a/instrumentation/aws-lambda/aws-lambda-core-1.0/library/README.md +++ b/instrumentation/aws-lambda/aws-lambda-core-1.0/library/README.md @@ -50,7 +50,7 @@ link to tracing information provided by Lambda itself. To do so, add a dependenc `io.opentelemetry.contrib:opentelemetry-aws-xray-propagator`. Replace `OPENTELEMETRY_VERSION` with the [latest -release](https://search.maven.org/search?q=g:io.opentelemetry.contrib%20AND%20a:opentelemetry-aws-xray-propagator). +release]( https://central.sonatype.com/artifact/io.opentelemetry.contrib/opentelemetry-aws-xray-propagator). Gradle: diff --git a/instrumentation/aws-lambda/aws-lambda-core-1.0/metadata.yaml b/instrumentation/aws-lambda/aws-lambda-core-1.0/metadata.yaml index bd5ba34b9aec..9874a606a002 100644 --- a/instrumentation/aws-lambda/aws-lambda-core-1.0/metadata.yaml +++ b/instrumentation/aws-lambda/aws-lambda-core-1.0/metadata.yaml @@ -1,9 +1,14 @@ description: > Provides lightweight instrumentation of the Lambda core library, supporting all versions. It - generates FaaS SERVER spans with the `faas.invocation_id` attribute. Use this package if you only + generates FaaS server spans with the `faas.invocation_id` attribute. Use this package if you only use `RequestStreamHandler` or know you don't use any event classes from `aws-lambda-java-events`. This also includes when you are using `aws-serverless-java-container` to run e.g., a Spring Boot application on Lambda. + + For custom wrappers when using library instrumentation, you can configure the `OTEL_INSTRUMENTATION_AWS_LAMBDA_HANDLER` + environment variable to contain your lambda handler method (in the format `package.ClassName::methodName`) and use + one of wrappers as your lambda `Handler`. +library_link: https://docs.aws.amazon.com/lambda/latest/dg/java-handler.html configurations: - name: otel.instrumentation.aws-lambda.flush-timeout type: int diff --git a/instrumentation/aws-lambda/aws-lambda-events-2.2/javaagent/build.gradle.kts b/instrumentation/aws-lambda/aws-lambda-events-2.2/javaagent/build.gradle.kts index acfad8626dde..dba82d6f6c5a 100644 --- a/instrumentation/aws-lambda/aws-lambda-events-2.2/javaagent/build.gradle.kts +++ b/instrumentation/aws-lambda/aws-lambda-events-2.2/javaagent/build.gradle.kts @@ -17,7 +17,7 @@ dependencies { implementation(project(":instrumentation:aws-lambda:aws-lambda-core-1.0:library")) - implementation(project(":instrumentation:aws-lambda:aws-lambda-events-2.2:library")) { + implementation(project(":instrumentation:aws-lambda:aws-lambda-events-common-2.2:library")) { // Only needed by wrappers, not the javaagent. Muzzle will catch if we accidentally change this. exclude("com.fasterxml.jackson.core", "jackson-databind") } diff --git a/instrumentation/aws-lambda/aws-lambda-events-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/awslambdaevents/v2_2/AwsLambdaSingletons.java b/instrumentation/aws-lambda/aws-lambda-events-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/awslambdaevents/v2_2/AwsLambdaSingletons.java index 1ecafdeb19f1..8ffd8f7a10bd 100644 --- a/instrumentation/aws-lambda/aws-lambda-events-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/awslambdaevents/v2_2/AwsLambdaSingletons.java +++ b/instrumentation/aws-lambda/aws-lambda-events-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/awslambdaevents/v2_2/AwsLambdaSingletons.java @@ -10,19 +10,21 @@ import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; import io.opentelemetry.instrumentation.awslambdacore.v1_0.internal.AwsLambdaFunctionInstrumenter; import io.opentelemetry.instrumentation.awslambdacore.v1_0.internal.WrapperConfiguration; -import io.opentelemetry.instrumentation.awslambdaevents.v2_2.internal.AwsLambdaEventsInstrumenterFactory; -import io.opentelemetry.instrumentation.awslambdaevents.v2_2.internal.AwsLambdaSqsInstrumenterFactory; +import io.opentelemetry.instrumentation.awslambdaevents.common.v2_2.internal.AwsLambdaEventsInstrumenterFactory; +import io.opentelemetry.instrumentation.awslambdaevents.common.v2_2.internal.AwsLambdaSqsInstrumenterFactory; import io.opentelemetry.javaagent.bootstrap.internal.AgentCommonConfig; import io.opentelemetry.javaagent.bootstrap.internal.AgentInstrumentationConfig; import java.time.Duration; public final class AwsLambdaSingletons { - + private static final String INSTRUMENTATION_NAME = "io.opentelemetry.aws-lambda-events-2.2"; private static final AwsLambdaFunctionInstrumenter FUNCTION_INSTRUMENTER = AwsLambdaEventsInstrumenterFactory.createInstrumenter( - GlobalOpenTelemetry.get(), AgentCommonConfig.get().getKnownHttpRequestMethods()); + GlobalOpenTelemetry.get(), + INSTRUMENTATION_NAME, + AgentCommonConfig.get().getKnownHttpRequestMethods()); private static final Instrumenter MESSAGE_TRACER = - AwsLambdaSqsInstrumenterFactory.forEvent(GlobalOpenTelemetry.get()); + AwsLambdaSqsInstrumenterFactory.forEvent(GlobalOpenTelemetry.get(), INSTRUMENTATION_NAME); private static final Duration FLUSH_TIMEOUT = Duration.ofMillis( AgentInstrumentationConfig.get() diff --git a/instrumentation/aws-lambda/aws-lambda-events-2.2/library/build.gradle.kts b/instrumentation/aws-lambda/aws-lambda-events-2.2/library/build.gradle.kts index baae82471bf5..b57f150698a2 100644 --- a/instrumentation/aws-lambda/aws-lambda-events-2.2/library/build.gradle.kts +++ b/instrumentation/aws-lambda/aws-lambda-events-2.2/library/build.gradle.kts @@ -4,13 +4,12 @@ plugins { dependencies { api(project(":instrumentation:aws-lambda:aws-lambda-core-1.0:library")) + implementation(project(":instrumentation:aws-lambda:aws-lambda-events-common-2.2:library")) + compileOnly(project(":instrumentation:aws-lambda:aws-lambda-events-3.11:library")) compileOnly("io.opentelemetry:opentelemetry-sdk") compileOnly("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure") - compileOnly("com.google.auto.value:auto-value-annotations") - annotationProcessor("com.google.auto.value:auto-value") - library("com.amazonaws:aws-lambda-java-core:1.0.0") // First version to includes support for SQSEvent, currently the most popular message queue used // with lambda. @@ -25,13 +24,6 @@ dependencies { // So that is the reason that why we add it as compile only dependency. compileOnly("com.amazonaws:aws-lambda-java-serialization:1.1.5") - // We need Jackson for wrappers to reproduce the serialization does when Lambda invokes a RequestHandler with event - // since Lambda will only be able to invoke the wrapper itself with a generic Object. - // Note that Lambda itself uses Jackson, but does not expose it to the function so we need to include it here. - // TODO: Switch to aws-lambda-java-serialization to more robustly follow Lambda's serialization logic. - implementation("com.fasterxml.jackson.core:jackson-databind") - implementation("io.opentelemetry.contrib:opentelemetry-aws-xray-propagator") - // allows to get the function ARN testLibrary("com.amazonaws:aws-lambda-java-core:1.2.1") // allows to get the default events diff --git a/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/TracingRequestApiGatewayWrapper.java b/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/TracingRequestApiGatewayWrapper.java index 711b7e3079a0..717cf7d30de9 100644 --- a/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/TracingRequestApiGatewayWrapper.java +++ b/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/TracingRequestApiGatewayWrapper.java @@ -9,7 +9,7 @@ import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent; import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent; import io.opentelemetry.instrumentation.awslambdacore.v1_0.internal.WrappedLambda; -import io.opentelemetry.instrumentation.awslambdaevents.v2_2.internal.SerializationUtil; +import io.opentelemetry.instrumentation.awslambdaevents.common.v2_2.internal.SerializationUtil; import io.opentelemetry.sdk.OpenTelemetrySdk; import java.util.function.BiFunction; @@ -17,7 +17,12 @@ * Wrapper for {@link io.opentelemetry.instrumentation.awslambdacore.v1_0.TracingRequestHandler}. * Allows for wrapping a lambda proxied through API Gateway, enabling single span tracing and HTTP * context propagation. + * + * @deprecated use {@link + * io.opentelemetry.instrumentation.awslambdaevents.v3_11.TracingRequestApiGatewayWrapper} + * instead. */ +@Deprecated public class TracingRequestApiGatewayWrapper extends TracingRequestWrapperBase { diff --git a/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/TracingRequestWrapper.java b/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/TracingRequestWrapper.java index 484ee169d623..a062beea246c 100644 --- a/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/TracingRequestWrapper.java +++ b/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/TracingRequestWrapper.java @@ -12,7 +12,8 @@ import io.opentelemetry.instrumentation.awslambdacore.v1_0.internal.ApiGatewayProxyRequest; import io.opentelemetry.instrumentation.awslambdacore.v1_0.internal.MapUtils; import io.opentelemetry.instrumentation.awslambdacore.v1_0.internal.WrappedLambda; -import io.opentelemetry.instrumentation.awslambdaevents.v2_2.internal.SerializationUtil; +import io.opentelemetry.instrumentation.awslambdaevents.common.v2_2.internal.LambdaParameters; +import io.opentelemetry.instrumentation.awslambdaevents.common.v2_2.internal.SerializationUtil; import io.opentelemetry.sdk.OpenTelemetrySdk; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; @@ -26,7 +27,11 @@ /** * Wrapper for {@link com.amazonaws.services.lambda.runtime.RequestHandler} based Lambda handlers. + * + * @deprecated use {@link + * io.opentelemetry.instrumentation.awslambdaevents.v3_11.TracingRequestWrapper} instead. */ +@Deprecated public class TracingRequestWrapper extends TracingRequestStreamWrapper { public TracingRequestWrapper() { super(); diff --git a/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/TracingRequestWrapperBase.java b/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/TracingRequestWrapperBase.java index 5169c8f121ee..239cf076201a 100644 --- a/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/TracingRequestWrapperBase.java +++ b/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/TracingRequestWrapperBase.java @@ -12,7 +12,8 @@ import io.opentelemetry.instrumentation.awslambdacore.v1_0.internal.MapUtils; import io.opentelemetry.instrumentation.awslambdacore.v1_0.internal.WrappedLambda; import io.opentelemetry.instrumentation.awslambdacore.v1_0.internal.WrapperConfiguration; -import io.opentelemetry.instrumentation.awslambdaevents.v2_2.internal.AwsLambdaEventsInstrumenterFactory; +import io.opentelemetry.instrumentation.awslambdaevents.common.v2_2.internal.AwsLambdaEventsInstrumenterFactory; +import io.opentelemetry.instrumentation.awslambdaevents.common.v2_2.internal.LambdaParameters; import io.opentelemetry.sdk.OpenTelemetrySdk; import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk; import java.lang.reflect.InvocationTargetException; @@ -26,6 +27,7 @@ * env property OTEL_INSTRUMENTATION_AWS_LAMBDA_HANDLER in package.ClassName::methodName format */ abstract class TracingRequestWrapperBase extends TracingRequestHandler { + private static final String INSTRUMENTATION_NAME = "io.opentelemetry.aws-lambda-events-2.2"; private final WrappedLambda wrappedLambda; private final Method targetMethod; @@ -47,7 +49,7 @@ protected TracingRequestWrapperBase(BiFunction, Object> parameterMap openTelemetrySdk, WrapperConfiguration.flushTimeout(), AwsLambdaEventsInstrumenterFactory.createInstrumenter( - openTelemetrySdk, HttpConstants.KNOWN_METHODS)); + openTelemetrySdk, INSTRUMENTATION_NAME, HttpConstants.KNOWN_METHODS)); this.wrappedLambda = wrappedLambda; this.targetMethod = wrappedLambda.getRequestTargetMethod(); this.parameterMapper = parameterMapper; diff --git a/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/TracingSqsEventHandler.java b/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/TracingSqsEventHandler.java index 1ca7835c01da..4924b0e010fe 100644 --- a/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/TracingSqsEventHandler.java +++ b/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/TracingSqsEventHandler.java @@ -10,11 +10,17 @@ import io.opentelemetry.context.Scope; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; import io.opentelemetry.instrumentation.awslambdacore.v1_0.TracingRequestHandler; -import io.opentelemetry.instrumentation.awslambdaevents.v2_2.internal.AwsLambdaSqsInstrumenterFactory; +import io.opentelemetry.instrumentation.awslambdaevents.common.v2_2.internal.AwsLambdaSqsInstrumenterFactory; import io.opentelemetry.sdk.OpenTelemetrySdk; import java.time.Duration; +/** + * @deprecated use {@link + * io.opentelemetry.instrumentation.awslambdaevents.v3_11.TracingSqsEventHandler} instead. + */ +@Deprecated public abstract class TracingSqsEventHandler extends TracingRequestHandler { + static final String INSTRUMENTATION_NAME = "io.opentelemetry.aws-lambda-events-2.2"; private final Instrumenter instrumenter; @@ -33,7 +39,9 @@ protected TracingSqsEventHandler(OpenTelemetrySdk openTelemetrySdk) { */ protected TracingSqsEventHandler(OpenTelemetrySdk openTelemetrySdk, Duration flushTimeout) { this( - openTelemetrySdk, flushTimeout, AwsLambdaSqsInstrumenterFactory.forEvent(openTelemetrySdk)); + openTelemetrySdk, + flushTimeout, + AwsLambdaSqsInstrumenterFactory.forEvent(openTelemetrySdk, INSTRUMENTATION_NAME)); } /** diff --git a/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/TracingSqsEventWrapper.java b/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/TracingSqsEventWrapper.java index 7ace4248bca6..b01072940caf 100644 --- a/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/TracingSqsEventWrapper.java +++ b/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/TracingSqsEventWrapper.java @@ -9,11 +9,17 @@ import com.amazonaws.services.lambda.runtime.events.SQSEvent; import io.opentelemetry.instrumentation.awslambdacore.v1_0.internal.WrappedLambda; import io.opentelemetry.instrumentation.awslambdacore.v1_0.internal.WrapperConfiguration; +import io.opentelemetry.instrumentation.awslambdaevents.common.v2_2.internal.LambdaParameters; import io.opentelemetry.sdk.OpenTelemetrySdk; import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +/** + * @deprecated use {@link + * io.opentelemetry.instrumentation.awslambdaevents.v3_11.TracingSqsEventWrapper} instead. + */ +@Deprecated public class TracingSqsEventWrapper extends TracingSqsEventHandler { private final WrappedLambda wrappedLambda; diff --git a/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/TracingSqsMessageHandler.java b/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/TracingSqsMessageHandler.java index 9e82199d3d31..94acccbd8f75 100644 --- a/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/TracingSqsMessageHandler.java +++ b/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/TracingSqsMessageHandler.java @@ -10,10 +10,15 @@ import com.amazonaws.services.lambda.runtime.events.SQSEvent.SQSMessage; import io.opentelemetry.context.Scope; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; -import io.opentelemetry.instrumentation.awslambdaevents.v2_2.internal.AwsLambdaSqsInstrumenterFactory; +import io.opentelemetry.instrumentation.awslambdaevents.common.v2_2.internal.AwsLambdaSqsInstrumenterFactory; import io.opentelemetry.sdk.OpenTelemetrySdk; import java.time.Duration; +/** + * @deprecated use {@link + * io.opentelemetry.instrumentation.awslambdaevents.v3_11.TracingSqsMessageHandler} instead. + */ +@Deprecated public abstract class TracingSqsMessageHandler extends TracingSqsEventHandler { private final Instrumenter messageInstrumenter; @@ -33,7 +38,9 @@ protected TracingSqsMessageHandler(OpenTelemetrySdk openTelemetrySdk) { */ protected TracingSqsMessageHandler(OpenTelemetrySdk openTelemetrySdk, Duration flushTimeout) { this( - openTelemetrySdk, flushTimeout, AwsLambdaSqsInstrumenterFactory.forEvent(openTelemetrySdk)); + openTelemetrySdk, + flushTimeout, + AwsLambdaSqsInstrumenterFactory.forEvent(openTelemetrySdk, INSTRUMENTATION_NAME)); } /** @@ -50,7 +57,7 @@ protected TracingSqsMessageHandler( openTelemetrySdk, flushTimeout, eventInstrumenter, - AwsLambdaSqsInstrumenterFactory.forMessage(openTelemetrySdk)); + AwsLambdaSqsInstrumenterFactory.forMessage(openTelemetrySdk, INSTRUMENTATION_NAME)); } /** diff --git a/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/test/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/AwsLambdaApiGatewayWrapperTest.java b/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/test/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/AwsLambdaApiGatewayWrapperTest.java index 4c37d92a5b83..f19e3b848816 100644 --- a/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/test/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/AwsLambdaApiGatewayWrapperTest.java +++ b/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/test/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/AwsLambdaApiGatewayWrapperTest.java @@ -36,6 +36,7 @@ import org.mockito.junit.jupiter.MockitoSettings; import org.mockito.quality.Strictness; +@SuppressWarnings("deprecation") // testing deprecated class @ExtendWith(MockitoExtension.class) @MockitoSettings(strictness = Strictness.LENIENT) class AwsLambdaApiGatewayWrapperTest { diff --git a/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/test/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/AwsLambdaSqsEventHandlerTest.java b/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/test/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/AwsLambdaSqsEventHandlerTest.java index f46fa01eb42b..e9486d53f2e1 100644 --- a/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/test/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/AwsLambdaSqsEventHandlerTest.java +++ b/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/test/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/AwsLambdaSqsEventHandlerTest.java @@ -13,6 +13,7 @@ import io.opentelemetry.sdk.OpenTelemetrySdk; import org.junit.jupiter.api.extension.RegisterExtension; +@SuppressWarnings("deprecation") // testing deprecated class class AwsLambdaSqsEventHandlerTest extends AbstractAwsLambdaSqsEventHandlerTest { @RegisterExtension diff --git a/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/test/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/AwsLambdaSqsEventWrapperTest.java b/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/test/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/AwsLambdaSqsEventWrapperTest.java index 18344969fd02..54e5e83055a1 100644 --- a/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/test/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/AwsLambdaSqsEventWrapperTest.java +++ b/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/test/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/AwsLambdaSqsEventWrapperTest.java @@ -32,6 +32,7 @@ import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; +@SuppressWarnings("deprecation") // testing deprecated class @ExtendWith(MockitoExtension.class) @SetEnvironmentVariable( key = WrappedLambda.OTEL_LAMBDA_HANDLER_ENV_KEY, diff --git a/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/test/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/AwsLambdaSqsMessageHandlerTest.java b/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/test/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/AwsLambdaSqsMessageHandlerTest.java index 04c6538bbe97..13deea3b52a7 100644 --- a/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/test/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/AwsLambdaSqsMessageHandlerTest.java +++ b/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/test/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/AwsLambdaSqsMessageHandlerTest.java @@ -36,6 +36,7 @@ import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; +@SuppressWarnings("deprecation") // testing deprecated class @ExtendWith(MockitoExtension.class) class AwsLambdaSqsMessageHandlerTest { diff --git a/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/test/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/AwsLambdaWrapperTest.java b/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/test/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/AwsLambdaWrapperTest.java index f72fe08f5d6f..d9af41616fcc 100644 --- a/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/test/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/AwsLambdaWrapperTest.java +++ b/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/test/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/AwsLambdaWrapperTest.java @@ -29,6 +29,7 @@ import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; +@SuppressWarnings("deprecation") // testing deprecated class @ExtendWith(MockitoExtension.class) class AwsLambdaWrapperTest { diff --git a/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/test/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/TracingRequestWrapperStandardEventsTest.java b/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/test/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/TracingRequestWrapperStandardEventsTest.java index c25d911ed1cb..1c9d773668f3 100644 --- a/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/test/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/TracingRequestWrapperStandardEventsTest.java +++ b/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/test/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/TracingRequestWrapperStandardEventsTest.java @@ -16,7 +16,7 @@ import com.amazonaws.services.lambda.runtime.events.SQSEvent; import com.amazonaws.services.lambda.runtime.events.ScheduledEvent; import io.opentelemetry.instrumentation.awslambdacore.v1_0.internal.WrappedLambda; -import io.opentelemetry.instrumentation.awslambdaevents.v2_2.internal.SerializationUtil; +import io.opentelemetry.instrumentation.awslambdaevents.common.v2_2.internal.SerializationUtil; import io.opentelemetry.sdk.OpenTelemetrySdk; import java.io.IOException; import java.util.HashMap; @@ -24,6 +24,7 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; +@SuppressWarnings("deprecation") // testing deprecated class class TracingRequestWrapperStandardEventsTest { private static final Map, EventInfo> EVENTS_JSON = buildEventExamples(); diff --git a/instrumentation/aws-lambda/aws-lambda-events-2.2/metadata.yaml b/instrumentation/aws-lambda/aws-lambda-events-2.2/metadata.yaml index 3dc33adf8ec7..5ebd193e9c1e 100644 --- a/instrumentation/aws-lambda/aws-lambda-events-2.2/metadata.yaml +++ b/instrumentation/aws-lambda/aws-lambda-events-2.2/metadata.yaml @@ -1,9 +1,16 @@ description: > - Provides full instrumentation of the Lambda library, including standard and custom event types, - from `aws-lambda-java-events` 2.2+. + This version of the library instrumentation is deprecated, please use the `aws-lambda-events-3.11` + library instrumentation instead. This instrumentation builds on top of the `aws-lambda-core-1.0` + instrumentation, expanding support to cover the Lambda library, including standard and custom event types. +library_link: https://docs.aws.amazon.com/lambda/latest/dg/java-handler.html configurations: - name: otel.instrumentation.aws-lambda.flush-timeout type: int default: 10000 description: Flush timeout in milliseconds. - + - name: otel.instrumentation.http.known-methods + description: > + Configures the instrumentation to recognize an alternative set of HTTP request methods. All + other methods will be treated as `_OTHER`. + type: list + default: "CONNECT,DELETE,GET,HEAD,OPTIONS,PATCH,POST,PUT,TRACE" diff --git a/instrumentation/aws-lambda/aws-lambda-events-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/AbstractAwsLambdaSqsEventHandlerTest.java b/instrumentation/aws-lambda/aws-lambda-events-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/AbstractAwsLambdaSqsEventHandlerTest.java index 4ee227887368..7c69c27e38cb 100644 --- a/instrumentation/aws-lambda/aws-lambda-events-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/AbstractAwsLambdaSqsEventHandlerTest.java +++ b/instrumentation/aws-lambda/aws-lambda-events-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/AbstractAwsLambdaSqsEventHandlerTest.java @@ -35,7 +35,7 @@ public abstract class AbstractAwsLambdaSqsEventHandlerTest { private static final String AWS_TRACE_HEADER = "Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1"; - protected abstract RequestHandler handler(); + protected abstract RequestHandler handler(); protected abstract InstrumentationExtension testing(); diff --git a/instrumentation/aws-lambda/aws-lambda-events-3.11/library/README.md b/instrumentation/aws-lambda/aws-lambda-events-3.11/library/README.md new file mode 100644 index 000000000000..cbde50929e96 --- /dev/null +++ b/instrumentation/aws-lambda/aws-lambda-events-3.11/library/README.md @@ -0,0 +1,136 @@ +# AWS Lambda Instrumentation + +This package contains libraries to help instrument AWS lambda functions in your code. + +## Using wrappers + +To use the instrumentation, configure `OTEL_INSTRUMENTATION_AWS_LAMBDA_HANDLER` env property to your lambda handler method in following format `package.ClassName::methodName` +and use one of wrappers as your lambda `Handler`. + +In order to configure a span flush timeout (default is set to 10 seconds), please configure `OTEL_INSTRUMENTATION_AWS_LAMBDA_FLUSH_TIMEOUT` env property. The value is in milliseconds. + +Available wrappers: + +- `io.opentelemetry.instrumentation.awslambdaevents.v3_11.TracingRequestWrapper` - for wrapping regular handlers (implementing `RequestHandler`) +- `io.opentelemetry.instrumentation.awslambdaevents.v3_11.TracingRequestApiGatewayWrapper` - for wrapping regular handlers (implementing `RequestHandler`) proxied through API Gateway, enabling HTTP context propagation +- `io.opentelemetry.instrumentation.awslambdacore.v1_0.TracingRequestStreamWrapper` - for wrapping streaming handlers (implementing `RequestStreamHandler`), enabling HTTP context propagation for HTTP requests + +If you are only using `TracingRequestStreamWrapper`, consider using [aws-lambda-core-1.0](../../aws-lambda-core-1.0/library) instead to reduce the size of +your compiled function. + +## Using handlers + +To use the instrumentation, replace your function classes that implement `RequestHandler` (or `RequestStreamHandler`) with those +that extend `TracingRequestHandler` (or `TracingRequestStreamHandler`). You will need to change the method name to `doHandleRequest` +and pass an initialized `OpenTelemetrySdk` to the base class. + +```java +public class MyRequestHandler extends TracingRequestHandler { + + private static final OpenTelemetrySdk SDK = OpenTelemetrySdk.builder() + .addSpanProcessor(spanProcessor) + .buildAndRegisterGlobal(); + + public MyRequestHandler() { + super(SDK); + } + + // Note the method is named doHandleRequest instead of handleRequest. + @Override + protected String doHandleRequest(String input, Context context) { + if (input.equals("hello")) { + return "world"; + } + return "goodbye"; + } +} +``` + +A `SERVER` span will be created with the name you specify for the function when deploying it. + +In addition, it is recommended to set up X-Ray trace propagation to be able to +link to tracing information provided by Lambda itself. To do so, add a dependency on +`opentelemetry-extension-tracepropagators`. Make sure the version matches the version of the SDK +you use. + +Gradle: + +```kotlin +dependencies { + implementation("io.opentelemetry:opentelemetry-extension-trace-propagators:0.8.0") +} +``` + +Maven: + +```xml + + + io.opentelemetry + opentelemetry-extension-trace-propagators + 0.8.0 + + +``` + +## SQS Handler + +This package provides a special handler for SQS-triggered functions to include messaging data. +If using SQS, it is recommended to use them instead of `TracingRequestHandler`. + +If your application processes one message at a time, each independently, it is recommended to extend +`TracingSQSMessageHandler`. This will create a single span corresponding to a received batch of +messages along with one span for each of the messages as you process them. + +```java +public class MyMessageHandler extends TracingSQSMessageHandler { + @Override + protected boolean handleMessage(SQSMessage message, Context context) { + System.out.println(message.getBody()); + return true; + } +} +``` + +If you handle a batch of messages together, for example by aggregating them into a single unit, +extend `TracingSQSEventHandler` to process a batch at a time. + +```java +public class MyBatchHandler extends TracingSQSEventHandler { + @Override + protected SQSBatchResponse handleEvent(SQSEvent event, Context context) { + System.out.println(event.getRecords().size()); + return null; + } +} +``` + +## Trace propagation + +Context propagation for this instrumentation can be done either with X-Ray propagation or regular HTTP propagation. If X-Ray is enabled for instrumented lambda, it will be preferred. If X-Ray is disabled, HTTP propagation will be tried (that is HTTP headers will be read to check for a valid trace context). + +### X-Ray propagation + +This instrumentation supports propagating traces using the `X-Amzn-Trace-Id` format for both normal +requests and SQS requests. X-Ray propagation is always enabled, there is no need to configure it explicitly. + +### HTTP headers based propagation + +For API Gateway (HTTP) requests instrumented by using one of following methods: + +- extending `TracingRequestStreamHandler` or `TracingRequestHandler` +- wrapping with `TracingRequestStreamWrapper` or `TracingRequestApiGatewayWrapper` + traces can be propagated with supported HTTP headers (see ). + +In order to enable requested propagation for a handler, configure it on the SDK you build. + +```java + static { + OpenTelemetrySdk.builder() + ... + .setPropagators(ContextPropagators.create(B3Propagator.injectingSingleHeader())) + .buildAndRegisterGlobal(); + } +``` + +If using the wrappers, set the `OTEL_PROPAGATORS` environment variable as described [here](https://github.com/open-telemetry/opentelemetry-java/blob/main/sdk-extensions/autoconfigure/README.md#propagator). diff --git a/instrumentation/aws-lambda/aws-lambda-events-3.11/library/build.gradle.kts b/instrumentation/aws-lambda/aws-lambda-events-3.11/library/build.gradle.kts new file mode 100644 index 000000000000..d21c0b8ebfba --- /dev/null +++ b/instrumentation/aws-lambda/aws-lambda-events-3.11/library/build.gradle.kts @@ -0,0 +1,36 @@ +plugins { + id("otel.library-instrumentation") +} + +dependencies { + implementation(project(":instrumentation:aws-lambda:aws-lambda-events-common-2.2:library")) + + compileOnly("io.opentelemetry:opentelemetry-sdk") + compileOnly("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure") + + library("com.amazonaws:aws-lambda-java-core:1.0.0") + library("com.amazonaws:aws-lambda-java-events:3.11.0") + + // allows to get the function ARN + testLibrary("com.amazonaws:aws-lambda-java-core:1.2.1") + + testImplementation("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure") + testImplementation("io.opentelemetry:opentelemetry-extension-trace-propagators") + testImplementation("com.amazonaws:aws-lambda-java-serialization:1.1.5") + + testImplementation(project(":instrumentation:aws-lambda:aws-lambda-events-2.2:testing")) + testImplementation("uk.org.webcompere:system-stubs-jupiter") +} + +tasks { + withType().configureEach { + // required on jdk17 + jvmArgs("--add-opens=java.base/java.lang=ALL-UNNAMED") + jvmArgs("--add-opens=java.base/java.util=ALL-UNNAMED") + jvmArgs("-XX:+IgnoreUnrecognizedVMOptions") + } + + test { + systemProperty("collectMetadata", findProperty("collectMetadata")?.toString() ?: "false") + } +} diff --git a/instrumentation/aws-lambda/aws-lambda-events-3.11/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v3_11/TracingRequestApiGatewayWrapper.java b/instrumentation/aws-lambda/aws-lambda-events-3.11/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v3_11/TracingRequestApiGatewayWrapper.java new file mode 100644 index 000000000000..717458b082d3 --- /dev/null +++ b/instrumentation/aws-lambda/aws-lambda-events-3.11/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v3_11/TracingRequestApiGatewayWrapper.java @@ -0,0 +1,55 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.awslambdaevents.v3_11; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent; +import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent; +import io.opentelemetry.instrumentation.awslambdacore.v1_0.internal.WrappedLambda; +import io.opentelemetry.instrumentation.awslambdaevents.common.v2_2.internal.SerializationUtil; +import io.opentelemetry.sdk.OpenTelemetrySdk; +import java.util.function.BiFunction; + +/** + * Wrapper for {@link io.opentelemetry.instrumentation.awslambdacore.v1_0.TracingRequestHandler}. + * Allows for wrapping a lambda proxied through API Gateway, enabling single span tracing and HTTP + * context propagation. + */ +public class TracingRequestApiGatewayWrapper + extends TracingRequestWrapperBase { + + public TracingRequestApiGatewayWrapper() { + super(TracingRequestApiGatewayWrapper::map); + } + + // Visible for testing + TracingRequestApiGatewayWrapper( + OpenTelemetrySdk openTelemetrySdk, + WrappedLambda wrappedLambda, + BiFunction, Object> mapper) { + super(openTelemetrySdk, wrappedLambda, mapper); + } + + // Visible for testing + static T map(APIGatewayProxyRequestEvent event, Class clazz) { + return SerializationUtil.fromJson(event.getBody(), clazz); + } + + @Override + protected APIGatewayProxyResponseEvent doHandleRequest( + APIGatewayProxyRequestEvent input, Context context) { + Object result = super.doHandleRequest(input, context); + APIGatewayProxyResponseEvent event; + // map to response event if needed + if (result instanceof APIGatewayProxyResponseEvent) { + event = (APIGatewayProxyResponseEvent) result; + } else { + event = new APIGatewayProxyResponseEvent(); + event.setBody(SerializationUtil.toJson(result)); + } + return event; + } +} diff --git a/instrumentation/aws-lambda/aws-lambda-events-3.11/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v3_11/TracingRequestWrapper.java b/instrumentation/aws-lambda/aws-lambda-events-3.11/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v3_11/TracingRequestWrapper.java new file mode 100644 index 000000000000..c3627ea65a6f --- /dev/null +++ b/instrumentation/aws-lambda/aws-lambda-events-3.11/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v3_11/TracingRequestWrapper.java @@ -0,0 +1,98 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.awslambdaevents.v3_11; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent; +import io.opentelemetry.instrumentation.awslambdacore.v1_0.AwsLambdaRequest; +import io.opentelemetry.instrumentation.awslambdacore.v1_0.TracingRequestStreamWrapper; +import io.opentelemetry.instrumentation.awslambdacore.v1_0.internal.ApiGatewayProxyRequest; +import io.opentelemetry.instrumentation.awslambdacore.v1_0.internal.MapUtils; +import io.opentelemetry.instrumentation.awslambdacore.v1_0.internal.WrappedLambda; +import io.opentelemetry.instrumentation.awslambdaevents.common.v2_2.internal.LambdaParameters; +import io.opentelemetry.instrumentation.awslambdaevents.common.v2_2.internal.SerializationUtil; +import io.opentelemetry.sdk.OpenTelemetrySdk; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Collections; +import java.util.Map; + +/** + * Wrapper for {@link com.amazonaws.services.lambda.runtime.RequestHandler} based Lambda handlers. + */ +public class TracingRequestWrapper extends TracingRequestStreamWrapper { + public TracingRequestWrapper() { + super(); + } + + // Visible for testing + TracingRequestWrapper(OpenTelemetrySdk openTelemetrySdk, WrappedLambda wrappedLambda) { + super(openTelemetrySdk, wrappedLambda); + } + + @Override + protected final AwsLambdaRequest createRequest( + InputStream inputStream, Context context, ApiGatewayProxyRequest proxyRequest) { + Method targetMethod = wrappedLambda.getRequestTargetMethod(); + Object input = LambdaParameters.toInput(targetMethod, inputStream, TracingRequestWrapper::map); + return AwsLambdaRequest.create(context, input, extractHeaders(input)); + } + + protected Map extractHeaders(Object input) { + if (input instanceof APIGatewayProxyRequestEvent) { + return MapUtils.emptyIfNull(((APIGatewayProxyRequestEvent) input).getHeaders()); + } + return Collections.emptyMap(); + } + + @Override + protected final void doHandleRequest( + InputStream input, OutputStream output, Context context, AwsLambdaRequest request) { + Method targetMethod = wrappedLambda.getRequestTargetMethod(); + Object[] parameters = LambdaParameters.toParameters(targetMethod, request.getInput(), context); + try { + Object result = targetMethod.invoke(wrappedLambda.getTargetObject(), parameters); + SerializationUtil.toJson(output, result); + } catch (IllegalAccessException e) { + throw new IllegalStateException("Method is inaccessible", e); + } catch (InvocationTargetException e) { + throw (e.getCause() instanceof RuntimeException + ? (RuntimeException) e.getCause() + : new IllegalStateException(e.getTargetException())); + } + } + + @SuppressWarnings({"unchecked", "TypeParameterUnusedInFormals"}) + // Used for testing + OUTPUT handleRequest(INPUT input, Context context) throws IOException { + byte[] inputJsonData = SerializationUtil.toJsonData(input); + ByteArrayInputStream inputStream = new ByteArrayInputStream(inputJsonData); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + + super.handleRequest(inputStream, outputStream, context); + + byte[] outputJsonData = outputStream.toByteArray(); + return (OUTPUT) + SerializationUtil.fromJson( + new ByteArrayInputStream(outputJsonData), + wrappedLambda.getRequestTargetMethod().getReturnType()); + } + + // Visible for testing + static T map(InputStream inputStream, Class clazz) { + try { + return SerializationUtil.fromJson(inputStream, clazz); + } catch (IllegalArgumentException e) { + throw new IllegalStateException( + "Could not map input to requested parameter type: " + clazz, e); + } + } +} diff --git a/instrumentation/aws-lambda/aws-lambda-events-3.11/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v3_11/TracingRequestWrapperBase.java b/instrumentation/aws-lambda/aws-lambda-events-3.11/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v3_11/TracingRequestWrapperBase.java new file mode 100644 index 000000000000..df6e6a650434 --- /dev/null +++ b/instrumentation/aws-lambda/aws-lambda-events-3.11/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v3_11/TracingRequestWrapperBase.java @@ -0,0 +1,82 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.awslambdaevents.v3_11; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent; +import io.opentelemetry.instrumentation.api.internal.HttpConstants; +import io.opentelemetry.instrumentation.awslambdacore.v1_0.TracingRequestHandler; +import io.opentelemetry.instrumentation.awslambdacore.v1_0.internal.MapUtils; +import io.opentelemetry.instrumentation.awslambdacore.v1_0.internal.WrappedLambda; +import io.opentelemetry.instrumentation.awslambdacore.v1_0.internal.WrapperConfiguration; +import io.opentelemetry.instrumentation.awslambdaevents.common.v2_2.internal.AwsLambdaEventsInstrumenterFactory; +import io.opentelemetry.instrumentation.awslambdaevents.common.v2_2.internal.LambdaParameters; +import io.opentelemetry.sdk.OpenTelemetrySdk; +import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Collections; +import java.util.Map; +import java.util.function.BiFunction; + +/** + * Base abstract wrapper for {@link TracingRequestHandler}. Provides: - delegation to a lambda via + * env property OTEL_INSTRUMENTATION_AWS_LAMBDA_HANDLER in package.ClassName::methodName format + */ +abstract class TracingRequestWrapperBase extends TracingRequestHandler { + private static final String INSTRUMENTATION_NAME = "io.opentelemetry.aws-lambda-events-3.11"; + + private final WrappedLambda wrappedLambda; + private final Method targetMethod; + private final BiFunction, Object> parameterMapper; + + protected TracingRequestWrapperBase(BiFunction, Object> parameterMapper) { + this( + AutoConfiguredOpenTelemetrySdk.initialize().getOpenTelemetrySdk(), + WrappedLambda.fromConfiguration(), + parameterMapper); + } + + // Visible for testing + TracingRequestWrapperBase( + OpenTelemetrySdk openTelemetrySdk, + WrappedLambda wrappedLambda, + BiFunction, Object> parameterMapper) { + super( + openTelemetrySdk, + WrapperConfiguration.flushTimeout(), + AwsLambdaEventsInstrumenterFactory.createInstrumenter( + openTelemetrySdk, INSTRUMENTATION_NAME, HttpConstants.KNOWN_METHODS)); + this.wrappedLambda = wrappedLambda; + this.targetMethod = wrappedLambda.getRequestTargetMethod(); + this.parameterMapper = parameterMapper; + } + + @Override + @SuppressWarnings("unchecked") + protected O doHandleRequest(I input, Context context) { + Object[] parameters = LambdaParameters.toArray(targetMethod, input, context, parameterMapper); + O result; + try { + result = (O) targetMethod.invoke(wrappedLambda.getTargetObject(), parameters); + } catch (IllegalAccessException e) { + throw new IllegalStateException("Method is inaccessible", e); + } catch (InvocationTargetException e) { + throw (e.getCause() instanceof RuntimeException + ? (RuntimeException) e.getCause() + : new IllegalStateException(e.getTargetException())); + } + return result; + } + + @Override + protected final Map extractHttpHeaders(I input) { + if (input instanceof APIGatewayProxyRequestEvent) { + return MapUtils.emptyIfNull(((APIGatewayProxyRequestEvent) input).getHeaders()); + } + return Collections.emptyMap(); + } +} diff --git a/instrumentation/aws-lambda/aws-lambda-events-3.11/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v3_11/TracingSqsEventHandler.java b/instrumentation/aws-lambda/aws-lambda-events-3.11/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v3_11/TracingSqsEventHandler.java new file mode 100644 index 000000000000..b0e7727eec3a --- /dev/null +++ b/instrumentation/aws-lambda/aws-lambda-events-3.11/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v3_11/TracingSqsEventHandler.java @@ -0,0 +1,85 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.awslambdaevents.v3_11; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.events.SQSBatchResponse; +import com.amazonaws.services.lambda.runtime.events.SQSEvent; +import io.opentelemetry.context.Scope; +import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; +import io.opentelemetry.instrumentation.awslambdacore.v1_0.TracingRequestHandler; +import io.opentelemetry.instrumentation.awslambdaevents.common.v2_2.internal.AwsLambdaSqsInstrumenterFactory; +import io.opentelemetry.sdk.OpenTelemetrySdk; +import java.time.Duration; +import javax.annotation.Nullable; + +public abstract class TracingSqsEventHandler + extends TracingRequestHandler { + static final String INSTRUMENTATION_NAME = "io.opentelemetry.aws-lambda-events-3.11"; + + private final Instrumenter instrumenter; + + /** + * Creates a new {@link TracingSqsEventHandler} which traces using the provided {@link + * OpenTelemetrySdk} and has a timeout of 1s when flushing at the end of an invocation. + */ + protected TracingSqsEventHandler(OpenTelemetrySdk openTelemetrySdk) { + this(openTelemetrySdk, DEFAULT_FLUSH_TIMEOUT); + } + + /** + * Creates a new {@link TracingSqsEventHandler} which traces using the provided {@link + * OpenTelemetrySdk} and has a timeout of {@code flushTimeout} when flushing at the end of an + * invocation. + */ + protected TracingSqsEventHandler(OpenTelemetrySdk openTelemetrySdk, Duration flushTimeout) { + this( + openTelemetrySdk, + flushTimeout, + AwsLambdaSqsInstrumenterFactory.forEvent(openTelemetrySdk, INSTRUMENTATION_NAME)); + } + + /** + * Creates a new {@link TracingSqsEventHandler} which flushes the provided {@link + * OpenTelemetrySdk}, has a timeout of {@code flushTimeout} when flushing at the end of an + * invocation, and traces using the provided {@link + * io.opentelemetry.instrumentation.awslambdacore.v1_0.internal.AwsLambdaFunctionInstrumenter}. + */ + protected TracingSqsEventHandler( + OpenTelemetrySdk openTelemetrySdk, + Duration flushTimeout, + Instrumenter instrumenter) { + super(openTelemetrySdk, flushTimeout); + this.instrumenter = instrumenter; + } + + @Nullable + @Override + public SQSBatchResponse doHandleRequest(SQSEvent event, Context context) { + io.opentelemetry.context.Context parentContext = io.opentelemetry.context.Context.current(); + if (instrumenter.shouldStart(parentContext, event)) { + io.opentelemetry.context.Context otelContext = instrumenter.start(parentContext, event); + Throwable error = null; + try (Scope ignored = otelContext.makeCurrent()) { + return handleEvent(event, context); + } catch (Throwable t) { + error = t; + throw t; + } finally { + instrumenter.end(otelContext, event, null, error); + } + } else { + return handleEvent(event, context); + } + } + + /** + * Handles a {@linkplain SQSEvent batch of messages}. Implement this class to do the actual + * processing of incoming SQS messages. + */ + @Nullable + protected abstract SQSBatchResponse handleEvent(SQSEvent event, Context context); +} diff --git a/instrumentation/aws-lambda/aws-lambda-events-3.11/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v3_11/TracingSqsEventWrapper.java b/instrumentation/aws-lambda/aws-lambda-events-3.11/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v3_11/TracingSqsEventWrapper.java new file mode 100644 index 000000000000..7163d374735b --- /dev/null +++ b/instrumentation/aws-lambda/aws-lambda-events-3.11/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v3_11/TracingSqsEventWrapper.java @@ -0,0 +1,52 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.awslambdaevents.v3_11; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.events.SQSBatchResponse; +import com.amazonaws.services.lambda.runtime.events.SQSEvent; +import io.opentelemetry.instrumentation.awslambdacore.v1_0.internal.WrappedLambda; +import io.opentelemetry.instrumentation.awslambdacore.v1_0.internal.WrapperConfiguration; +import io.opentelemetry.instrumentation.awslambdaevents.common.v2_2.internal.LambdaParameters; +import io.opentelemetry.sdk.OpenTelemetrySdk; +import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +public class TracingSqsEventWrapper extends TracingSqsEventHandler { + + private final WrappedLambda wrappedLambda; + private final Method targetMethod; + + public TracingSqsEventWrapper() { + this( + AutoConfiguredOpenTelemetrySdk.initialize().getOpenTelemetrySdk(), + WrappedLambda.fromConfiguration()); + } + + // Visible for testing + TracingSqsEventWrapper(OpenTelemetrySdk openTelemetrySdk, WrappedLambda wrappedLambda) { + super(openTelemetrySdk, WrapperConfiguration.flushTimeout()); + this.wrappedLambda = wrappedLambda; + this.targetMethod = wrappedLambda.getRequestTargetMethod(); + } + + @Override + protected SQSBatchResponse handleEvent(SQSEvent sqsEvent, Context context) { + Object[] parameters = + LambdaParameters.toArray(targetMethod, sqsEvent, context, (event, clazz) -> event); + try { + Object result = targetMethod.invoke(wrappedLambda.getTargetObject(), parameters); + return result instanceof SQSBatchResponse ? (SQSBatchResponse) result : null; + } catch (IllegalAccessException e) { + throw new IllegalStateException("Method is inaccessible", e); + } catch (InvocationTargetException e) { + throw (e.getCause() instanceof RuntimeException + ? (RuntimeException) e.getCause() + : new IllegalStateException(e.getTargetException())); + } + } +} diff --git a/instrumentation/aws-lambda/aws-lambda-events-3.11/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v3_11/TracingSqsMessageHandler.java b/instrumentation/aws-lambda/aws-lambda-events-3.11/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v3_11/TracingSqsMessageHandler.java new file mode 100644 index 000000000000..d6d315a46d23 --- /dev/null +++ b/instrumentation/aws-lambda/aws-lambda-events-3.11/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v3_11/TracingSqsMessageHandler.java @@ -0,0 +1,118 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.awslambdaevents.v3_11; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.events.SQSBatchResponse; +import com.amazonaws.services.lambda.runtime.events.SQSEvent; +import com.amazonaws.services.lambda.runtime.events.SQSEvent.SQSMessage; +import io.opentelemetry.context.Scope; +import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; +import io.opentelemetry.instrumentation.awslambdaevents.common.v2_2.internal.AwsLambdaSqsInstrumenterFactory; +import io.opentelemetry.sdk.OpenTelemetrySdk; +import java.time.Duration; +import java.util.ArrayList; +import java.util.List; + +public abstract class TracingSqsMessageHandler extends TracingSqsEventHandler { + + private final Instrumenter messageInstrumenter; + + /** + * Creates a new {@link TracingSqsMessageHandler} which traces using the provided {@link + * OpenTelemetrySdk} and has a timeout of 1s when flushing at the end of an invocation. + */ + protected TracingSqsMessageHandler(OpenTelemetrySdk openTelemetrySdk) { + this(openTelemetrySdk, DEFAULT_FLUSH_TIMEOUT); + } + + /** + * Creates a new {@link TracingSqsMessageHandler} which traces using the provided {@link + * OpenTelemetrySdk} and has a timeout of {@code flushTimeout} when flushing at the end of an + * invocation. + */ + protected TracingSqsMessageHandler(OpenTelemetrySdk openTelemetrySdk, Duration flushTimeout) { + this( + openTelemetrySdk, + flushTimeout, + AwsLambdaSqsInstrumenterFactory.forEvent(openTelemetrySdk, INSTRUMENTATION_NAME)); + } + + /** + * Creates a new {@link TracingSqsMessageHandler} which flushes the provided {@link + * OpenTelemetrySdk}, has a timeout of {@code flushTimeout} when flushing at the end of an + * invocation, and instruments {@link SQSEvent} using the provided {@code Instrumenter}. + */ + protected TracingSqsMessageHandler( + OpenTelemetrySdk openTelemetrySdk, + Duration flushTimeout, + Instrumenter eventInstrumenter) { + this( + openTelemetrySdk, + flushTimeout, + eventInstrumenter, + AwsLambdaSqsInstrumenterFactory.forMessage(openTelemetrySdk, INSTRUMENTATION_NAME)); + } + + /** + * Creates a new {@link TracingSqsMessageHandler} which flushes the provided {@link + * OpenTelemetrySdk}, has a timeout of {@code flushTimeout} when flushing at the end of an + * invocation, and traces using the provided {@code Instrumenter} and {@code + * Instrumenter}. + */ + protected TracingSqsMessageHandler( + OpenTelemetrySdk openTelemetrySdk, + Duration flushTimeout, + Instrumenter eventInstrumenter, + Instrumenter messageInstrumenter) { + super(openTelemetrySdk, flushTimeout, eventInstrumenter); + this.messageInstrumenter = messageInstrumenter; + } + + @Override + protected final SQSBatchResponse handleEvent(SQSEvent event, Context context) { + List batchItemFailures = new ArrayList<>(); + io.opentelemetry.context.Context parentContext = io.opentelemetry.context.Context.current(); + for (SQSMessage message : event.getRecords()) { + if (messageInstrumenter.shouldStart(parentContext, message)) { + io.opentelemetry.context.Context otelContext = + messageInstrumenter.start(parentContext, message); + Throwable error = null; + try (Scope ignored = otelContext.makeCurrent()) { + handleMessage(message, context, batchItemFailures); + } catch (Throwable t) { + error = t; + throw t; + } finally { + messageInstrumenter.end(otelContext, message, null, error); + } + } else { + handleMessage(message, context, batchItemFailures); + } + } + + return new SQSBatchResponse(batchItemFailures); + } + + private void handleMessage( + SQSMessage message, + Context context, + List batchItemFailures) { + if (!handleMessage(message, context)) { + batchItemFailures.add(new SQSBatchResponse.BatchItemFailure(message.getMessageId())); + } + } + + /** + * Handles a {@linkplain SQSMessage message}. Implement this class to do the actual processing of + * incoming SQS messages. + * + * @return {@code true} when message was processed successfully, {@code false} when it should be + * reported as a failed batch item. + */ + protected abstract boolean handleMessage(SQSMessage message, Context context); +} diff --git a/instrumentation/aws-lambda/aws-lambda-events-3.11/library/src/test/java/io/opentelemetry/instrumentation/awslambdaevents/v3_11/AwsLambdaApiGatewayWrapperTest.java b/instrumentation/aws-lambda/aws-lambda-events-3.11/library/src/test/java/io/opentelemetry/instrumentation/awslambdaevents/v3_11/AwsLambdaApiGatewayWrapperTest.java new file mode 100644 index 000000000000..d0bf6a93e5e4 --- /dev/null +++ b/instrumentation/aws-lambda/aws-lambda-events-3.11/library/src/test/java/io/opentelemetry/instrumentation/awslambdaevents/v3_11/AwsLambdaApiGatewayWrapperTest.java @@ -0,0 +1,295 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.awslambdaevents.v3_11; + +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; +import static io.opentelemetry.semconv.HttpAttributes.HTTP_REQUEST_METHOD; +import static io.opentelemetry.semconv.HttpAttributes.HTTP_RESPONSE_STATUS_CODE; +import static io.opentelemetry.semconv.UrlAttributes.URL_FULL; +import static io.opentelemetry.semconv.UserAgentAttributes.USER_AGENT_ORIGINAL; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.when; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestHandler; +import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent; +import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent; +import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.instrumentation.awslambdacore.v1_0.internal.WrappedLambda; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension; +import io.opentelemetry.semconv.incubating.CloudIncubatingAttributes; +import io.opentelemetry.semconv.incubating.FaasIncubatingAttributes; +import java.util.HashMap; +import java.util.Map; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.junitpioneer.jupiter.SetEnvironmentVariable; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +class AwsLambdaApiGatewayWrapperTest { + + @RegisterExtension + static final InstrumentationExtension testing = LibraryInstrumentationExtension.create(); + + @Mock private Context context; + + @BeforeEach + void setUp() { + when(context.getFunctionName()).thenReturn("my_function"); + when(context.getAwsRequestId()).thenReturn("1-22-333"); + when(context.getInvokedFunctionArn()) + .thenReturn("arn:aws:lambda:us-east-1:123456789:function:test"); + } + + @AfterEach + void tearDown() { + assertThat(testing.forceFlushCalled()).isTrue(); + } + + @Test + @SetEnvironmentVariable( + key = WrappedLambda.OTEL_LAMBDA_HANDLER_ENV_KEY, + value = + "io.opentelemetry.instrumentation.awslambdaevents.v3_11.AwsLambdaApiGatewayWrapperTest$TestRequestHandlerApiGateway::handleRequest") + void tracedWithHttpPropagation() { + TracingRequestApiGatewayWrapper wrapper = + new TracingRequestApiGatewayWrapper( + testing.getOpenTelemetrySdk(), + WrappedLambda.fromConfiguration(), + TracingRequestApiGatewayWrapper::map); + + Map headers = new HashMap<>(); + headers.put("traceparent", "00-4fd0b6131f19f39af59518d127b0cafe-0000000000000456-01"); + headers.put("User-Agent", "Test Client"); + headers.put("host", "localhost:123"); + headers.put("X-FORWARDED-PROTO", "http"); + Map query = new HashMap<>(); + query.put("a", "b"); + query.put("c", "d"); + APIGatewayProxyRequestEvent input = + new APIGatewayProxyRequestEvent() + .withHttpMethod("GET") + .withResource("/hello/{param}") + .withPath("/hello/world") + .withBody("hello") + .withQueryStringParameters(query) + .withHeaders(headers); + + APIGatewayProxyResponseEvent result = + (APIGatewayProxyResponseEvent) wrapper.handleRequest(input, context); + + assertThat(result.getBody()).isEqualTo("world"); + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> + span.hasName("GET /hello/{param}") + .hasKind(SpanKind.SERVER) + .hasTraceId("4fd0b6131f19f39af59518d127b0cafe") + .hasParentSpanId("0000000000000456") + .hasAttributesSatisfyingExactly( + equalTo( + CloudIncubatingAttributes.CLOUD_RESOURCE_ID, + "arn:aws:lambda:us-east-1:123456789:function:test"), + equalTo(CloudIncubatingAttributes.CLOUD_ACCOUNT_ID, "123456789"), + equalTo(FaasIncubatingAttributes.FAAS_INVOCATION_ID, "1-22-333"), + equalTo(FaasIncubatingAttributes.FAAS_TRIGGER, "http"), + equalTo(HTTP_REQUEST_METHOD, "GET"), + equalTo(USER_AGENT_ORIGINAL, "Test Client"), + equalTo(URL_FULL, "http://localhost:123/hello/world?a=b&c=d"), + equalTo(HTTP_RESPONSE_STATUS_CODE, 200L)))); + } + + @Test + @SetEnvironmentVariable( + key = WrappedLambda.OTEL_LAMBDA_HANDLER_ENV_KEY, + value = + "io.opentelemetry.instrumentation.awslambdaevents.v3_11.AwsLambdaApiGatewayWrapperTest$TestRequestHandlerApiGateway::handleRequest") + void handlerTraced_empty() { + TracingRequestApiGatewayWrapper wrapper = + new TracingRequestApiGatewayWrapper( + testing.getOpenTelemetrySdk(), + WrappedLambda.fromConfiguration(), + TracingRequestApiGatewayWrapper::map); + APIGatewayProxyResponseEvent result = + (APIGatewayProxyResponseEvent) + wrapper.handleRequest(new APIGatewayProxyRequestEvent().withBody("empty"), context); + + assertThat(result.getBody()).isNull(); + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> + span.hasName("my_function") + .hasKind(SpanKind.SERVER) + .hasAttributesSatisfyingExactly( + equalTo( + CloudIncubatingAttributes.CLOUD_RESOURCE_ID, + "arn:aws:lambda:us-east-1:123456789:function:test"), + equalTo(CloudIncubatingAttributes.CLOUD_ACCOUNT_ID, "123456789"), + equalTo(FaasIncubatingAttributes.FAAS_INVOCATION_ID, "1-22-333"), + equalTo(FaasIncubatingAttributes.FAAS_TRIGGER, "http")))); + } + + @Test + @SetEnvironmentVariable( + key = WrappedLambda.OTEL_LAMBDA_HANDLER_ENV_KEY, + value = + "io.opentelemetry.instrumentation.awslambdaevents.v3_11.AwsLambdaApiGatewayWrapperTest$TestRequestHandlerString::handleRequest") + void handlerTraced_string() { + TracingRequestApiGatewayWrapper wrapper = + new TracingRequestApiGatewayWrapper( + testing.getOpenTelemetrySdk(), + WrappedLambda.fromConfiguration(), + TracingRequestApiGatewayWrapper::map); + APIGatewayProxyResponseEvent result = + (APIGatewayProxyResponseEvent) + wrapper.handleRequest(new APIGatewayProxyRequestEvent().withBody("\"hello\""), context); + + assertThat(result.getBody()).isEqualTo("\"world\""); + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> + span.hasName("my_function") + .hasKind(SpanKind.SERVER) + .hasAttributesSatisfyingExactly( + equalTo( + CloudIncubatingAttributes.CLOUD_RESOURCE_ID, + "arn:aws:lambda:us-east-1:123456789:function:test"), + equalTo(CloudIncubatingAttributes.CLOUD_ACCOUNT_ID, "123456789"), + equalTo(FaasIncubatingAttributes.FAAS_INVOCATION_ID, "1-22-333"), + equalTo(FaasIncubatingAttributes.FAAS_TRIGGER, "http")))); + } + + @Test + @SetEnvironmentVariable( + key = WrappedLambda.OTEL_LAMBDA_HANDLER_ENV_KEY, + value = + "io.opentelemetry.instrumentation.awslambdaevents.v3_11.AwsLambdaApiGatewayWrapperTest$TestRequestHandlerInteger::handleRequest") + void handlerTraced_integer() { + TracingRequestApiGatewayWrapper wrapper = + new TracingRequestApiGatewayWrapper( + testing.getOpenTelemetrySdk(), + WrappedLambda.fromConfiguration(), + TracingRequestApiGatewayWrapper::map); + APIGatewayProxyResponseEvent result = + (APIGatewayProxyResponseEvent) + wrapper.handleRequest(new APIGatewayProxyRequestEvent().withBody("1"), context); + + assertThat(result.getBody()).isEqualTo("\"world\""); + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> + span.hasName("my_function") + .hasKind(SpanKind.SERVER) + .hasAttributesSatisfyingExactly( + equalTo( + CloudIncubatingAttributes.CLOUD_RESOURCE_ID, + "arn:aws:lambda:us-east-1:123456789:function:test"), + equalTo(CloudIncubatingAttributes.CLOUD_ACCOUNT_ID, "123456789"), + equalTo(FaasIncubatingAttributes.FAAS_INVOCATION_ID, "1-22-333"), + equalTo(FaasIncubatingAttributes.FAAS_TRIGGER, "http")))); + } + + @Test + @SetEnvironmentVariable( + key = WrappedLambda.OTEL_LAMBDA_HANDLER_ENV_KEY, + value = + "io.opentelemetry.instrumentation.awslambdaevents.v3_11.AwsLambdaApiGatewayWrapperTest$TestRequestHandlerCustomType::handleRequest") + void handlerTraced_customType() { + TracingRequestApiGatewayWrapper wrapper = + new TracingRequestApiGatewayWrapper( + testing.getOpenTelemetrySdk(), + WrappedLambda.fromConfiguration(), + TracingRequestApiGatewayWrapper::map); + APIGatewayProxyResponseEvent result = + (APIGatewayProxyResponseEvent) + wrapper.handleRequest( + new APIGatewayProxyRequestEvent() + .withBody("{\"key\":\"hello\", \"value\":\"General Kenobi\"}"), + context); + + assertThat(result.getBody()).isEqualTo("\"General Kenobi\""); + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> + span.hasName("my_function") + .hasKind(SpanKind.SERVER) + .hasAttributesSatisfyingExactly( + equalTo( + CloudIncubatingAttributes.CLOUD_RESOURCE_ID, + "arn:aws:lambda:us-east-1:123456789:function:test"), + equalTo(CloudIncubatingAttributes.CLOUD_ACCOUNT_ID, "123456789"), + equalTo(FaasIncubatingAttributes.FAAS_INVOCATION_ID, "1-22-333"), + equalTo(FaasIncubatingAttributes.FAAS_TRIGGER, "http")))); + } + + public static class TestRequestHandlerApiGateway + implements RequestHandler { + + @Override + public APIGatewayProxyResponseEvent handleRequest( + APIGatewayProxyRequestEvent input, Context context) { + if (input.getBody().equals("hello")) { + return new APIGatewayProxyResponseEvent().withStatusCode(200).withBody("world"); + } else if (input.getBody().equals("empty")) { + return new APIGatewayProxyResponseEvent(); + } + throw new IllegalStateException("bad request"); + } + } + + public static final class TestRequestHandlerString implements RequestHandler { + + @Override + public String handleRequest(String input, Context context) { + if (input.equals("hello")) { + return "world"; + } + throw new IllegalArgumentException("bad argument"); + } + } + + public static final class TestRequestHandlerInteger implements RequestHandler { + + @Override + public String handleRequest(Integer input, Context context) { + if (input == 1) { + return "world"; + } + throw new IllegalArgumentException("bad argument"); + } + } + + public static class CustomType { + public String key; + public String value; + } + + public static final class TestRequestHandlerCustomType + implements RequestHandler { + + @Override + public String handleRequest(CustomType input, Context context) { + if (input.key.equals("hello")) { + return input.value; + } + throw new IllegalArgumentException("bad argument"); + } + } +} diff --git a/instrumentation/aws-lambda/aws-lambda-events-3.11/library/src/test/java/io/opentelemetry/instrumentation/awslambdaevents/v3_11/AwsLambdaSqsEventHandlerTest.java b/instrumentation/aws-lambda/aws-lambda-events-3.11/library/src/test/java/io/opentelemetry/instrumentation/awslambdaevents/v3_11/AwsLambdaSqsEventHandlerTest.java new file mode 100644 index 000000000000..50efefcd4a32 --- /dev/null +++ b/instrumentation/aws-lambda/aws-lambda-events-3.11/library/src/test/java/io/opentelemetry/instrumentation/awslambdaevents/v3_11/AwsLambdaSqsEventHandlerTest.java @@ -0,0 +1,44 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.awslambdaevents.v3_11; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestHandler; +import com.amazonaws.services.lambda.runtime.events.SQSBatchResponse; +import com.amazonaws.services.lambda.runtime.events.SQSEvent; +import io.opentelemetry.instrumentation.awslambdaevents.v2_2.AbstractAwsLambdaSqsEventHandlerTest; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension; +import io.opentelemetry.sdk.OpenTelemetrySdk; +import org.junit.jupiter.api.extension.RegisterExtension; + +class AwsLambdaSqsEventHandlerTest extends AbstractAwsLambdaSqsEventHandlerTest { + + @RegisterExtension + static final InstrumentationExtension testing = LibraryInstrumentationExtension.create(); + + @Override + protected RequestHandler handler() { + return new TestHandler(testing.getOpenTelemetrySdk()); + } + + @Override + protected InstrumentationExtension testing() { + return testing; + } + + private static final class TestHandler extends TracingSqsEventHandler { + + TestHandler(OpenTelemetrySdk openTelemetrySdk) { + super(openTelemetrySdk); + } + + @Override + protected SQSBatchResponse handleEvent(SQSEvent event, Context context) { + return null; + } + } +} diff --git a/instrumentation/aws-lambda/aws-lambda-events-3.11/library/src/test/java/io/opentelemetry/instrumentation/awslambdaevents/v3_11/AwsLambdaSqsEventWrapperTest.java b/instrumentation/aws-lambda/aws-lambda-events-3.11/library/src/test/java/io/opentelemetry/instrumentation/awslambdaevents/v3_11/AwsLambdaSqsEventWrapperTest.java new file mode 100644 index 000000000000..e2216828bf35 --- /dev/null +++ b/instrumentation/aws-lambda/aws-lambda-events-3.11/library/src/test/java/io/opentelemetry/instrumentation/awslambdaevents/v3_11/AwsLambdaSqsEventWrapperTest.java @@ -0,0 +1,115 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.awslambdaevents.v3_11; + +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; +import static io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes.MESSAGING_OPERATION; +import static io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes.MESSAGING_SYSTEM; +import static org.mockito.Mockito.when; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestHandler; +import com.amazonaws.services.lambda.runtime.events.SQSBatchResponse; +import com.amazonaws.services.lambda.runtime.events.SQSEvent; +import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.instrumentation.awslambdacore.v1_0.internal.WrappedLambda; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension; +import io.opentelemetry.semconv.incubating.CloudIncubatingAttributes; +import io.opentelemetry.semconv.incubating.FaasIncubatingAttributes; +import io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes; +import java.lang.reflect.Constructor; +import java.util.Collections; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.junitpioneer.jupiter.SetEnvironmentVariable; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +@SetEnvironmentVariable( + key = WrappedLambda.OTEL_LAMBDA_HANDLER_ENV_KEY, + value = + "io.opentelemetry.instrumentation.awslambdaevents.v3_11.AwsLambdaSqsEventWrapperTest$TestRequestHandler::handleRequest") +class AwsLambdaSqsEventWrapperTest { + + @RegisterExtension + static final InstrumentationExtension testing = LibraryInstrumentationExtension.create(); + + @Mock private Context context; + + @BeforeEach + void setUp() { + when(context.getFunctionName()).thenReturn("my_function"); + when(context.getAwsRequestId()).thenReturn("1-22-333"); + when(context.getInvokedFunctionArn()) + .thenReturn("arn:aws:lambda:us-east-1:123456789:function:test"); + } + + @AfterEach + void tearDown() { + assertThat(testing.forceFlushCalled()).isTrue(); + } + + @SuppressWarnings("deprecation") // using deprecated semconv + @Test + void eventTraced() { + SQSEvent event = new SQSEvent(); + SQSEvent.SQSMessage record = newMessage(); + record.setEventSource("otel"); + record.setAttributes(Collections.emptyMap()); + event.setRecords(Collections.singletonList(record)); + + TracingSqsEventWrapper wrapper = + new TracingSqsEventWrapper( + testing.getOpenTelemetrySdk(), WrappedLambda.fromConfiguration()); + wrapper.handleRequest(event, context); + + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> + span.hasName("my_function") + .hasKind(SpanKind.SERVER) + .hasAttributesSatisfyingExactly( + equalTo( + CloudIncubatingAttributes.CLOUD_RESOURCE_ID, + "arn:aws:lambda:us-east-1:123456789:function:test"), + equalTo(CloudIncubatingAttributes.CLOUD_ACCOUNT_ID, "123456789"), + equalTo(FaasIncubatingAttributes.FAAS_INVOCATION_ID, "1-22-333")), + span -> + span.hasName("otel process") + .hasKind(SpanKind.CONSUMER) + .hasAttributesSatisfyingExactly( + equalTo( + MESSAGING_SYSTEM, + MessagingIncubatingAttributes.MessagingSystemIncubatingValues + .AWS_SQS), + equalTo(MESSAGING_OPERATION, "process")))); + } + + public static final class TestRequestHandler + implements RequestHandler { + @Override + public SQSBatchResponse handleRequest(SQSEvent input, Context context) { + return null; + } + } + + // Constructor private in early versions. + private static SQSEvent.SQSMessage newMessage() { + try { + Constructor ctor = SQSEvent.SQSMessage.class.getDeclaredConstructor(); + return ctor.newInstance(); + } catch (Throwable t) { + throw new AssertionError(t); + } + } +} diff --git a/instrumentation/aws-lambda/aws-lambda-events-3.11/library/src/test/java/io/opentelemetry/instrumentation/awslambdaevents/v3_11/AwsLambdaSqsMessageHandlerTest.java b/instrumentation/aws-lambda/aws-lambda-events-3.11/library/src/test/java/io/opentelemetry/instrumentation/awslambdaevents/v3_11/AwsLambdaSqsMessageHandlerTest.java new file mode 100644 index 000000000000..26863fbf08e9 --- /dev/null +++ b/instrumentation/aws-lambda/aws-lambda-events-3.11/library/src/test/java/io/opentelemetry/instrumentation/awslambdaevents/v3_11/AwsLambdaSqsMessageHandlerTest.java @@ -0,0 +1,177 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.awslambdaevents.v3_11; + +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; +import static io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes.MESSAGING_DESTINATION_NAME; +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.mockito.Mockito.when; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.events.SQSBatchResponse; +import com.amazonaws.services.lambda.runtime.events.SQSEvent; +import io.opentelemetry.api.trace.SpanContext; +import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.api.trace.TraceFlags; +import io.opentelemetry.api.trace.TraceState; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension; +import io.opentelemetry.sdk.OpenTelemetrySdk; +import io.opentelemetry.sdk.trace.data.LinkData; +import io.opentelemetry.semconv.incubating.FaasIncubatingAttributes; +import io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes; +import java.lang.reflect.Constructor; +import java.util.Arrays; +import java.util.Collections; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +class AwsLambdaSqsMessageHandlerTest { + + private static final String AWS_TRACE_HEADER1 = + "Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1"; + private static final String AWS_TRACE_HEADER2 = + "Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad9;Sampled=1"; + + @RegisterExtension + static final InstrumentationExtension testing = LibraryInstrumentationExtension.create(); + + @Mock private Context context; + + @BeforeEach + void setUp() { + when(context.getFunctionName()).thenReturn("my_function"); + when(context.getAwsRequestId()).thenReturn("1-22-333"); + } + + @AfterEach + void tearDown() { + assertThat(testing.forceFlushCalled()).isTrue(); + } + + @SuppressWarnings("deprecation") // using deprecated semconv + @Test + void processSpans() { + SQSEvent.SQSMessage message1 = newMessage(); + message1.setAttributes(Collections.singletonMap("AWSTraceHeader", AWS_TRACE_HEADER1)); + message1.setMessageId("message1"); + message1.setEventSource("queue1"); + + SQSEvent.SQSMessage message2 = newMessage(); + message2.setAttributes(Collections.singletonMap("AWSTraceHeader", AWS_TRACE_HEADER2)); + message2.setMessageId("message2"); + message2.setEventSource("queue1"); + + SQSEvent event = new SQSEvent(); + event.setRecords(Arrays.asList(message1, message2)); + + SQSBatchResponse response = + new TestHandler(testing.getOpenTelemetrySdk()).handleRequest(event, context); + assertThat(response.getBatchItemFailures()) + .satisfiesExactly(item -> assertThat(item.getItemIdentifier()).isEqualTo("message2")); + + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> + span.hasName("my_function") + .hasKind(SpanKind.SERVER) + .hasAttributesSatisfyingExactly( + equalTo(FaasIncubatingAttributes.FAAS_INVOCATION_ID, "1-22-333")), + span -> + span.hasName("queue1 process") + .hasKind(SpanKind.CONSUMER) + .hasParentSpanId(trace.getSpan(0).getSpanId()) + .hasAttributesSatisfyingExactly( + equalTo( + MESSAGING_SYSTEM, + MessagingIncubatingAttributes.MessagingSystemIncubatingValues + .AWS_SQS), + equalTo(MESSAGING_OPERATION, "process")) + .hasLinks( + LinkData.create( + SpanContext.createFromRemoteParent( + "5759e988bd862e3fe1be46a994272793", + "53995c3f42cd8ad8", + TraceFlags.getSampled(), + TraceState.getDefault())), + LinkData.create( + SpanContext.createFromRemoteParent( + "5759e988bd862e3fe1be46a994272793", + "53995c3f42cd8ad9", + TraceFlags.getSampled(), + TraceState.getDefault()))), + span -> + span.hasName("queue1 process") + .hasKind(SpanKind.CONSUMER) + .hasParentSpanId(trace.getSpan(1).getSpanId()) + .hasAttributesSatisfyingExactly( + equalTo( + MESSAGING_SYSTEM, + MessagingIncubatingAttributes.MessagingSystemIncubatingValues + .AWS_SQS), + equalTo(MESSAGING_OPERATION, "process"), + equalTo(MESSAGING_MESSAGE_ID, "message1"), + equalTo(MESSAGING_DESTINATION_NAME, "queue1")) + .hasLinks( + LinkData.create( + SpanContext.createFromRemoteParent( + "5759e988bd862e3fe1be46a994272793", + "53995c3f42cd8ad8", + TraceFlags.getSampled(), + TraceState.getDefault()))), + span -> + span.hasName("queue1 process") + .hasKind(SpanKind.CONSUMER) + .hasParentSpanId(trace.getSpan(1).getSpanId()) + .hasAttributesSatisfyingExactly( + equalTo( + MESSAGING_SYSTEM, + MessagingIncubatingAttributes.MessagingSystemIncubatingValues + .AWS_SQS), + equalTo(MESSAGING_OPERATION, "process"), + equalTo(MESSAGING_MESSAGE_ID, "message2"), + equalTo(MESSAGING_DESTINATION_NAME, "queue1")) + .hasLinks( + LinkData.create( + SpanContext.createFromRemoteParent( + "5759e988bd862e3fe1be46a994272793", + "53995c3f42cd8ad9", + TraceFlags.getSampled(), + TraceState.getDefault()))))); + } + + // Constructor private in early versions. + private static SQSEvent.SQSMessage newMessage() { + try { + Constructor ctor = SQSEvent.SQSMessage.class.getDeclaredConstructor(); + return ctor.newInstance(); + } catch (Throwable t) { + throw new AssertionError(t); + } + } + + private static final class TestHandler extends TracingSqsMessageHandler { + + TestHandler(OpenTelemetrySdk openTelemetrySdk) { + super(openTelemetrySdk); + } + + @Override + protected boolean handleMessage(SQSEvent.SQSMessage message, Context context) { + return "message1".equals(message.getMessageId()); + } + } +} diff --git a/instrumentation/aws-lambda/aws-lambda-events-3.11/library/src/test/java/io/opentelemetry/instrumentation/awslambdaevents/v3_11/AwsLambdaWrapperTest.java b/instrumentation/aws-lambda/aws-lambda-events-3.11/library/src/test/java/io/opentelemetry/instrumentation/awslambdaevents/v3_11/AwsLambdaWrapperTest.java new file mode 100644 index 000000000000..ad32986d443b --- /dev/null +++ b/instrumentation/aws-lambda/aws-lambda-events-3.11/library/src/test/java/io/opentelemetry/instrumentation/awslambdaevents/v3_11/AwsLambdaWrapperTest.java @@ -0,0 +1,214 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.awslambdaevents.v3_11; + +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.catchThrowable; +import static org.mockito.Mockito.when; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestHandler; +import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.instrumentation.awslambdacore.v1_0.internal.WrappedLambda; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension; +import io.opentelemetry.sdk.trace.data.StatusData; +import io.opentelemetry.semconv.incubating.CloudIncubatingAttributes; +import io.opentelemetry.semconv.incubating.FaasIncubatingAttributes; +import java.io.IOException; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.junitpioneer.jupiter.SetEnvironmentVariable; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +class AwsLambdaWrapperTest { + + @RegisterExtension + static final InstrumentationExtension testing = LibraryInstrumentationExtension.create(); + + @Mock private Context context; + + @BeforeEach + void setUp() { + when(context.getFunctionName()).thenReturn("my_function"); + when(context.getAwsRequestId()).thenReturn("1-22-333"); + when(context.getInvokedFunctionArn()) + .thenReturn("arn:aws:lambda:us-east-1:123456789:function:test"); + } + + @AfterEach + void tearDown() { + assertThat(testing.forceFlushCalled()).isTrue(); + } + + @Test + @SetEnvironmentVariable( + key = WrappedLambda.OTEL_LAMBDA_HANDLER_ENV_KEY, + value = + "io.opentelemetry.instrumentation.awslambdaevents.v3_11.AwsLambdaWrapperTest$TestRequestHandlerString::handleRequest") + void handlerTraced() throws IOException { + TracingRequestWrapper wrapper = + new TracingRequestWrapper(testing.getOpenTelemetrySdk(), WrappedLambda.fromConfiguration()); + Object result = wrapper.handleRequest("hello", context); + + assertThat(result).isEqualTo("world"); + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> + span.hasName("my_function") + .hasKind(SpanKind.SERVER) + .hasAttributesSatisfyingExactly( + equalTo( + CloudIncubatingAttributes.CLOUD_RESOURCE_ID, + "arn:aws:lambda:us-east-1:123456789:function:test"), + equalTo(CloudIncubatingAttributes.CLOUD_ACCOUNT_ID, "123456789"), + equalTo(FaasIncubatingAttributes.FAAS_INVOCATION_ID, "1-22-333")))); + } + + @Test + @SetEnvironmentVariable( + key = WrappedLambda.OTEL_LAMBDA_HANDLER_ENV_KEY, + value = + "io.opentelemetry.instrumentation.awslambdaevents.v3_11.AwsLambdaWrapperTest$TestRequestHandlerString::handleRequest") + void handlerTracedWithException() { + TracingRequestWrapper wrapper = + new TracingRequestWrapper(testing.getOpenTelemetrySdk(), WrappedLambda.fromConfiguration()); + Throwable thrown = catchThrowable(() -> wrapper.handleRequest("goodbye", context)); + + assertThat(thrown).isInstanceOf(IllegalArgumentException.class); + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> + span.hasName("my_function") + .hasKind(SpanKind.SERVER) + .hasStatus(StatusData.error()) + .hasException(thrown) + .hasAttributesSatisfyingExactly( + equalTo( + CloudIncubatingAttributes.CLOUD_RESOURCE_ID, + "arn:aws:lambda:us-east-1:123456789:function:test"), + equalTo(CloudIncubatingAttributes.CLOUD_ACCOUNT_ID, "123456789"), + equalTo(FaasIncubatingAttributes.FAAS_INVOCATION_ID, "1-22-333")))); + } + + @Test + @SetEnvironmentVariable( + key = WrappedLambda.OTEL_LAMBDA_HANDLER_ENV_KEY, + value = + "io.opentelemetry.instrumentation.awslambdaevents.v3_11.AwsLambdaWrapperTest$TestRequestHandlerInteger::handleRequest") + void handlerTraced_integer() throws IOException { + TracingRequestWrapper wrapper = + new TracingRequestWrapper(testing.getOpenTelemetrySdk(), WrappedLambda.fromConfiguration()); + Object result = wrapper.handleRequest(1, context); + + assertThat(result).isEqualTo("world"); + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> + span.hasName("my_function") + .hasKind(SpanKind.SERVER) + .hasAttributesSatisfyingExactly( + equalTo( + CloudIncubatingAttributes.CLOUD_RESOURCE_ID, + "arn:aws:lambda:us-east-1:123456789:function:test"), + equalTo(CloudIncubatingAttributes.CLOUD_ACCOUNT_ID, "123456789"), + equalTo(FaasIncubatingAttributes.FAAS_INVOCATION_ID, "1-22-333")))); + } + + @Test + @SetEnvironmentVariable( + key = WrappedLambda.OTEL_LAMBDA_HANDLER_ENV_KEY, + value = + "io.opentelemetry.instrumentation.awslambdaevents.v3_11.AwsLambdaWrapperTest$TestRequestHandlerCustomType::handleRequest") + void handlerTraced_custom() throws IOException { + TracingRequestWrapper wrapper = + new TracingRequestWrapper(testing.getOpenTelemetrySdk(), WrappedLambda.fromConfiguration()); + CustomType ct = new CustomType(); + ct.key = "hello there"; + ct.value = "General Kenobi"; + Object result = wrapper.handleRequest(ct, context); + + assertThat(result).isEqualTo("General Kenobi"); + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> + span.hasName("my_function") + .hasKind(SpanKind.SERVER) + .hasAttributesSatisfyingExactly( + equalTo( + CloudIncubatingAttributes.CLOUD_RESOURCE_ID, + "arn:aws:lambda:us-east-1:123456789:function:test"), + equalTo(CloudIncubatingAttributes.CLOUD_ACCOUNT_ID, "123456789"), + equalTo(FaasIncubatingAttributes.FAAS_INVOCATION_ID, "1-22-333")))); + } + + public static final class TestRequestHandlerString implements RequestHandler { + + @Override + public String handleRequest(String input, Context context) { + if (input.equals("hello")) { + return "world"; + } + throw new IllegalArgumentException("bad argument"); + } + } + + public static final class TestRequestHandlerInteger implements RequestHandler { + + @Override + public String handleRequest(Integer input, Context context) { + if (input == 1) { + return "world"; + } + throw new IllegalArgumentException("bad argument"); + } + } + + static class CustomType { + String key; + String value; + + // Need getter/setter of all the attributes for serialization/deserialization + + public String getKey() { + return key; + } + + public void setKey(String key) { + this.key = key; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + } + + public static final class TestRequestHandlerCustomType + implements RequestHandler { + + @Override + public String handleRequest(CustomType input, Context context) { + if (input.key.equals("hello there")) { + return input.value; + } + throw new IllegalArgumentException("bad argument"); + } + } +} diff --git a/instrumentation/aws-lambda/aws-lambda-events-3.11/library/src/test/java/io/opentelemetry/instrumentation/awslambdaevents/v3_11/TracingRequestWrapperStandardEventsTest.java b/instrumentation/aws-lambda/aws-lambda-events-3.11/library/src/test/java/io/opentelemetry/instrumentation/awslambdaevents/v3_11/TracingRequestWrapperStandardEventsTest.java new file mode 100644 index 000000000000..917014df1cb0 --- /dev/null +++ b/instrumentation/aws-lambda/aws-lambda-events-3.11/library/src/test/java/io/opentelemetry/instrumentation/awslambdaevents/v3_11/TracingRequestWrapperStandardEventsTest.java @@ -0,0 +1,295 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.awslambdaevents.v3_11; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestHandler; +import com.amazonaws.services.lambda.runtime.events.KinesisEvent; +import com.amazonaws.services.lambda.runtime.events.S3Event; +import com.amazonaws.services.lambda.runtime.events.SNSEvent; +import com.amazonaws.services.lambda.runtime.events.SQSEvent; +import com.amazonaws.services.lambda.runtime.events.ScheduledEvent; +import io.opentelemetry.instrumentation.awslambdacore.v1_0.internal.WrappedLambda; +import io.opentelemetry.instrumentation.awslambdaevents.common.v2_2.internal.SerializationUtil; +import io.opentelemetry.sdk.OpenTelemetrySdk; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +class TracingRequestWrapperStandardEventsTest { + private static final Map, EventInfo> EVENTS_JSON = buildEventExamples(); + + private final OpenTelemetrySdk sdk = OpenTelemetrySdk.builder().build(); + private final Context context = mock(Context.class); + private TracingRequestWrapper wrapper; + + static final class EventInfo { + final Class eventType; + final String eventBody; + + EventInfo(Class eventType, String eventBody) { + this.eventType = eventType; + this.eventBody = eventBody; + } + } + + private static Map, EventInfo> buildEventExamples() { + Map, EventInfo> events = new HashMap<>(); + events.put( + ScheduledEventRequestHandler.class, + new EventInfo( + ScheduledEvent.class, + "{\n" + + " \"version\": \"0\",\n" + + " \"id\": \"53dc4d37-cffa-4f76-80c9-8b7d4a4d2eaa\",\n" + + " \"detail-type\": \"Scheduled Event\",\n" + + " \"source\": \"aws.events\",\n" + + " \"account\": \"123456789012\",\n" + + " \"time\": \"2015-10-08T16:53:06Z\",\n" + + " \"region\": \"us-east-1\",\n" + + " \"resources\": [\n" + + " \"arn:aws:events:us-east-1:123456789012:rule/my-scheduled-rule\"\n" + + " ],\n" + + " \"detail\": {}\n" + + "}")); + events.put( + KinesisEventRequestHandler.class, + new EventInfo( + KinesisEvent.class, + "{\n" + + " \"Records\": [\n" + + " {\n" + + " \"kinesis\": {\n" + + " \"kinesisSchemaVersion\": \"1.0\",\n" + + " \"partitionKey\": \"1\",\n" + + " \"sequenceNumber\": \"49590338271490256608559692538361571095921575989136588898\",\n" + + " \"data\": \"SGVsbG8sIHRoaXMgaXMgYSB0ZXN0Lg==\",\n" + + " \"approximateArrivalTimestamp\": 1545084650.987\n" + + " },\n" + + " \"eventSource\": \"aws:kinesis\",\n" + + " \"eventVersion\": \"1.0\",\n" + + " \"eventID\": \"shardId-000000000006:49590338271490256608559692538361571095921575989136588898\",\n" + + " \"eventName\": \"aws:kinesis:record\",\n" + + " \"invokeIdentityArn\": \"arn:aws:iam::123456789012:role/lambda-role\",\n" + + " \"awsRegion\": \"us-east-2\",\n" + + " \"eventSourceARN\": \"arn:aws:kinesis:us-east-2:123456789012:stream/lambda-stream\"\n" + + " },\n" + + " {\n" + + " \"kinesis\": {\n" + + " \"kinesisSchemaVersion\": \"1.0\",\n" + + " \"partitionKey\": \"1\",\n" + + " \"sequenceNumber\": \"49590338271490256608559692540925702759324208523137515618\",\n" + + " \"data\": \"VGhpcyBpcyBvbmx5IGEgdGVzdC4=\",\n" + + " \"approximateArrivalTimestamp\": 1545084711.166\n" + + " },\n" + + " \"eventSource\": \"aws:kinesis\",\n" + + " \"eventVersion\": \"1.0\",\n" + + " \"eventID\": \"shardId-000000000006:49590338271490256608559692540925702759324208523137515618\",\n" + + " \"eventName\": \"aws:kinesis:record\",\n" + + " \"invokeIdentityArn\": \"arn:aws:iam::123456789012:role/lambda-role\",\n" + + " \"awsRegion\": \"us-east-2\",\n" + + " \"eventSourceARN\": \"arn:aws:kinesis:us-east-2:123456789012:stream/lambda-stream\"\n" + + " }\n" + + " ]\n" + + "}")); + events.put( + SqsEventRequestHandler.class, + new EventInfo( + SQSEvent.class, + "{\n" + + " \"Records\": [\n" + + " {\n" + + " \"messageId\": \"059f36b4-87a3-44ab-83d2-661975830a7d\",\n" + + " \"receiptHandle\": \"AQEBwJnKyrHigUMZj6rYigCgxlaS3SLy0a...\",\n" + + " \"body\": \"Test message.\",\n" + + " \"attributes\": {\n" + + " \"ApproximateReceiveCount\": \"1\",\n" + + " \"SentTimestamp\": \"1545082649183\",\n" + + " \"SenderId\": \"AIDAIENQZJOLO23YVJ4VO\",\n" + + " \"ApproximateFirstReceiveTimestamp\": \"1545082649185\"\n" + + " },\n" + + " \"messageAttributes\": {},\n" + + " \"md5OfBody\": \"e4e68fb7bd0e697a0ae8f1bb342846b3\",\n" + + " \"eventSource\": \"aws:sqs\",\n" + + " \"eventSourceARN\": \"arn:aws:sqs:us-east-2:123456789012:my-queue\",\n" + + " \"awsRegion\": \"us-east-2\"\n" + + " },\n" + + " {\n" + + " \"messageId\": \"2e1424d4-f796-459a-8184-9c92662be6da\",\n" + + " \"receiptHandle\": \"AQEBzWwaftRI0KuVm4tP+/7q1rGgNqicHq...\",\n" + + " \"body\": \"Test message.\",\n" + + " \"attributes\": {\n" + + " \"ApproximateReceiveCount\": \"1\",\n" + + " \"SentTimestamp\": \"1545082650636\",\n" + + " \"SenderId\": \"AIDAIENQZJOLO23YVJ4VO\",\n" + + " \"ApproximateFirstReceiveTimestamp\": \"1545082650649\"\n" + + " },\n" + + " \"messageAttributes\": {},\n" + + " \"md5OfBody\": \"e4e68fb7bd0e697a0ae8f1bb342846b3\",\n" + + " \"eventSource\": \"aws:sqs\",\n" + + " \"eventSourceARN\": \"arn:aws:sqs:us-east-2:123456789012:my-queue\",\n" + + " \"awsRegion\": \"us-east-2\"\n" + + " }\n" + + " ]\n" + + "}")); + events.put( + S3EventRequestHandler.class, + new EventInfo( + S3Event.class, + "{\n" + + " \"Records\": [\n" + + " {\n" + + " \"eventVersion\": \"2.1\",\n" + + " \"eventSource\": \"aws:s3\",\n" + + " \"awsRegion\": \"us-east-2\",\n" + + " \"eventTime\": \"2019-09-03T19:37:27.192Z\",\n" + + " \"eventName\": \"ObjectCreated:Put\",\n" + + " \"userIdentity\": {\n" + + " \"principalId\": \"AWS:AIDAINPONIXQXHT3IKHL2\"\n" + + " },\n" + + " \"requestParameters\": {\n" + + " \"sourceIPAddress\": \"205.255.255.255\"\n" + + " },\n" + + " \"responseElements\": {\n" + + " \"x-amz-request-id\": \"D82B88E5F771F645\",\n" + + " \"x-amz-id-2\": \"vlR7PnpV2Ce81l0PRw6jlUpck7Jo5ZsQjryTjKlc5aLWGVHPZLj5NeC6qMa0emYBDXOo6QBU0Wo=\"\n" + + " },\n" + + " \"s3\": {\n" + + " \"s3SchemaVersion\": \"1.0\",\n" + + " \"configurationId\": \"828aa6fc-f7b5-4305-8584-487c791949c1\",\n" + + " \"bucket\": {\n" + + " \"name\": \"DOC-EXAMPLE-BUCKET\",\n" + + " \"ownerIdentity\": {\n" + + " \"principalId\": \"A3I5XTEXAMAI3E\"\n" + + " },\n" + + " \"arn\": \"arn:aws:s3:::lambda-artifacts-deafc19498e3f2df\"\n" + + " },\n" + + " \"object\": {\n" + + " \"key\": \"b21b84d653bb07b05b1e6b33684dc11b\",\n" + + " \"size\": 1305107,\n" + + " \"eTag\": \"b21b84d653bb07b05b1e6b33684dc11b\",\n" + + " \"sequencer\": \"0C0F6F405D6ED209E1\"\n" + + " }\n" + + " }\n" + + " }\n" + + " ]\n" + + "}")); + events.put( + SnsEventRequestHandler.class, + new EventInfo( + SNSEvent.class, + "{\n" + + " \"Records\": [\n" + + " {\n" + + " \"EventVersion\": \"1.0\",\n" + + " \"EventSubscriptionArn\": \"arn:aws:sns:us-east-2:123456789012:sns-lambda:21be56ed-a058-49f5-8c98-aedd2564c486\",\n" + + " \"EventSource\": \"aws:sns\",\n" + + " \"Sns\": {\n" + + " \"SignatureVersion\": \"1\",\n" + + " \"Timestamp\": \"2019-01-02T12:45:07.000Z\",\n" + + " \"Signature\": \"tcc6faL2yUC6dgZdmrwh1Y4cGa/ebXEkAi6RibDsvpi+tE/1+82j...65r==\",\n" + + " \"SigningCertUrl\": \"https://sns.us-east-2.amazonaws.com/SimpleNotificationService-ac565b8b1a6c5d002d285f9598aa1d9b.pem\",\n" + + " \"MessageId\": \"95df01b4-ee98-5cb9-9903-4c221d41eb5e\",\n" + + " \"Message\": \"Hello from SNS!\",\n" + + " \"MessageAttributes\": {\n" + + " \"Test\": {\n" + + " \"Type\": \"String\",\n" + + " \"Value\": \"TestString\"\n" + + " },\n" + + " \"TestBinary\": {\n" + + " \"Type\": \"Binary\",\n" + + " \"Value\": \"TestBinary\"\n" + + " }\n" + + " },\n" + + " \"Type\": \"Notification\",\n" + + " \"UnsubscribeUrl\": \"https://sns.us-east-2.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns:us-east-2:123456789012:test-lambda:21be56ed-a058-49f5-8c98-aedd2564c486\",\n" + + " \"TopicArn\":\"arn:aws:sns:us-east-2:123456789012:sns-lambda\",\n" + + " \"Subject\": \"TestInvoke\"\n" + + " }\n" + + " }\n" + + " ]\n" + + "}")); + return events; + } + + private TracingRequestWrapper buildWrapper(Class targetClass) { + WrappedLambda wrappedLambda = new WrappedLambda(targetClass, "handleRequest"); + + return new TracingRequestWrapper(sdk, wrappedLambda); + } + + @ParameterizedTest + @ValueSource( + classes = { + ScheduledEventRequestHandler.class, + KinesisEventRequestHandler.class, + SqsEventRequestHandler.class, + S3EventRequestHandler.class, + SnsEventRequestHandler.class + }) + void handleLambdaEvent(Class targetClass) throws IOException { + wrapper = buildWrapper(targetClass); + EventInfo eventInfo = EVENTS_JSON.get(targetClass); + + Object input = SerializationUtil.fromJson(eventInfo.eventBody, eventInfo.eventType); + + // Call to object based "O handleRequest(I input, Context context)" method + // delegates to the stream based + // "handleRequest(InputStream input, OutputStream output, Context context)" method. + // So serialization/deserialization of both input and outputs are triggered to be verified here. + Object output = wrapper.handleRequest(input, context); + + assertThat(input.getClass()).isEqualTo(output.getClass()); + + // "equals" methods are not properly implemented of Lambda events, + // so we are comparing them over their serialized json data + String inputJson = SerializationUtil.toJson(input); + String outputJson = SerializationUtil.toJson(output); + assertThat(inputJson).isEqualTo(outputJson); + } + + public static class ScheduledEventRequestHandler + implements RequestHandler { + @Override + public ScheduledEvent handleRequest(ScheduledEvent i, Context cntxt) { + return i; + } + } + + public static class KinesisEventRequestHandler + implements RequestHandler { + @Override + public KinesisEvent handleRequest(KinesisEvent i, Context cntxt) { + return i; + } + } + + public static class SqsEventRequestHandler implements RequestHandler { + @Override + public SQSEvent handleRequest(SQSEvent i, Context cntxt) { + return i; + } + } + + public static class S3EventRequestHandler implements RequestHandler { + @Override + public S3Event handleRequest(S3Event i, Context cntxt) { + return i; + } + } + + public static class SnsEventRequestHandler implements RequestHandler { + @Override + public SNSEvent handleRequest(SNSEvent i, Context cntxt) { + return i; + } + } +} diff --git a/instrumentation/aws-lambda/aws-lambda-events-3.11/metadata.yaml b/instrumentation/aws-lambda/aws-lambda-events-3.11/metadata.yaml new file mode 100644 index 000000000000..391729cd71df --- /dev/null +++ b/instrumentation/aws-lambda/aws-lambda-events-3.11/metadata.yaml @@ -0,0 +1,10 @@ +description: > + This instrumentation builds on top of the `aws-lambda-core-1.0` instrumentation, expanding support + to cover the Lambda library, including standard and custom event types. +library_link: https://docs.aws.amazon.com/lambda/latest/dg/java-handler.html +configurations: + - name: otel.instrumentation.aws-lambda.flush-timeout + type: int + default: 10000 + description: Flush timeout in milliseconds. + diff --git a/instrumentation/aws-lambda/aws-lambda-events-common-2.2/library/build.gradle.kts b/instrumentation/aws-lambda/aws-lambda-events-common-2.2/library/build.gradle.kts new file mode 100644 index 000000000000..0e52a2231caf --- /dev/null +++ b/instrumentation/aws-lambda/aws-lambda-events-common-2.2/library/build.gradle.kts @@ -0,0 +1,41 @@ +plugins { + id("otel.library-instrumentation") +} + +dependencies { + api(project(":instrumentation:aws-lambda:aws-lambda-core-1.0:library")) + + compileOnly("io.opentelemetry:opentelemetry-sdk") + compileOnly("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure") + + library("com.amazonaws:aws-lambda-java-core:1.0.0") + // First version to includes support for SQSEvent, currently the most popular message queue used + // with lambda. + // NB: 2.2.0 includes a class called SQSEvent but isn't usable due to it returning private classes + // in public API. + library("com.amazonaws:aws-lambda-java-events:2.2.1") + + // By default, "aws-lambda-java-serialization" library is enabled in the classpath + // at the AWS Lambda environment except "java8" runtime which is deprecated. + // But it is available at "java8.al2" runtime, so it is still can be used + // by Java 8 based Lambda functions. + // So that is the reason that why we add it as compile only dependency. + library("com.amazonaws:aws-lambda-java-serialization:1.1.5") + + // We need Jackson for wrappers to reproduce the serialization does when Lambda invokes a RequestHandler with event + // since Lambda will only be able to invoke the wrapper itself with a generic Object. + // Note that Lambda itself uses Jackson, but does not expose it to the function so we need to include it here. + // TODO: Switch to aws-lambda-java-serialization to more robustly follow Lambda's serialization logic. + implementation("com.fasterxml.jackson.core:jackson-databind") + implementation("io.opentelemetry.contrib:opentelemetry-aws-xray-propagator") + + // allows to get the default events + testLibrary("com.amazonaws:aws-lambda-java-events:3.10.0") +} + +tasks.withType().configureEach { + // required on jdk17 + jvmArgs("--add-opens=java.base/java.lang=ALL-UNNAMED") + jvmArgs("--add-opens=java.base/java.util=ALL-UNNAMED") + jvmArgs("-XX:+IgnoreUnrecognizedVMOptions") +} diff --git a/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/internal/ApiGatewayProxyAttributesExtractor.java b/instrumentation/aws-lambda/aws-lambda-events-common-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/common/v2_2/internal/ApiGatewayProxyAttributesExtractor.java similarity index 98% rename from instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/internal/ApiGatewayProxyAttributesExtractor.java rename to instrumentation/aws-lambda/aws-lambda-events-common-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/common/v2_2/internal/ApiGatewayProxyAttributesExtractor.java index 36f8dcd0b53e..5413f1b09b13 100644 --- a/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/internal/ApiGatewayProxyAttributesExtractor.java +++ b/instrumentation/aws-lambda/aws-lambda-events-common-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/common/v2_2/internal/ApiGatewayProxyAttributesExtractor.java @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.instrumentation.awslambdaevents.v2_2.internal; +package io.opentelemetry.instrumentation.awslambdaevents.common.v2_2.internal; import static io.opentelemetry.instrumentation.api.internal.AttributesExtractorUtil.internalSet; import static io.opentelemetry.instrumentation.api.internal.HttpConstants._OTHER; diff --git a/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/internal/AwsLambdaEventsInstrumenterFactory.java b/instrumentation/aws-lambda/aws-lambda-events-common-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/common/v2_2/internal/AwsLambdaEventsInstrumenterFactory.java similarity index 86% rename from instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/internal/AwsLambdaEventsInstrumenterFactory.java rename to instrumentation/aws-lambda/aws-lambda-events-common-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/common/v2_2/internal/AwsLambdaEventsInstrumenterFactory.java index 502a8e016da8..e5d615113253 100644 --- a/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/internal/AwsLambdaEventsInstrumenterFactory.java +++ b/instrumentation/aws-lambda/aws-lambda-events-common-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/common/v2_2/internal/AwsLambdaEventsInstrumenterFactory.java @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.instrumentation.awslambdaevents.v2_2.internal; +package io.opentelemetry.instrumentation.awslambdaevents.common.v2_2.internal; import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent; import io.opentelemetry.api.OpenTelemetry; @@ -21,13 +21,11 @@ public final class AwsLambdaEventsInstrumenterFactory { public static AwsLambdaFunctionInstrumenter createInstrumenter( - OpenTelemetry openTelemetry, Set knownMethods) { + OpenTelemetry openTelemetry, String instrumentationName, Set knownMethods) { return new AwsLambdaFunctionInstrumenter( openTelemetry, Instrumenter.builder( - openTelemetry, - "io.opentelemetry.aws-lambda-events-2.2", - AwsLambdaEventsInstrumenterFactory::spanName) + openTelemetry, instrumentationName, AwsLambdaEventsInstrumenterFactory::spanName) .addAttributesExtractor(new AwsLambdaFunctionAttributesExtractor()) .addAttributesExtractor(new ApiGatewayProxyAttributesExtractor(knownMethods)) .buildInstrumenter(SpanKindExtractor.alwaysServer())); diff --git a/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/internal/AwsLambdaSqsInstrumenterFactory.java b/instrumentation/aws-lambda/aws-lambda-events-common-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/common/v2_2/internal/AwsLambdaSqsInstrumenterFactory.java similarity index 77% rename from instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/internal/AwsLambdaSqsInstrumenterFactory.java rename to instrumentation/aws-lambda/aws-lambda-events-common-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/common/v2_2/internal/AwsLambdaSqsInstrumenterFactory.java index 4cd11fc0c4f1..e4ce0be1a911 100644 --- a/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/internal/AwsLambdaSqsInstrumenterFactory.java +++ b/instrumentation/aws-lambda/aws-lambda-events-common-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/common/v2_2/internal/AwsLambdaSqsInstrumenterFactory.java @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.instrumentation.awslambdaevents.v2_2.internal; +package io.opentelemetry.instrumentation.awslambdaevents.common.v2_2.internal; import com.amazonaws.services.lambda.runtime.events.SQSEvent; import com.amazonaws.services.lambda.runtime.events.SQSEvent.SQSMessage; @@ -18,21 +18,19 @@ */ public final class AwsLambdaSqsInstrumenterFactory { - public static Instrumenter forEvent(OpenTelemetry openTelemetry) { + public static Instrumenter forEvent( + OpenTelemetry openTelemetry, String instrumentationName) { return Instrumenter.builder( - openTelemetry, - "io.opentelemetry.aws-lambda-events-2.2", - AwsLambdaSqsInstrumenterFactory::spanName) + openTelemetry, instrumentationName, AwsLambdaSqsInstrumenterFactory::spanName) .addAttributesExtractor(new SqsEventAttributesExtractor()) .addSpanLinksExtractor(new SqsEventSpanLinksExtractor()) .buildInstrumenter(SpanKindExtractor.alwaysConsumer()); } - public static Instrumenter forMessage(OpenTelemetry openTelemetry) { + public static Instrumenter forMessage( + OpenTelemetry openTelemetry, String instrumentationName) { return Instrumenter.builder( - openTelemetry, - "io.opentelemetry.aws-lambda-events-2.2", - message -> message.getEventSource() + " process") + openTelemetry, instrumentationName, message -> message.getEventSource() + " process") .addAttributesExtractor(new SqsMessageAttributesExtractor()) .addSpanLinksExtractor(new SqsMessageSpanLinksExtractor()) .buildInstrumenter(SpanKindExtractor.alwaysConsumer()); diff --git a/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/internal/CustomJodaModule.java b/instrumentation/aws-lambda/aws-lambda-events-common-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/common/v2_2/internal/CustomJodaModule.java similarity index 96% rename from instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/internal/CustomJodaModule.java rename to instrumentation/aws-lambda/aws-lambda-events-common-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/common/v2_2/internal/CustomJodaModule.java index 1be598ae8ffb..5be884879c65 100644 --- a/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/internal/CustomJodaModule.java +++ b/instrumentation/aws-lambda/aws-lambda-events-common-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/common/v2_2/internal/CustomJodaModule.java @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.instrumentation.awslambdaevents.v2_2.internal; +package io.opentelemetry.instrumentation.awslambdaevents.common.v2_2.internal; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.databind.DeserializationContext; @@ -34,7 +34,6 @@ class CustomJodaModule extends SimpleModule { private static final long serialVersionUID = 1L; public CustomJodaModule() { - super(); addDeserializer(DateTime.class, new DateTimeDeserialiser()); } diff --git a/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/LambdaParameters.java b/instrumentation/aws-lambda/aws-lambda-events-common-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/common/v2_2/internal/LambdaParameters.java similarity index 81% rename from instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/LambdaParameters.java rename to instrumentation/aws-lambda/aws-lambda-events-common-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/common/v2_2/internal/LambdaParameters.java index 53e68280bade..7e27bed622b4 100644 --- a/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/LambdaParameters.java +++ b/instrumentation/aws-lambda/aws-lambda-events-common-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/common/v2_2/internal/LambdaParameters.java @@ -3,16 +3,20 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.instrumentation.awslambdaevents.v2_2; +package io.opentelemetry.instrumentation.awslambdaevents.common.v2_2.internal; import com.amazonaws.services.lambda.runtime.Context; import java.io.InputStream; import java.lang.reflect.Method; import java.util.function.BiFunction; -final class LambdaParameters { +/** + * This class is internal and is hence not for public use. Its APIs are unstable and can change at + * any time. + */ +public final class LambdaParameters { - static Object[] toArray( + public static Object[] toArray( Method targetMethod, T input, Context context, BiFunction, Object> mapper) { Class[] parameterTypes = targetMethod.getParameterTypes(); Object[] parameters = new Object[parameterTypes.length]; @@ -28,7 +32,7 @@ static Object[] toArray( return parameters; } - static Object[] toParameters(Method targetMethod, T input, Context context) { + public static Object[] toParameters(Method targetMethod, T input, Context context) { Class[] parameterTypes = targetMethod.getParameterTypes(); Object[] parameters = new Object[parameterTypes.length]; for (int i = 0; i < parameterTypes.length; i++) { @@ -43,7 +47,7 @@ static Object[] toParameters(Method targetMethod, T input, Context context) return parameters; } - static Object toInput( + public static Object toInput( Method targetMethod, InputStream inputStream, BiFunction, Object> mapper) { diff --git a/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/internal/SerializationUtil.java b/instrumentation/aws-lambda/aws-lambda-events-common-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/common/v2_2/internal/SerializationUtil.java similarity index 98% rename from instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/internal/SerializationUtil.java rename to instrumentation/aws-lambda/aws-lambda-events-common-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/common/v2_2/internal/SerializationUtil.java index 81649340e649..e8c1e88fb7a0 100644 --- a/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/internal/SerializationUtil.java +++ b/instrumentation/aws-lambda/aws-lambda-events-common-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/common/v2_2/internal/SerializationUtil.java @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.instrumentation.awslambdaevents.v2_2.internal; +package io.opentelemetry.instrumentation.awslambdaevents.common.v2_2.internal; import com.amazonaws.services.lambda.runtime.serialization.PojoSerializer; import com.amazonaws.services.lambda.runtime.serialization.events.LambdaEventSerializers; diff --git a/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/internal/SqsEventAttributesExtractor.java b/instrumentation/aws-lambda/aws-lambda-events-common-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/common/v2_2/internal/SqsEventAttributesExtractor.java similarity index 94% rename from instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/internal/SqsEventAttributesExtractor.java rename to instrumentation/aws-lambda/aws-lambda-events-common-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/common/v2_2/internal/SqsEventAttributesExtractor.java index a29b6a7c4ca7..debabc8c7611 100644 --- a/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/internal/SqsEventAttributesExtractor.java +++ b/instrumentation/aws-lambda/aws-lambda-events-common-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/common/v2_2/internal/SqsEventAttributesExtractor.java @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.instrumentation.awslambdaevents.v2_2.internal; +package io.opentelemetry.instrumentation.awslambdaevents.common.v2_2.internal; import com.amazonaws.services.lambda.runtime.events.SQSEvent; import io.opentelemetry.api.common.AttributeKey; diff --git a/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/internal/SqsEventSpanLinksExtractor.java b/instrumentation/aws-lambda/aws-lambda-events-common-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/common/v2_2/internal/SqsEventSpanLinksExtractor.java similarity index 90% rename from instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/internal/SqsEventSpanLinksExtractor.java rename to instrumentation/aws-lambda/aws-lambda-events-common-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/common/v2_2/internal/SqsEventSpanLinksExtractor.java index afb01ec9845d..131f1c15ca4e 100644 --- a/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/internal/SqsEventSpanLinksExtractor.java +++ b/instrumentation/aws-lambda/aws-lambda-events-common-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/common/v2_2/internal/SqsEventSpanLinksExtractor.java @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.instrumentation.awslambdaevents.v2_2.internal; +package io.opentelemetry.instrumentation.awslambdaevents.common.v2_2.internal; import com.amazonaws.services.lambda.runtime.events.SQSEvent; import io.opentelemetry.context.Context; diff --git a/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/internal/SqsMessageAttributesExtractor.java b/instrumentation/aws-lambda/aws-lambda-events-common-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/common/v2_2/internal/SqsMessageAttributesExtractor.java similarity index 95% rename from instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/internal/SqsMessageAttributesExtractor.java rename to instrumentation/aws-lambda/aws-lambda-events-common-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/common/v2_2/internal/SqsMessageAttributesExtractor.java index 8962dfe08ffc..975f4237872e 100644 --- a/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/internal/SqsMessageAttributesExtractor.java +++ b/instrumentation/aws-lambda/aws-lambda-events-common-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/common/v2_2/internal/SqsMessageAttributesExtractor.java @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.instrumentation.awslambdaevents.v2_2.internal; +package io.opentelemetry.instrumentation.awslambdaevents.common.v2_2.internal; import com.amazonaws.services.lambda.runtime.events.SQSEvent.SQSMessage; import io.opentelemetry.api.common.AttributeKey; diff --git a/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/internal/SqsMessageSpanLinksExtractor.java b/instrumentation/aws-lambda/aws-lambda-events-common-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/common/v2_2/internal/SqsMessageSpanLinksExtractor.java similarity index 96% rename from instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/internal/SqsMessageSpanLinksExtractor.java rename to instrumentation/aws-lambda/aws-lambda-events-common-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/common/v2_2/internal/SqsMessageSpanLinksExtractor.java index 305e3e62a0b1..9c628fe89be2 100644 --- a/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/internal/SqsMessageSpanLinksExtractor.java +++ b/instrumentation/aws-lambda/aws-lambda-events-common-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/common/v2_2/internal/SqsMessageSpanLinksExtractor.java @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.instrumentation.awslambdaevents.v2_2.internal; +package io.opentelemetry.instrumentation.awslambdaevents.common.v2_2.internal; import com.amazonaws.services.lambda.runtime.events.SQSEvent.SQSMessage; import io.opentelemetry.api.trace.Span; diff --git a/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/test/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/LambdaParametersTest.java b/instrumentation/aws-lambda/aws-lambda-events-common-2.2/library/src/test/java/io/opentelemetry/instrumentation/awslambdaevents/common/v2_2/internal/LambdaParametersTest.java similarity index 97% rename from instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/test/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/LambdaParametersTest.java rename to instrumentation/aws-lambda/aws-lambda-events-common-2.2/library/src/test/java/io/opentelemetry/instrumentation/awslambdaevents/common/v2_2/internal/LambdaParametersTest.java index f738b5a34932..efa49d766517 100644 --- a/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/test/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/LambdaParametersTest.java +++ b/instrumentation/aws-lambda/aws-lambda-events-common-2.2/library/src/test/java/io/opentelemetry/instrumentation/awslambdaevents/common/v2_2/internal/LambdaParametersTest.java @@ -3,13 +3,12 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.instrumentation.awslambdaevents.v2_2; +package io.opentelemetry.instrumentation.awslambdaevents.common.v2_2.internal; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; import com.amazonaws.services.lambda.runtime.Context; -import io.opentelemetry.instrumentation.awslambdaevents.v2_2.internal.SerializationUtil; import java.io.ByteArrayInputStream; import java.lang.reflect.Method; import org.junit.jupiter.api.Test; diff --git a/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/test/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/internal/SerializationUtilTest.java b/instrumentation/aws-lambda/aws-lambda-events-common-2.2/library/src/test/java/io/opentelemetry/instrumentation/awslambdaevents/common/v2_2/internal/SerializationUtilTest.java similarity index 99% rename from instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/test/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/internal/SerializationUtilTest.java rename to instrumentation/aws-lambda/aws-lambda-events-common-2.2/library/src/test/java/io/opentelemetry/instrumentation/awslambdaevents/common/v2_2/internal/SerializationUtilTest.java index 7711d15779c3..fe2df60c9081 100644 --- a/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/test/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/internal/SerializationUtilTest.java +++ b/instrumentation/aws-lambda/aws-lambda-events-common-2.2/library/src/test/java/io/opentelemetry/instrumentation/awslambdaevents/common/v2_2/internal/SerializationUtilTest.java @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.instrumentation.awslambdaevents.v2_2.internal; +package io.opentelemetry.instrumentation.awslambdaevents.common.v2_2.internal; import static org.assertj.core.api.Assertions.assertThat; diff --git a/instrumentation/aws-sdk/aws-sdk-1.11/javaagent/build.gradle.kts b/instrumentation/aws-sdk/aws-sdk-1.11/javaagent/build.gradle.kts index 31e40dd3c8aa..e537f915e06f 100644 --- a/instrumentation/aws-sdk/aws-sdk-1.11/javaagent/build.gradle.kts +++ b/instrumentation/aws-sdk/aws-sdk-1.11/javaagent/build.gradle.kts @@ -144,17 +144,6 @@ tasks { } } - val testStableSemconv by registering(Test::class) { - jvmArgs("-Dotel.semconv-stability.opt-in=database") - - systemProperty("collectMetadata", collectMetadata) - systemProperty("metadataConfig", "otel.semconv-stability.opt-in=database") - } - - check { - dependsOn(testStableSemconv) - } - test { usesService(gradle.sharedServices.registrations["testcontainersBuildService"].service) systemProperty("collectMetadata", collectMetadata) @@ -165,6 +154,19 @@ tasks { jvmArgs("-Dotel.instrumentation.aws-sdk.experimental-span-attributes=true") systemProperty("testLatestDeps", findProperty("testLatestDeps") as Boolean) } + + val testStableSemconv by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath + + jvmArgs("-Dotel.semconv-stability.opt-in=database") + systemProperty("collectMetadata", collectMetadata) + systemProperty("metadataConfig", "otel.semconv-stability.opt-in=database") + } + + check { + dependsOn(testStableSemconv) + } } if (!(findProperty("testLatestDeps") as Boolean)) { diff --git a/instrumentation/aws-sdk/aws-sdk-1.11/library-autoconfigure/build.gradle.kts b/instrumentation/aws-sdk/aws-sdk-1.11/library-autoconfigure/build.gradle.kts index d2e3ff87d5ee..f210d0e79173 100644 --- a/instrumentation/aws-sdk/aws-sdk-1.11/library-autoconfigure/build.gradle.kts +++ b/instrumentation/aws-sdk/aws-sdk-1.11/library-autoconfigure/build.gradle.kts @@ -26,10 +26,13 @@ dependencies { tasks { withType().configureEach { systemProperty("otel.instrumentation.aws-sdk.experimental-span-attributes", "true") - systemProperty("otel.instrumentation.messaging.experimental.capture-headers", "test-message-header") + systemProperty("otel.instrumentation.messaging.experimental.capture-headers", "Test-Message-Header") } val testReceiveSpansDisabled by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath + filter { includeTestsMatching("SqsSuppressReceiveSpansTest") } diff --git a/instrumentation/aws-sdk/aws-sdk-1.11/library/build.gradle.kts b/instrumentation/aws-sdk/aws-sdk-1.11/library/build.gradle.kts index 8a3e396a43d8..1ccae1d21c7a 100644 --- a/instrumentation/aws-sdk/aws-sdk-1.11/library/build.gradle.kts +++ b/instrumentation/aws-sdk/aws-sdk-1.11/library/build.gradle.kts @@ -53,11 +53,12 @@ testing { tasks { val testStableSemconv by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath jvmArgs("-Dotel.semconv-stability.opt-in=database") } check { - dependsOn(testing.suites) - dependsOn(testStableSemconv) + dependsOn(testing.suites, testStableSemconv) } } diff --git a/instrumentation/aws-sdk/aws-sdk-1.11/library/src/test/java/io/opentelemetry/instrumentation/awssdk/v1_11/SqsTracingTest.java b/instrumentation/aws-sdk/aws-sdk-1.11/library/src/test/java/io/opentelemetry/instrumentation/awssdk/v1_11/SqsTracingTest.java index 6384fa1aaa72..41c47c9fc507 100644 --- a/instrumentation/aws-sdk/aws-sdk-1.11/library/src/test/java/io/opentelemetry/instrumentation/awssdk/v1_11/SqsTracingTest.java +++ b/instrumentation/aws-sdk/aws-sdk-1.11/library/src/test/java/io/opentelemetry/instrumentation/awssdk/v1_11/SqsTracingTest.java @@ -28,7 +28,7 @@ public AmazonSQSAsyncClientBuilder configureClient(AmazonSQSAsyncClientBuilder c AwsSdkTelemetry.builder(testing().getOpenTelemetry()) .setCaptureExperimentalSpanAttributes(true) .setMessagingReceiveInstrumentationEnabled(true) - .setCapturedHeaders(singletonList("test-message-header")) + .setCapturedHeaders(singletonList("Test-Message-Header")) .build() .newRequestHandler()); } diff --git a/instrumentation/aws-sdk/aws-sdk-1.11/metadata.yaml b/instrumentation/aws-sdk/aws-sdk-1.11/metadata.yaml index 67a599bc9606..1e9e99f398b0 100644 --- a/instrumentation/aws-sdk/aws-sdk-1.11/metadata.yaml +++ b/instrumentation/aws-sdk/aws-sdk-1.11/metadata.yaml @@ -2,6 +2,7 @@ description: > This instrumentation covers the AWS SDK 1.11+ client library, enabling messaging and client spans and metrics for calls to AWS services including DynamoDB, EC2, Kinesis, Lambda, RDS, S3, secrets manager, SNS/SQS and step functions. +library_link: https://aws.amazon.com/sdk-for-java/ configurations: - name: otel.instrumentation.aws-sdk.experimental-span-attributes description: > diff --git a/instrumentation/aws-sdk/aws-sdk-1.11/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v1_11/AbstractSqsTracingTest.java b/instrumentation/aws-sdk/aws-sdk-1.11/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v1_11/AbstractSqsTracingTest.java index 40f349ec87e4..284559753f2c 100644 --- a/instrumentation/aws-sdk/aws-sdk-1.11/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v1_11/AbstractSqsTracingTest.java +++ b/instrumentation/aws-sdk/aws-sdk-1.11/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v1_11/AbstractSqsTracingTest.java @@ -106,7 +106,7 @@ void testSimpleSqsProducerConsumerServicesCaptureHeaders(boolean testCaptureHead if (testCaptureHeaders) { sendMessageRequest.addMessageAttributesEntry( - "test-message-header", + "Test-Message-Header", new MessageAttributeValue().withDataType("String").withStringValue("test")); } sqsClient.sendMessage(sendMessageRequest); @@ -114,7 +114,7 @@ void testSimpleSqsProducerConsumerServicesCaptureHeaders(boolean testCaptureHead ReceiveMessageRequest receiveMessageRequest = new ReceiveMessageRequest("http://localhost:" + sqsPort + "/000000000000/testSdkSqs"); if (testCaptureHeaders) { - receiveMessageRequest.withMessageAttributeNames("test-message-header"); + receiveMessageRequest.withMessageAttributeNames("Test-Message-Header"); } ReceiveMessageResult receiveMessageResult = sqsClient.receiveMessage(receiveMessageRequest); @@ -182,7 +182,7 @@ void testSimpleSqsProducerConsumerServicesCaptureHeaders(boolean testCaptureHead if (testCaptureHeaders) { attributes.add( satisfies( - stringArrayKey("messaging.header.test_message_header"), + stringArrayKey("messaging.header.Test_Message_Header"), val -> val.isEqualTo(singletonList("test")))); } @@ -222,7 +222,7 @@ void testSimpleSqsProducerConsumerServicesCaptureHeaders(boolean testCaptureHead if (testCaptureHeaders) { attributes.add( satisfies( - stringArrayKey("messaging.header.test_message_header"), + stringArrayKey("messaging.header.Test_Message_Header"), val -> val.isEqualTo(singletonList("test")))); } @@ -261,7 +261,7 @@ void testSimpleSqsProducerConsumerServicesCaptureHeaders(boolean testCaptureHead if (testCaptureHeaders) { attributes.add( satisfies( - stringArrayKey("messaging.header.test_message_header"), + stringArrayKey("messaging.header.Test_Message_Header"), val -> val.isEqualTo(singletonList("test")))); } span.hasName("testSdkSqs process") diff --git a/instrumentation/aws-sdk/aws-sdk-2.2/javaagent/build.gradle.kts b/instrumentation/aws-sdk/aws-sdk-2.2/javaagent/build.gradle.kts index 79258da0e422..41e002d5cdb1 100644 --- a/instrumentation/aws-sdk/aws-sdk-2.2/javaagent/build.gradle.kts +++ b/instrumentation/aws-sdk/aws-sdk-2.2/javaagent/build.gradle.kts @@ -1,3 +1,5 @@ +import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar + plugins { id("otel.javaagent-instrumentation") } @@ -173,6 +175,9 @@ testing { tasks { val testExperimentalSqs by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath + filter { excludeTestsMatching("Aws2SqsSuppressReceiveSpansTest") } @@ -181,12 +186,28 @@ tasks { } val testReceiveSpansDisabled by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath + filter { includeTestsMatching("Aws2SqsSuppressReceiveSpansTest") } include("**/Aws2SqsSuppressReceiveSpansTest.*") } + val testStableSemconv by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath + + filter { + excludeTestsMatching("Aws2SqsSuppressReceiveSpansTest") + } + systemProperty("otel.instrumentation.messaging.experimental.receive-telemetry.enabled", "true") + jvmArgs("-Dotel.semconv-stability.opt-in=database") + + systemProperty("metadataConfig", "otel.semconv-stability.opt-in=database") + } + test { filter { excludeTestsMatching("Aws2SqsSuppressReceiveSpansTest") @@ -196,9 +217,7 @@ tasks { } check { - dependsOn(testExperimentalSqs) - dependsOn(testReceiveSpansDisabled) - dependsOn(testing.suites) + dependsOn(testing.suites, testExperimentalSqs, testStableSemconv, testReceiveSpansDisabled) } withType().configureEach { @@ -209,23 +228,13 @@ tasks { systemProperty("collectMetadata", collectMetadata) } - withType().configureEach { + withType().configureEach { mergeServiceFiles { include("software/amazon/awssdk/global/handlers/execution.interceptors") } - } - - val testStableSemconv by registering(Test::class) { - filter { - excludeTestsMatching("Aws2SqsSuppressReceiveSpansTest") + // mergeServiceFiles requires that duplicate strategy is set to include + filesMatching("software/amazon/awssdk/global/handlers/execution.interceptors") { + duplicatesStrategy = DuplicatesStrategy.INCLUDE } - systemProperty("otel.instrumentation.messaging.experimental.receive-telemetry.enabled", "true") - jvmArgs("-Dotel.semconv-stability.opt-in=database") - - systemProperty("metadataConfig", "otel.semconv-stability.opt-in=database") - } - - check { - dependsOn(testStableSemconv) } } diff --git a/instrumentation/aws-sdk/aws-sdk-2.2/library-autoconfigure/build.gradle.kts b/instrumentation/aws-sdk/aws-sdk-2.2/library-autoconfigure/build.gradle.kts index 3cdb0f82abfb..54daa03f7e08 100644 --- a/instrumentation/aws-sdk/aws-sdk-2.2/library-autoconfigure/build.gradle.kts +++ b/instrumentation/aws-sdk/aws-sdk-2.2/library-autoconfigure/build.gradle.kts @@ -28,11 +28,14 @@ tasks { withType().configureEach { systemProperty("otel.instrumentation.aws-sdk.experimental-span-attributes", true) systemProperty("otel.instrumentation.aws-sdk.experimental-record-individual-http-error", true) - systemProperty("otel.instrumentation.messaging.experimental.capture-headers", "test-message-header") + systemProperty("otel.instrumentation.messaging.experimental.capture-headers", "Test-Message-Header") systemProperty("testLatestDeps", findProperty("testLatestDeps") as Boolean) } val testStableSemconv by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath + jvmArgs("-Dotel.semconv-stability.opt-in=database") } diff --git a/instrumentation/aws-sdk/aws-sdk-2.2/library/build.gradle.kts b/instrumentation/aws-sdk/aws-sdk-2.2/library/build.gradle.kts index c832ee002c1e..a44678d8d2a3 100644 --- a/instrumentation/aws-sdk/aws-sdk-2.2/library/build.gradle.kts +++ b/instrumentation/aws-sdk/aws-sdk-2.2/library/build.gradle.kts @@ -78,11 +78,13 @@ tasks { } val testStableSemconv by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath + jvmArgs("-Dotel.semconv-stability.opt-in=database") } check { - dependsOn(testing.suites) - dependsOn(testStableSemconv) + dependsOn(testing.suites, testStableSemconv) } } diff --git a/instrumentation/aws-sdk/aws-sdk-2.2/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/internal/BedrockRuntimeAttributesGetter.java b/instrumentation/aws-sdk/aws-sdk-2.2/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/internal/BedrockRuntimeAttributesGetter.java index 58f88e68517f..e3c0d2be75f3 100644 --- a/instrumentation/aws-sdk/aws-sdk-2.2/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/internal/BedrockRuntimeAttributesGetter.java +++ b/instrumentation/aws-sdk/aws-sdk-2.2/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/internal/BedrockRuntimeAttributesGetter.java @@ -17,10 +17,10 @@ enum BedrockRuntimeAttributesGetter implements GenAiAttributesGetter { INSTANCE; - static final class GenAiSystemIncubatingValues { + static final class GenAiProviderNameIncubatingValues { static final String AWS_BEDROCK = "aws.bedrock"; - private GenAiSystemIncubatingValues() {} + private GenAiProviderNameIncubatingValues() {} } @Override @@ -30,7 +30,7 @@ public String getOperationName(ExecutionAttributes executionAttributes) { @Override public String getSystem(ExecutionAttributes executionAttributes) { - return GenAiSystemIncubatingValues.AWS_BEDROCK; + return GenAiProviderNameIncubatingValues.AWS_BEDROCK; } @Nullable diff --git a/instrumentation/aws-sdk/aws-sdk-2.2/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/internal/BedrockRuntimeImpl.java b/instrumentation/aws-sdk/aws-sdk-2.2/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/internal/BedrockRuntimeImpl.java index 9ed75301c5be..a01b9775ac6a 100644 --- a/instrumentation/aws-sdk/aws-sdk-2.2/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/internal/BedrockRuntimeImpl.java +++ b/instrumentation/aws-sdk/aws-sdk-2.2/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/internal/BedrockRuntimeImpl.java @@ -88,7 +88,8 @@ private GenAiOperationNameIncubatingValues() {} } private static final AttributeKey EVENT_NAME = stringKey("event.name"); - private static final AttributeKey GEN_AI_SYSTEM = stringKey("gen_ai.system"); + private static final AttributeKey GEN_AI_PROVIDER_NAME = + stringKey("gen_ai.provider.name"); private static final ExecutionAttribute INVOKE_MODEL_REQUEST_BODY = new ExecutionAttribute<>(BedrockRuntimeImpl.class.getName() + ".InvokeModelRequestBody"); @@ -1618,7 +1619,8 @@ private static LogRecordBuilder newEvent(Context otelContext, Logger eventLogger .logRecordBuilder() .setContext(otelContext) .setAttribute( - GEN_AI_SYSTEM, BedrockRuntimeAttributesGetter.GenAiSystemIncubatingValues.AWS_BEDROCK); + GEN_AI_PROVIDER_NAME, + BedrockRuntimeAttributesGetter.GenAiProviderNameIncubatingValues.AWS_BEDROCK); } private static void emitToolResultEvents( diff --git a/instrumentation/aws-sdk/aws-sdk-2.2/library/src/test/java/io/opentelemetry/instrumentation/awssdk/v2_2/Aws2SqsTracingTest.java b/instrumentation/aws-sdk/aws-sdk-2.2/library/src/test/java/io/opentelemetry/instrumentation/awssdk/v2_2/Aws2SqsTracingTest.java index 6720cdbbc6ea..540ef500de26 100644 --- a/instrumentation/aws-sdk/aws-sdk-2.2/library/src/test/java/io/opentelemetry/instrumentation/awssdk/v2_2/Aws2SqsTracingTest.java +++ b/instrumentation/aws-sdk/aws-sdk-2.2/library/src/test/java/io/opentelemetry/instrumentation/awssdk/v2_2/Aws2SqsTracingTest.java @@ -33,7 +33,7 @@ void setup() { AwsSdkTelemetry.builder(getTesting().getOpenTelemetry()) .setCaptureExperimentalSpanAttributes(true) .setMessagingReceiveInstrumentationEnabled(true) - .setCapturedHeaders(singletonList("test-message-header")); + .setCapturedHeaders(singletonList("Test-Message-Header")); configure(telemetryBuilder); telemetry = telemetryBuilder.build(); diff --git a/instrumentation/aws-sdk/aws-sdk-2.2/library/src/testBedrockRuntime/java/io/opentelemetry/instrumentation/awssdk/v2_2/internal/Aws2BedrockRuntimeTest.java b/instrumentation/aws-sdk/aws-sdk-2.2/library/src/testBedrockRuntime/java/io/opentelemetry/instrumentation/awssdk/v2_2/internal/Aws2BedrockRuntimeTest.java index 6725bdd0e140..1bdf53c1c04a 100644 --- a/instrumentation/aws-sdk/aws-sdk-2.2/library/src/testBedrockRuntime/java/io/opentelemetry/instrumentation/awssdk/v2_2/internal/Aws2BedrockRuntimeTest.java +++ b/instrumentation/aws-sdk/aws-sdk-2.2/library/src/testBedrockRuntime/java/io/opentelemetry/instrumentation/awssdk/v2_2/internal/Aws2BedrockRuntimeTest.java @@ -7,13 +7,13 @@ import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; import static io.opentelemetry.semconv.incubating.GenAiIncubatingAttributes.GEN_AI_OPERATION_NAME; +import static io.opentelemetry.semconv.incubating.GenAiIncubatingAttributes.GEN_AI_PROVIDER_NAME; import static io.opentelemetry.semconv.incubating.GenAiIncubatingAttributes.GEN_AI_REQUEST_MODEL; import static io.opentelemetry.semconv.incubating.GenAiIncubatingAttributes.GEN_AI_RESPONSE_FINISH_REASONS; -import static io.opentelemetry.semconv.incubating.GenAiIncubatingAttributes.GEN_AI_SYSTEM; import static io.opentelemetry.semconv.incubating.GenAiIncubatingAttributes.GEN_AI_TOKEN_TYPE; import static io.opentelemetry.semconv.incubating.GenAiIncubatingAttributes.GEN_AI_USAGE_INPUT_TOKENS; import static io.opentelemetry.semconv.incubating.GenAiIncubatingAttributes.GEN_AI_USAGE_OUTPUT_TOKENS; -import static io.opentelemetry.semconv.incubating.GenAiIncubatingAttributes.GenAiSystemIncubatingValues.AWS_BEDROCK; +import static io.opentelemetry.semconv.incubating.GenAiIncubatingAttributes.GenAiProviderNameIncubatingValues.AWS_BEDROCK; import static java.util.Arrays.asList; import static java.util.Collections.emptyMap; import static java.util.Collections.singletonList; @@ -162,7 +162,7 @@ void testConverseToolCallNoMessageContent() { span.hasName("chat amazon.nova-micro-v1:0") .hasKind(SpanKind.CLIENT) .hasAttributesSatisfying( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_OPERATION_NAME, GenAiIncubatingAttributes.GenAiOperationNameIncubatingValues @@ -188,7 +188,7 @@ void testConverseToolCallNoMessageContent() { .hasSum(415) .hasCount(1) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_TOKEN_TYPE, GenAiIncubatingAttributes @@ -203,11 +203,11 @@ void testConverseToolCallNoMessageContent() { .hasSum(162) .hasCount(1) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_TOKEN_TYPE, GenAiIncubatingAttributes - .GenAiTokenTypeIncubatingValues.COMPLETION), + .GenAiTokenTypeIncubatingValues.OUTPUT), equalTo( GEN_AI_OPERATION_NAME, GenAiIncubatingAttributes @@ -225,7 +225,7 @@ void testConverseToolCallNoMessageContent() { point .hasSumGreaterThan(0.0) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_OPERATION_NAME, GenAiIncubatingAttributes @@ -238,13 +238,14 @@ void testConverseToolCallNoMessageContent() { .waitAndAssertLogRecords( log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo(EVENT_NAME, "gen_ai.user.message")) .hasSpanContext(spanCtx0) .hasBody(Value.of(Collections.emptyMap())), log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), equalTo(EVENT_NAME, "gen_ai.choice")) + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), + equalTo(EVENT_NAME, "gen_ai.choice")) .hasSpanContext(spanCtx0) .hasBody( Value.of( @@ -315,7 +316,7 @@ void testConverseToolCallNoMessageContent() { span.hasName("chat amazon.nova-micro-v1:0") .hasKind(SpanKind.CLIENT) .hasAttributesSatisfying( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_OPERATION_NAME, GenAiIncubatingAttributes.GenAiOperationNameIncubatingValues @@ -341,7 +342,7 @@ void testConverseToolCallNoMessageContent() { .hasSum(554) .hasCount(1) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_TOKEN_TYPE, GenAiIncubatingAttributes @@ -356,11 +357,11 @@ void testConverseToolCallNoMessageContent() { .hasSum(57) .hasCount(1) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_TOKEN_TYPE, GenAiIncubatingAttributes - .GenAiTokenTypeIncubatingValues.COMPLETION), + .GenAiTokenTypeIncubatingValues.OUTPUT), equalTo( GEN_AI_OPERATION_NAME, GenAiIncubatingAttributes @@ -378,7 +379,7 @@ void testConverseToolCallNoMessageContent() { point .hasSumGreaterThan(0.0) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_OPERATION_NAME, GenAiIncubatingAttributes @@ -391,13 +392,13 @@ void testConverseToolCallNoMessageContent() { .waitAndAssertLogRecords( log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo(EVENT_NAME, "gen_ai.user.message")) .hasSpanContext(spanCtx1) .hasBody(Value.of(emptyMap())), log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo(EVENT_NAME, "gen_ai.assistant.message")) .hasSpanContext(spanCtx1) .hasBody( @@ -415,19 +416,20 @@ void testConverseToolCallNoMessageContent() { KeyValue.of("type", Value.of("function"))))))), log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo(EVENT_NAME, "gen_ai.tool.message")) .hasSpanContext(spanCtx1) .hasBody(Value.of(KeyValue.of("id", Value.of(seattleToolUseId)))), log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo(EVENT_NAME, "gen_ai.tool.message")) .hasSpanContext(spanCtx1) .hasBody(Value.of(KeyValue.of("id", Value.of(sanFranciscoToolUseId)))), log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), equalTo(EVENT_NAME, "gen_ai.choice")) + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), + equalTo(EVENT_NAME, "gen_ai.choice")) .hasSpanContext(spanCtx1) .hasBody( Value.of( @@ -543,7 +545,7 @@ void testConverseToolCallStreamNoMessageContent() span.hasName("chat amazon.nova-micro-v1:0") .hasKind(SpanKind.CLIENT) .hasAttributesSatisfying( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_OPERATION_NAME, GenAiIncubatingAttributes.GenAiOperationNameIncubatingValues @@ -569,7 +571,7 @@ void testConverseToolCallStreamNoMessageContent() .hasSum(415) .hasCount(1) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_TOKEN_TYPE, GenAiIncubatingAttributes @@ -584,11 +586,11 @@ void testConverseToolCallStreamNoMessageContent() .hasSum(162) .hasCount(1) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_TOKEN_TYPE, GenAiIncubatingAttributes - .GenAiTokenTypeIncubatingValues.COMPLETION), + .GenAiTokenTypeIncubatingValues.OUTPUT), equalTo( GEN_AI_OPERATION_NAME, GenAiIncubatingAttributes @@ -606,7 +608,7 @@ void testConverseToolCallStreamNoMessageContent() point .hasSumGreaterThan(0.0) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_OPERATION_NAME, GenAiIncubatingAttributes @@ -619,13 +621,14 @@ void testConverseToolCallStreamNoMessageContent() .waitAndAssertLogRecords( log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo(EVENT_NAME, "gen_ai.user.message")) .hasSpanContext(spanCtx0) .hasBody(Value.of(Collections.emptyMap())), log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), equalTo(EVENT_NAME, "gen_ai.choice")) + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), + equalTo(EVENT_NAME, "gen_ai.choice")) .hasSpanContext(spanCtx0) .hasBody( Value.of( @@ -715,7 +718,7 @@ void testConverseToolCallStreamNoMessageContent() span.hasName("chat amazon.nova-micro-v1:0") .hasKind(SpanKind.CLIENT) .hasAttributesSatisfying( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_OPERATION_NAME, GenAiIncubatingAttributes.GenAiOperationNameIncubatingValues @@ -741,7 +744,7 @@ void testConverseToolCallStreamNoMessageContent() .hasSum(554) .hasCount(1) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_TOKEN_TYPE, GenAiIncubatingAttributes @@ -756,11 +759,11 @@ void testConverseToolCallStreamNoMessageContent() .hasSum(59) .hasCount(1) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_TOKEN_TYPE, GenAiIncubatingAttributes - .GenAiTokenTypeIncubatingValues.COMPLETION), + .GenAiTokenTypeIncubatingValues.OUTPUT), equalTo( GEN_AI_OPERATION_NAME, GenAiIncubatingAttributes @@ -778,7 +781,7 @@ void testConverseToolCallStreamNoMessageContent() point .hasSumGreaterThan(0.0) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_OPERATION_NAME, GenAiIncubatingAttributes @@ -791,13 +794,13 @@ void testConverseToolCallStreamNoMessageContent() .waitAndAssertLogRecords( log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo(EVENT_NAME, "gen_ai.user.message")) .hasSpanContext(spanCtx1) .hasBody(Value.of(emptyMap())), log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo(EVENT_NAME, "gen_ai.assistant.message")) .hasSpanContext(spanCtx1) .hasBody( @@ -815,19 +818,20 @@ void testConverseToolCallStreamNoMessageContent() KeyValue.of("type", Value.of("function"))))))), log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo(EVENT_NAME, "gen_ai.tool.message")) .hasSpanContext(spanCtx1) .hasBody(Value.of(KeyValue.of("id", Value.of(seattleToolUseId)))), log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo(EVENT_NAME, "gen_ai.tool.message")) .hasSpanContext(spanCtx1) .hasBody(Value.of(KeyValue.of("id", Value.of(sanFranciscoToolUseId)))), log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), equalTo(EVENT_NAME, "gen_ai.choice")) + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), + equalTo(EVENT_NAME, "gen_ai.choice")) .hasSpanContext(spanCtx1) .hasBody( Value.of( @@ -990,7 +994,7 @@ void testInvokeModelToolCallAmazonNovaNoMessageContent() { span.hasName("chat amazon.nova-micro-v1:0") .hasKind(SpanKind.CLIENT) .hasAttributesSatisfying( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_OPERATION_NAME, GenAiIncubatingAttributes.GenAiOperationNameIncubatingValues @@ -1016,7 +1020,7 @@ void testInvokeModelToolCallAmazonNovaNoMessageContent() { .hasSum(416) .hasCount(1) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_TOKEN_TYPE, GenAiIncubatingAttributes @@ -1031,11 +1035,11 @@ void testInvokeModelToolCallAmazonNovaNoMessageContent() { .hasSum(166) .hasCount(1) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_TOKEN_TYPE, GenAiIncubatingAttributes - .GenAiTokenTypeIncubatingValues.COMPLETION), + .GenAiTokenTypeIncubatingValues.OUTPUT), equalTo( GEN_AI_OPERATION_NAME, GenAiIncubatingAttributes @@ -1053,7 +1057,7 @@ void testInvokeModelToolCallAmazonNovaNoMessageContent() { point .hasSumGreaterThan(0.0) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_OPERATION_NAME, GenAiIncubatingAttributes @@ -1065,13 +1069,14 @@ void testInvokeModelToolCallAmazonNovaNoMessageContent() { .waitAndAssertLogRecords( log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo(EVENT_NAME, "gen_ai.user.message")) .hasSpanContext(spanCtx0) .hasBody(Value.of(Collections.emptyMap())), log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), equalTo(EVENT_NAME, "gen_ai.choice")) + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), + equalTo(EVENT_NAME, "gen_ai.choice")) .hasSpanContext(spanCtx0) .hasBody( Value.of( @@ -1171,7 +1176,7 @@ void testInvokeModelToolCallAmazonNovaNoMessageContent() { span.hasName("chat amazon.nova-micro-v1:0") .hasKind(SpanKind.CLIENT) .hasAttributesSatisfying( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_OPERATION_NAME, GenAiIncubatingAttributes.GenAiOperationNameIncubatingValues @@ -1197,7 +1202,7 @@ void testInvokeModelToolCallAmazonNovaNoMessageContent() { .hasSum(559) .hasCount(1) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_TOKEN_TYPE, GenAiIncubatingAttributes @@ -1212,11 +1217,11 @@ void testInvokeModelToolCallAmazonNovaNoMessageContent() { .hasSum(59) .hasCount(1) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_TOKEN_TYPE, GenAiIncubatingAttributes - .GenAiTokenTypeIncubatingValues.COMPLETION), + .GenAiTokenTypeIncubatingValues.OUTPUT), equalTo( GEN_AI_OPERATION_NAME, GenAiIncubatingAttributes @@ -1234,7 +1239,7 @@ void testInvokeModelToolCallAmazonNovaNoMessageContent() { point .hasSumGreaterThan(0.0) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_OPERATION_NAME, GenAiIncubatingAttributes @@ -1247,13 +1252,13 @@ void testInvokeModelToolCallAmazonNovaNoMessageContent() { .waitAndAssertLogRecords( log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo(EVENT_NAME, "gen_ai.user.message")) .hasSpanContext(spanCtx1) .hasBody(Value.of(Collections.emptyMap())), log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo(EVENT_NAME, "gen_ai.assistant.message")) .hasSpanContext(spanCtx1) .hasBody( @@ -1271,19 +1276,20 @@ void testInvokeModelToolCallAmazonNovaNoMessageContent() { KeyValue.of("type", Value.of("function"))))))), log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo(EVENT_NAME, "gen_ai.tool.message")) .hasSpanContext(spanCtx1) .hasBody(Value.of(KeyValue.of("id", Value.of(seattleToolUseId)))), log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo(EVENT_NAME, "gen_ai.tool.message")) .hasSpanContext(spanCtx1) .hasBody(Value.of(KeyValue.of("id", Value.of(sanFranciscoToolUseId)))), log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), equalTo(EVENT_NAME, "gen_ai.choice")) + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), + equalTo(EVENT_NAME, "gen_ai.choice")) .hasSpanContext(spanCtx1) .hasBody( Value.of( @@ -1466,7 +1472,7 @@ public void accept(PayloadPart chunk) { span.hasName("chat amazon.nova-micro-v1:0") .hasKind(SpanKind.CLIENT) .hasAttributesSatisfying( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_OPERATION_NAME, GenAiIncubatingAttributes.GenAiOperationNameIncubatingValues @@ -1492,7 +1498,7 @@ public void accept(PayloadPart chunk) { .hasSum(416) .hasCount(1) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_TOKEN_TYPE, GenAiIncubatingAttributes @@ -1507,11 +1513,11 @@ public void accept(PayloadPart chunk) { .hasSum(165) .hasCount(1) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_TOKEN_TYPE, GenAiIncubatingAttributes - .GenAiTokenTypeIncubatingValues.COMPLETION), + .GenAiTokenTypeIncubatingValues.OUTPUT), equalTo( GEN_AI_OPERATION_NAME, GenAiIncubatingAttributes @@ -1529,7 +1535,7 @@ public void accept(PayloadPart chunk) { point .hasSumGreaterThan(0.0) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_OPERATION_NAME, GenAiIncubatingAttributes @@ -1541,13 +1547,14 @@ public void accept(PayloadPart chunk) { .waitAndAssertLogRecords( log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo(EVENT_NAME, "gen_ai.user.message")) .hasSpanContext(spanCtx0) .hasBody(Value.of(Collections.emptyMap())), log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), equalTo(EVENT_NAME, "gen_ai.choice")) + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), + equalTo(EVENT_NAME, "gen_ai.choice")) .hasSpanContext(spanCtx0) .hasBody( Value.of( @@ -1690,7 +1697,7 @@ public void accept(PayloadPart chunk) { span.hasName("chat amazon.nova-micro-v1:0") .hasKind(SpanKind.CLIENT) .hasAttributesSatisfying( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_OPERATION_NAME, GenAiIncubatingAttributes.GenAiOperationNameIncubatingValues @@ -1716,7 +1723,7 @@ public void accept(PayloadPart chunk) { .hasSum(558) .hasCount(1) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_TOKEN_TYPE, GenAiIncubatingAttributes @@ -1731,11 +1738,11 @@ public void accept(PayloadPart chunk) { .hasSum(58) .hasCount(1) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_TOKEN_TYPE, GenAiIncubatingAttributes - .GenAiTokenTypeIncubatingValues.COMPLETION), + .GenAiTokenTypeIncubatingValues.OUTPUT), equalTo( GEN_AI_OPERATION_NAME, GenAiIncubatingAttributes @@ -1753,7 +1760,7 @@ public void accept(PayloadPart chunk) { point .hasSumGreaterThan(0.0) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_OPERATION_NAME, GenAiIncubatingAttributes @@ -1766,13 +1773,13 @@ public void accept(PayloadPart chunk) { .waitAndAssertLogRecords( log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo(EVENT_NAME, "gen_ai.user.message")) .hasSpanContext(spanCtx1) .hasBody(Value.of(Collections.emptyMap())), log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo(EVENT_NAME, "gen_ai.assistant.message")) .hasSpanContext(spanCtx1) .hasBody( @@ -1790,19 +1797,20 @@ public void accept(PayloadPart chunk) { KeyValue.of("type", Value.of("function"))))))), log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo(EVENT_NAME, "gen_ai.tool.message")) .hasSpanContext(spanCtx1) .hasBody(Value.of(KeyValue.of("id", Value.of(seattleToolUseId)))), log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo(EVENT_NAME, "gen_ai.tool.message")) .hasSpanContext(spanCtx1) .hasBody(Value.of(KeyValue.of("id", Value.of(sanFranciscoToolUseId)))), log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), equalTo(EVENT_NAME, "gen_ai.choice")) + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), + equalTo(EVENT_NAME, "gen_ai.choice")) .hasSpanContext(spanCtx1) .hasBody( Value.of( @@ -1916,7 +1924,7 @@ void testInvokeModelToolCallAnthropicClaudeNoMessageContent() { span.hasName("chat anthropic.claude-3-5-sonnet-20240620-v1:0") .hasKind(SpanKind.CLIENT) .hasAttributesSatisfying( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_OPERATION_NAME, GenAiIncubatingAttributes.GenAiOperationNameIncubatingValues @@ -1942,7 +1950,7 @@ void testInvokeModelToolCallAnthropicClaudeNoMessageContent() { .hasSum(380) .hasCount(1) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_TOKEN_TYPE, GenAiIncubatingAttributes @@ -1957,11 +1965,11 @@ void testInvokeModelToolCallAnthropicClaudeNoMessageContent() { .hasSum(133) .hasCount(1) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_TOKEN_TYPE, GenAiIncubatingAttributes - .GenAiTokenTypeIncubatingValues.COMPLETION), + .GenAiTokenTypeIncubatingValues.OUTPUT), equalTo( GEN_AI_OPERATION_NAME, GenAiIncubatingAttributes @@ -1979,7 +1987,7 @@ void testInvokeModelToolCallAnthropicClaudeNoMessageContent() { point .hasSumGreaterThan(0.0) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_OPERATION_NAME, GenAiIncubatingAttributes @@ -1991,13 +1999,14 @@ void testInvokeModelToolCallAnthropicClaudeNoMessageContent() { .waitAndAssertLogRecords( log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo(EVENT_NAME, "gen_ai.user.message")) .hasSpanContext(spanCtx0) .hasBody(Value.of(Collections.emptyMap())), log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), equalTo(EVENT_NAME, "gen_ai.choice")) + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), + equalTo(EVENT_NAME, "gen_ai.choice")) .hasSpanContext(spanCtx0) .hasBody( Value.of( @@ -2075,7 +2084,7 @@ void testInvokeModelToolCallAnthropicClaudeNoMessageContent() { span.hasName("chat anthropic.claude-3-5-sonnet-20240620-v1:0") .hasKind(SpanKind.CLIENT) .hasAttributesSatisfying( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_OPERATION_NAME, GenAiIncubatingAttributes.GenAiOperationNameIncubatingValues @@ -2101,7 +2110,7 @@ void testInvokeModelToolCallAnthropicClaudeNoMessageContent() { .hasSum(590) .hasCount(1) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_TOKEN_TYPE, GenAiIncubatingAttributes @@ -2116,11 +2125,11 @@ void testInvokeModelToolCallAnthropicClaudeNoMessageContent() { .hasSum(132) .hasCount(1) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_TOKEN_TYPE, GenAiIncubatingAttributes - .GenAiTokenTypeIncubatingValues.COMPLETION), + .GenAiTokenTypeIncubatingValues.OUTPUT), equalTo( GEN_AI_OPERATION_NAME, GenAiIncubatingAttributes @@ -2138,7 +2147,7 @@ void testInvokeModelToolCallAnthropicClaudeNoMessageContent() { point .hasSumGreaterThan(0.0) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_OPERATION_NAME, GenAiIncubatingAttributes @@ -2151,13 +2160,13 @@ void testInvokeModelToolCallAnthropicClaudeNoMessageContent() { .waitAndAssertLogRecords( log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo(EVENT_NAME, "gen_ai.user.message")) .hasSpanContext(spanCtx1) .hasBody(Value.of(Collections.emptyMap())), log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo(EVENT_NAME, "gen_ai.assistant.message")) .hasSpanContext(spanCtx1) .hasBody( @@ -2175,19 +2184,20 @@ void testInvokeModelToolCallAnthropicClaudeNoMessageContent() { KeyValue.of("type", Value.of("function"))))))), log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo(EVENT_NAME, "gen_ai.tool.message")) .hasSpanContext(spanCtx1) .hasBody(Value.of(KeyValue.of("id", Value.of(seattleToolUseId)))), log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo(EVENT_NAME, "gen_ai.tool.message")) .hasSpanContext(spanCtx1) .hasBody(Value.of(KeyValue.of("id", Value.of(sanFranciscoToolUseId)))), log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), equalTo(EVENT_NAME, "gen_ai.choice")) + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), + equalTo(EVENT_NAME, "gen_ai.choice")) .hasSpanContext(spanCtx1) .hasBody( Value.of( @@ -2373,7 +2383,7 @@ public void accept(PayloadPart chunk) { span.hasName("chat anthropic.claude-3-5-sonnet-20240620-v1:0") .hasKind(SpanKind.CLIENT) .hasAttributesSatisfying( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_OPERATION_NAME, GenAiIncubatingAttributes.GenAiOperationNameIncubatingValues @@ -2399,7 +2409,7 @@ public void accept(PayloadPart chunk) { .hasSum(380) .hasCount(1) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_TOKEN_TYPE, GenAiIncubatingAttributes @@ -2414,11 +2424,11 @@ public void accept(PayloadPart chunk) { .hasSum(144) .hasCount(1) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_TOKEN_TYPE, GenAiIncubatingAttributes - .GenAiTokenTypeIncubatingValues.COMPLETION), + .GenAiTokenTypeIncubatingValues.OUTPUT), equalTo( GEN_AI_OPERATION_NAME, GenAiIncubatingAttributes @@ -2436,7 +2446,7 @@ public void accept(PayloadPart chunk) { point .hasSumGreaterThan(0.0) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_OPERATION_NAME, GenAiIncubatingAttributes @@ -2448,13 +2458,14 @@ public void accept(PayloadPart chunk) { .waitAndAssertLogRecords( log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo(EVENT_NAME, "gen_ai.user.message")) .hasSpanContext(spanCtx0) .hasBody(Value.of(Collections.emptyMap())), log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), equalTo(EVENT_NAME, "gen_ai.choice")) + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), + equalTo(EVENT_NAME, "gen_ai.choice")) .hasSpanContext(spanCtx0) .hasBody( Value.of( @@ -2572,7 +2583,7 @@ public void accept(PayloadPart chunk) { span.hasName("chat anthropic.claude-3-5-sonnet-20240620-v1:0") .hasKind(SpanKind.CLIENT) .hasAttributesSatisfying( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_OPERATION_NAME, GenAiIncubatingAttributes.GenAiOperationNameIncubatingValues @@ -2598,7 +2609,7 @@ public void accept(PayloadPart chunk) { .hasSum(601) .hasCount(1) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_TOKEN_TYPE, GenAiIncubatingAttributes @@ -2613,11 +2624,11 @@ public void accept(PayloadPart chunk) { .hasSum(145) .hasCount(1) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_TOKEN_TYPE, GenAiIncubatingAttributes - .GenAiTokenTypeIncubatingValues.COMPLETION), + .GenAiTokenTypeIncubatingValues.OUTPUT), equalTo( GEN_AI_OPERATION_NAME, GenAiIncubatingAttributes @@ -2635,7 +2646,7 @@ public void accept(PayloadPart chunk) { point .hasSumGreaterThan(0.0) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_OPERATION_NAME, GenAiIncubatingAttributes @@ -2648,13 +2659,13 @@ public void accept(PayloadPart chunk) { .waitAndAssertLogRecords( log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo(EVENT_NAME, "gen_ai.user.message")) .hasSpanContext(spanCtx1) .hasBody(Value.of(Collections.emptyMap())), log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo(EVENT_NAME, "gen_ai.assistant.message")) .hasSpanContext(spanCtx1) .hasBody( @@ -2672,19 +2683,20 @@ public void accept(PayloadPart chunk) { KeyValue.of("type", Value.of("function"))))))), log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo(EVENT_NAME, "gen_ai.tool.message")) .hasSpanContext(spanCtx1) .hasBody(Value.of(KeyValue.of("id", Value.of(seattleToolUseId)))), log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo(EVENT_NAME, "gen_ai.tool.message")) .hasSpanContext(spanCtx1) .hasBody(Value.of(KeyValue.of("id", Value.of(sanFranciscoToolUseId)))), log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), equalTo(EVENT_NAME, "gen_ai.choice")) + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), + equalTo(EVENT_NAME, "gen_ai.choice")) .hasSpanContext(spanCtx1) .hasBody( Value.of( diff --git a/instrumentation/aws-sdk/aws-sdk-2.2/metadata.yaml b/instrumentation/aws-sdk/aws-sdk-2.2/metadata.yaml index 81605dccdafc..352990cd9d53 100644 --- a/instrumentation/aws-sdk/aws-sdk-2.2/metadata.yaml +++ b/instrumentation/aws-sdk/aws-sdk-2.2/metadata.yaml @@ -2,6 +2,7 @@ description: > This instrumentation covers the AWS SDK 2.2+ client library, enabling messaging and client spans and metrics for calls to AWS services including DynamoDB, EC2, Kinesis, Lambda, RDS, S3, SNS/SQS and Bedrock. +library_link: https://aws.amazon.com/sdk-for-java/ configurations: - name: otel.instrumentation.messaging.experimental.receive-telemetry.enabled description: > diff --git a/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2BedrockRuntimeTest.java b/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2BedrockRuntimeTest.java index 3f5d12d057bb..285d1be48fba 100644 --- a/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2BedrockRuntimeTest.java +++ b/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2BedrockRuntimeTest.java @@ -8,17 +8,17 @@ import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies; import static io.opentelemetry.semconv.incubating.GenAiIncubatingAttributes.GEN_AI_OPERATION_NAME; +import static io.opentelemetry.semconv.incubating.GenAiIncubatingAttributes.GEN_AI_PROVIDER_NAME; import static io.opentelemetry.semconv.incubating.GenAiIncubatingAttributes.GEN_AI_REQUEST_MAX_TOKENS; import static io.opentelemetry.semconv.incubating.GenAiIncubatingAttributes.GEN_AI_REQUEST_MODEL; import static io.opentelemetry.semconv.incubating.GenAiIncubatingAttributes.GEN_AI_REQUEST_STOP_SEQUENCES; import static io.opentelemetry.semconv.incubating.GenAiIncubatingAttributes.GEN_AI_REQUEST_TEMPERATURE; import static io.opentelemetry.semconv.incubating.GenAiIncubatingAttributes.GEN_AI_REQUEST_TOP_P; import static io.opentelemetry.semconv.incubating.GenAiIncubatingAttributes.GEN_AI_RESPONSE_FINISH_REASONS; -import static io.opentelemetry.semconv.incubating.GenAiIncubatingAttributes.GEN_AI_SYSTEM; import static io.opentelemetry.semconv.incubating.GenAiIncubatingAttributes.GEN_AI_TOKEN_TYPE; import static io.opentelemetry.semconv.incubating.GenAiIncubatingAttributes.GEN_AI_USAGE_INPUT_TOKENS; import static io.opentelemetry.semconv.incubating.GenAiIncubatingAttributes.GEN_AI_USAGE_OUTPUT_TOKENS; -import static io.opentelemetry.semconv.incubating.GenAiIncubatingAttributes.GenAiSystemIncubatingValues.AWS_BEDROCK; +import static io.opentelemetry.semconv.incubating.GenAiIncubatingAttributes.GenAiProviderNameIncubatingValues.AWS_BEDROCK; import static java.util.Arrays.asList; import static java.util.Collections.singletonList; import static org.assertj.core.api.Assertions.assertThat; @@ -137,7 +137,7 @@ void testConverseBasic() { span.hasName("chat amazon.titan-text-lite-v1") .hasKind(SpanKind.CLIENT) .hasAttributesSatisfying( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_OPERATION_NAME, GenAiIncubatingAttributes.GenAiOperationNameIncubatingValues @@ -163,7 +163,7 @@ void testConverseBasic() { .hasSum(8) .hasCount(1) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_TOKEN_TYPE, GenAiIncubatingAttributes @@ -178,11 +178,11 @@ void testConverseBasic() { .hasSum(14) .hasCount(1) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_TOKEN_TYPE, GenAiIncubatingAttributes - .GenAiTokenTypeIncubatingValues.COMPLETION), + .GenAiTokenTypeIncubatingValues.OUTPUT), equalTo( GEN_AI_OPERATION_NAME, GenAiIncubatingAttributes @@ -200,7 +200,7 @@ void testConverseBasic() { point .hasSumGreaterThan(0.0) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_OPERATION_NAME, GenAiIncubatingAttributes @@ -213,13 +213,14 @@ void testConverseBasic() { .waitAndAssertLogRecords( log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo(EVENT_NAME, "gen_ai.user.message")) .hasSpanContext(spanCtx) .hasBody(Value.of(KeyValue.of("content", Value.of("Say this is a test")))), log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), equalTo(EVENT_NAME, "gen_ai.choice")) + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), + equalTo(EVENT_NAME, "gen_ai.choice")) .hasSpanContext(spanCtx) .hasBody( Value.of( @@ -265,7 +266,7 @@ void testConverseOptions() { span.hasName("chat amazon.titan-text-lite-v1") .hasKind(SpanKind.CLIENT) .hasAttributesSatisfying( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_OPERATION_NAME, GenAiIncubatingAttributes.GenAiOperationNameIncubatingValues @@ -297,7 +298,7 @@ void testConverseOptions() { .hasSum(8) .hasCount(1) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_TOKEN_TYPE, GenAiIncubatingAttributes @@ -312,11 +313,11 @@ void testConverseOptions() { .hasSum(10) .hasCount(1) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_TOKEN_TYPE, GenAiIncubatingAttributes - .GenAiTokenTypeIncubatingValues.COMPLETION), + .GenAiTokenTypeIncubatingValues.OUTPUT), equalTo( GEN_AI_OPERATION_NAME, GenAiIncubatingAttributes @@ -334,7 +335,7 @@ void testConverseOptions() { point .hasSumGreaterThan(0.0) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_OPERATION_NAME, GenAiIncubatingAttributes @@ -347,13 +348,14 @@ void testConverseOptions() { .waitAndAssertLogRecords( log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo(EVENT_NAME, "gen_ai.user.message")) .hasSpanContext(spanCtx) .hasBody(Value.of(KeyValue.of("content", Value.of("Say this is a test")))), log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), equalTo(EVENT_NAME, "gen_ai.choice")) + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), + equalTo(EVENT_NAME, "gen_ai.choice")) .hasSpanContext(spanCtx) .hasBody( Value.of( @@ -414,7 +416,7 @@ void testConverseToolCall() { span.hasName("chat amazon.nova-micro-v1:0") .hasKind(SpanKind.CLIENT) .hasAttributesSatisfying( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_OPERATION_NAME, GenAiIncubatingAttributes.GenAiOperationNameIncubatingValues @@ -440,7 +442,7 @@ void testConverseToolCall() { .hasSum(415) .hasCount(1) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_TOKEN_TYPE, GenAiIncubatingAttributes @@ -455,11 +457,11 @@ void testConverseToolCall() { .hasSum(162) .hasCount(1) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_TOKEN_TYPE, GenAiIncubatingAttributes - .GenAiTokenTypeIncubatingValues.COMPLETION), + .GenAiTokenTypeIncubatingValues.OUTPUT), equalTo( GEN_AI_OPERATION_NAME, GenAiIncubatingAttributes @@ -477,7 +479,7 @@ void testConverseToolCall() { point .hasSumGreaterThan(0.0) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_OPERATION_NAME, GenAiIncubatingAttributes @@ -490,7 +492,7 @@ void testConverseToolCall() { .waitAndAssertLogRecords( log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo(EVENT_NAME, "gen_ai.user.message")) .hasSpanContext(spanCtx0) .hasBody( @@ -501,7 +503,8 @@ void testConverseToolCall() { "What is the weather in Seattle and San Francisco today?")))), log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), equalTo(EVENT_NAME, "gen_ai.choice")) + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), + equalTo(EVENT_NAME, "gen_ai.choice")) .hasSpanContext(spanCtx0) .hasBody( Value.of( @@ -581,7 +584,7 @@ void testConverseToolCall() { span.hasName("chat amazon.nova-micro-v1:0") .hasKind(SpanKind.CLIENT) .hasAttributesSatisfying( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_OPERATION_NAME, GenAiIncubatingAttributes.GenAiOperationNameIncubatingValues @@ -607,7 +610,7 @@ void testConverseToolCall() { .hasSum(554) .hasCount(1) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_TOKEN_TYPE, GenAiIncubatingAttributes @@ -622,11 +625,11 @@ void testConverseToolCall() { .hasSum(57) .hasCount(1) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_TOKEN_TYPE, GenAiIncubatingAttributes - .GenAiTokenTypeIncubatingValues.COMPLETION), + .GenAiTokenTypeIncubatingValues.OUTPUT), equalTo( GEN_AI_OPERATION_NAME, GenAiIncubatingAttributes @@ -644,7 +647,7 @@ void testConverseToolCall() { point .hasSumGreaterThan(0.0) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_OPERATION_NAME, GenAiIncubatingAttributes @@ -657,7 +660,7 @@ void testConverseToolCall() { .waitAndAssertLogRecords( log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo(EVENT_NAME, "gen_ai.user.message")) .hasSpanContext(spanCtx1) .hasBody( @@ -668,7 +671,7 @@ void testConverseToolCall() { "What is the weather in Seattle and San Francisco today?")))), log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo(EVENT_NAME, "gen_ai.assistant.message")) .hasSpanContext(spanCtx1) .hasBody( @@ -695,7 +698,7 @@ void testConverseToolCall() { " The User has asked for the current weather in two locations: Seattle and San Francisco. To provide the requested information, I will use the \"get_current_weather\" tool for each location separately. \n")))), log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo(EVENT_NAME, "gen_ai.tool.message")) .hasSpanContext(spanCtx1) .hasBody( @@ -705,7 +708,7 @@ void testConverseToolCall() { "content", Value.of("{\"weather\":\"50 degrees and raining\"}")))), log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo(EVENT_NAME, "gen_ai.tool.message")) .hasSpanContext(spanCtx1) .hasBody( @@ -715,7 +718,8 @@ void testConverseToolCall() { "content", Value.of("{\"weather\":\"70 degrees and sunny\"}")))), log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), equalTo(EVENT_NAME, "gen_ai.choice")) + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), + equalTo(EVENT_NAME, "gen_ai.choice")) .hasSpanContext(spanCtx1) .hasBody( Value.of( @@ -829,7 +833,7 @@ void testConverseToolCallStream() throws InterruptedException, ExecutionExceptio span.hasName("chat amazon.nova-micro-v1:0") .hasKind(SpanKind.CLIENT) .hasAttributesSatisfying( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_OPERATION_NAME, GenAiIncubatingAttributes.GenAiOperationNameIncubatingValues @@ -855,7 +859,7 @@ void testConverseToolCallStream() throws InterruptedException, ExecutionExceptio .hasSum(415) .hasCount(1) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_TOKEN_TYPE, GenAiIncubatingAttributes @@ -870,11 +874,11 @@ void testConverseToolCallStream() throws InterruptedException, ExecutionExceptio .hasSum(162) .hasCount(1) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_TOKEN_TYPE, GenAiIncubatingAttributes - .GenAiTokenTypeIncubatingValues.COMPLETION), + .GenAiTokenTypeIncubatingValues.OUTPUT), equalTo( GEN_AI_OPERATION_NAME, GenAiIncubatingAttributes @@ -892,7 +896,7 @@ void testConverseToolCallStream() throws InterruptedException, ExecutionExceptio point .hasSumGreaterThan(0.0) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_OPERATION_NAME, GenAiIncubatingAttributes @@ -905,7 +909,7 @@ void testConverseToolCallStream() throws InterruptedException, ExecutionExceptio .waitAndAssertLogRecords( log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo(EVENT_NAME, "gen_ai.user.message")) .hasSpanContext(spanCtx0) .hasBody( @@ -916,7 +920,8 @@ void testConverseToolCallStream() throws InterruptedException, ExecutionExceptio "What is the weather in Seattle and San Francisco today?")))), log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), equalTo(EVENT_NAME, "gen_ai.choice")) + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), + equalTo(EVENT_NAME, "gen_ai.choice")) .hasSpanContext(spanCtx0) .hasBody( Value.of( @@ -1015,7 +1020,7 @@ void testConverseToolCallStream() throws InterruptedException, ExecutionExceptio span.hasName("chat amazon.nova-micro-v1:0") .hasKind(SpanKind.CLIENT) .hasAttributesSatisfying( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_OPERATION_NAME, GenAiIncubatingAttributes.GenAiOperationNameIncubatingValues @@ -1041,7 +1046,7 @@ void testConverseToolCallStream() throws InterruptedException, ExecutionExceptio .hasSum(554) .hasCount(1) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_TOKEN_TYPE, GenAiIncubatingAttributes @@ -1056,11 +1061,11 @@ void testConverseToolCallStream() throws InterruptedException, ExecutionExceptio .hasSum(59) .hasCount(1) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_TOKEN_TYPE, GenAiIncubatingAttributes - .GenAiTokenTypeIncubatingValues.COMPLETION), + .GenAiTokenTypeIncubatingValues.OUTPUT), equalTo( GEN_AI_OPERATION_NAME, GenAiIncubatingAttributes @@ -1078,7 +1083,7 @@ void testConverseToolCallStream() throws InterruptedException, ExecutionExceptio point .hasSumGreaterThan(0.0) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_OPERATION_NAME, GenAiIncubatingAttributes @@ -1091,7 +1096,7 @@ void testConverseToolCallStream() throws InterruptedException, ExecutionExceptio .waitAndAssertLogRecords( log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo(EVENT_NAME, "gen_ai.user.message")) .hasSpanContext(spanCtx1) .hasBody( @@ -1102,7 +1107,7 @@ void testConverseToolCallStream() throws InterruptedException, ExecutionExceptio "What is the weather in Seattle and San Francisco today?")))), log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo(EVENT_NAME, "gen_ai.assistant.message")) .hasSpanContext(spanCtx1) .hasBody( @@ -1129,7 +1134,7 @@ void testConverseToolCallStream() throws InterruptedException, ExecutionExceptio " The User has asked for the current weather in two locations: Seattle and San Francisco. To provide the requested information, I will use the \"get_current_weather\" tool for each location separately. \n")))), log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo(EVENT_NAME, "gen_ai.tool.message")) .hasSpanContext(spanCtx1) .hasBody( @@ -1139,7 +1144,7 @@ void testConverseToolCallStream() throws InterruptedException, ExecutionExceptio "content", Value.of("{\"weather\":\"50 degrees and raining\"}")))), log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo(EVENT_NAME, "gen_ai.tool.message")) .hasSpanContext(spanCtx1) .hasBody( @@ -1149,7 +1154,8 @@ void testConverseToolCallStream() throws InterruptedException, ExecutionExceptio "content", Value.of("{\"weather\":\"70 degrees and sunny\"}")))), log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), equalTo(EVENT_NAME, "gen_ai.choice")) + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), + equalTo(EVENT_NAME, "gen_ai.choice")) .hasSpanContext(spanCtx1) .hasBody( Value.of( @@ -1242,10 +1248,7 @@ void testConverseStream() throws InterruptedException, ExecutionException { span.hasName("chat amazon.titan-text-lite-v1") .hasKind(SpanKind.CLIENT) .hasAttributesSatisfying( - equalTo( - GEN_AI_SYSTEM, - GenAiIncubatingAttributes.GenAiSystemIncubatingValues - .AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_OPERATION_NAME, GenAiIncubatingAttributes.GenAiOperationNameIncubatingValues @@ -1271,7 +1274,7 @@ void testConverseStream() throws InterruptedException, ExecutionException { .hasSum(8) .hasCount(1) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_TOKEN_TYPE, GenAiIncubatingAttributes @@ -1286,11 +1289,11 @@ void testConverseStream() throws InterruptedException, ExecutionException { .hasSum(10) .hasCount(1) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_TOKEN_TYPE, GenAiIncubatingAttributes - .GenAiTokenTypeIncubatingValues.COMPLETION), + .GenAiTokenTypeIncubatingValues.OUTPUT), equalTo( GEN_AI_OPERATION_NAME, GenAiIncubatingAttributes @@ -1308,7 +1311,7 @@ void testConverseStream() throws InterruptedException, ExecutionException { point .hasSumGreaterThan(0.0) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_OPERATION_NAME, GenAiIncubatingAttributes @@ -1321,13 +1324,14 @@ void testConverseStream() throws InterruptedException, ExecutionException { .waitAndAssertLogRecords( log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo(EVENT_NAME, "gen_ai.user.message")) .hasSpanContext(spanCtx) .hasBody(Value.of(KeyValue.of("content", Value.of("Say this is a test")))), log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), equalTo(EVENT_NAME, "gen_ai.choice")) + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), + equalTo(EVENT_NAME, "gen_ai.choice")) .hasSpanContext(spanCtx) .hasBody( Value.of( @@ -1388,10 +1392,7 @@ void testConverseStreamOptions() throws InterruptedException, ExecutionException span.hasName("chat amazon.titan-text-lite-v1") .hasKind(SpanKind.CLIENT) .hasAttributesSatisfying( - equalTo( - GEN_AI_SYSTEM, - GenAiIncubatingAttributes.GenAiSystemIncubatingValues - .AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_OPERATION_NAME, GenAiIncubatingAttributes.GenAiOperationNameIncubatingValues @@ -1413,13 +1414,14 @@ void testConverseStreamOptions() throws InterruptedException, ExecutionException .waitAndAssertLogRecords( log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo(EVENT_NAME, "gen_ai.user.message")) .hasSpanContext(spanCtx) .hasBody(Value.of(KeyValue.of("content", Value.of("Say this is a test")))), log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), equalTo(EVENT_NAME, "gen_ai.choice")) + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), + equalTo(EVENT_NAME, "gen_ai.choice")) .hasSpanContext(spanCtx) .hasBody( Value.of( @@ -1476,7 +1478,7 @@ void testInvokeModelAmazonTitan() { span.hasName("text_completion amazon.titan-text-lite-v1") .hasKind(SpanKind.CLIENT) .hasAttributesSatisfying( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_OPERATION_NAME, GenAiIncubatingAttributes.GenAiOperationNameIncubatingValues @@ -1508,7 +1510,7 @@ void testInvokeModelAmazonTitan() { .hasSum(5) .hasCount(1) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_TOKEN_TYPE, GenAiIncubatingAttributes @@ -1524,11 +1526,11 @@ void testInvokeModelAmazonTitan() { .hasSum(10) .hasCount(1) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_TOKEN_TYPE, GenAiIncubatingAttributes - .GenAiTokenTypeIncubatingValues.COMPLETION), + .GenAiTokenTypeIncubatingValues.OUTPUT), equalTo( GEN_AI_OPERATION_NAME, GenAiIncubatingAttributes @@ -1547,7 +1549,7 @@ void testInvokeModelAmazonTitan() { point .hasSumGreaterThan(0.0) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_OPERATION_NAME, GenAiIncubatingAttributes @@ -1561,13 +1563,14 @@ void testInvokeModelAmazonTitan() { .waitAndAssertLogRecords( log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo(EVENT_NAME, "gen_ai.user.message")) .hasSpanContext(spanCtx) .hasBody(Value.of(KeyValue.of("content", Value.of("Say this is a test")))), log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), equalTo(EVENT_NAME, "gen_ai.choice")) + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), + equalTo(EVENT_NAME, "gen_ai.choice")) .hasSpanContext(spanCtx) .hasBody( Value.of( @@ -1640,7 +1643,7 @@ void testInvokeModelWithResponseStreamAmazonTitan() span.hasName("text_completion amazon.titan-text-lite-v1") .hasKind(SpanKind.CLIENT) .hasAttributesSatisfying( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_OPERATION_NAME, GenAiIncubatingAttributes.GenAiOperationNameIncubatingValues @@ -1672,7 +1675,7 @@ void testInvokeModelWithResponseStreamAmazonTitan() .hasSum(7) .hasCount(1) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_TOKEN_TYPE, GenAiIncubatingAttributes @@ -1688,11 +1691,11 @@ void testInvokeModelWithResponseStreamAmazonTitan() .hasSum(100) .hasCount(1) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_TOKEN_TYPE, GenAiIncubatingAttributes - .GenAiTokenTypeIncubatingValues.COMPLETION), + .GenAiTokenTypeIncubatingValues.OUTPUT), equalTo( GEN_AI_OPERATION_NAME, GenAiIncubatingAttributes @@ -1711,7 +1714,7 @@ void testInvokeModelWithResponseStreamAmazonTitan() point .hasSumGreaterThan(0.0) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_OPERATION_NAME, GenAiIncubatingAttributes @@ -1725,7 +1728,7 @@ void testInvokeModelWithResponseStreamAmazonTitan() .waitAndAssertLogRecords( log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo(EVENT_NAME, "gen_ai.user.message")) .hasSpanContext(spanCtx) .hasBody( @@ -1734,7 +1737,8 @@ void testInvokeModelWithResponseStreamAmazonTitan() "content", Value.of("List out every country in the world")))), log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), equalTo(EVENT_NAME, "gen_ai.choice")) + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), + equalTo(EVENT_NAME, "gen_ai.choice")) .hasSpanContext(spanCtx) .hasBody( Value.of( @@ -1813,7 +1817,7 @@ void testInvokeModelAmazonNova() { span.hasName("chat amazon.nova-micro-v1:0") .hasKind(SpanKind.CLIENT) .hasAttributesSatisfying( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_OPERATION_NAME, GenAiIncubatingAttributes.GenAiOperationNameIncubatingValues @@ -1845,7 +1849,7 @@ void testInvokeModelAmazonNova() { .hasSum(5) .hasCount(1) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_TOKEN_TYPE, GenAiIncubatingAttributes @@ -1860,11 +1864,11 @@ void testInvokeModelAmazonNova() { .hasSum(10) .hasCount(1) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_TOKEN_TYPE, GenAiIncubatingAttributes - .GenAiTokenTypeIncubatingValues.COMPLETION), + .GenAiTokenTypeIncubatingValues.OUTPUT), equalTo( GEN_AI_OPERATION_NAME, GenAiIncubatingAttributes @@ -1882,7 +1886,7 @@ void testInvokeModelAmazonNova() { point .hasSumGreaterThan(0.0) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_OPERATION_NAME, GenAiIncubatingAttributes @@ -1895,13 +1899,14 @@ void testInvokeModelAmazonNova() { .waitAndAssertLogRecords( log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo(EVENT_NAME, "gen_ai.user.message")) .hasSpanContext(spanCtx) .hasBody(Value.of(KeyValue.of("content", Value.of("Say this is a test")))), log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), equalTo(EVENT_NAME, "gen_ai.choice")) + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), + equalTo(EVENT_NAME, "gen_ai.choice")) .hasSpanContext(spanCtx) .hasBody( Value.of( @@ -1994,7 +1999,7 @@ void testInvokeModelWithResponseStreamAmazonNova() span.hasName("chat amazon.nova-micro-v1:0") .hasKind(SpanKind.CLIENT) .hasAttributesSatisfying( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_OPERATION_NAME, GenAiIncubatingAttributes.GenAiOperationNameIncubatingValues @@ -2026,7 +2031,7 @@ void testInvokeModelWithResponseStreamAmazonNova() .hasSum(7) .hasCount(1) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_TOKEN_TYPE, GenAiIncubatingAttributes @@ -2041,11 +2046,11 @@ void testInvokeModelWithResponseStreamAmazonNova() .hasSum(100) .hasCount(1) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_TOKEN_TYPE, GenAiIncubatingAttributes - .GenAiTokenTypeIncubatingValues.COMPLETION), + .GenAiTokenTypeIncubatingValues.OUTPUT), equalTo( GEN_AI_OPERATION_NAME, GenAiIncubatingAttributes @@ -2063,7 +2068,7 @@ void testInvokeModelWithResponseStreamAmazonNova() point .hasSumGreaterThan(0.0) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_OPERATION_NAME, GenAiIncubatingAttributes @@ -2076,7 +2081,7 @@ void testInvokeModelWithResponseStreamAmazonNova() .waitAndAssertLogRecords( log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo(EVENT_NAME, "gen_ai.user.message")) .hasSpanContext(spanCtx) .hasBody( @@ -2085,7 +2090,8 @@ void testInvokeModelWithResponseStreamAmazonNova() "content", Value.of("List out every country in the world")))), log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), equalTo(EVENT_NAME, "gen_ai.choice")) + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), + equalTo(EVENT_NAME, "gen_ai.choice")) .hasSpanContext(spanCtx) .hasBody( Value.of( @@ -2151,7 +2157,7 @@ void testInvokeModelAnthropicClaude() { span.hasName("chat anthropic.claude-v2") .hasKind(SpanKind.CLIENT) .hasAttributesSatisfying( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_OPERATION_NAME, GenAiIncubatingAttributes.GenAiOperationNameIncubatingValues @@ -2183,7 +2189,7 @@ void testInvokeModelAnthropicClaude() { .hasSum(14) .hasCount(1) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_TOKEN_TYPE, GenAiIncubatingAttributes @@ -2198,11 +2204,11 @@ void testInvokeModelAnthropicClaude() { .hasSum(10) .hasCount(1) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_TOKEN_TYPE, GenAiIncubatingAttributes - .GenAiTokenTypeIncubatingValues.COMPLETION), + .GenAiTokenTypeIncubatingValues.OUTPUT), equalTo( GEN_AI_OPERATION_NAME, GenAiIncubatingAttributes @@ -2220,7 +2226,7 @@ void testInvokeModelAnthropicClaude() { point .hasSumGreaterThan(0.0) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_OPERATION_NAME, GenAiIncubatingAttributes @@ -2233,13 +2239,14 @@ void testInvokeModelAnthropicClaude() { .waitAndAssertLogRecords( log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo(EVENT_NAME, "gen_ai.user.message")) .hasSpanContext(spanCtx) .hasBody(Value.of(KeyValue.of("content", Value.of("Say this is a test")))), log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), equalTo(EVENT_NAME, "gen_ai.choice")) + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), + equalTo(EVENT_NAME, "gen_ai.choice")) .hasSpanContext(spanCtx) .hasBody( Value.of( @@ -2291,7 +2298,7 @@ void testInvokeModelMetaLlama() { span.hasName("chat meta.llama3-3-70b-instruct-v1:0") .hasKind(SpanKind.CLIENT) .hasAttributesSatisfying( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_OPERATION_NAME, GenAiIncubatingAttributes.GenAiOperationNameIncubatingValues @@ -2350,7 +2357,7 @@ void testInvokeModelCohereCommandR() { span.hasName("chat cohere.command-r-v1:0") .hasKind(SpanKind.CLIENT) .hasAttributesSatisfying( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_OPERATION_NAME, GenAiIncubatingAttributes.GenAiOperationNameIncubatingValues @@ -2418,7 +2425,7 @@ void testInvokeModelCohereCommand() { span.hasName("chat cohere.command-light-text-v14") .hasKind(SpanKind.CLIENT) .hasAttributesSatisfying( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_OPERATION_NAME, GenAiIncubatingAttributes.GenAiOperationNameIncubatingValues @@ -2479,7 +2486,7 @@ void testInvokeModelMistralMistral() { span.hasName("chat mistral.mistral-7b-instruct-v0:2") .hasKind(SpanKind.CLIENT) .hasAttributesSatisfying( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_OPERATION_NAME, GenAiIncubatingAttributes.GenAiOperationNameIncubatingValues @@ -2571,7 +2578,7 @@ void testInvokeModelWithResponseStreamAnthropicClaude() span.hasName("chat anthropic.claude-v2") .hasKind(SpanKind.CLIENT) .hasAttributesSatisfying( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_OPERATION_NAME, GenAiIncubatingAttributes.GenAiOperationNameIncubatingValues @@ -2603,7 +2610,7 @@ void testInvokeModelWithResponseStreamAnthropicClaude() .hasSum(16) .hasCount(1) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_TOKEN_TYPE, GenAiIncubatingAttributes @@ -2618,11 +2625,11 @@ void testInvokeModelWithResponseStreamAnthropicClaude() .hasSum(10) .hasCount(1) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_TOKEN_TYPE, GenAiIncubatingAttributes - .GenAiTokenTypeIncubatingValues.COMPLETION), + .GenAiTokenTypeIncubatingValues.OUTPUT), equalTo( GEN_AI_OPERATION_NAME, GenAiIncubatingAttributes @@ -2640,7 +2647,7 @@ void testInvokeModelWithResponseStreamAnthropicClaude() point .hasSumGreaterThan(0.0) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_OPERATION_NAME, GenAiIncubatingAttributes @@ -2653,7 +2660,7 @@ void testInvokeModelWithResponseStreamAnthropicClaude() .waitAndAssertLogRecords( log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo(EVENT_NAME, "gen_ai.user.message")) .hasSpanContext(spanCtx) .hasBody( @@ -2662,7 +2669,8 @@ void testInvokeModelWithResponseStreamAnthropicClaude() "content", Value.of("List out every country in the world")))), log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), equalTo(EVENT_NAME, "gen_ai.choice")) + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), + equalTo(EVENT_NAME, "gen_ai.choice")) .hasSpanContext(spanCtx) .hasBody( Value.of( @@ -2786,7 +2794,7 @@ void testInvokeModelToolCallAmazonNova() { span.hasName("chat amazon.nova-micro-v1:0") .hasKind(SpanKind.CLIENT) .hasAttributesSatisfying( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_OPERATION_NAME, GenAiIncubatingAttributes.GenAiOperationNameIncubatingValues @@ -2812,7 +2820,7 @@ void testInvokeModelToolCallAmazonNova() { .hasSum(416) .hasCount(1) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_TOKEN_TYPE, GenAiIncubatingAttributes @@ -2827,11 +2835,11 @@ void testInvokeModelToolCallAmazonNova() { .hasSum(166) .hasCount(1) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_TOKEN_TYPE, GenAiIncubatingAttributes - .GenAiTokenTypeIncubatingValues.COMPLETION), + .GenAiTokenTypeIncubatingValues.OUTPUT), equalTo( GEN_AI_OPERATION_NAME, GenAiIncubatingAttributes @@ -2849,7 +2857,7 @@ void testInvokeModelToolCallAmazonNova() { point .hasSumGreaterThan(0.0) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_OPERATION_NAME, GenAiIncubatingAttributes @@ -2861,7 +2869,7 @@ void testInvokeModelToolCallAmazonNova() { .waitAndAssertLogRecords( log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo(EVENT_NAME, "gen_ai.user.message")) .hasSpanContext(spanCtx0) .hasBody( @@ -2872,7 +2880,8 @@ void testInvokeModelToolCallAmazonNova() { "What is the weather in Seattle and San Francisco today?")))), log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), equalTo(EVENT_NAME, "gen_ai.choice")) + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), + equalTo(EVENT_NAME, "gen_ai.choice")) .hasSpanContext(spanCtx0) .hasBody( Value.of( @@ -2981,7 +2990,7 @@ void testInvokeModelToolCallAmazonNova() { span.hasName("chat amazon.nova-micro-v1:0") .hasKind(SpanKind.CLIENT) .hasAttributesSatisfying( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_OPERATION_NAME, GenAiIncubatingAttributes.GenAiOperationNameIncubatingValues @@ -3007,7 +3016,7 @@ void testInvokeModelToolCallAmazonNova() { .hasSum(559) .hasCount(1) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_TOKEN_TYPE, GenAiIncubatingAttributes @@ -3022,11 +3031,11 @@ void testInvokeModelToolCallAmazonNova() { .hasSum(59) .hasCount(1) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_TOKEN_TYPE, GenAiIncubatingAttributes - .GenAiTokenTypeIncubatingValues.COMPLETION), + .GenAiTokenTypeIncubatingValues.OUTPUT), equalTo( GEN_AI_OPERATION_NAME, GenAiIncubatingAttributes @@ -3044,7 +3053,7 @@ void testInvokeModelToolCallAmazonNova() { point .hasSumGreaterThan(0.0) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_OPERATION_NAME, GenAiIncubatingAttributes @@ -3057,7 +3066,7 @@ void testInvokeModelToolCallAmazonNova() { .waitAndAssertLogRecords( log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo(EVENT_NAME, "gen_ai.user.message")) .hasSpanContext(spanCtx1) .hasBody( @@ -3068,7 +3077,7 @@ void testInvokeModelToolCallAmazonNova() { "What is the weather in Seattle and San Francisco today?")))), log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo(EVENT_NAME, "gen_ai.assistant.message")) .hasSpanContext(spanCtx1) .hasBody( @@ -3095,7 +3104,7 @@ void testInvokeModelToolCallAmazonNova() { " To provide the current weather in both Seattle and San Francisco, I will use the \"get_current_weather\" tool twice, once for each city. I will need to specify the \"location\" argument for each call.\n")))), log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo(EVENT_NAME, "gen_ai.tool.message")) .hasSpanContext(spanCtx1) .hasBody( @@ -3105,7 +3114,7 @@ void testInvokeModelToolCallAmazonNova() { "content", Value.of("{\"weather\":\"50 degrees and raining\"}")))), log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo(EVENT_NAME, "gen_ai.tool.message")) .hasSpanContext(spanCtx1) .hasBody( @@ -3115,7 +3124,8 @@ void testInvokeModelToolCallAmazonNova() { "content", Value.of("{\"weather\":\"70 degrees and sunny\"}")))), log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), equalTo(EVENT_NAME, "gen_ai.choice")) + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), + equalTo(EVENT_NAME, "gen_ai.choice")) .hasSpanContext(spanCtx1) .hasBody( Value.of( @@ -3297,7 +3307,7 @@ public void accept(PayloadPart chunk) { span.hasName("chat amazon.nova-micro-v1:0") .hasKind(SpanKind.CLIENT) .hasAttributesSatisfying( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_OPERATION_NAME, GenAiIncubatingAttributes.GenAiOperationNameIncubatingValues @@ -3323,7 +3333,7 @@ public void accept(PayloadPart chunk) { .hasSum(416) .hasCount(1) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_TOKEN_TYPE, GenAiIncubatingAttributes @@ -3338,11 +3348,11 @@ public void accept(PayloadPart chunk) { .hasSum(165) .hasCount(1) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_TOKEN_TYPE, GenAiIncubatingAttributes - .GenAiTokenTypeIncubatingValues.COMPLETION), + .GenAiTokenTypeIncubatingValues.OUTPUT), equalTo( GEN_AI_OPERATION_NAME, GenAiIncubatingAttributes @@ -3360,7 +3370,7 @@ public void accept(PayloadPart chunk) { point .hasSumGreaterThan(0.0) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_OPERATION_NAME, GenAiIncubatingAttributes @@ -3372,7 +3382,7 @@ public void accept(PayloadPart chunk) { .waitAndAssertLogRecords( log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo(EVENT_NAME, "gen_ai.user.message")) .hasSpanContext(spanCtx0) .hasBody( @@ -3383,7 +3393,8 @@ public void accept(PayloadPart chunk) { "What is the weather in Seattle and San Francisco today?")))), log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), equalTo(EVENT_NAME, "gen_ai.choice")) + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), + equalTo(EVENT_NAME, "gen_ai.choice")) .hasSpanContext(spanCtx0) .hasBody( Value.of( @@ -3535,7 +3546,7 @@ public void accept(PayloadPart chunk) { span.hasName("chat amazon.nova-micro-v1:0") .hasKind(SpanKind.CLIENT) .hasAttributesSatisfying( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_OPERATION_NAME, GenAiIncubatingAttributes.GenAiOperationNameIncubatingValues @@ -3561,7 +3572,7 @@ public void accept(PayloadPart chunk) { .hasSum(558) .hasCount(1) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_TOKEN_TYPE, GenAiIncubatingAttributes @@ -3576,11 +3587,11 @@ public void accept(PayloadPart chunk) { .hasSum(58) .hasCount(1) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_TOKEN_TYPE, GenAiIncubatingAttributes - .GenAiTokenTypeIncubatingValues.COMPLETION), + .GenAiTokenTypeIncubatingValues.OUTPUT), equalTo( GEN_AI_OPERATION_NAME, GenAiIncubatingAttributes @@ -3598,7 +3609,7 @@ public void accept(PayloadPart chunk) { point .hasSumGreaterThan(0.0) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_OPERATION_NAME, GenAiIncubatingAttributes @@ -3611,7 +3622,7 @@ public void accept(PayloadPart chunk) { .waitAndAssertLogRecords( log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo(EVENT_NAME, "gen_ai.user.message")) .hasSpanContext(spanCtx1) .hasBody( @@ -3622,7 +3633,7 @@ public void accept(PayloadPart chunk) { "What is the weather in Seattle and San Francisco today?")))), log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo(EVENT_NAME, "gen_ai.assistant.message")) .hasSpanContext(spanCtx1) .hasBody( @@ -3649,7 +3660,7 @@ public void accept(PayloadPart chunk) { " To provide the current weather in Seattle and San Francisco today, I will need to use the \"get_current_weather\" tool twice, once for each city. I will need to specify the location for each call.\n")))), log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo(EVENT_NAME, "gen_ai.tool.message")) .hasSpanContext(spanCtx1) .hasBody( @@ -3659,7 +3670,7 @@ public void accept(PayloadPart chunk) { "content", Value.of("{\"weather\":\"50 degrees and raining\"}")))), log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo(EVENT_NAME, "gen_ai.tool.message")) .hasSpanContext(spanCtx1) .hasBody( @@ -3669,7 +3680,8 @@ public void accept(PayloadPart chunk) { "content", Value.of("{\"weather\":\"70 degrees and sunny\"}")))), log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), equalTo(EVENT_NAME, "gen_ai.choice")) + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), + equalTo(EVENT_NAME, "gen_ai.choice")) .hasSpanContext(spanCtx1) .hasBody( Value.of( @@ -3783,7 +3795,7 @@ void testInvokeModelToolCallAnthropicClaude() { span.hasName("chat anthropic.claude-3-5-sonnet-20240620-v1:0") .hasKind(SpanKind.CLIENT) .hasAttributesSatisfying( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_OPERATION_NAME, GenAiIncubatingAttributes.GenAiOperationNameIncubatingValues @@ -3809,7 +3821,7 @@ void testInvokeModelToolCallAnthropicClaude() { .hasSum(380) .hasCount(1) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_TOKEN_TYPE, GenAiIncubatingAttributes @@ -3824,11 +3836,11 @@ void testInvokeModelToolCallAnthropicClaude() { .hasSum(133) .hasCount(1) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_TOKEN_TYPE, GenAiIncubatingAttributes - .GenAiTokenTypeIncubatingValues.COMPLETION), + .GenAiTokenTypeIncubatingValues.OUTPUT), equalTo( GEN_AI_OPERATION_NAME, GenAiIncubatingAttributes @@ -3846,7 +3858,7 @@ void testInvokeModelToolCallAnthropicClaude() { point .hasSumGreaterThan(0.0) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_OPERATION_NAME, GenAiIncubatingAttributes @@ -3858,7 +3870,7 @@ void testInvokeModelToolCallAnthropicClaude() { .waitAndAssertLogRecords( log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo(EVENT_NAME, "gen_ai.user.message")) .hasSpanContext(spanCtx0) .hasBody( @@ -3869,7 +3881,8 @@ void testInvokeModelToolCallAnthropicClaude() { "What is the weather in Seattle and San Francisco today?")))), log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), equalTo(EVENT_NAME, "gen_ai.choice")) + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), + equalTo(EVENT_NAME, "gen_ai.choice")) .hasSpanContext(spanCtx0) .hasBody( Value.of( @@ -3956,7 +3969,7 @@ void testInvokeModelToolCallAnthropicClaude() { span.hasName("chat anthropic.claude-3-5-sonnet-20240620-v1:0") .hasKind(SpanKind.CLIENT) .hasAttributesSatisfying( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_OPERATION_NAME, GenAiIncubatingAttributes.GenAiOperationNameIncubatingValues @@ -3982,7 +3995,7 @@ void testInvokeModelToolCallAnthropicClaude() { .hasSum(590) .hasCount(1) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_TOKEN_TYPE, GenAiIncubatingAttributes @@ -3997,11 +4010,11 @@ void testInvokeModelToolCallAnthropicClaude() { .hasSum(132) .hasCount(1) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_TOKEN_TYPE, GenAiIncubatingAttributes - .GenAiTokenTypeIncubatingValues.COMPLETION), + .GenAiTokenTypeIncubatingValues.OUTPUT), equalTo( GEN_AI_OPERATION_NAME, GenAiIncubatingAttributes @@ -4019,7 +4032,7 @@ void testInvokeModelToolCallAnthropicClaude() { point .hasSumGreaterThan(0.0) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_OPERATION_NAME, GenAiIncubatingAttributes @@ -4032,7 +4045,7 @@ void testInvokeModelToolCallAnthropicClaude() { .waitAndAssertLogRecords( log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo(EVENT_NAME, "gen_ai.user.message")) .hasSpanContext(spanCtx1) .hasBody( @@ -4043,7 +4056,7 @@ void testInvokeModelToolCallAnthropicClaude() { "What is the weather in Seattle and San Francisco today?")))), log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo(EVENT_NAME, "gen_ai.assistant.message")) .hasSpanContext(spanCtx1) .hasBody( @@ -4070,7 +4083,7 @@ void testInvokeModelToolCallAnthropicClaude() { "To answer your question about the weather in Seattle and San Francisco today, I'll need to use the get_current_weather function for both cities. Let me do that for you now.")))), log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo(EVENT_NAME, "gen_ai.tool.message")) .hasSpanContext(spanCtx1) .hasBody( @@ -4079,7 +4092,7 @@ void testInvokeModelToolCallAnthropicClaude() { KeyValue.of("content", Value.of("\"50 degrees and raining\"")))), log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo(EVENT_NAME, "gen_ai.tool.message")) .hasSpanContext(spanCtx1) .hasBody( @@ -4088,7 +4101,8 @@ void testInvokeModelToolCallAnthropicClaude() { KeyValue.of("content", Value.of("\"70 degrees and sunny\"")))), log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), equalTo(EVENT_NAME, "gen_ai.choice")) + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), + equalTo(EVENT_NAME, "gen_ai.choice")) .hasSpanContext(spanCtx1) .hasBody( Value.of( @@ -4278,7 +4292,7 @@ public void accept(PayloadPart chunk) { span.hasName("chat anthropic.claude-3-5-sonnet-20240620-v1:0") .hasKind(SpanKind.CLIENT) .hasAttributesSatisfying( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_OPERATION_NAME, GenAiIncubatingAttributes.GenAiOperationNameIncubatingValues @@ -4304,7 +4318,7 @@ public void accept(PayloadPart chunk) { .hasSum(380) .hasCount(1) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_TOKEN_TYPE, GenAiIncubatingAttributes @@ -4319,11 +4333,11 @@ public void accept(PayloadPart chunk) { .hasSum(144) .hasCount(1) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_TOKEN_TYPE, GenAiIncubatingAttributes - .GenAiTokenTypeIncubatingValues.COMPLETION), + .GenAiTokenTypeIncubatingValues.OUTPUT), equalTo( GEN_AI_OPERATION_NAME, GenAiIncubatingAttributes @@ -4341,7 +4355,7 @@ public void accept(PayloadPart chunk) { point .hasSumGreaterThan(0.0) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_OPERATION_NAME, GenAiIncubatingAttributes @@ -4353,7 +4367,7 @@ public void accept(PayloadPart chunk) { .waitAndAssertLogRecords( log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo(EVENT_NAME, "gen_ai.user.message")) .hasSpanContext(spanCtx0) .hasBody( @@ -4364,7 +4378,8 @@ public void accept(PayloadPart chunk) { "What is the weather in Seattle and San Francisco today?")))), log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), equalTo(EVENT_NAME, "gen_ai.choice")) + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), + equalTo(EVENT_NAME, "gen_ai.choice")) .hasSpanContext(spanCtx0) .hasBody( Value.of( @@ -4491,7 +4506,7 @@ public void accept(PayloadPart chunk) { span.hasName("chat anthropic.claude-3-5-sonnet-20240620-v1:0") .hasKind(SpanKind.CLIENT) .hasAttributesSatisfying( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_OPERATION_NAME, GenAiIncubatingAttributes.GenAiOperationNameIncubatingValues @@ -4517,7 +4532,7 @@ public void accept(PayloadPart chunk) { .hasSum(601) .hasCount(1) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_TOKEN_TYPE, GenAiIncubatingAttributes @@ -4532,11 +4547,11 @@ public void accept(PayloadPart chunk) { .hasSum(145) .hasCount(1) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_TOKEN_TYPE, GenAiIncubatingAttributes - .GenAiTokenTypeIncubatingValues.COMPLETION), + .GenAiTokenTypeIncubatingValues.OUTPUT), equalTo( GEN_AI_OPERATION_NAME, GenAiIncubatingAttributes @@ -4554,7 +4569,7 @@ public void accept(PayloadPart chunk) { point .hasSumGreaterThan(0.0) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo( GEN_AI_OPERATION_NAME, GenAiIncubatingAttributes @@ -4567,7 +4582,7 @@ public void accept(PayloadPart chunk) { .waitAndAssertLogRecords( log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo(EVENT_NAME, "gen_ai.user.message")) .hasSpanContext(spanCtx1) .hasBody( @@ -4578,7 +4593,7 @@ public void accept(PayloadPart chunk) { "What is the weather in Seattle and San Francisco today?")))), log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo(EVENT_NAME, "gen_ai.assistant.message")) .hasSpanContext(spanCtx1) .hasBody( @@ -4605,7 +4620,7 @@ public void accept(PayloadPart chunk) { "To get the current weather for both Seattle and San Francisco, I'll need to use the get_current_weather function for each city. I'll make two separate calls within the same function calls block since these requests are independent of each other.")))), log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo(EVENT_NAME, "gen_ai.tool.message")) .hasSpanContext(spanCtx1) .hasBody( @@ -4614,7 +4629,7 @@ public void accept(PayloadPart chunk) { KeyValue.of("content", Value.of("\"50 degrees and raining\"")))), log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), equalTo(EVENT_NAME, "gen_ai.tool.message")) .hasSpanContext(spanCtx1) .hasBody( @@ -4623,7 +4638,8 @@ public void accept(PayloadPart chunk) { KeyValue.of("content", Value.of("\"70 degrees and sunny\"")))), log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, AWS_BEDROCK), equalTo(EVENT_NAME, "gen_ai.choice")) + equalTo(GEN_AI_PROVIDER_NAME, AWS_BEDROCK), + equalTo(EVENT_NAME, "gen_ai.choice")) .hasSpanContext(spanCtx1) .hasBody( Value.of( diff --git a/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2ClientTest.java b/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2ClientTest.java index 51b23fd75f66..aca4e9a86c0e 100644 --- a/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2ClientTest.java +++ b/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2ClientTest.java @@ -95,6 +95,8 @@ import software.amazon.awssdk.services.secretsmanager.SecretsManagerAsyncClientBuilder; import software.amazon.awssdk.services.secretsmanager.SecretsManagerClient; import software.amazon.awssdk.services.secretsmanager.SecretsManagerClientBuilder; +import software.amazon.awssdk.services.secretsmanager.model.DescribeSecretRequest; +import software.amazon.awssdk.services.secretsmanager.model.DescribeSecretResponse; import software.amazon.awssdk.services.secretsmanager.model.GetSecretValueRequest; import software.amazon.awssdk.services.secretsmanager.model.GetSecretValueResponse; import software.amazon.awssdk.services.sfn.SfnAsyncClient; @@ -473,8 +475,8 @@ private static Stream provideS3Arguments() { @ParameterizedTest @MethodSource("provideS3Arguments") - void testS3SendOperationRequestWithBuilder( - String operation, String method, Function call) throws Exception { + void testS3Client(String operation, String method, Function call) + throws Exception { S3ClientBuilder builder = S3Client.builder(); if (Boolean.getBoolean("testLatestDeps")) { Method forcePathStyleMethod = @@ -493,26 +495,26 @@ void testS3SendOperationRequestWithBuilder( Object response = call.apply(client); - assertThat(response.getClass().getSimpleName()) + assertThat(response) .satisfiesAnyOf( - v -> assertThat(v).startsWith(operation), - v -> assertThat(response).isInstanceOf(ResponseInputStream.class)); + r -> assertThat(r.getClass().getSimpleName()).startsWith(operation), + r -> assertThat(r).isInstanceOf(ResponseInputStream.class)); clientAssertions("S3", operation, method, response, "UNKNOWN"); } @ParameterizedTest @MethodSource("provideS3Arguments") - void testS3AsyncSendOperationRequestWithBuilder( + void testS3AsyncClient( String operation, String method, Function call, Function> asyncCall, String body) - throws NoSuchMethodException, - InvocationTargetException, + throws ExecutionException, IllegalAccessException, - ExecutionException, - InterruptedException { + InterruptedException, + InvocationTargetException, + NoSuchMethodException { S3AsyncClientBuilder builder = S3AsyncClient.builder(); if (Boolean.getBoolean("testLatestDeps")) { Method forcePathStyleMethod = @@ -536,7 +538,7 @@ void testS3AsyncSendOperationRequestWithBuilder( } @Test - void testKinesisSendOperationRequestWithBuilder() { + void testKinesisClient() { KinesisClientBuilder builder = KinesisClient.builder(); configureSdkClient(builder); KinesisClient client = @@ -551,10 +553,10 @@ void testKinesisSendOperationRequestWithBuilder() { Object response = client.deleteStream(DeleteStreamRequest.builder().streamName("somestream").build()); - assertThat(response.getClass().getSimpleName()) + assertThat(response) .satisfiesAnyOf( - v -> assertThat(v).startsWith("DeleteStream"), - v -> assertThat(response).isInstanceOf(ResponseInputStream.class)); + r -> assertThat(r.getClass().getSimpleName()).startsWith("DeleteStream"), + r -> assertThat(r).isInstanceOf(ResponseInputStream.class)); clientAssertions("Kinesis", "DeleteStream", "POST", response, "UNKNOWN"); } @@ -626,7 +628,7 @@ private static Stream provideSqsArguments() { @ParameterizedTest @MethodSource("provideSqsArguments") - void testSqsSendOperationRequestWithBuilder( + void testSqsClient( String operation, String requestId, Callable serverResponse, @@ -646,16 +648,16 @@ void testSqsSendOperationRequestWithBuilder( server.enqueue(serverResponse.call()); Object response = call.apply(client); - assertThat(response.getClass().getSimpleName()) + assertThat(response) .satisfiesAnyOf( - v -> assertThat(v).startsWith(operation), - v -> assertThat(response).isInstanceOf(ResponseInputStream.class)); + r -> assertThat(r.getClass().getSimpleName()).startsWith(operation), + r -> assertThat(r).isInstanceOf(ResponseInputStream.class)); clientAssertions("Sqs", operation, "POST", response, requestId); } @ParameterizedTest @MethodSource("provideSqsArguments") - void testSqsAsyncSendOperationRequestWithBuilder( + void testSqsAsyncClient( String operation, String requestId, Callable serverResponse, @@ -716,7 +718,7 @@ private static Stream provideSnsArguments() { @ParameterizedTest @MethodSource("provideSnsArguments") - void testSnsSendOperationRequestWithBuilder( + void testSnsClient( Function call, String operation, String method, @@ -734,17 +736,17 @@ void testSnsSendOperationRequestWithBuilder( server.enqueue(HttpResponse.of(HttpStatus.OK, MediaType.PLAIN_TEXT_UTF_8, responseBody)); Object response = call.apply(client); - assertThat(response.getClass().getSimpleName()) + assertThat(response) .satisfiesAnyOf( - v -> assertThat(response).isInstanceOf(CreateTopicResponse.class), - v -> assertThat(response).isInstanceOf(PublishResponse.class), - v -> assertThat(response).isInstanceOf(SubscribeResponse.class)); + r -> assertThat(r).isInstanceOf(CreateTopicResponse.class), + r -> assertThat(r).isInstanceOf(PublishResponse.class), + r -> assertThat(r).isInstanceOf(SubscribeResponse.class)); clientAssertions("Sns", operation, method, response, requestId); } @ParameterizedTest @MethodSource("provideSnsArguments") - void testSnsAsyncSendOperationRequestWithBuilder( + void testSnsAsyncClient( Function call, String operation, String method, @@ -762,16 +764,16 @@ void testSnsAsyncSendOperationRequestWithBuilder( server.enqueue(HttpResponse.of(HttpStatus.OK, MediaType.PLAIN_TEXT_UTF_8, responseBody)); Object response = call.apply(wrapClient(SnsClient.class, SnsAsyncClient.class, client)); - assertThat(response.getClass().getSimpleName()) + assertThat(response) .satisfiesAnyOf( - v -> assertThat(response).isInstanceOf(CreateTopicResponse.class), - v -> assertThat(response).isInstanceOf(PublishResponse.class), - v -> assertThat(response).isInstanceOf(SubscribeResponse.class)); + r -> assertThat(r).isInstanceOf(CreateTopicResponse.class), + r -> assertThat(r).isInstanceOf(PublishResponse.class), + r -> assertThat(r).isInstanceOf(SubscribeResponse.class)); clientAssertions("Sns", operation, method, response, requestId); } @Test - void testEc2SendOperationRequestWithBuilder() { + void testEc2Client() { Ec2ClientBuilder builder = Ec2Client.builder(); configureSdkClient(builder); Ec2Client client = @@ -784,16 +786,16 @@ void testEc2SendOperationRequestWithBuilder() { server.enqueue(HttpResponse.of(HttpStatus.OK, MediaType.PLAIN_TEXT_UTF_8, ec2BodyContent)); Object response = client.allocateAddress(); - assertThat(response.getClass().getSimpleName()) + assertThat(response) .satisfiesAnyOf( - v -> assertThat(v).startsWith("AllocateAddress"), - v -> assertThat(response).isInstanceOf(ResponseInputStream.class)); + r -> assertThat(r.getClass().getSimpleName()).startsWith("AllocateAddress"), + r -> assertThat(r).isInstanceOf(ResponseInputStream.class)); clientAssertions( "Ec2", "AllocateAddress", "POST", response, "59dbff89-35bd-4eac-99ed-be587EXAMPLE"); } @Test - void testEc2AsyncSendOperationRequestWithBuilder() { + void testEc2AsyncClient() { Ec2AsyncClientBuilder builder = Ec2AsyncClient.builder(); configureSdkClient(builder); Ec2AsyncClient client = @@ -811,7 +813,7 @@ void testEc2AsyncSendOperationRequestWithBuilder() { } @Test - void testRdsSendOperationRequestWithBuilder() { + void testRdsClient() { RdsClientBuilder builder = RdsClient.builder(); configureSdkClient(builder); RdsClient client = @@ -824,16 +826,16 @@ void testRdsSendOperationRequestWithBuilder() { server.enqueue(HttpResponse.of(HttpStatus.OK, MediaType.PLAIN_TEXT_UTF_8, rdsBodyContent)); Object response = client.deleteOptionGroup(DeleteOptionGroupRequest.builder().build()); - assertThat(response.getClass().getSimpleName()) + assertThat(response) .satisfiesAnyOf( - v -> assertThat(v).startsWith("DeleteOptionGroup"), - v -> assertThat(response).isInstanceOf(ResponseInputStream.class)); + r -> assertThat(r.getClass().getSimpleName()).startsWith("DeleteOptionGroup"), + r -> assertThat(r).isInstanceOf(ResponseInputStream.class)); clientAssertions( "Rds", "DeleteOptionGroup", "POST", response, "0ac9cda2-bbf4-11d3-f92b-31fa5e8dbc99"); } @Test - void testRdsAsyncSendOperationRequestWithBuilder() { + void testRdsAsyncClient() { RdsAsyncClientBuilder builder = RdsAsyncClient.builder(); configureSdkClient(builder); RdsAsyncClient client = @@ -971,7 +973,7 @@ private static Stream provideStepFunctionsArguments() { @ParameterizedTest @MethodSource("provideStepFunctionsArguments") - void testSfnSendOperationRequestWithBuilder( + void testSfnClient( Function call, String operation, String method, String requestId) { SfnClientBuilder builder = SfnClient.builder(); configureSdkClient(builder); @@ -984,14 +986,14 @@ void testSfnSendOperationRequestWithBuilder( server.enqueue(HttpResponse.of(HttpStatus.OK, MediaType.PLAIN_TEXT_UTF_8, "")); Object response = call.apply(client); - assertThat(response.getClass().getSimpleName()) + assertThat(response) .satisfiesAnyOf( - v -> - assertThat(response) + r -> + assertThat(r) .isInstanceOf( software.amazon.awssdk.services.sfn.model.DescribeActivityResponse.class), - v -> - assertThat(response) + r -> + assertThat(r) .isInstanceOf( software.amazon.awssdk.services.sfn.model.DescribeStateMachineResponse .class)); @@ -1000,7 +1002,7 @@ void testSfnSendOperationRequestWithBuilder( @ParameterizedTest @MethodSource("provideStepFunctionsArguments") - void testSfnAsyncSendOperationRequestWithBuilder( + void testSfnAsyncClient( Function call, String operation, String method, String requestId) { SfnAsyncClientBuilder builder = SfnAsyncClient.builder(); configureSdkClient(builder); @@ -1013,22 +1015,50 @@ void testSfnAsyncSendOperationRequestWithBuilder( server.enqueue(HttpResponse.of(HttpStatus.OK, MediaType.PLAIN_TEXT_UTF_8, "")); Object response = call.apply(wrapClient(SfnClient.class, SfnAsyncClient.class, client)); - assertThat(response.getClass().getSimpleName()) + assertThat(response) .satisfiesAnyOf( - v -> - assertThat(response) + r -> + assertThat(r) .isInstanceOf( software.amazon.awssdk.services.sfn.model.DescribeActivityResponse.class), - v -> - assertThat(response) + r -> + assertThat(r) .isInstanceOf( software.amazon.awssdk.services.sfn.model.DescribeStateMachineResponse .class)); clientAssertions("Sfn", operation, method, response, requestId); } - @Test - void testSecretsManagerSendOperationRequestWithBuilder() { + private static Stream provideSecretsManagerArguments() { + return Stream.of( + Arguments.of( + (Function) + c -> + c.getSecretValue( + GetSecretValueRequest.builder().secretId("MySecretFromCLI").build()), + "GetSecretValue", + "POST", + secretsManagerBodyContent, + "UNKNOWN"), + Arguments.of( + (Function) + c -> + c.describeSecret( + DescribeSecretRequest.builder().secretId("MySecretFromCLI").build()), + "DescribeSecret", + "POST", + secretsManagerBodyContent, + "UNKNOWN")); + } + + @ParameterizedTest + @MethodSource("provideSecretsManagerArguments") + void testSecretsManagerClient( + Function call, + String operation, + String method, + String responseBody, + String requestId) { SecretsManagerClientBuilder builder = SecretsManagerClient.builder(); configureSdkClient(builder); SecretsManagerClient client = @@ -1038,19 +1068,23 @@ void testSecretsManagerSendOperationRequestWithBuilder() { .credentialsProvider(CREDENTIALS_PROVIDER) .build(); - server.enqueue( - HttpResponse.of(HttpStatus.OK, MediaType.PLAIN_TEXT_UTF_8, secretsManagerBodyContent)); - Object response = - client.getSecretValue(GetSecretValueRequest.builder().secretId("MySecretFromCLI").build()); - assertThat(response.getClass().getSimpleName()) - .satisfies( - v -> assertThat(v).isEqualTo("GetSecretValueResponse"), - v -> assertThat(response).isInstanceOf(GetSecretValueResponse.class)); - clientAssertions("SecretsManager", "GetSecretValue", "POST", response, "UNKNOWN"); + server.enqueue(HttpResponse.of(HttpStatus.OK, MediaType.PLAIN_TEXT_UTF_8, responseBody)); + Object response = call.apply(client); + assertThat(response) + .satisfiesAnyOf( + r -> assertThat(r).isInstanceOf(GetSecretValueResponse.class), + r -> assertThat(r).isInstanceOf(DescribeSecretResponse.class)); + clientAssertions("SecretsManager", operation, method, response, requestId); } - @Test - void testSecretsManagerAsyncSendOperationRequestWithBuilder() { + @ParameterizedTest + @MethodSource("provideSecretsManagerArguments") + void testSecretsManagerAsyncClient( + Function call, + String operation, + String method, + String responseBody, + String requestId) { SecretsManagerAsyncClientBuilder builder = SecretsManagerAsyncClient.builder(); configureSdkClient(builder); SecretsManagerAsyncClient client = @@ -1060,11 +1094,10 @@ void testSecretsManagerAsyncSendOperationRequestWithBuilder() { .credentialsProvider(CREDENTIALS_PROVIDER) .build(); - server.enqueue( - HttpResponse.of(HttpStatus.OK, MediaType.PLAIN_TEXT_UTF_8, secretsManagerBodyContent)); + server.enqueue(HttpResponse.of(HttpStatus.OK, MediaType.PLAIN_TEXT_UTF_8, responseBody)); Object response = - client.getSecretValue(GetSecretValueRequest.builder().secretId("MySecretFromCLI").build()); - clientAssertions("SecretsManager", "GetSecretValue", "POST", response, "UNKNOWN"); + call.apply(wrapClient(SecretsManagerClient.class, SecretsManagerAsyncClient.class, client)); + clientAssertions("SecretsManager", operation, method, response, requestId); } private static Stream provideLambdaArguments() { @@ -1109,7 +1142,7 @@ private static Stream provideLambdaArguments() { @ParameterizedTest @MethodSource("provideLambdaArguments") - void testLambdaSendOperationRequestWithBuilder( + void testLambdaClient( Function call, String operation, String method, @@ -1126,19 +1159,19 @@ void testLambdaSendOperationRequestWithBuilder( server.enqueue(HttpResponse.of(HttpStatus.OK, MediaType.PLAIN_TEXT_UTF_8, responseBody)); Object response = call.apply(client); - assertThat(response.getClass().getSimpleName()) + assertThat(response) .satisfiesAnyOf( - v -> - assertThat(response) + r -> + assertThat(r) .isInstanceOf( software.amazon.awssdk.services.lambda.model.GetFunctionResponse.class), - v -> - assertThat(response) + r -> + assertThat(r) .isInstanceOf( software.amazon.awssdk.services.lambda.model .CreateEventSourceMappingResponse.class), - v -> - assertThat(response) + r -> + assertThat(r) .isInstanceOf( software.amazon.awssdk.services.lambda.model.GetEventSourceMappingResponse .class)); @@ -1147,7 +1180,7 @@ void testLambdaSendOperationRequestWithBuilder( @ParameterizedTest @MethodSource("provideLambdaArguments") - void testLambdaAsyncSendOperationRequestWithBuilder( + void testLambdaAsyncClient( Function call, String operation, String method, @@ -1164,19 +1197,19 @@ void testLambdaAsyncSendOperationRequestWithBuilder( server.enqueue(HttpResponse.of(HttpStatus.OK, MediaType.PLAIN_TEXT_UTF_8, responseBody)); Object response = call.apply(wrapClient(LambdaClient.class, LambdaAsyncClient.class, client)); - assertThat(response.getClass().getSimpleName()) + assertThat(response) .satisfiesAnyOf( - v -> - assertThat(response) + r -> + assertThat(r) .isInstanceOf( software.amazon.awssdk.services.lambda.model.GetFunctionResponse.class), - v -> - assertThat(response) + r -> + assertThat(r) .isInstanceOf( software.amazon.awssdk.services.lambda.model .CreateEventSourceMappingResponse.class), - v -> - assertThat(response) + r -> + assertThat(r) .isInstanceOf( software.amazon.awssdk.services.lambda.model.GetEventSourceMappingResponse .class)); diff --git a/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2SqsTracingTest.java b/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2SqsTracingTest.java index 6fa897d46292..426cd60f7263 100644 --- a/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2SqsTracingTest.java +++ b/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2SqsTracingTest.java @@ -93,7 +93,7 @@ protected void assertSqsTraces(boolean withParent, boolean captureHeaders) { if (captureHeaders) { attributes.add( satisfies( - stringArrayKey("messaging.header.test_message_header"), + stringArrayKey("messaging.header.Test_Message_Header"), v -> v.isEqualTo(ImmutableList.of("test")))); } span.hasName("testSdkSqs publish") @@ -163,7 +163,7 @@ protected void assertSqsTraces(boolean withParent, boolean captureHeaders) { if (captureHeaders) { attributes.add( satisfies( - stringArrayKey("messaging.header.test_message_header"), + stringArrayKey("messaging.header.Test_Message_Header"), v -> v.isEqualTo(ImmutableList.of("test")))); } @@ -201,7 +201,7 @@ protected void assertSqsTraces(boolean withParent, boolean captureHeaders) { if (captureHeaders) { attributes.add( satisfies( - stringArrayKey("messaging.header.test_message_header"), + stringArrayKey("messaging.header.Test_Message_Header"), v -> v.isEqualTo(singletonList("test")))); } @@ -238,13 +238,13 @@ void testCaptureMessageHeaderAsAttributeSpan() throws URISyntaxException { sendMessageRequest.toBuilder() .messageAttributes( Collections.singletonMap( - "test-message-header", + "Test-Message-Header", MessageAttributeValue.builder().dataType("String").stringValue("test").build())) .build(); client.sendMessage(newSendMessageRequest); ReceiveMessageRequest newReceiveMessageRequest = - receiveMessageRequest.toBuilder().messageAttributeNames("test-message-header").build(); + receiveMessageRequest.toBuilder().messageAttributeNames("Test-Message-Header").build(); ReceiveMessageResponse response = client.receiveMessage(newReceiveMessageRequest); assertThat(response.messages().size()).isEqualTo(1); diff --git a/instrumentation/azure-core/azure-core-1.14/metadata.yaml b/instrumentation/azure-core/azure-core-1.14/metadata.yaml index bee04ae8b1c4..a225b87a74c9 100644 --- a/instrumentation/azure-core/azure-core-1.14/metadata.yaml +++ b/instrumentation/azure-core/azure-core-1.14/metadata.yaml @@ -1 +1,2 @@ description: This instrumentation enables context propagation for the Azure Core library, it does not emit any telemetry on its own. +library_link: https://learn.microsoft.com/en-us/java/api/overview/azure/core-readme?view=azure-java-stable diff --git a/instrumentation/azure-core/azure-core-1.19/metadata.yaml b/instrumentation/azure-core/azure-core-1.19/metadata.yaml index bee04ae8b1c4..a225b87a74c9 100644 --- a/instrumentation/azure-core/azure-core-1.19/metadata.yaml +++ b/instrumentation/azure-core/azure-core-1.19/metadata.yaml @@ -1 +1,2 @@ description: This instrumentation enables context propagation for the Azure Core library, it does not emit any telemetry on its own. +library_link: https://learn.microsoft.com/en-us/java/api/overview/azure/core-readme?view=azure-java-stable diff --git a/instrumentation/azure-core/azure-core-1.36/metadata.yaml b/instrumentation/azure-core/azure-core-1.36/metadata.yaml index bee04ae8b1c4..a225b87a74c9 100644 --- a/instrumentation/azure-core/azure-core-1.36/metadata.yaml +++ b/instrumentation/azure-core/azure-core-1.36/metadata.yaml @@ -1 +1,2 @@ description: This instrumentation enables context propagation for the Azure Core library, it does not emit any telemetry on its own. +library_link: https://learn.microsoft.com/en-us/java/api/overview/azure/core-readme?view=azure-java-stable diff --git a/instrumentation/c3p0-0.9/javaagent/build.gradle.kts b/instrumentation/c3p0-0.9/javaagent/build.gradle.kts index 509cd19804aa..fe931a40ab1c 100644 --- a/instrumentation/c3p0-0.9/javaagent/build.gradle.kts +++ b/instrumentation/c3p0-0.9/javaagent/build.gradle.kts @@ -25,8 +25,10 @@ val collectMetadata = findProperty("collectMetadata")?.toString() ?: "false" tasks { val testStableSemconv by registering(Test::class) { - jvmArgs("-Dotel.semconv-stability.opt-in=database") + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath + jvmArgs("-Dotel.semconv-stability.opt-in=database") systemProperty("collectMetadata", collectMetadata) systemProperty("metadataConfig", "otel.semconv-stability.opt-in=database") } diff --git a/instrumentation/c3p0-0.9/library/README.md b/instrumentation/c3p0-0.9/library/README.md index 55ac8d775ffe..25104e1f1565 100644 --- a/instrumentation/c3p0-0.9/library/README.md +++ b/instrumentation/c3p0-0.9/library/README.md @@ -7,7 +7,7 @@ Provides OpenTelemetry instrumentation for [C3P0](https://www.mchange.com/projec ### Add these dependencies to your project Replace `OPENTELEMETRY_VERSION` with the [latest -release](https://search.maven.org/search?q=g:io.opentelemetry.instrumentation%20AND%20a:opentelemetry-c3p0-0.9). +release]( https://central.sonatype.com/artifact/io.opentelemetry.instrumentation/opentelemetry-c3p0-0.9). For Maven, add to your `pom.xml` dependencies: diff --git a/instrumentation/c3p0-0.9/library/build.gradle.kts b/instrumentation/c3p0-0.9/library/build.gradle.kts index a7b7f6a397ee..8624b977a5f2 100644 --- a/instrumentation/c3p0-0.9/library/build.gradle.kts +++ b/instrumentation/c3p0-0.9/library/build.gradle.kts @@ -11,6 +11,9 @@ dependencies { tasks { val testStableSemconv by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath + jvmArgs("-Dotel.semconv-stability.opt-in=database") } diff --git a/instrumentation/c3p0-0.9/metadata.yaml b/instrumentation/c3p0-0.9/metadata.yaml index 0c307d232160..9a8b08995180 100644 --- a/instrumentation/c3p0-0.9/metadata.yaml +++ b/instrumentation/c3p0-0.9/metadata.yaml @@ -1 +1,2 @@ description: The c3p0 instrumentation provides connection pool metrics for c3p0 data sources. +library_link: https://github.com/swaldman/c3p0 diff --git a/instrumentation/camel-2.20/javaagent/build.gradle.kts b/instrumentation/camel-2.20/javaagent/build.gradle.kts index d401d4f03f1b..d6df3d8e637e 100644 --- a/instrumentation/camel-2.20/javaagent/build.gradle.kts +++ b/instrumentation/camel-2.20/javaagent/build.gradle.kts @@ -84,11 +84,17 @@ tasks { } val testExperimental by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath + jvmArgs("-Dotel.instrumentation.camel.experimental-span-attributes=true") systemProperty("metadataConfig", "otel.instrumentation.camel.experimental-span-attributes=true") } val testStableSemconv by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath + jvmArgs("-Dotel.semconv-stability.opt-in=database") systemProperty("metadataConfig", "otel.semconv-stability.opt-in=database") } diff --git a/instrumentation/camel-2.20/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/apachecamel/aws/AwsSpanAssertions.java b/instrumentation/camel-2.20/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/apachecamel/aws/AwsSpanAssertions.java index b57677fb31ca..14b7fc6ce9b5 100644 --- a/instrumentation/camel-2.20/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/apachecamel/aws/AwsSpanAssertions.java +++ b/instrumentation/camel-2.20/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/apachecamel/aws/AwsSpanAssertions.java @@ -106,12 +106,10 @@ static SpanDataAssert sqs( attributeAssertions.add(equalTo(MESSAGING_OPERATION, "receive")); } else if (spanName.endsWith("process")) { attributeAssertions.add(equalTo(MESSAGING_OPERATION, "process")); - attributeAssertions.add( - satisfies(MESSAGING_MESSAGE_ID, val -> assertThat(val).isNotNull())); + attributeAssertions.add(satisfies(MESSAGING_MESSAGE_ID, val -> val.isNotNull())); } else if (spanName.endsWith("publish")) { attributeAssertions.add(equalTo(MESSAGING_OPERATION, "publish")); - attributeAssertions.add( - satisfies(MESSAGING_MESSAGE_ID, val -> assertThat(val).isNotNull())); + attributeAssertions.add(satisfies(MESSAGING_MESSAGE_ID, val -> val.isNotNull())); } } diff --git a/instrumentation/camel-2.20/metadata.yaml b/instrumentation/camel-2.20/metadata.yaml index 811ffc94c426..968f1d58d09d 100644 --- a/instrumentation/camel-2.20/metadata.yaml +++ b/instrumentation/camel-2.20/metadata.yaml @@ -2,6 +2,7 @@ description: > This instrumentation enables tracing for Apache Camel 2.x applications by generating spans for each route execution. For Camel versions 3.5 and newer, users should instead use the native 'camel-opentelemetry' component provided directly by the Camel project. +library_link: https://camel.apache.org/ configurations: - name: otel.instrumentation.camel.experimental-span-attributes description: > diff --git a/instrumentation/cassandra/cassandra-3.0/javaagent/build.gradle.kts b/instrumentation/cassandra/cassandra-3.0/javaagent/build.gradle.kts index 7abc06528df7..e09e209b9baf 100644 --- a/instrumentation/cassandra/cassandra-3.0/javaagent/build.gradle.kts +++ b/instrumentation/cassandra/cassandra-3.0/javaagent/build.gradle.kts @@ -47,6 +47,9 @@ tasks { } val testStableSemconv by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath + jvmArgs("-Dotel.semconv-stability.opt-in=database") systemProperty("metadataConfig", "otel.semconv-stability.opt-in=database") } diff --git a/instrumentation/cassandra/cassandra-3.0/metadata.yaml b/instrumentation/cassandra/cassandra-3.0/metadata.yaml index e0de86a5fe32..1ba853179653 100644 --- a/instrumentation/cassandra/cassandra-3.0/metadata.yaml +++ b/instrumentation/cassandra/cassandra-3.0/metadata.yaml @@ -1,6 +1,7 @@ description: > Instruments the Cassandra database client, providing database client spans and metrics for Cassandra queries. +library_link: https://github.com/apache/cassandra-java-driver configurations: - name: otel.instrumentation.common.db-statement-sanitizer.enabled description: Enables statement sanitization for database queries. diff --git a/instrumentation/cassandra/cassandra-4.0/javaagent/build.gradle.kts b/instrumentation/cassandra/cassandra-4.0/javaagent/build.gradle.kts index b49f63d13fa1..2ccb1fda9e01 100644 --- a/instrumentation/cassandra/cassandra-4.0/javaagent/build.gradle.kts +++ b/instrumentation/cassandra/cassandra-4.0/javaagent/build.gradle.kts @@ -30,6 +30,9 @@ tasks { } val testStableSemconv by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath + jvmArgs("-Dotel.semconv-stability.opt-in=database") systemProperty("metadataConfig", "otel.semconv-stability.opt-in=database") } diff --git a/instrumentation/cassandra/cassandra-4.0/metadata.yaml b/instrumentation/cassandra/cassandra-4.0/metadata.yaml index e0de86a5fe32..1ba853179653 100644 --- a/instrumentation/cassandra/cassandra-4.0/metadata.yaml +++ b/instrumentation/cassandra/cassandra-4.0/metadata.yaml @@ -1,6 +1,7 @@ description: > Instruments the Cassandra database client, providing database client spans and metrics for Cassandra queries. +library_link: https://github.com/apache/cassandra-java-driver configurations: - name: otel.instrumentation.common.db-statement-sanitizer.enabled description: Enables statement sanitization for database queries. diff --git a/instrumentation/cassandra/cassandra-4.4/javaagent/build.gradle.kts b/instrumentation/cassandra/cassandra-4.4/javaagent/build.gradle.kts index 1c9ec282a5c6..5f310ed60624 100644 --- a/instrumentation/cassandra/cassandra-4.4/javaagent/build.gradle.kts +++ b/instrumentation/cassandra/cassandra-4.4/javaagent/build.gradle.kts @@ -33,6 +33,9 @@ tasks { } val testStableSemconv by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath + jvmArgs("-Dotel.semconv-stability.opt-in=database") systemProperty("metadataConfig", "otel.semconv-stability.opt-in=database") } diff --git a/instrumentation/cassandra/cassandra-4.4/library/README.md b/instrumentation/cassandra/cassandra-4.4/library/README.md new file mode 100644 index 000000000000..d6fd08073233 --- /dev/null +++ b/instrumentation/cassandra/cassandra-4.4/library/README.md @@ -0,0 +1,52 @@ +# Library Instrumentation for Cassandra version 4.4 and higher + +Provides OpenTelemetry instrumentation for the [DataStax Java Driver for Apache Cassandra](https://docs.datastax.com/en/developer/java-driver/latest/), +enabling database client spans and metrics. + +## Quickstart + +### Add these dependencies to your project + +Replace `OPENTELEMETRY_VERSION` with the [latest release](https://central.sonatype.com/artifact/io.opentelemetry.instrumentation/opentelemetry-cassandra-4.4). + +For Maven, add to your `pom.xml` dependencies: + +```xml + + + io.opentelemetry.instrumentation + opentelemetry-cassandra-4.4 + OPENTELEMETRY_VERSION + + +``` + +For Gradle, add to your dependencies: + +```kotlin +implementation("io.opentelemetry.instrumentation:opentelemetry-cassandra-4.4:OPENTELEMETRY_VERSION") +``` + +### Usage + +```java +import com.datastax.oss.driver.api.core.CqlSession; +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.instrumentation.cassandra.v4_4.CassandraTelemetry; + +// ... + +// Get an OpenTelemetry instance +OpenTelemetry openTelemetry = ...; + +// Create a CassandraTelemetry instance +CassandraTelemetry telemetry = CassandraTelemetry.create(openTelemetry); + +// Create a CqlSession +CqlSession session = CqlSession.builder().build(); + +// Wrap the session +CqlSession tracedSession = telemetry.wrap(session); + +// ... use the tracedSession to make requests +``` diff --git a/instrumentation/cassandra/cassandra-4.4/library/build.gradle.kts b/instrumentation/cassandra/cassandra-4.4/library/build.gradle.kts index 19f0d3172cd5..13a5a8037356 100644 --- a/instrumentation/cassandra/cassandra-4.4/library/build.gradle.kts +++ b/instrumentation/cassandra/cassandra-4.4/library/build.gradle.kts @@ -17,6 +17,8 @@ tasks { } val testStableSemconv by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath jvmArgs("-Dotel.semconv-stability.opt-in=database") } diff --git a/instrumentation/cassandra/cassandra-4.4/metadata.yaml b/instrumentation/cassandra/cassandra-4.4/metadata.yaml index e0de86a5fe32..1ba853179653 100644 --- a/instrumentation/cassandra/cassandra-4.4/metadata.yaml +++ b/instrumentation/cassandra/cassandra-4.4/metadata.yaml @@ -1,6 +1,7 @@ description: > Instruments the Cassandra database client, providing database client spans and metrics for Cassandra queries. +library_link: https://github.com/apache/cassandra-java-driver configurations: - name: otel.instrumentation.common.db-statement-sanitizer.enabled description: Enables statement sanitization for database queries. diff --git a/instrumentation/clickhouse-client-0.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/ClickHouseScope.java b/instrumentation/clickhouse-client-0.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/ClickHouseScope.java deleted file mode 100644 index db779b684954..000000000000 --- a/instrumentation/clickhouse-client-0.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/ClickHouseScope.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.javaagent.instrumentation.clickhouse; - -import static io.opentelemetry.javaagent.instrumentation.clickhouse.ClickHouseSingletons.instrumenter; - -import io.opentelemetry.context.Context; -import io.opentelemetry.context.Scope; - -/** Container used to carry state between enter and exit advices */ -public final class ClickHouseScope { - private final ClickHouseDbRequest clickHouseDbRequest; - private final Context context; - private final Scope scope; - - private ClickHouseScope(ClickHouseDbRequest clickHouseDbRequest, Context context, Scope scope) { - this.clickHouseDbRequest = clickHouseDbRequest; - this.context = context; - this.scope = scope; - } - - public static ClickHouseScope start( - Context parentContext, ClickHouseDbRequest clickHouseDbRequest) { - if (!instrumenter().shouldStart(parentContext, clickHouseDbRequest)) { - return null; - } - - Context context = instrumenter().start(parentContext, clickHouseDbRequest); - return new ClickHouseScope(clickHouseDbRequest, context, context.makeCurrent()); - } - - public void end(Throwable throwable) { - scope.close(); - instrumenter().end(context, clickHouseDbRequest, null, throwable); - } -} diff --git a/instrumentation/clickhouse-client-0.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/ClickHouseSingletons.java b/instrumentation/clickhouse-client-0.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/ClickHouseSingletons.java deleted file mode 100644 index 764d0d2adf14..000000000000 --- a/instrumentation/clickhouse-client-0.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/ClickHouseSingletons.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.javaagent.instrumentation.clickhouse; - -import io.opentelemetry.api.GlobalOpenTelemetry; -import io.opentelemetry.instrumentation.api.incubator.semconv.db.DbClientAttributesExtractor; -import io.opentelemetry.instrumentation.api.incubator.semconv.db.DbClientMetrics; -import io.opentelemetry.instrumentation.api.incubator.semconv.db.DbClientSpanNameExtractor; -import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; -import io.opentelemetry.instrumentation.api.instrumenter.SpanKindExtractor; -import io.opentelemetry.instrumentation.api.semconv.network.ServerAttributesExtractor; - -public final class ClickHouseSingletons { - - private static final Instrumenter INSTRUMENTER; - - static { - ClickHouseAttributesGetter dbAttributesGetter = new ClickHouseAttributesGetter(); - - INSTRUMENTER = - Instrumenter.builder( - GlobalOpenTelemetry.get(), - "io.opentelemetry.clickhouse-client-0.5", - DbClientSpanNameExtractor.create(dbAttributesGetter)) - .addAttributesExtractor(DbClientAttributesExtractor.create(dbAttributesGetter)) - .addAttributesExtractor( - ServerAttributesExtractor.create(new ClickHouseNetworkAttributesGetter())) - .addOperationMetrics(DbClientMetrics.get()) - .buildInstrumenter(SpanKindExtractor.alwaysClient()); - } - - public static Instrumenter instrumenter() { - return INSTRUMENTER; - } - - private ClickHouseSingletons() {} -} diff --git a/instrumentation/clickhouse/clickhouse-client-common/javaagent/build.gradle.kts b/instrumentation/clickhouse/clickhouse-client-common/javaagent/build.gradle.kts new file mode 100644 index 000000000000..afe601decbdc --- /dev/null +++ b/instrumentation/clickhouse/clickhouse-client-common/javaagent/build.gradle.kts @@ -0,0 +1,8 @@ +plugins { + id("otel.javaagent-instrumentation") +} + +dependencies { + compileOnly("com.google.auto.value:auto-value-annotations") + annotationProcessor("com.google.auto.value:auto-value") +} diff --git a/instrumentation/clickhouse-client-0.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/ClickHouseAttributesGetter.java b/instrumentation/clickhouse/clickhouse-client-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/common/ClickHouseAttributesGetter.java similarity index 82% rename from instrumentation/clickhouse-client-0.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/ClickHouseAttributesGetter.java rename to instrumentation/clickhouse/clickhouse-client-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/common/ClickHouseAttributesGetter.java index d185e7227257..0958b3b46224 100644 --- a/instrumentation/clickhouse-client-0.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/ClickHouseAttributesGetter.java +++ b/instrumentation/clickhouse/clickhouse-client-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/common/ClickHouseAttributesGetter.java @@ -3,16 +3,22 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.javaagent.instrumentation.clickhouse; +package io.opentelemetry.javaagent.instrumentation.clickhouse.common; -import com.clickhouse.client.ClickHouseException; import io.opentelemetry.instrumentation.api.incubator.semconv.db.DbClientAttributesGetter; import io.opentelemetry.semconv.incubating.DbIncubatingAttributes; +import java.util.function.Function; import javax.annotation.Nullable; final class ClickHouseAttributesGetter implements DbClientAttributesGetter { + private final Function errorCodeExtractor; + + ClickHouseAttributesGetter(Function errorCodeExtractor) { + this.errorCodeExtractor = errorCodeExtractor; + } + @Nullable @Override public String getDbQueryText(ClickHouseDbRequest request) { @@ -64,9 +70,6 @@ public String getConnectionString(ClickHouseDbRequest request) { @Nullable @Override public String getResponseStatus(@Nullable Void response, @Nullable Throwable error) { - if (error instanceof ClickHouseException) { - return Integer.toString(((ClickHouseException) error).getErrorCode()); - } - return null; + return errorCodeExtractor.apply(error); } } diff --git a/instrumentation/clickhouse-client-0.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/ClickHouseDbRequest.java b/instrumentation/clickhouse/clickhouse-client-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/common/ClickHouseDbRequest.java similarity index 72% rename from instrumentation/clickhouse-client-0.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/ClickHouseDbRequest.java rename to instrumentation/clickhouse/clickhouse-client-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/common/ClickHouseDbRequest.java index 1afe12768aa2..35753981cb9b 100644 --- a/instrumentation/clickhouse-client-0.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/ClickHouseDbRequest.java +++ b/instrumentation/clickhouse/clickhouse-client-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/common/ClickHouseDbRequest.java @@ -3,12 +3,13 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.javaagent.instrumentation.clickhouse; +package io.opentelemetry.javaagent.instrumentation.clickhouse.common; import com.google.auto.value.AutoValue; import io.opentelemetry.instrumentation.api.incubator.semconv.db.SqlStatementInfo; import io.opentelemetry.instrumentation.api.incubator.semconv.db.SqlStatementSanitizer; import io.opentelemetry.javaagent.bootstrap.internal.AgentCommonConfig; +import javax.annotation.Nullable; @AutoValue public abstract class ClickHouseDbRequest { @@ -16,14 +17,18 @@ public abstract class ClickHouseDbRequest { private static final SqlStatementSanitizer sanitizer = SqlStatementSanitizer.create(AgentCommonConfig.get().isStatementSanitizationEnabled()); - public static ClickHouseDbRequest create(String host, int port, String dbName, String sql) { + public static ClickHouseDbRequest create( + @Nullable String host, @Nullable Integer port, @Nullable String dbName, String sql) { return new AutoValue_ClickHouseDbRequest(host, port, dbName, sanitizer.sanitize(sql)); } + @Nullable public abstract String getHost(); - public abstract int getPort(); + @Nullable + public abstract Integer getPort(); + @Nullable public abstract String getDbName(); public abstract SqlStatementInfo getSqlStatementInfo(); diff --git a/instrumentation/clickhouse/clickhouse-client-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/common/ClickHouseInstrumenterFactory.java b/instrumentation/clickhouse/clickhouse-client-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/common/ClickHouseInstrumenterFactory.java new file mode 100644 index 000000000000..7de945c17102 --- /dev/null +++ b/instrumentation/clickhouse/clickhouse-client-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/common/ClickHouseInstrumenterFactory.java @@ -0,0 +1,36 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.clickhouse.common; + +import io.opentelemetry.api.GlobalOpenTelemetry; +import io.opentelemetry.instrumentation.api.incubator.semconv.db.DbClientAttributesExtractor; +import io.opentelemetry.instrumentation.api.incubator.semconv.db.DbClientMetrics; +import io.opentelemetry.instrumentation.api.incubator.semconv.db.DbClientSpanNameExtractor; +import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; +import io.opentelemetry.instrumentation.api.instrumenter.SpanKindExtractor; +import io.opentelemetry.instrumentation.api.semconv.network.ServerAttributesExtractor; +import java.util.function.Function; + +public final class ClickHouseInstrumenterFactory { + + public static Instrumenter createInstrumenter( + String instrumenterName, Function errorCodeExtractor) { + ClickHouseAttributesGetter dbAttributesGetter = + new ClickHouseAttributesGetter(errorCodeExtractor); + + return Instrumenter.builder( + GlobalOpenTelemetry.get(), + instrumenterName, + DbClientSpanNameExtractor.create(dbAttributesGetter)) + .addAttributesExtractor(DbClientAttributesExtractor.create(dbAttributesGetter)) + .addAttributesExtractor( + ServerAttributesExtractor.create(new ClickHouseNetworkAttributesGetter())) + .addOperationMetrics(DbClientMetrics.get()) + .buildInstrumenter(SpanKindExtractor.alwaysClient()); + } + + private ClickHouseInstrumenterFactory() {} +} diff --git a/instrumentation/clickhouse-client-0.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/ClickHouseNetworkAttributesGetter.java b/instrumentation/clickhouse/clickhouse-client-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/common/ClickHouseNetworkAttributesGetter.java similarity index 87% rename from instrumentation/clickhouse-client-0.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/ClickHouseNetworkAttributesGetter.java rename to instrumentation/clickhouse/clickhouse-client-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/common/ClickHouseNetworkAttributesGetter.java index 21a57f7f9911..40d5a96176c7 100644 --- a/instrumentation/clickhouse-client-0.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/ClickHouseNetworkAttributesGetter.java +++ b/instrumentation/clickhouse/clickhouse-client-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/common/ClickHouseNetworkAttributesGetter.java @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.javaagent.instrumentation.clickhouse; +package io.opentelemetry.javaagent.instrumentation.clickhouse.common; import io.opentelemetry.instrumentation.api.semconv.network.ServerAttributesGetter; diff --git a/instrumentation/clickhouse/clickhouse-client-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/common/ClickHouseScope.java b/instrumentation/clickhouse/clickhouse-client-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/common/ClickHouseScope.java new file mode 100644 index 000000000000..c9bcfa00fbef --- /dev/null +++ b/instrumentation/clickhouse/clickhouse-client-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/common/ClickHouseScope.java @@ -0,0 +1,46 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.clickhouse.common; + +import io.opentelemetry.context.Context; +import io.opentelemetry.context.Scope; +import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; + +/** Container used to carry state between enter and exit advices */ +public final class ClickHouseScope { + private final ClickHouseDbRequest clickHouseDbRequest; + private final Context context; + private final Scope scope; + private final Instrumenter instrumenter; + + private ClickHouseScope( + ClickHouseDbRequest clickHouseDbRequest, + Context context, + Scope scope, + Instrumenter instrumenter) { + this.clickHouseDbRequest = clickHouseDbRequest; + this.context = context; + this.scope = scope; + this.instrumenter = instrumenter; + } + + public static ClickHouseScope start( + Instrumenter instrumenter, + Context parentContext, + ClickHouseDbRequest clickHouseDbRequest) { + if (!instrumenter.shouldStart(parentContext, clickHouseDbRequest)) { + return null; + } + + Context context = instrumenter.start(parentContext, clickHouseDbRequest); + return new ClickHouseScope(clickHouseDbRequest, context, context.makeCurrent(), instrumenter); + } + + public void end(Throwable throwable) { + scope.close(); + instrumenter.end(context, clickHouseDbRequest, null, throwable); + } +} diff --git a/instrumentation/clickhouse-client-0.5/javaagent/build.gradle.kts b/instrumentation/clickhouse/clickhouse-client-v1-0.5/javaagent/build.gradle.kts similarity index 71% rename from instrumentation/clickhouse-client-0.5/javaagent/build.gradle.kts rename to instrumentation/clickhouse/clickhouse-client-v1-0.5/javaagent/build.gradle.kts index cf36f4d1d23e..e7cc28a448ea 100644 --- a/instrumentation/clickhouse-client-0.5/javaagent/build.gradle.kts +++ b/instrumentation/clickhouse/clickhouse-client-v1-0.5/javaagent/build.gradle.kts @@ -12,9 +12,8 @@ muzzle { } dependencies { + implementation(project(":instrumentation:clickhouse:clickhouse-client-common:javaagent")) compileOnly("com.clickhouse:clickhouse-client:0.5.0") - compileOnly("com.google.auto.value:auto-value-annotations") - annotationProcessor("com.google.auto.value:auto-value") testImplementation("com.google.guava:guava") testLibrary("com.clickhouse:clickhouse-client:0.5.0") @@ -22,19 +21,18 @@ dependencies { testLibrary("org.apache.httpcomponents.client5:httpclient5:5.2.3") } -val collectMetadata = findProperty("collectMetadata")?.toString() ?: "false" - tasks { - test { + withType().configureEach { usesService(gradle.sharedServices.registrations["testcontainersBuildService"].service) - systemProperty("collectMetadata", collectMetadata) + systemProperty("collectMetadata", findProperty("collectMetadata")?.toString() ?: "false") } val testStableSemconv by registering(Test::class) { - jvmArgs("-Dotel.semconv-stability.opt-in=database") + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath + jvmArgs("-Dotel.semconv-stability.opt-in=database") systemProperty("metadataConfig", "otel.semconv-stability.opt-in=database") - systemProperty("collectMetadata", collectMetadata) } check { diff --git a/instrumentation/clickhouse-client-0.5/javaagent/src/main/java/com/clickhouse/client/ClickHouseRequestAccess.java b/instrumentation/clickhouse/clickhouse-client-v1-0.5/javaagent/src/main/java/com/clickhouse/client/ClickHouseRequestAccess.java similarity index 100% rename from instrumentation/clickhouse-client-0.5/javaagent/src/main/java/com/clickhouse/client/ClickHouseRequestAccess.java rename to instrumentation/clickhouse/clickhouse-client-v1-0.5/javaagent/src/main/java/com/clickhouse/client/ClickHouseRequestAccess.java diff --git a/instrumentation/clickhouse-client-0.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/ClickHouseClientInstrumentation.java b/instrumentation/clickhouse/clickhouse-client-v1-0.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/clientv1/v0_5/ClickHouseClientV1Instrumentation.java similarity index 80% rename from instrumentation/clickhouse-client-0.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/ClickHouseClientInstrumentation.java rename to instrumentation/clickhouse/clickhouse-client-v1-0.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/clientv1/v0_5/ClickHouseClientV1Instrumentation.java index 0b98a9724ec7..78a3c697f186 100644 --- a/instrumentation/clickhouse-client-0.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/ClickHouseClientInstrumentation.java +++ b/instrumentation/clickhouse/clickhouse-client-v1-0.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/clientv1/v0_5/ClickHouseClientV1Instrumentation.java @@ -3,10 +3,11 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.javaagent.instrumentation.clickhouse; +package io.opentelemetry.javaagent.instrumentation.clickhouse.clientv1.v0_5; import static io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge.currentContext; import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.implementsInterface; +import static io.opentelemetry.javaagent.instrumentation.clickhouse.clientv1.v0_5.ClickHouseClientV1Singletons.instrumenter; import static net.bytebuddy.matcher.ElementMatchers.isMethod; import static net.bytebuddy.matcher.ElementMatchers.named; import static net.bytebuddy.matcher.ElementMatchers.namedOneOf; @@ -16,15 +17,16 @@ import com.clickhouse.client.ClickHouseRequest; import com.clickhouse.client.ClickHouseRequestAccess; import com.clickhouse.client.config.ClickHouseDefaults; -import io.opentelemetry.context.Context; import io.opentelemetry.javaagent.bootstrap.CallDepth; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; +import io.opentelemetry.javaagent.instrumentation.clickhouse.common.ClickHouseDbRequest; +import io.opentelemetry.javaagent.instrumentation.clickhouse.common.ClickHouseScope; import net.bytebuddy.asm.Advice; import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.matcher.ElementMatcher; -public class ClickHouseClientInstrumentation implements TypeInstrumentation { +public class ClickHouseClientV1Instrumentation implements TypeInstrumentation { @Override public ElementMatcher typeMatcher() { return implementsInterface(named("com.clickhouse.client.ClickHouseClient")); @@ -36,11 +38,11 @@ public void transform(TypeTransformer transformer) { isMethod() .and(namedOneOf("executeAndWait", "execute")) .and(takesArgument(0, named("com.clickhouse.client.ClickHouseRequest"))), - this.getClass().getName() + "$ClickHouseExecuteAndWaitAdvice"); + this.getClass().getName() + "$ExecuteAndWaitAdvice"); } @SuppressWarnings("unused") - public static class ClickHouseExecuteAndWaitAdvice { + public static class ExecuteAndWaitAdvice { @Advice.OnMethodEnter(suppress = Throwable.class) public static ClickHouseScope onEnter( @Advice.Argument(0) ClickHouseRequest clickHouseRequest) { @@ -50,8 +52,6 @@ public static ClickHouseScope onEnter( return null; } - Context parentContext = currentContext(); - ClickHouseDbRequest request = ClickHouseDbRequest.create( clickHouseRequest.getServer().getHost(), @@ -62,7 +62,7 @@ public static ClickHouseScope onEnter( .orElse(ClickHouseDefaults.DATABASE.getDefaultValue().toString()), ClickHouseRequestAccess.getQuery(clickHouseRequest)); - return ClickHouseScope.start(parentContext, request); + return ClickHouseScope.start(instrumenter(), currentContext(), request); } @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) diff --git a/instrumentation/clickhouse-client-0.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/ClickHouseInstrumentationModule.java b/instrumentation/clickhouse/clickhouse-client-v1-0.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/clientv1/v0_5/ClickHouseClientV1InstrumentationModule.java similarity index 71% rename from instrumentation/clickhouse-client-0.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/ClickHouseInstrumentationModule.java rename to instrumentation/clickhouse/clickhouse-client-v1-0.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/clientv1/v0_5/ClickHouseClientV1InstrumentationModule.java index 28d10ee53894..f45d0c0b891a 100644 --- a/instrumentation/clickhouse-client-0.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/ClickHouseInstrumentationModule.java +++ b/instrumentation/clickhouse/clickhouse-client-v1-0.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/clientv1/v0_5/ClickHouseClientV1InstrumentationModule.java @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.javaagent.instrumentation.clickhouse; +package io.opentelemetry.javaagent.instrumentation.clickhouse.clientv1.v0_5; import static java.util.Collections.singletonList; @@ -14,11 +14,11 @@ import java.util.List; @AutoService(InstrumentationModule.class) -public class ClickHouseInstrumentationModule extends InstrumentationModule +public class ClickHouseClientV1InstrumentationModule extends InstrumentationModule implements ExperimentalInstrumentationModule { - public ClickHouseInstrumentationModule() { - super("clickhouse-client", "clickhouse-client-0.5", "clickhouse"); + public ClickHouseClientV1InstrumentationModule() { + super("clickhouse-client-v1", "clickhouse-client-v1-0.5", "clickhouse", "clickhouse-client"); } @Override @@ -33,6 +33,6 @@ public List injectedClassNames() { @Override public List typeInstrumentations() { - return singletonList(new ClickHouseClientInstrumentation()); + return singletonList(new ClickHouseClientV1Instrumentation()); } } diff --git a/instrumentation/clickhouse/clickhouse-client-v1-0.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/clientv1/v0_5/ClickHouseClientV1Singletons.java b/instrumentation/clickhouse/clickhouse-client-v1-0.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/clientv1/v0_5/ClickHouseClientV1Singletons.java new file mode 100644 index 000000000000..96615b838c7d --- /dev/null +++ b/instrumentation/clickhouse/clickhouse-client-v1-0.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/clientv1/v0_5/ClickHouseClientV1Singletons.java @@ -0,0 +1,35 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.clickhouse.clientv1.v0_5; + +import com.clickhouse.client.ClickHouseException; +import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; +import io.opentelemetry.javaagent.instrumentation.clickhouse.common.ClickHouseDbRequest; +import io.opentelemetry.javaagent.instrumentation.clickhouse.common.ClickHouseInstrumenterFactory; + +public final class ClickHouseClientV1Singletons { + + private static final String INSTRUMENTER_NAME = "io.opentelemetry.clickhouse-client-v1-0.5"; + private static final Instrumenter INSTRUMENTER; + + static { + INSTRUMENTER = + ClickHouseInstrumenterFactory.createInstrumenter( + INSTRUMENTER_NAME, + error -> { + if (error instanceof ClickHouseException) { + return Integer.toString(((ClickHouseException) error).getErrorCode()); + } + return null; + }); + } + + public static Instrumenter instrumenter() { + return INSTRUMENTER; + } + + private ClickHouseClientV1Singletons() {} +} diff --git a/instrumentation/clickhouse-client-0.5/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/clickhouse/ClickHouseClientTest.java b/instrumentation/clickhouse/clickhouse-client-v1-0.5/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/clickhouse/clientv1/v0_5/ClickHouseClientV1Test.java similarity index 97% rename from instrumentation/clickhouse-client-0.5/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/clickhouse/ClickHouseClientTest.java rename to instrumentation/clickhouse/clickhouse-client-v1-0.5/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/clickhouse/clientv1/v0_5/ClickHouseClientV1Test.java index b63258b596f4..53920b0b64f8 100644 --- a/instrumentation/clickhouse-client-0.5/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/clickhouse/ClickHouseClientTest.java +++ b/instrumentation/clickhouse/clickhouse-client-v1-0.5/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/clickhouse/clientv1/v0_5/ClickHouseClientV1Test.java @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.javaagent.instrumentation.clickhouse; +package io.opentelemetry.javaagent.instrumentation.clickhouse.clientv1.v0_5; import static io.opentelemetry.instrumentation.testing.junit.db.DbClientMetricsTestUtil.assertDurationMetric; import static io.opentelemetry.instrumentation.testing.junit.db.SemconvStabilityUtil.maybeStable; @@ -47,13 +47,10 @@ import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.TestInstance; -import org.junit.jupiter.api.TestInstance.Lifecycle; import org.junit.jupiter.api.extension.RegisterExtension; import org.testcontainers.containers.GenericContainer; -@TestInstance(Lifecycle.PER_CLASS) -class ClickHouseClientTest { +class ClickHouseClientV1Test { @RegisterExtension private static final InstrumentationExtension testing = AgentInstrumentationExtension.create(); @@ -69,7 +66,7 @@ class ClickHouseClientTest { private static ClickHouseClient client; @BeforeAll - void setup() throws ClickHouseException { + static void setup() throws ClickHouseException { clickhouseServer.start(); port = clickhouseServer.getMappedPort(8123); host = clickhouseServer.getHost(); @@ -89,7 +86,7 @@ void setup() throws ClickHouseException { } @AfterAll - void cleanup() { + static void cleanup() { if (client != null) { client.close(); } @@ -122,7 +119,7 @@ void testConnectionStringWithoutDatabaseSpecifiedStillGeneratesSpans() assertDurationMetric( testing, - "io.opentelemetry.clickhouse-client-0.5", + "io.opentelemetry.clickhouse-client-v1-0.5", DB_SYSTEM_NAME, DB_OPERATION_NAME, DB_NAMESPACE, diff --git a/instrumentation/clickhouse-client-0.5/metadata.yaml b/instrumentation/clickhouse/clickhouse-client-v1-0.5/metadata.yaml similarity index 53% rename from instrumentation/clickhouse-client-0.5/metadata.yaml rename to instrumentation/clickhouse/clickhouse-client-v1-0.5/metadata.yaml index 9287ee0c5f91..ba32cb2b0861 100644 --- a/instrumentation/clickhouse-client-0.5/metadata.yaml +++ b/instrumentation/clickhouse/clickhouse-client-v1-0.5/metadata.yaml @@ -1,4 +1,5 @@ -description: Instruments the V1 ClickHouseClient, providing database client spans and metrics. +description: This instrumentation enables database client spans and metrics for the V1 ClickHouse client. +library_link: https://github.com/ClickHouse/clickhouse-java configurations: - name: otel.instrumentation.common.db-statement-sanitizer.enabled description: Enables statement sanitization for database queries. diff --git a/instrumentation/clickhouse/clickhouse-client-v2-0.8/javaagent/build.gradle.kts b/instrumentation/clickhouse/clickhouse-client-v2-0.8/javaagent/build.gradle.kts new file mode 100644 index 000000000000..474c08da29a9 --- /dev/null +++ b/instrumentation/clickhouse/clickhouse-client-v2-0.8/javaagent/build.gradle.kts @@ -0,0 +1,36 @@ +plugins { + id("otel.javaagent-instrumentation") +} + +muzzle { + pass { + group.set("com.clickhouse") + module.set("client-v2") + versions.set("[0.6.4,)") + assertInverse.set(true) + } +} + +dependencies { + implementation(project(":instrumentation:clickhouse:clickhouse-client-common:javaagent")) + library("com.clickhouse:client-v2:0.8.0") +} + +tasks { + withType().configureEach { + usesService(gradle.sharedServices.registrations["testcontainersBuildService"].service) + systemProperty("collectMetadata", findProperty("collectMetadata")?.toString() ?: "false") + } + + val testStableSemconv by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath + + jvmArgs("-Dotel.semconv-stability.opt-in=database") + systemProperty("metadataConfig", "otel.semconv-stability.opt-in=database") + } + + check { + dependsOn(testStableSemconv) + } +} diff --git a/instrumentation/clickhouse/clickhouse-client-v2-0.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/clientv2/v0_8/ClickHouseClientV2Instrumentation.java b/instrumentation/clickhouse/clickhouse-client-v2-0.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/clientv2/v0_8/ClickHouseClientV2Instrumentation.java new file mode 100644 index 000000000000..3255589460fe --- /dev/null +++ b/instrumentation/clickhouse/clickhouse-client-v2-0.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/clientv2/v0_8/ClickHouseClientV2Instrumentation.java @@ -0,0 +1,90 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.clickhouse.clientv2.v0_8; + +import static io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge.currentContext; +import static io.opentelemetry.javaagent.instrumentation.clickhouse.clientv2.v0_8.ClickHouseClientV2Singletons.instrumenter; +import static net.bytebuddy.matcher.ElementMatchers.isMethod; +import static net.bytebuddy.matcher.ElementMatchers.isPublic; +import static net.bytebuddy.matcher.ElementMatchers.isSubTypeOf; +import static net.bytebuddy.matcher.ElementMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.takesArgument; + +import com.clickhouse.client.api.Client; +import com.clickhouse.client.api.query.QuerySettings; +import io.opentelemetry.context.Context; +import io.opentelemetry.instrumentation.api.semconv.network.internal.AddressAndPort; +import io.opentelemetry.javaagent.bootstrap.CallDepth; +import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; +import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; +import io.opentelemetry.javaagent.instrumentation.clickhouse.common.ClickHouseDbRequest; +import io.opentelemetry.javaagent.instrumentation.clickhouse.common.ClickHouseScope; +import java.util.Map; +import net.bytebuddy.asm.Advice; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; + +public class ClickHouseClientV2Instrumentation implements TypeInstrumentation { + @Override + public ElementMatcher typeMatcher() { + return named("com.clickhouse.client.api.Client"); + } + + @Override + public void transform(TypeTransformer transformer) { + transformer.applyAdviceToMethod( + isMethod() + .and(isPublic()) + .and(named("query")) + .and(takesArgument(0, String.class)) + .and(takesArgument(1, isSubTypeOf(Map.class))) + .and(takesArgument(2, named("com.clickhouse.client.api.query.QuerySettings"))), + this.getClass().getName() + "$QueryAdvice"); + } + + @SuppressWarnings("unused") + public static class QueryAdvice { + @Advice.OnMethodEnter(suppress = Throwable.class) + public static ClickHouseScope onEnter( + @Advice.This Client client, + @Advice.Argument(0) String sqlQuery, + @Advice.Argument(1) Map queryParams, + @Advice.Argument(2) QuerySettings querySettings) { + CallDepth callDepth = CallDepth.forClass(Client.class); + if (callDepth.getAndIncrement() > 0 || sqlQuery == null) { + return null; + } + + // https://clickhouse.com/docs/integrations/language-clients/java/client#client-configuration + // Currently, clientv2 supports only one endpoint. Since the endpoint is not going to change + // we'll cache it in a virtual field. + AddressAndPort addressAndPort = ClickHouseClientV2Singletons.getAddressAndPort(client); + if (addressAndPort == null) { + String endpoint = client.getEndpoints().stream().findFirst().orElse(null); + addressAndPort = ClickHouseClientV2Singletons.setAddressAndPort(client, endpoint); + } + + String database = client.getConfiguration().get("database"); + Context parentContext = currentContext(); + ClickHouseDbRequest request = + ClickHouseDbRequest.create( + addressAndPort.getAddress(), addressAndPort.getPort(), database, sqlQuery); + + return ClickHouseScope.start(instrumenter(), parentContext, request); + } + + @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) + public static void onExit( + @Advice.Thrown Throwable throwable, @Advice.Enter ClickHouseScope scope) { + CallDepth callDepth = CallDepth.forClass(Client.class); + if (callDepth.decrementAndGet() > 0 || scope == null) { + return; + } + + scope.end(throwable); + } + } +} diff --git a/instrumentation/clickhouse/clickhouse-client-v2-0.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/clientv2/v0_8/ClickHouseClientV2InstrumentationModule.java b/instrumentation/clickhouse/clickhouse-client-v2-0.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/clientv2/v0_8/ClickHouseClientV2InstrumentationModule.java new file mode 100644 index 000000000000..d08b758e6d68 --- /dev/null +++ b/instrumentation/clickhouse/clickhouse-client-v2-0.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/clientv2/v0_8/ClickHouseClientV2InstrumentationModule.java @@ -0,0 +1,28 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.clickhouse.clientv2.v0_8; + +import static java.util.Collections.singletonList; + +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 ClickHouseClientV2InstrumentationModule extends InstrumentationModule + implements ExperimentalInstrumentationModule { + + public ClickHouseClientV2InstrumentationModule() { + super("clickhouse-client-v2", "clickhouse-client-v2-0.8", "clickhouse", "clickhouse-client"); + } + + @Override + public List typeInstrumentations() { + return singletonList(new ClickHouseClientV2Instrumentation()); + } +} diff --git a/instrumentation/clickhouse/clickhouse-client-v2-0.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/clientv2/v0_8/ClickHouseClientV2Singletons.java b/instrumentation/clickhouse/clickhouse-client-v2-0.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/clientv2/v0_8/ClickHouseClientV2Singletons.java new file mode 100644 index 000000000000..9d24b12a0378 --- /dev/null +++ b/instrumentation/clickhouse/clickhouse-client-v2-0.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/clientv2/v0_8/ClickHouseClientV2Singletons.java @@ -0,0 +1,58 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.clickhouse.clientv2.v0_8; + +import com.clickhouse.client.api.Client; +import com.clickhouse.client.api.ServerException; +import io.opentelemetry.instrumentation.api.incubator.semconv.net.internal.UrlParser; +import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; +import io.opentelemetry.instrumentation.api.semconv.network.internal.AddressAndPort; +import io.opentelemetry.instrumentation.api.util.VirtualField; +import io.opentelemetry.javaagent.instrumentation.clickhouse.common.ClickHouseDbRequest; +import io.opentelemetry.javaagent.instrumentation.clickhouse.common.ClickHouseInstrumenterFactory; + +public final class ClickHouseClientV2Singletons { + + private static final String INSTRUMENTER_NAME = "io.opentelemetry.clickhouse-client-v2-0.8"; + private static final Instrumenter INSTRUMENTER; + + static { + INSTRUMENTER = + ClickHouseInstrumenterFactory.createInstrumenter( + INSTRUMENTER_NAME, + error -> { + if (error instanceof ServerException) { + return Integer.toString(((ServerException) error).getCode()); + } + return null; + }); + } + + public static Instrumenter instrumenter() { + return INSTRUMENTER; + } + + private static final VirtualField ADDRESS_AND_PORT = + VirtualField.find(Client.class, AddressAndPort.class); + + public static AddressAndPort getAddressAndPort(Client client) { + return ADDRESS_AND_PORT.get(client); + } + + public static AddressAndPort setAddressAndPort(Client client, String endpoint) { + AddressAndPort addressAndPort = new AddressAndPort(); + + if (endpoint != null) { + addressAndPort.setAddress(UrlParser.getHost(endpoint)); + addressAndPort.setPort(UrlParser.getPort(endpoint)); + } + ADDRESS_AND_PORT.set(client, addressAndPort); + + return addressAndPort; + } + + private ClickHouseClientV2Singletons() {} +} diff --git a/instrumentation/clickhouse/clickhouse-client-v2-0.8/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/clickhouse/clientv2/v0_8/ClickHouseClientV2Test.java b/instrumentation/clickhouse/clickhouse-client-v2-0.8/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/clickhouse/clientv2/v0_8/ClickHouseClientV2Test.java new file mode 100644 index 000000000000..93b396d3ccb8 --- /dev/null +++ b/instrumentation/clickhouse/clickhouse-client-v2-0.8/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/clickhouse/clientv2/v0_8/ClickHouseClientV2Test.java @@ -0,0 +1,343 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.clickhouse.clientv2.v0_8; + +import static io.opentelemetry.instrumentation.testing.junit.db.DbClientMetricsTestUtil.assertDurationMetric; +import static io.opentelemetry.instrumentation.testing.junit.db.SemconvStabilityUtil.maybeStable; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; +import static io.opentelemetry.semconv.DbAttributes.DB_NAMESPACE; +import static io.opentelemetry.semconv.DbAttributes.DB_OPERATION_NAME; +import static io.opentelemetry.semconv.DbAttributes.DB_RESPONSE_STATUS_CODE; +import static io.opentelemetry.semconv.DbAttributes.DB_SYSTEM_NAME; +import static io.opentelemetry.semconv.ErrorAttributes.ERROR_TYPE; +import static io.opentelemetry.semconv.ServerAttributes.SERVER_ADDRESS; +import static io.opentelemetry.semconv.ServerAttributes.SERVER_PORT; +import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_NAME; +import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_OPERATION; +import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_STATEMENT; +import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_SYSTEM; +import static java.util.Arrays.asList; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.catchThrowable; + +import com.clickhouse.client.api.Client; +import com.clickhouse.client.api.ServerException; +import com.clickhouse.client.api.command.CommandResponse; +import com.clickhouse.client.api.enums.Protocol; +import com.clickhouse.client.api.query.GenericRecord; +import com.clickhouse.client.api.query.QueryResponse; +import com.clickhouse.client.api.query.QuerySettings; +import com.clickhouse.client.api.query.Records; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.instrumentation.api.internal.SemconvStability; +import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.sdk.testing.assertj.AttributeAssertion; +import io.opentelemetry.sdk.trace.data.StatusData; +import io.opentelemetry.semconv.incubating.DbIncubatingAttributes; +import java.time.Instant; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +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; +import org.testcontainers.containers.GenericContainer; + +class ClickHouseClientV2Test { + + @RegisterExtension + private static final InstrumentationExtension testing = AgentInstrumentationExtension.create(); + + private static final GenericContainer clickhouseServer = + new GenericContainer<>("clickhouse/clickhouse-server:24.4.2").withExposedPorts(8123); + + private static final String dbName = "default"; + private static final String tableName = "test_table"; + private static int port; + private static String host; + private static Client client; + private static final String username = "default"; + private static final String password = ""; + + @BeforeAll + static void setup() throws Exception { + clickhouseServer.start(); + port = clickhouseServer.getMappedPort(8123); + host = clickhouseServer.getHost(); + + client = + new Client.Builder() + .addEndpoint(Protocol.HTTP, host, port, false) + .setDefaultDatabase(dbName) + .setUsername(username) + .setPassword(password) + .setOption("compress", "false") + .build(); + + QueryResponse response = + client + .query("create table if not exists " + tableName + "(value String) engine=Memory") + .get(); + response.close(); + + // wait for CREATE operation and clear + testing.waitForTraces(1); + testing.clearData(); + } + + @AfterAll + static void cleanup() { + if (client != null) { + client.close(); + } + clickhouseServer.stop(); + } + + @Test + void testConnectionStringWithoutDatabaseSpecifiedStillGeneratesSpans() throws Exception { + Client client = + new Client.Builder() + .addEndpoint(Protocol.HTTP, host, port, false) + .setOption("compress", "false") + .setUsername(username) + .setPassword(password) + .build(); + + QueryResponse response = client.query("select * from " + tableName).get(); + response.close(); + + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> + span.hasName("SELECT " + dbName) + .hasKind(SpanKind.CLIENT) + .hasNoParent() + .hasAttributesSatisfyingExactly( + attributeAssertions("select * from " + tableName, "SELECT")))); + + assertDurationMetric( + testing, + "io.opentelemetry.clickhouse-client-v2-0.8", + DB_SYSTEM_NAME, + DB_OPERATION_NAME, + DB_NAMESPACE, + SERVER_ADDRESS, + SERVER_PORT); + } + + @Test + void testQueryWithStringQuery() throws Exception { + testing.runWithSpan( + "parent", + () -> { + QueryResponse response = + client.query("insert into " + tableName + " values('1')('2')('3')").get(); + response.close(); + + response = client.query("select * from " + tableName).get(); + response.close(); + }); + + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> span.hasName("parent").hasNoParent().hasAttributes(Attributes.empty()), + span -> + span.hasName("INSERT " + dbName) + .hasKind(SpanKind.CLIENT) + .hasParent(trace.getSpan(0)) + .hasAttributesSatisfyingExactly( + attributeAssertions( + "insert into " + tableName + " values(?)(?)(?)", "INSERT")), + span -> + span.hasName("SELECT " + dbName) + .hasKind(SpanKind.CLIENT) + .hasParent(trace.getSpan(0)) + .hasAttributesSatisfyingExactly( + attributeAssertions("select * from " + tableName, "SELECT")))); + } + + @Test + void testQueryWithStringQueryAndId() throws Exception { + testing.runWithSpan( + "parent", + () -> { + QuerySettings querySettings = new QuerySettings(); + querySettings.setQueryId("test_query_id"); + + QueryResponse response = client.query("select * from " + tableName, querySettings).get(); + response.close(); + }); + + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> span.hasName("parent").hasNoParent().hasAttributes(Attributes.empty()), + span -> + span.hasName("SELECT " + dbName) + .hasKind(SpanKind.CLIENT) + .hasParent(trace.getSpan(0)) + .hasAttributesSatisfyingExactly( + attributeAssertions("select * from " + tableName, "SELECT")))); + } + + @Test + void testQueryThrowsServerException() { + Throwable thrown = + catchThrowable( + () -> { + QueryResponse response = client.query("select * from non_existent_table").get(); + response.close(); + }); + + assertThat(thrown).isInstanceOf(ServerException.class); + + List assertions = + new ArrayList<>(attributeAssertions("select * from non_existent_table", "SELECT")); + if (SemconvStability.emitStableDatabaseSemconv()) { + assertions.add(equalTo(DB_RESPONSE_STATUS_CODE, "60")); + assertions.add(equalTo(ERROR_TYPE, "com.clickhouse.client.api.ServerException")); + } + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> + span.hasName("SELECT " + dbName) + .hasKind(SpanKind.CLIENT) + .hasStatus(StatusData.error()) + .hasException(thrown) + .hasAttributesSatisfyingExactly(assertions))); + } + + @Test + void testSendQuery() throws Exception { + testing.runWithSpan( + "parent", + () -> { + CompletableFuture future = + client.execute("select * from " + tableName + " limit 1"); + CommandResponse results = future.get(); + assertThat(results.getReadRows()).isEqualTo(0); + }); + + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> span.hasName("parent").hasNoParent().hasAttributes(Attributes.empty()), + span -> + span.hasName("SELECT " + dbName) + .hasKind(SpanKind.CLIENT) + .hasParent(trace.getSpan(0)) + .hasAttributesSatisfyingExactly( + attributeAssertions( + "select * from " + tableName + " limit ?", "SELECT")))); + } + + @Test + void testSendQueryAll() { + testing.runWithSpan( + "parent", + () -> { + List records = client.queryAll("select * from " + tableName + " limit 1"); + assertThat(records.size()).isEqualTo(0); + }); + + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> span.hasName("parent").hasNoParent().hasAttributes(Attributes.empty()), + span -> + span.hasName("SELECT " + dbName) + .hasKind(SpanKind.CLIENT) + .hasParent(trace.getSpan(0)) + .hasAttributesSatisfyingExactly( + attributeAssertions( + "select * from " + tableName + " limit ?", "SELECT")))); + } + + @Test + void testSendQueryRecords() throws Exception { + testing.runWithSpan( + "parent", + () -> { + Records records = + client.queryRecords("insert into " + tableName + " values('test_value')").get(); + records.close(); + + records = client.queryRecords("select * from " + tableName + " limit 1").get(); + records.close(); + assertThat(records.getReadRows()).isEqualTo(1); + }); + + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> span.hasName("parent").hasNoParent().hasAttributes(Attributes.empty()), + span -> + span.hasName("INSERT " + dbName) + .hasKind(SpanKind.CLIENT) + .hasParent(trace.getSpan(0)) + .hasAttributesSatisfyingExactly( + attributeAssertions( + "insert into " + tableName + " values(?)", "INSERT")), + span -> + span.hasName("SELECT " + dbName) + .hasKind(SpanKind.CLIENT) + .hasParent(trace.getSpan(0)) + .hasAttributesSatisfyingExactly( + attributeAssertions( + "select * from " + tableName + " limit ?", "SELECT")))); + } + + @Test + void testPlaceholderQuery() throws Exception { + Map queryParams = new HashMap<>(); + queryParams.put("param_s", Instant.now().getEpochSecond()); + + testing.runWithSpan( + "parent", + () -> { + QueryResponse response = + client + .query( + "select * from " + tableName + " where value={param_s: String}", + queryParams, + null) + .get(); + response.close(); + }); + + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> span.hasName("parent").hasNoParent().hasAttributes(Attributes.empty()), + span -> + span.hasName("SELECT " + dbName) + .hasKind(SpanKind.CLIENT) + .hasParent(trace.getSpan(0)) + .hasAttributesSatisfyingExactly( + attributeAssertions( + "select * from " + tableName + " where value={param_s: String}", + "SELECT")))); + } + + @SuppressWarnings("deprecation") // using deprecated semconv + private static List attributeAssertions(String statement, String operation) { + return asList( + equalTo(maybeStable(DB_SYSTEM), DbIncubatingAttributes.DbSystemIncubatingValues.CLICKHOUSE), + equalTo(maybeStable(DB_NAME), dbName), + equalTo(SERVER_ADDRESS, host), + equalTo(SERVER_PORT, port), + equalTo(maybeStable(DB_STATEMENT), statement), + equalTo(maybeStable(DB_OPERATION), operation)); + } +} diff --git a/instrumentation/clickhouse/clickhouse-client-v2-0.8/metadata.yaml b/instrumentation/clickhouse/clickhouse-client-v2-0.8/metadata.yaml new file mode 100644 index 000000000000..7eb21f947bc4 --- /dev/null +++ b/instrumentation/clickhouse/clickhouse-client-v2-0.8/metadata.yaml @@ -0,0 +1,7 @@ +description: This instrumentation enables database client spans and metrics for the V2 ClickHouse client. +library_link: https://github.com/ClickHouse/clickhouse-java +configurations: + - name: otel.instrumentation.common.db-statement-sanitizer.enabled + description: Enables statement sanitization for database queries. + type: boolean + default: true diff --git a/instrumentation/couchbase/couchbase-2.0/javaagent/build.gradle.kts b/instrumentation/couchbase/couchbase-2.0/javaagent/build.gradle.kts index 3f6ce16b21e7..a404912c99cf 100644 --- a/instrumentation/couchbase/couchbase-2.0/javaagent/build.gradle.kts +++ b/instrumentation/couchbase/couchbase-2.0/javaagent/build.gradle.kts @@ -36,10 +36,16 @@ tasks { jvmArgs("--add-opens=java.base/java.lang=ALL-UNNAMED") jvmArgs("--add-opens=java.base/java.lang.invoke=ALL-UNNAMED") jvmArgs("-XX:+IgnoreUnrecognizedVMOptions") + + systemProperty("collectMetadata", findProperty("collectMetadata")?.toString() ?: "false") } val testStableSemconv by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath + jvmArgs("-Dotel.semconv-stability.opt-in=database") + systemProperty("metadataConfig", "otel.semconv-stability.opt-in=database") } check { diff --git a/instrumentation/couchbase/couchbase-2.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/couchbase/v2_0/CouchbaseAsyncClientTest.java b/instrumentation/couchbase/couchbase-2.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/couchbase/v2_0/CouchbaseAsyncClientTest.java index a28658223a61..3f7dd749bbe3 100644 --- a/instrumentation/couchbase/couchbase-2.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/couchbase/v2_0/CouchbaseAsyncClientTest.java +++ b/instrumentation/couchbase/couchbase-2.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/couchbase/v2_0/CouchbaseAsyncClientTest.java @@ -8,6 +8,8 @@ import com.couchbase.client.java.cluster.BucketSettings; import com.couchbase.client.java.env.DefaultCouchbaseEnvironment; import io.opentelemetry.instrumentation.couchbase.AbstractCouchbaseAsyncClientTest; +import io.opentelemetry.sdk.testing.assertj.AttributeAssertion; +import java.util.List; class CouchbaseAsyncClientTest extends AbstractCouchbaseAsyncClientTest { @@ -16,4 +18,24 @@ protected DefaultCouchbaseEnvironment.Builder envBuilder( BucketSettings bucketSettings, int carrierDirectPort, int httpDirectPort) { return CouchbaseUtil.envBuilder(bucketSettings, carrierDirectPort, httpDirectPort); } + + @Override + protected List couchbaseAttributes() { + return CouchbaseUtil.couchbaseAttributes(); + } + + @Override + protected List couchbaseQueryAttributes() { + return CouchbaseUtil.couchbaseQueryAttributes(); + } + + @Override + protected List couchbaseClusterManagerAttributes() { + return CouchbaseUtil.couchbaseClusterManagerAttributes(); + } + + @Override + protected List couchbaseN1qlAttributes() { + return CouchbaseUtil.couchbaseN1qlAttributes(); + } } diff --git a/instrumentation/couchbase/couchbase-2.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/couchbase/v2_0/CouchbaseClientTest.java b/instrumentation/couchbase/couchbase-2.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/couchbase/v2_0/CouchbaseClientTest.java index 0890358a8ead..3f8958bceac9 100644 --- a/instrumentation/couchbase/couchbase-2.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/couchbase/v2_0/CouchbaseClientTest.java +++ b/instrumentation/couchbase/couchbase-2.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/couchbase/v2_0/CouchbaseClientTest.java @@ -5,9 +5,19 @@ package io.opentelemetry.javaagent.instrumentation.couchbase.v2_0; +import static io.opentelemetry.instrumentation.testing.junit.db.DbClientMetricsTestUtil.assertDurationMetric; +import static io.opentelemetry.semconv.DbAttributes.DB_OPERATION_NAME; +import static io.opentelemetry.semconv.DbAttributes.DB_SYSTEM_NAME; +import static org.assertj.core.api.Assertions.assertThat; + +import com.couchbase.client.java.CouchbaseCluster; import com.couchbase.client.java.cluster.BucketSettings; +import com.couchbase.client.java.cluster.ClusterManager; import com.couchbase.client.java.env.DefaultCouchbaseEnvironment; import io.opentelemetry.instrumentation.couchbase.AbstractCouchbaseClientTest; +import io.opentelemetry.sdk.testing.assertj.AttributeAssertion; +import java.util.List; +import org.junit.jupiter.api.Test; class CouchbaseClientTest extends AbstractCouchbaseClientTest { @@ -16,4 +26,39 @@ protected DefaultCouchbaseEnvironment.Builder envBuilder( BucketSettings bucketSettings, int carrierDirectPort, int httpDirectPort) { return CouchbaseUtil.envBuilder(bucketSettings, carrierDirectPort, httpDirectPort); } + + @Override + protected List couchbaseAttributes() { + return CouchbaseUtil.couchbaseAttributes(); + } + + @Override + protected List couchbaseQueryAttributes() { + return CouchbaseUtil.couchbaseQueryAttributes(); + } + + @Override + protected List couchbaseClusterManagerAttributes() { + return CouchbaseUtil.couchbaseClusterManagerAttributes(); + } + + @Override + protected List couchbaseN1qlAttributes() { + return CouchbaseUtil.couchbaseN1qlAttributes(); + } + + @Test + void hasDurationMetric() { + CouchbaseCluster cluster = prepareCluster(bucketCouchbase); + ClusterManager manager = cluster.clusterManager(USERNAME, PASSWORD); + + testing.waitForTraces(1); + testing.clearData(); + + boolean hasBucket = manager.hasBucket(bucketCouchbase.name()); + assertThat(hasBucket).isTrue(); + + assertDurationMetric( + testing, "io.opentelemetry.couchbase-2.0", DB_SYSTEM_NAME, DB_OPERATION_NAME); + } } diff --git a/instrumentation/couchbase/couchbase-2.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/couchbase/v2_0/CouchbaseUtil.java b/instrumentation/couchbase/couchbase-2.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/couchbase/v2_0/CouchbaseUtil.java index 97a746fe0fbb..43661a5c44cb 100644 --- a/instrumentation/couchbase/couchbase-2.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/couchbase/v2_0/CouchbaseUtil.java +++ b/instrumentation/couchbase/couchbase-2.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/couchbase/v2_0/CouchbaseUtil.java @@ -5,14 +5,38 @@ package io.opentelemetry.javaagent.instrumentation.couchbase.v2_0; +import static java.util.Collections.emptyList; + import com.couchbase.client.core.metrics.DefaultLatencyMetricsCollectorConfig; import com.couchbase.client.core.metrics.DefaultMetricsCollectorConfig; import com.couchbase.client.java.cluster.BucketSettings; import com.couchbase.client.java.env.DefaultCouchbaseEnvironment; +import io.opentelemetry.sdk.testing.assertj.AttributeAssertion; +import java.util.List; import java.util.concurrent.TimeUnit; public class CouchbaseUtil { + public static List couchbaseAttributes() { + return couchbaseKvAttributes(); + } + + public static List couchbaseKvAttributes() { + return emptyList(); + } + + public static List couchbaseQueryAttributes() { + return emptyList(); + } + + public static List couchbaseClusterManagerAttributes() { + return emptyList(); + } + + public static List couchbaseN1qlAttributes() { + return emptyList(); + } + public static DefaultCouchbaseEnvironment.Builder envBuilder( BucketSettings bucketSettings, int carrierDirectPort, int httpDirectPort) { // Couchbase seems to be really slow to start sometimes diff --git a/instrumentation/couchbase/couchbase-2.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/couchbase/v2_0/springdata/CouchbaseSpringRepositoryTest.java b/instrumentation/couchbase/couchbase-2.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/couchbase/v2_0/springdata/CouchbaseSpringRepositoryTest.java index db5ba3d31379..4443d0c5e34a 100644 --- a/instrumentation/couchbase/couchbase-2.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/couchbase/v2_0/springdata/CouchbaseSpringRepositoryTest.java +++ b/instrumentation/couchbase/couchbase-2.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/couchbase/v2_0/springdata/CouchbaseSpringRepositoryTest.java @@ -11,6 +11,8 @@ import io.opentelemetry.instrumentation.couchbase.springdata.TestDocument; import io.opentelemetry.instrumentation.couchbase.springdata.TestRepository; import io.opentelemetry.javaagent.instrumentation.couchbase.v2_0.CouchbaseUtil; +import io.opentelemetry.sdk.testing.assertj.AttributeAssertion; +import java.util.List; class CouchbaseSpringRepositoryTest extends AbstractCouchbaseSpringRepositoryTest { @@ -20,6 +22,26 @@ protected DefaultCouchbaseEnvironment.Builder envBuilder( return CouchbaseUtil.envBuilder(bucketSettings, carrierDirectPort, httpDirectPort); } + @Override + protected List couchbaseAttributes() { + return CouchbaseUtil.couchbaseAttributes(); + } + + @Override + protected List couchbaseQueryAttributes() { + return CouchbaseUtil.couchbaseQueryAttributes(); + } + + @Override + protected List couchbaseClusterManagerAttributes() { + return CouchbaseUtil.couchbaseClusterManagerAttributes(); + } + + @Override + protected List couchbaseN1qlAttributes() { + return CouchbaseUtil.couchbaseN1qlAttributes(); + } + @Override protected TestDocument findById(TestRepository repository, String id) { return repository.findOne(id); diff --git a/instrumentation/couchbase/couchbase-2.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/couchbase/v2_0/springdata/CouchbaseSpringTemplateTest.java b/instrumentation/couchbase/couchbase-2.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/couchbase/v2_0/springdata/CouchbaseSpringTemplateTest.java index 3f4c193bd782..829bed4907c5 100644 --- a/instrumentation/couchbase/couchbase-2.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/couchbase/v2_0/springdata/CouchbaseSpringTemplateTest.java +++ b/instrumentation/couchbase/couchbase-2.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/couchbase/v2_0/springdata/CouchbaseSpringTemplateTest.java @@ -9,6 +9,8 @@ import com.couchbase.client.java.env.DefaultCouchbaseEnvironment; import io.opentelemetry.instrumentation.couchbase.springdata.AbstractCouchbaseSpringTemplateTest; import io.opentelemetry.javaagent.instrumentation.couchbase.v2_0.CouchbaseUtil; +import io.opentelemetry.sdk.testing.assertj.AttributeAssertion; +import java.util.List; class CouchbaseSpringTemplateTest extends AbstractCouchbaseSpringTemplateTest { @@ -17,4 +19,24 @@ protected DefaultCouchbaseEnvironment.Builder envBuilder( BucketSettings bucketSettings, int carrierDirectPort, int httpDirectPort) { return CouchbaseUtil.envBuilder(bucketSettings, carrierDirectPort, httpDirectPort); } + + @Override + protected List couchbaseAttributes() { + return CouchbaseUtil.couchbaseAttributes(); + } + + @Override + protected List couchbaseQueryAttributes() { + return CouchbaseUtil.couchbaseQueryAttributes(); + } + + @Override + protected List couchbaseClusterManagerAttributes() { + return CouchbaseUtil.couchbaseClusterManagerAttributes(); + } + + @Override + protected List couchbaseN1qlAttributes() { + return CouchbaseUtil.couchbaseN1qlAttributes(); + } } diff --git a/instrumentation/couchbase/couchbase-2.0/metadata.yaml b/instrumentation/couchbase/couchbase-2.0/metadata.yaml index 6295fa1c643e..eb4b19f0695b 100644 --- a/instrumentation/couchbase/couchbase-2.0/metadata.yaml +++ b/instrumentation/couchbase/couchbase-2.0/metadata.yaml @@ -1,5 +1,5 @@ -configurations: - - name: otel.instrumentation.couchbase.experimental-span-attributes - description: Enables experimental span attributes `couchbase.operation_id` and `couchbase.local.address` - type: boolean - default: false +description: > + This instrumentation enables database client spans and database client metrics for Couchbase 2.0 + operations. It automatically traces key-value operations (get, upsert, replace, remove), view + queries, N1QL queries, and cluster management operations. +library_link: https://github.com/couchbase/couchbase-java-client diff --git a/instrumentation/couchbase/couchbase-2.6/javaagent/build.gradle.kts b/instrumentation/couchbase/couchbase-2.6/javaagent/build.gradle.kts index 891771afade8..0c9e93ccd4c5 100644 --- a/instrumentation/couchbase/couchbase-2.6/javaagent/build.gradle.kts +++ b/instrumentation/couchbase/couchbase-2.6/javaagent/build.gradle.kts @@ -35,19 +35,30 @@ dependencies { tasks { withType().configureEach { - // TODO run tests both with and without experimental span attributes - jvmArgs("-Dotel.instrumentation.couchbase.experimental-span-attributes=true") - // required on jdk17 jvmArgs("--add-opens=java.base/java.lang=ALL-UNNAMED") jvmArgs("-XX:+IgnoreUnrecognizedVMOptions") + + systemProperty("collectMetadata", findProperty("collectMetadata")?.toString() ?: "false") } val testStableSemconv by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath + jvmArgs("-Dotel.semconv-stability.opt-in=database") + systemProperty("metadataConfig", "otel.semconv-stability.opt-in=database") + } + + val testExperimental by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath + + jvmArgs("-Dotel.instrumentation.couchbase.experimental-span-attributes=true") + systemProperty("metadataConfig", "otel.instrumentation.couchbase.experimental-span-attributes=true") } check { - dependsOn(testStableSemconv) + dependsOn(testStableSemconv, testExperimental) } } diff --git a/instrumentation/couchbase/couchbase-2.6/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/couchbase/v2_6/Couchbase26Util.java b/instrumentation/couchbase/couchbase-2.6/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/couchbase/v2_6/Couchbase26Util.java index f884a4857eeb..a6bed714ca2c 100644 --- a/instrumentation/couchbase/couchbase-2.6/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/couchbase/v2_6/Couchbase26Util.java +++ b/instrumentation/couchbase/couchbase-2.6/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/couchbase/v2_6/Couchbase26Util.java @@ -5,23 +5,27 @@ package io.opentelemetry.javaagent.instrumentation.couchbase.v2_6; +import static io.opentelemetry.api.common.AttributeKey.stringKey; 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 static io.opentelemetry.semconv.NetworkAttributes.NETWORK_PEER_ADDRESS; +import static io.opentelemetry.semconv.NetworkAttributes.NETWORK_PEER_PORT; +import static io.opentelemetry.semconv.NetworkAttributes.NETWORK_TYPE; import com.couchbase.client.core.metrics.DefaultLatencyMetricsCollectorConfig; import com.couchbase.client.core.metrics.DefaultMetricsCollectorConfig; import com.couchbase.client.java.cluster.BucketSettings; import com.couchbase.client.java.env.DefaultCouchbaseEnvironment; -import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.sdk.testing.assertj.AttributeAssertion; -import io.opentelemetry.semconv.NetworkAttributes; -import java.util.Arrays; +import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeUnit; public class Couchbase26Util { + private static final String EXPERIMENTAL_FLAG = + "otel.instrumentation.couchbase.experimental-span-attributes"; + public static DefaultCouchbaseEnvironment.Builder envBuilder( BucketSettings bucketSettings, int carrierDirectPort, int httpDirectPort) { // Couchbase seems to be really slow to start sometimes @@ -47,14 +51,57 @@ public static DefaultCouchbaseEnvironment.Builder envBuilder( } public static List couchbaseAttributes() { - return Arrays.asList( - equalTo(NetworkAttributes.NETWORK_TYPE, "ipv4"), - equalTo(NetworkAttributes.NETWORK_PEER_ADDRESS, "127.0.0.1"), - satisfies(NetworkAttributes.NETWORK_PEER_PORT, val -> assertThat(val).isNotNull()), - satisfies( - AttributeKey.stringKey("couchbase.local.address"), val -> assertThat(val).isNotNull()), - satisfies( - AttributeKey.stringKey("couchbase.operation_id"), val -> assertThat(val).isNotNull())); + return couchbaseKvAttributes(); + } + + public static List couchbaseKvAttributes() { + return baseNetworkAttributes().withLocalAddress().withOperationId().build(); + } + + public static List couchbaseQueryAttributes() { + return baseNetworkAttributes().withLocalAddress().build(); + } + + public static List couchbaseN1qlAttributes() { + return baseNetworkAttributes().withOperationId().build(); + } + + public static List couchbaseClusterManagerAttributes() { + return baseNetworkAttributes().build(); + } + + private static AttributeAssertionBuilder baseNetworkAttributes() { + return new AttributeAssertionBuilder() + .add(equalTo(NETWORK_TYPE, "ipv4")) + .add(equalTo(NETWORK_PEER_ADDRESS, "127.0.0.1")) + .add(satisfies(NETWORK_PEER_PORT, val -> val.isNotNull())); + } + + private static class AttributeAssertionBuilder { + private final List assertions = new ArrayList<>(); + + AttributeAssertionBuilder add(AttributeAssertion assertion) { + assertions.add(assertion); + return this; + } + + AttributeAssertionBuilder withLocalAddress() { + if (Boolean.getBoolean(EXPERIMENTAL_FLAG)) { + assertions.add(satisfies(stringKey("couchbase.local.address"), val -> val.isNotNull())); + } + return this; + } + + AttributeAssertionBuilder withOperationId() { + if (Boolean.getBoolean(EXPERIMENTAL_FLAG)) { + assertions.add(satisfies(stringKey("couchbase.operation_id"), val -> val.isNotNull())); + } + return this; + } + + List build() { + return new ArrayList<>(assertions); + } } private Couchbase26Util() {} diff --git a/instrumentation/couchbase/couchbase-2.6/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/couchbase/v2_6/CouchbaseAsyncClient26Test.java b/instrumentation/couchbase/couchbase-2.6/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/couchbase/v2_6/CouchbaseAsyncClient26Test.java index 8257d7014ec4..66fc60dc4eb4 100644 --- a/instrumentation/couchbase/couchbase-2.6/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/couchbase/v2_6/CouchbaseAsyncClient26Test.java +++ b/instrumentation/couchbase/couchbase-2.6/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/couchbase/v2_6/CouchbaseAsyncClient26Test.java @@ -23,4 +23,19 @@ protected DefaultCouchbaseEnvironment.Builder envBuilder( protected List couchbaseAttributes() { return Couchbase26Util.couchbaseAttributes(); } + + @Override + protected List couchbaseQueryAttributes() { + return Couchbase26Util.couchbaseQueryAttributes(); + } + + @Override + protected List couchbaseClusterManagerAttributes() { + return Couchbase26Util.couchbaseClusterManagerAttributes(); + } + + @Override + protected List couchbaseN1qlAttributes() { + return Couchbase26Util.couchbaseN1qlAttributes(); + } } diff --git a/instrumentation/couchbase/couchbase-2.6/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/couchbase/v2_6/CouchbaseClient26Test.java b/instrumentation/couchbase/couchbase-2.6/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/couchbase/v2_6/CouchbaseClient26Test.java index 65ace4b6f395..8b4cb586a52f 100644 --- a/instrumentation/couchbase/couchbase-2.6/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/couchbase/v2_6/CouchbaseClient26Test.java +++ b/instrumentation/couchbase/couchbase-2.6/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/couchbase/v2_6/CouchbaseClient26Test.java @@ -5,11 +5,21 @@ package io.opentelemetry.javaagent.instrumentation.couchbase.v2_6; +import static io.opentelemetry.instrumentation.testing.junit.db.DbClientMetricsTestUtil.assertDurationMetric; +import static io.opentelemetry.semconv.DbAttributes.DB_OPERATION_NAME; +import static io.opentelemetry.semconv.DbAttributes.DB_SYSTEM_NAME; +import static io.opentelemetry.semconv.NetworkAttributes.NETWORK_PEER_ADDRESS; +import static io.opentelemetry.semconv.NetworkAttributes.NETWORK_PEER_PORT; +import static org.assertj.core.api.Assertions.assertThat; + +import com.couchbase.client.java.CouchbaseCluster; import com.couchbase.client.java.cluster.BucketSettings; +import com.couchbase.client.java.cluster.ClusterManager; import com.couchbase.client.java.env.DefaultCouchbaseEnvironment; import io.opentelemetry.instrumentation.couchbase.AbstractCouchbaseClientTest; import io.opentelemetry.sdk.testing.assertj.AttributeAssertion; import java.util.List; +import org.junit.jupiter.api.Test; class CouchbaseClient26Test extends AbstractCouchbaseClientTest { @@ -23,4 +33,40 @@ protected DefaultCouchbaseEnvironment.Builder envBuilder( protected List couchbaseAttributes() { return Couchbase26Util.couchbaseAttributes(); } + + @Override + protected List couchbaseQueryAttributes() { + return Couchbase26Util.couchbaseQueryAttributes(); + } + + @Override + protected List couchbaseClusterManagerAttributes() { + return Couchbase26Util.couchbaseClusterManagerAttributes(); + } + + @Override + protected List couchbaseN1qlAttributes() { + return Couchbase26Util.couchbaseN1qlAttributes(); + } + + @Test + void hasDurationMetric() { + CouchbaseCluster cluster = prepareCluster(bucketCouchbase); + ClusterManager manager = cluster.clusterManager(USERNAME, PASSWORD); + + testing.waitForTraces(1); + testing.clearData(); + + boolean hasBucket = manager.hasBucket(bucketCouchbase.name()); + assertThat(hasBucket).isTrue(); + + assertDurationMetric( + testing, + // The metric is generated by couchbase-2.0 instrumentation + "io.opentelemetry.couchbase-2.0", + DB_SYSTEM_NAME, + DB_OPERATION_NAME, + NETWORK_PEER_ADDRESS, + NETWORK_PEER_PORT); + } } diff --git a/instrumentation/couchbase/couchbase-2.6/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/couchbase/v2_6/springdata/CouchbaseSpringRepository26Test.java b/instrumentation/couchbase/couchbase-2.6/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/couchbase/v2_6/springdata/CouchbaseSpringRepository26Test.java index 8f1765ad6852..1054324d6b57 100644 --- a/instrumentation/couchbase/couchbase-2.6/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/couchbase/v2_6/springdata/CouchbaseSpringRepository26Test.java +++ b/instrumentation/couchbase/couchbase-2.6/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/couchbase/v2_6/springdata/CouchbaseSpringRepository26Test.java @@ -27,6 +27,21 @@ protected List couchbaseAttributes() { return Couchbase26Util.couchbaseAttributes(); } + @Override + protected List couchbaseQueryAttributes() { + return Couchbase26Util.couchbaseQueryAttributes(); + } + + @Override + protected List couchbaseClusterManagerAttributes() { + return Couchbase26Util.couchbaseClusterManagerAttributes(); + } + + @Override + protected List couchbaseN1qlAttributes() { + return Couchbase26Util.couchbaseN1qlAttributes(); + } + @Override protected TestDocument findById(TestRepository repository, String id) { return repository.findById(id).get(); diff --git a/instrumentation/couchbase/couchbase-2.6/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/couchbase/v2_6/springdata/CouchbaseSpringTemplate26Test.java b/instrumentation/couchbase/couchbase-2.6/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/couchbase/v2_6/springdata/CouchbaseSpringTemplate26Test.java index cb7e4a1d978b..19db585da8dd 100644 --- a/instrumentation/couchbase/couchbase-2.6/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/couchbase/v2_6/springdata/CouchbaseSpringTemplate26Test.java +++ b/instrumentation/couchbase/couchbase-2.6/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/couchbase/v2_6/springdata/CouchbaseSpringTemplate26Test.java @@ -24,4 +24,19 @@ protected DefaultCouchbaseEnvironment.Builder envBuilder( protected List couchbaseAttributes() { return Couchbase26Util.couchbaseAttributes(); } + + @Override + protected List couchbaseQueryAttributes() { + return Couchbase26Util.couchbaseQueryAttributes(); + } + + @Override + protected List couchbaseClusterManagerAttributes() { + return Couchbase26Util.couchbaseClusterManagerAttributes(); + } + + @Override + protected List couchbaseN1qlAttributes() { + return Couchbase26Util.couchbaseN1qlAttributes(); + } } diff --git a/instrumentation/couchbase/couchbase-2.6/metadata.yaml b/instrumentation/couchbase/couchbase-2.6/metadata.yaml index a91bbaa98dab..0776918ae8ba 100644 --- a/instrumentation/couchbase/couchbase-2.6/metadata.yaml +++ b/instrumentation/couchbase/couchbase-2.6/metadata.yaml @@ -1,5 +1,12 @@ +description: > + This instrumentation enables database client spans and database client metrics for Couchbase 2.6 + operations. It automatically traces key-value operations (get, upsert, replace, remove), view + queries, N1QL queries, and cluster management operations. +library_link: https://github.com/couchbase/couchbase-java-client configurations: - name: otel.instrumentation.couchbase.experimental-span-attributes - description: Enables experimental span attributes couchbase.operation_id and couchbase.local.address + description: > + Enables experimental span attributes `couchbase.operation_id` and `couchbase.local.address`. + Different operation types receive different experimental attributes. type: boolean default: false diff --git a/instrumentation/couchbase/couchbase-3.1.6/javaagent/build.gradle.kts b/instrumentation/couchbase/couchbase-3.1.6/javaagent/build.gradle.kts index c0128fac5e38..e337e431d369 100644 --- a/instrumentation/couchbase/couchbase-3.1.6/javaagent/build.gradle.kts +++ b/instrumentation/couchbase/couchbase-3.1.6/javaagent/build.gradle.kts @@ -44,6 +44,9 @@ tasks { } val testStableSemconv by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath + jvmArgs("-Dotel.semconv-stability.opt-in=database") } diff --git a/instrumentation/couchbase/couchbase-3.1.6/metadata.yaml b/instrumentation/couchbase/couchbase-3.1.6/metadata.yaml index 3fec95f03fcf..ecbda30e26eb 100644 --- a/instrumentation/couchbase/couchbase-3.1.6/metadata.yaml +++ b/instrumentation/couchbase/couchbase-3.1.6/metadata.yaml @@ -1,3 +1,4 @@ description: > - Couchbase instrumentation is owned by the Couchbase project. This instrumentation automatically - configures the instrumentation provided by the Couchbase library. + Couchbase instrumentation is owned by the Couchbase project for versions 3+. This instrumentation + automatically configures the instrumentation provided by the Couchbase library. +library_link: https://github.com/couchbase/couchbase-java-client diff --git a/instrumentation/couchbase/couchbase-3.1/javaagent/build.gradle.kts b/instrumentation/couchbase/couchbase-3.1/javaagent/build.gradle.kts index 8efea0a3a21f..f4b2de4e97e4 100644 --- a/instrumentation/couchbase/couchbase-3.1/javaagent/build.gradle.kts +++ b/instrumentation/couchbase/couchbase-3.1/javaagent/build.gradle.kts @@ -45,6 +45,8 @@ tasks { } val testStableSemconv by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath jvmArgs("-Dotel.semconv-stability.opt-in=database") } diff --git a/instrumentation/couchbase/couchbase-3.1/metadata.yaml b/instrumentation/couchbase/couchbase-3.1/metadata.yaml index 3fec95f03fcf..ecbda30e26eb 100644 --- a/instrumentation/couchbase/couchbase-3.1/metadata.yaml +++ b/instrumentation/couchbase/couchbase-3.1/metadata.yaml @@ -1,3 +1,4 @@ description: > - Couchbase instrumentation is owned by the Couchbase project. This instrumentation automatically - configures the instrumentation provided by the Couchbase library. + Couchbase instrumentation is owned by the Couchbase project for versions 3+. This instrumentation + automatically configures the instrumentation provided by the Couchbase library. +library_link: https://github.com/couchbase/couchbase-java-client diff --git a/instrumentation/couchbase/couchbase-3.2/javaagent/build.gradle.kts b/instrumentation/couchbase/couchbase-3.2/javaagent/build.gradle.kts index 333fdbdcfced..8da259c0876b 100644 --- a/instrumentation/couchbase/couchbase-3.2/javaagent/build.gradle.kts +++ b/instrumentation/couchbase/couchbase-3.2/javaagent/build.gradle.kts @@ -43,6 +43,9 @@ tasks { } val testStableSemconv by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath + jvmArgs("-Dotel.semconv-stability.opt-in=database") } diff --git a/instrumentation/couchbase/couchbase-3.2/metadata.yaml b/instrumentation/couchbase/couchbase-3.2/metadata.yaml index 3fec95f03fcf..ecbda30e26eb 100644 --- a/instrumentation/couchbase/couchbase-3.2/metadata.yaml +++ b/instrumentation/couchbase/couchbase-3.2/metadata.yaml @@ -1,3 +1,4 @@ description: > - Couchbase instrumentation is owned by the Couchbase project. This instrumentation automatically - configures the instrumentation provided by the Couchbase library. + Couchbase instrumentation is owned by the Couchbase project for versions 3+. This instrumentation + automatically configures the instrumentation provided by the Couchbase library. +library_link: https://github.com/couchbase/couchbase-java-client diff --git a/instrumentation/couchbase/couchbase-common/testing/src/main/java/io/opentelemetry/instrumentation/couchbase/AbstractCouchbaseAsyncClientTest.java b/instrumentation/couchbase/couchbase-common/testing/src/main/java/io/opentelemetry/instrumentation/couchbase/AbstractCouchbaseAsyncClientTest.java index 8e81e841615a..29f56eaf1c23 100644 --- a/instrumentation/couchbase/couchbase-common/testing/src/main/java/io/opentelemetry/instrumentation/couchbase/AbstractCouchbaseAsyncClientTest.java +++ b/instrumentation/couchbase/couchbase-common/testing/src/main/java/io/opentelemetry/instrumentation/couchbase/AbstractCouchbaseAsyncClientTest.java @@ -9,6 +9,7 @@ import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_OPERATION; import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_SYSTEM; +import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DbSystemNameIncubatingValues.COUCHBASE; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Named.named; @@ -24,7 +25,6 @@ import io.opentelemetry.instrumentation.testing.internal.AutoCleanupExtension; import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension; import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; -import io.opentelemetry.semconv.incubating.DbIncubatingAttributes; import java.util.Collections; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; @@ -115,9 +115,7 @@ void hasBucket(BucketSettings bucketSettings) .hasKind(SpanKind.CLIENT) .hasNoParent() .hasAttributesSatisfyingExactly( - equalTo( - maybeStable(DB_SYSTEM), - DbIncubatingAttributes.DbSystemIncubatingValues.COUCHBASE), + equalTo(maybeStable(DB_SYSTEM), COUCHBASE), equalTo(maybeStable(DB_OPERATION), "Cluster.openBucket")), span -> assertCouchbaseSpan(span, "ClusterManager.hasBucket") @@ -156,9 +154,7 @@ void upsert(BucketSettings bucketSettings) .hasKind(SpanKind.CLIENT) .hasParent(trace.getSpan(0)) .hasAttributesSatisfyingExactly( - equalTo( - maybeStable(DB_SYSTEM), - DbIncubatingAttributes.DbSystemIncubatingValues.COUCHBASE), + equalTo(maybeStable(DB_SYSTEM), COUCHBASE), equalTo(maybeStable(DB_OPERATION), "Cluster.openBucket")), span -> assertCouchbaseSpan(span, "Bucket.upsert", bucketSettings.name()) @@ -204,9 +200,7 @@ void upsertAndGet(BucketSettings bucketSettings) .hasKind(SpanKind.CLIENT) .hasParent(trace.getSpan(0)) .hasAttributesSatisfyingExactly( - equalTo( - maybeStable(DB_SYSTEM), - DbIncubatingAttributes.DbSystemIncubatingValues.COUCHBASE), + equalTo(maybeStable(DB_SYSTEM), COUCHBASE), equalTo(maybeStable(DB_OPERATION), "Cluster.openBucket")), span -> assertCouchbaseSpan(span, "Bucket.upsert", bucketSettings.name()) @@ -249,9 +243,7 @@ void query() throws ExecutionException, InterruptedException, TimeoutException { .hasKind(SpanKind.CLIENT) .hasParent(trace.getSpan(0)) .hasAttributesSatisfyingExactly( - equalTo( - maybeStable(DB_SYSTEM), - DbIncubatingAttributes.DbSystemIncubatingValues.COUCHBASE), + equalTo(maybeStable(DB_SYSTEM), COUCHBASE), equalTo(maybeStable(DB_OPERATION), "Cluster.openBucket")), span -> assertCouchbaseSpan( diff --git a/instrumentation/couchbase/couchbase-common/testing/src/main/java/io/opentelemetry/instrumentation/couchbase/AbstractCouchbaseClientTest.java b/instrumentation/couchbase/couchbase-common/testing/src/main/java/io/opentelemetry/instrumentation/couchbase/AbstractCouchbaseClientTest.java index c39389ccf80c..fd33f5f3f5aa 100644 --- a/instrumentation/couchbase/couchbase-common/testing/src/main/java/io/opentelemetry/instrumentation/couchbase/AbstractCouchbaseClientTest.java +++ b/instrumentation/couchbase/couchbase-common/testing/src/main/java/io/opentelemetry/instrumentation/couchbase/AbstractCouchbaseClientTest.java @@ -39,7 +39,7 @@ public abstract class AbstractCouchbaseClientTest extends AbstractCouchbaseTest { @RegisterExtension - static final InstrumentationExtension testing = AgentInstrumentationExtension.create(); + protected static final InstrumentationExtension testing = AgentInstrumentationExtension.create(); @RegisterExtension static final AutoCleanupExtension cleanup = AutoCleanupExtension.create(); @@ -49,7 +49,7 @@ private static Stream bucketSettings() { Arguments.of(named(bucketMemcache.type().name(), bucketMemcache))); } - private CouchbaseCluster prepareCluster(BucketSettings bucketSettings) { + protected CouchbaseCluster prepareCluster(BucketSettings bucketSettings) { CouchbaseEnvironment environment = envBuilder(bucketSettings).build(); CouchbaseCluster cluster = CouchbaseCluster.create(environment, Collections.singletonList("127.0.0.1")); diff --git a/instrumentation/couchbase/couchbase-common/testing/src/main/java/io/opentelemetry/instrumentation/couchbase/AbstractCouchbaseTest.java b/instrumentation/couchbase/couchbase-common/testing/src/main/java/io/opentelemetry/instrumentation/couchbase/AbstractCouchbaseTest.java index ec8d82ce0555..de3aea98cf87 100644 --- a/instrumentation/couchbase/couchbase-common/testing/src/main/java/io/opentelemetry/instrumentation/couchbase/AbstractCouchbaseTest.java +++ b/instrumentation/couchbase/couchbase-common/testing/src/main/java/io/opentelemetry/instrumentation/couchbase/AbstractCouchbaseTest.java @@ -12,6 +12,8 @@ import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_OPERATION; import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_STATEMENT; import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_SYSTEM; +import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DbSystemNameIncubatingValues.COUCHBASE; +import static java.util.Collections.emptyList; import com.couchbase.client.java.bucket.BucketType; import com.couchbase.client.java.cluster.BucketSettings; @@ -26,10 +28,8 @@ import io.opentelemetry.instrumentation.test.utils.PortUtils; import io.opentelemetry.sdk.testing.assertj.AttributeAssertion; import io.opentelemetry.sdk.testing.assertj.SpanDataAssert; -import io.opentelemetry.semconv.incubating.DbIncubatingAttributes; import java.lang.reflect.Field; import java.util.ArrayList; -import java.util.Collections; import java.util.List; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; @@ -125,8 +125,7 @@ protected SpanDataAssert assertCouchbaseSpan( span.hasName(spanName).hasKind(SpanKind.CLIENT); List assertions = new ArrayList<>(); - assertions.add( - equalTo(maybeStable(DB_SYSTEM), DbIncubatingAttributes.DbSystemIncubatingValues.COUCHBASE)); + assertions.add(equalTo(maybeStable(DB_SYSTEM), COUCHBASE)); if (operation != null) { assertions.add(equalTo(maybeStable(DB_OPERATION), operation)); } @@ -136,7 +135,22 @@ protected SpanDataAssert assertCouchbaseSpan( if (statement != null) { assertions.add(satisfies(maybeStable(DB_STATEMENT), s -> s.startsWith(statement))); } - assertions.addAll(couchbaseAttributes()); + + if (statement != null) { + if (statement.startsWith("SELECT")) { + // N1QL queries get operation_id but NOT local.address experimental attribute + assertions.addAll(couchbaseN1qlAttributes()); + } else { + // ViewQuery operations get local.address but NOT operation_id experimental attribute + assertions.addAll(couchbaseQueryAttributes()); + } + } else if (operation != null && operation.startsWith("ClusterManager.")) { + // ClusterManager operations have no experimental attributes + assertions.addAll(couchbaseClusterManagerAttributes()); + } else { + // KV operations (get, upsert, etc.) get both experimental attributes + assertions.addAll(couchbaseAttributes()); + } span.hasAttributesSatisfyingExactly(assertions); @@ -144,6 +158,18 @@ protected SpanDataAssert assertCouchbaseSpan( } protected List couchbaseAttributes() { - return Collections.emptyList(); + return emptyList(); + } + + protected List couchbaseQueryAttributes() { + return emptyList(); + } + + protected List couchbaseClusterManagerAttributes() { + return emptyList(); + } + + protected List couchbaseN1qlAttributes() { + return emptyList(); } } diff --git a/instrumentation/dropwizard/dropwizard-metrics-4.0/metadata.yaml b/instrumentation/dropwizard/dropwizard-metrics-4.0/metadata.yaml index 930c129fa366..808f1b80beb0 100644 --- a/instrumentation/dropwizard/dropwizard-metrics-4.0/metadata.yaml +++ b/instrumentation/dropwizard/dropwizard-metrics-4.0/metadata.yaml @@ -5,6 +5,7 @@ description: > The Dropwizard metrics API does not have a concept of metric labels/tags/attributes, thus the data produced by this integration might be of very low quality, depending on how the API is used in the instrumented application. +library_link: https://metrics.dropwizard.io/4.2.0/ disabled_by_default: true configurations: - name: otel.instrumentation.dropwizard-metrics.enabled diff --git a/instrumentation/dropwizard/dropwizard-views-0.7/javaagent/build.gradle.kts b/instrumentation/dropwizard/dropwizard-views-0.7/javaagent/build.gradle.kts index 2672a8d00d4f..419eeb767e75 100644 --- a/instrumentation/dropwizard/dropwizard-views-0.7/javaagent/build.gradle.kts +++ b/instrumentation/dropwizard/dropwizard-views-0.7/javaagent/build.gradle.kts @@ -18,5 +18,8 @@ dependencies { } tasks.withType().configureEach { - jvmArgs("-Dotel.instrumentation.common.experimental.controller-telemetry.enabled=true") + jvmArgs("-Dotel.instrumentation.common.experimental.view-telemetry.enabled=true") + + systemProperty("collectMetadata", findProperty("collectMetadata")?.toString() ?: "false") + systemProperty("metadataConfig", "otel.instrumentation.common.experimental.view-telemetry.enabled=true") } diff --git a/instrumentation/dropwizard/dropwizard-views-0.7/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/dropwizardviews/DropwizardSingletons.java b/instrumentation/dropwizard/dropwizard-views-0.7/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/dropwizardviews/DropwizardSingletons.java index f9c799c12e65..d9902baa96ab 100644 --- a/instrumentation/dropwizard/dropwizard-views-0.7/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/dropwizardviews/DropwizardSingletons.java +++ b/instrumentation/dropwizard/dropwizard-views-0.7/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/dropwizardviews/DropwizardSingletons.java @@ -17,7 +17,7 @@ public final class DropwizardSingletons { private static final Instrumenter INSTRUMENTER = Instrumenter.builder( GlobalOpenTelemetry.get(), INSTRUMENTATION_NAME, DropwizardSingletons::spanName) - .setEnabled(ExperimentalConfig.get().controllerTelemetryEnabled()) + .setEnabled(ExperimentalConfig.get().viewTelemetryEnabled()) .buildInstrumenter(); private static String spanName(View view) { diff --git a/instrumentation/dropwizard/dropwizard-views-0.7/metadata.yaml b/instrumentation/dropwizard/dropwizard-views-0.7/metadata.yaml index af390acec7fa..b0fa4287635d 100644 --- a/instrumentation/dropwizard/dropwizard-views-0.7/metadata.yaml +++ b/instrumentation/dropwizard/dropwizard-views-0.7/metadata.yaml @@ -1,9 +1,7 @@ +description: This instrumentation enables the creation of spans for Dropwizard views. +library_link: https://www.dropwizard.io/en/latest/manual/views.html configurations: - name: otel.instrumentation.common.experimental.view-telemetry.enabled description: Enables the creation of experimental view (INTERNAL) spans. type: boolean default: false - - name: otel.instrumentation.common.experimental.controller-telemetry.enabled - description: Enables the creation of experimental controller (INTERNAL) spans. - type: boolean - default: false diff --git a/instrumentation/elasticsearch/elasticsearch-api-client-7.16/javaagent/build.gradle.kts b/instrumentation/elasticsearch/elasticsearch-api-client-7.16/javaagent/build.gradle.kts index 231579a5c1c9..aba169d3246e 100644 --- a/instrumentation/elasticsearch/elasticsearch-api-client-7.16/javaagent/build.gradle.kts +++ b/instrumentation/elasticsearch/elasticsearch-api-client-7.16/javaagent/build.gradle.kts @@ -82,12 +82,14 @@ tasks { } val testStableSemconv by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath + jvmArgs("-Dotel.semconv-stability.opt-in=database") systemProperty("metadataConfig", "otel.semconv-stability.opt-in=database") } check { - dependsOn(testing.suites) - dependsOn(testStableSemconv) + dependsOn(testing.suites, testStableSemconv) } } diff --git a/instrumentation/elasticsearch/elasticsearch-api-client-7.16/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/elasticsearch/apiclient/ElasticsearchClientTest.java b/instrumentation/elasticsearch/elasticsearch-api-client-7.16/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/elasticsearch/apiclient/ElasticsearchClientTest.java index f883419aa422..284c41a820e6 100644 --- a/instrumentation/elasticsearch/elasticsearch-api-client-7.16/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/elasticsearch/apiclient/ElasticsearchClientTest.java +++ b/instrumentation/elasticsearch/elasticsearch-api-client-7.16/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/elasticsearch/apiclient/ElasticsearchClientTest.java @@ -6,6 +6,7 @@ package io.opentelemetry.javaagent.instrumentation.elasticsearch.apiclient; import static io.opentelemetry.instrumentation.testing.GlobalTraceUtil.runWithSpan; +import static io.opentelemetry.instrumentation.testing.junit.db.DbClientMetricsTestUtil.assertDurationMetric; import static io.opentelemetry.instrumentation.testing.junit.db.SemconvStabilityUtil.maybeStable; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; import static io.opentelemetry.semconv.HttpAttributes.HTTP_REQUEST_METHOD; @@ -16,7 +17,9 @@ import static io.opentelemetry.semconv.UrlAttributes.URL_FULL; import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_ELASTICSEARCH_PATH_PARTS; import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_OPERATION; +import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_OPERATION_NAME; import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_SYSTEM; +import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_SYSTEM_NAME; import static org.assertj.core.api.Assertions.assertThat; import co.elastic.clients.elasticsearch.ElasticsearchAsyncClient; @@ -154,6 +157,14 @@ void elasticsearchIndex() throws IOException { URL_FULL, httpHost.toURI() + "/test-index/_doc/test-id?timeout=10s"), equalTo(HTTP_RESPONSE_STATUS_CODE, 201L)))); + + assertDurationMetric( + testing, + "io.opentelemetry.elasticsearch-rest-7.0", + DB_OPERATION_NAME, + DB_SYSTEM_NAME, + SERVER_ADDRESS, + SERVER_PORT); } @Test diff --git a/instrumentation/elasticsearch/elasticsearch-api-client-7.16/metadata.yaml b/instrumentation/elasticsearch/elasticsearch-api-client-7.16/metadata.yaml index 3dc6d3cf9da4..5db4ba15cf6f 100644 --- a/instrumentation/elasticsearch/elasticsearch-api-client-7.16/metadata.yaml +++ b/instrumentation/elasticsearch/elasticsearch-api-client-7.16/metadata.yaml @@ -1,2 +1,6 @@ -description: This instrumentation enables client spans for Elasticsearch API client requests for - version 7 of the client. Versions 8.10 and later have native support for OpenTelemetry. +description: > + This instrumentation extends the elasticsearch-rest-7.0 instrumentation by adding + additional `db.elasticsearch.path_parts.id` and `db.elasticsearch.path_parts.index` attributes to + Elasticsearch database client spans. Versions 8.10 and later of the client have native support for + OpenTelemetry. +library_link: https://www.elastic.co/docs/reference/elasticsearch/clients/java diff --git a/instrumentation/elasticsearch/elasticsearch-rest-5.0/javaagent/build.gradle.kts b/instrumentation/elasticsearch/elasticsearch-rest-5.0/javaagent/build.gradle.kts index 122908d82e05..2fb1d5df5d46 100644 --- a/instrumentation/elasticsearch/elasticsearch-rest-5.0/javaagent/build.gradle.kts +++ b/instrumentation/elasticsearch/elasticsearch-rest-5.0/javaagent/build.gradle.kts @@ -42,4 +42,16 @@ tasks { systemProperty("collectMetadata", findProperty("collectMetadata")?.toString() ?: "false") } + + val testStableSemconv by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath + + jvmArgs("-Dotel.semconv-stability.opt-in=database") + systemProperty("metadataConfig", "otel.semconv-stability.opt-in=database") + } + + check { + dependsOn(testStableSemconv) + } } diff --git a/instrumentation/elasticsearch/elasticsearch-rest-5.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/elasticsearch/rest/v5_0/ElasticsearchRest5Test.java b/instrumentation/elasticsearch/elasticsearch-rest-5.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/elasticsearch/rest/v5_0/ElasticsearchRest5Test.java index fc0b9313f7b6..9617787c383b 100644 --- a/instrumentation/elasticsearch/elasticsearch-rest-5.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/elasticsearch/rest/v5_0/ElasticsearchRest5Test.java +++ b/instrumentation/elasticsearch/elasticsearch-rest-5.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/elasticsearch/rest/v5_0/ElasticsearchRest5Test.java @@ -5,6 +5,7 @@ package io.opentelemetry.javaagent.instrumentation.elasticsearch.rest.v5_0; +import static io.opentelemetry.instrumentation.testing.junit.db.DbClientMetricsTestUtil.assertDurationMetric; import static io.opentelemetry.instrumentation.testing.junit.db.SemconvStabilityUtil.maybeStable; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; import static io.opentelemetry.semconv.HttpAttributes.HTTP_REQUEST_METHOD; @@ -14,6 +15,7 @@ import static io.opentelemetry.semconv.ServerAttributes.SERVER_PORT; import static io.opentelemetry.semconv.UrlAttributes.URL_FULL; import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_SYSTEM; +import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_SYSTEM_NAME; import static org.assertj.core.api.Assertions.assertThat; import com.fasterxml.jackson.databind.ObjectMapper; @@ -115,6 +117,13 @@ void elasticsearchStatus() throws IOException { equalTo(NETWORK_PROTOCOL_VERSION, "1.1"), equalTo(URL_FULL, httpHost.toURI() + "/_cluster/health"), equalTo(HTTP_RESPONSE_STATUS_CODE, 200)))); + + assertDurationMetric( + testing, + "io.opentelemetry.elasticsearch-rest-5.0", + DB_SYSTEM_NAME, + SERVER_ADDRESS, + SERVER_PORT); } @Test diff --git a/instrumentation/elasticsearch/elasticsearch-rest-5.0/metadata.yaml b/instrumentation/elasticsearch/elasticsearch-rest-5.0/metadata.yaml index 091cbe4861c3..de0261adb368 100644 --- a/instrumentation/elasticsearch/elasticsearch-rest-5.0/metadata.yaml +++ b/instrumentation/elasticsearch/elasticsearch-rest-5.0/metadata.yaml @@ -1,8 +1,15 @@ -description: This instrumentation enables tracing for Elasticsearch REST clients. +description: This instrumentation enables database client spans and database client metrics for Elasticsearch REST clients. +library_link: https://www.elastic.co/guide/en/elasticsearch/client/java-rest configurations: - name: otel.instrumentation.elasticsearch.capture-search-query - description: | + description: > Enable the capture of search query bodies. It is important to note that Elasticsearch queries may contain personal or sensitive information. type: boolean default: false + - name: otel.instrumentation.http.known-methods + description: > + Configures the instrumentation to recognize an alternative set of HTTP request methods. All + other methods will be treated as `_OTHER`. + type: list + default: "CONNECT,DELETE,GET,HEAD,OPTIONS,PATCH,POST,PUT,TRACE" diff --git a/instrumentation/elasticsearch/elasticsearch-rest-6.4/javaagent/build.gradle.kts b/instrumentation/elasticsearch/elasticsearch-rest-6.4/javaagent/build.gradle.kts index 2fdeea1a6e55..072b1bb10e5a 100644 --- a/instrumentation/elasticsearch/elasticsearch-rest-6.4/javaagent/build.gradle.kts +++ b/instrumentation/elasticsearch/elasticsearch-rest-6.4/javaagent/build.gradle.kts @@ -36,9 +36,20 @@ dependencies { } tasks { - test { + withType().configureEach { usesService(gradle.sharedServices.registrations["testcontainersBuildService"].service) - systemProperty("collectMetadata", findProperty("collectMetadata")?.toString() ?: "false") } + + val testStableSemconv by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath + + jvmArgs("-Dotel.semconv-stability.opt-in=database") + systemProperty("metadataConfig", "otel.semconv-stability.opt-in=database") + } + + check { + dependsOn(testStableSemconv) + } } diff --git a/instrumentation/elasticsearch/elasticsearch-rest-6.4/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/elasticsearch/rest/v6_4/ElasticsearchRest6Test.java b/instrumentation/elasticsearch/elasticsearch-rest-6.4/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/elasticsearch/rest/v6_4/ElasticsearchRest6Test.java index f9c4f8291323..35ba08f6d115 100644 --- a/instrumentation/elasticsearch/elasticsearch-rest-6.4/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/elasticsearch/rest/v6_4/ElasticsearchRest6Test.java +++ b/instrumentation/elasticsearch/elasticsearch-rest-6.4/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/elasticsearch/rest/v6_4/ElasticsearchRest6Test.java @@ -5,6 +5,7 @@ package io.opentelemetry.javaagent.instrumentation.elasticsearch.rest.v6_4; +import static io.opentelemetry.instrumentation.testing.junit.db.DbClientMetricsTestUtil.assertDurationMetric; import static io.opentelemetry.instrumentation.testing.junit.db.SemconvStabilityUtil.maybeStable; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; import static io.opentelemetry.semconv.HttpAttributes.HTTP_REQUEST_METHOD; @@ -14,6 +15,7 @@ import static io.opentelemetry.semconv.ServerAttributes.SERVER_PORT; import static io.opentelemetry.semconv.UrlAttributes.URL_FULL; import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_SYSTEM; +import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_SYSTEM_NAME; import static org.assertj.core.api.Assertions.assertThat; import com.fasterxml.jackson.databind.ObjectMapper; @@ -82,30 +84,36 @@ void elasticsearchStatus() throws IOException { assertThat(result.get("status")).isEqualTo("green"); testing.waitAndAssertTraces( - trace -> { - trace.hasSpansSatisfyingExactly( - span -> - span.hasName("GET") - .hasKind(SpanKind.CLIENT) - .hasNoParent() - .hasAttributesSatisfyingExactly( - equalTo(maybeStable(DB_SYSTEM), "elasticsearch"), - equalTo(HTTP_REQUEST_METHOD, "GET"), - equalTo(SERVER_ADDRESS, httpHost.getHostName()), - equalTo(SERVER_PORT, httpHost.getPort()), - equalTo(URL_FULL, httpHost.toURI() + "/_cluster/health")), - span -> - span.hasName("GET") - .hasKind(SpanKind.CLIENT) - .hasParent(trace.getSpan(0)) - .hasAttributesSatisfyingExactly( - equalTo(SERVER_ADDRESS, httpHost.getHostName()), - equalTo(SERVER_PORT, httpHost.getPort()), - equalTo(HTTP_REQUEST_METHOD, "GET"), - equalTo(NETWORK_PROTOCOL_VERSION, "1.1"), - equalTo(URL_FULL, httpHost.toURI() + "/_cluster/health"), - equalTo(HTTP_RESPONSE_STATUS_CODE, 200L))); - }); + trace -> + trace.hasSpansSatisfyingExactly( + span -> + span.hasName("GET") + .hasKind(SpanKind.CLIENT) + .hasNoParent() + .hasAttributesSatisfyingExactly( + equalTo(maybeStable(DB_SYSTEM), "elasticsearch"), + equalTo(HTTP_REQUEST_METHOD, "GET"), + equalTo(SERVER_ADDRESS, httpHost.getHostName()), + equalTo(SERVER_PORT, httpHost.getPort()), + equalTo(URL_FULL, httpHost.toURI() + "/_cluster/health")), + span -> + span.hasName("GET") + .hasKind(SpanKind.CLIENT) + .hasParent(trace.getSpan(0)) + .hasAttributesSatisfyingExactly( + equalTo(SERVER_ADDRESS, httpHost.getHostName()), + equalTo(SERVER_PORT, httpHost.getPort()), + equalTo(HTTP_REQUEST_METHOD, "GET"), + equalTo(NETWORK_PROTOCOL_VERSION, "1.1"), + equalTo(URL_FULL, httpHost.toURI() + "/_cluster/health"), + equalTo(HTTP_RESPONSE_STATUS_CODE, 200L)))); + + assertDurationMetric( + testing, + "io.opentelemetry.elasticsearch-rest-6.4", + DB_SYSTEM_NAME, + SERVER_ADDRESS, + SERVER_PORT); } @Test @@ -138,10 +146,7 @@ public void onFailure(Exception e) { } }; testing.runWithSpan( - "parent", - () -> { - client.performRequestAsync("GET", "_cluster/health", responseListener); - }); + "parent", () -> client.performRequestAsync("GET", "_cluster/health", responseListener)); countDownLatch.await(); if (exception[0] != null) { diff --git a/instrumentation/elasticsearch/elasticsearch-rest-6.4/metadata.yaml b/instrumentation/elasticsearch/elasticsearch-rest-6.4/metadata.yaml index 224089a16b6f..de0261adb368 100644 --- a/instrumentation/elasticsearch/elasticsearch-rest-6.4/metadata.yaml +++ b/instrumentation/elasticsearch/elasticsearch-rest-6.4/metadata.yaml @@ -1,4 +1,5 @@ -description: This instrumentation enables tracing for Elasticsearch REST clients. +description: This instrumentation enables database client spans and database client metrics for Elasticsearch REST clients. +library_link: https://www.elastic.co/guide/en/elasticsearch/client/java-rest configurations: - name: otel.instrumentation.elasticsearch.capture-search-query description: > @@ -6,3 +7,9 @@ configurations: may contain personal or sensitive information. type: boolean default: false + - name: otel.instrumentation.http.known-methods + description: > + Configures the instrumentation to recognize an alternative set of HTTP request methods. All + other methods will be treated as `_OTHER`. + type: list + default: "CONNECT,DELETE,GET,HEAD,OPTIONS,PATCH,POST,PUT,TRACE" diff --git a/instrumentation/elasticsearch/elasticsearch-rest-7.0/javaagent/build.gradle.kts b/instrumentation/elasticsearch/elasticsearch-rest-7.0/javaagent/build.gradle.kts index 5076b34c3203..dc79d9c89a74 100644 --- a/instrumentation/elasticsearch/elasticsearch-rest-7.0/javaagent/build.gradle.kts +++ b/instrumentation/elasticsearch/elasticsearch-rest-7.0/javaagent/build.gradle.kts @@ -39,9 +39,22 @@ dependencies { } tasks { - test { + withType().configureEach { + systemProperty("testLatestDeps", findProperty("testLatestDeps") as Boolean) usesService(gradle.sharedServices.registrations["testcontainersBuildService"].service) systemProperty("collectMetadata", findProperty("collectMetadata")?.toString() ?: "false") } + + val testStableSemconv by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath + + jvmArgs("-Dotel.semconv-stability.opt-in=database") + systemProperty("metadataConfig", "otel.semconv-stability.opt-in=database") + } + + check { + dependsOn(testStableSemconv) + } } diff --git a/instrumentation/elasticsearch/elasticsearch-rest-7.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/elasticsearch/rest/v7_0/ElasticsearchRest7Test.java b/instrumentation/elasticsearch/elasticsearch-rest-7.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/elasticsearch/rest/v7_0/ElasticsearchRest7Test.java index bde1b70f7d93..42d0dc148584 100644 --- a/instrumentation/elasticsearch/elasticsearch-rest-7.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/elasticsearch/rest/v7_0/ElasticsearchRest7Test.java +++ b/instrumentation/elasticsearch/elasticsearch-rest-7.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/elasticsearch/rest/v7_0/ElasticsearchRest7Test.java @@ -6,6 +6,7 @@ package io.opentelemetry.javaagent.instrumentation.elasticsearch.rest.v7_0; import static io.opentelemetry.instrumentation.testing.GlobalTraceUtil.runWithSpan; +import static io.opentelemetry.instrumentation.testing.junit.db.DbClientMetricsTestUtil.assertDurationMetric; import static io.opentelemetry.instrumentation.testing.junit.db.SemconvStabilityUtil.maybeStable; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; import static io.opentelemetry.semconv.HttpAttributes.HTTP_REQUEST_METHOD; @@ -15,6 +16,7 @@ import static io.opentelemetry.semconv.ServerAttributes.SERVER_PORT; import static io.opentelemetry.semconv.UrlAttributes.URL_FULL; import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_SYSTEM; +import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_SYSTEM_NAME; import static org.assertj.core.api.Assertions.assertThat; import io.opentelemetry.api.trace.SpanKind; @@ -106,6 +108,13 @@ void elasticsearchStatus() throws Exception { equalTo(NETWORK_PROTOCOL_VERSION, "1.1"), equalTo(URL_FULL, httpHost.toURI() + "/_cluster/health"), equalTo(HTTP_RESPONSE_STATUS_CODE, 200L)))); + + assertDurationMetric( + testing, + "io.opentelemetry.elasticsearch-rest-7.0", + DB_SYSTEM_NAME, + SERVER_ADDRESS, + SERVER_PORT); } @Test diff --git a/instrumentation/elasticsearch/elasticsearch-rest-7.0/library/README.md b/instrumentation/elasticsearch/elasticsearch-rest-7.0/library/README.md new file mode 100644 index 000000000000..b455d8575bfd --- /dev/null +++ b/instrumentation/elasticsearch/elasticsearch-rest-7.0/library/README.md @@ -0,0 +1,53 @@ +# Library Instrumentation for Elasticsearch REST Client version 7.0 and higher + +Provides OpenTelemetry instrumentation for the [Elasticsearch REST Client](https://www.elastic.co/guide/en/elasticsearch/client/java-rest/current/java-rest-high.html), +enabling database client spans and metrics. + +## Quickstart + +### Add these dependencies to your project + +Replace `OPENTELEMETRY_VERSION` with the [latest release](https://central.sonatype.com/artifact/io.opentelemetry.instrumentation/opentelemetry-elasticsearch-rest-7.0). + +For Maven, add to your `pom.xml` dependencies: + +```xml + + + io.opentelemetry.instrumentation + opentelemetry-elasticsearch-rest-7.0 + OPENTELEMETRY_VERSION + + +``` + +For Gradle, add to your dependencies: + +```kotlin +implementation("io.opentelemetry.instrumentation:opentelemetry-elasticsearch-rest-7.0:OPENTELEMETRY_VERSION") +``` + +### Usage + +```java +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.instrumentation.elasticsearch.rest.v7_0.ElasticsearchRest7Telemetry; +import org.apache.http.HttpHost; +import org.elasticsearch.client.RestClient; + +// ... + +// Get an OpenTelemetry instance +OpenTelemetry openTelemetry = ...; + +// Create an ElasticsearchRest7Telemetry instance +ElasticsearchRest7Telemetry telemetry = ElasticsearchRest7Telemetry.create(openTelemetry); + +// Create a RestClient +RestClient restClient = RestClient.builder(new HttpHost("localhost", 9200, "http")).build(); + +// Wrap the client +RestClient tracedClient = telemetry.wrap(restClient); + +// ... use the tracedClient to make requests +``` diff --git a/instrumentation/elasticsearch/elasticsearch-rest-7.0/metadata.yaml b/instrumentation/elasticsearch/elasticsearch-rest-7.0/metadata.yaml index 224089a16b6f..de0261adb368 100644 --- a/instrumentation/elasticsearch/elasticsearch-rest-7.0/metadata.yaml +++ b/instrumentation/elasticsearch/elasticsearch-rest-7.0/metadata.yaml @@ -1,4 +1,5 @@ -description: This instrumentation enables tracing for Elasticsearch REST clients. +description: This instrumentation enables database client spans and database client metrics for Elasticsearch REST clients. +library_link: https://www.elastic.co/guide/en/elasticsearch/client/java-rest configurations: - name: otel.instrumentation.elasticsearch.capture-search-query description: > @@ -6,3 +7,9 @@ configurations: may contain personal or sensitive information. type: boolean default: false + - name: otel.instrumentation.http.known-methods + description: > + Configures the instrumentation to recognize an alternative set of HTTP request methods. All + other methods will be treated as `_OTHER`. + type: list + default: "CONNECT,DELETE,GET,HEAD,OPTIONS,PATCH,POST,PUT,TRACE" diff --git a/instrumentation/elasticsearch/elasticsearch-transport-5.0/javaagent/build.gradle.kts b/instrumentation/elasticsearch/elasticsearch-transport-5.0/javaagent/build.gradle.kts index df0e3a7d4f36..53ce0034efeb 100644 --- a/instrumentation/elasticsearch/elasticsearch-transport-5.0/javaagent/build.gradle.kts +++ b/instrumentation/elasticsearch/elasticsearch-transport-5.0/javaagent/build.gradle.kts @@ -58,16 +58,22 @@ tasks { } val testStableSemconv by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath + jvmArgs("-Dotel.semconv-stability.opt-in=database") systemProperty("metadataConfig", "otel.semconv-stability.opt-in=database") } val testExperimental by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath + jvmArgs("-Dotel.instrumentation.elasticsearch.experimental-span-attributes=true") systemProperty("metadataConfig", "otel.instrumentation.elasticsearch.experimental-span-attributes=true") } check { - dependsOn(testStableSemconv, testExperimental) + dependsOn(testing.suites, testStableSemconv, testExperimental) } } diff --git a/instrumentation/elasticsearch/elasticsearch-transport-5.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/elasticsearch/transport/v5_0/Elasticsearch5NodeClientTest.java b/instrumentation/elasticsearch/elasticsearch-transport-5.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/elasticsearch/transport/v5_0/Elasticsearch5NodeClientTest.java index 0bd700ef2383..e24c6efc67df 100644 --- a/instrumentation/elasticsearch/elasticsearch-transport-5.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/elasticsearch/transport/v5_0/Elasticsearch5NodeClientTest.java +++ b/instrumentation/elasticsearch/elasticsearch-transport-5.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/elasticsearch/transport/v5_0/Elasticsearch5NodeClientTest.java @@ -19,6 +19,7 @@ import org.elasticsearch.transport.Netty3Plugin; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -82,4 +83,9 @@ public Client client() { protected boolean hasWriteVersion() { return false; } + + @Test + void testDurationMetric() throws Exception { + metricAssertion("io.opentelemetry.elasticsearch-transport-5.0"); + } } diff --git a/instrumentation/elasticsearch/elasticsearch-transport-5.0/metadata.yaml b/instrumentation/elasticsearch/elasticsearch-transport-5.0/metadata.yaml index c327ced42d23..e2f0f47bd0f5 100644 --- a/instrumentation/elasticsearch/elasticsearch-transport-5.0/metadata.yaml +++ b/instrumentation/elasticsearch/elasticsearch-transport-5.0/metadata.yaml @@ -1,8 +1,22 @@ description: > - This instrumentation enables client spans for Elasticsearch transport client requests. Each call - produces a span named after the Elasticsearch action, enriched with transport-specific attributes. + This instrumentation enables database client spans and database client metrics for Elasticsearch + transport client requests. Each call produces a span named after the Elasticsearch action, + enriched with transport-specific attributes. +library_link: https://www.elastic.co/guide/en/elasticsearch/client/java-api/ configurations: - name: otel.instrumentation.elasticsearch.experimental-span-attributes - description: Enable the capture of experimental span attributes. + description: > + Enable the capture of the experimental span attributes `elasticsearch.action`, + `elasticsearch.id`, `elasticsearch.request`, `elasticsearch.request.indices`, + `elasticsearch.request.write.routing`, `elasticsearch.request.write.type`, + `elasticsearch.response.status`, `elasticsearch.shard.replication.failed`, + `elasticsearch.shard.replication.successful`, `elasticsearch.shard.replication.total`, + `elasticsearch.type`, and `elasticsearch.version`. type: boolean default: false + - name: otel.instrumentation.http.known-methods + description: > + Configures the instrumentation to recognize an alternative set of HTTP request methods. All + other methods will be treated as `_OTHER`. + type: list + default: "CONNECT,DELETE,GET,HEAD,OPTIONS,PATCH,POST,PUT,TRACE" diff --git a/instrumentation/elasticsearch/elasticsearch-transport-5.3/javaagent/build.gradle.kts b/instrumentation/elasticsearch/elasticsearch-transport-5.3/javaagent/build.gradle.kts index 33473220b647..321b531d96b4 100644 --- a/instrumentation/elasticsearch/elasticsearch-transport-5.3/javaagent/build.gradle.kts +++ b/instrumentation/elasticsearch/elasticsearch-transport-5.3/javaagent/build.gradle.kts @@ -77,11 +77,17 @@ tasks { } val testStableSemconv by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath + jvmArgs("-Dotel.semconv-stability.opt-in=database,code") systemProperty("metadataConfig", "otel.semconv-stability.opt-in=database") } val testExperimental by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath + jvmArgs("-Dotel.instrumentation.elasticsearch.experimental-span-attributes=true") systemProperty("metadataConfig", "otel.instrumentation.elasticsearch.experimental-span-attributes=true") } diff --git a/instrumentation/elasticsearch/elasticsearch-transport-5.3/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/elasticsearch/transport/v5_3/Elasticsearch53NodeClientTest.java b/instrumentation/elasticsearch/elasticsearch-transport-5.3/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/elasticsearch/transport/v5_3/Elasticsearch53NodeClientTest.java index 13eafa5b4d47..ae5175535104 100644 --- a/instrumentation/elasticsearch/elasticsearch-transport-5.3/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/elasticsearch/transport/v5_3/Elasticsearch53NodeClientTest.java +++ b/instrumentation/elasticsearch/elasticsearch-transport-5.3/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/elasticsearch/transport/v5_3/Elasticsearch53NodeClientTest.java @@ -20,6 +20,7 @@ import org.elasticsearch.transport.Netty3Plugin; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -89,4 +90,9 @@ static void cleanUp() throws Exception { public Client client() { return client; } + + @Test + void testDurationMetric() throws Exception { + metricAssertion("io.opentelemetry.elasticsearch-transport-5.3"); + } } diff --git a/instrumentation/elasticsearch/elasticsearch-transport-5.3/metadata.yaml b/instrumentation/elasticsearch/elasticsearch-transport-5.3/metadata.yaml index c327ced42d23..0a739c76095c 100644 --- a/instrumentation/elasticsearch/elasticsearch-transport-5.3/metadata.yaml +++ b/instrumentation/elasticsearch/elasticsearch-transport-5.3/metadata.yaml @@ -1,8 +1,18 @@ description: > - This instrumentation enables client spans for Elasticsearch transport client requests. Each call - produces a span named after the Elasticsearch action, enriched with transport-specific attributes. + This instrumentation enables database client spans and database client metrics for Elasticsearch + transport client requests. Each call produces a span named after the Elasticsearch action, + enriched with transport-specific attributes. +library_link: https://www.elastic.co/guide/en/elasticsearch/client/java-api/ configurations: - name: otel.instrumentation.elasticsearch.experimental-span-attributes - description: Enable the capture of experimental span attributes. + description: > + Enable the capture of `elasticsearch.action`, `elasticsearch.id`, `elasticsearch.request`, + `elasticsearch.request.indices`, `elasticsearch.request.search.types`, + `elasticsearch.request.write.type`, `elasticsearch.request.write.version`, + `elasticsearch.response.status`, `elasticsearch.shard.broadcast.failed`, + `elasticsearch.shard.broadcast.successful`, `elasticsearch.shard.broadcast.total`, + `elasticsearch.shard.replication.failed`, `elasticsearch.shard.replication.successful`, + `elasticsearch.shard.replication.total`, `elasticsearch.type`, and + `elasticsearch.version` experimental span attributes. type: boolean default: false diff --git a/instrumentation/elasticsearch/elasticsearch-transport-6.0/metadata.yaml b/instrumentation/elasticsearch/elasticsearch-transport-6.0/metadata.yaml index c327ced42d23..9236d3480708 100644 --- a/instrumentation/elasticsearch/elasticsearch-transport-6.0/metadata.yaml +++ b/instrumentation/elasticsearch/elasticsearch-transport-6.0/metadata.yaml @@ -1,8 +1,16 @@ description: > - This instrumentation enables client spans for Elasticsearch transport client requests. Each call - produces a span named after the Elasticsearch action, enriched with transport-specific attributes. + This instrumentation enables database client spans and database client metrics for Elasticsearch + transport client requests. Each call produces a span named after the Elasticsearch action, + enriched with transport-specific attributes. +library_link: https://www.elastic.co/guide/en/elasticsearch/client/java-api/ configurations: - name: otel.instrumentation.elasticsearch.experimental-span-attributes - description: Enable the capture of experimental span attributes. + description: > + Enable the capture of `elasticsearch.action`, `elasticsearch.id`, `elasticsearch.request`, + `elasticsearch.request.indices`, `elasticsearch.request.write.type`, + `elasticsearch.request.write.version`, `elasticsearch.response.status`, + `elasticsearch.shard.replication.failed`, `elasticsearch.shard.replication.successful`, + `elasticsearch.shard.replication.total`, `elasticsearch.type`, and + `elasticsearch.version` experimental span attributes. type: boolean default: false diff --git a/instrumentation/elasticsearch/elasticsearch-transport-6.0/testing/src/main/java/io/opentelemetry/javaagent/instrumentation/elasticsearch/transport/v6_0/AbstractElasticsearch6NodeClientTest.java b/instrumentation/elasticsearch/elasticsearch-transport-6.0/testing/src/main/java/io/opentelemetry/javaagent/instrumentation/elasticsearch/transport/v6_0/AbstractElasticsearch6NodeClientTest.java index 614ed4c66371..e00fd3d10588 100644 --- a/instrumentation/elasticsearch/elasticsearch-transport-6.0/testing/src/main/java/io/opentelemetry/javaagent/instrumentation/elasticsearch/transport/v6_0/AbstractElasticsearch6NodeClientTest.java +++ b/instrumentation/elasticsearch/elasticsearch-transport-6.0/testing/src/main/java/io/opentelemetry/javaagent/instrumentation/elasticsearch/transport/v6_0/AbstractElasticsearch6NodeClientTest.java @@ -17,6 +17,7 @@ import org.elasticsearch.node.Node; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -98,4 +99,9 @@ protected void waitYellowStatus() { .execute() .actionGet(TIMEOUT); } + + @Test + void testDurationMetric() throws Exception { + metricAssertion("io.opentelemetry.elasticsearch-transport-6.0"); + } } diff --git a/instrumentation/elasticsearch/elasticsearch-transport-common/testing/src/main/java/io/opentelemetry/javaagent/instrumentation/elasticsearch/transport/AbstractElasticsearchNodeClientTest.java b/instrumentation/elasticsearch/elasticsearch-transport-common/testing/src/main/java/io/opentelemetry/javaagent/instrumentation/elasticsearch/transport/AbstractElasticsearchNodeClientTest.java index 5988fd9fd9cc..14c15485a95e 100644 --- a/instrumentation/elasticsearch/elasticsearch-transport-common/testing/src/main/java/io/opentelemetry/javaagent/instrumentation/elasticsearch/transport/AbstractElasticsearchNodeClientTest.java +++ b/instrumentation/elasticsearch/elasticsearch-transport-common/testing/src/main/java/io/opentelemetry/javaagent/instrumentation/elasticsearch/transport/AbstractElasticsearchNodeClientTest.java @@ -7,8 +7,11 @@ import static io.opentelemetry.api.common.AttributeKey.longKey; import static io.opentelemetry.api.common.AttributeKey.stringKey; +import static io.opentelemetry.instrumentation.testing.junit.db.DbClientMetricsTestUtil.assertDurationMetric; import static io.opentelemetry.instrumentation.testing.junit.db.SemconvStabilityUtil.maybeStable; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; +import static io.opentelemetry.semconv.DbAttributes.DB_OPERATION_NAME; +import static io.opentelemetry.semconv.DbAttributes.DB_SYSTEM_NAME; import static io.opentelemetry.semconv.ErrorAttributes.ERROR_TYPE; import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_OPERATION; import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_SYSTEM; @@ -276,4 +279,14 @@ void elasticsearchGet() { protected boolean hasWriteVersion() { return true; } + + protected void metricAssertion(String instrumentationName) throws Exception { + ClusterHealthStatus clusterHealthStatus = + testing.runWithSpan("parent", this::clusterHealthSync); + + assertThat(clusterHealthStatus.name()).isEqualTo("GREEN"); + testing.waitForTraces(1); + + assertDurationMetric(testing, instrumentationName, DB_SYSTEM_NAME, DB_OPERATION_NAME); + } } diff --git a/instrumentation/executors/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/executors/JavaExecutorInstrumentation.java b/instrumentation/executors/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/executors/JavaExecutorInstrumentation.java index 46957bf399e8..3d9276fe0eaf 100644 --- a/instrumentation/executors/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/executors/JavaExecutorInstrumentation.java +++ b/instrumentation/executors/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/executors/JavaExecutorInstrumentation.java @@ -17,6 +17,7 @@ import io.opentelemetry.context.Context; import io.opentelemetry.instrumentation.api.util.VirtualField; +import io.opentelemetry.javaagent.bootstrap.CallDepth; import io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge; import io.opentelemetry.javaagent.bootstrap.executors.ContextPropagatingRunnable; import io.opentelemetry.javaagent.bootstrap.executors.ExecutorAdviceHelper; @@ -89,7 +90,13 @@ public static class SetExecuteRunnableStateAdvice { @Advice.OnMethodEnter(suppress = Throwable.class) public static PropagatedContext enterJobSubmit( - @Advice.Argument(value = 0, readOnly = false) Runnable task) { + @Advice.This Object executor, + @Advice.Argument(value = 0, readOnly = false) Runnable task, + @Advice.Local("otelCallDepth") CallDepth callDepth) { + callDepth = CallDepth.forClass(executor.getClass()); + if (callDepth.getAndIncrement() > 0) { + return null; + } Context context = Java8BytecodeBridge.currentContext(); if (!ExecutorAdviceHelper.shouldPropagateContext(context, task)) { return null; @@ -107,7 +114,11 @@ public static PropagatedContext enterJobSubmit( public static void exitJobSubmit( @Advice.Argument(0) Runnable task, @Advice.Enter PropagatedContext propagatedContext, - @Advice.Thrown Throwable throwable) { + @Advice.Thrown Throwable throwable, + @Advice.Local("otelCallDepth") CallDepth callDepth) { + if (callDepth.decrementAndGet() > 0) { + return; + } VirtualField virtualField = VirtualField.find(Runnable.class, PropagatedContext.class); ExecutorAdviceHelper.cleanUpAfterSubmit(propagatedContext, throwable, virtualField, task); @@ -144,7 +155,13 @@ public static class SetSubmitRunnableStateAdvice { @Advice.OnMethodEnter(suppress = Throwable.class) public static PropagatedContext enterJobSubmit( - @Advice.Argument(value = 0, readOnly = false) Runnable task) { + @Advice.This Object executor, + @Advice.Argument(value = 0, readOnly = false) Runnable task, + @Advice.Local("otelCallDepth") CallDepth callDepth) { + callDepth = CallDepth.forClass(executor.getClass()); + if (callDepth.getAndIncrement() > 0) { + return null; + } Context context = Java8BytecodeBridge.currentContext(); if (ExecutorAdviceHelper.shouldPropagateContext(context, task)) { VirtualField virtualField = @@ -159,7 +176,11 @@ public static void exitJobSubmit( @Advice.Argument(0) Runnable task, @Advice.Enter PropagatedContext propagatedContext, @Advice.Thrown Throwable throwable, - @Advice.Return Future future) { + @Advice.Return Future future, + @Advice.Local("otelCallDepth") CallDepth callDepth) { + if (callDepth.decrementAndGet() > 0) { + return; + } if (propagatedContext != null && future != null) { VirtualField, PropagatedContext> virtualField = VirtualField.find(Future.class, PropagatedContext.class); @@ -175,7 +196,14 @@ public static void exitJobSubmit( public static class SetCallableStateAdvice { @Advice.OnMethodEnter(suppress = Throwable.class) - public static PropagatedContext enterJobSubmit(@Advice.Argument(0) Callable task) { + public static PropagatedContext enterJobSubmit( + @Advice.This Object executor, + @Advice.Argument(0) Callable task, + @Advice.Local("otelCallDepth") CallDepth callDepth) { + callDepth = CallDepth.forClass(executor.getClass()); + if (callDepth.getAndIncrement() > 0) { + return null; + } Context context = Java8BytecodeBridge.currentContext(); if (ExecutorAdviceHelper.shouldPropagateContext(context, task)) { VirtualField, PropagatedContext> virtualField = @@ -190,7 +218,11 @@ public static void exitJobSubmit( @Advice.Argument(0) Callable task, @Advice.Enter PropagatedContext propagatedContext, @Advice.Thrown Throwable throwable, - @Advice.Return Future future) { + @Advice.Return Future future, + @Advice.Local("otelCallDepth") CallDepth callDepth) { + if (callDepth.decrementAndGet() > 0) { + return; + } if (propagatedContext != null && future != null) { VirtualField, PropagatedContext> virtualField = VirtualField.find(Future.class, PropagatedContext.class); @@ -207,11 +239,18 @@ public static class SetCallableStateForCallableCollectionAdvice { @Advice.OnMethodEnter(suppress = Throwable.class) public static Collection submitEnter( - @Advice.Argument(0) Collection> tasks) { + @Advice.This Object executor, + @Advice.Argument(0) Collection> tasks, + @Advice.Local("otelCallDepth") CallDepth callDepth) { if (tasks == null) { return Collections.emptyList(); } + callDepth = CallDepth.forClass(executor.getClass()); + if (callDepth.getAndIncrement() > 0) { + return Collections.emptyList(); + } + Context context = Java8BytecodeBridge.currentContext(); for (Callable task : tasks) { if (ExecutorAdviceHelper.shouldPropagateContext(context, task)) { @@ -228,7 +267,13 @@ public static Collection submitEnter( @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) public static void submitExit( - @Advice.Enter Collection> tasks, @Advice.Thrown Throwable throwable) { + @Advice.Enter Collection> tasks, + @Advice.Thrown Throwable throwable, + @Advice.Local("otelCallDepth") CallDepth callDepth) { + if (callDepth.decrementAndGet() > 0) { + return; + } + /* Note1: invokeAny doesn't return any futures so all we need to do for it is to make sure we close all scopes in case of an exception. diff --git a/instrumentation/executors/metadata.yaml b/instrumentation/executors/metadata.yaml index d2b8978137a6..ac0dde35cb95 100644 --- a/instrumentation/executors/metadata.yaml +++ b/instrumentation/executors/metadata.yaml @@ -4,7 +4,7 @@ description: > is submitted, the current context is captured and bound to the task. Then, when the task eventually runs, even if it’s on a different thread, the instrumentation reactivates that context, enabling consistent correlation across concurrent and asynchronous workflows. - +library_link: https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/Executor.html configurations: - name: otel.instrumentation.executors.include description: List of Executor subclasses to be instrumented. diff --git a/instrumentation/external-annotations/javaagent/build.gradle.kts b/instrumentation/external-annotations/javaagent/build.gradle.kts index 40241cf2c0ed..86111211c8fe 100644 --- a/instrumentation/external-annotations/javaagent/build.gradle.kts +++ b/instrumentation/external-annotations/javaagent/build.gradle.kts @@ -36,6 +36,9 @@ dependencies { tasks { val testIncludeProperty by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath + filter { includeTestsMatching("ConfiguredTraceAnnotationsTest") } @@ -44,6 +47,9 @@ tasks { } val testExcludeMethodsProperty by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath + filter { includeTestsMatching("TracedMethodsExclusionTest") } @@ -61,7 +67,6 @@ tasks { } check { - dependsOn(testIncludeProperty) - dependsOn(testExcludeMethodsProperty) + dependsOn(testIncludeProperty, testExcludeMethodsProperty) } } diff --git a/instrumentation/finagle-http-23.11/metadata.yaml b/instrumentation/finagle-http-23.11/metadata.yaml index 44f7205b4ae5..7a0347ec55c3 100644 --- a/instrumentation/finagle-http-23.11/metadata.yaml +++ b/instrumentation/finagle-http-23.11/metadata.yaml @@ -2,3 +2,4 @@ description: > This instrumentation for Finagle HTTP clients and servers ensures that telemetry is correctly generated by the underlying Netty instrumentation. It augments existing telemetry by bridging the gap between Finagle's abstractions and Netty's pipeline, primarily for context propagation. +library_link: https://github.com/twitter/finagle diff --git a/instrumentation/finatra-2.9/javaagent/build.gradle.kts b/instrumentation/finatra-2.9/javaagent/build.gradle.kts index 9749ad1c57bc..cbf109a0e37e 100644 --- a/instrumentation/finatra-2.9/javaagent/build.gradle.kts +++ b/instrumentation/finatra-2.9/javaagent/build.gradle.kts @@ -89,3 +89,16 @@ tasks { systemProperty("collectMetadata", findProperty("collectMetadata")?.toString() ?: "false") } } + +if (findProperty("testLatestDeps") as Boolean) { + configurations.named("latestDepTestRuntimeClasspath") { + resolutionStrategy { + eachDependency { + // finatra 24.2.0 doesn't work with jackson 2.20.0 + if (requested.group.startsWith("com.fasterxml.jackson")) { + useVersion("2.19.2") + } + } + } + } +} diff --git a/instrumentation/finatra-2.9/metadata.yaml b/instrumentation/finatra-2.9/metadata.yaml index 366d7524d9fb..ce91068a77b8 100644 --- a/instrumentation/finatra-2.9/metadata.yaml +++ b/instrumentation/finatra-2.9/metadata.yaml @@ -1 +1,2 @@ description: This instrumentation for the Finatra web framework augments the telemetry generated by the underlying Netty instrumentation. It provides more specific, high-level context, such as route information, to the spans generated by Netty. +library_link: https://github.com/twitter/finatra diff --git a/instrumentation/geode-1.4/javaagent/build.gradle.kts b/instrumentation/geode-1.4/javaagent/build.gradle.kts index dd436deef63f..32e606d2b127 100644 --- a/instrumentation/geode-1.4/javaagent/build.gradle.kts +++ b/instrumentation/geode-1.4/javaagent/build.gradle.kts @@ -17,9 +17,20 @@ dependencies { annotationProcessor("com.google.auto.value:auto-value") } +val collectMetadata = findProperty("collectMetadata")?.toString() ?: "false" + tasks { val testStableSemconv by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath + jvmArgs("-Dotel.semconv-stability.opt-in=database") + systemProperty("collectMetadata", collectMetadata) + systemProperty("metadataConfig", "otel.semconv-stability.opt-in=database") + } + + test { + systemProperty("collectMetadata", collectMetadata) } check { diff --git a/instrumentation/geode-1.4/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/geode/PutGetTest.java b/instrumentation/geode-1.4/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/geode/PutGetTest.java index 26c71e418a6d..6689844df13a 100644 --- a/instrumentation/geode-1.4/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/geode/PutGetTest.java +++ b/instrumentation/geode-1.4/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/geode/PutGetTest.java @@ -5,12 +5,16 @@ package io.opentelemetry.javaagent.instrumentation.geode; +import static io.opentelemetry.instrumentation.testing.junit.db.DbClientMetricsTestUtil.assertDurationMetric; import static io.opentelemetry.instrumentation.testing.junit.db.SemconvStabilityUtil.maybeStable; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_NAME; +import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_NAMESPACE; import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_OPERATION; +import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_OPERATION_NAME; import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_STATEMENT; import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_SYSTEM; +import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_SYSTEM_NAME; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -52,6 +56,15 @@ private static Stream provideParameters() { Arguments.of("One", Integer.valueOf(1))); } + @Test + @SuppressWarnings("deprecation") // using deprecated semconv + void testDurationMetric() { + region.put("key", "value"); + + assertDurationMetric( + testing, "io.opentelemetry.geode-1.4", DB_SYSTEM_NAME, DB_NAMESPACE, DB_OPERATION_NAME); + } + @ParameterizedTest @MethodSource("provideParameters") void testPutAndGet(Object key, Object value) { diff --git a/instrumentation/geode-1.4/metadata.yaml b/instrumentation/geode-1.4/metadata.yaml new file mode 100644 index 000000000000..9a6927191655 --- /dev/null +++ b/instrumentation/geode-1.4/metadata.yaml @@ -0,0 +1,7 @@ +description: This instrumentation enables database client spans and database client metrics for Apache Geode cache operations. +library_link: https://geode.apache.org/ +configurations: + - name: otel.instrumentation.common.db-statement-sanitizer.enabled + description: Enables statement sanitization for database queries. + type: boolean + default: true diff --git a/instrumentation/google-http-client-1.19/metadata.yaml b/instrumentation/google-http-client-1.19/metadata.yaml new file mode 100644 index 000000000000..7fb2d572d923 --- /dev/null +++ b/instrumentation/google-http-client-1.19/metadata.yaml @@ -0,0 +1,32 @@ +description: This instrumentation enables HTTP client spans and HTTP client metrics for Google HTTP Client requests. +library_link: https://github.com/googleapis/google-http-java-client +configurations: + - name: otel.instrumentation.http.known-methods + description: > + Configures the instrumentation to recognize an alternative set of HTTP request methods. All + other methods will be treated as `_OTHER`. + type: list + default: "CONNECT,DELETE,GET,HEAD,OPTIONS,PATCH,POST,PUT,TRACE" + - name: otel.instrumentation.http.client.capture-request-headers + description: List of HTTP request headers to capture in HTTP client telemetry. + type: list + default: "" + - name: otel.instrumentation.http.client.capture-response-headers + description: List of HTTP response headers to capture in HTTP client telemetry. + type: list + default: "" + - name: otel.instrumentation.common.peer-service-mapping + description: Used to specify a mapping from host names or IP addresses to peer services. + type: map + default: "" + - name: otel.instrumentation.http.client.emit-experimental-telemetry + description: > + Enable the capture of experimental HTTP client telemetry. Adds the `http.request.body.size` + and `http.response.body.size` attributes to spans, and records `http.client.request.size` and + `http.client.response.size` metrics. + type: boolean + default: false + - name: otel.instrumentation.http.client.experimental.redact-query-parameters + description: Redact sensitive URL parameters. See https://opentelemetry.io/docs/specs/semconv/http/http-spans. + type: boolean + default: true diff --git a/instrumentation/grails-3.0/javaagent/build.gradle.kts b/instrumentation/grails-3.0/javaagent/build.gradle.kts index f21870dbf652..7f14d382c89c 100644 --- a/instrumentation/grails-3.0/javaagent/build.gradle.kts +++ b/instrumentation/grails-3.0/javaagent/build.gradle.kts @@ -81,5 +81,8 @@ tasks { jvmArgs("--add-opens=java.base/java.lang=ALL-UNNAMED") jvmArgs("-XX:+IgnoreUnrecognizedVMOptions") jvmArgs("-Dotel.instrumentation.common.experimental.controller-telemetry.enabled=true") + + systemProperty("collectMetadata", findProperty("collectMetadata")?.toString() ?: "false") + systemProperty("metadataConfig", "otel.instrumentation.common.experimental.controller-telemetry.enabled=true") } } diff --git a/instrumentation/grails-3.0/metadata.yaml b/instrumentation/grails-3.0/metadata.yaml new file mode 100644 index 000000000000..8277b165a4f2 --- /dev/null +++ b/instrumentation/grails-3.0/metadata.yaml @@ -0,0 +1,9 @@ +description: > + This instrumentation enriches existing HTTP server spans with HTTP route information, and + optionally enables experimental controller (INTERNAL) spans for Grails applications. +library_link: https://grails.apache.org/ +configurations: + - name: otel.instrumentation.common.experimental.controller-telemetry.enabled + description: Enables the creation of experimental controller (INTERNAL) spans. + type: boolean + default: false diff --git a/instrumentation/graphql-java/graphql-java-12.0/javaagent/build.gradle.kts b/instrumentation/graphql-java/graphql-java-12.0/javaagent/build.gradle.kts index 9a24a559281a..d8f20efe533e 100644 --- a/instrumentation/graphql-java/graphql-java-12.0/javaagent/build.gradle.kts +++ b/instrumentation/graphql-java/graphql-java-12.0/javaagent/build.gradle.kts @@ -27,4 +27,6 @@ dependencies { tasks.withType().configureEach { jvmArgs("-Dotel.instrumentation.graphql.add-operation-name-to-span-name.enabled=true") + + systemProperty("collectMetadata", findProperty("collectMetadata")?.toString() ?: "false") } diff --git a/instrumentation/graphql-java/graphql-java-12.0/library/README.md b/instrumentation/graphql-java/graphql-java-12.0/library/README.md index 447473d1df20..7c1ff1c7b718 100644 --- a/instrumentation/graphql-java/graphql-java-12.0/library/README.md +++ b/instrumentation/graphql-java/graphql-java-12.0/library/README.md @@ -7,7 +7,7 @@ Provides OpenTelemetry instrumentation for [GraphQL Java](https://www.graphql-ja ### Add these dependencies to your project Replace `OPENTELEMETRY_VERSION` with the [latest -release](https://search.maven.org/search?q=g:io.opentelemetry.instrumentation%20AND%20a:opentelemetry-graphql-java-12.0). +release]( https://central.sonatype.com/artifact/io.opentelemetry.instrumentation/opentelemetry-graphql-java-12.0). For Maven, add to your `pom.xml` dependencies: diff --git a/instrumentation/graphql-java/graphql-java-12.0/metadata.yaml b/instrumentation/graphql-java/graphql-java-12.0/metadata.yaml new file mode 100644 index 000000000000..5bdb67a7e26e --- /dev/null +++ b/instrumentation/graphql-java/graphql-java-12.0/metadata.yaml @@ -0,0 +1,14 @@ +description: This instrumentation enables spans for GraphQL Java operations. +library_link: https://www.graphql-java.com/ +configurations: + - name: otel.instrumentation.graphql.query-sanitizer.enabled + description: Enables sanitization of sensitive information from queries so they aren't added as span attributes. + type: boolean + default: true + - name: otel.instrumentation.graphql.add-operation-name-to-span-name.enabled + description: > + Whether GraphQL operation name is added to the span name. WARNING: The GraphQL operation name is + provided by the client and can have high cardinality. Use only when the server is not exposed to malicious + clients. + type: boolean + default: false diff --git a/instrumentation/graphql-java/graphql-java-20.0/javaagent/build.gradle.kts b/instrumentation/graphql-java/graphql-java-20.0/javaagent/build.gradle.kts index 2d1c55a270f0..d9fb4776f0d2 100644 --- a/instrumentation/graphql-java/graphql-java-20.0/javaagent/build.gradle.kts +++ b/instrumentation/graphql-java/graphql-java-20.0/javaagent/build.gradle.kts @@ -23,9 +23,23 @@ dependencies { testImplementation(project(":instrumentation:graphql-java:graphql-java-common:testing")) } -tasks.withType().configureEach { - jvmArgs("-Dotel.instrumentation.graphql.data-fetcher.enabled=true") - jvmArgs("-Dotel.instrumentation.graphql.add-operation-name-to-span-name.enabled=true") +tasks { + withType().configureEach { + jvmArgs("-Dotel.instrumentation.graphql.add-operation-name-to-span-name.enabled=true") + systemProperty("collectMetadata", findProperty("collectMetadata")?.toString() ?: "false") + } + + val testDataFetcher by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath + + jvmArgs("-Dotel.instrumentation.graphql.data-fetcher.enabled=true") + systemProperty("metadataConfig", "otel.instrumentation.graphql.data-fetcher.enabled=true") + } + + check { + dependsOn(testDataFetcher) + } } if (findProperty("testLatestDeps") as Boolean) { diff --git a/instrumentation/graphql-java/graphql-java-20.0/library/README.md b/instrumentation/graphql-java/graphql-java-20.0/library/README.md index 2482e76ee8d9..7c3a122a0653 100644 --- a/instrumentation/graphql-java/graphql-java-20.0/library/README.md +++ b/instrumentation/graphql-java/graphql-java-20.0/library/README.md @@ -7,7 +7,7 @@ Provides OpenTelemetry instrumentation for [GraphQL Java](https://www.graphql-ja ### Add these dependencies to your project Replace `OPENTELEMETRY_VERSION` with the [latest -release](https://search.maven.org/search?q=g:io.opentelemetry.instrumentation%20AND%20a:opentelemetry-graphql-java-12.0). +release]( https://central.sonatype.com/artifact/io.opentelemetry.instrumentation/opentelemetry-graphql-java-12.0). For Maven, add to your `pom.xml` dependencies: diff --git a/instrumentation/graphql-java/graphql-java-20.0/library/build.gradle.kts b/instrumentation/graphql-java/graphql-java-20.0/library/build.gradle.kts index 79f49cf21985..6dd190a344b1 100644 --- a/instrumentation/graphql-java/graphql-java-20.0/library/build.gradle.kts +++ b/instrumentation/graphql-java/graphql-java-20.0/library/build.gradle.kts @@ -14,3 +14,7 @@ if (findProperty("testLatestDeps") as Boolean) { minJavaVersionSupported.set(JavaVersion.VERSION_11) } } + +tasks.withType().configureEach { + jvmArgs("-Dotel.instrumentation.graphql.data-fetcher.enabled=true") +} diff --git a/instrumentation/graphql-java/graphql-java-20.0/metadata.yaml b/instrumentation/graphql-java/graphql-java-20.0/metadata.yaml new file mode 100644 index 000000000000..7601d5633894 --- /dev/null +++ b/instrumentation/graphql-java/graphql-java-20.0/metadata.yaml @@ -0,0 +1,22 @@ +description: This instrumentation enables spans for GraphQL Java operations. +library_link: https://www.graphql-java.com/ +configurations: + - name: otel.instrumentation.graphql.query-sanitizer.enabled + description: Enables sanitization of sensitive information from queries so they aren't added as span attributes. + type: boolean + default: true + - name: otel.instrumentation.graphql.add-operation-name-to-span-name.enabled + description: > + Whether GraphQL operation name is added to the span name. WARNING: The GraphQL operation name is + provided by the client and can have high cardinality. Use only when the server is not exposed to malicious + clients. + type: boolean + default: false + - name: otel.instrumentation.graphql.data-fetcher.enabled + description: Enables span generation for data fetchers. + type: boolean + default: false + - name: otel.instrumentation.graphql.trivial-data-fetcher.enabled + description: Whether to create spans for trivial data fetchers. A trivial data fetcher is one that simply maps data from an object to a field. + type: boolean + default: false diff --git a/instrumentation/graphql-java/graphql-java-common/testing/src/main/java/io/opentelemetry/instrumentation/graphql/AbstractGraphqlTest.java b/instrumentation/graphql-java/graphql-java-common/testing/src/main/java/io/opentelemetry/instrumentation/graphql/AbstractGraphqlTest.java index 351241a569f9..133610aa75dc 100644 --- a/instrumentation/graphql-java/graphql-java-common/testing/src/main/java/io/opentelemetry/instrumentation/graphql/AbstractGraphqlTest.java +++ b/instrumentation/graphql-java/graphql-java-common/testing/src/main/java/io/opentelemetry/instrumentation/graphql/AbstractGraphqlTest.java @@ -44,6 +44,9 @@ @TestInstance(TestInstance.Lifecycle.PER_CLASS) public abstract class AbstractGraphqlTest { + private static final String DATA_FETCHER_PROPERTY = + "otel.instrumentation.graphql.data-fetcher.enabled"; + private final List> books = new ArrayList<>(); private final List> authors = new ArrayList<>(); @@ -58,6 +61,10 @@ protected boolean hasDataFetcherSpans() { return false; } + private boolean includeDataFetcher() { + return Boolean.getBoolean(DATA_FETCHER_PROPERTY) && hasDataFetcherSpans(); + } + @BeforeAll void setup() throws IOException { addAuthor("author-1", "John"); @@ -162,7 +169,7 @@ void successfulQuery() { normalizedQueryEqualsTo( GraphqlIncubatingAttributes.GRAPHQL_DOCUMENT, "query findBookById { bookById(id: ?) { name } }"))); - if (hasDataFetcherSpans()) { + if (includeDataFetcher()) { assertions.add( span -> span.hasName("bookById") @@ -174,7 +181,7 @@ void successfulQuery() { assertions.add( span -> span.hasName("fetchBookById") - .hasParent(trace.getSpan(hasDataFetcherSpans() ? 1 : 0))); + .hasParent(trace.getSpan(includeDataFetcher() ? 1 : 0))); trace.hasSpansSatisfyingExactly(assertions); }); @@ -207,7 +214,7 @@ void successfulQueryWithoutName() { normalizedQueryEqualsTo( AttributeKey.stringKey("graphql.document"), "{ bookById(id: ?) { name } }"))); - if (hasDataFetcherSpans()) { + if (includeDataFetcher()) { assertions.add( span -> span.hasName("bookById") @@ -219,7 +226,7 @@ void successfulQueryWithoutName() { assertions.add( span -> span.hasName("fetchBookById") - .hasParent(trace.getSpan(hasDataFetcherSpans() ? 1 : 0))); + .hasParent(trace.getSpan(includeDataFetcher() ? 1 : 0))); trace.hasSpansSatisfyingExactly(assertions); }); } diff --git a/instrumentation/grizzly-2.3/javaagent/build.gradle.kts b/instrumentation/grizzly-2.3/javaagent/build.gradle.kts index a025c78e44fe..548503d8e316 100644 --- a/instrumentation/grizzly-2.3/javaagent/build.gradle.kts +++ b/instrumentation/grizzly-2.3/javaagent/build.gradle.kts @@ -24,6 +24,7 @@ tasks { // required on jdk17 jvmArgs("--add-opens=java.base/java.lang=ALL-UNNAMED") jvmArgs("-XX:+IgnoreUnrecognizedVMOptions") + systemProperty("collectMetadata", findProperty("collectMetadata")?.toString() ?: "false") } } diff --git a/instrumentation/grizzly-2.3/metadata.yaml b/instrumentation/grizzly-2.3/metadata.yaml new file mode 100644 index 000000000000..dcf6845fafac --- /dev/null +++ b/instrumentation/grizzly-2.3/metadata.yaml @@ -0,0 +1,28 @@ +description: This instrumentation enables HTTP server spans and HTTP server metrics for Grizzly applications. +library_link: https://javaee.github.io/grizzly/httpserverframework.html +configurations: + - name: otel.instrumentation.http.known-methods + description: > + Configures the instrumentation to recognize an alternative set of HTTP request methods. All + other methods will be treated as `_OTHER`. + type: list + default: "CONNECT,DELETE,GET,HEAD,OPTIONS,PATCH,POST,PUT,TRACE" + - name: otel.instrumentation.http.server.capture-request-headers + description: List of HTTP request headers to capture in HTTP server telemetry. + type: list + default: "" + - name: otel.instrumentation.http.server.capture-response-headers + description: List of HTTP response headers to capture in HTTP server telemetry. + type: list + default: "" + - name: otel.instrumentation.common.peer-service-mapping + description: Used to specify a mapping from host names or IP addresses to peer services. + type: map + default: "" + - name: otel.instrumentation.http.server.emit-experimental-telemetry + description: > + Enable the capture of experimental HTTP server telemetry. Adds the `http.request.body.size` and + `http.response.body.size` attributes to spans, and records `http.server.request.body.size` + and `http.server.response.body.size` metrics. + type: boolean + default: false diff --git a/instrumentation/grpc-1.6/javaagent/build.gradle.kts b/instrumentation/grpc-1.6/javaagent/build.gradle.kts index 671fdb315e90..88f75c8b3fc9 100644 --- a/instrumentation/grpc-1.6/javaagent/build.gradle.kts +++ b/instrumentation/grpc-1.6/javaagent/build.gradle.kts @@ -28,6 +28,8 @@ dependencies { testImplementation(project(":instrumentation:grpc-1.6:testing")) } +val collectMetadata = findProperty("collectMetadata")?.toString() ?: "false" + tasks { test { systemProperty("testLatestDeps", findProperty("testLatestDeps") as Boolean) @@ -46,6 +48,34 @@ tasks { classpath = classpath.filter { !it.absolutePath.contains("opentelemetry-grpc-1.6") } + + systemProperty("collectMetadata", collectMetadata) + } + + val testExperimental by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath + + // replicated base config from standard test task + systemProperty("testLatestDeps", findProperty("testLatestDeps") as Boolean) + jvmArgs("-Dotel.javaagent.experimental.thread-propagation-debugger.enabled=false") + jvmArgs("-Dotel.instrumentation.grpc.capture-metadata.client.request=some-client-key") + jvmArgs("-Dotel.instrumentation.grpc.capture-metadata.server.request=some-server-key") + jvmArgs("-Dotel.instrumentation.common.experimental.controller-telemetry.enabled=true") + + // exclude our grpc library instrumentation, the ContextStorageOverride contained within it + // breaks the tests + classpath = classpath.filter { + !it.absolutePath.contains("opentelemetry-grpc-1.6") + } + + systemProperty("collectMetadata", collectMetadata) + systemProperty("metadataConfig", "otel.instrumentation.grpc.experimental-span-attributes=true") + jvmArgs("-Dotel.instrumentation.grpc.experimental-span-attributes=true") + } + + check { + dependsOn(testExperimental) } } diff --git a/instrumentation/grpc-1.6/library/README.md b/instrumentation/grpc-1.6/library/README.md index 4d9ba31291cc..2a9c548a0942 100644 --- a/instrumentation/grpc-1.6/library/README.md +++ b/instrumentation/grpc-1.6/library/README.md @@ -6,7 +6,7 @@ Provides OpenTelemetry instrumentation for [gRPC](https://grpc.io/). ### Add the following dependencies to your project -Replace `OPENTELEMETRY_VERSION` with the [latest release](https://search.maven.org/search?q=g:io.opentelemetry.instrumentation%20AND%20a:opentelemetry-grpc-1.6). +Replace `OPENTELEMETRY_VERSION` with the [latest release]( https://central.sonatype.com/artifact/io.opentelemetry.instrumentation/opentelemetry-grpc-1.6). For Maven, add the following to your `pom.xml` dependencies: diff --git a/instrumentation/grpc-1.6/metadata.yaml b/instrumentation/grpc-1.6/metadata.yaml new file mode 100644 index 000000000000..06687f61e3fd --- /dev/null +++ b/instrumentation/grpc-1.6/metadata.yaml @@ -0,0 +1,25 @@ +description: This instrumentation enables RPC client spans and metrics, and RPC server spans and metrics for gRPC version 1.6 and above. +library_link: https://github.com/grpc/grpc-java +configurations: + - name: otel.instrumentation.grpc.emit-message-events + type: boolean + description: Determines whether to emit a span event for each individual message received and sent. + default: true + - name: otel.instrumentation.grpc.experimental-span-attributes + type: boolean + description: > + Enable the capture of experimental span attributes `grpc.received.message_count`, + `grpc.sent.message_count` and `grpc.canceled`. + default: false + - name: otel.instrumentation.grpc.capture-metadata.client.request + type: list + description: > + A comma-separated list of request metadata keys. gRPC client instrumentation will capture + metadata values corresponding to configured keys as span attributes. + default: '' + - name: otel.instrumentation.grpc.capture-metadata.server.request + type: list + description: > + A comma-separated list of request metadata keys. gRPC server instrumentation will capture + metadata values corresponding to configured keys as span attributes. + default: '' diff --git a/instrumentation/grpc-1.6/testing/build.gradle.kts b/instrumentation/grpc-1.6/testing/build.gradle.kts index 259d70dbb53e..374be72da2b2 100644 --- a/instrumentation/grpc-1.6/testing/build.gradle.kts +++ b/instrumentation/grpc-1.6/testing/build.gradle.kts @@ -31,6 +31,11 @@ tasks { compilerArgs.add("-Xlint:-cast") } } + + named("checkstyleMain") { + // exclude generated classes + exclude("**/example/**") + } } protobuf { diff --git a/instrumentation/grpc-1.6/testing/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/AbstractGrpcStreamingTest.java b/instrumentation/grpc-1.6/testing/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/AbstractGrpcStreamingTest.java index 57806ccb0d3d..fa46f3dbb26c 100644 --- a/instrumentation/grpc-1.6/testing/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/AbstractGrpcStreamingTest.java +++ b/instrumentation/grpc-1.6/testing/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/AbstractGrpcStreamingTest.java @@ -6,6 +6,9 @@ package io.opentelemetry.instrumentation.grpc.v1_6; import static io.opentelemetry.instrumentation.grpc.v1_6.AbstractGrpcTest.addExtraClientAttributes; +import static io.opentelemetry.instrumentation.grpc.v1_6.ExperimentalTestHelper.GRPC_RECEIVED_MESSAGE_COUNT; +import static io.opentelemetry.instrumentation.grpc.v1_6.ExperimentalTestHelper.GRPC_SENT_MESSAGE_COUNT; +import static io.opentelemetry.instrumentation.grpc.v1_6.ExperimentalTestHelper.experimentalSatisfies; 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; @@ -223,6 +226,12 @@ public void onCompleted() { .hasNoParent() .hasAttributesSatisfyingExactly( addExtraClientAttributes( + experimentalSatisfies( + GRPC_RECEIVED_MESSAGE_COUNT, + v -> assertThat(v).isGreaterThan(0)), + experimentalSatisfies( + GRPC_SENT_MESSAGE_COUNT, + v -> assertThat(v).isGreaterThan(0)), equalTo(RPC_SYSTEM, "grpc"), equalTo(RPC_SERVICE, "example.Greeter"), equalTo(RPC_METHOD, "Conversation"), @@ -238,6 +247,11 @@ public void onCompleted() { .hasKind(SpanKind.SERVER) .hasParent(trace.getSpan(0)) .hasAttributesSatisfyingExactly( + experimentalSatisfies( + GRPC_RECEIVED_MESSAGE_COUNT, + v -> assertThat(v).isGreaterThan(0)), + experimentalSatisfies( + GRPC_SENT_MESSAGE_COUNT, v -> assertThat(v).isGreaterThan(0)), equalTo(RPC_SYSTEM, "grpc"), equalTo(RPC_SERVICE, "example.Greeter"), equalTo(RPC_METHOD, "Conversation"), @@ -246,7 +260,12 @@ public void onCompleted() { equalTo(SERVER_PORT, server.getPort()), equalTo(NETWORK_TYPE, "ipv4"), equalTo(NETWORK_PEER_ADDRESS, "127.0.0.1"), - satisfies(NETWORK_PEER_PORT, val -> assertThat(val).isNotNull())) + experimentalSatisfies( + GRPC_RECEIVED_MESSAGE_COUNT, + v -> assertThat(v).isGreaterThan(0)), + experimentalSatisfies( + GRPC_SENT_MESSAGE_COUNT, v -> assertThat(v).isGreaterThan(0)), + satisfies(NETWORK_PEER_PORT, val -> val.isNotNull())) .satisfies( spanData -> assertThat(spanData.getEvents()) 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 31db3557c5b8..02332d164532 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 @@ -5,6 +5,9 @@ package io.opentelemetry.instrumentation.grpc.v1_6; +import static io.opentelemetry.instrumentation.grpc.v1_6.ExperimentalTestHelper.GRPC_RECEIVED_MESSAGE_COUNT; +import static io.opentelemetry.instrumentation.grpc.v1_6.ExperimentalTestHelper.GRPC_SENT_MESSAGE_COUNT; +import static io.opentelemetry.instrumentation.grpc.v1_6.ExperimentalTestHelper.experimentalSatisfies; 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; @@ -140,6 +143,12 @@ public void sayHello( .hasParent(trace.getSpan(0)) .hasAttributesSatisfyingExactly( addExtraClientAttributes( + experimentalSatisfies( + GRPC_RECEIVED_MESSAGE_COUNT, + v -> assertThat(v).isGreaterThan(0)), + experimentalSatisfies( + GRPC_SENT_MESSAGE_COUNT, + v -> assertThat(v).isGreaterThan(0)), equalTo(RPC_SYSTEM, "grpc"), equalTo(RPC_SERVICE, "example.Greeter"), equalTo(RPC_METHOD, "SayHello"), @@ -167,6 +176,11 @@ public void sayHello( .hasKind(SpanKind.SERVER) .hasParent(trace.getSpan(1)) .hasAttributesSatisfyingExactly( + experimentalSatisfies( + GRPC_RECEIVED_MESSAGE_COUNT, + v -> assertThat(v).isGreaterThan(0)), + experimentalSatisfies( + GRPC_SENT_MESSAGE_COUNT, v -> assertThat(v).isGreaterThan(0)), equalTo(RPC_SYSTEM, "grpc"), equalTo(RPC_SERVICE, "example.Greeter"), equalTo(RPC_METHOD, "SayHello"), @@ -175,7 +189,7 @@ public void sayHello( equalTo(SERVER_PORT, server.getPort()), equalTo(NETWORK_TYPE, "ipv4"), equalTo(NETWORK_PEER_ADDRESS, "127.0.0.1"), - satisfies(NETWORK_PEER_PORT, val -> assertThat(val).isNotNull())) + satisfies(NETWORK_PEER_PORT, val -> val.isNotNull())) .hasEventsSatisfyingExactly( event -> event @@ -293,6 +307,12 @@ public void sayHello( .hasParent(trace.getSpan(0)) .hasAttributesSatisfyingExactly( addExtraClientAttributes( + experimentalSatisfies( + GRPC_RECEIVED_MESSAGE_COUNT, + v -> assertThat(v).isGreaterThan(0)), + experimentalSatisfies( + GRPC_SENT_MESSAGE_COUNT, + v -> assertThat(v).isGreaterThan(0)), equalTo(RPC_SYSTEM, "grpc"), equalTo(RPC_SERVICE, "example.Greeter"), equalTo(RPC_METHOD, "SayHello"), @@ -320,6 +340,11 @@ public void sayHello( .hasKind(SpanKind.SERVER) .hasParent(trace.getSpan(1)) .hasAttributesSatisfyingExactly( + experimentalSatisfies( + GRPC_RECEIVED_MESSAGE_COUNT, + v -> assertThat(v).isGreaterThan(0)), + experimentalSatisfies( + GRPC_SENT_MESSAGE_COUNT, v -> assertThat(v).isGreaterThan(0)), equalTo(RPC_SYSTEM, "grpc"), equalTo(RPC_SERVICE, "example.Greeter"), equalTo(RPC_METHOD, "SayHello"), @@ -328,7 +353,7 @@ public void sayHello( equalTo(SERVER_PORT, server.getPort()), equalTo(NETWORK_TYPE, "ipv4"), equalTo(NETWORK_PEER_ADDRESS, "127.0.0.1"), - satisfies(NETWORK_PEER_PORT, val -> assertThat(val).isNotNull())) + satisfies(NETWORK_PEER_PORT, val -> val.isNotNull())) .hasEventsSatisfyingExactly( event -> event @@ -458,6 +483,12 @@ public void onCompleted() { .hasParent(trace.getSpan(0)) .hasAttributesSatisfyingExactly( addExtraClientAttributes( + experimentalSatisfies( + GRPC_RECEIVED_MESSAGE_COUNT, + v -> assertThat(v).isGreaterThan(0)), + experimentalSatisfies( + GRPC_SENT_MESSAGE_COUNT, + v -> assertThat(v).isGreaterThan(0)), equalTo(RPC_SYSTEM, "grpc"), equalTo(RPC_SERVICE, "example.Greeter"), equalTo(RPC_METHOD, "SayHello"), @@ -485,6 +516,11 @@ public void onCompleted() { .hasKind(SpanKind.SERVER) .hasParent(trace.getSpan(1)) .hasAttributesSatisfyingExactly( + experimentalSatisfies( + GRPC_RECEIVED_MESSAGE_COUNT, + v -> assertThat(v).isGreaterThan(0)), + experimentalSatisfies( + GRPC_SENT_MESSAGE_COUNT, v -> assertThat(v).isGreaterThan(0)), equalTo(RPC_SYSTEM, "grpc"), equalTo(RPC_SERVICE, "example.Greeter"), equalTo(RPC_METHOD, "SayHello"), @@ -493,7 +529,7 @@ public void onCompleted() { equalTo(SERVER_PORT, server.getPort()), equalTo(NETWORK_TYPE, "ipv4"), equalTo(NETWORK_PEER_ADDRESS, "127.0.0.1"), - satisfies(NETWORK_PEER_PORT, val -> assertThat(val).isNotNull())) + satisfies(NETWORK_PEER_PORT, val -> val.isNotNull())) .hasEventsSatisfyingExactly( event -> event @@ -598,6 +634,12 @@ public void sayHello( .hasStatus(StatusData.error()) .hasAttributesSatisfyingExactly( addExtraClientAttributes( + experimentalSatisfies( + GRPC_RECEIVED_MESSAGE_COUNT, + v -> assertThat(v).isEqualTo(0)), + experimentalSatisfies( + GRPC_SENT_MESSAGE_COUNT, + v -> assertThat(v).isGreaterThan(0)), equalTo(RPC_SYSTEM, "grpc"), equalTo(RPC_SERVICE, "example.Greeter"), equalTo(RPC_METHOD, "SayHello"), @@ -618,6 +660,11 @@ public void sayHello( .hasParent(trace.getSpan(0)) .hasStatus(isServerError ? StatusData.error() : StatusData.unset()) .hasAttributesSatisfyingExactly( + experimentalSatisfies( + GRPC_RECEIVED_MESSAGE_COUNT, + v -> assertThat(v).isGreaterThan(0)), + experimentalSatisfies( + GRPC_SENT_MESSAGE_COUNT, v -> assertThat(v).isEqualTo(0)), equalTo(RPC_SYSTEM, "grpc"), equalTo(RPC_SERVICE, "example.Greeter"), equalTo(RPC_METHOD, "SayHello"), @@ -626,7 +673,7 @@ public void sayHello( equalTo(SERVER_PORT, server.getPort()), equalTo(NETWORK_TYPE, "ipv4"), equalTo(NETWORK_PEER_ADDRESS, "127.0.0.1"), - satisfies(NETWORK_PEER_PORT, val -> assertThat(val).isNotNull())) + satisfies(NETWORK_PEER_PORT, val -> val.isNotNull())) .hasEventsSatisfying( events -> { assertThat(events).isNotEmpty(); @@ -735,6 +782,12 @@ public void sayHello( .hasStatus(StatusData.error()) .hasAttributesSatisfyingExactly( addExtraClientAttributes( + experimentalSatisfies( + GRPC_RECEIVED_MESSAGE_COUNT, + v -> assertThat(v).isEqualTo(0)), + experimentalSatisfies( + GRPC_SENT_MESSAGE_COUNT, + v -> assertThat(v).isGreaterThan(0)), equalTo(RPC_SYSTEM, "grpc"), equalTo(RPC_SERVICE, "example.Greeter"), equalTo(RPC_METHOD, "SayHello"), @@ -765,7 +818,7 @@ public void sayHello( equalTo(SERVER_PORT, server.getPort()), equalTo(NETWORK_TYPE, "ipv4"), equalTo(NETWORK_PEER_ADDRESS, "127.0.0.1"), - satisfies(NETWORK_PEER_PORT, val -> assertThat(val).isNotNull())) + satisfies(NETWORK_PEER_PORT, val -> val.isNotNull())) .hasEventsSatisfying( events -> { assertThat(events).hasSize(2); @@ -967,6 +1020,12 @@ public void onCompleted() { .hasParent(trace.getSpan(0)) .hasAttributesSatisfyingExactly( addExtraClientAttributes( + experimentalSatisfies( + GRPC_RECEIVED_MESSAGE_COUNT, + v -> assertThat(v).isGreaterThan(0)), + experimentalSatisfies( + GRPC_SENT_MESSAGE_COUNT, + v -> assertThat(v).isGreaterThan(0)), equalTo(RPC_SYSTEM, "grpc"), equalTo(RPC_SERVICE, "example.Greeter"), equalTo(RPC_METHOD, "SayHello"), @@ -994,6 +1053,11 @@ public void onCompleted() { .hasKind(SpanKind.SERVER) .hasParent(trace.getSpan(1)) .hasAttributesSatisfyingExactly( + experimentalSatisfies( + GRPC_RECEIVED_MESSAGE_COUNT, + v -> assertThat(v).isGreaterThan(0)), + experimentalSatisfies( + GRPC_SENT_MESSAGE_COUNT, v -> assertThat(v).isGreaterThan(0)), equalTo(RPC_SYSTEM, "grpc"), equalTo(RPC_SERVICE, "example.Greeter"), equalTo(RPC_METHOD, "SayHello"), @@ -1002,7 +1066,7 @@ public void onCompleted() { equalTo(SERVER_PORT, server.getPort()), equalTo(NETWORK_TYPE, "ipv4"), equalTo(NETWORK_PEER_ADDRESS, "127.0.0.1"), - satisfies(NETWORK_PEER_PORT, val -> assertThat(val).isNotNull())) + satisfies(NETWORK_PEER_PORT, val -> val.isNotNull())) .hasEventsSatisfyingExactly( event -> event @@ -1084,6 +1148,12 @@ public void onCompleted() { .hasStatus(StatusData.error()) .hasAttributesSatisfyingExactly( addExtraClientAttributes( + experimentalSatisfies( + GRPC_RECEIVED_MESSAGE_COUNT, + v -> assertThat(v).isGreaterThan(0)), + experimentalSatisfies( + GRPC_SENT_MESSAGE_COUNT, + v -> assertThat(v).isGreaterThan(0)), equalTo(RPC_SYSTEM, "grpc"), equalTo(RPC_SERVICE, "example.Greeter"), equalTo(RPC_METHOD, "SayMultipleHello"), @@ -1112,6 +1182,11 @@ public void onCompleted() { .hasKind(SpanKind.SERVER) .hasParent(trace.getSpan(1)) .hasAttributesSatisfyingExactly( + experimentalSatisfies( + GRPC_RECEIVED_MESSAGE_COUNT, + v -> assertThat(v).isGreaterThan(0)), + experimentalSatisfies( + GRPC_SENT_MESSAGE_COUNT, v -> assertThat(v).isGreaterThan(0)), equalTo(RPC_SYSTEM, "grpc"), equalTo(RPC_SERVICE, "example.Greeter"), equalTo(RPC_METHOD, "SayMultipleHello"), @@ -1120,7 +1195,7 @@ public void onCompleted() { equalTo(SERVER_PORT, server.getPort()), equalTo(NETWORK_TYPE, "ipv4"), equalTo(NETWORK_PEER_ADDRESS, "127.0.0.1"), - satisfies(NETWORK_PEER_PORT, val -> assertThat(val).isNotNull())) + satisfies(NETWORK_PEER_PORT, val -> val.isNotNull())) .hasEventsSatisfyingExactly( event -> event @@ -1198,6 +1273,12 @@ public void onCompleted() { .hasNoParent() .hasAttributesSatisfyingExactly( addExtraClientAttributes( + experimentalSatisfies( + GRPC_RECEIVED_MESSAGE_COUNT, + v -> assertThat(v).isGreaterThan(0)), + experimentalSatisfies( + GRPC_SENT_MESSAGE_COUNT, + v -> assertThat(v).isGreaterThan(0)), equalTo(RPC_SYSTEM, "grpc"), equalTo( RPC_SERVICE, "grpc.reflection.v1alpha.ServerReflection"), @@ -1227,6 +1308,11 @@ public void onCompleted() { .hasKind(SpanKind.SERVER) .hasParent(trace.getSpan(0)) .hasAttributesSatisfyingExactly( + experimentalSatisfies( + GRPC_RECEIVED_MESSAGE_COUNT, + v -> assertThat(v).isGreaterThan(0)), + experimentalSatisfies( + GRPC_SENT_MESSAGE_COUNT, v -> assertThat(v).isGreaterThan(0)), equalTo(RPC_SYSTEM, "grpc"), equalTo(RPC_SERVICE, "grpc.reflection.v1alpha.ServerReflection"), equalTo(RPC_METHOD, "ServerReflectionInfo"), @@ -1235,7 +1321,7 @@ public void onCompleted() { equalTo(SERVER_PORT, server.getPort()), equalTo(NETWORK_TYPE, "ipv4"), equalTo(NETWORK_PEER_ADDRESS, "127.0.0.1"), - satisfies(NETWORK_PEER_PORT, val -> assertThat(val).isNotNull())) + satisfies(NETWORK_PEER_PORT, val -> val.isNotNull())) .hasEventsSatisfyingExactly( event -> event @@ -1301,6 +1387,12 @@ public void sayHello( .hasParent(trace.getSpan(0)) .hasAttributesSatisfyingExactly( addExtraClientAttributes( + experimentalSatisfies( + GRPC_RECEIVED_MESSAGE_COUNT, + v -> assertThat(v).isGreaterThan(0)), + experimentalSatisfies( + GRPC_SENT_MESSAGE_COUNT, + v -> assertThat(v).isGreaterThan(0)), equalTo(RPC_SYSTEM, "grpc"), equalTo(RPC_SERVICE, "example.Greeter"), equalTo(RPC_METHOD, "SayHello"), @@ -1328,6 +1420,11 @@ public void sayHello( .hasKind(SpanKind.SERVER) .hasParent(trace.getSpan(1)) .hasAttributesSatisfyingExactly( + experimentalSatisfies( + GRPC_RECEIVED_MESSAGE_COUNT, + v -> assertThat(v).isGreaterThan(0)), + experimentalSatisfies( + GRPC_SENT_MESSAGE_COUNT, v -> assertThat(v).isGreaterThan(0)), equalTo(RPC_SYSTEM, "grpc"), equalTo(RPC_SERVICE, "example.Greeter"), equalTo(RPC_METHOD, "SayHello"), @@ -1336,7 +1433,7 @@ public void sayHello( equalTo(SERVER_PORT, server.getPort()), equalTo(NETWORK_TYPE, "ipv4"), equalTo(NETWORK_PEER_ADDRESS, "127.0.0.1"), - satisfies(NETWORK_PEER_PORT, val -> assertThat(val).isNotNull())) + satisfies(NETWORK_PEER_PORT, val -> val.isNotNull())) .hasEventsSatisfyingExactly( event -> event @@ -1595,7 +1692,7 @@ static List addExtraClientAttributes(AttributeAssertion... a if (Boolean.getBoolean("testLatestDeps")) { result.add(equalTo(NETWORK_TYPE, "ipv4")); result.add(equalTo(NETWORK_PEER_ADDRESS, "127.0.0.1")); - result.add(satisfies(NETWORK_PEER_PORT, val -> assertThat(val).isNotNull())); + result.add(satisfies(NETWORK_PEER_PORT, val -> val.isNotNull())); } return result; } diff --git a/instrumentation/grpc-1.6/testing/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/ExperimentalTestHelper.java b/instrumentation/grpc-1.6/testing/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/ExperimentalTestHelper.java new file mode 100644 index 000000000000..6fdd94d018ad --- /dev/null +++ b/instrumentation/grpc-1.6/testing/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/ExperimentalTestHelper.java @@ -0,0 +1,35 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.grpc.v1_6; + +import static io.opentelemetry.api.common.AttributeKey.longKey; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies; + +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.sdk.testing.assertj.AttributeAssertion; +import java.util.function.Consumer; + +class ExperimentalTestHelper { + private static final boolean isEnabled = + Boolean.getBoolean("otel.instrumentation.grpc.experimental-span-attributes"); + + static final AttributeKey GRPC_RECEIVED_MESSAGE_COUNT = + longKey("grpc.received.message_count"); + static final AttributeKey GRPC_SENT_MESSAGE_COUNT = longKey("grpc.sent.message_count"); + + static AttributeAssertion experimentalSatisfies( + AttributeKey key, Consumer assertion) { + return satisfies( + key, + val -> { + if (isEnabled) { + val.satisfies(assertion::accept); + } + }); + } + + private ExperimentalTestHelper() {} +} diff --git a/instrumentation/guava-10.0/javaagent/build.gradle.kts b/instrumentation/guava-10.0/javaagent/build.gradle.kts index 1dd1aa52d4b6..57c6bfc7b064 100644 --- a/instrumentation/guava-10.0/javaagent/build.gradle.kts +++ b/instrumentation/guava-10.0/javaagent/build.gradle.kts @@ -33,17 +33,21 @@ dependencies { } tasks { - val testStableSemconv by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath + jvmArgs("-Dotel.semconv-stability.opt-in=code") } val testBothSemconv by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath + jvmArgs("-Dotel.semconv-stability.opt-in=code/dup") } check { - dependsOn(testStableSemconv) - dependsOn(testBothSemconv) + dependsOn(testStableSemconv, testBothSemconv) } } diff --git a/instrumentation/gwt-2.0/javaagent/build.gradle.kts b/instrumentation/gwt-2.0/javaagent/build.gradle.kts index aca71d8c2a9a..89d3b4abb739 100644 --- a/instrumentation/gwt-2.0/javaagent/build.gradle.kts +++ b/instrumentation/gwt-2.0/javaagent/build.gradle.kts @@ -120,4 +120,5 @@ tasks.withType().configureEach { // required on jdk17 jvmArgs("--add-opens=java.base/java.lang=ALL-UNNAMED") jvmArgs("-XX:+IgnoreUnrecognizedVMOptions") + systemProperty("collectMetadata", findProperty("collectMetadata")?.toString() ?: "false") } diff --git a/instrumentation/gwt-2.0/metadata.yaml b/instrumentation/gwt-2.0/metadata.yaml new file mode 100644 index 000000000000..9a9ee7a9cd10 --- /dev/null +++ b/instrumentation/gwt-2.0/metadata.yaml @@ -0,0 +1,2 @@ +description: This instrumentation enables RPC server spans for GWT RPC requests. +library_link: https://www.gwtproject.org/ diff --git a/instrumentation/hibernate/hibernate-3.3/javaagent/build.gradle.kts b/instrumentation/hibernate/hibernate-3.3/javaagent/build.gradle.kts index f46f2d1bf8ec..5605449caa2e 100644 --- a/instrumentation/hibernate/hibernate-3.3/javaagent/build.gradle.kts +++ b/instrumentation/hibernate/hibernate-3.3/javaagent/build.gradle.kts @@ -35,6 +35,7 @@ dependencies { testImplementation("com.sun.xml.bind:jaxb-core:2.2.11") testImplementation("com.sun.xml.bind:jaxb-impl:2.2.11") testImplementation("javax.activation:activation:1.1.1") + testImplementation(project(":instrumentation:hibernate:testing")) latestDepTestLibrary("org.hibernate:hibernate-core:3.+") // see hibernate-4.0 module } @@ -50,18 +51,29 @@ if (findProperty("testLatestDeps") as Boolean) { tasks { withType().configureEach { - // TODO run tests both with and without experimental span attributes - jvmArgs("-Dotel.instrumentation.hibernate.experimental-span-attributes=true") // required on jdk17 jvmArgs("--add-opens=java.base/java.lang=ALL-UNNAMED") jvmArgs("-XX:+IgnoreUnrecognizedVMOptions") + + systemProperty("collectMetadata", findProperty("collectMetadata")?.toString() ?: "false") + } + + val testExperimental by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath + + jvmArgs("-Dotel.instrumentation.hibernate.experimental-span-attributes=true") + systemProperty("metadataConfig", "otel.instrumentation.hibernate.experimental-span-attributes=true") } val testStableSemconv by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath + jvmArgs("-Dotel.semconv-stability.opt-in=database") } check { - dependsOn(testStableSemconv) + dependsOn(testStableSemconv, testExperimental) } } diff --git a/instrumentation/hibernate/hibernate-3.3/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v3_3/AbstractHibernateTest.java b/instrumentation/hibernate/hibernate-3.3/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v3_3/AbstractHibernateTest.java index cc504baa77bb..31d266a36cc4 100644 --- a/instrumentation/hibernate/hibernate-3.3/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v3_3/AbstractHibernateTest.java +++ b/instrumentation/hibernate/hibernate-3.3/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v3_3/AbstractHibernateTest.java @@ -8,6 +8,9 @@ import static io.opentelemetry.instrumentation.api.internal.SemconvStability.emitStableDatabaseSemconv; import static io.opentelemetry.instrumentation.testing.junit.db.SemconvStabilityUtil.maybeStable; import static io.opentelemetry.instrumentation.testing.junit.db.SemconvStabilityUtil.maybeStableDbSystemName; +import static io.opentelemetry.javaagent.instrumentation.hibernate.ExperimentalTestHelper.HIBERNATE_SESSION_ID; +import static io.opentelemetry.javaagent.instrumentation.hibernate.ExperimentalTestHelper.experimental; +import static io.opentelemetry.javaagent.instrumentation.hibernate.ExperimentalTestHelper.experimentalSatisfies; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies; import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_CONNECTION_STRING; @@ -17,8 +20,8 @@ import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_STATEMENT; import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_SYSTEM; import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_USER; +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; @@ -35,7 +38,6 @@ import org.junit.jupiter.api.extension.RegisterExtension; abstract class AbstractHibernateTest { - @RegisterExtension protected static final InstrumentationExtension testing = AgentInstrumentationExtension.create(); @@ -66,8 +68,8 @@ static void cleanUp() { } @SuppressWarnings("deprecation") // TODO DB_CONNECTION_STRING deprecation - static SpanDataAssert assertClientSpan(SpanDataAssert span, SpanData parent) { - return span.hasKind(SpanKind.CLIENT) + static void assertClientSpan(SpanDataAssert span, SpanData parent) { + span.hasKind(SpanKind.CLIENT) .hasParent(parent) .hasAttributesSatisfyingExactly( equalTo(maybeStable(DB_SYSTEM), maybeStableDbSystemName("h2")), @@ -80,8 +82,8 @@ static SpanDataAssert assertClientSpan(SpanDataAssert span, SpanData parent) { } @SuppressWarnings("deprecation") // TODO DB_CONNECTION_STRING deprecation - static SpanDataAssert assertClientSpan(SpanDataAssert span, SpanData parent, String verb) { - return span.hasName(verb.concat(" db1.Value")) + static void assertClientSpan(SpanDataAssert span, SpanData parent, String verb) { + span.hasName(verb.concat(" db1.Value")) .hasKind(SpanKind.CLIENT) .hasParent(parent) .hasAttributesSatisfyingExactly( @@ -101,17 +103,15 @@ static SpanDataAssert assertSessionSpan(SpanDataAssert span, SpanData parent, St .hasKind(SpanKind.INTERNAL) .hasParent(parent) .hasAttributesSatisfyingExactly( - satisfies( - AttributeKey.stringKey("hibernate.session_id"), - val -> val.isInstanceOf(String.class))); + experimentalSatisfies( + HIBERNATE_SESSION_ID, val -> assertThat(val).isInstanceOf(String.class))); } - static SpanDataAssert assertSpanWithSessionId( + static void assertSpanWithSessionId( SpanDataAssert span, SpanData parent, String spanName, String sessionId) { - return span.hasName(spanName) + span.hasName(spanName) .hasKind(SpanKind.INTERNAL) .hasParent(parent) - .hasAttributesSatisfyingExactly( - equalTo(AttributeKey.stringKey("hibernate.session_id"), sessionId)); + .hasAttributesSatisfyingExactly(equalTo(HIBERNATE_SESSION_ID, experimental(sessionId))); } } diff --git a/instrumentation/hibernate/hibernate-3.3/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v3_3/CriteriaTest.java b/instrumentation/hibernate/hibernate-3.3/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v3_3/CriteriaTest.java index b3d20591e5c6..2576ca55d5e0 100644 --- a/instrumentation/hibernate/hibernate-3.3/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v3_3/CriteriaTest.java +++ b/instrumentation/hibernate/hibernate-3.3/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v3_3/CriteriaTest.java @@ -5,7 +5,9 @@ package io.opentelemetry.javaagent.instrumentation.hibernate.v3_3; -import io.opentelemetry.api.common.AttributeKey; +import static io.opentelemetry.javaagent.instrumentation.hibernate.ExperimentalTestHelper.HIBERNATE_SESSION_ID; +import static io.opentelemetry.javaagent.instrumentation.hibernate.ExperimentalTestHelper.experimental; + import io.opentelemetry.api.trace.SpanKind; import java.util.function.Consumer; import java.util.stream.Stream; @@ -55,10 +57,7 @@ void testCriteria(String methodName, Consumer interaction) { span, trace.getSpan(0), "Transaction.commit", - trace - .getSpan(1) - .getAttributes() - .get(AttributeKey.stringKey("hibernate.session_id"))))); + experimental(trace.getSpan(1).getAttributes().get(HIBERNATE_SESSION_ID))))); } private static Stream provideArguments() { diff --git a/instrumentation/hibernate/hibernate-3.3/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v3_3/QueryTest.java b/instrumentation/hibernate/hibernate-3.3/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v3_3/QueryTest.java index 2dbb864f2b3c..d28914b1cd59 100644 --- a/instrumentation/hibernate/hibernate-3.3/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v3_3/QueryTest.java +++ b/instrumentation/hibernate/hibernate-3.3/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v3_3/QueryTest.java @@ -5,9 +5,9 @@ package io.opentelemetry.javaagent.instrumentation.hibernate.v3_3; +import static io.opentelemetry.javaagent.instrumentation.hibernate.ExperimentalTestHelper.HIBERNATE_SESSION_ID; import static org.junit.jupiter.api.Named.named; -import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.api.trace.SpanKind; import java.util.function.Consumer; import java.util.stream.Stream; @@ -58,10 +58,7 @@ void testHibernateQuery(Parameter parameters) { span, trace.getSpan(0), "Transaction.commit", - trace - .getSpan(1) - .getAttributes() - .get(AttributeKey.stringKey("hibernate.session_id")))); + trace.getSpan(1).getAttributes().get(HIBERNATE_SESSION_ID))); } else { // Without Transaction trace.hasSpansSatisfyingExactly( diff --git a/instrumentation/hibernate/hibernate-3.3/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v3_3/SessionTest.java b/instrumentation/hibernate/hibernate-3.3/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v3_3/SessionTest.java index 21228dd7fec0..cbda6ec3bf45 100644 --- a/instrumentation/hibernate/hibernate-3.3/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v3_3/SessionTest.java +++ b/instrumentation/hibernate/hibernate-3.3/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v3_3/SessionTest.java @@ -5,6 +5,7 @@ package io.opentelemetry.javaagent.instrumentation.hibernate.v3_3; +import static io.opentelemetry.javaagent.instrumentation.hibernate.ExperimentalTestHelper.HIBERNATE_SESSION_ID; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies; import static io.opentelemetry.semconv.ExceptionAttributes.EXCEPTION_MESSAGE; @@ -12,7 +13,6 @@ import static io.opentelemetry.semconv.ExceptionAttributes.EXCEPTION_TYPE; import static org.junit.jupiter.api.Named.named; -import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.api.trace.SpanKind; import io.opentelemetry.sdk.testing.assertj.TraceAssert; import java.util.function.BiConsumer; @@ -92,10 +92,7 @@ void testHibernateStatelessAction(Parameter parameter) { span, trace.getSpan(0), "Transaction.commit", - trace - .getSpan(1) - .getAttributes() - .get(AttributeKey.stringKey("hibernate.session_id"))), + trace.getSpan(1).getAttributes().get(HIBERNATE_SESSION_ID)), span -> assertClientSpan(span, trace.getSpan(2)))); } @@ -127,10 +124,7 @@ void testHibernateReplicate(Parameter parameter) { span, trace.getSpan(0), "Transaction.commit", - trace - .getSpan(1) - .getAttributes() - .get(AttributeKey.stringKey("hibernate.session_id"))), + trace.getSpan(1).getAttributes().get(HIBERNATE_SESSION_ID)), span -> assertClientSpan(span, trace.getSpan(3)))); } @@ -172,10 +166,7 @@ void testHibernateFailedReplicate() { span, trace.getSpan(0), "Transaction.commit", - trace - .getSpan(1) - .getAttributes() - .get(AttributeKey.stringKey("hibernate.session_id"))))); + trace.getSpan(1).getAttributes().get(HIBERNATE_SESSION_ID)))); } @ParameterizedTest @@ -205,10 +196,7 @@ void testHibernateCommitAction(Parameter parameter) { span, trace.getSpan(0), "Transaction.commit", - trace - .getSpan(1) - .getAttributes() - .get(AttributeKey.stringKey("hibernate.session_id"))), + trace.getSpan(1).getAttributes().get(HIBERNATE_SESSION_ID)), span -> assertClientSpan(span, trace.getSpan(2)))); } @@ -237,10 +225,7 @@ void testAttachesStateToQueryCreatedViaQueryMethods(Parameter parameter) { span, trace.getSpan(0), "Transaction.commit", - trace - .getSpan(1) - .getAttributes() - .get(AttributeKey.stringKey("hibernate.session_id"))))); + trace.getSpan(1).getAttributes().get(HIBERNATE_SESSION_ID)))); } @Test @@ -294,19 +279,13 @@ void testHibernateOverlappingSessions() { span, trace.getSpan(0), "Session.delete io.opentelemetry.javaagent.instrumentation.hibernate.v3_3.Value", - trace - .getSpan(1) - .getAttributes() - .get(AttributeKey.stringKey("hibernate.session_id"))), + trace.getSpan(1).getAttributes().get(HIBERNATE_SESSION_ID)), span -> assertSpanWithSessionId( span, trace.getSpan(0), "Transaction.commit", - trace - .getSpan(1) - .getAttributes() - .get(AttributeKey.stringKey("hibernate.session_id"))), + trace.getSpan(1).getAttributes().get(HIBERNATE_SESSION_ID)), span -> assertClientSpan(span, trace.getSpan(5), "INSERT"), span -> assertClientSpan(span, trace.getSpan(5), "DELETE"))); } @@ -606,9 +585,6 @@ static void sessionAssertion(TraceAssert trace, String methodName, String resour span, trace.getSpan(0), "Transaction.commit", - trace - .getSpan(1) - .getAttributes() - .get(AttributeKey.stringKey("hibernate.session_id")))); + trace.getSpan(1).getAttributes().get(HIBERNATE_SESSION_ID))); } } diff --git a/instrumentation/hibernate/hibernate-3.3/metadata.yaml b/instrumentation/hibernate/hibernate-3.3/metadata.yaml new file mode 100644 index 000000000000..f8ee68b40f38 --- /dev/null +++ b/instrumentation/hibernate/hibernate-3.3/metadata.yaml @@ -0,0 +1,9 @@ +description: > + This instrumentation enables the generation of INTERNAL spans for Hibernate operations, including + session methods (e.g., `save`, `update`, `delete`), transaction commits, and query executions. +library_link: https://github.com/hibernate/hibernate-orm +configurations: + - name: otel.instrumentation.hibernate.experimental-span-attributes + type: boolean + description: Enables the addition of the experimental `hibernate.session_id` span attribute. + default: false diff --git a/instrumentation/hibernate/hibernate-4.0/javaagent/build.gradle.kts b/instrumentation/hibernate/hibernate-4.0/javaagent/build.gradle.kts index aa70c11f7a09..9ac4ad5169f4 100644 --- a/instrumentation/hibernate/hibernate-4.0/javaagent/build.gradle.kts +++ b/instrumentation/hibernate/hibernate-4.0/javaagent/build.gradle.kts @@ -32,6 +32,7 @@ dependencies { testImplementation("org.hibernate:hibernate-core:4.0.0.Final") testImplementation("org.hibernate:hibernate-entitymanager:4.0.0.Final") + testImplementation(project(":instrumentation:hibernate:testing")) testImplementation("org.javassist:javassist:3.28.0-GA") } @@ -40,6 +41,11 @@ val latestDepTest = findProperty("testLatestDeps") as Boolean testing { suites { val version5Test by registering(JvmTestSuite::class) { + targets.all { + testTask.configure { + jvmArgs("-Dotel.instrumentation.hibernate.experimental-span-attributes=true") + } + } dependencies { sources { java { @@ -56,6 +62,7 @@ testing { implementation("com.sun.xml.bind:jaxb-impl:2.2.11") implementation("javax.activation:activation:1.1.1") implementation("org.hsqldb:hsqldb:2.0.0") + implementation(project(":instrumentation:hibernate:testing")) if (latestDepTest) { implementation("org.hibernate:hibernate-core:5.0.0.Final") @@ -78,18 +85,27 @@ tasks { jvmArgs("--add-opens=java.base/java.lang.invoke=ALL-UNNAMED") jvmArgs("-XX:+IgnoreUnrecognizedVMOptions") - // TODO run tests both with and without experimental span attributes - jvmArgs("-Dotel.instrumentation.hibernate.experimental-span-attributes=true") - jvmArgs("-Dotel.instrumentation.common.experimental.controller-telemetry.enabled=true") + + systemProperty("collectMetadata", findProperty("collectMetadata")?.toString() ?: "false") + } + + val testExperimental by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath + + jvmArgs("-Dotel.instrumentation.hibernate.experimental-span-attributes=true") + systemProperty("metadataConfig", "otel.instrumentation.hibernate.experimental-span-attributes=true") } val testStableSemconv by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath + jvmArgs("-Dotel.semconv-stability.opt-in=database") } check { - dependsOn(testing.suites) - dependsOn(testStableSemconv) + dependsOn(testing.suites, testStableSemconv, testExperimental) } } diff --git a/instrumentation/hibernate/hibernate-4.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_0/CriteriaTest.java b/instrumentation/hibernate/hibernate-4.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_0/CriteriaTest.java index cb1274f82652..d2d0774b38a1 100644 --- a/instrumentation/hibernate/hibernate-4.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_0/CriteriaTest.java +++ b/instrumentation/hibernate/hibernate-4.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_0/CriteriaTest.java @@ -5,12 +5,14 @@ package io.opentelemetry.javaagent.instrumentation.hibernate.v4_0; -import static io.opentelemetry.api.common.AttributeKey.stringKey; import static io.opentelemetry.api.trace.SpanKind.CLIENT; import static io.opentelemetry.api.trace.SpanKind.INTERNAL; import static io.opentelemetry.instrumentation.api.internal.SemconvStability.emitStableDatabaseSemconv; import static io.opentelemetry.instrumentation.testing.junit.db.SemconvStabilityUtil.maybeStable; import static io.opentelemetry.instrumentation.testing.junit.db.SemconvStabilityUtil.maybeStableDbSystemName; +import static io.opentelemetry.javaagent.instrumentation.hibernate.ExperimentalTestHelper.HIBERNATE_SESSION_ID; +import static io.opentelemetry.javaagent.instrumentation.hibernate.ExperimentalTestHelper.experimental; +import static io.opentelemetry.javaagent.instrumentation.hibernate.ExperimentalTestHelper.experimentalSatisfies; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies; import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_CONNECTION_STRING; @@ -20,6 +22,7 @@ import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_STATEMENT; import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_SYSTEM; import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_USER; +import static org.assertj.core.api.Assertions.assertThat; import io.opentelemetry.api.common.Attributes; import java.util.function.Consumer; @@ -67,9 +70,9 @@ void testCriteria(String methodName, Consumer interaction) { .hasKind(INTERNAL) .hasParent(trace.getSpan(0)) .hasAttributesSatisfyingExactly( - satisfies( - stringKey("hibernate.session_id"), - val -> val.isInstanceOf(String.class))), + experimentalSatisfies( + HIBERNATE_SESSION_ID, + val -> assertThat(val).isInstanceOf(String.class))), span -> span.hasName("SELECT db1.Value") .hasKind(CLIENT) @@ -92,11 +95,9 @@ void testCriteria(String methodName, Consumer interaction) { .hasParent(trace.getSpan(0)) .hasAttributesSatisfyingExactly( equalTo( - stringKey("hibernate.session_id"), - trace - .getSpan(1) - .getAttributes() - .get(stringKey("hibernate.session_id")))))); + HIBERNATE_SESSION_ID, + experimental( + trace.getSpan(1).getAttributes().get(HIBERNATE_SESSION_ID)))))); } private static Stream provideArguments() { diff --git a/instrumentation/hibernate/hibernate-4.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_0/EntityManagerTest.java b/instrumentation/hibernate/hibernate-4.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_0/EntityManagerTest.java index b94fbccd4f2d..6811fd97898a 100644 --- a/instrumentation/hibernate/hibernate-4.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_0/EntityManagerTest.java +++ b/instrumentation/hibernate/hibernate-4.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_0/EntityManagerTest.java @@ -5,12 +5,14 @@ package io.opentelemetry.javaagent.instrumentation.hibernate.v4_0; -import static io.opentelemetry.api.common.AttributeKey.stringKey; import static io.opentelemetry.api.trace.SpanKind.CLIENT; import static io.opentelemetry.api.trace.SpanKind.INTERNAL; import static io.opentelemetry.instrumentation.api.internal.SemconvStability.emitStableDatabaseSemconv; import static io.opentelemetry.instrumentation.testing.junit.db.SemconvStabilityUtil.maybeStable; import static io.opentelemetry.instrumentation.testing.junit.db.SemconvStabilityUtil.maybeStableDbSystemName; +import static io.opentelemetry.javaagent.instrumentation.hibernate.ExperimentalTestHelper.HIBERNATE_SESSION_ID; +import static io.opentelemetry.javaagent.instrumentation.hibernate.ExperimentalTestHelper.experimental; +import static io.opentelemetry.javaagent.instrumentation.hibernate.ExperimentalTestHelper.experimentalSatisfies; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies; import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_CONNECTION_STRING; @@ -21,6 +23,7 @@ import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_SYSTEM; import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_USER; import static java.util.Collections.emptyList; +import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.junit.jupiter.api.Named.named; @@ -96,20 +99,18 @@ void testHibernateActions(Parameter parameter) { .hasKind(INTERNAL) .hasParent(trace.getSpan(0)) .hasAttributesSatisfyingExactly( - satisfies( - stringKey("hibernate.session_id"), - val -> val.isInstanceOf(String.class))), + experimentalSatisfies( + HIBERNATE_SESSION_ID, + val -> assertThat(val).isInstanceOf(String.class))), span -> span.hasName("Transaction.commit") .hasKind(INTERNAL) .hasParent(trace.getSpan(0)) .hasAttributesSatisfyingExactly( equalTo( - stringKey("hibernate.session_id"), - trace - .getSpan(1) - .getAttributes() - .get(stringKey("hibernate.session_id")))), + HIBERNATE_SESSION_ID, + experimental( + trace.getSpan(1).getAttributes().get(HIBERNATE_SESSION_ID)))), span -> span.hasKind(CLIENT) .hasParent(trace.getSpan(2)) @@ -138,9 +139,9 @@ void testHibernateActions(Parameter parameter) { .hasKind(INTERNAL) .hasParent(trace.getSpan(0)) .hasAttributesSatisfyingExactly( - satisfies( - stringKey("hibernate.session_id"), - val -> val.isInstanceOf(String.class))), + experimentalSatisfies( + HIBERNATE_SESSION_ID, + val -> assertThat(val).isInstanceOf(String.class))), span -> span.hasKind(CLIENT) .hasParent(trace.getSpan(1)) @@ -162,11 +163,9 @@ void testHibernateActions(Parameter parameter) { .hasParent(trace.getSpan(0)) .hasAttributesSatisfyingExactly( equalTo( - stringKey("hibernate.session_id"), - trace - .getSpan(1) - .getAttributes() - .get(stringKey("hibernate.session_id"))))); + HIBERNATE_SESSION_ID, + experimental( + trace.getSpan(1).getAttributes().get(HIBERNATE_SESSION_ID))))); } }); } @@ -248,11 +247,9 @@ void testHibernatePersist() { .hasParent(trace.getSpan(0)) .hasAttributesSatisfyingExactly( equalTo( - stringKey("hibernate.session_id"), - trace - .getSpan(1) - .getAttributes() - .get(stringKey("hibernate.session_id")))), + HIBERNATE_SESSION_ID, + experimental( + trace.getSpan(1).getAttributes().get(HIBERNATE_SESSION_ID)))), span -> span.hasKind(CLIENT) .hasParent(trace.getSpan(3)) @@ -298,9 +295,9 @@ void testAttachesStateToQuery(Function queryBuildMethod) { .hasKind(INTERNAL) .hasParent(trace.getSpan(0)) .hasAttributesSatisfyingExactly( - satisfies( - stringKey("hibernate.session_id"), - val -> val.isInstanceOf(String.class))), + experimentalSatisfies( + HIBERNATE_SESSION_ID, + val -> assertThat(val).isInstanceOf(String.class))), span -> span.hasName("SELECT db1.Value") .hasKind(CLIENT) @@ -323,11 +320,9 @@ void testAttachesStateToQuery(Function queryBuildMethod) { .hasParent(trace.getSpan(0)) .hasAttributesSatisfyingExactly( equalTo( - stringKey("hibernate.session_id"), - trace - .getSpan(1) - .getAttributes() - .get(stringKey("hibernate.session_id")))))); + HIBERNATE_SESSION_ID, + experimental( + trace.getSpan(1).getAttributes().get(HIBERNATE_SESSION_ID)))))); } @Test diff --git a/instrumentation/hibernate/hibernate-4.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_0/QueryTest.java b/instrumentation/hibernate/hibernate-4.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_0/QueryTest.java index 4e997d19ec28..6938558ec3da 100644 --- a/instrumentation/hibernate/hibernate-4.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_0/QueryTest.java +++ b/instrumentation/hibernate/hibernate-4.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_0/QueryTest.java @@ -5,12 +5,14 @@ package io.opentelemetry.javaagent.instrumentation.hibernate.v4_0; -import static io.opentelemetry.api.common.AttributeKey.stringKey; import static io.opentelemetry.api.trace.SpanKind.CLIENT; import static io.opentelemetry.api.trace.SpanKind.INTERNAL; import static io.opentelemetry.instrumentation.api.internal.SemconvStability.emitStableDatabaseSemconv; import static io.opentelemetry.instrumentation.testing.junit.db.SemconvStabilityUtil.maybeStable; import static io.opentelemetry.instrumentation.testing.junit.db.SemconvStabilityUtil.maybeStableDbSystemName; +import static io.opentelemetry.javaagent.instrumentation.hibernate.ExperimentalTestHelper.HIBERNATE_SESSION_ID; +import static io.opentelemetry.javaagent.instrumentation.hibernate.ExperimentalTestHelper.experimental; +import static io.opentelemetry.javaagent.instrumentation.hibernate.ExperimentalTestHelper.experimentalSatisfies; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies; import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_CONNECTION_STRING; @@ -20,6 +22,7 @@ import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_STATEMENT; import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_SYSTEM; import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_USER; +import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Named.named; import io.opentelemetry.api.common.Attributes; @@ -63,9 +66,9 @@ void testHibernateQueryExecuteUpdateWithTransaction() { .hasKind(INTERNAL) .hasParent(trace.getSpan(0)) .hasAttributesSatisfyingExactly( - satisfies( - stringKey("hibernate.session_id"), - val -> val.isInstanceOf(String.class))), + experimentalSatisfies( + HIBERNATE_SESSION_ID, + val -> assertThat(val).isInstanceOf(String.class))), span -> span.hasKind(CLIENT) .hasParent(trace.getSpan(1)) @@ -87,11 +90,9 @@ void testHibernateQueryExecuteUpdateWithTransaction() { .hasParent(trace.getSpan(0)) .hasAttributesSatisfyingExactly( equalTo( - stringKey("hibernate.session_id"), - trace - .getSpan(1) - .getAttributes() - .get(stringKey("hibernate.session_id")))))); + HIBERNATE_SESSION_ID, + experimental( + trace.getSpan(1).getAttributes().get(HIBERNATE_SESSION_ID)))))); } @SuppressWarnings("deprecation") // TODO DB_CONNECTION_STRING deprecation @@ -120,9 +121,9 @@ void testHibernateQuerySingleCall(Parameter parameter) { .hasKind(INTERNAL) .hasParent(trace.getSpan(0)) .hasAttributesSatisfyingExactly( - satisfies( - stringKey("hibernate.session_id"), - val -> val.isInstanceOf(String.class))), + experimentalSatisfies( + HIBERNATE_SESSION_ID, + val -> assertThat(val).isInstanceOf(String.class))), span -> span.hasName("SELECT db1.Value") .hasKind(CLIENT) @@ -194,9 +195,9 @@ void testHibernateQueryIterate() { .hasKind(INTERNAL) .hasParent(trace.getSpan(0)) .hasAttributesSatisfyingExactly( - satisfies( - stringKey("hibernate.session_id"), - val -> val.isInstanceOf(String.class))), + experimentalSatisfies( + HIBERNATE_SESSION_ID, + val -> assertThat(val).isInstanceOf(String.class))), span -> span.hasName("SELECT db1.Value") .hasKind(CLIENT) @@ -217,11 +218,9 @@ void testHibernateQueryIterate() { .hasParent(trace.getSpan(0)) .hasAttributesSatisfyingExactly( equalTo( - stringKey("hibernate.session_id"), - trace - .getSpan(1) - .getAttributes() - .get(stringKey("hibernate.session_id")))))); + HIBERNATE_SESSION_ID, + experimental( + trace.getSpan(1).getAttributes().get(HIBERNATE_SESSION_ID)))))); } private static class Parameter { diff --git a/instrumentation/hibernate/hibernate-4.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_0/SessionTest.java b/instrumentation/hibernate/hibernate-4.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_0/SessionTest.java index b524440a4f9c..2642eaf53b39 100644 --- a/instrumentation/hibernate/hibernate-4.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_0/SessionTest.java +++ b/instrumentation/hibernate/hibernate-4.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_0/SessionTest.java @@ -5,12 +5,14 @@ package io.opentelemetry.javaagent.instrumentation.hibernate.v4_0; -import static io.opentelemetry.api.common.AttributeKey.stringKey; import static io.opentelemetry.api.trace.SpanKind.CLIENT; import static io.opentelemetry.api.trace.SpanKind.INTERNAL; import static io.opentelemetry.instrumentation.api.internal.SemconvStability.emitStableDatabaseSemconv; import static io.opentelemetry.instrumentation.testing.junit.db.SemconvStabilityUtil.maybeStable; import static io.opentelemetry.instrumentation.testing.junit.db.SemconvStabilityUtil.maybeStableDbSystemName; +import static io.opentelemetry.javaagent.instrumentation.hibernate.ExperimentalTestHelper.HIBERNATE_SESSION_ID; +import static io.opentelemetry.javaagent.instrumentation.hibernate.ExperimentalTestHelper.experimental; +import static io.opentelemetry.javaagent.instrumentation.hibernate.ExperimentalTestHelper.experimentalSatisfies; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies; import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_CONNECTION_STRING; @@ -22,8 +24,10 @@ import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_USER; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.ThrowableAssert.catchThrowable; +import static org.junit.jupiter.api.Assumptions.assumeTrue; import static org.junit.jupiter.api.Named.named; +import io.opentelemetry.javaagent.instrumentation.hibernate.ExperimentalTestHelper; import io.opentelemetry.sdk.trace.data.StatusData; import java.util.concurrent.atomic.AtomicReference; import java.util.function.BiConsumer; @@ -66,9 +70,9 @@ void testHibernateAction(Parameter parameter) { .hasKind(INTERNAL) .hasParent(trace.getSpan(0)) .hasAttributesSatisfyingExactly( - satisfies( - stringKey("hibernate.session_id"), - val -> val.isInstanceOf(String.class))), + experimentalSatisfies( + HIBERNATE_SESSION_ID, + val -> assertThat(val).isInstanceOf(String.class))), span -> span.hasKind(CLIENT) .hasParent(trace.getSpan(1)) @@ -90,11 +94,9 @@ void testHibernateAction(Parameter parameter) { .hasParent(trace.getSpan(0)) .hasAttributesSatisfyingExactly( equalTo( - stringKey("hibernate.session_id"), - trace - .getSpan(1) - .getAttributes() - .get(stringKey("hibernate.session_id")))))); + HIBERNATE_SESSION_ID, + experimental( + trace.getSpan(1).getAttributes().get(HIBERNATE_SESSION_ID)))))); } private static Stream provideArgumentsHibernateAction() { @@ -177,9 +179,9 @@ void testHibernateActionStateless(Parameter parameter) { .hasKind(INTERNAL) .hasParent(trace.getSpan(0)) .hasAttributesSatisfyingExactly( - satisfies( - stringKey("hibernate.session_id"), - val -> val.isInstanceOf(String.class))), + experimentalSatisfies( + HIBERNATE_SESSION_ID, + val -> assertThat(val).isInstanceOf(String.class))), span -> span.hasKind(CLIENT) .hasParent(trace.getSpan(1)) @@ -201,11 +203,9 @@ void testHibernateActionStateless(Parameter parameter) { .hasParent(trace.getSpan(0)) .hasAttributesSatisfyingExactly( equalTo( - stringKey("hibernate.session_id"), - trace - .getSpan(1) - .getAttributes() - .get(stringKey("hibernate.session_id")))))); + HIBERNATE_SESSION_ID, + experimental( + trace.getSpan(1).getAttributes().get(HIBERNATE_SESSION_ID)))))); } private static Stream provideArgumentsHibernateActionStateless() { @@ -324,9 +324,9 @@ void testHibernateReplicate(Parameter parameter) { .hasKind(INTERNAL) .hasParent(trace.getSpan(0)) .hasAttributesSatisfyingExactly( - satisfies( - stringKey("hibernate.session_id"), - val -> val.isInstanceOf(String.class))), + experimentalSatisfies( + HIBERNATE_SESSION_ID, + val -> assertThat(val).isInstanceOf(String.class))), span -> span.hasKind(CLIENT) .hasParent(trace.getSpan(1)) @@ -348,11 +348,9 @@ void testHibernateReplicate(Parameter parameter) { .hasParent(trace.getSpan(0)) .hasAttributesSatisfyingExactly( equalTo( - stringKey("hibernate.session_id"), - trace - .getSpan(1) - .getAttributes() - .get(stringKey("hibernate.session_id")))), + HIBERNATE_SESSION_ID, + experimental( + trace.getSpan(1).getAttributes().get(HIBERNATE_SESSION_ID)))), span -> span.hasKind(CLIENT) .hasParent(trace.getSpan(3)) @@ -430,11 +428,8 @@ void testHibernateFailedReplicate() { .hasStatus(StatusData.error()) .hasAttributesSatisfyingExactly( equalTo( - stringKey("hibernate.session_id"), - trace - .getSpan(1) - .getAttributes() - .get(stringKey("hibernate.session_id")))) + HIBERNATE_SESSION_ID, + trace.getSpan(1).getAttributes().get(HIBERNATE_SESSION_ID))) .hasException(mappingException), span -> span.hasName("Transaction.commit") @@ -442,11 +437,8 @@ void testHibernateFailedReplicate() { .hasParent(trace.getSpan(0)) .hasAttributesSatisfyingExactly( equalTo( - stringKey("hibernate.session_id"), - trace - .getSpan(1) - .getAttributes() - .get(stringKey("hibernate.session_id")))))); + HIBERNATE_SESSION_ID, + trace.getSpan(1).getAttributes().get(HIBERNATE_SESSION_ID))))); } @SuppressWarnings("deprecation") // TODO DB_CONNECTION_STRING deprecation @@ -473,20 +465,18 @@ void testHibernateCommitAction(Parameter parameter) { .hasKind(INTERNAL) .hasParent(trace.getSpan(0)) .hasAttributesSatisfyingExactly( - satisfies( - stringKey("hibernate.session_id"), - val -> val.isInstanceOf(String.class))), + experimentalSatisfies( + HIBERNATE_SESSION_ID, + val -> assertThat(val).isInstanceOf(String.class))), span -> span.hasName("Transaction.commit") .hasKind(INTERNAL) .hasParent(trace.getSpan(0)) .hasAttributesSatisfyingExactly( equalTo( - stringKey("hibernate.session_id"), - trace - .getSpan(1) - .getAttributes() - .get(stringKey("hibernate.session_id")))), + HIBERNATE_SESSION_ID, + experimental( + trace.getSpan(1).getAttributes().get(HIBERNATE_SESSION_ID)))), span -> span.hasKind(CLIENT) .hasParent(trace.getSpan(2)) @@ -663,9 +653,9 @@ void testAttachesStateToQueryCreated(Consumer queryBuilder) { .hasKind(INTERNAL) .hasParent(trace.getSpan(0)) .hasAttributesSatisfyingExactly( - satisfies( - stringKey("hibernate.session_id"), - val -> val.isInstanceOf(String.class))), + experimentalSatisfies( + HIBERNATE_SESSION_ID, + val -> assertThat(val).isInstanceOf(String.class))), span -> span.hasKind(CLIENT) .hasParent(trace.getSpan(1)) @@ -687,11 +677,9 @@ void testAttachesStateToQueryCreated(Consumer queryBuilder) { .hasParent(trace.getSpan(0)) .hasAttributesSatisfyingExactly( equalTo( - stringKey("hibernate.session_id"), - trace - .getSpan(1) - .getAttributes() - .get(stringKey("hibernate.session_id")))))); + HIBERNATE_SESSION_ID, + experimental( + trace.getSpan(1).getAttributes().get(HIBERNATE_SESSION_ID)))))); } private static Stream provideArgumentsStateQuery() { @@ -715,6 +703,8 @@ private static Stream provideArgumentsStateQuery() { @SuppressWarnings("deprecation") // TODO DB_CONNECTION_STRING deprecation @Test void testHibernateOverlappingSessions() { + assumeTrue(ExperimentalTestHelper.isEnabled); // needs experimental session id + testing.runWithSpan( "overlapping Sessions", () -> { @@ -749,22 +739,16 @@ void testHibernateOverlappingSessions() { .hasKind(INTERNAL) .hasParent(trace.getSpan(0)) .hasAttributesSatisfyingExactly( - satisfies( - stringKey("hibernate.session_id"), - val -> val.isInstanceOf(String.class))); - sessionId1.set( - trace.getSpan(1).getAttributes().get(stringKey("hibernate.session_id"))); + satisfies(HIBERNATE_SESSION_ID, val -> val.isInstanceOf(String.class))); + sessionId1.set(trace.getSpan(1).getAttributes().get(HIBERNATE_SESSION_ID)); }, span -> { span.hasName("Session.insert " + Value.class.getName()) .hasKind(INTERNAL) .hasParent(trace.getSpan(0)) .hasAttributesSatisfyingExactly( - satisfies( - stringKey("hibernate.session_id"), - val -> val.isInstanceOf(String.class))); - sessionId2.set( - trace.getSpan(2).getAttributes().get(stringKey("hibernate.session_id"))); + satisfies(HIBERNATE_SESSION_ID, val -> val.isInstanceOf(String.class))); + sessionId2.set(trace.getSpan(2).getAttributes().get(HIBERNATE_SESSION_ID)); }, span -> span.hasName("INSERT db1.Value") @@ -787,31 +771,23 @@ void testHibernateOverlappingSessions() { .hasKind(INTERNAL) .hasParent(trace.getSpan(0)) .hasAttributesSatisfyingExactly( - satisfies( - stringKey("hibernate.session_id"), - val -> val.isInstanceOf(String.class))); - sessionId3.set( - trace.getSpan(4).getAttributes().get(stringKey("hibernate.session_id"))); + satisfies(HIBERNATE_SESSION_ID, val -> val.isInstanceOf(String.class))); + sessionId3.set(trace.getSpan(4).getAttributes().get(HIBERNATE_SESSION_ID)); }, span -> span.hasName("Session.delete " + Value.class.getName()) .hasKind(INTERNAL) .hasParent(trace.getSpan(0)) .hasAttributesSatisfyingExactly( - satisfies( - stringKey("hibernate.session_id"), - val -> val.isInstanceOf(String.class))), + satisfies(HIBERNATE_SESSION_ID, val -> val.isInstanceOf(String.class))), span -> span.hasName("Transaction.commit") .hasKind(INTERNAL) .hasParent(trace.getSpan(0)) .hasAttributesSatisfyingExactly( equalTo( - stringKey("hibernate.session_id"), - trace - .getSpan(1) - .getAttributes() - .get(stringKey("hibernate.session_id")))), + HIBERNATE_SESSION_ID, + trace.getSpan(1).getAttributes().get(HIBERNATE_SESSION_ID))), span -> span.hasName("INSERT db1.Value") .hasKind(CLIENT) diff --git a/instrumentation/hibernate/hibernate-4.0/javaagent/src/test/java/spring/jpa/SpringJpaTest.java b/instrumentation/hibernate/hibernate-4.0/javaagent/src/test/java/spring/jpa/SpringJpaTest.java index 56170ea88b26..d2f67a542b25 100644 --- a/instrumentation/hibernate/hibernate-4.0/javaagent/src/test/java/spring/jpa/SpringJpaTest.java +++ b/instrumentation/hibernate/hibernate-4.0/javaagent/src/test/java/spring/jpa/SpringJpaTest.java @@ -10,6 +10,8 @@ import static io.opentelemetry.api.trace.SpanKind.INTERNAL; import static io.opentelemetry.instrumentation.api.internal.SemconvStability.emitStableDatabaseSemconv; import static io.opentelemetry.instrumentation.testing.junit.db.SemconvStabilityUtil.maybeStable; +import static io.opentelemetry.javaagent.instrumentation.hibernate.ExperimentalTestHelper.experimental; +import static io.opentelemetry.javaagent.instrumentation.hibernate.ExperimentalTestHelper.experimentalSatisfies; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies; import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_CONNECTION_STRING; @@ -66,9 +68,9 @@ void testCrud() { .hasKind(INTERNAL) .hasParent(trace.getSpan(0)) .hasAttributesSatisfyingExactly( - satisfies( + experimentalSatisfies( stringKey("hibernate.session_id"), - val -> val.isInstanceOf(String.class))), + val -> assertThat(val).isInstanceOf(String.class))), span -> span.hasName("SELECT test.Customer") .hasKind(CLIENT) @@ -95,10 +97,11 @@ void testCrud() { .hasAttributesSatisfyingExactly( equalTo( stringKey("hibernate.session_id"), - trace - .getSpan(1) - .getAttributes() - .get(stringKey("hibernate.session_id")))))); + experimental( + trace + .getSpan(1) + .getAttributes() + .get(stringKey("hibernate.session_id"))))))); testing.clearData(); testing.runWithSpan( @@ -123,9 +126,9 @@ void testCrud() { .hasKind(INTERNAL) .hasParent(trace.getSpan(0)) .hasAttributesSatisfyingExactly( - satisfies( + experimentalSatisfies( stringKey("hibernate.session_id"), - val -> val.isInstanceOf(String.class))), + val -> assertThat(val).isInstanceOf(String.class))), span -> span.hasName("INSERT test.Customer") .hasKind(CLIENT) @@ -152,10 +155,11 @@ void testCrud() { .hasAttributesSatisfyingExactly( equalTo( stringKey("hibernate.session_id"), - trace - .getSpan(1) - .getAttributes() - .get(stringKey("hibernate.session_id"))))); + experimental( + trace + .getSpan(1) + .getAttributes() + .get(stringKey("hibernate.session_id")))))); } else { trace.hasSpansSatisfyingExactly( span -> @@ -168,9 +172,9 @@ void testCrud() { .hasKind(INTERNAL) .hasParent(trace.getSpan(0)) .hasAttributesSatisfyingExactly( - satisfies( + experimentalSatisfies( stringKey("hibernate.session_id"), - val -> val.isInstanceOf(String.class))), + val -> assertThat(val).isInstanceOf(String.class))), span -> span.hasName("CALL test") .hasKind(CLIENT) @@ -193,10 +197,11 @@ void testCrud() { .hasAttributesSatisfyingExactly( equalTo( stringKey("hibernate.session_id"), - trace - .getSpan(1) - .getAttributes() - .get(stringKey("hibernate.session_id")))), + experimental( + trace + .getSpan(1) + .getAttributes() + .get(stringKey("hibernate.session_id"))))), span -> span.hasName("INSERT test.Customer") .hasKind(CLIENT) @@ -243,9 +248,9 @@ void testCrud() { .hasKind(INTERNAL) .hasParent(trace.getSpan(0)) .hasAttributesSatisfyingExactly( - satisfies( + experimentalSatisfies( stringKey("hibernate.session_id"), - val -> val.isInstanceOf(String.class))), + val -> assertThat(val).isInstanceOf(String.class))), span -> span.hasName("SELECT test.Customer") .hasKind(CLIENT) @@ -272,10 +277,11 @@ void testCrud() { .hasAttributesSatisfyingExactly( equalTo( stringKey("hibernate.session_id"), - trace - .getSpan(1) - .getAttributes() - .get(stringKey("hibernate.session_id")))), + experimental( + trace + .getSpan(1) + .getAttributes() + .get(stringKey("hibernate.session_id"))))), span -> span.hasName("UPDATE test.Customer") .hasKind(CLIENT) @@ -313,9 +319,9 @@ void testCrud() { .hasKind(INTERNAL) .hasParent(trace.getSpan(0)) .hasAttributesSatisfyingExactly( - satisfies( + experimentalSatisfies( stringKey("hibernate.session_id"), - val -> val.isInstanceOf(String.class))), + val -> assertThat(val).isInstanceOf(String.class))), span -> span.hasName("SELECT test.Customer") .hasKind(CLIENT) @@ -353,9 +359,9 @@ void testCrud() { .hasKind(INTERNAL) .hasParent(trace.getSpan(0)) .hasAttributesSatisfyingExactly( - satisfies( + experimentalSatisfies( stringKey("hibernate.session_id"), - val -> val.isInstanceOf(String.class))), + val -> assertThat(val).isInstanceOf(String.class))), span -> span.hasName("SELECT test.Customer") .hasKind(CLIENT) @@ -380,17 +386,17 @@ void testCrud() { .hasKind(INTERNAL) .hasParent(trace.getSpan(0)) .hasAttributesSatisfyingExactly( - satisfies( + experimentalSatisfies( stringKey("hibernate.session_id"), - val -> val.isInstanceOf(String.class))), + val -> assertThat(val).isInstanceOf(String.class))), span -> span.hasName("Transaction.commit") .hasKind(INTERNAL) .hasParent(trace.getSpan(0)) .hasAttributesSatisfyingExactly( - satisfies( + experimentalSatisfies( stringKey("hibernate.session_id"), - val -> val.isInstanceOf(String.class))), + val -> assertThat(val).isInstanceOf(String.class))), span -> span.hasName("DELETE test.Customer") .hasKind(CLIENT) @@ -420,9 +426,9 @@ void testCrud() { .hasKind(INTERNAL) .hasParent(trace.getSpan(0)) .hasAttributesSatisfyingExactly( - satisfies( + experimentalSatisfies( stringKey("hibernate.session_id"), - val -> val.isInstanceOf(String.class))), + val -> assertThat(val).isInstanceOf(String.class))), span -> span.hasName("SELECT test.Customer") .hasKind(CLIENT) @@ -447,25 +453,25 @@ void testCrud() { .hasKind(INTERNAL) .hasParent(trace.getSpan(0)) .hasAttributesSatisfyingExactly( - satisfies( + experimentalSatisfies( stringKey("hibernate.session_id"), - val -> val.isInstanceOf(String.class))), + val -> assertThat(val).isInstanceOf(String.class))), span -> span.hasName("Session.delete spring.jpa.Customer") .hasKind(INTERNAL) .hasParent(trace.getSpan(0)) .hasAttributesSatisfyingExactly( - satisfies( + experimentalSatisfies( stringKey("hibernate.session_id"), - val -> val.isInstanceOf(String.class))), + val -> assertThat(val).isInstanceOf(String.class))), span -> span.hasName("Transaction.commit") .hasKind(INTERNAL) .hasParent(trace.getSpan(0)) .hasAttributesSatisfyingExactly( - satisfies( + experimentalSatisfies( stringKey("hibernate.session_id"), - val -> val.isInstanceOf(String.class))), + val -> assertThat(val).isInstanceOf(String.class))), span -> span.hasName("DELETE test.Customer") .hasKind(CLIENT) diff --git a/instrumentation/hibernate/hibernate-4.0/metadata.yaml b/instrumentation/hibernate/hibernate-4.0/metadata.yaml new file mode 100644 index 000000000000..f8ee68b40f38 --- /dev/null +++ b/instrumentation/hibernate/hibernate-4.0/metadata.yaml @@ -0,0 +1,9 @@ +description: > + This instrumentation enables the generation of INTERNAL spans for Hibernate operations, including + session methods (e.g., `save`, `update`, `delete`), transaction commits, and query executions. +library_link: https://github.com/hibernate/hibernate-orm +configurations: + - name: otel.instrumentation.hibernate.experimental-span-attributes + type: boolean + description: Enables the addition of the experimental `hibernate.session_id` span attribute. + default: false diff --git a/instrumentation/hibernate/hibernate-6.0/javaagent/build.gradle.kts b/instrumentation/hibernate/hibernate-6.0/javaagent/build.gradle.kts index e55b3d811672..e5faee1dda36 100644 --- a/instrumentation/hibernate/hibernate-6.0/javaagent/build.gradle.kts +++ b/instrumentation/hibernate/hibernate-6.0/javaagent/build.gradle.kts @@ -28,6 +28,7 @@ dependencies { testImplementation("com.sun.xml.bind:jaxb-impl:2.2.11") testImplementation("javax.activation:activation:1.1.1") testImplementation("org.hsqldb:hsqldb:2.0.0") + testImplementation(project(":instrumentation:hibernate:testing")) testLibrary("org.springframework.data:spring-data-jpa:3.0.0") } @@ -40,6 +41,11 @@ val latestDepTest = findProperty("testLatestDeps") as Boolean testing { suites { val hibernate6Test by registering(JvmTestSuite::class) { + targets.all { + testTask.configure { + jvmArgs("-Dotel.instrumentation.hibernate.experimental-span-attributes=true") + } + } dependencies { implementation("com.h2database:h2:1.4.197") implementation("org.hsqldb:hsqldb:2.0.0") @@ -52,6 +58,11 @@ testing { } val hibernate7Test by registering(JvmTestSuite::class) { + targets.all { + testTask.configure { + jvmArgs("-Dotel.instrumentation.hibernate.experimental-span-attributes=true") + } + } dependencies { implementation("com.h2database:h2:1.4.197") implementation("org.hsqldb:hsqldb:2.0.0") @@ -67,8 +78,7 @@ testing { tasks { withType().configureEach { - // TODO run tests both with and without experimental span attributes - jvmArgs("-Dotel.instrumentation.hibernate.experimental-span-attributes=true") + systemProperty("collectMetadata", findProperty("collectMetadata")?.toString() ?: "false") } named("compileHibernate7TestJava", JavaCompile::class).configure { @@ -83,12 +93,22 @@ tasks { } } + val testExperimental by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath + + jvmArgs("-Dotel.instrumentation.hibernate.experimental-span-attributes=true") + systemProperty("metadataConfig", "otel.instrumentation.hibernate.experimental-span-attributes=true") + } + val testStableSemconv by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath + jvmArgs("-Dotel.semconv-stability.opt-in=database") } check { - dependsOn(testStableSemconv) - dependsOn(testing.suites) + dependsOn(testing.suites, testStableSemconv, testExperimental) } } diff --git a/instrumentation/hibernate/hibernate-6.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v6_0/CriteriaTest.java b/instrumentation/hibernate/hibernate-6.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v6_0/CriteriaTest.java index f4a78cb78ecb..7ecbb4953faa 100644 --- a/instrumentation/hibernate/hibernate-6.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v6_0/CriteriaTest.java +++ b/instrumentation/hibernate/hibernate-6.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v6_0/CriteriaTest.java @@ -8,6 +8,9 @@ import static io.opentelemetry.instrumentation.api.internal.SemconvStability.emitStableDatabaseSemconv; import static io.opentelemetry.instrumentation.testing.junit.db.SemconvStabilityUtil.maybeStable; import static io.opentelemetry.instrumentation.testing.junit.db.SemconvStabilityUtil.maybeStableDbSystemName; +import static io.opentelemetry.javaagent.instrumentation.hibernate.ExperimentalTestHelper.HIBERNATE_SESSION_ID; +import static io.opentelemetry.javaagent.instrumentation.hibernate.ExperimentalTestHelper.experimental; +import static io.opentelemetry.javaagent.instrumentation.hibernate.ExperimentalTestHelper.experimentalSatisfies; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies; import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_CONNECTION_STRING; @@ -17,9 +20,9 @@ import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_STATEMENT; import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_SYSTEM; import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_USER; +import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Named.named; -import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.api.trace.SpanKind; import jakarta.persistence.criteria.CriteriaBuilder; import jakarta.persistence.criteria.CriteriaQuery; @@ -77,9 +80,9 @@ void testCriteriaQuery(Consumer> interaction) { .hasKind(SpanKind.INTERNAL) .hasParent(trace.getSpan(0)) .hasAttributesSatisfyingExactly( - satisfies( - AttributeKey.stringKey("hibernate.session_id"), - val -> val.isInstanceOf(String.class))), + experimentalSatisfies( + HIBERNATE_SESSION_ID, + val -> assertThat(val).isInstanceOf(String.class))), span -> span.hasName("SELECT db1.Value") .hasKind(SpanKind.CLIENT) @@ -102,10 +105,8 @@ void testCriteriaQuery(Consumer> interaction) { .hasParent(trace.getSpan(0)) .hasAttributesSatisfyingExactly( equalTo( - AttributeKey.stringKey("hibernate.session_id"), - trace - .getSpan(1) - .getAttributes() - .get(AttributeKey.stringKey("hibernate.session_id")))))); + HIBERNATE_SESSION_ID, + experimental( + trace.getSpan(1).getAttributes().get(HIBERNATE_SESSION_ID)))))); } } diff --git a/instrumentation/hibernate/hibernate-6.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v6_0/EntityManagerTest.java b/instrumentation/hibernate/hibernate-6.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v6_0/EntityManagerTest.java index 1adc80598757..d983709f338d 100644 --- a/instrumentation/hibernate/hibernate-6.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v6_0/EntityManagerTest.java +++ b/instrumentation/hibernate/hibernate-6.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v6_0/EntityManagerTest.java @@ -8,6 +8,9 @@ import static io.opentelemetry.instrumentation.api.internal.SemconvStability.emitStableDatabaseSemconv; import static io.opentelemetry.instrumentation.testing.junit.db.SemconvStabilityUtil.maybeStable; import static io.opentelemetry.instrumentation.testing.junit.db.SemconvStabilityUtil.maybeStableDbSystemName; +import static io.opentelemetry.javaagent.instrumentation.hibernate.ExperimentalTestHelper.HIBERNATE_SESSION_ID; +import static io.opentelemetry.javaagent.instrumentation.hibernate.ExperimentalTestHelper.experimental; +import static io.opentelemetry.javaagent.instrumentation.hibernate.ExperimentalTestHelper.experimentalSatisfies; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies; import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_CONNECTION_STRING; @@ -18,10 +21,10 @@ import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_SYSTEM; import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_USER; import static java.util.Collections.emptyList; +import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.junit.jupiter.api.Named.named; -import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.api.trace.SpanKind; import io.opentelemetry.sdk.testing.assertj.SpanDataAssert; import io.opentelemetry.sdk.trace.data.SpanData; @@ -56,12 +59,7 @@ void testHibernateAction(Parameter parameter) { Value entity; if (parameter.attach) { - entity = - testing.runWithSpan( - "setup", - () -> { - return entityManager.merge(prepopulated.get(0)); - }); + entity = testing.runWithSpan("setup", () -> entityManager.merge(prepopulated.get(0))); testing.clearData(); } else { entity = prepopulated.get(0); @@ -99,10 +97,7 @@ void testHibernateAction(Parameter parameter) { assertTransactionCommitSpan( span, trace.getSpan(0), - trace - .getSpan(1) - .getAttributes() - .get(AttributeKey.stringKey("hibernate.session_id")))); + experimental(trace.getSpan(1).getAttributes().get(HIBERNATE_SESSION_ID)))); } else { trace.hasSpansSatisfyingExactlyInAnyOrder( span -> span.hasName("parent").hasKind(SpanKind.INTERNAL).hasNoParent(), @@ -118,10 +113,7 @@ void testHibernateAction(Parameter parameter) { assertTransactionCommitSpan( span, trace.getSpan(0), - trace - .getSpan(1) - .getAttributes() - .get(AttributeKey.stringKey("hibernate.session_id")))); + experimental(trace.getSpan(1).getAttributes().get(HIBERNATE_SESSION_ID)))); } }); } @@ -166,10 +158,7 @@ void testAttachesStateToQuery(Parameter parameter) { assertTransactionCommitSpan( span, trace.getSpan(0), - trace - .getSpan(1) - .getAttributes() - .get(AttributeKey.stringKey("hibernate.session_id"))))); + experimental(trace.getSpan(1).getAttributes().get(HIBERNATE_SESSION_ID))))); } @Test @@ -326,8 +315,8 @@ private static class Parameter { } @SuppressWarnings("deprecation") // TODO DB_CONNECTION_STRING deprecation - private static SpanDataAssert assertClientSpan(SpanDataAssert span, SpanData parent) { - return span.hasKind(SpanKind.CLIENT) + private static void assertClientSpan(SpanDataAssert span, SpanData parent) { + span.hasKind(SpanKind.CLIENT) .hasParent(parent) .hasAttributesSatisfyingExactly( equalTo(maybeStable(DB_SYSTEM), maybeStableDbSystemName("h2")), @@ -340,9 +329,8 @@ private static SpanDataAssert assertClientSpan(SpanDataAssert span, SpanData par } @SuppressWarnings("deprecation") // TODO DB_CONNECTION_STRING deprecation - private static SpanDataAssert assertClientSpan( - SpanDataAssert span, SpanData parent, String spanName) { - return span.hasName(spanName) + private static void assertClientSpan(SpanDataAssert span, SpanData parent, String spanName) { + span.hasName(spanName) .hasKind(SpanKind.CLIENT) .hasParent(parent) .hasAttributesSatisfyingExactly( @@ -355,23 +343,20 @@ private static SpanDataAssert assertClientSpan( equalTo(maybeStable(DB_SQL_TABLE), "Value")); } - private static SpanDataAssert assertSessionSpan( - SpanDataAssert span, SpanData parent, String spanName) { - return span.hasName(spanName) + private static void assertSessionSpan(SpanDataAssert span, SpanData parent, String spanName) { + span.hasName(spanName) .hasKind(SpanKind.INTERNAL) .hasParent(parent) .hasAttributesSatisfyingExactly( - satisfies( - AttributeKey.stringKey("hibernate.session_id"), - val -> val.isInstanceOf(String.class))); + experimentalSatisfies( + HIBERNATE_SESSION_ID, val -> assertThat(val).isInstanceOf(String.class))); } - private static SpanDataAssert assertTransactionCommitSpan( + private static void assertTransactionCommitSpan( SpanDataAssert span, SpanData parent, String sessionId) { - return span.hasName("Transaction.commit") + span.hasName("Transaction.commit") .hasKind(SpanKind.INTERNAL) .hasParent(parent) - .hasAttributesSatisfyingExactly( - equalTo(AttributeKey.stringKey("hibernate.session_id"), sessionId)); + .hasAttributesSatisfyingExactly(equalTo(HIBERNATE_SESSION_ID, sessionId)); } } diff --git a/instrumentation/hibernate/hibernate-6.0/metadata.yaml b/instrumentation/hibernate/hibernate-6.0/metadata.yaml new file mode 100644 index 000000000000..f8ee68b40f38 --- /dev/null +++ b/instrumentation/hibernate/hibernate-6.0/metadata.yaml @@ -0,0 +1,9 @@ +description: > + This instrumentation enables the generation of INTERNAL spans for Hibernate operations, including + session methods (e.g., `save`, `update`, `delete`), transaction commits, and query executions. +library_link: https://github.com/hibernate/hibernate-orm +configurations: + - name: otel.instrumentation.hibernate.experimental-span-attributes + type: boolean + description: Enables the addition of the experimental `hibernate.session_id` span attribute. + default: false diff --git a/instrumentation/hibernate/hibernate-6.0/spring-testing/build.gradle.kts b/instrumentation/hibernate/hibernate-6.0/spring-testing/build.gradle.kts index 4a00fb6e5a56..fa25ca965b2b 100644 --- a/instrumentation/hibernate/hibernate-6.0/spring-testing/build.gradle.kts +++ b/instrumentation/hibernate/hibernate-6.0/spring-testing/build.gradle.kts @@ -35,6 +35,9 @@ tasks { } val testStableSemconv by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath + jvmArgs("-Dotel.semconv-stability.opt-in=database") } diff --git a/instrumentation/hibernate/hibernate-procedure-call-4.3/javaagent/build.gradle.kts b/instrumentation/hibernate/hibernate-procedure-call-4.3/javaagent/build.gradle.kts index dbf4fd8ee5da..acb5b3423cdf 100644 --- a/instrumentation/hibernate/hibernate-procedure-call-4.3/javaagent/build.gradle.kts +++ b/instrumentation/hibernate/hibernate-procedure-call-4.3/javaagent/build.gradle.kts @@ -17,6 +17,7 @@ dependencies { implementation(project(":instrumentation:hibernate:hibernate-common:javaagent")) testInstrumentation(project(":instrumentation:jdbc:javaagent")) + testImplementation(project(":instrumentation:hibernate:testing")) // Added to ensure cross compatibility: testInstrumentation(project(":instrumentation:hibernate:hibernate-3.3:javaagent")) testInstrumentation(project(":instrumentation:hibernate:hibernate-4.0:javaagent")) @@ -33,15 +34,25 @@ dependencies { tasks { withType().configureEach { - // TODO run tests both with and without experimental span attributes + systemProperty("collectMetadata", findProperty("collectMetadata")?.toString() ?: "false") + } + + val testExperimental by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath + jvmArgs("-Dotel.instrumentation.hibernate.experimental-span-attributes=true") + systemProperty("metadataConfig", "otel.instrumentation.hibernate.experimental-span-attributes=true") } val testStableSemconv by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath + jvmArgs("-Dotel.semconv-stability.opt-in=database") } check { - dependsOn(testStableSemconv) + dependsOn(testStableSemconv, testExperimental) } } diff --git a/instrumentation/hibernate/hibernate-procedure-call-4.3/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_3/ProcedureCallTest.java b/instrumentation/hibernate/hibernate-procedure-call-4.3/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_3/ProcedureCallTest.java index 7b9471d06b4d..49ddba42ce93 100644 --- a/instrumentation/hibernate/hibernate-procedure-call-4.3/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_3/ProcedureCallTest.java +++ b/instrumentation/hibernate/hibernate-procedure-call-4.3/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_3/ProcedureCallTest.java @@ -5,13 +5,14 @@ package io.opentelemetry.javaagent.instrumentation.hibernate.v4_3; -import static io.opentelemetry.api.common.AttributeKey.stringKey; import static io.opentelemetry.api.trace.SpanKind.CLIENT; import static io.opentelemetry.api.trace.SpanKind.INTERNAL; import static io.opentelemetry.instrumentation.api.internal.SemconvStability.emitStableDatabaseSemconv; import static io.opentelemetry.instrumentation.testing.junit.db.SemconvStabilityUtil.maybeStable; +import static io.opentelemetry.javaagent.instrumentation.hibernate.ExperimentalTestHelper.HIBERNATE_SESSION_ID; +import static io.opentelemetry.javaagent.instrumentation.hibernate.ExperimentalTestHelper.experimental; +import static io.opentelemetry.javaagent.instrumentation.hibernate.ExperimentalTestHelper.experimentalSatisfies; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; -import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies; import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_CONNECTION_STRING; import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_NAME; import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_OPERATION; @@ -113,9 +114,9 @@ void testProcedureCall() { .hasKind(INTERNAL) .hasParent(trace.getSpan(0)) .hasAttributesSatisfyingExactly( - satisfies( - stringKey("hibernate.session_id"), - val -> val.isInstanceOf(String.class))), + experimentalSatisfies( + HIBERNATE_SESSION_ID, + val -> assertThat(val).isInstanceOf(String.class))), span -> span.hasName("CALL test.TEST_PROC") .hasKind(CLIENT) @@ -135,11 +136,9 @@ void testProcedureCall() { .hasParent(trace.getSpan(0)) .hasAttributesSatisfyingExactly( equalTo( - stringKey("hibernate.session_id"), - trace - .getSpan(1) - .getAttributes() - .get(stringKey("hibernate.session_id")))))); + HIBERNATE_SESSION_ID, + experimental( + trace.getSpan(1).getAttributes().get(HIBERNATE_SESSION_ID)))))); } @Test @@ -181,19 +180,17 @@ void testFailingProcedureCall() { .hasException(exception) .hasStatus(StatusData.error()) .hasAttributesSatisfyingExactly( - satisfies( - stringKey("hibernate.session_id"), - val -> val.isInstanceOf(String.class))), + experimentalSatisfies( + HIBERNATE_SESSION_ID, + val -> assertThat(val).isInstanceOf(String.class))), span -> span.hasName("Transaction.commit") .hasKind(INTERNAL) .hasParent(trace.getSpan(0)) .hasAttributesSatisfyingExactly( equalTo( - stringKey("hibernate.session_id"), - trace - .getSpan(1) - .getAttributes() - .get(stringKey("hibernate.session_id")))))); + HIBERNATE_SESSION_ID, + experimental( + trace.getSpan(1).getAttributes().get(HIBERNATE_SESSION_ID)))))); } } diff --git a/instrumentation/hibernate/hibernate-procedure-call-4.3/metadata.yaml b/instrumentation/hibernate/hibernate-procedure-call-4.3/metadata.yaml new file mode 100644 index 000000000000..9ef27db898cd --- /dev/null +++ b/instrumentation/hibernate/hibernate-procedure-call-4.3/metadata.yaml @@ -0,0 +1,7 @@ +description: This instrumentation enables the generation of INTERNAL spans for Hibernate stored procedure calls. +library_link: https://github.com/hibernate/hibernate-orm +configurations: + - name: otel.instrumentation.hibernate.experimental-span-attributes + type: boolean + description: Enables the addition of the experimental `hibernate.session_id` span attribute. + default: false diff --git a/instrumentation/hibernate/hibernate-reactive-1.0/javaagent/build.gradle.kts b/instrumentation/hibernate/hibernate-reactive-1.0/javaagent/build.gradle.kts index 8e60b78a15f3..45634ff761c4 100644 --- a/instrumentation/hibernate/hibernate-reactive-1.0/javaagent/build.gradle.kts +++ b/instrumentation/hibernate/hibernate-reactive-1.0/javaagent/build.gradle.kts @@ -105,12 +105,14 @@ tasks { } val testStableSemconv by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath + jvmArgs("-Dotel.semconv-stability.opt-in=database") } check { - dependsOn(testing.suites) - dependsOn(testStableSemconv) + dependsOn(testing.suites, testStableSemconv) } } diff --git a/instrumentation/hibernate/hibernate-reactive-1.0/metadata.yaml b/instrumentation/hibernate/hibernate-reactive-1.0/metadata.yaml new file mode 100644 index 000000000000..f385670c021e --- /dev/null +++ b/instrumentation/hibernate/hibernate-reactive-1.0/metadata.yaml @@ -0,0 +1,4 @@ +description: > + This instrumentation does not emit any telemetry on its own. Instead, it enables context + propagation for Hibernate Reactive asynchronous operations. +library_link: https://hibernate.org/reactive/ diff --git a/instrumentation/hibernate/testing/build.gradle.kts b/instrumentation/hibernate/testing/build.gradle.kts new file mode 100644 index 000000000000..484e04028aaa --- /dev/null +++ b/instrumentation/hibernate/testing/build.gradle.kts @@ -0,0 +1,7 @@ +plugins { + id("otel.java-conventions") +} + +dependencies { + api(project(":testing-common")) +} diff --git a/instrumentation/hibernate/testing/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/ExperimentalTestHelper.java b/instrumentation/hibernate/testing/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/ExperimentalTestHelper.java new file mode 100644 index 000000000000..341fd132899f --- /dev/null +++ b/instrumentation/hibernate/testing/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/ExperimentalTestHelper.java @@ -0,0 +1,42 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.hibernate; + +import static io.opentelemetry.api.common.AttributeKey.stringKey; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies; + +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.sdk.testing.assertj.AttributeAssertion; +import java.util.function.Consumer; +import javax.annotation.Nullable; + +public class ExperimentalTestHelper { + public static final boolean isEnabled = + Boolean.getBoolean("otel.instrumentation.hibernate.experimental-span-attributes"); + + public static final AttributeKey HIBERNATE_SESSION_ID = stringKey("hibernate.session_id"); + + @Nullable + public static String experimental(String value) { + if (isEnabled) { + return value; + } + return null; + } + + public static AttributeAssertion experimentalSatisfies( + AttributeKey key, Consumer assertion) { + return satisfies( + key, + val -> { + if (isEnabled) { + val.satisfies(assertion::accept); + } + }); + } + + private ExperimentalTestHelper() {} +} diff --git a/instrumentation/hikaricp-3.0/javaagent/build.gradle.kts b/instrumentation/hikaricp-3.0/javaagent/build.gradle.kts index 2ed1ce77f193..8a1ab5bd6430 100644 --- a/instrumentation/hikaricp-3.0/javaagent/build.gradle.kts +++ b/instrumentation/hikaricp-3.0/javaagent/build.gradle.kts @@ -26,8 +26,10 @@ val collectMetadata = findProperty("collectMetadata")?.toString() ?: "false" tasks { val testStableSemconv by registering(Test::class) { - jvmArgs("-Dotel.semconv-stability.opt-in=database") + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath + jvmArgs("-Dotel.semconv-stability.opt-in=database") systemProperty("collectMetadata", collectMetadata) systemProperty("metadataConfig", "otel.semconv-stability.opt-in=database") } diff --git a/instrumentation/hikaricp-3.0/library/README.md b/instrumentation/hikaricp-3.0/library/README.md index 233b5c6e8956..49fd620870c2 100644 --- a/instrumentation/hikaricp-3.0/library/README.md +++ b/instrumentation/hikaricp-3.0/library/README.md @@ -7,7 +7,7 @@ Provides OpenTelemetry instrumentation for [HikariCP](https://github.com/brettwo ### Add these dependencies to your project Replace `OPENTELEMETRY_VERSION` with the [latest -release](https://search.maven.org/search?q=g:io.opentelemetry.instrumentation%20AND%20a:opentelemetry-hikaricp-3.0). +release]( https://central.sonatype.com/artifact/io.opentelemetry.instrumentation/opentelemetry-hikaricp-3.0). For Maven, add to your `pom.xml` dependencies: diff --git a/instrumentation/hikaricp-3.0/library/build.gradle.kts b/instrumentation/hikaricp-3.0/library/build.gradle.kts index 73fda349d202..5dee32325642 100644 --- a/instrumentation/hikaricp-3.0/library/build.gradle.kts +++ b/instrumentation/hikaricp-3.0/library/build.gradle.kts @@ -11,6 +11,9 @@ dependencies { tasks { val testStableSemconv by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath + jvmArgs("-Dotel.semconv-stability.opt-in=database") } diff --git a/instrumentation/hikaricp-3.0/metadata.yaml b/instrumentation/hikaricp-3.0/metadata.yaml new file mode 100644 index 000000000000..212c71342f0d --- /dev/null +++ b/instrumentation/hikaricp-3.0/metadata.yaml @@ -0,0 +1,5 @@ +description: > + This instrumentation provides database client metrics for HikariCP 3.0+ connection pools. It + reports metrics like connection timeouts, creation time, wait time, and usage time, along with + connection pool statistics such as the number of active, idle, and pending connections. +library_link: https://github.com/brettwooldridge/HikariCP diff --git a/instrumentation/http-url-connection/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/httpurlconnection/HttpMethodAttributeExtractor.java b/instrumentation/http-url-connection/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/httpurlconnection/HttpMethodAttributeExtractor.java index 83bfe1e46f1f..3c522826d044 100644 --- a/instrumentation/http-url-connection/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/httpurlconnection/HttpMethodAttributeExtractor.java +++ b/instrumentation/http-url-connection/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/httpurlconnection/HttpMethodAttributeExtractor.java @@ -11,7 +11,9 @@ import io.opentelemetry.api.common.AttributesBuilder; import io.opentelemetry.api.trace.Span; import io.opentelemetry.context.Context; +import io.opentelemetry.instrumentation.api.incubator.semconv.http.HttpClientUrlTemplate; import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor; +import io.opentelemetry.javaagent.bootstrap.internal.AgentCommonConfig; import io.opentelemetry.semconv.HttpAttributes; import java.net.HttpURLConnection; import java.util.Set; @@ -22,9 +24,12 @@ public class HttpMethodAttributeExtractor< implements AttributesExtractor { private final Set knownMethods; + private final boolean emitExperimentalHttpClientTelemetry; private HttpMethodAttributeExtractor(Set knownMethods) { this.knownMethods = knownMethods; + emitExperimentalHttpClientTelemetry = + AgentCommonConfig.get().shouldEmitExperimentalHttpClientTelemetry(); } public static AttributesExtractor create( @@ -55,9 +60,12 @@ public void onEnd( } else { internalSet(attributes, HttpAttributes.HTTP_REQUEST_METHOD, _OTHER); internalSet(attributes, HttpAttributes.HTTP_REQUEST_METHOD_ORIGINAL, method); + method = "HTTP"; } Span span = Span.fromContext(context); - span.updateName(method); + String urlTemplate = + emitExperimentalHttpClientTelemetry ? HttpClientUrlTemplate.get(context) : null; + span.updateName(method + (urlTemplate != null ? " " + urlTemplate : "")); } } } diff --git a/instrumentation/http-url-connection/metadata.yaml b/instrumentation/http-url-connection/metadata.yaml new file mode 100644 index 000000000000..990f2f71a6ca --- /dev/null +++ b/instrumentation/http-url-connection/metadata.yaml @@ -0,0 +1,34 @@ +description: > + This instrumentation enables the generation of HTTP client spans and HTTP client metrics for + requests made using `java.net.HttpURLConnection`. +library_link: https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/net/HttpURLConnection.html +configurations: + - name: otel.instrumentation.http.known-methods + description: > + Configures the instrumentation to recognize an alternative set of HTTP request methods. All + other methods will be treated as `_OTHER`. + type: list + default: "CONNECT,DELETE,GET,HEAD,OPTIONS,PATCH,POST,PUT,TRACE" + - name: otel.instrumentation.http.client.capture-request-headers + description: List of HTTP request headers to capture in HTTP client telemetry. + type: list + default: "" + - name: otel.instrumentation.http.client.capture-response-headers + description: List of HTTP response headers to capture in HTTP client telemetry. + type: list + default: "" + - name: otel.instrumentation.common.peer-service-mapping + description: Used to specify a mapping from host names or IP addresses to peer services. + type: map + default: "" + - name: otel.instrumentation.http.client.emit-experimental-telemetry + description: > + Enable the capture of experimental HTTP client telemetry. Adds the `http.request.body.size` + and `http.response.body.size` attributes to spans, and records `http.client.request.size` and + `http.client.response.size` metrics. + type: boolean + default: false + - name: otel.instrumentation.http.client.experimental.redact-query-parameters + description: Redact sensitive URL parameters. See https://opentelemetry.io/docs/specs/semconv/http/http-spans. + type: boolean + default: true diff --git a/instrumentation/hystrix-1.4/javaagent/build.gradle.kts b/instrumentation/hystrix-1.4/javaagent/build.gradle.kts index 8a231df889e7..a5b26f2d81c6 100644 --- a/instrumentation/hystrix-1.4/javaagent/build.gradle.kts +++ b/instrumentation/hystrix-1.4/javaagent/build.gradle.kts @@ -20,13 +20,27 @@ dependencies { library("io.reactivex:rxjava:1.0.8") } -tasks.withType().configureEach { - // TODO run tests both with and without experimental span attributes - jvmArgs("-Dotel.instrumentation.hystrix.experimental-span-attributes=true") - // Disable so failure testing below doesn't inadvertently change the behavior. - jvmArgs("-Dhystrix.command.default.circuitBreaker.enabled=false") - jvmArgs("-Dio.opentelemetry.javaagent.shaded.io.opentelemetry.context.enableStrictContext=false") - - // Uncomment for debugging: - // jvmArgs("-Dhystrix.command.default.execution.timeout.enabled=false") +tasks { + withType().configureEach { + // Disable so failure testing below doesn't inadvertently change the behavior. + jvmArgs("-Dhystrix.command.default.circuitBreaker.enabled=false") + jvmArgs("-Dio.opentelemetry.javaagent.shaded.io.opentelemetry.context.enableStrictContext=false") + + systemProperty("collectMetadata", findProperty("collectMetadata")?.toString() ?: "false") + + // Uncomment for debugging: + // jvmArgs("-Dhystrix.command.default.execution.timeout.enabled=false") + } + + val testExperimental by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath + + jvmArgs("-Dotel.instrumentation.hystrix.experimental-span-attributes=true") + systemProperty("metadataConfig", "otel.instrumentation.hystrix.experimental-span-attributes=true") + } + + check { + dependsOn(testExperimental) + } } diff --git a/instrumentation/hystrix-1.4/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hystrix/ExperimentalTestHelper.java b/instrumentation/hystrix-1.4/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hystrix/ExperimentalTestHelper.java new file mode 100644 index 000000000000..f8f92b468ca9 --- /dev/null +++ b/instrumentation/hystrix-1.4/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hystrix/ExperimentalTestHelper.java @@ -0,0 +1,39 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.hystrix; + +import static io.opentelemetry.api.common.AttributeKey.booleanKey; +import static io.opentelemetry.api.common.AttributeKey.stringKey; + +import io.opentelemetry.api.common.AttributeKey; +import javax.annotation.Nullable; + +class ExperimentalTestHelper { + private static final boolean isEnabled = + Boolean.getBoolean("otel.instrumentation.hystrix.experimental-span-attributes"); + + static final AttributeKey HYSTRIX_COMMAND = stringKey("hystrix.command"); + static final AttributeKey HYSTRIX_GROUP = stringKey("hystrix.group"); + static final AttributeKey HYSTRIX_CIRCUIT_OPEN = booleanKey("hystrix.circuit_open"); + + @Nullable + static String experimental(String value) { + if (isEnabled) { + return value; + } + return null; + } + + @Nullable + static Boolean experimental(Boolean value) { + if (isEnabled) { + return value; + } + return null; + } + + private ExperimentalTestHelper() {} +} diff --git a/instrumentation/hystrix-1.4/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hystrix/HystrixObservableChainTest.java b/instrumentation/hystrix-1.4/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hystrix/HystrixObservableChainTest.java index 852d24ddba3a..32a6a9ecb7a2 100644 --- a/instrumentation/hystrix-1.4/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hystrix/HystrixObservableChainTest.java +++ b/instrumentation/hystrix-1.4/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hystrix/HystrixObservableChainTest.java @@ -5,8 +5,10 @@ package io.opentelemetry.javaagent.instrumentation.hystrix; -import static io.opentelemetry.api.common.AttributeKey.booleanKey; -import static io.opentelemetry.api.common.AttributeKey.stringKey; +import static io.opentelemetry.javaagent.instrumentation.hystrix.ExperimentalTestHelper.HYSTRIX_CIRCUIT_OPEN; +import static io.opentelemetry.javaagent.instrumentation.hystrix.ExperimentalTestHelper.HYSTRIX_COMMAND; +import static io.opentelemetry.javaagent.instrumentation.hystrix.ExperimentalTestHelper.HYSTRIX_GROUP; +import static io.opentelemetry.javaagent.instrumentation.hystrix.ExperimentalTestHelper.experimental; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; import static org.assertj.core.api.Assertions.assertThat; @@ -24,7 +26,7 @@ class HystrixObservableChainTest { @RegisterExtension - protected static final InstrumentationExtension testing = AgentInstrumentationExtension.create(); + static final InstrumentationExtension testing = AgentInstrumentationExtension.create(); @Test @SuppressWarnings("RxReturnValueIgnored") @@ -93,9 +95,9 @@ protected Observable construct() { span.hasName("ExampleGroup.TestCommand.execute") .hasParent(trace.getSpan(0)) .hasAttributesSatisfyingExactly( - equalTo(stringKey("hystrix.command"), "TestCommand"), - equalTo(stringKey("hystrix.group"), "ExampleGroup"), - equalTo(booleanKey("hystrix.circuit_open"), false)), + equalTo(HYSTRIX_COMMAND, experimental("TestCommand")), + equalTo(HYSTRIX_GROUP, experimental("ExampleGroup")), + equalTo(HYSTRIX_CIRCUIT_OPEN, experimental(false))), span -> span.hasName("tracedMethod") .hasParent(trace.getSpan(1)) @@ -104,9 +106,9 @@ protected Observable construct() { span.hasName("OtherGroup.AnotherTestCommand.execute") .hasParent(trace.getSpan(1)) .hasAttributesSatisfyingExactly( - equalTo(stringKey("hystrix.command"), "AnotherTestCommand"), - equalTo(stringKey("hystrix.group"), "OtherGroup"), - equalTo(booleanKey("hystrix.circuit_open"), false)), + equalTo(HYSTRIX_COMMAND, experimental("AnotherTestCommand")), + equalTo(HYSTRIX_GROUP, experimental("OtherGroup")), + equalTo(HYSTRIX_CIRCUIT_OPEN, experimental(false))), span -> span.hasName("anotherTracedMethod") .hasParent(trace.getSpan(3)) diff --git a/instrumentation/hystrix-1.4/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hystrix/HystrixObservableTest.java b/instrumentation/hystrix-1.4/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hystrix/HystrixObservableTest.java index bc1aa3a5e00e..fc623a11ef4b 100644 --- a/instrumentation/hystrix-1.4/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hystrix/HystrixObservableTest.java +++ b/instrumentation/hystrix-1.4/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hystrix/HystrixObservableTest.java @@ -5,8 +5,10 @@ package io.opentelemetry.javaagent.instrumentation.hystrix; -import static io.opentelemetry.api.common.AttributeKey.booleanKey; -import static io.opentelemetry.api.common.AttributeKey.stringKey; +import static io.opentelemetry.javaagent.instrumentation.hystrix.ExperimentalTestHelper.HYSTRIX_CIRCUIT_OPEN; +import static io.opentelemetry.javaagent.instrumentation.hystrix.ExperimentalTestHelper.HYSTRIX_COMMAND; +import static io.opentelemetry.javaagent.instrumentation.hystrix.ExperimentalTestHelper.HYSTRIX_GROUP; +import static io.opentelemetry.javaagent.instrumentation.hystrix.ExperimentalTestHelper.experimental; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.catchException; @@ -36,7 +38,7 @@ class HystrixObservableTest { @RegisterExtension - protected static final InstrumentationExtension testing = AgentInstrumentationExtension.create(); + static final InstrumentationExtension testing = AgentInstrumentationExtension.create(); @ParameterizedTest @MethodSource("provideCommandActionArguments") @@ -83,9 +85,9 @@ protected Observable construct() { span.hasName("ExampleGroup.TestCommand.execute") .hasParent(trace.getSpan(0)) .hasAttributesSatisfyingExactly( - equalTo(stringKey("hystrix.command"), "TestCommand"), - equalTo(stringKey("hystrix.group"), "ExampleGroup"), - equalTo(booleanKey("hystrix.circuit_open"), false)), + equalTo(HYSTRIX_COMMAND, experimental("TestCommand")), + equalTo(HYSTRIX_GROUP, experimental("ExampleGroup")), + equalTo(HYSTRIX_CIRCUIT_OPEN, experimental(false))), span -> span.hasName("tracedMethod") .hasParent(trace.getSpan(1)) @@ -292,9 +294,9 @@ protected Observable resumeWithFallback() { span.hasName("ExampleGroup.TestCommand.fallback") .hasParent(trace.getSpan(1)) .hasAttributesSatisfyingExactly( - equalTo(stringKey("hystrix.command"), "TestCommand"), - equalTo(stringKey("hystrix.group"), "ExampleGroup"), - equalTo(booleanKey("hystrix.circuit_open"), false)))); + equalTo(HYSTRIX_COMMAND, experimental("TestCommand")), + equalTo(HYSTRIX_GROUP, experimental("ExampleGroup")), + equalTo(HYSTRIX_CIRCUIT_OPEN, experimental(false))))); } private static Stream provideCommandFallbackArguments() { @@ -383,17 +385,17 @@ protected Observable construct() { .hasStatus(StatusData.error()) .hasException(exception.getCause()) .hasAttributesSatisfyingExactly( - equalTo(stringKey("hystrix.command"), "TestCommand"), - equalTo(stringKey("hystrix.group"), "FailingGroup"), - equalTo(booleanKey("hystrix.circuit_open"), false)), + equalTo(HYSTRIX_COMMAND, experimental("TestCommand")), + equalTo(HYSTRIX_GROUP, experimental("FailingGroup")), + equalTo(HYSTRIX_CIRCUIT_OPEN, experimental(false))), span -> span.hasName("FailingGroup.TestCommand.fallback") .hasParent(trace.getSpan(1)) .hasException(hystrixRuntimeException.getFallbackException()) .hasAttributesSatisfyingExactly( - equalTo(stringKey("hystrix.command"), "TestCommand"), - equalTo(stringKey("hystrix.group"), "FailingGroup"), - equalTo(booleanKey("hystrix.circuit_open"), false)))); + equalTo(HYSTRIX_COMMAND, experimental("TestCommand")), + equalTo(HYSTRIX_GROUP, experimental("FailingGroup")), + equalTo(HYSTRIX_CIRCUIT_OPEN, experimental(false))))); } private static Stream provideCommandNoFallbackResultsInErrorArguments() { diff --git a/instrumentation/hystrix-1.4/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hystrix/HystrixTest.java b/instrumentation/hystrix-1.4/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hystrix/HystrixTest.java index 675a53b51f24..f87014395f03 100644 --- a/instrumentation/hystrix-1.4/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hystrix/HystrixTest.java +++ b/instrumentation/hystrix-1.4/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hystrix/HystrixTest.java @@ -5,8 +5,10 @@ package io.opentelemetry.javaagent.instrumentation.hystrix; -import static io.opentelemetry.api.common.AttributeKey.booleanKey; -import static io.opentelemetry.api.common.AttributeKey.stringKey; +import static io.opentelemetry.javaagent.instrumentation.hystrix.ExperimentalTestHelper.HYSTRIX_CIRCUIT_OPEN; +import static io.opentelemetry.javaagent.instrumentation.hystrix.ExperimentalTestHelper.HYSTRIX_COMMAND; +import static io.opentelemetry.javaagent.instrumentation.hystrix.ExperimentalTestHelper.HYSTRIX_GROUP; +import static io.opentelemetry.javaagent.instrumentation.hystrix.ExperimentalTestHelper.experimental; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Named.named; @@ -31,7 +33,7 @@ class HystrixTest { @RegisterExtension - protected static final InstrumentationExtension testing = AgentInstrumentationExtension.create(); + static final InstrumentationExtension testing = AgentInstrumentationExtension.create(); @ParameterizedTest @MethodSource("provideCommandActionArguments") @@ -42,7 +44,7 @@ class TestCommand extends HystrixCommand { } @Override - protected String run() throws Exception { + protected String run() { return tracedMethod(); } @@ -65,9 +67,9 @@ private String tracedMethod() { span.hasName("ExampleGroup.TestCommand.execute") .hasParent(trace.getSpan(0)) .hasAttributesSatisfyingExactly( - equalTo(stringKey("hystrix.command"), "TestCommand"), - equalTo(stringKey("hystrix.group"), "ExampleGroup"), - equalTo(booleanKey("hystrix.circuit_open"), false)), + equalTo(HYSTRIX_COMMAND, experimental("TestCommand")), + equalTo(HYSTRIX_GROUP, experimental("ExampleGroup")), + equalTo(HYSTRIX_CIRCUIT_OPEN, experimental(false))), span -> span.hasName("tracedMethod") .hasParent(trace.getSpan(1)) @@ -108,16 +110,16 @@ protected String getFallback() { .hasStatus(StatusData.error()) .hasException(new IllegalArgumentException()) .hasAttributesSatisfyingExactly( - equalTo(stringKey("hystrix.command"), "TestCommand"), - equalTo(stringKey("hystrix.group"), "ExampleGroup"), - equalTo(booleanKey("hystrix.circuit_open"), false)), + equalTo(HYSTRIX_COMMAND, experimental("TestCommand")), + equalTo(HYSTRIX_GROUP, experimental("ExampleGroup")), + equalTo(HYSTRIX_CIRCUIT_OPEN, experimental(false))), span -> span.hasName("ExampleGroup.TestCommand.fallback") .hasParent(trace.getSpan(1)) .hasAttributesSatisfyingExactly( - equalTo(stringKey("hystrix.command"), "TestCommand"), - equalTo(stringKey("hystrix.group"), "ExampleGroup"), - equalTo(booleanKey("hystrix.circuit_open"), false)))); + equalTo(HYSTRIX_COMMAND, experimental("TestCommand")), + equalTo(HYSTRIX_GROUP, experimental("ExampleGroup")), + equalTo(HYSTRIX_CIRCUIT_OPEN, experimental(false))))); } private static Stream provideCommandActionArguments() { diff --git a/instrumentation/hystrix-1.4/metadata.yaml b/instrumentation/hystrix-1.4/metadata.yaml new file mode 100644 index 000000000000..90482242a5c9 --- /dev/null +++ b/instrumentation/hystrix-1.4/metadata.yaml @@ -0,0 +1,8 @@ +description: This instrumentation enables the generation of INTERNAL spans for Hystrix command executions and fallbacks. +library_link: https://github.com/Netflix/Hystrix +configurations: + - name: otel.instrumentation.hystrix.experimental-span-attributes + description: Enables capturing the experimental `hystrix.command`, `hystrix.circuit_open` and `hystrix.group` span attributes. + type: boolean + default: false + diff --git a/instrumentation/influxdb-2.4/javaagent/build.gradle.kts b/instrumentation/influxdb-2.4/javaagent/build.gradle.kts index 04909adbc594..6143eb0fd014 100644 --- a/instrumentation/influxdb-2.4/javaagent/build.gradle.kts +++ b/instrumentation/influxdb-2.4/javaagent/build.gradle.kts @@ -41,6 +41,7 @@ tasks { // from the okhttp instrumentation we need OkHttp3IgnoredTypesConfigurer to fix context leaks jvmArgs("-Dotel.instrumentation.okhttp.enabled=false") usesService(gradle.sharedServices.registrations["testcontainersBuildService"].service) + systemProperty("collectMetadata", findProperty("collectMetadata")?.toString() ?: "false") } if (!(findProperty("testLatestDeps") as Boolean)) { @@ -50,6 +51,10 @@ tasks { } val testStableSemconv by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath + + systemProperty("metadataConfig", "otel.semconv-stability.opt-in=database") jvmArgs("-Dotel.semconv-stability.opt-in=database") } diff --git a/instrumentation/influxdb-2.4/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/influxdb/v2_4/InfluxDbClientTest.java b/instrumentation/influxdb-2.4/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/influxdb/v2_4/InfluxDbClientTest.java index c3650f6fa05f..19adf74b6ce4 100644 --- a/instrumentation/influxdb-2.4/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/influxdb/v2_4/InfluxDbClientTest.java +++ b/instrumentation/influxdb-2.4/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/influxdb/v2_4/InfluxDbClientTest.java @@ -5,14 +5,18 @@ package io.opentelemetry.javaagent.instrumentation.influxdb.v2_4; +import static io.opentelemetry.instrumentation.testing.junit.db.DbClientMetricsTestUtil.assertDurationMetric; import static io.opentelemetry.instrumentation.testing.junit.db.SemconvStabilityUtil.maybeStable; 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.DbIncubatingAttributes.DB_NAME; +import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_NAMESPACE; import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_OPERATION; +import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_OPERATION_NAME; import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_STATEMENT; import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_SYSTEM; +import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_SYSTEM_NAME; import static java.util.Arrays.asList; import static org.assertj.core.api.Assertions.assertThat; @@ -150,6 +154,15 @@ void testQueryWithTwoArguments() { "SELECT * FROM cpu_load where test1 = ?", "SELECT", databaseName)))); + + assertDurationMetric( + testing, + "io.opentelemetry.influxdb-2.4", + DB_SYSTEM_NAME, + DB_NAMESPACE, + DB_OPERATION_NAME, + SERVER_ADDRESS, + SERVER_PORT); } @Test diff --git a/instrumentation/influxdb-2.4/metadata.yaml b/instrumentation/influxdb-2.4/metadata.yaml new file mode 100644 index 000000000000..6ef19652fa60 --- /dev/null +++ b/instrumentation/influxdb-2.4/metadata.yaml @@ -0,0 +1,2 @@ +description: This instrumentation enables the generation of database client spans and metrics for the InfluxDB Java client. +library_link: https://github.com/influxdata/influxdb-java diff --git a/instrumentation/java-http-client/library/README.md b/instrumentation/java-http-client/library/README.md index c413e1b87c03..3904ad561d2d 100644 --- a/instrumentation/java-http-client/library/README.md +++ b/instrumentation/java-http-client/library/README.md @@ -7,7 +7,7 @@ Provides OpenTelemetry instrumentation for [Java HTTP Client](https://openjdk.or ### Add these dependencies to your project Replace `OPENTELEMETRY_VERSION` with the [latest -release](https://search.maven.org/search?q=g:io.opentelemetry.instrumentation%20AND%20a:opentelemetry-java-http-client). +release]( https://central.sonatype.com/artifact/io.opentelemetry.instrumentation/opentelemetry-java-http-client). For Maven, add to your `pom.xml` dependencies: diff --git a/instrumentation/java-http-client/metadata.yaml b/instrumentation/java-http-client/metadata.yaml new file mode 100644 index 000000000000..9283e71e4fad --- /dev/null +++ b/instrumentation/java-http-client/metadata.yaml @@ -0,0 +1,32 @@ +description: This instrumentation enables HTTP client spans and HTTP client metrics for requests made using the Java HTTP client. +library_link: https://docs.oracle.com/en/java/javase/11/docs/api/java.net.http/java/net/http/package-summary.html +configurations: + - name: otel.instrumentation.http.known-methods + description: > + Configures the instrumentation to recognize an alternative set of HTTP request methods. All + other methods will be treated as `_OTHER`. + type: list + default: "CONNECT,DELETE,GET,HEAD,OPTIONS,PATCH,POST,PUT,TRACE" + - name: otel.instrumentation.http.client.capture-request-headers + description: List of HTTP request headers to capture in HTTP client telemetry. + type: list + default: "" + - name: otel.instrumentation.http.client.capture-response-headers + description: List of HTTP response headers to capture in HTTP client telemetry. + type: list + default: "" + - name: otel.instrumentation.common.peer-service-mapping + description: Used to specify a mapping from host names or IP addresses to peer services. + type: map + default: "" + - name: otel.instrumentation.http.client.emit-experimental-telemetry + description: > + Enable the capture of experimental HTTP client telemetry. Adds the `http.request.body.size` + and `http.response.body.size` attributes to spans, and records `http.client.request.size` and + `http.client.response.size` metrics. + type: boolean + default: false + - name: otel.instrumentation.http.client.experimental.redact-query-parameters + description: Redact sensitive URL parameters. See https://opentelemetry.io/docs/specs/semconv/http/http-spans. + type: boolean + default: true diff --git a/instrumentation/java-http-server/javaagent/build.gradle.kts b/instrumentation/java-http-server/javaagent/build.gradle.kts index 1c8be721a562..537a9539f9ed 100644 --- a/instrumentation/java-http-server/javaagent/build.gradle.kts +++ b/instrumentation/java-http-server/javaagent/build.gradle.kts @@ -12,3 +12,7 @@ dependencies { implementation(project(":instrumentation:java-http-server:library")) testImplementation(project(":instrumentation:java-http-server:testing")) } + +tasks.test { + systemProperty("collectMetadata", findProperty("collectMetadata")?.toString() ?: "false") +} diff --git a/instrumentation/java-http-server/library/README.md b/instrumentation/java-http-server/library/README.md index c4d6d9382dc8..4cd445097f4f 100644 --- a/instrumentation/java-http-server/library/README.md +++ b/instrumentation/java-http-server/library/README.md @@ -7,7 +7,7 @@ Provides OpenTelemetry instrumentation for [Java HTTP Server](https://docs.oracl ### Add these dependencies to your project Replace `OPENTELEMETRY_VERSION` with the [latest -release](https://search.maven.org/search?q=g:io.opentelemetry.instrumentation%20AND%20a:opentelemetry-java-http-server). +release]( https://central.sonatype.com/artifact/io.opentelemetry.instrumentation/opentelemetry-java-http-server). For Maven, add to your `pom.xml` dependencies: diff --git a/instrumentation/java-http-server/metadata.yaml b/instrumentation/java-http-server/metadata.yaml new file mode 100644 index 000000000000..2d80c4936be2 --- /dev/null +++ b/instrumentation/java-http-server/metadata.yaml @@ -0,0 +1,28 @@ +description: This instrumentation enables HTTP server spans and HTTP server metrics for the Java HTTP server. +library_link: https://docs.oracle.com/en/java/javase/21/docs/api/jdk.httpserver/module-summary.html +configurations: + - name: otel.instrumentation.http.known-methods + description: > + Configures the instrumentation to recognize an alternative set of HTTP request methods. All + other methods will be treated as `_OTHER`. + type: list + default: "CONNECT,DELETE,GET,HEAD,OPTIONS,PATCH,POST,PUT,TRACE" + - name: otel.instrumentation.http.server.capture-request-headers + description: List of HTTP request headers to capture in HTTP server telemetry. + type: list + default: "" + - name: otel.instrumentation.http.server.capture-response-headers + description: List of HTTP response headers to capture in HTTP server telemetry. + type: list + default: "" + - name: otel.instrumentation.common.peer-service-mapping + description: Used to specify a mapping from host names or IP addresses to peer services. + type: map + default: "" + - name: otel.instrumentation.http.server.emit-experimental-telemetry + description: > + Enable the capture of experimental HTTP server telemetry. Adds the `http.request.body.size` and + `http.response.body.size` attributes to spans, and records `http.server.request.body.size` + and `http.server.response.body.size` metrics. + type: boolean + default: false diff --git a/instrumentation/java-util-logging/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jul/JavaUtilLoggingHelper.java b/instrumentation/java-util-logging/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jul/JavaUtilLoggingHelper.java index 780e7307c845..dfc29d6ad673 100644 --- a/instrumentation/java-util-logging/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jul/JavaUtilLoggingHelper.java +++ b/instrumentation/java-util-logging/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jul/JavaUtilLoggingHelper.java @@ -9,14 +9,12 @@ import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.common.AttributesBuilder; +import io.opentelemetry.api.incubator.logs.ExtendedLogRecordBuilder; import io.opentelemetry.api.logs.LogRecordBuilder; import io.opentelemetry.api.logs.Severity; import io.opentelemetry.context.Context; import io.opentelemetry.javaagent.bootstrap.internal.AgentInstrumentationConfig; -import io.opentelemetry.semconv.ExceptionAttributes; import io.opentelemetry.semconv.incubating.ThreadIncubatingAttributes; -import java.io.PrintWriter; -import java.io.StringWriter; import java.util.concurrent.TimeUnit; import java.util.logging.Formatter; import java.util.logging.Level; @@ -85,13 +83,8 @@ private static void mapLogRecord(LogRecordBuilder builder, LogRecord logRecord) // throwable Throwable throwable = logRecord.getThrown(); if (throwable != null) { - // TODO (trask) extract method for recording exception into - // io.opentelemetry:opentelemetry-api - attributes.put(ExceptionAttributes.EXCEPTION_TYPE, throwable.getClass().getName()); - attributes.put(ExceptionAttributes.EXCEPTION_MESSAGE, throwable.getMessage()); - StringWriter writer = new StringWriter(); - throwable.printStackTrace(new PrintWriter(writer)); - attributes.put(ExceptionAttributes.EXCEPTION_STACKTRACE, writer.toString()); + // this cast is safe within java agent instrumentation + ((ExtendedLogRecordBuilder) builder).setException(throwable); } if (captureExperimentalAttributes) { diff --git a/instrumentation/javalin-5.0/metadata.yaml b/instrumentation/javalin-5.0/metadata.yaml new file mode 100644 index 000000000000..683a861143e8 --- /dev/null +++ b/instrumentation/javalin-5.0/metadata.yaml @@ -0,0 +1,2 @@ +description: This instrumentation enriches existing HTTP server spans with route information, it does not emit any telemetry on its own. +library_link: https://javalin.io/ diff --git a/instrumentation/jaxws/jaxws-2.0-common-testing/build.gradle.kts b/instrumentation/jaxws/jaxws-2.0-common-testing/build.gradle.kts index 607effb0c6c1..161408814315 100644 --- a/instrumentation/jaxws/jaxws-2.0-common-testing/build.gradle.kts +++ b/instrumentation/jaxws/jaxws-2.0-common-testing/build.gradle.kts @@ -1,5 +1,5 @@ plugins { - id("org.unbroken-dome.xjc") + id("com.github.bjornvester.xjc") id("otel.java-conventions") } @@ -10,6 +10,11 @@ tasks { } } +xjc { + xsdDir.set(layout.projectDirectory.dir("src/main/schema")) + useJakarta.set(false) +} + dependencies { api("javax.xml.ws:jaxws-api:2.0") api("javax.jws:javax.jws-api:1.1") diff --git a/instrumentation/jaxws/jaxws-3.0-common-testing/build.gradle.kts b/instrumentation/jaxws/jaxws-3.0-common-testing/build.gradle.kts index 0827852d5c94..4ec0ee7c4675 100644 --- a/instrumentation/jaxws/jaxws-3.0-common-testing/build.gradle.kts +++ b/instrumentation/jaxws/jaxws-3.0-common-testing/build.gradle.kts @@ -1,5 +1,5 @@ plugins { - id("org.unbroken-dome.xjc") + id("com.github.bjornvester.xjc") id("otel.java-conventions") } @@ -10,6 +10,11 @@ tasks { } } +xjc { + xsdDir.set(layout.projectDirectory.dir("src/main/schema")) + useJakarta.set(true) +} + dependencies { api("jakarta.xml.ws:jakarta.xml.ws-api:3.0.0") api("jakarta.jws:jakarta.jws-api:3.0.0") @@ -18,7 +23,4 @@ dependencies { api("org.springframework.ws:spring-ws-core:4.0.0") implementation(project(":testing-common")) - - xjcTool("com.sun.xml.bind:jaxb-xjc:3.0.2") - xjcTool("com.sun.xml.bind:jaxb-impl:3.0.2") } diff --git a/instrumentation/jboss-logmanager/jboss-logmanager-appender-1.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jbosslogmanager/appender/v1_1/LoggingEventMapper.java b/instrumentation/jboss-logmanager/jboss-logmanager-appender-1.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jbosslogmanager/appender/v1_1/LoggingEventMapper.java index b8e13d2f3e19..344c831605ac 100644 --- a/instrumentation/jboss-logmanager/jboss-logmanager-appender-1.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jbosslogmanager/appender/v1_1/LoggingEventMapper.java +++ b/instrumentation/jboss-logmanager/jboss-logmanager-appender-1.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jbosslogmanager/appender/v1_1/LoggingEventMapper.java @@ -12,15 +12,13 @@ import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.common.AttributesBuilder; +import io.opentelemetry.api.incubator.logs.ExtendedLogRecordBuilder; import io.opentelemetry.api.logs.LogRecordBuilder; import io.opentelemetry.api.logs.Severity; import io.opentelemetry.context.Context; import io.opentelemetry.instrumentation.api.internal.cache.Cache; import io.opentelemetry.javaagent.bootstrap.internal.AgentInstrumentationConfig; -import io.opentelemetry.semconv.ExceptionAttributes; import io.opentelemetry.semconv.incubating.ThreadIncubatingAttributes; -import java.io.PrintWriter; -import java.io.StringWriter; import java.util.List; import java.util.Map; import org.jboss.logmanager.ExtLogRecord; @@ -81,13 +79,8 @@ public void capture(Logger logger, ExtLogRecord record) { Throwable throwable = record.getThrown(); if (throwable != null) { - // TODO (trask) extract method for recording exception into - // io.opentelemetry:opentelemetry-api - attributes.put(ExceptionAttributes.EXCEPTION_TYPE, throwable.getClass().getName()); - attributes.put(ExceptionAttributes.EXCEPTION_MESSAGE, throwable.getMessage()); - StringWriter writer = new StringWriter(); - throwable.printStackTrace(new PrintWriter(writer)); - attributes.put(ExceptionAttributes.EXCEPTION_STACKTRACE, writer.toString()); + // this cast is safe within java agent instrumentation + ((ExtendedLogRecordBuilder) builder).setException(throwable); } captureMdcAttributes(attributes); diff --git a/instrumentation/jdbc/javaagent/build.gradle.kts b/instrumentation/jdbc/javaagent/build.gradle.kts index 894616e9cf9e..56308493f1f6 100644 --- a/instrumentation/jdbc/javaagent/build.gradle.kts +++ b/instrumentation/jdbc/javaagent/build.gradle.kts @@ -56,21 +56,19 @@ sourceSets { tasks { val testSlick by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath + filter { includeTestsMatching("SlickTest") } include("**/SlickTest.*") } - test { - filter { - excludeTestsMatching("SlickTest") - excludeTestsMatching("PreparedStatementParametersTest") - } - jvmArgs("-Dotel.instrumentation.jdbc-datasource.enabled=true") - } - val testStableSemconv by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath + filter { excludeTestsMatching("SlickTest") excludeTestsMatching("PreparedStatementParametersTest") @@ -80,6 +78,9 @@ tasks { } val testSlickStableSemconv by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath + filter { includeTestsMatching("SlickTest") } @@ -88,17 +89,25 @@ tasks { } val testCaptureParameters by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath + filter { includeTestsMatching("PreparedStatementParametersTest") } jvmArgs("-Dotel.instrumentation.jdbc.experimental.capture-query-parameters=true") } + test { + filter { + excludeTestsMatching("SlickTest") + excludeTestsMatching("PreparedStatementParametersTest") + } + jvmArgs("-Dotel.instrumentation.jdbc-datasource.enabled=true") + } + check { - dependsOn(testSlick) - dependsOn(testStableSemconv) - dependsOn(testSlickStableSemconv) - dependsOn(testCaptureParameters) + dependsOn(testSlick, testStableSemconv, testSlickStableSemconv, testCaptureParameters) } } diff --git a/instrumentation/jdbc/library/README.md b/instrumentation/jdbc/library/README.md index 2c21cfd2146e..1d1ebb5138c6 100644 --- a/instrumentation/jdbc/library/README.md +++ b/instrumentation/jdbc/library/README.md @@ -8,7 +8,7 @@ Provides OpenTelemetry instrumentation for ### Add these dependencies to your project Replace `OPENTELEMETRY_VERSION` with the [latest -release](https://search.maven.org/search?q=g:io.opentelemetry.instrumentation%20AND%20a:opentelemetry-jdbc). +release]( https://central.sonatype.com/artifact/io.opentelemetry.instrumentation/opentelemetry-jdbc). For Maven, add to your `pom.xml` dependencies: diff --git a/instrumentation/jdbc/library/build.gradle.kts b/instrumentation/jdbc/library/build.gradle.kts index 03e0f372c10d..3c3246102fbb 100644 --- a/instrumentation/jdbc/library/build.gradle.kts +++ b/instrumentation/jdbc/library/build.gradle.kts @@ -52,6 +52,9 @@ tasks { } val testStableSemconv by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath + jvmArgs("-Dotel.semconv-stability.opt-in=database") } diff --git a/instrumentation/jdbc/metadata.yaml b/instrumentation/jdbc/metadata.yaml index 696dfc541bd0..f3c143e99490 100644 --- a/instrumentation/jdbc/metadata.yaml +++ b/instrumentation/jdbc/metadata.yaml @@ -5,6 +5,7 @@ description: > There is also a "jdbc-datasource" instrumentation that creates spans for datasource connections, but is disabled by default due to the volume of telemetry produced. +library_link: https://docs.oracle.com/javase/8/docs/api/java/sql/package-summary.html configurations: - name: otel.instrumentation.jdbc.statement-sanitizer.enabled description: Enables statement sanitization for database queries. Takes precedent to diff --git a/instrumentation/jedis/jedis-1.4/javaagent/build.gradle.kts b/instrumentation/jedis/jedis-1.4/javaagent/build.gradle.kts index 22293482fb0a..8fc19ca3fe50 100644 --- a/instrumentation/jedis/jedis-1.4/javaagent/build.gradle.kts +++ b/instrumentation/jedis/jedis-1.4/javaagent/build.gradle.kts @@ -44,11 +44,13 @@ tasks { } val testStableSemconv by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath + jvmArgs("-Dotel.semconv-stability.opt-in=database") } check { - dependsOn(testing.suites) - dependsOn(testStableSemconv) + dependsOn(testing.suites, testStableSemconv) } } diff --git a/instrumentation/jedis/jedis-3.0/javaagent/build.gradle.kts b/instrumentation/jedis/jedis-3.0/javaagent/build.gradle.kts index 532a471e280b..8379c562943a 100644 --- a/instrumentation/jedis/jedis-3.0/javaagent/build.gradle.kts +++ b/instrumentation/jedis/jedis-3.0/javaagent/build.gradle.kts @@ -34,6 +34,9 @@ tasks { } val testStableSemconv by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath + jvmArgs("-Dotel.semconv-stability.opt-in=database") } diff --git a/instrumentation/jedis/jedis-4.0/javaagent/build.gradle.kts b/instrumentation/jedis/jedis-4.0/javaagent/build.gradle.kts index 01afcaa36f52..34293541c6f9 100644 --- a/instrumentation/jedis/jedis-4.0/javaagent/build.gradle.kts +++ b/instrumentation/jedis/jedis-4.0/javaagent/build.gradle.kts @@ -32,6 +32,9 @@ tasks { } val testStableSemconv by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath + jvmArgs("-Dotel.semconv-stability.opt-in=database") } diff --git a/instrumentation/jetty-httpclient/jetty-httpclient-12.0/library/README.md b/instrumentation/jetty-httpclient/jetty-httpclient-12.0/library/README.md new file mode 100644 index 000000000000..e5c86f7bbbb7 --- /dev/null +++ b/instrumentation/jetty-httpclient/jetty-httpclient-12.0/library/README.md @@ -0,0 +1,49 @@ +# Library Instrumentation for Jetty HttpClient version 12.0 and higher + +Provides OpenTelemetry instrumentation for the [Jetty HttpClient](https://www.eclipse.org/jetty/documentation/jetty-12/programming-guide/index.html#pg-client-http), +enabling database client spans and metrics. + +## Quickstart + +### Add these dependencies to your project + +Replace `OPENTELEMETRY_VERSION` with the [latest release](https://central.sonatype.com/artifact/io.opentelemetry.instrumentation/opentelemetry-jetty-httpclient-12.0). + +For Maven, add to your `pom.xml` dependencies: + +```xml + + + io.opentelemetry.instrumentation + opentelemetry-jetty-httpclient-12.0 + OPENTELEMETRY_VERSION + + +``` + +For Gradle, add to your dependencies: + +```kotlin +implementation("io.opentelemetry.instrumentation:opentelemetry-jetty-httpclient-12.0:OPENTELEMETRY_VERSION") +``` + +### Usage + +```java +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.instrumentation.jetty.httpclient.v12_0.JettyClientTelemetry; +import org.eclipse.jetty.client.HttpClient; + +// ... + +// Get an OpenTelemetry instance +OpenTelemetry openTelemetry = ...; + +// Create a JettyClientTelemetry instance +JettyClientTelemetry telemetry = JettyClientTelemetry.create(openTelemetry); + +// Get a traced HttpClient +HttpClient httpClient = telemetry.getHttpClient(); + +// ... use the httpClient to make requests +``` diff --git a/instrumentation/jetty-httpclient/jetty-httpclient-12.0/testing/src/main/java/io/opentelemetry/instrumentation/jetty/httpclient/v12_0/AbstractJettyClient12Test.java b/instrumentation/jetty-httpclient/jetty-httpclient-12.0/testing/src/main/java/io/opentelemetry/instrumentation/jetty/httpclient/v12_0/AbstractJettyClient12Test.java index d68f84c499db..2d77b92cbaa2 100644 --- a/instrumentation/jetty-httpclient/jetty-httpclient-12.0/testing/src/main/java/io/opentelemetry/instrumentation/jetty/httpclient/v12_0/AbstractJettyClient12Test.java +++ b/instrumentation/jetty-httpclient/jetty-httpclient-12.0/testing/src/main/java/io/opentelemetry/instrumentation/jetty/httpclient/v12_0/AbstractJettyClient12Test.java @@ -14,11 +14,11 @@ import java.net.URI; import java.util.Map; import java.util.Objects; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import org.eclipse.jetty.client.ContentResponse; -import org.eclipse.jetty.client.FutureResponseListener; import org.eclipse.jetty.client.HttpClient; import org.eclipse.jetty.client.Request; import org.eclipse.jetty.client.Response; @@ -39,7 +39,7 @@ public abstract class AbstractJettyClient12Test extends AbstractHttpClientTest super.onHeaders(response)); - } - @Override - public void onSuccess(Response response) { - testing.runWithSpan("onSuccess", () -> super.onSuccess(response)); - } + CompletableFuture responseFuture = new CompletableFuture<>(); + TracingResponseListener responseListener = new TracingResponseListener(responseFuture); - @Override - public void onComplete(Result result) { - testing.runWithSpan("onComplete", () -> super.onComplete(result)); - } - }; testing.runWithSpan("parent", () -> request.send(responseListener)); - Response response = responseListener.get(); + Response response = responseFuture.get(); assertThat(response.getStatus()).isEqualTo(200); testing.waitAndAssertTraces( @@ -162,6 +149,39 @@ public void onComplete(Result result) { .hasParent(trace.getSpan(0)))); } + private class TracingResponseListener + implements Response.HeadersListener, Response.SuccessListener, Response.CompleteListener { + + private final CompletableFuture responseFuture; + + TracingResponseListener(CompletableFuture responseFuture) { + this.responseFuture = responseFuture; + } + + @Override + public void onHeaders(Response response) { + testing.runWithSpan("onHeaders", () -> {}); + } + + @Override + public void onSuccess(Response response) { + testing.runWithSpan("onSuccess", () -> {}); + } + + @Override + public void onComplete(Result result) { + testing.runWithSpan( + "onComplete", + () -> { + if (result.isSucceeded()) { + responseFuture.complete(result.getResponse()); + } else { + responseFuture.completeExceptionally(result.getFailure()); + } + }); + } + } + private static class JettyClientListener implements Request.FailureListener, Response.FailureListener { volatile Throwable failure; diff --git a/instrumentation/jetty-httpclient/jetty-httpclient-9.2/library/README.md b/instrumentation/jetty-httpclient/jetty-httpclient-9.2/library/README.md new file mode 100644 index 000000000000..e9a769cc5dcb --- /dev/null +++ b/instrumentation/jetty-httpclient/jetty-httpclient-9.2/library/README.md @@ -0,0 +1,49 @@ +# Library Instrumentation for Jetty HttpClient version 9.2 and higher + +Provides OpenTelemetry instrumentation for the [Jetty HttpClient](https://github.com/jetty/jetty.project/tree/jetty-9.4.x), enabling http client spans +and metrics. + +## Quickstart + +### Add these dependencies to your project + +Replace `OPENTELEMETRY_VERSION` with the [latest release](https://central.sonatype.com/artifact/io.opentelemetry.instrumentation/opentelemetry-jetty-httpclient-9.2). + +For Maven, add to your `pom.xml` dependencies: + +```xml + + + io.opentelemetry.instrumentation + opentelemetry-jetty-httpclient-9.2 + OPENTELEMETRY_VERSION + + +``` + +For Gradle, add to your dependencies: + +```kotlin +implementation("io.opentelemetry.instrumentation:opentelemetry-jetty-httpclient-9.2:OPENTELEMETRY_VERSION") +``` + +### Usage + +```java +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.instrumentation.jetty.httpclient.v9_2.JettyClientTelemetry; +import org.eclipse.jetty.client.HttpClient; + +// ... + +// Get an OpenTelemetry instance +OpenTelemetry openTelemetry = ...; + +// Create a JettyClientTelemetry instance +JettyClientTelemetry telemetry = JettyClientTelemetry.create(openTelemetry); + +// Get a traced HttpClient +HttpClient httpClient = telemetry.getHttpClient(); + +// ... use the httpClient to make requests +``` diff --git a/instrumentation/jms/jms-1.1/javaagent/build.gradle.kts b/instrumentation/jms/jms-1.1/javaagent/build.gradle.kts index 8c769fd89f04..a7d88a8ab604 100644 --- a/instrumentation/jms/jms-1.1/javaagent/build.gradle.kts +++ b/instrumentation/jms/jms-1.1/javaagent/build.gradle.kts @@ -54,6 +54,9 @@ tasks { } val testReceiveSpansDisabled by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath + filter { includeTestsMatching("Jms1SuppressReceiveSpansTest") } @@ -68,8 +71,7 @@ tasks { } check { - dependsOn(testing.suites) - dependsOn(testReceiveSpansDisabled) + dependsOn(testing.suites, testReceiveSpansDisabled) } } diff --git a/instrumentation/jms/jms-1.1/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/jms/v1_1/AbstractJms1Test.java b/instrumentation/jms/jms-1.1/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/jms/v1_1/AbstractJms1Test.java index 49cfffb23734..b5cbc90be6d7 100644 --- a/instrumentation/jms/jms-1.1/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/jms/v1_1/AbstractJms1Test.java +++ b/instrumentation/jms/jms-1.1/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/jms/v1_1/AbstractJms1Test.java @@ -177,8 +177,8 @@ void shouldCaptureMessageHeaders( // given Destination destination = destinationFactory.create(session); TextMessage sentMessage = session.createTextMessage("a message"); - sentMessage.setStringProperty("test_message_header", "test"); - sentMessage.setIntProperty("test_message_int_header", 1234); + sentMessage.setStringProperty("Test_Message_Header", "test"); + sentMessage.setIntProperty("Test_Message_Int_Header", 1234); MessageProducer producer = session.createProducer(destination); cleanup.deferCleanup(producer::close); @@ -215,10 +215,10 @@ void shouldCaptureMessageHeaders( equalTo(MESSAGING_MESSAGE_ID, messageId), messagingTempDestination(isTemporary), equalTo( - stringArrayKey("messaging.header.test_message_header"), + stringArrayKey("messaging.header.Test_Message_Header"), singletonList("test")), equalTo( - stringArrayKey("messaging.header.test_message_int_header"), + stringArrayKey("messaging.header.Test_Message_Int_Header"), singletonList("1234"))), span -> span.hasName(destinationName + " process") @@ -231,10 +231,10 @@ void shouldCaptureMessageHeaders( equalTo(MESSAGING_MESSAGE_ID, messageId), messagingTempDestination(isTemporary), equalTo( - stringArrayKey("messaging.header.test_message_header"), + stringArrayKey("messaging.header.Test_Message_Header"), singletonList("test")), equalTo( - stringArrayKey("messaging.header.test_message_int_header"), + stringArrayKey("messaging.header.Test_Message_Int_Header"), singletonList("1234"))), span -> span.hasName("consumer").hasParent(trace.getSpan(2)))); } diff --git a/instrumentation/jms/jms-3.0/javaagent/build.gradle.kts b/instrumentation/jms/jms-3.0/javaagent/build.gradle.kts index d52b6ce0be57..b4da635222d1 100644 --- a/instrumentation/jms/jms-3.0/javaagent/build.gradle.kts +++ b/instrumentation/jms/jms-3.0/javaagent/build.gradle.kts @@ -39,6 +39,9 @@ tasks { } val testReceiveSpansDisabled by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath + filter { includeTestsMatching("Jms3SuppressReceiveSpansTest") } @@ -53,6 +56,6 @@ tasks { } check { - dependsOn(testReceiveSpansDisabled) + dependsOn(testing.suites, testReceiveSpansDisabled) } } diff --git a/instrumentation/jms/jms-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/jms/v3_0/AbstractJms3Test.java b/instrumentation/jms/jms-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/jms/v3_0/AbstractJms3Test.java index 524b96a39c38..1f4df4da5864 100644 --- a/instrumentation/jms/jms-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/jms/v3_0/AbstractJms3Test.java +++ b/instrumentation/jms/jms-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/jms/v3_0/AbstractJms3Test.java @@ -191,8 +191,8 @@ void shouldCaptureMessageHeaders(DestinationFactory destinationFactory, boolean // given Destination destination = destinationFactory.create(session); TextMessage sentMessage = session.createTextMessage("hello there"); - sentMessage.setStringProperty("test_message_header", "test"); - sentMessage.setIntProperty("test_message_int_header", 1234); + sentMessage.setStringProperty("Test_Message_Header", "test"); + sentMessage.setIntProperty("Test_Message_Int_Header", 1234); MessageProducer producer = session.createProducer(destination); cleanup.deferCleanup(producer); @@ -232,10 +232,10 @@ void shouldCaptureMessageHeaders(DestinationFactory destinationFactory, boolean equalTo(MESSAGING_MESSAGE_ID, messageId), messagingTempDestination(isTemporary), equalTo( - stringArrayKey("messaging.header.test_message_header"), + stringArrayKey("messaging.header.Test_Message_Header"), singletonList("test")), equalTo( - stringArrayKey("messaging.header.test_message_int_header"), + stringArrayKey("messaging.header.Test_Message_Int_Header"), singletonList("1234"))), span -> span.hasName(actualDestinationName + " process") @@ -247,10 +247,10 @@ void shouldCaptureMessageHeaders(DestinationFactory destinationFactory, boolean equalTo(MESSAGING_OPERATION, "process"), equalTo(MESSAGING_MESSAGE_ID, messageId), equalTo( - stringArrayKey("messaging.header.test_message_header"), + stringArrayKey("messaging.header.Test_Message_Header"), singletonList("test")), equalTo( - stringArrayKey("messaging.header.test_message_int_header"), + stringArrayKey("messaging.header.Test_Message_Int_Header"), singletonList("1234"))), span -> span.hasName("consumer").hasParent(trace.getSpan(2)))); } diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-0.11/javaagent/build.gradle.kts b/instrumentation/kafka/kafka-clients/kafka-clients-0.11/javaagent/build.gradle.kts index 96b8a675cc73..84fc9831d8a7 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-0.11/javaagent/build.gradle.kts +++ b/instrumentation/kafka/kafka-clients/kafka-clients-0.11/javaagent/build.gradle.kts @@ -36,6 +36,8 @@ tasks { } val testPropagationDisabled by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath filter { includeTestsMatching("KafkaClientPropagationDisabledTest") } @@ -44,6 +46,8 @@ tasks { } val testReceiveSpansDisabled by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath filter { includeTestsMatching("KafkaClientSuppressReceiveSpansTest") } @@ -59,7 +63,15 @@ tasks { } check { - dependsOn(testPropagationDisabled) - dependsOn(testReceiveSpansDisabled) + dependsOn(testPropagationDisabled, testReceiveSpansDisabled) + } +} + +val latestDepTest = findProperty("testLatestDeps") as Boolean + +// kafka 4.1 requires java 11 +if (latestDepTest) { + otelJava { + minJavaVersionSupported.set(JavaVersion.VERSION_11) } } diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-0.11/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/kafkaclients/v0_11/KafkaClientDefaultTest.java b/instrumentation/kafka/kafka-clients/kafka-clients-0.11/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/kafkaclients/v0_11/KafkaClientDefaultTest.java index 98e3a8d43553..e0e7821b4b46 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-0.11/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/kafkaclients/v0_11/KafkaClientDefaultTest.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-0.11/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/kafkaclients/v0_11/KafkaClientDefaultTest.java @@ -49,7 +49,7 @@ void testKafkaProducerAndConsumerSpan(boolean testHeaders) throws Exception { if (testHeaders) { producerRecord .headers() - .add("test-message-header", "test".getBytes(StandardCharsets.UTF_8)); + .add("Test-Message-Header", "test".getBytes(StandardCharsets.UTF_8)); } producer .send( @@ -214,7 +214,7 @@ void testKafkaHeaderNull() throws Exception { () -> { ProducerRecord producerRecord = new ProducerRecord<>(SHARED_TOPIC, 10, greeting); - producerRecord.headers().add("test-message-header", null); + producerRecord.headers().add("Test-Message-Header", null); producer .send( producerRecord, @@ -238,7 +238,7 @@ void testKafkaHeaderNull() throws Exception { () -> { assertThat(record.key()).isEqualTo(10); assertThat(record.value()).isEqualTo(greeting); - assertThat(record.headers().lastHeader("test-message-header").value()).isNull(); + assertThat(record.headers().lastHeader("Test-Message-Header").value()).isNull(); }); } AtomicReference producerSpan = new AtomicReference<>(); diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-0.11/metadata.yaml b/instrumentation/kafka/kafka-clients/kafka-clients-0.11/metadata.yaml index 84e85395ac72..edf9dc017838 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-0.11/metadata.yaml +++ b/instrumentation/kafka/kafka-clients/kafka-clients-0.11/metadata.yaml @@ -1,6 +1,7 @@ description: > This instrumentation enables messaging spans and metrics for Apache Kafka 0.11 clients. It automatically traces message production and consumption, propagates context, and emits metrics for production and consumption. +library_link: https://kafka.apache.org/ configurations: - name: otel.instrumentation.kafka.producer-propagation.enabled description: Enable context propagation for kafka message producers. diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-0.11/testing/src/main/java/io/opentelemetry/instrumentation/kafkaclients/common/v0_11/internal/KafkaClientBaseTest.java b/instrumentation/kafka/kafka-clients/kafka-clients-0.11/testing/src/main/java/io/opentelemetry/instrumentation/kafkaclients/common/v0_11/internal/KafkaClientBaseTest.java index 84a99ca65d4e..6c7a334a48ec 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-0.11/testing/src/main/java/io/opentelemetry/instrumentation/kafkaclients/common/v0_11/internal/KafkaClientBaseTest.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-0.11/testing/src/main/java/io/opentelemetry/instrumentation/kafkaclients/common/v0_11/internal/KafkaClientBaseTest.java @@ -188,7 +188,7 @@ protected static List sendAttributes( if (testHeaders) { assertions.add( equalTo( - AttributeKey.stringArrayKey("messaging.header.test_message_header"), + AttributeKey.stringArrayKey("messaging.header.Test_Message_Header"), Collections.singletonList("test"))); } return assertions; @@ -211,7 +211,7 @@ protected static List receiveAttributes(boolean testHeaders) if (testHeaders) { assertions.add( equalTo( - AttributeKey.stringArrayKey("messaging.header.test_message_header"), + AttributeKey.stringArrayKey("messaging.header.Test_Message_Header"), Collections.singletonList("test"))); } return assertions; @@ -249,7 +249,7 @@ protected static List processAttributes( if (testHeaders) { assertions.add( equalTo( - AttributeKey.stringArrayKey("messaging.header.test_message_header"), + AttributeKey.stringArrayKey("messaging.header.Test_Message_Header"), Collections.singletonList("test"))); } diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/README.md b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/README.md index b244222c1815..2d4019de6fd1 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/README.md +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/README.md @@ -5,7 +5,7 @@ ### Add these dependencies to your project Replace `OPENTELEMETRY_VERSION` with the [latest -release](https://search.maven.org/search?q=g:io.opentelemetry.instrumentation%20AND%20a:opentelemetry-kafka-clients-2.6). +release]( https://central.sonatype.com/artifact/io.opentelemetry.instrumentation/opentelemetry-kafka-clients-2.6). For Maven, add to your `pom.xml` dependencies: diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/build.gradle.kts b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/build.gradle.kts index 17dadd3bc579..b23528d55646 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/build.gradle.kts +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/build.gradle.kts @@ -23,6 +23,8 @@ tasks { } val testReceiveSpansDisabled by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath filter { includeTestsMatching("InterceptorsSuppressReceiveSpansTest") includeTestsMatching("WrapperSuppressReceiveSpansTest") @@ -36,9 +38,19 @@ tasks { excludeTestsMatching("WrapperSuppressReceiveSpansTest") } jvmArgs("-Dotel.instrumentation.messaging.experimental.receive-telemetry.enabled=true") + systemProperty("otel.instrumentation.messaging.experimental.capture-headers", "Test-Message-Header") } check { dependsOn(testReceiveSpansDisabled) } } + +val latestDepTest = findProperty("testLatestDeps") as Boolean + +// kafka 4.1 requires java 11 +if (latestDepTest) { + otelJava { + minJavaVersionSupported.set(JavaVersion.VERSION_11) + } +} diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingConsumerInterceptor.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingConsumerInterceptor.java index 368767c1022a..8f60b07e6a88 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingConsumerInterceptor.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingConsumerInterceptor.java @@ -5,6 +5,8 @@ package io.opentelemetry.instrumentation.kafkaclients.v2_6; +import static java.util.Collections.emptyList; + import com.google.errorprone.annotations.CanIgnoreReturnValue; import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.context.Context; @@ -32,6 +34,9 @@ public class TracingConsumerInterceptor implements ConsumerInterceptor implements ProducerInterceptor { - private static final KafkaTelemetry telemetry = KafkaTelemetry.create(GlobalOpenTelemetry.get()); + private static final KafkaTelemetry telemetry = + KafkaTelemetry.builder(GlobalOpenTelemetry.get()) + .setCapturedHeaders( + ConfigPropertiesUtil.getList( + "otel.instrumentation.messaging.experimental.capture-headers", emptyList())) + .build(); @Nullable private String clientId; diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/AbstractInterceptorsTest.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/AbstractInterceptorsTest.java index 73d62592a267..108b80635425 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/AbstractInterceptorsTest.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/AbstractInterceptorsTest.java @@ -53,6 +53,8 @@ void testInterceptors() throws InterruptedException { new ProducerRecord<>(SHARED_TOPIC, greeting); producerRecord .headers() + // add header to test capturing header value as span attribute + .add("Test-Message-Header", "test".getBytes(StandardCharsets.UTF_8)) // adding baggage header in w3c baggage format .add( "baggage", diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/AbstractWrapperTest.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/AbstractWrapperTest.java index c52d67d43440..4dc84990ef94 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/AbstractWrapperTest.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/AbstractWrapperTest.java @@ -34,7 +34,7 @@ abstract class AbstractWrapperTest extends KafkaClientBaseTest { void testWrappers(boolean testHeaders) throws InterruptedException { KafkaTelemetryBuilder telemetryBuilder = KafkaTelemetry.builder(testing.getOpenTelemetry()) - .setCapturedHeaders(singletonList("test-message-header")) + .setCapturedHeaders(singletonList("Test-Message-Header")) // TODO run tests both with and without experimental span attributes .setCaptureExperimentalSpanAttributes(true); configure(telemetryBuilder); @@ -50,7 +50,7 @@ void testWrappers(boolean testHeaders) throws InterruptedException { if (testHeaders) { producerRecord .headers() - .add("test-message-header", "test".getBytes(StandardCharsets.UTF_8)); + .add("Test-Message-Header", "test".getBytes(StandardCharsets.UTF_8)); } wrappedProducer.send( producerRecord, diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/InterceptorsTest.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/InterceptorsTest.java index 8f4f65dc2262..f8003095032d 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/InterceptorsTest.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/InterceptorsTest.java @@ -5,6 +5,7 @@ package io.opentelemetry.instrumentation.kafkaclients.v2_6; +import static io.opentelemetry.api.common.AttributeKey.stringArrayKey; import static io.opentelemetry.instrumentation.testing.util.TelemetryDataUtil.orderByRootSpanName; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies; @@ -16,6 +17,7 @@ import static io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes.MESSAGING_MESSAGE_BODY_SIZE; import static io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes.MESSAGING_OPERATION; import static io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes.MESSAGING_SYSTEM; +import static java.util.Collections.singletonList; import static org.assertj.core.api.Assertions.assertThat; import io.opentelemetry.api.trace.SpanContext; @@ -42,6 +44,9 @@ void assertTraces() { .hasKind(SpanKind.PRODUCER) .hasParent(trace.getSpan(0)) .hasAttributesSatisfyingExactly( + equalTo( + stringArrayKey("messaging.header.Test_Message_Header"), + singletonList("test")), equalTo(MESSAGING_SYSTEM, "kafka"), equalTo(MESSAGING_DESTINATION_NAME, SHARED_TOPIC), equalTo(MESSAGING_OPERATION, "publish"), @@ -64,6 +69,9 @@ void assertTraces() { .hasNoParent() .hasLinksSatisfying(links -> assertThat(links).isEmpty()) .hasAttributesSatisfyingExactly( + equalTo( + stringArrayKey("messaging.header.Test_Message_Header"), + singletonList("test")), equalTo(MESSAGING_SYSTEM, "kafka"), equalTo(MESSAGING_DESTINATION_NAME, SHARED_TOPIC), equalTo(MESSAGING_OPERATION, "receive"), @@ -78,6 +86,9 @@ void assertTraces() { .hasParent(trace.getSpan(0)) .hasLinks(LinkData.create(producerSpanContext.get())) .hasAttributesSatisfyingExactly( + equalTo( + stringArrayKey("messaging.header.Test_Message_Header"), + singletonList("test")), equalTo(MESSAGING_SYSTEM, "kafka"), equalTo(MESSAGING_DESTINATION_NAME, SHARED_TOPIC), equalTo(MESSAGING_OPERATION, "process"), diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/WrapperSuppressReceiveSpansTest.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/WrapperSuppressReceiveSpansTest.java index ab588285c1b3..b3cd095d09e4 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/WrapperSuppressReceiveSpansTest.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/WrapperSuppressReceiveSpansTest.java @@ -73,7 +73,7 @@ protected static List sendAttributes(boolean testHeaders) { if (testHeaders) { assertions.add( equalTo( - AttributeKey.stringArrayKey("messaging.header.test_message_header"), + AttributeKey.stringArrayKey("messaging.header.Test_Message_Header"), Collections.singletonList("test"))); } return assertions; @@ -100,7 +100,7 @@ private static List processAttributes(String greeting, boole if (testHeaders) { assertions.add( equalTo( - AttributeKey.stringArrayKey("messaging.header.test_message_header"), + AttributeKey.stringArrayKey("messaging.header.Test_Message_Header"), Collections.singletonList("test"))); } return assertions; diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/WrapperTest.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/WrapperTest.java index 890b68733f2e..85ddd5aa4cad 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/WrapperTest.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/WrapperTest.java @@ -96,7 +96,7 @@ protected static List sendAttributes(boolean testHeaders) { if (testHeaders) { assertions.add( equalTo( - AttributeKey.stringArrayKey("messaging.header.test_message_header"), + AttributeKey.stringArrayKey("messaging.header.Test_Message_Header"), Collections.singletonList("test"))); } return assertions; @@ -123,7 +123,7 @@ private static List processAttributes(String greeting, boole if (testHeaders) { assertions.add( equalTo( - AttributeKey.stringArrayKey("messaging.header.test_message_header"), + AttributeKey.stringArrayKey("messaging.header.Test_Message_Header"), Collections.singletonList("test"))); } return assertions; @@ -143,7 +143,7 @@ protected static List receiveAttributes(boolean testHeaders) if (testHeaders) { assertions.add( equalTo( - AttributeKey.stringArrayKey("messaging.header.test_message_header"), + AttributeKey.stringArrayKey("messaging.header.Test_Message_Header"), Collections.singletonList("test"))); } return assertions; diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/metadata.yaml b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/metadata.yaml index 4c8b2bdb538c..85d9ffa83e6c 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/metadata.yaml +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/metadata.yaml @@ -1,2 +1,14 @@ description: > This instrumentation provides a library integration that enables messaging spans and metrics for Apache Kafka 2.6+ clients. +library_link: https://kafka.apache.org/ +configurations: + - name: otel.instrumentation.messaging.experimental.capture-headers + description: A comma-separated list of header names to capture as span attributes. + type: list + default: '' + - name: otel.instrumentation.messaging.experimental.receive-telemetry.enabled + description: > + Enables experimental receive telemetry, which will cause consumers to start a new trace, with + only a span link connecting it to the producer trace. + type: boolean + default: false diff --git a/instrumentation/kafka/kafka-streams-0.11/javaagent/build.gradle.kts b/instrumentation/kafka/kafka-streams-0.11/javaagent/build.gradle.kts index 9483d0527bd1..b488203aad78 100644 --- a/instrumentation/kafka/kafka-streams-0.11/javaagent/build.gradle.kts +++ b/instrumentation/kafka/kafka-streams-0.11/javaagent/build.gradle.kts @@ -34,6 +34,8 @@ tasks { } val testReceiveSpansDisabled by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath filter { includeTestsMatching("KafkaStreamsSuppressReceiveSpansTest") } @@ -51,3 +53,12 @@ tasks { dependsOn(testReceiveSpansDisabled) } } + +val latestDepTest = findProperty("testLatestDeps") as Boolean + +// kafka 4.1 requires java 11 +if (latestDepTest) { + otelJava { + minJavaVersionSupported.set(JavaVersion.VERSION_11) + } +} diff --git a/instrumentation/kafka/kafka-streams-0.11/metadata.yaml b/instrumentation/kafka/kafka-streams-0.11/metadata.yaml new file mode 100644 index 000000000000..7e93d3199ffe --- /dev/null +++ b/instrumentation/kafka/kafka-streams-0.11/metadata.yaml @@ -0,0 +1 @@ +library_link: https://kafka.apache.org/documentation/streams/ diff --git a/instrumentation/ktor/ktor-1.0/library/README.md b/instrumentation/ktor/ktor-1.0/library/README.md index 6ec0bedff8f0..a8028d4571d0 100644 --- a/instrumentation/ktor/ktor-1.0/library/README.md +++ b/instrumentation/ktor/ktor-1.0/library/README.md @@ -8,7 +8,7 @@ Currently, only server instrumentation is supported. ### Add these dependencies to your project Replace `OPENTELEMETRY_VERSION` with the [latest -release](https://search.maven.org/search?q=g:io.opentelemetry.instrumentation%20AND%20a:opentelemetry-ktor-1.0). +release]( https://central.sonatype.com/artifact/io.opentelemetry.instrumentation/opentelemetry-ktor-1.0). For Maven, add to your `pom.xml` dependencies: diff --git a/instrumentation/ktor/ktor-2.0/library/README.md b/instrumentation/ktor/ktor-2.0/library/README.md index 6740d0c0fcea..22c9d8395d17 100644 --- a/instrumentation/ktor/ktor-2.0/library/README.md +++ b/instrumentation/ktor/ktor-2.0/library/README.md @@ -8,7 +8,7 @@ Server and client instrumentations are supported. ### Add these dependencies to your project Replace `OPENTELEMETRY_VERSION` with the [latest -release](https://search.maven.org/search?q=g:io.opentelemetry.instrumentation%20AND%20a:opentelemetry-ktor-2.0). +release]( https://central.sonatype.com/artifact/io.opentelemetry.instrumentation/opentelemetry-ktor-2.0). For Maven, add to your `pom.xml` dependencies: diff --git a/instrumentation/ktor/ktor-3.0/library/README.md b/instrumentation/ktor/ktor-3.0/library/README.md index da499a3f787e..80a3fa2033bc 100644 --- a/instrumentation/ktor/ktor-3.0/library/README.md +++ b/instrumentation/ktor/ktor-3.0/library/README.md @@ -8,7 +8,7 @@ Server and client instrumentations are supported. ### Add these dependencies to your project Replace `OPENTELEMETRY_VERSION` with the [latest -release](https://search.maven.org/search?q=g:io.opentelemetry.instrumentation%20AND%20a:opentelemetry-ktor-3.0). +release]( https://central.sonatype.com/artifact/io.opentelemetry.instrumentation/opentelemetry-ktor-3.0). For Maven, add to your `pom.xml` dependencies: diff --git a/instrumentation/lettuce/lettuce-4.0/javaagent/build.gradle.kts b/instrumentation/lettuce/lettuce-4.0/javaagent/build.gradle.kts index 97a05becb7c4..945cf6d6190a 100644 --- a/instrumentation/lettuce/lettuce-4.0/javaagent/build.gradle.kts +++ b/instrumentation/lettuce/lettuce-4.0/javaagent/build.gradle.kts @@ -26,6 +26,8 @@ tasks { } val testStableSemconv by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath jvmArgs("-Dotel.semconv-stability.opt-in=database") } diff --git a/instrumentation/lettuce/lettuce-5.0/javaagent/build.gradle.kts b/instrumentation/lettuce/lettuce-5.0/javaagent/build.gradle.kts index e9ac67e15dbc..a97763c59a70 100644 --- a/instrumentation/lettuce/lettuce-5.0/javaagent/build.gradle.kts +++ b/instrumentation/lettuce/lettuce-5.0/javaagent/build.gradle.kts @@ -33,6 +33,8 @@ tasks { } val testStableSemconv by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath jvmArgs("-Dotel.semconv-stability.opt-in=database") } diff --git a/instrumentation/lettuce/lettuce-5.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/lettuce/v5_0/LettuceAsyncClientTest.java b/instrumentation/lettuce/lettuce-5.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/lettuce/v5_0/LettuceAsyncClientTest.java index da1c3f2c2345..215cfc86f275 100644 --- a/instrumentation/lettuce/lettuce-5.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/lettuce/v5_0/LettuceAsyncClientTest.java +++ b/instrumentation/lettuce/lettuce-5.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/lettuce/v5_0/LettuceAsyncClientTest.java @@ -52,7 +52,6 @@ import java.util.function.BiFunction; import java.util.function.Consumer; import java.util.function.Function; -import org.assertj.core.api.AbstractAssert; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; @@ -177,7 +176,7 @@ void testConnectExceptionInsideTheConnectionFuture() { + incorrectPort), satisfies( AttributeKey.stringKey("exception.stacktrace"), - AbstractAssert::isNotNull))))); + val -> val.isNotNull()))))); } @Test diff --git a/instrumentation/lettuce/lettuce-5.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/lettuce/v5_0/LettuceSyncClientTest.java b/instrumentation/lettuce/lettuce-5.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/lettuce/v5_0/LettuceSyncClientTest.java index 6037d9d4433d..50cdd3698a1d 100644 --- a/instrumentation/lettuce/lettuce-5.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/lettuce/v5_0/LettuceSyncClientTest.java +++ b/instrumentation/lettuce/lettuce-5.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/lettuce/v5_0/LettuceSyncClientTest.java @@ -28,7 +28,6 @@ import java.net.InetAddress; import java.net.UnknownHostException; import java.util.Map; -import org.assertj.core.api.AbstractAssert; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; @@ -137,7 +136,7 @@ void testConnectException() { + incorrectPort), satisfies( AttributeKey.stringKey("exception.stacktrace"), - AbstractAssert::isNotNull))))); + val -> val.isNotNull()))))); } @Test diff --git a/instrumentation/lettuce/lettuce-5.1/javaagent/build.gradle.kts b/instrumentation/lettuce/lettuce-5.1/javaagent/build.gradle.kts index 654a6172715d..014f750bb29a 100644 --- a/instrumentation/lettuce/lettuce-5.1/javaagent/build.gradle.kts +++ b/instrumentation/lettuce/lettuce-5.1/javaagent/build.gradle.kts @@ -30,6 +30,8 @@ tasks { } val testStableSemconv by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath jvmArgs("-Dotel.semconv-stability.opt-in=database") } diff --git a/instrumentation/lettuce/lettuce-5.1/library/build.gradle.kts b/instrumentation/lettuce/lettuce-5.1/library/build.gradle.kts index 1ea17a553d5d..d3a526877992 100644 --- a/instrumentation/lettuce/lettuce-5.1/library/build.gradle.kts +++ b/instrumentation/lettuce/lettuce-5.1/library/build.gradle.kts @@ -19,6 +19,8 @@ tasks { } val testStableSemconv by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath jvmArgs("-Dotel.semconv-stability.opt-in=database") } diff --git a/instrumentation/log4j/log4j-appender-1.2/javaagent/build.gradle.kts b/instrumentation/log4j/log4j-appender-1.2/javaagent/build.gradle.kts index bc336ded66ca..f31453561e78 100644 --- a/instrumentation/log4j/log4j-appender-1.2/javaagent/build.gradle.kts +++ b/instrumentation/log4j/log4j-appender-1.2/javaagent/build.gradle.kts @@ -34,6 +34,7 @@ configurations { tasks.withType().configureEach { // TODO run tests both with and without experimental log attributes jvmArgs("-Dotel.instrumentation.log4j-appender.experimental.capture-mdc-attributes=*") + jvmArgs("-Dotel.instrumentation.log4j-appender.experimental.capture-code-attributes=true") jvmArgs("-Dotel.instrumentation.log4j-appender.experimental-log-attributes=true") jvmArgs("-Dotel.instrumentation.log4j-appender.experimental-log-attributes=true") jvmArgs("-Dotel.instrumentation.common.experimental.controller-telemetry.enabled=true") diff --git a/instrumentation/log4j/log4j-appender-1.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/log4j/appender/v1_2/Log4jAppenderInstrumentation.java b/instrumentation/log4j/log4j-appender-1.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/log4j/appender/v1_2/Log4jAppenderInstrumentation.java index 1ec27c84bff7..009d07bf983f 100644 --- a/instrumentation/log4j/log4j-appender-1.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/log4j/appender/v1_2/Log4jAppenderInstrumentation.java +++ b/instrumentation/log4j/log4j-appender-1.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/log4j/appender/v1_2/Log4jAppenderInstrumentation.java @@ -48,6 +48,7 @@ public static class ForcedLogAdvice { @Advice.OnMethodEnter(suppress = Throwable.class) public static void methodEnter( @Advice.This Category logger, + @Advice.Argument(0) String fqcn, @Advice.Argument(1) Priority level, @Advice.Argument(2) Object message, @Advice.Argument(3) Throwable t, @@ -56,7 +57,7 @@ public static void methodEnter( // framework delegates to another callDepth = CallDepth.forClass(LoggerProvider.class); if (callDepth.getAndIncrement() == 0) { - LogEventMapper.INSTANCE.capture(logger, level, message, t); + LogEventMapper.INSTANCE.capture(fqcn, logger, level, message, t); } } diff --git a/instrumentation/log4j/log4j-appender-1.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/log4j/appender/v1_2/LogEventMapper.java b/instrumentation/log4j/log4j-appender-1.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/log4j/appender/v1_2/LogEventMapper.java index 522ef4ad9fb2..1f6966503fde 100644 --- a/instrumentation/log4j/log4j-appender-1.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/log4j/appender/v1_2/LogEventMapper.java +++ b/instrumentation/log4j/log4j-appender-1.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/log4j/appender/v1_2/LogEventMapper.java @@ -11,11 +11,14 @@ import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.common.AttributesBuilder; +import io.opentelemetry.api.incubator.logs.ExtendedLogRecordBuilder; import io.opentelemetry.api.logs.LogRecordBuilder; import io.opentelemetry.api.logs.Severity; import io.opentelemetry.context.Context; +import io.opentelemetry.instrumentation.api.internal.SemconvStability; import io.opentelemetry.instrumentation.api.internal.cache.Cache; import io.opentelemetry.javaagent.bootstrap.internal.AgentInstrumentationConfig; +import io.opentelemetry.semconv.CodeAttributes; import io.opentelemetry.semconv.ExceptionAttributes; import io.opentelemetry.semconv.incubating.ThreadIncubatingAttributes; import java.io.PrintWriter; @@ -28,6 +31,7 @@ import org.apache.log4j.Category; import org.apache.log4j.MDC; import org.apache.log4j.Priority; +import org.apache.log4j.spi.LocationInfo; public final class LogEventMapper { @@ -35,6 +39,11 @@ public final class LogEventMapper { public static final LogEventMapper INSTANCE = new LogEventMapper(); + private static final AttributeKey CODE_FILEPATH = AttributeKey.stringKey("code.filepath"); + private static final AttributeKey CODE_FUNCTION = AttributeKey.stringKey("code.function"); + private static final AttributeKey CODE_LINENO = AttributeKey.longKey("code.lineno"); + private static final AttributeKey CODE_NAMESPACE = + AttributeKey.stringKey("code.namespace"); // copied from org.apache.log4j.Level because it was only introduced in 1.2.12 private static final int TRACE_INT = 5000; @@ -60,7 +69,13 @@ private LogEventMapper() { captureMdcAttributes.size() == 1 && captureMdcAttributes.get(0).equals("*"); } - public void capture(Category logger, Priority level, Object message, Throwable throwable) { + boolean captureCodeAttributes = + AgentInstrumentationConfig.get() + .getBoolean( + "otel.instrumentation.log4j-appender.experimental.capture-code-attributes", false); + + public void capture( + String fqcn, Category logger, Priority level, Object message, Throwable throwable) { String instrumentationName = logger.getName(); if (instrumentationName == null || instrumentationName.isEmpty()) { instrumentationName = "ROOT"; @@ -87,13 +102,15 @@ public void capture(Category logger, Priority level, Object message, Throwable t // throwable if (throwable != null) { - // TODO (trask) extract method for recording exception into - // io.opentelemetry:opentelemetry-api - attributes.put(ExceptionAttributes.EXCEPTION_TYPE, throwable.getClass().getName()); - attributes.put(ExceptionAttributes.EXCEPTION_MESSAGE, throwable.getMessage()); - StringWriter writer = new StringWriter(); - throwable.printStackTrace(new PrintWriter(writer)); - attributes.put(ExceptionAttributes.EXCEPTION_STACKTRACE, writer.toString()); + if (builder instanceof ExtendedLogRecordBuilder) { + ((ExtendedLogRecordBuilder) builder).setException(throwable); + } else { + attributes.put(ExceptionAttributes.EXCEPTION_TYPE, throwable.getClass().getName()); + attributes.put(ExceptionAttributes.EXCEPTION_MESSAGE, throwable.getMessage()); + StringWriter writer = new StringWriter(); + throwable.printStackTrace(new PrintWriter(writer)); + attributes.put(ExceptionAttributes.EXCEPTION_STACKTRACE, writer.toString()); + } } captureMdcAttributes(attributes); @@ -104,6 +121,47 @@ public void capture(Category logger, Priority level, Object message, Throwable t attributes.put(ThreadIncubatingAttributes.THREAD_ID, currentThread.getId()); } + if (captureCodeAttributes) { + LocationInfo locationInfo = new LocationInfo(new Throwable(), fqcn); + String fileName = locationInfo.getFileName(); + if (fileName != null) { + if (SemconvStability.isEmitStableCodeSemconv()) { + attributes.put(CodeAttributes.CODE_FILE_PATH, fileName); + } + if (SemconvStability.isEmitOldCodeSemconv()) { + attributes.put(CODE_FILEPATH, fileName); + } + } + + if (SemconvStability.isEmitStableCodeSemconv()) { + attributes.put( + CodeAttributes.CODE_FUNCTION_NAME, + locationInfo.getClassName() + "." + locationInfo.getMethodName()); + } + if (SemconvStability.isEmitOldCodeSemconv()) { + attributes.put(CODE_NAMESPACE, locationInfo.getClassName()); + attributes.put(CODE_FUNCTION, locationInfo.getMethodName()); + } + + String lineNumber = locationInfo.getLineNumber(); + int codeLineNo = -1; + if (!lineNumber.equals("?")) { + try { + codeLineNo = Integer.parseInt(lineNumber); + } catch (NumberFormatException e) { + // ignore + } + } + if (codeLineNo >= 0) { + if (SemconvStability.isEmitStableCodeSemconv()) { + attributes.put(CodeAttributes.CODE_LINE_NUMBER, codeLineNo); + } + if (SemconvStability.isEmitOldCodeSemconv()) { + attributes.put(CODE_LINENO, codeLineNo); + } + } + } + builder.setAllAttributes(attributes.build()); // span context diff --git a/instrumentation/log4j/log4j-appender-1.2/javaagent/src/test/java/io/opentelemetry/instrumentation/log4j/appender/v1_2/Log4j1Test.java b/instrumentation/log4j/log4j-appender-1.2/javaagent/src/test/java/io/opentelemetry/instrumentation/log4j/appender/v1_2/Log4j1Test.java index 669dbb5053a5..b43d402e3c24 100644 --- a/instrumentation/log4j/log4j-appender-1.2/javaagent/src/test/java/io/opentelemetry/instrumentation/log4j/appender/v1_2/Log4j1Test.java +++ b/instrumentation/log4j/log4j-appender-1.2/javaagent/src/test/java/io/opentelemetry/instrumentation/log4j/appender/v1_2/Log4j1Test.java @@ -11,6 +11,8 @@ import static io.opentelemetry.semconv.ExceptionAttributes.EXCEPTION_MESSAGE; import static io.opentelemetry.semconv.ExceptionAttributes.EXCEPTION_STACKTRACE; import static io.opentelemetry.semconv.ExceptionAttributes.EXCEPTION_TYPE; +import static io.opentelemetry.semconv.incubating.ThreadIncubatingAttributes.THREAD_ID; +import static io.opentelemetry.semconv.incubating.ThreadIncubatingAttributes.THREAD_NAME; import static java.util.concurrent.TimeUnit.MILLISECONDS; import io.opentelemetry.api.common.AttributeKey; @@ -18,10 +20,10 @@ import io.opentelemetry.api.trace.SpanContext; import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension; import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.code.SemconvCodeStabilityUtil; import io.opentelemetry.sdk.common.InstrumentationScopeInfo; import io.opentelemetry.sdk.logs.data.LogRecordData; import io.opentelemetry.sdk.testing.assertj.AttributeAssertion; -import io.opentelemetry.semconv.incubating.ThreadIncubatingAttributes; import java.lang.reflect.Field; import java.time.Instant; import java.util.ArrayList; @@ -67,6 +69,26 @@ private static Stream provideParameters() { Arguments.of(true, true)); } + @Test + public void testCodeAttributes() { + logger.info("this is test message"); + List assertions = + SemconvCodeStabilityUtil.codeFileAndLineAssertions("Log4j1Test.java"); + assertions.addAll( + SemconvCodeStabilityUtil.codeFunctionAssertions(Log4j1Test.class, "testCodeAttributes")); + assertions.add(equalTo(THREAD_NAME, Thread.currentThread().getName())); + assertions.add(equalTo(THREAD_ID, Thread.currentThread().getId())); + + testing.waitAndAssertLogRecords( + logRecord -> + logRecord + .hasBody("this is test message") + .hasInstrumentationScope(InstrumentationScopeInfo.builder("abc").build()) + .hasSeverity(Severity.INFO) + .hasSeverityText("INFO") + .hasAttributesSatisfyingExactly(assertions)); + } + @ParameterizedTest @MethodSource("provideParameters") public void test(boolean logException, boolean withParent) throws InterruptedException { @@ -122,11 +144,8 @@ private static void test( List attributeAsserts = new ArrayList<>( Arrays.asList( - equalTo( - ThreadIncubatingAttributes.THREAD_NAME, - Thread.currentThread().getName()), - equalTo( - ThreadIncubatingAttributes.THREAD_ID, Thread.currentThread().getId()))); + equalTo(THREAD_NAME, Thread.currentThread().getName()), + equalTo(THREAD_ID, Thread.currentThread().getId()))); if (logException) { attributeAsserts.addAll( Arrays.asList( @@ -135,6 +154,11 @@ private static void test( satisfies( EXCEPTION_STACKTRACE, v -> v.contains(Log4j1Test.class.getName())))); } + attributeAsserts.addAll( + SemconvCodeStabilityUtil.codeFunctionAssertions( + Log4j1Test.class, "performLogging")); + attributeAsserts.addAll( + SemconvCodeStabilityUtil.codeFileAndLineAssertions("Log4j1Test.java")); logRecord.hasAttributesSatisfyingExactly(attributeAsserts); LogRecordData logRecordData = AssertAccess.getActual(logRecord); @@ -159,6 +183,14 @@ void testMdc() { MDC.remove("key2"); } + List assertions = + SemconvCodeStabilityUtil.codeFileAndLineAssertions("Log4j1Test.java"); + assertions.addAll(SemconvCodeStabilityUtil.codeFunctionAssertions(Log4j1Test.class, "testMdc")); + assertions.add(equalTo(AttributeKey.stringKey("key1"), "val1")); + assertions.add(equalTo(AttributeKey.stringKey("key2"), "val2")); + assertions.add(equalTo(THREAD_NAME, Thread.currentThread().getName())); + assertions.add(equalTo(THREAD_ID, Thread.currentThread().getId())); + testing.waitAndAssertLogRecords( logRecord -> logRecord @@ -166,12 +198,7 @@ void testMdc() { .hasInstrumentationScope(InstrumentationScopeInfo.builder("abc").build()) .hasSeverity(Severity.INFO) .hasSeverityText("INFO") - .hasAttributesSatisfyingExactly( - equalTo(AttributeKey.stringKey("key1"), "val1"), - equalTo(AttributeKey.stringKey("key2"), "val2"), - equalTo( - ThreadIncubatingAttributes.THREAD_NAME, Thread.currentThread().getName()), - equalTo(ThreadIncubatingAttributes.THREAD_ID, Thread.currentThread().getId()))); + .hasAttributesSatisfyingExactly(assertions)); } private static void performLogging( diff --git a/instrumentation/log4j/log4j-appender-2.17/javaagent/build.gradle.kts b/instrumentation/log4j/log4j-appender-2.17/javaagent/build.gradle.kts index 23a8803c18b7..09ab724ab340 100644 --- a/instrumentation/log4j/log4j-appender-2.17/javaagent/build.gradle.kts +++ b/instrumentation/log4j/log4j-appender-2.17/javaagent/build.gradle.kts @@ -47,6 +47,8 @@ tasks.withType().configureEach { tasks { val testAsync by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath jvmArgs("-DLog4j2.contextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector") } diff --git a/instrumentation/log4j/log4j-appender-2.17/library/README.md b/instrumentation/log4j/log4j-appender-2.17/library/README.md index 9d4a7be38e1f..07bd37bba715 100644 --- a/instrumentation/log4j/log4j-appender-2.17/library/README.md +++ b/instrumentation/log4j/log4j-appender-2.17/library/README.md @@ -9,7 +9,7 @@ which forwards Log4j2 log events to the ### Add these dependencies to your project Replace `OPENTELEMETRY_VERSION` with the [latest -release](https://search.maven.org/search?q=g:io.opentelemetry.instrumentation%20AND%20a:opentelemetry-log4j-appender-2.17). +release]( https://central.sonatype.com/artifact/io.opentelemetry.instrumentation/opentelemetry-log4j-appender-2.17). For Maven, add to your `pom.xml` dependencies: diff --git a/instrumentation/log4j/log4j-appender-2.17/library/build.gradle.kts b/instrumentation/log4j/log4j-appender-2.17/library/build.gradle.kts index e4aac90150d9..b462fb694f2d 100644 --- a/instrumentation/log4j/log4j-appender-2.17/library/build.gradle.kts +++ b/instrumentation/log4j/log4j-appender-2.17/library/build.gradle.kts @@ -24,20 +24,24 @@ tasks { } val testAsyncLogger by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath jvmArgs("-DLog4j2.contextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector") } val testStableSemconv by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath jvmArgs("-Dotel.semconv-stability.opt-in=code") } val testBothSemconv by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath jvmArgs("-Dotel.semconv-stability.opt-in=code/dup") } check { - dependsOn(testAsyncLogger) - dependsOn(testStableSemconv) - dependsOn(testBothSemconv) + dependsOn(testAsyncLogger, testStableSemconv, testBothSemconv) } } diff --git a/instrumentation/log4j/log4j-appender-2.17/library/src/main/java/io/opentelemetry/instrumentation/log4j/appender/v2_17/internal/LogEventMapper.java b/instrumentation/log4j/log4j-appender-2.17/library/src/main/java/io/opentelemetry/instrumentation/log4j/appender/v2_17/internal/LogEventMapper.java index a167f5ebbb38..36ec63fcd186 100644 --- a/instrumentation/log4j/log4j-appender-2.17/library/src/main/java/io/opentelemetry/instrumentation/log4j/appender/v2_17/internal/LogEventMapper.java +++ b/instrumentation/log4j/log4j-appender-2.17/library/src/main/java/io/opentelemetry/instrumentation/log4j/appender/v2_17/internal/LogEventMapper.java @@ -8,6 +8,7 @@ import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.common.AttributesBuilder; +import io.opentelemetry.api.incubator.logs.ExtendedLogRecordBuilder; import io.opentelemetry.api.logs.LogRecordBuilder; import io.opentelemetry.api.logs.Severity; import io.opentelemetry.context.Context; @@ -117,7 +118,7 @@ public void mapLogEvent( } if (throwable != null) { - setThrowable(attributes, throwable); + setThrowable(builder, attributes, throwable); } captureContextDataAttributes(attributes, contextData); @@ -233,14 +234,17 @@ public static AttributeKey getMapMessageAttributeKey(String key) { key, k -> AttributeKey.stringKey("log4j.map_message." + k)); } - private static void setThrowable(AttributesBuilder attributes, Throwable throwable) { - // TODO (trask) extract method for recording exception into - // io.opentelemetry:opentelemetry-api - attributes.put(ExceptionAttributes.EXCEPTION_TYPE, throwable.getClass().getName()); - attributes.put(ExceptionAttributes.EXCEPTION_MESSAGE, throwable.getMessage()); - StringWriter writer = new StringWriter(); - throwable.printStackTrace(new PrintWriter(writer)); - attributes.put(ExceptionAttributes.EXCEPTION_STACKTRACE, writer.toString()); + private static void setThrowable( + LogRecordBuilder builder, AttributesBuilder attributes, Throwable throwable) { + if (builder instanceof ExtendedLogRecordBuilder) { + ((ExtendedLogRecordBuilder) builder).setException(throwable); + } else { + attributes.put(ExceptionAttributes.EXCEPTION_TYPE, throwable.getClass().getName()); + attributes.put(ExceptionAttributes.EXCEPTION_MESSAGE, throwable.getMessage()); + StringWriter writer = new StringWriter(); + throwable.printStackTrace(new PrintWriter(writer)); + attributes.put(ExceptionAttributes.EXCEPTION_STACKTRACE, writer.toString()); + } } private static Severity levelToSeverity(Level level) { diff --git a/instrumentation/log4j/log4j-context-data/log4j-context-data-2.17/library-autoconfigure/README.md b/instrumentation/log4j/log4j-context-data/log4j-context-data-2.17/library-autoconfigure/README.md index 903427f4e920..f81e256cca1f 100644 --- a/instrumentation/log4j/log4j-context-data/log4j-context-data-2.17/library-autoconfigure/README.md +++ b/instrumentation/log4j/log4j-context-data/log4j-context-data-2.17/library-autoconfigure/README.md @@ -8,7 +8,7 @@ into log context. ### Add these dependencies to your project Replace `OPENTELEMETRY_VERSION` with the [latest -release](https://search.maven.org/search?q=g:io.opentelemetry.instrumentation%20AND%20a:opentelemetry-log4j-context-data-2.17-autoconfigure). +release]( https://central.sonatype.com/artifact/io.opentelemetry.instrumentation/opentelemetry-log4j-context-data-2.17-autoconfigure). For Maven, add to your `pom.xml` dependencies: diff --git a/instrumentation/log4j/log4j-context-data/log4j-context-data-2.17/library-autoconfigure/build.gradle.kts b/instrumentation/log4j/log4j-context-data/log4j-context-data-2.17/library-autoconfigure/build.gradle.kts index b0f3d1a23c67..531ed79bcf72 100644 --- a/instrumentation/log4j/log4j-context-data/log4j-context-data-2.17/library-autoconfigure/build.gradle.kts +++ b/instrumentation/log4j/log4j-context-data/log4j-context-data-2.17/library-autoconfigure/build.gradle.kts @@ -20,6 +20,8 @@ tasks { } val testAddBaggage by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath filter { includeTestsMatching("LibraryLog4j2BaggageTest") } @@ -27,6 +29,8 @@ tasks { } val testLoggingKeys by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath filter { includeTestsMatching("LibraryLog4j2LoggingKeysTest") } @@ -36,7 +40,6 @@ tasks { } named("check") { - dependsOn(testAddBaggage) - dependsOn(testLoggingKeys) + dependsOn(testAddBaggage, testLoggingKeys) } } diff --git a/instrumentation/log4j/log4j-context-data/log4j-context-data-2.7/javaagent/build.gradle.kts b/instrumentation/log4j/log4j-context-data/log4j-context-data-2.7/javaagent/build.gradle.kts index 678322b8df6b..01733c59db22 100644 --- a/instrumentation/log4j/log4j-context-data/log4j-context-data-2.7/javaagent/build.gradle.kts +++ b/instrumentation/log4j/log4j-context-data/log4j-context-data-2.7/javaagent/build.gradle.kts @@ -31,6 +31,8 @@ tasks { } val testAddBaggage by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath filter { includeTestsMatching("Log4j27BaggageTest") } @@ -38,6 +40,8 @@ tasks { } val testLoggingKeys by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath filter { includeTestsMatching("Log4j27LoggingKeysTest") } @@ -47,7 +51,6 @@ tasks { } named("check") { - dependsOn(testAddBaggage) - dependsOn(testLoggingKeys) + dependsOn(testAddBaggage, testLoggingKeys) } } diff --git a/instrumentation/logback/logback-appender-1.0/library/README.md b/instrumentation/logback/logback-appender-1.0/library/README.md index 659d32ae5d47..fe98849e6747 100644 --- a/instrumentation/logback/logback-appender-1.0/library/README.md +++ b/instrumentation/logback/logback-appender-1.0/library/README.md @@ -9,7 +9,7 @@ forwards Logback log events to the ### Add these dependencies to your project Replace `OPENTELEMETRY_VERSION` with the [latest -release](https://search.maven.org/search?q=g:io.opentelemetry.instrumentation%20AND%20a:opentelemetry-logback-appender-1.0). +release]( https://central.sonatype.com/artifact/io.opentelemetry.instrumentation/opentelemetry-logback-appender-1.0). For Maven, add to your `pom.xml` dependencies: diff --git a/instrumentation/logback/logback-appender-1.0/library/build.gradle.kts b/instrumentation/logback/logback-appender-1.0/library/build.gradle.kts index fbc8cf09056a..b5fa1af6d883 100644 --- a/instrumentation/logback/logback-appender-1.0/library/build.gradle.kts +++ b/instrumentation/logback/logback-appender-1.0/library/build.gradle.kts @@ -57,6 +57,12 @@ graalvmNative { toolchainDetection.set(false) } +// Disable collectReachabilityMetadata task to avoid configuration isolation issues +// See https://github.com/gradle/gradle/issues/17559 +tasks.named("collectReachabilityMetadata").configure { + enabled = false +} + // To be able to execute the tests as GraalVM native executables configurations.configureEach { exclude("org.apache.groovy", "groovy") @@ -127,17 +133,19 @@ testing { tasks { val testStableSemconv by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath jvmArgs("-Dotel.semconv-stability.opt-in=code") } val testBothSemconv by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath jvmArgs("-Dotel.semconv-stability.opt-in=code/dup") } check { - dependsOn(testing.suites) - dependsOn(testStableSemconv) - dependsOn(testBothSemconv) + dependsOn(testing.suites, testStableSemconv, testBothSemconv) } } 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 20f92ec2cc10..0819c73a93d1 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 @@ -17,6 +17,7 @@ import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.common.AttributesBuilder; +import io.opentelemetry.api.incubator.logs.ExtendedLogRecordBuilder; import io.opentelemetry.api.logs.LogRecordBuilder; import io.opentelemetry.api.logs.LoggerProvider; import io.opentelemetry.api.logs.Severity; @@ -147,7 +148,7 @@ private void mapLoggingEvent( throwable = ((ThrowableProxy) throwableProxy).getThrowable(); } if (throwable != null) { - setThrowable(attributes, throwable); + setThrowable(builder, attributes, throwable); } captureMdcAttributes(attributes, loggingEvent.getMDCPropertyMap()); @@ -269,14 +270,17 @@ public static AttributeKey getMdcAttributeKey(String key) { return mdcAttributeKeys.computeIfAbsent(key, AttributeKey::stringKey); } - private static void setThrowable(AttributesBuilder attributes, Throwable throwable) { - // TODO (trask) extract method for recording exception into - // io.opentelemetry:opentelemetry-api - attributes.put(ExceptionAttributes.EXCEPTION_TYPE, throwable.getClass().getName()); - attributes.put(ExceptionAttributes.EXCEPTION_MESSAGE, throwable.getMessage()); - StringWriter writer = new StringWriter(); - throwable.printStackTrace(new PrintWriter(writer)); - attributes.put(ExceptionAttributes.EXCEPTION_STACKTRACE, writer.toString()); + private static void setThrowable( + LogRecordBuilder builder, AttributesBuilder attributes, Throwable throwable) { + if (builder instanceof ExtendedLogRecordBuilder) { + ((ExtendedLogRecordBuilder) builder).setException(throwable); + } else { + attributes.put(ExceptionAttributes.EXCEPTION_TYPE, throwable.getClass().getName()); + attributes.put(ExceptionAttributes.EXCEPTION_MESSAGE, throwable.getMessage()); + StringWriter writer = new StringWriter(); + throwable.printStackTrace(new PrintWriter(writer)); + attributes.put(ExceptionAttributes.EXCEPTION_STACKTRACE, writer.toString()); + } } private static Severity levelToSeverity(Level level) { diff --git a/instrumentation/logback/logback-mdc-1.0/library/README.md b/instrumentation/logback/logback-mdc-1.0/library/README.md index e2d1de2391d5..ad10a3ee34ac 100644 --- a/instrumentation/logback/logback-mdc-1.0/library/README.md +++ b/instrumentation/logback/logback-mdc-1.0/library/README.md @@ -8,7 +8,7 @@ mounted span using a custom Logback appender. ### Add these dependencies to your project Replace `OPENTELEMETRY_VERSION` with the [latest -release](https://search.maven.org/search?q=g:io.opentelemetry.instrumentation%20AND%20a:opentelemetry-logback-mdc-1.0). +release]( https://central.sonatype.com/artifact/io.opentelemetry.instrumentation/opentelemetry-logback-mdc-1.0). For Maven, add to your `pom.xml` dependencies: diff --git a/instrumentation/methods/javaagent/src/declarativeConfigTest/resources/declarative-config.yaml b/instrumentation/methods/javaagent/src/declarativeConfigTest/resources/declarative-config.yaml index ed85a2ba344b..d72535ac3256 100644 --- a/instrumentation/methods/javaagent/src/declarativeConfigTest/resources/declarative-config.yaml +++ b/instrumentation/methods/javaagent/src/declarativeConfigTest/resources/declarative-config.yaml @@ -1,4 +1,4 @@ -file_format: "0.4" +file_format: "1.0-rc.1" instrumentation/development: java: methods: diff --git a/instrumentation/micrometer/micrometer-1.5/javaagent/build.gradle.kts b/instrumentation/micrometer/micrometer-1.5/javaagent/build.gradle.kts index 66ab5db32fdc..1ce775c6700d 100644 --- a/instrumentation/micrometer/micrometer-1.5/javaagent/build.gradle.kts +++ b/instrumentation/micrometer/micrometer-1.5/javaagent/build.gradle.kts @@ -21,6 +21,8 @@ dependencies { tasks { val testPrometheusMode by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath filter { includeTestsMatching("*PrometheusModeTest") } @@ -29,6 +31,8 @@ tasks { } val testBaseTimeUnit by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath filter { includeTestsMatching("*TimerMillisecondsTest") } @@ -37,6 +41,8 @@ tasks { } val testHistogramGauges by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath filter { includeTestsMatching("*HistogramGaugesTest") } @@ -53,9 +59,7 @@ tasks { } check { - dependsOn(testBaseTimeUnit) - dependsOn(testPrometheusMode) - dependsOn(testHistogramGauges) + dependsOn(testBaseTimeUnit, testPrometheusMode, testHistogramGauges) } withType().configureEach { diff --git a/instrumentation/micrometer/micrometer-1.5/library/README.md b/instrumentation/micrometer/micrometer-1.5/library/README.md index e12bee1aebcb..0c27de3155e7 100644 --- a/instrumentation/micrometer/micrometer-1.5/library/README.md +++ b/instrumentation/micrometer/micrometer-1.5/library/README.md @@ -9,7 +9,7 @@ sends Micrometer metrics to the ### Add these dependencies to your project Replace `OPENTELEMETRY_VERSION` with the [latest -release](https://search.maven.org/search?q=g:io.opentelemetry.instrumentation%20AND%20a:opentelemetry-micrometer-1.5). +release]( https://central.sonatype.com/artifact/io.opentelemetry.instrumentation/opentelemetry-micrometer-1.5). For Maven, add to your `pom.xml` dependencies: diff --git a/instrumentation/mongo/mongo-3.1/javaagent/build.gradle.kts b/instrumentation/mongo/mongo-3.1/javaagent/build.gradle.kts index 6d5716d02711..6aae40ef6776 100644 --- a/instrumentation/mongo/mongo-3.1/javaagent/build.gradle.kts +++ b/instrumentation/mongo/mongo-3.1/javaagent/build.gradle.kts @@ -29,6 +29,8 @@ tasks { } val testStableSemconv by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath jvmArgs("-Dotel.semconv-stability.opt-in=database") } diff --git a/instrumentation/mongo/mongo-3.1/library/build.gradle.kts b/instrumentation/mongo/mongo-3.1/library/build.gradle.kts index c0376f9c0f11..dae94556bc6c 100644 --- a/instrumentation/mongo/mongo-3.1/library/build.gradle.kts +++ b/instrumentation/mongo/mongo-3.1/library/build.gradle.kts @@ -18,6 +18,8 @@ tasks { } val testStableSemconv by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath jvmArgs("-Dotel.semconv-stability.opt-in=database") } diff --git a/instrumentation/mongo/mongo-3.7/javaagent/build.gradle.kts b/instrumentation/mongo/mongo-3.7/javaagent/build.gradle.kts index f6b3d3495eed..705fb69ff07a 100644 --- a/instrumentation/mongo/mongo-3.7/javaagent/build.gradle.kts +++ b/instrumentation/mongo/mongo-3.7/javaagent/build.gradle.kts @@ -39,6 +39,8 @@ tasks { } val testStableSemconv by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath jvmArgs("-Dotel.semconv-stability.opt-in=database") } diff --git a/instrumentation/mongo/mongo-4.0/javaagent/build.gradle.kts b/instrumentation/mongo/mongo-4.0/javaagent/build.gradle.kts index 969ed989247d..db3481d9a7fa 100644 --- a/instrumentation/mongo/mongo-4.0/javaagent/build.gradle.kts +++ b/instrumentation/mongo/mongo-4.0/javaagent/build.gradle.kts @@ -35,6 +35,8 @@ tasks { } val testStableSemconv by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath jvmArgs("-Dotel.semconv-stability.opt-in=database") } diff --git a/instrumentation/mongo/mongo-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/mongo/v4_0/DefaultConnectionPoolTaskInstrumentation.java b/instrumentation/mongo/mongo-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/mongo/v4_0/DefaultConnectionPoolTaskInstrumentation.java index 2d142c66a7db..b5df18ed04ae 100644 --- a/instrumentation/mongo/mongo-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/mongo/v4_0/DefaultConnectionPoolTaskInstrumentation.java +++ b/instrumentation/mongo/mongo-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/mongo/v4_0/DefaultConnectionPoolTaskInstrumentation.java @@ -34,6 +34,10 @@ public void transform(TypeTransformer transformer) { transformer.applyAdviceToMethod( isConstructor().and(takesArgument(3, Consumer.class)), this.getClass().getName() + "$TaskArg3Advice"); + // since 5.6.0 + transformer.applyAdviceToMethod( + isConstructor().and(takesArgument(4, Consumer.class)), + this.getClass().getName() + "$TaskArg4Advice"); } @SuppressWarnings("unused") @@ -55,4 +59,14 @@ public static void wrapCallback( action = new TaskWrapper(Java8BytecodeBridge.currentContext(), action); } } + + @SuppressWarnings("unused") + public static class TaskArg4Advice { + + @Advice.OnMethodEnter(suppress = Throwable.class) + public static void wrapCallback( + @Advice.Argument(value = 4, readOnly = false) Consumer action) { + action = new TaskWrapper(Java8BytecodeBridge.currentContext(), action); + } + } } diff --git a/instrumentation/mongo/mongo-async-3.3/javaagent/build.gradle.kts b/instrumentation/mongo/mongo-async-3.3/javaagent/build.gradle.kts index d33c2ae01465..682a47f552e4 100644 --- a/instrumentation/mongo/mongo-async-3.3/javaagent/build.gradle.kts +++ b/instrumentation/mongo/mongo-async-3.3/javaagent/build.gradle.kts @@ -30,6 +30,8 @@ tasks { } val testStableSemconv by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath jvmArgs("-Dotel.semconv-stability.opt-in=database") } diff --git a/instrumentation/netty/netty-4.0/javaagent/build.gradle.kts b/instrumentation/netty/netty-4.0/javaagent/build.gradle.kts index ac89ed679b18..48f1fa137145 100644 --- a/instrumentation/netty/netty-4.0/javaagent/build.gradle.kts +++ b/instrumentation/netty/netty-4.0/javaagent/build.gradle.kts @@ -37,12 +37,13 @@ dependencies { tasks { val testConnectionSpan by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath filter { includeTestsMatching("Netty40ConnectionSpanTest") includeTestsMatching("Netty40ClientSslTest") } include("**/Netty40ConnectionSpanTest.*", "**/Netty40ClientSslTest.*") - jvmArgs("-Dotel.instrumentation.netty.connection-telemetry.enabled=true") jvmArgs("-Dotel.instrumentation.netty.ssl-telemetry.enabled=true") } diff --git a/instrumentation/netty/netty-4.1/javaagent/build.gradle.kts b/instrumentation/netty/netty-4.1/javaagent/build.gradle.kts index 048d21736923..f5a169a7cf44 100644 --- a/instrumentation/netty/netty-4.1/javaagent/build.gradle.kts +++ b/instrumentation/netty/netty-4.1/javaagent/build.gradle.kts @@ -44,12 +44,13 @@ dependencies { tasks { val testConnectionSpan by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath filter { includeTestsMatching("Netty41ConnectionSpanTest") includeTestsMatching("Netty41ClientSslTest") } include("**/Netty41ConnectionSpanTest.*", "**/Netty41ClientSslTest.*") - jvmArgs("-Dotel.instrumentation.netty.connection-telemetry.enabled=true") jvmArgs("-Dotel.instrumentation.netty.ssl-telemetry.enabled=true") } diff --git a/instrumentation/okhttp/okhttp-3.0/library/README.md b/instrumentation/okhttp/okhttp-3.0/library/README.md index 84b21642309e..e4fc17efe4c7 100644 --- a/instrumentation/okhttp/okhttp-3.0/library/README.md +++ b/instrumentation/okhttp/okhttp-3.0/library/README.md @@ -7,7 +7,7 @@ Provides OpenTelemetry instrumentation for [okhttp3](https://square.github.io/ok ### Add these dependencies to your project Replace `OPENTELEMETRY_VERSION` with the [latest -release](https://search.maven.org/search?q=g:io.opentelemetry.instrumentation%20AND%20a:opentelemetry-okhttp-3.0). +release]( https://central.sonatype.com/artifact/io.opentelemetry.instrumentation/opentelemetry-okhttp-3.0). For Maven, add to your `pom.xml` dependencies: diff --git a/instrumentation/openai/openai-java-1.1/library/README.md b/instrumentation/openai/openai-java-1.1/library/README.md index 4451ec510262..d8b1e745d93e 100644 --- a/instrumentation/openai/openai-java-1.1/library/README.md +++ b/instrumentation/openai/openai-java-1.1/library/README.md @@ -7,7 +7,7 @@ Provides OpenTelemetry instrumentation for [openai-java](https://github.com/open ### Add these dependencies to your project Replace `OPENTELEMETRY_VERSION` with the [latest -release](https://search.maven.org/search?q=g:io.opentelemetry.instrumentation%20AND%20a:opentelemetry-openai-java-1.1). +release]( https://central.sonatype.com/artifact/io.opentelemetry.instrumentation/opentelemetry-openai-java-1.1). For Maven, add to your `pom.xml` dependencies: diff --git a/instrumentation/openai/openai-java-1.1/library/src/main/java/io/opentelemetry/instrumentation/openai/v1_1/ChatAttributesGetter.java b/instrumentation/openai/openai-java-1.1/library/src/main/java/io/opentelemetry/instrumentation/openai/v1_1/ChatAttributesGetter.java index a78b2985d946..84752ad0c2fd 100644 --- a/instrumentation/openai/openai-java-1.1/library/src/main/java/io/opentelemetry/instrumentation/openai/v1_1/ChatAttributesGetter.java +++ b/instrumentation/openai/openai-java-1.1/library/src/main/java/io/opentelemetry/instrumentation/openai/v1_1/ChatAttributesGetter.java @@ -27,7 +27,7 @@ public String getOperationName(ChatCompletionCreateParams request) { @Override public String getSystem(ChatCompletionCreateParams request) { - return GenAiAttributes.GenAiSystemIncubatingValues.OPENAI; + return GenAiAttributes.GenAiProviderNameIncubatingValues.OPENAI; } @Override diff --git a/instrumentation/openai/openai-java-1.1/library/src/main/java/io/opentelemetry/instrumentation/openai/v1_1/ChatCompletionEventsHelper.java b/instrumentation/openai/openai-java-1.1/library/src/main/java/io/opentelemetry/instrumentation/openai/v1_1/ChatCompletionEventsHelper.java index 66d337bdfbab..5aa2551c2d20 100644 --- a/instrumentation/openai/openai-java-1.1/library/src/main/java/io/opentelemetry/instrumentation/openai/v1_1/ChatCompletionEventsHelper.java +++ b/instrumentation/openai/openai-java-1.1/library/src/main/java/io/opentelemetry/instrumentation/openai/v1_1/ChatCompletionEventsHelper.java @@ -6,7 +6,7 @@ package io.opentelemetry.instrumentation.openai.v1_1; import static io.opentelemetry.api.common.AttributeKey.stringKey; -import static io.opentelemetry.instrumentation.openai.v1_1.GenAiAttributes.GEN_AI_SYSTEM; +import static io.opentelemetry.instrumentation.openai.v1_1.GenAiAttributes.GEN_AI_PROVIDER_NAME; import com.openai.models.chat.completions.ChatCompletion; import com.openai.models.chat.completions.ChatCompletionAssistantMessageParam; @@ -214,7 +214,7 @@ private static LogRecordBuilder newEvent(Logger eventLogger, String name) { return eventLogger .logRecordBuilder() .setAttribute(EVENT_NAME, name) - .setAttribute(GEN_AI_SYSTEM, "openai"); + .setAttribute(GEN_AI_PROVIDER_NAME, "openai"); } private static Value buildToolCallEventObject( diff --git a/instrumentation/openai/openai-java-1.1/library/src/main/java/io/opentelemetry/instrumentation/openai/v1_1/EmbeddingAttributesGetter.java b/instrumentation/openai/openai-java-1.1/library/src/main/java/io/opentelemetry/instrumentation/openai/v1_1/EmbeddingAttributesGetter.java index 3503ff4da4c1..18ad487a452c 100644 --- a/instrumentation/openai/openai-java-1.1/library/src/main/java/io/opentelemetry/instrumentation/openai/v1_1/EmbeddingAttributesGetter.java +++ b/instrumentation/openai/openai-java-1.1/library/src/main/java/io/opentelemetry/instrumentation/openai/v1_1/EmbeddingAttributesGetter.java @@ -25,7 +25,7 @@ public String getOperationName(EmbeddingCreateParams request) { @Override public String getSystem(EmbeddingCreateParams request) { - return GenAiAttributes.GenAiSystemIncubatingValues.OPENAI; + return GenAiAttributes.GenAiProviderNameIncubatingValues.OPENAI; } @Override diff --git a/instrumentation/openai/openai-java-1.1/library/src/main/java/io/opentelemetry/instrumentation/openai/v1_1/GenAiAttributes.java b/instrumentation/openai/openai-java-1.1/library/src/main/java/io/opentelemetry/instrumentation/openai/v1_1/GenAiAttributes.java index c6209d44acdb..4cccf2182aaa 100644 --- a/instrumentation/openai/openai-java-1.1/library/src/main/java/io/opentelemetry/instrumentation/openai/v1_1/GenAiAttributes.java +++ b/instrumentation/openai/openai-java-1.1/library/src/main/java/io/opentelemetry/instrumentation/openai/v1_1/GenAiAttributes.java @@ -11,7 +11,7 @@ // copied from GenAiIncubatingAttributes final class GenAiAttributes { - static final AttributeKey GEN_AI_SYSTEM = stringKey("gen_ai.system"); + static final AttributeKey GEN_AI_PROVIDER_NAME = stringKey("gen_ai.provider.name"); static final class GenAiOperationNameIncubatingValues { static final String CHAT = "chat"; @@ -20,10 +20,10 @@ static final class GenAiOperationNameIncubatingValues { private GenAiOperationNameIncubatingValues() {} } - static final class GenAiSystemIncubatingValues { + static final class GenAiProviderNameIncubatingValues { static final String OPENAI = "openai"; - private GenAiSystemIncubatingValues() {} + private GenAiProviderNameIncubatingValues() {} } private GenAiAttributes() {} diff --git a/instrumentation/openai/openai-java-1.1/library/src/test/java/io/opentelemetry/instrumentation/openai/v1_1/ChatTest.java b/instrumentation/openai/openai-java-1.1/library/src/test/java/io/opentelemetry/instrumentation/openai/v1_1/ChatTest.java index 2ae72f19ba33..d312be5fa996 100644 --- a/instrumentation/openai/openai-java-1.1/library/src/test/java/io/opentelemetry/instrumentation/openai/v1_1/ChatTest.java +++ b/instrumentation/openai/openai-java-1.1/library/src/test/java/io/opentelemetry/instrumentation/openai/v1_1/ChatTest.java @@ -8,18 +8,18 @@ import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies; import static io.opentelemetry.semconv.incubating.GenAiIncubatingAttributes.GEN_AI_OPERATION_NAME; +import static io.opentelemetry.semconv.incubating.GenAiIncubatingAttributes.GEN_AI_PROVIDER_NAME; import static io.opentelemetry.semconv.incubating.GenAiIncubatingAttributes.GEN_AI_REQUEST_MODEL; import static io.opentelemetry.semconv.incubating.GenAiIncubatingAttributes.GEN_AI_RESPONSE_FINISH_REASONS; import static io.opentelemetry.semconv.incubating.GenAiIncubatingAttributes.GEN_AI_RESPONSE_ID; import static io.opentelemetry.semconv.incubating.GenAiIncubatingAttributes.GEN_AI_RESPONSE_MODEL; -import static io.opentelemetry.semconv.incubating.GenAiIncubatingAttributes.GEN_AI_SYSTEM; import static io.opentelemetry.semconv.incubating.GenAiIncubatingAttributes.GEN_AI_TOKEN_TYPE; import static io.opentelemetry.semconv.incubating.GenAiIncubatingAttributes.GEN_AI_USAGE_INPUT_TOKENS; import static io.opentelemetry.semconv.incubating.GenAiIncubatingAttributes.GEN_AI_USAGE_OUTPUT_TOKENS; import static io.opentelemetry.semconv.incubating.GenAiIncubatingAttributes.GenAiOperationNameIncubatingValues.CHAT; -import static io.opentelemetry.semconv.incubating.GenAiIncubatingAttributes.GenAiSystemIncubatingValues.OPENAI; -import static io.opentelemetry.semconv.incubating.GenAiIncubatingAttributes.GenAiTokenTypeIncubatingValues.COMPLETION; +import static io.opentelemetry.semconv.incubating.GenAiIncubatingAttributes.GenAiProviderNameIncubatingValues.OPENAI; import static io.opentelemetry.semconv.incubating.GenAiIncubatingAttributes.GenAiTokenTypeIncubatingValues.INPUT; +import static io.opentelemetry.semconv.incubating.GenAiIncubatingAttributes.GenAiTokenTypeIncubatingValues.OUTPUT; import static java.util.Collections.emptyMap; import static java.util.Collections.singletonList; import static org.assertj.core.api.Assertions.assertThat; @@ -110,7 +110,7 @@ void basicNoCaptureContent() { trace.hasSpansSatisfyingExactly( span -> span.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, OPENAI), + equalTo(GEN_AI_PROVIDER_NAME, OPENAI), equalTo(GEN_AI_OPERATION_NAME, CHAT), equalTo(GEN_AI_REQUEST_MODEL, TEST_CHAT_MODEL), satisfies(GEN_AI_RESPONSE_ID, id -> id.startsWith("chatcmpl-")), @@ -134,8 +134,8 @@ void basicNoCaptureContent() { point .hasSumGreaterThan(0.0) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, "openai"), - equalTo(GEN_AI_OPERATION_NAME, "chat"), + equalTo(GEN_AI_PROVIDER_NAME, OPENAI), + equalTo(GEN_AI_OPERATION_NAME, CHAT), equalTo(GEN_AI_REQUEST_MODEL, TEST_CHAT_MODEL), equalTo( GEN_AI_RESPONSE_MODEL, TEST_CHAT_RESPONSE_MODEL)))), @@ -149,8 +149,8 @@ void basicNoCaptureContent() { point .hasSum(22.0) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, "openai"), - equalTo(GEN_AI_OPERATION_NAME, "chat"), + equalTo(GEN_AI_PROVIDER_NAME, OPENAI), + equalTo(GEN_AI_OPERATION_NAME, CHAT), equalTo(GEN_AI_REQUEST_MODEL, TEST_CHAT_MODEL), equalTo( GEN_AI_RESPONSE_MODEL, TEST_CHAT_RESPONSE_MODEL), @@ -159,12 +159,12 @@ void basicNoCaptureContent() { point .hasSum(2.0) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, "openai"), - equalTo(GEN_AI_OPERATION_NAME, "chat"), + equalTo(GEN_AI_PROVIDER_NAME, OPENAI), + equalTo(GEN_AI_OPERATION_NAME, CHAT), equalTo(GEN_AI_REQUEST_MODEL, TEST_CHAT_MODEL), equalTo( GEN_AI_RESPONSE_MODEL, TEST_CHAT_RESPONSE_MODEL), - equalTo(GEN_AI_TOKEN_TYPE, COMPLETION))))); + equalTo(GEN_AI_TOKEN_TYPE, OUTPUT))))); SpanContext spanCtx = getTesting().waitForTraces(1).get(0).get(0).getSpanContext(); @@ -172,12 +172,13 @@ void basicNoCaptureContent() { .waitAndAssertLogRecords( log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, OPENAI), equalTo(EVENT_NAME, "gen_ai.user.message")) + equalTo(GEN_AI_PROVIDER_NAME, OPENAI), + equalTo(EVENT_NAME, "gen_ai.user.message")) .hasSpanContext(spanCtx) .hasBody(Value.of(emptyMap())), log -> { log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, OPENAI), equalTo(EVENT_NAME, "gen_ai.choice")) + equalTo(GEN_AI_PROVIDER_NAME, OPENAI), equalTo(EVENT_NAME, "gen_ai.choice")) .hasSpanContext(spanCtx) .hasBody( Value.of( @@ -209,7 +210,7 @@ void multipleChoicesNoCaptureContent() { trace.hasSpansSatisfyingExactly( span -> span.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, OPENAI), + equalTo(GEN_AI_PROVIDER_NAME, OPENAI), equalTo(GEN_AI_OPERATION_NAME, CHAT), equalTo(GEN_AI_REQUEST_MODEL, TEST_CHAT_MODEL), satisfies(GEN_AI_RESPONSE_ID, id -> id.startsWith("chatcmpl-")), @@ -233,8 +234,8 @@ void multipleChoicesNoCaptureContent() { point .hasSumGreaterThan(0.0) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, "openai"), - equalTo(GEN_AI_OPERATION_NAME, "chat"), + equalTo(GEN_AI_PROVIDER_NAME, OPENAI), + equalTo(GEN_AI_OPERATION_NAME, CHAT), equalTo(GEN_AI_REQUEST_MODEL, TEST_CHAT_MODEL), equalTo( GEN_AI_RESPONSE_MODEL, TEST_CHAT_RESPONSE_MODEL)))), @@ -248,8 +249,8 @@ void multipleChoicesNoCaptureContent() { point .hasSum(22.0) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, "openai"), - equalTo(GEN_AI_OPERATION_NAME, "chat"), + equalTo(GEN_AI_PROVIDER_NAME, OPENAI), + equalTo(GEN_AI_OPERATION_NAME, CHAT), equalTo(GEN_AI_REQUEST_MODEL, TEST_CHAT_MODEL), equalTo( GEN_AI_RESPONSE_MODEL, TEST_CHAT_RESPONSE_MODEL), @@ -258,12 +259,12 @@ void multipleChoicesNoCaptureContent() { point .hasSum(7.0) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, "openai"), - equalTo(GEN_AI_OPERATION_NAME, "chat"), + equalTo(GEN_AI_PROVIDER_NAME, OPENAI), + equalTo(GEN_AI_OPERATION_NAME, CHAT), equalTo(GEN_AI_REQUEST_MODEL, TEST_CHAT_MODEL), equalTo( GEN_AI_RESPONSE_MODEL, TEST_CHAT_RESPONSE_MODEL), - equalTo(GEN_AI_TOKEN_TYPE, COMPLETION))))); + equalTo(GEN_AI_TOKEN_TYPE, OUTPUT))))); SpanContext spanCtx = getTesting().waitForTraces(1).get(0).get(0).getSpanContext(); @@ -271,12 +272,13 @@ void multipleChoicesNoCaptureContent() { .waitAndAssertLogRecords( log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, OPENAI), equalTo(EVENT_NAME, "gen_ai.user.message")) + equalTo(GEN_AI_PROVIDER_NAME, OPENAI), + equalTo(EVENT_NAME, "gen_ai.user.message")) .hasSpanContext(spanCtx) .hasBody(Value.of(emptyMap())), log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, OPENAI), equalTo(EVENT_NAME, "gen_ai.choice")) + equalTo(GEN_AI_PROVIDER_NAME, OPENAI), equalTo(EVENT_NAME, "gen_ai.choice")) .hasSpanContext(spanCtx) .hasBody( Value.of( @@ -285,7 +287,7 @@ void multipleChoicesNoCaptureContent() { KeyValue.of("message", Value.of(emptyMap())))), log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, OPENAI), equalTo(EVENT_NAME, "gen_ai.choice")) + equalTo(GEN_AI_PROVIDER_NAME, OPENAI), equalTo(EVENT_NAME, "gen_ai.choice")) .hasSpanContext(spanCtx) .hasBody( Value.of( @@ -337,7 +339,7 @@ void toolCallsNoCaptureContent() { trace.hasSpansSatisfyingExactly( span -> span.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, OPENAI), + equalTo(GEN_AI_PROVIDER_NAME, OPENAI), equalTo(GEN_AI_OPERATION_NAME, CHAT), equalTo(GEN_AI_REQUEST_MODEL, TEST_CHAT_MODEL), satisfies(GEN_AI_RESPONSE_ID, id -> id.startsWith("chatcmpl-")), @@ -361,8 +363,8 @@ void toolCallsNoCaptureContent() { point .hasSumGreaterThan(0.0) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, "openai"), - equalTo(GEN_AI_OPERATION_NAME, "chat"), + equalTo(GEN_AI_PROVIDER_NAME, OPENAI), + equalTo(GEN_AI_OPERATION_NAME, CHAT), equalTo(GEN_AI_REQUEST_MODEL, TEST_CHAT_MODEL), equalTo( GEN_AI_RESPONSE_MODEL, TEST_CHAT_RESPONSE_MODEL)))), @@ -376,8 +378,8 @@ void toolCallsNoCaptureContent() { point .hasSum(67) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, "openai"), - equalTo(GEN_AI_OPERATION_NAME, "chat"), + equalTo(GEN_AI_PROVIDER_NAME, OPENAI), + equalTo(GEN_AI_OPERATION_NAME, CHAT), equalTo(GEN_AI_REQUEST_MODEL, TEST_CHAT_MODEL), equalTo( GEN_AI_RESPONSE_MODEL, TEST_CHAT_RESPONSE_MODEL), @@ -386,12 +388,12 @@ void toolCallsNoCaptureContent() { point .hasSum(46.0) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, "openai"), - equalTo(GEN_AI_OPERATION_NAME, "chat"), + equalTo(GEN_AI_PROVIDER_NAME, OPENAI), + equalTo(GEN_AI_OPERATION_NAME, CHAT), equalTo(GEN_AI_REQUEST_MODEL, TEST_CHAT_MODEL), equalTo( GEN_AI_RESPONSE_MODEL, TEST_CHAT_RESPONSE_MODEL), - equalTo(GEN_AI_TOKEN_TYPE, COMPLETION))))); + equalTo(GEN_AI_TOKEN_TYPE, OUTPUT))))); SpanContext spanCtx = getTesting().waitForTraces(1).get(0).get(0).getSpanContext(); @@ -399,18 +401,19 @@ void toolCallsNoCaptureContent() { .waitAndAssertLogRecords( log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, OPENAI), + equalTo(GEN_AI_PROVIDER_NAME, OPENAI), equalTo(EVENT_NAME, "gen_ai.system.message")) .hasSpanContext(spanCtx) .hasBody(Value.of(emptyMap())), log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, OPENAI), equalTo(EVENT_NAME, "gen_ai.user.message")) + equalTo(GEN_AI_PROVIDER_NAME, OPENAI), + equalTo(EVENT_NAME, "gen_ai.user.message")) .hasSpanContext(spanCtx) .hasBody(Value.of(emptyMap())), log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, OPENAI), equalTo(EVENT_NAME, "gen_ai.choice")) + equalTo(GEN_AI_PROVIDER_NAME, OPENAI), equalTo(EVENT_NAME, "gen_ai.choice")) .hasSpanContext(spanCtx) .hasBody( Value.of( @@ -458,7 +461,7 @@ void toolCallsNoCaptureContent() { trace.hasSpansSatisfyingExactly( span -> span.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, OPENAI), + equalTo(GEN_AI_PROVIDER_NAME, OPENAI), equalTo(GEN_AI_OPERATION_NAME, CHAT), equalTo(GEN_AI_REQUEST_MODEL, TEST_CHAT_MODEL), satisfies(GEN_AI_RESPONSE_ID, id -> id.startsWith("chatcmpl-")), @@ -482,8 +485,8 @@ void toolCallsNoCaptureContent() { point .hasSumGreaterThan(0.0) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, "openai"), - equalTo(GEN_AI_OPERATION_NAME, "chat"), + equalTo(GEN_AI_PROVIDER_NAME, OPENAI), + equalTo(GEN_AI_OPERATION_NAME, CHAT), equalTo(GEN_AI_REQUEST_MODEL, TEST_CHAT_MODEL), equalTo( GEN_AI_RESPONSE_MODEL, TEST_CHAT_RESPONSE_MODEL)))), @@ -497,8 +500,8 @@ void toolCallsNoCaptureContent() { point .hasSum(99) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, "openai"), - equalTo(GEN_AI_OPERATION_NAME, "chat"), + equalTo(GEN_AI_PROVIDER_NAME, OPENAI), + equalTo(GEN_AI_OPERATION_NAME, CHAT), equalTo(GEN_AI_REQUEST_MODEL, TEST_CHAT_MODEL), equalTo( GEN_AI_RESPONSE_MODEL, TEST_CHAT_RESPONSE_MODEL), @@ -507,12 +510,12 @@ void toolCallsNoCaptureContent() { point .hasSum(25.0) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, "openai"), - equalTo(GEN_AI_OPERATION_NAME, "chat"), + equalTo(GEN_AI_PROVIDER_NAME, OPENAI), + equalTo(GEN_AI_OPERATION_NAME, CHAT), equalTo(GEN_AI_REQUEST_MODEL, TEST_CHAT_MODEL), equalTo( GEN_AI_RESPONSE_MODEL, TEST_CHAT_RESPONSE_MODEL), - equalTo(GEN_AI_TOKEN_TYPE, COMPLETION))))); + equalTo(GEN_AI_TOKEN_TYPE, OUTPUT))))); SpanContext spanCtx1 = getTesting().waitForTraces(1).get(0).get(0).getSpanContext(); @@ -520,18 +523,19 @@ void toolCallsNoCaptureContent() { .waitAndAssertLogRecords( log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, OPENAI), + equalTo(GEN_AI_PROVIDER_NAME, OPENAI), equalTo(EVENT_NAME, "gen_ai.system.message")) .hasSpanContext(spanCtx1) .hasBody(Value.of(emptyMap())), log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, OPENAI), equalTo(EVENT_NAME, "gen_ai.user.message")) + equalTo(GEN_AI_PROVIDER_NAME, OPENAI), + equalTo(EVENT_NAME, "gen_ai.user.message")) .hasSpanContext(spanCtx1) .hasBody(Value.of(emptyMap())), log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, OPENAI), + equalTo(GEN_AI_PROVIDER_NAME, OPENAI), equalTo(EVENT_NAME, "gen_ai.assistant.message")) .hasSpanContext(spanCtx1) .hasBody( @@ -553,17 +557,19 @@ void toolCallsNoCaptureContent() { KeyValue.of("type", Value.of("function"))))))), log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, OPENAI), equalTo(EVENT_NAME, "gen_ai.tool.message")) + equalTo(GEN_AI_PROVIDER_NAME, OPENAI), + equalTo(EVENT_NAME, "gen_ai.tool.message")) .hasSpanContext(spanCtx1) .hasBody(Value.of(KeyValue.of("id", Value.of(newYorkCallId)))), log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, OPENAI), equalTo(EVENT_NAME, "gen_ai.tool.message")) + equalTo(GEN_AI_PROVIDER_NAME, OPENAI), + equalTo(EVENT_NAME, "gen_ai.tool.message")) .hasSpanContext(spanCtx1) .hasBody(Value.of(KeyValue.of("id", Value.of(londonCallId)))), log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, OPENAI), equalTo(EVENT_NAME, "gen_ai.choice")) + equalTo(GEN_AI_PROVIDER_NAME, OPENAI), equalTo(EVENT_NAME, "gen_ai.choice")) .hasSpanContext(spanCtx1) .hasBody( Value.of( @@ -606,7 +612,7 @@ void streamNoCaptureContent() { maybeWithTransportSpan( span -> span.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, OPENAI), + equalTo(GEN_AI_PROVIDER_NAME, OPENAI), equalTo(GEN_AI_OPERATION_NAME, CHAT), equalTo(GEN_AI_REQUEST_MODEL, TEST_CHAT_MODEL), satisfies(GEN_AI_RESPONSE_ID, id -> id.startsWith("chatcmpl-")), @@ -628,8 +634,8 @@ void streamNoCaptureContent() { point .hasSumGreaterThan(0.0) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, "openai"), - equalTo(GEN_AI_OPERATION_NAME, "chat"), + equalTo(GEN_AI_PROVIDER_NAME, OPENAI), + equalTo(GEN_AI_OPERATION_NAME, CHAT), equalTo(GEN_AI_REQUEST_MODEL, TEST_CHAT_MODEL), equalTo( GEN_AI_RESPONSE_MODEL, @@ -641,12 +647,13 @@ void streamNoCaptureContent() { .waitAndAssertLogRecords( log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, OPENAI), equalTo(EVENT_NAME, "gen_ai.user.message")) + equalTo(GEN_AI_PROVIDER_NAME, OPENAI), + equalTo(EVENT_NAME, "gen_ai.user.message")) .hasSpanContext(spanCtx) .hasBody(Value.of(emptyMap())), log -> { log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, OPENAI), equalTo(EVENT_NAME, "gen_ai.choice")) + equalTo(GEN_AI_PROVIDER_NAME, OPENAI), equalTo(EVENT_NAME, "gen_ai.choice")) .hasSpanContext(spanCtx) .hasBody( Value.of( @@ -699,7 +706,7 @@ void streamMultipleChoicesNoCaptureContent() { maybeWithTransportSpan( span -> span.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, OPENAI), + equalTo(GEN_AI_PROVIDER_NAME, OPENAI), equalTo(GEN_AI_OPERATION_NAME, CHAT), equalTo(GEN_AI_REQUEST_MODEL, TEST_CHAT_MODEL), satisfies(GEN_AI_RESPONSE_ID, id -> id.startsWith("chatcmpl-")), @@ -721,8 +728,8 @@ void streamMultipleChoicesNoCaptureContent() { point .hasSumGreaterThan(0.0) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, "openai"), - equalTo(GEN_AI_OPERATION_NAME, "chat"), + equalTo(GEN_AI_PROVIDER_NAME, OPENAI), + equalTo(GEN_AI_OPERATION_NAME, CHAT), equalTo(GEN_AI_REQUEST_MODEL, TEST_CHAT_MODEL), equalTo( GEN_AI_RESPONSE_MODEL, @@ -734,12 +741,13 @@ void streamMultipleChoicesNoCaptureContent() { .waitAndAssertLogRecords( log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, OPENAI), equalTo(EVENT_NAME, "gen_ai.user.message")) + equalTo(GEN_AI_PROVIDER_NAME, OPENAI), + equalTo(EVENT_NAME, "gen_ai.user.message")) .hasSpanContext(spanCtx) .hasBody(Value.of(emptyMap())), log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, OPENAI), equalTo(EVENT_NAME, "gen_ai.choice")) + equalTo(GEN_AI_PROVIDER_NAME, OPENAI), equalTo(EVENT_NAME, "gen_ai.choice")) .hasSpanContext(spanCtx) .hasBody( Value.of( @@ -748,7 +756,7 @@ void streamMultipleChoicesNoCaptureContent() { KeyValue.of("message", Value.of(emptyMap())))), log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, OPENAI), equalTo(EVENT_NAME, "gen_ai.choice")) + equalTo(GEN_AI_PROVIDER_NAME, OPENAI), equalTo(EVENT_NAME, "gen_ai.choice")) .hasSpanContext(spanCtx) .hasBody( Value.of( @@ -798,7 +806,7 @@ void streamToolCallsNoCaptureContent() { maybeWithTransportSpan( span -> span.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, OPENAI), + equalTo(GEN_AI_PROVIDER_NAME, OPENAI), equalTo(GEN_AI_OPERATION_NAME, CHAT), equalTo(GEN_AI_REQUEST_MODEL, TEST_CHAT_MODEL), satisfies(GEN_AI_RESPONSE_ID, id -> id.startsWith("chatcmpl-")), @@ -820,8 +828,8 @@ void streamToolCallsNoCaptureContent() { point .hasSumGreaterThan(0.0) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, "openai"), - equalTo(GEN_AI_OPERATION_NAME, "chat"), + equalTo(GEN_AI_PROVIDER_NAME, OPENAI), + equalTo(GEN_AI_OPERATION_NAME, CHAT), equalTo(GEN_AI_REQUEST_MODEL, TEST_CHAT_MODEL), equalTo( GEN_AI_RESPONSE_MODEL, @@ -833,18 +841,19 @@ void streamToolCallsNoCaptureContent() { .waitAndAssertLogRecords( log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, OPENAI), + equalTo(GEN_AI_PROVIDER_NAME, OPENAI), equalTo(EVENT_NAME, "gen_ai.system.message")) .hasSpanContext(spanCtx) .hasBody(Value.of(emptyMap())), log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, OPENAI), equalTo(EVENT_NAME, "gen_ai.user.message")) + equalTo(GEN_AI_PROVIDER_NAME, OPENAI), + equalTo(EVENT_NAME, "gen_ai.user.message")) .hasSpanContext(spanCtx) .hasBody(Value.of(emptyMap())), log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, OPENAI), equalTo(EVENT_NAME, "gen_ai.choice")) + equalTo(GEN_AI_PROVIDER_NAME, OPENAI), equalTo(EVENT_NAME, "gen_ai.choice")) .hasSpanContext(spanCtx) .hasBody( Value.of( @@ -897,7 +906,7 @@ void streamToolCallsNoCaptureContent() { maybeWithTransportSpan( span -> span.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, OPENAI), + equalTo(GEN_AI_PROVIDER_NAME, OPENAI), equalTo(GEN_AI_OPERATION_NAME, CHAT), equalTo(GEN_AI_REQUEST_MODEL, TEST_CHAT_MODEL), satisfies(GEN_AI_RESPONSE_ID, id -> id.startsWith("chatcmpl-")), @@ -919,8 +928,8 @@ void streamToolCallsNoCaptureContent() { point .hasSumGreaterThan(0.0) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, "openai"), - equalTo(GEN_AI_OPERATION_NAME, "chat"), + equalTo(GEN_AI_PROVIDER_NAME, OPENAI), + equalTo(GEN_AI_OPERATION_NAME, CHAT), equalTo(GEN_AI_REQUEST_MODEL, TEST_CHAT_MODEL), equalTo( GEN_AI_RESPONSE_MODEL, @@ -932,18 +941,19 @@ void streamToolCallsNoCaptureContent() { .waitAndAssertLogRecords( log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, OPENAI), + equalTo(GEN_AI_PROVIDER_NAME, OPENAI), equalTo(EVENT_NAME, "gen_ai.system.message")) .hasSpanContext(spanCtx1) .hasBody(Value.of(emptyMap())), log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, OPENAI), equalTo(EVENT_NAME, "gen_ai.user.message")) + equalTo(GEN_AI_PROVIDER_NAME, OPENAI), + equalTo(EVENT_NAME, "gen_ai.user.message")) .hasSpanContext(spanCtx1) .hasBody(Value.of(emptyMap())), log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, OPENAI), + equalTo(GEN_AI_PROVIDER_NAME, OPENAI), equalTo(EVENT_NAME, "gen_ai.assistant.message")) .hasSpanContext(spanCtx1) .hasBody( @@ -965,17 +975,19 @@ void streamToolCallsNoCaptureContent() { KeyValue.of("type", Value.of("function"))))))), log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, OPENAI), equalTo(EVENT_NAME, "gen_ai.tool.message")) + equalTo(GEN_AI_PROVIDER_NAME, OPENAI), + equalTo(EVENT_NAME, "gen_ai.tool.message")) .hasSpanContext(spanCtx1) .hasBody(Value.of(KeyValue.of("id", Value.of(newYorkCallId)))), log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, OPENAI), equalTo(EVENT_NAME, "gen_ai.tool.message")) + equalTo(GEN_AI_PROVIDER_NAME, OPENAI), + equalTo(EVENT_NAME, "gen_ai.tool.message")) .hasSpanContext(spanCtx1) .hasBody(Value.of(KeyValue.of("id", Value.of(londonCallId)))), log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, OPENAI), equalTo(EVENT_NAME, "gen_ai.choice")) + equalTo(GEN_AI_PROVIDER_NAME, OPENAI), equalTo(EVENT_NAME, "gen_ai.choice")) .hasSpanContext(spanCtx1) .hasBody( Value.of( diff --git a/instrumentation/openai/openai-java-1.1/metadata.yaml b/instrumentation/openai/openai-java-1.1/metadata.yaml index f1f6cc9dcd50..7aaf7d895a2b 100644 --- a/instrumentation/openai/openai-java-1.1/metadata.yaml +++ b/instrumentation/openai/openai-java-1.1/metadata.yaml @@ -1,4 +1,5 @@ description: This instrumentation enables Gen AI client spans and metrics for OpenAI Java SDK 1.1+. +library_link: https://github.com/openai/openai-java configurations: - name: otel.instrumentation.genai.capture-message-content type: boolean diff --git a/instrumentation/openai/openai-java-1.1/testing/src/main/java/io/opentelemetry/instrumentation/openai/v1_1/AbstractChatTest.java b/instrumentation/openai/openai-java-1.1/testing/src/main/java/io/opentelemetry/instrumentation/openai/v1_1/AbstractChatTest.java index 12369e4ccee2..315e6c62d753 100644 --- a/instrumentation/openai/openai-java-1.1/testing/src/main/java/io/opentelemetry/instrumentation/openai/v1_1/AbstractChatTest.java +++ b/instrumentation/openai/openai-java-1.1/testing/src/main/java/io/opentelemetry/instrumentation/openai/v1_1/AbstractChatTest.java @@ -8,6 +8,7 @@ import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies; import static io.opentelemetry.semconv.incubating.GenAiIncubatingAttributes.GEN_AI_OPERATION_NAME; +import static io.opentelemetry.semconv.incubating.GenAiIncubatingAttributes.GEN_AI_PROVIDER_NAME; import static io.opentelemetry.semconv.incubating.GenAiIncubatingAttributes.GEN_AI_REQUEST_FREQUENCY_PENALTY; import static io.opentelemetry.semconv.incubating.GenAiIncubatingAttributes.GEN_AI_REQUEST_MAX_TOKENS; import static io.opentelemetry.semconv.incubating.GenAiIncubatingAttributes.GEN_AI_REQUEST_MODEL; @@ -19,14 +20,13 @@ import static io.opentelemetry.semconv.incubating.GenAiIncubatingAttributes.GEN_AI_RESPONSE_FINISH_REASONS; import static io.opentelemetry.semconv.incubating.GenAiIncubatingAttributes.GEN_AI_RESPONSE_ID; import static io.opentelemetry.semconv.incubating.GenAiIncubatingAttributes.GEN_AI_RESPONSE_MODEL; -import static io.opentelemetry.semconv.incubating.GenAiIncubatingAttributes.GEN_AI_SYSTEM; import static io.opentelemetry.semconv.incubating.GenAiIncubatingAttributes.GEN_AI_TOKEN_TYPE; import static io.opentelemetry.semconv.incubating.GenAiIncubatingAttributes.GEN_AI_USAGE_INPUT_TOKENS; import static io.opentelemetry.semconv.incubating.GenAiIncubatingAttributes.GEN_AI_USAGE_OUTPUT_TOKENS; import static io.opentelemetry.semconv.incubating.GenAiIncubatingAttributes.GenAiOperationNameIncubatingValues.CHAT; -import static io.opentelemetry.semconv.incubating.GenAiIncubatingAttributes.GenAiSystemIncubatingValues.OPENAI; -import static io.opentelemetry.semconv.incubating.GenAiIncubatingAttributes.GenAiTokenTypeIncubatingValues.COMPLETION; +import static io.opentelemetry.semconv.incubating.GenAiIncubatingAttributes.GenAiProviderNameIncubatingValues.OPENAI; import static io.opentelemetry.semconv.incubating.GenAiIncubatingAttributes.GenAiTokenTypeIncubatingValues.INPUT; +import static io.opentelemetry.semconv.incubating.GenAiIncubatingAttributes.GenAiTokenTypeIncubatingValues.OUTPUT; import static java.util.Collections.emptyList; import static java.util.Collections.singletonList; import static org.assertj.core.api.Assertions.assertThat; @@ -175,7 +175,7 @@ void basic() { maybeWithTransportSpan( span -> span.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, OPENAI), + equalTo(GEN_AI_PROVIDER_NAME, OPENAI), equalTo(GEN_AI_OPERATION_NAME, CHAT), equalTo(GEN_AI_REQUEST_MODEL, TEST_CHAT_MODEL), satisfies(GEN_AI_RESPONSE_ID, id -> id.startsWith("chatcmpl-")), @@ -199,7 +199,7 @@ void basic() { point .hasSumGreaterThan(0.0) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, "openai"), + equalTo(GEN_AI_PROVIDER_NAME, "openai"), equalTo(GEN_AI_OPERATION_NAME, "chat"), equalTo(GEN_AI_REQUEST_MODEL, TEST_CHAT_MODEL), equalTo( @@ -214,7 +214,7 @@ void basic() { point .hasSum(22.0) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, "openai"), + equalTo(GEN_AI_PROVIDER_NAME, "openai"), equalTo(GEN_AI_OPERATION_NAME, "chat"), equalTo(GEN_AI_REQUEST_MODEL, TEST_CHAT_MODEL), equalTo( @@ -224,12 +224,12 @@ void basic() { point .hasSum(2.0) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, "openai"), + equalTo(GEN_AI_PROVIDER_NAME, "openai"), equalTo(GEN_AI_OPERATION_NAME, "chat"), equalTo(GEN_AI_REQUEST_MODEL, TEST_CHAT_MODEL), equalTo( GEN_AI_RESPONSE_MODEL, TEST_CHAT_RESPONSE_MODEL), - equalTo(GEN_AI_TOKEN_TYPE, COMPLETION))))); + equalTo(GEN_AI_TOKEN_TYPE, OUTPUT))))); SpanContext spanCtx = getTesting().waitForTraces(1).get(0).get(0).getSpanContext(); @@ -237,12 +237,13 @@ void basic() { .waitAndAssertLogRecords( log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, OPENAI), equalTo(EVENT_NAME, "gen_ai.user.message")) + equalTo(GEN_AI_PROVIDER_NAME, OPENAI), + equalTo(EVENT_NAME, "gen_ai.user.message")) .hasSpanContext(spanCtx) .hasBody(Value.of(KeyValue.of("content", Value.of(TEST_CHAT_INPUT)))), log -> { log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, OPENAI), equalTo(EVENT_NAME, "gen_ai.choice")) + equalTo(GEN_AI_PROVIDER_NAME, OPENAI), equalTo(EVENT_NAME, "gen_ai.choice")) .hasSpanContext(spanCtx) .hasBody( Value.of( @@ -275,7 +276,7 @@ void testDeveloperMessage() { .waitAndAssertLogRecords( log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, OPENAI), + equalTo(GEN_AI_PROVIDER_NAME, OPENAI), equalTo(EVENT_NAME, "gen_ai.system.message")) .hasSpanContext(spanCtx) .hasBody( @@ -287,12 +288,13 @@ void testDeveloperMessage() { "You are an assistant which just answers every query with tomato")))), log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, OPENAI), equalTo(EVENT_NAME, "gen_ai.user.message")) + equalTo(GEN_AI_PROVIDER_NAME, OPENAI), + equalTo(EVENT_NAME, "gen_ai.user.message")) .hasSpanContext(spanCtx) .hasBody(Value.of(KeyValue.of("content", Value.of("Say something")))), log -> { log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, OPENAI), equalTo(EVENT_NAME, "gen_ai.choice")) + equalTo(GEN_AI_PROVIDER_NAME, OPENAI), equalTo(EVENT_NAME, "gen_ai.choice")) .hasSpanContext(spanCtx) .hasBody( Value.of( @@ -330,7 +332,7 @@ void allTheClientOptions() { maybeWithTransportSpan( span -> span.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, OPENAI), + equalTo(GEN_AI_PROVIDER_NAME, OPENAI), equalTo(GEN_AI_OPERATION_NAME, CHAT), equalTo(GEN_AI_REQUEST_MODEL, TEST_CHAT_MODEL), equalTo(GEN_AI_REQUEST_SEED, 100L), @@ -361,7 +363,7 @@ void allTheClientOptions() { point .hasSumGreaterThan(0.0) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, "openai"), + equalTo(GEN_AI_PROVIDER_NAME, "openai"), equalTo(GEN_AI_OPERATION_NAME, "chat"), equalTo(GEN_AI_REQUEST_MODEL, TEST_CHAT_MODEL), equalTo( @@ -376,7 +378,7 @@ void allTheClientOptions() { point .hasSum(22.0) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, "openai"), + equalTo(GEN_AI_PROVIDER_NAME, "openai"), equalTo(GEN_AI_OPERATION_NAME, "chat"), equalTo(GEN_AI_REQUEST_MODEL, TEST_CHAT_MODEL), equalTo( @@ -386,12 +388,12 @@ void allTheClientOptions() { point .hasSum(3.0) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, "openai"), + equalTo(GEN_AI_PROVIDER_NAME, "openai"), equalTo(GEN_AI_OPERATION_NAME, "chat"), equalTo(GEN_AI_REQUEST_MODEL, TEST_CHAT_MODEL), equalTo( GEN_AI_RESPONSE_MODEL, TEST_CHAT_RESPONSE_MODEL), - equalTo(GEN_AI_TOKEN_TYPE, COMPLETION))))); + equalTo(GEN_AI_TOKEN_TYPE, OUTPUT))))); SpanContext spanCtx = getTesting().waitForTraces(1).get(0).get(0).getSpanContext(); @@ -399,12 +401,13 @@ void allTheClientOptions() { .waitAndAssertLogRecords( log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, OPENAI), equalTo(EVENT_NAME, "gen_ai.user.message")) + equalTo(GEN_AI_PROVIDER_NAME, OPENAI), + equalTo(EVENT_NAME, "gen_ai.user.message")) .hasSpanContext(spanCtx) .hasBody(Value.of(KeyValue.of("content", Value.of(TEST_CHAT_INPUT)))), log -> { log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, OPENAI), equalTo(EVENT_NAME, "gen_ai.choice")) + equalTo(GEN_AI_PROVIDER_NAME, OPENAI), equalTo(EVENT_NAME, "gen_ai.choice")) .hasSpanContext(spanCtx) .hasBody( Value.of( @@ -437,7 +440,7 @@ void multipleChoices() { maybeWithTransportSpan( span -> span.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, OPENAI), + equalTo(GEN_AI_PROVIDER_NAME, OPENAI), equalTo(GEN_AI_OPERATION_NAME, CHAT), equalTo(GEN_AI_REQUEST_MODEL, TEST_CHAT_MODEL), satisfies(GEN_AI_RESPONSE_ID, id -> id.startsWith("chatcmpl-")), @@ -461,7 +464,7 @@ void multipleChoices() { point .hasSumGreaterThan(0.0) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, "openai"), + equalTo(GEN_AI_PROVIDER_NAME, "openai"), equalTo(GEN_AI_OPERATION_NAME, "chat"), equalTo(GEN_AI_REQUEST_MODEL, TEST_CHAT_MODEL), equalTo( @@ -476,7 +479,7 @@ void multipleChoices() { point .hasSum(22.0) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, "openai"), + equalTo(GEN_AI_PROVIDER_NAME, "openai"), equalTo(GEN_AI_OPERATION_NAME, "chat"), equalTo(GEN_AI_REQUEST_MODEL, TEST_CHAT_MODEL), equalTo( @@ -486,12 +489,12 @@ void multipleChoices() { point .hasSum(7.0) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, "openai"), + equalTo(GEN_AI_PROVIDER_NAME, "openai"), equalTo(GEN_AI_OPERATION_NAME, "chat"), equalTo(GEN_AI_REQUEST_MODEL, TEST_CHAT_MODEL), equalTo( GEN_AI_RESPONSE_MODEL, TEST_CHAT_RESPONSE_MODEL), - equalTo(GEN_AI_TOKEN_TYPE, COMPLETION))))); + equalTo(GEN_AI_TOKEN_TYPE, OUTPUT))))); SpanContext spanCtx = getTesting().waitForTraces(1).get(0).get(0).getSpanContext(); @@ -499,12 +502,13 @@ void multipleChoices() { .waitAndAssertLogRecords( log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, OPENAI), equalTo(EVENT_NAME, "gen_ai.user.message")) + equalTo(GEN_AI_PROVIDER_NAME, OPENAI), + equalTo(EVENT_NAME, "gen_ai.user.message")) .hasSpanContext(spanCtx) .hasBody(Value.of(KeyValue.of("content", Value.of(TEST_CHAT_INPUT)))), log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, OPENAI), equalTo(EVENT_NAME, "gen_ai.choice")) + equalTo(GEN_AI_PROVIDER_NAME, OPENAI), equalTo(EVENT_NAME, "gen_ai.choice")) .hasSpanContext(spanCtx) .hasBody( Value.of( @@ -514,7 +518,7 @@ void multipleChoices() { "message", Value.of(KeyValue.of("content", Value.of(content1)))))), log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, OPENAI), equalTo(EVENT_NAME, "gen_ai.choice")) + equalTo(GEN_AI_PROVIDER_NAME, OPENAI), equalTo(EVENT_NAME, "gen_ai.choice")) .hasSpanContext(spanCtx) .hasBody( Value.of( @@ -567,7 +571,7 @@ void toolCalls() { maybeWithTransportSpan( span -> span.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, OPENAI), + equalTo(GEN_AI_PROVIDER_NAME, OPENAI), equalTo(GEN_AI_OPERATION_NAME, CHAT), equalTo(GEN_AI_REQUEST_MODEL, TEST_CHAT_MODEL), satisfies(GEN_AI_RESPONSE_ID, id -> id.startsWith("chatcmpl-")), @@ -591,7 +595,7 @@ void toolCalls() { point .hasSumGreaterThan(0.0) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, "openai"), + equalTo(GEN_AI_PROVIDER_NAME, "openai"), equalTo(GEN_AI_OPERATION_NAME, "chat"), equalTo(GEN_AI_REQUEST_MODEL, TEST_CHAT_MODEL), equalTo( @@ -606,7 +610,7 @@ void toolCalls() { point .hasSum(67) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, "openai"), + equalTo(GEN_AI_PROVIDER_NAME, "openai"), equalTo(GEN_AI_OPERATION_NAME, "chat"), equalTo(GEN_AI_REQUEST_MODEL, TEST_CHAT_MODEL), equalTo( @@ -616,12 +620,12 @@ void toolCalls() { point .hasSum(46.0) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, "openai"), + equalTo(GEN_AI_PROVIDER_NAME, "openai"), equalTo(GEN_AI_OPERATION_NAME, "chat"), equalTo(GEN_AI_REQUEST_MODEL, TEST_CHAT_MODEL), equalTo( GEN_AI_RESPONSE_MODEL, TEST_CHAT_RESPONSE_MODEL), - equalTo(GEN_AI_TOKEN_TYPE, COMPLETION))))); + equalTo(GEN_AI_TOKEN_TYPE, OUTPUT))))); SpanContext spanCtx = getTesting().waitForTraces(1).get(0).get(0).getSpanContext(); @@ -629,7 +633,7 @@ void toolCalls() { .waitAndAssertLogRecords( log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, OPENAI), + equalTo(GEN_AI_PROVIDER_NAME, OPENAI), equalTo(EVENT_NAME, "gen_ai.system.message")) .hasSpanContext(spanCtx) .hasBody( @@ -640,7 +644,8 @@ void toolCalls() { "You are a helpful assistant providing weather updates.")))), log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, OPENAI), equalTo(EVENT_NAME, "gen_ai.user.message")) + equalTo(GEN_AI_PROVIDER_NAME, OPENAI), + equalTo(EVENT_NAME, "gen_ai.user.message")) .hasSpanContext(spanCtx) .hasBody( Value.of( @@ -649,7 +654,7 @@ void toolCalls() { Value.of("What is the weather in New York City and London?")))), log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, OPENAI), equalTo(EVENT_NAME, "gen_ai.choice")) + equalTo(GEN_AI_PROVIDER_NAME, OPENAI), equalTo(EVENT_NAME, "gen_ai.choice")) .hasSpanContext(spanCtx) .hasBody( Value.of( @@ -714,7 +719,7 @@ void toolCalls() { maybeWithTransportSpan( span -> span.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, OPENAI), + equalTo(GEN_AI_PROVIDER_NAME, OPENAI), equalTo(GEN_AI_OPERATION_NAME, CHAT), equalTo(GEN_AI_REQUEST_MODEL, TEST_CHAT_MODEL), satisfies(GEN_AI_RESPONSE_ID, id -> id.startsWith("chatcmpl-")), @@ -738,7 +743,7 @@ void toolCalls() { point .hasSumGreaterThan(0.0) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, "openai"), + equalTo(GEN_AI_PROVIDER_NAME, "openai"), equalTo(GEN_AI_OPERATION_NAME, "chat"), equalTo(GEN_AI_REQUEST_MODEL, TEST_CHAT_MODEL), equalTo( @@ -753,7 +758,7 @@ void toolCalls() { point .hasSum(99) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, "openai"), + equalTo(GEN_AI_PROVIDER_NAME, "openai"), equalTo(GEN_AI_OPERATION_NAME, "chat"), equalTo(GEN_AI_REQUEST_MODEL, TEST_CHAT_MODEL), equalTo( @@ -763,12 +768,12 @@ void toolCalls() { point .hasSum(25.0) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, "openai"), + equalTo(GEN_AI_PROVIDER_NAME, "openai"), equalTo(GEN_AI_OPERATION_NAME, "chat"), equalTo(GEN_AI_REQUEST_MODEL, TEST_CHAT_MODEL), equalTo( GEN_AI_RESPONSE_MODEL, TEST_CHAT_RESPONSE_MODEL), - equalTo(GEN_AI_TOKEN_TYPE, COMPLETION))))); + equalTo(GEN_AI_TOKEN_TYPE, OUTPUT))))); SpanContext spanCtx1 = getTesting().waitForTraces(1).get(0).get(0).getSpanContext(); @@ -776,7 +781,7 @@ void toolCalls() { .waitAndAssertLogRecords( log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, OPENAI), + equalTo(GEN_AI_PROVIDER_NAME, OPENAI), equalTo(EVENT_NAME, "gen_ai.system.message")) .hasSpanContext(spanCtx1) .hasBody( @@ -787,7 +792,8 @@ void toolCalls() { "You are a helpful assistant providing weather updates.")))), log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, OPENAI), equalTo(EVENT_NAME, "gen_ai.user.message")) + equalTo(GEN_AI_PROVIDER_NAME, OPENAI), + equalTo(EVENT_NAME, "gen_ai.user.message")) .hasSpanContext(spanCtx1) .hasBody( Value.of( @@ -796,7 +802,7 @@ void toolCalls() { Value.of("What is the weather in New York City and London?")))), log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, OPENAI), + equalTo(GEN_AI_PROVIDER_NAME, OPENAI), equalTo(EVENT_NAME, "gen_ai.assistant.message")) .hasSpanContext(spanCtx1) .hasBody( @@ -827,7 +833,8 @@ void toolCalls() { KeyValue.of("type", Value.of("function"))))))), log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, OPENAI), equalTo(EVENT_NAME, "gen_ai.tool.message")) + equalTo(GEN_AI_PROVIDER_NAME, OPENAI), + equalTo(EVENT_NAME, "gen_ai.tool.message")) .hasSpanContext(spanCtx1) .hasBody( Value.of( @@ -835,7 +842,8 @@ void toolCalls() { KeyValue.of("content", Value.of("25 degrees and sunny")))), log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, OPENAI), equalTo(EVENT_NAME, "gen_ai.tool.message")) + equalTo(GEN_AI_PROVIDER_NAME, OPENAI), + equalTo(EVENT_NAME, "gen_ai.tool.message")) .hasSpanContext(spanCtx1) .hasBody( Value.of( @@ -843,7 +851,7 @@ void toolCalls() { KeyValue.of("content", Value.of("15 degrees and raining")))), log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, OPENAI), equalTo(EVENT_NAME, "gen_ai.choice")) + equalTo(GEN_AI_PROVIDER_NAME, OPENAI), equalTo(EVENT_NAME, "gen_ai.choice")) .hasSpanContext(spanCtx1) .hasBody( Value.of( @@ -888,7 +896,7 @@ void connectionError() { span -> span.hasException(thrown) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, OPENAI), + equalTo(GEN_AI_PROVIDER_NAME, OPENAI), equalTo(GEN_AI_OPERATION_NAME, CHAT), equalTo(GEN_AI_REQUEST_MODEL, TEST_CHAT_MODEL))))); @@ -905,7 +913,7 @@ void connectionError() { point .hasSumGreaterThan(0.0) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, "openai"), + equalTo(GEN_AI_PROVIDER_NAME, "openai"), equalTo(GEN_AI_OPERATION_NAME, "chat"), equalTo(GEN_AI_REQUEST_MODEL, TEST_CHAT_MODEL))))); @@ -915,7 +923,8 @@ void connectionError() { .waitAndAssertLogRecords( log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, OPENAI), equalTo(EVENT_NAME, "gen_ai.user.message")) + equalTo(GEN_AI_PROVIDER_NAME, OPENAI), + equalTo(EVENT_NAME, "gen_ai.user.message")) .hasSpanContext(spanCtx) .hasBody(Value.of(KeyValue.of("content", Value.of(TEST_CHAT_INPUT))))); } @@ -953,7 +962,7 @@ void stream() { maybeWithTransportSpan( span -> span.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, OPENAI), + equalTo(GEN_AI_PROVIDER_NAME, OPENAI), equalTo(GEN_AI_OPERATION_NAME, CHAT), equalTo(GEN_AI_REQUEST_MODEL, TEST_CHAT_MODEL), satisfies(GEN_AI_RESPONSE_ID, id -> id.startsWith("chatcmpl-")), @@ -975,7 +984,7 @@ void stream() { point .hasSumGreaterThan(0.0) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, "openai"), + equalTo(GEN_AI_PROVIDER_NAME, "openai"), equalTo(GEN_AI_OPERATION_NAME, "chat"), equalTo(GEN_AI_REQUEST_MODEL, TEST_CHAT_MODEL), equalTo( @@ -988,12 +997,13 @@ void stream() { .waitAndAssertLogRecords( log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, OPENAI), equalTo(EVENT_NAME, "gen_ai.user.message")) + equalTo(GEN_AI_PROVIDER_NAME, OPENAI), + equalTo(EVENT_NAME, "gen_ai.user.message")) .hasSpanContext(spanCtx) .hasBody(Value.of(KeyValue.of("content", Value.of(TEST_CHAT_INPUT)))), log -> { log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, OPENAI), equalTo(EVENT_NAME, "gen_ai.choice")) + equalTo(GEN_AI_PROVIDER_NAME, OPENAI), equalTo(EVENT_NAME, "gen_ai.choice")) .hasSpanContext(spanCtx) .hasBody( Value.of( @@ -1038,7 +1048,7 @@ void streamIncludeUsage() { maybeWithTransportSpan( span -> span.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, OPENAI), + equalTo(GEN_AI_PROVIDER_NAME, OPENAI), equalTo(GEN_AI_OPERATION_NAME, CHAT), equalTo(GEN_AI_REQUEST_MODEL, TEST_CHAT_MODEL), satisfies(GEN_AI_RESPONSE_ID, id -> id.startsWith("chatcmpl-")), @@ -1062,7 +1072,7 @@ void streamIncludeUsage() { point .hasSumGreaterThan(0.0) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, "openai"), + equalTo(GEN_AI_PROVIDER_NAME, "openai"), equalTo(GEN_AI_OPERATION_NAME, "chat"), equalTo(GEN_AI_REQUEST_MODEL, TEST_CHAT_MODEL), equalTo( @@ -1077,7 +1087,7 @@ void streamIncludeUsage() { point .hasSum(22.0) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, "openai"), + equalTo(GEN_AI_PROVIDER_NAME, "openai"), equalTo(GEN_AI_OPERATION_NAME, "chat"), equalTo(GEN_AI_REQUEST_MODEL, TEST_CHAT_MODEL), equalTo( @@ -1087,12 +1097,12 @@ void streamIncludeUsage() { point .hasSum(3.0) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, "openai"), + equalTo(GEN_AI_PROVIDER_NAME, "openai"), equalTo(GEN_AI_OPERATION_NAME, "chat"), equalTo(GEN_AI_REQUEST_MODEL, TEST_CHAT_MODEL), equalTo( GEN_AI_RESPONSE_MODEL, TEST_CHAT_RESPONSE_MODEL), - equalTo(GEN_AI_TOKEN_TYPE, COMPLETION))))); + equalTo(GEN_AI_TOKEN_TYPE, OUTPUT))))); SpanContext spanCtx = getTesting().waitForTraces(1).get(0).get(0).getSpanContext(); @@ -1100,12 +1110,13 @@ void streamIncludeUsage() { .waitAndAssertLogRecords( log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, OPENAI), equalTo(EVENT_NAME, "gen_ai.user.message")) + equalTo(GEN_AI_PROVIDER_NAME, OPENAI), + equalTo(EVENT_NAME, "gen_ai.user.message")) .hasSpanContext(spanCtx) .hasBody(Value.of(KeyValue.of("content", Value.of(TEST_CHAT_INPUT)))), log -> { log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, OPENAI), equalTo(EVENT_NAME, "gen_ai.choice")) + equalTo(GEN_AI_PROVIDER_NAME, OPENAI), equalTo(EVENT_NAME, "gen_ai.choice")) .hasSpanContext(spanCtx) .hasBody( Value.of( @@ -1158,7 +1169,7 @@ void streamMultipleChoices() { maybeWithTransportSpan( span -> span.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, OPENAI), + equalTo(GEN_AI_PROVIDER_NAME, OPENAI), equalTo(GEN_AI_OPERATION_NAME, CHAT), equalTo(GEN_AI_REQUEST_MODEL, TEST_CHAT_MODEL), satisfies(GEN_AI_RESPONSE_ID, id -> id.startsWith("chatcmpl-")), @@ -1180,7 +1191,7 @@ void streamMultipleChoices() { point .hasSumGreaterThan(0.0) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, "openai"), + equalTo(GEN_AI_PROVIDER_NAME, "openai"), equalTo(GEN_AI_OPERATION_NAME, "chat"), equalTo(GEN_AI_REQUEST_MODEL, TEST_CHAT_MODEL), equalTo( @@ -1193,12 +1204,13 @@ void streamMultipleChoices() { .waitAndAssertLogRecords( log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, OPENAI), equalTo(EVENT_NAME, "gen_ai.user.message")) + equalTo(GEN_AI_PROVIDER_NAME, OPENAI), + equalTo(EVENT_NAME, "gen_ai.user.message")) .hasSpanContext(spanCtx) .hasBody(Value.of(KeyValue.of("content", Value.of(TEST_CHAT_INPUT)))), log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, OPENAI), equalTo(EVENT_NAME, "gen_ai.choice")) + equalTo(GEN_AI_PROVIDER_NAME, OPENAI), equalTo(EVENT_NAME, "gen_ai.choice")) .hasSpanContext(spanCtx) .hasBody( Value.of( @@ -1208,7 +1220,7 @@ void streamMultipleChoices() { "message", Value.of(KeyValue.of("content", Value.of(content1)))))), log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, OPENAI), equalTo(EVENT_NAME, "gen_ai.choice")) + equalTo(GEN_AI_PROVIDER_NAME, OPENAI), equalTo(EVENT_NAME, "gen_ai.choice")) .hasSpanContext(spanCtx) .hasBody( Value.of( @@ -1309,7 +1321,7 @@ void streamToolCalls() { maybeWithTransportSpan( span -> span.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, OPENAI), + equalTo(GEN_AI_PROVIDER_NAME, OPENAI), equalTo(GEN_AI_OPERATION_NAME, CHAT), equalTo(GEN_AI_REQUEST_MODEL, TEST_CHAT_MODEL), satisfies(GEN_AI_RESPONSE_ID, id -> id.startsWith("chatcmpl-")), @@ -1331,7 +1343,7 @@ void streamToolCalls() { point .hasSumGreaterThan(0.0) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, "openai"), + equalTo(GEN_AI_PROVIDER_NAME, "openai"), equalTo(GEN_AI_OPERATION_NAME, "chat"), equalTo(GEN_AI_REQUEST_MODEL, TEST_CHAT_MODEL), equalTo( @@ -1344,7 +1356,7 @@ void streamToolCalls() { .waitAndAssertLogRecords( log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, OPENAI), + equalTo(GEN_AI_PROVIDER_NAME, OPENAI), equalTo(EVENT_NAME, "gen_ai.system.message")) .hasSpanContext(spanCtx) .hasBody( @@ -1355,7 +1367,8 @@ void streamToolCalls() { "You are a helpful assistant providing weather updates.")))), log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, OPENAI), equalTo(EVENT_NAME, "gen_ai.user.message")) + equalTo(GEN_AI_PROVIDER_NAME, OPENAI), + equalTo(EVENT_NAME, "gen_ai.user.message")) .hasSpanContext(spanCtx) .hasBody( Value.of( @@ -1364,7 +1377,7 @@ void streamToolCalls() { Value.of("What is the weather in New York City and London?")))), log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, OPENAI), equalTo(EVENT_NAME, "gen_ai.choice")) + equalTo(GEN_AI_PROVIDER_NAME, OPENAI), equalTo(EVENT_NAME, "gen_ai.choice")) .hasSpanContext(spanCtx) .hasBody( Value.of( @@ -1438,7 +1451,7 @@ void streamToolCalls() { maybeWithTransportSpan( span -> span.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, OPENAI), + equalTo(GEN_AI_PROVIDER_NAME, OPENAI), equalTo(GEN_AI_OPERATION_NAME, CHAT), equalTo(GEN_AI_REQUEST_MODEL, TEST_CHAT_MODEL), satisfies(GEN_AI_RESPONSE_ID, id -> id.startsWith("chatcmpl-")), @@ -1460,7 +1473,7 @@ void streamToolCalls() { point .hasSumGreaterThan(0.0) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, "openai"), + equalTo(GEN_AI_PROVIDER_NAME, "openai"), equalTo(GEN_AI_OPERATION_NAME, "chat"), equalTo(GEN_AI_REQUEST_MODEL, TEST_CHAT_MODEL), equalTo( @@ -1473,7 +1486,7 @@ void streamToolCalls() { .waitAndAssertLogRecords( log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, OPENAI), + equalTo(GEN_AI_PROVIDER_NAME, OPENAI), equalTo(EVENT_NAME, "gen_ai.system.message")) .hasSpanContext(spanCtx1) .hasBody( @@ -1484,7 +1497,8 @@ void streamToolCalls() { "You are a helpful assistant providing weather updates.")))), log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, OPENAI), equalTo(EVENT_NAME, "gen_ai.user.message")) + equalTo(GEN_AI_PROVIDER_NAME, OPENAI), + equalTo(EVENT_NAME, "gen_ai.user.message")) .hasSpanContext(spanCtx1) .hasBody( Value.of( @@ -1493,7 +1507,7 @@ void streamToolCalls() { Value.of("What is the weather in New York City and London?")))), log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, OPENAI), + equalTo(GEN_AI_PROVIDER_NAME, OPENAI), equalTo(EVENT_NAME, "gen_ai.assistant.message")) .hasSpanContext(spanCtx1) .hasBody( @@ -1524,7 +1538,8 @@ void streamToolCalls() { KeyValue.of("type", Value.of("function"))))))), log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, OPENAI), equalTo(EVENT_NAME, "gen_ai.tool.message")) + equalTo(GEN_AI_PROVIDER_NAME, OPENAI), + equalTo(EVENT_NAME, "gen_ai.tool.message")) .hasSpanContext(spanCtx1) .hasBody( Value.of( @@ -1532,7 +1547,8 @@ void streamToolCalls() { KeyValue.of("content", Value.of("25 degrees and sunny")))), log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, OPENAI), equalTo(EVENT_NAME, "gen_ai.tool.message")) + equalTo(GEN_AI_PROVIDER_NAME, OPENAI), + equalTo(EVENT_NAME, "gen_ai.tool.message")) .hasSpanContext(spanCtx1) .hasBody( Value.of( @@ -1540,7 +1556,7 @@ void streamToolCalls() { KeyValue.of("content", Value.of("15 degrees and raining")))), log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, OPENAI), equalTo(EVENT_NAME, "gen_ai.choice")) + equalTo(GEN_AI_PROVIDER_NAME, OPENAI), equalTo(EVENT_NAME, "gen_ai.choice")) .hasSpanContext(spanCtx1) .hasBody( Value.of( @@ -1585,7 +1601,7 @@ void streamConnectionError() { span -> span.hasException(thrown) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, OPENAI), + equalTo(GEN_AI_PROVIDER_NAME, OPENAI), equalTo(GEN_AI_OPERATION_NAME, CHAT), equalTo(GEN_AI_REQUEST_MODEL, TEST_CHAT_MODEL))))); @@ -1602,7 +1618,7 @@ void streamConnectionError() { point .hasSumGreaterThan(0.0) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, "openai"), + equalTo(GEN_AI_PROVIDER_NAME, "openai"), equalTo(GEN_AI_OPERATION_NAME, "chat"), equalTo(GEN_AI_REQUEST_MODEL, TEST_CHAT_MODEL))))); @@ -1612,7 +1628,8 @@ void streamConnectionError() { .waitAndAssertLogRecords( log -> log.hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, OPENAI), equalTo(EVENT_NAME, "gen_ai.user.message")) + equalTo(GEN_AI_PROVIDER_NAME, OPENAI), + equalTo(EVENT_NAME, "gen_ai.user.message")) .hasSpanContext(spanCtx) .hasBody(Value.of(KeyValue.of("content", Value.of(TEST_CHAT_INPUT))))); } diff --git a/instrumentation/openai/openai-java-1.1/testing/src/main/java/io/opentelemetry/instrumentation/openai/v1_1/AbstractEmbeddingsTest.java b/instrumentation/openai/openai-java-1.1/testing/src/main/java/io/opentelemetry/instrumentation/openai/v1_1/AbstractEmbeddingsTest.java index a58a362501fe..df8e5806011f 100644 --- a/instrumentation/openai/openai-java-1.1/testing/src/main/java/io/opentelemetry/instrumentation/openai/v1_1/AbstractEmbeddingsTest.java +++ b/instrumentation/openai/openai-java-1.1/testing/src/main/java/io/opentelemetry/instrumentation/openai/v1_1/AbstractEmbeddingsTest.java @@ -8,14 +8,14 @@ import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies; import static io.opentelemetry.semconv.incubating.GenAiIncubatingAttributes.GEN_AI_OPERATION_NAME; +import static io.opentelemetry.semconv.incubating.GenAiIncubatingAttributes.GEN_AI_PROVIDER_NAME; import static io.opentelemetry.semconv.incubating.GenAiIncubatingAttributes.GEN_AI_REQUEST_ENCODING_FORMATS; import static io.opentelemetry.semconv.incubating.GenAiIncubatingAttributes.GEN_AI_REQUEST_MODEL; import static io.opentelemetry.semconv.incubating.GenAiIncubatingAttributes.GEN_AI_RESPONSE_MODEL; -import static io.opentelemetry.semconv.incubating.GenAiIncubatingAttributes.GEN_AI_SYSTEM; import static io.opentelemetry.semconv.incubating.GenAiIncubatingAttributes.GEN_AI_TOKEN_TYPE; import static io.opentelemetry.semconv.incubating.GenAiIncubatingAttributes.GEN_AI_USAGE_INPUT_TOKENS; import static io.opentelemetry.semconv.incubating.GenAiIncubatingAttributes.GenAiOperationNameIncubatingValues.EMBEDDINGS; -import static io.opentelemetry.semconv.incubating.GenAiIncubatingAttributes.GenAiSystemIncubatingValues.OPENAI; +import static io.opentelemetry.semconv.incubating.GenAiIncubatingAttributes.GenAiProviderNameIncubatingValues.OPENAI; import static io.opentelemetry.semconv.incubating.GenAiIncubatingAttributes.GenAiTokenTypeIncubatingValues.INPUT; import static java.util.Collections.singletonList; import static org.assertj.core.api.Assertions.assertThat; @@ -92,7 +92,7 @@ void basic() { span.hasName("embeddings text-embedding-3-small") .hasKind(SpanKind.CLIENT) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, OPENAI), + equalTo(GEN_AI_PROVIDER_NAME, OPENAI), equalTo(GEN_AI_OPERATION_NAME, EMBEDDINGS), equalTo(GEN_AI_REQUEST_MODEL, MODEL), equalTo(GEN_AI_RESPONSE_MODEL, MODEL), @@ -121,7 +121,7 @@ void basic() { point .hasSumGreaterThan(0.0) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, OPENAI), + equalTo(GEN_AI_PROVIDER_NAME, OPENAI), equalTo(GEN_AI_OPERATION_NAME, EMBEDDINGS), equalTo(GEN_AI_REQUEST_MODEL, MODEL), equalTo(GEN_AI_RESPONSE_MODEL, MODEL)))), @@ -135,7 +135,7 @@ void basic() { point .hasSum(4.0) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, OPENAI), + equalTo(GEN_AI_PROVIDER_NAME, OPENAI), equalTo(GEN_AI_OPERATION_NAME, EMBEDDINGS), equalTo(GEN_AI_REQUEST_MODEL, MODEL), equalTo(GEN_AI_RESPONSE_MODEL, MODEL), @@ -165,7 +165,7 @@ void allTheClientOptions() { span.hasName("embeddings text-embedding-3-small") .hasKind(SpanKind.CLIENT) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, OPENAI), + equalTo(GEN_AI_PROVIDER_NAME, OPENAI), equalTo(GEN_AI_OPERATION_NAME, EMBEDDINGS), equalTo(GEN_AI_REQUEST_MODEL, MODEL), equalTo( @@ -186,7 +186,7 @@ GEN_AI_REQUEST_ENCODING_FORMATS, singletonList("base64")), point .hasSumGreaterThan(0.0) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, OPENAI), + equalTo(GEN_AI_PROVIDER_NAME, OPENAI), equalTo(GEN_AI_OPERATION_NAME, EMBEDDINGS), equalTo(GEN_AI_REQUEST_MODEL, MODEL), equalTo(GEN_AI_RESPONSE_MODEL, MODEL)))), @@ -200,7 +200,7 @@ GEN_AI_REQUEST_ENCODING_FORMATS, singletonList("base64")), point .hasSum(4.0) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, OPENAI), + equalTo(GEN_AI_PROVIDER_NAME, OPENAI), equalTo(GEN_AI_OPERATION_NAME, EMBEDDINGS), equalTo(GEN_AI_REQUEST_MODEL, MODEL), equalTo(GEN_AI_RESPONSE_MODEL, MODEL), @@ -243,7 +243,7 @@ void connectionError() { .hasKind(SpanKind.CLIENT) .hasException(thrown) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, OPENAI), + equalTo(GEN_AI_PROVIDER_NAME, OPENAI), equalTo(GEN_AI_OPERATION_NAME, EMBEDDINGS), equalTo(GEN_AI_REQUEST_MODEL, MODEL), // Newer versions of the library populate base64 when unset by @@ -270,7 +270,7 @@ void connectionError() { point .hasSumGreaterThan(0.0) .hasAttributesSatisfyingExactly( - equalTo(GEN_AI_SYSTEM, OPENAI), + equalTo(GEN_AI_PROVIDER_NAME, OPENAI), equalTo(GEN_AI_OPERATION_NAME, EMBEDDINGS), equalTo(GEN_AI_REQUEST_MODEL, MODEL))))); } diff --git a/instrumentation/opensearch/opensearch-rest-1.0/javaagent/build.gradle.kts b/instrumentation/opensearch/opensearch-rest-1.0/javaagent/build.gradle.kts index d67ed6f01858..ca8079bdcf89 100644 --- a/instrumentation/opensearch/opensearch-rest-1.0/javaagent/build.gradle.kts +++ b/instrumentation/opensearch/opensearch-rest-1.0/javaagent/build.gradle.kts @@ -45,6 +45,8 @@ tasks { } val testStableSemconv by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath jvmArgs("-Dotel.semconv-stability.opt-in=database") } diff --git a/instrumentation/opensearch/opensearch-rest-3.0/javaagent/build.gradle.kts b/instrumentation/opensearch/opensearch-rest-3.0/javaagent/build.gradle.kts index 951deb88b4e9..887daaf25f6c 100644 --- a/instrumentation/opensearch/opensearch-rest-3.0/javaagent/build.gradle.kts +++ b/instrumentation/opensearch/opensearch-rest-3.0/javaagent/build.gradle.kts @@ -38,6 +38,8 @@ tasks { } val testStableSemconv by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath jvmArgs("-Dotel.semconv-stability.opt-in=database") } diff --git a/instrumentation/opentelemetry-api/opentelemetry-api-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/InstrumentationUtilInstrumentation.java b/instrumentation/opentelemetry-api/opentelemetry-api-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/InstrumentationUtilInstrumentation.java new file mode 100644 index 000000000000..8d76b334e5dd --- /dev/null +++ b/instrumentation/opentelemetry-api/opentelemetry-api-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/InstrumentationUtilInstrumentation.java @@ -0,0 +1,65 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.opentelemetryapi; + +import static net.bytebuddy.matcher.ElementMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.returns; +import static net.bytebuddy.matcher.ElementMatchers.takesArgument; + +import application.io.opentelemetry.context.Context; +import io.opentelemetry.api.internal.InstrumentationUtil; +import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; +import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; +import io.opentelemetry.javaagent.instrumentation.opentelemetryapi.context.AgentContextStorage; +import net.bytebuddy.asm.Advice; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; + +public class InstrumentationUtilInstrumentation implements TypeInstrumentation { + @Override + public ElementMatcher typeMatcher() { + return named("application.io.opentelemetry.api.internal.InstrumentationUtil"); + } + + @Override + public void transform(TypeTransformer transformer) { + transformer.applyAdviceToMethod( + named("shouldSuppressInstrumentation") + .and(takesArgument(0, named("application.io.opentelemetry.context.Context"))) + .and(returns(boolean.class)), + this.getClass().getName() + "$ShouldSuppressAdvice"); + transformer.applyAdviceToMethod( + named("suppressInstrumentation").and(takesArgument(0, Runnable.class)), + this.getClass().getName() + "$SuppressAdvice"); + } + + @SuppressWarnings("unused") + public static class ShouldSuppressAdvice { + + @Advice.OnMethodEnter(suppress = Throwable.class, skipOn = Advice.OnNonDefaultValue.class) + public static boolean methodEnter() { + return true; + } + + @Advice.OnMethodExit(suppress = Throwable.class) + public static void methodExit( + @Advice.Argument(0) Context context, @Advice.Return(readOnly = false) boolean result) { + result = + InstrumentationUtil.shouldSuppressInstrumentation( + AgentContextStorage.getAgentContext(context)); + } + } + + @SuppressWarnings("unused") + public static class SuppressAdvice { + + @Advice.OnMethodEnter(suppress = Throwable.class, skipOn = Advice.OnNonDefaultValue.class) + public static boolean methodEnter(@Advice.Argument(0) Runnable runnable) { + InstrumentationUtil.suppressInstrumentation(runnable); + return true; + } + } +} diff --git a/instrumentation/opentelemetry-api/opentelemetry-api-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/OpenTelemetryApiInstrumentationModule.java b/instrumentation/opentelemetry-api/opentelemetry-api-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/OpenTelemetryApiInstrumentationModule.java index 6ce2d6ddcec4..f0fb67e48f56 100644 --- a/instrumentation/opentelemetry-api/opentelemetry-api-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/OpenTelemetryApiInstrumentationModule.java +++ b/instrumentation/opentelemetry-api/opentelemetry-api-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/OpenTelemetryApiInstrumentationModule.java @@ -27,7 +27,8 @@ public List typeInstrumentations() { new ContextInstrumentation(), new ContextStorageWrappersInstrumentation(), new OpenTelemetryInstrumentation(), - new SpanInstrumentation()); + new SpanInstrumentation(), + new InstrumentationUtilInstrumentation()); } @Override diff --git a/instrumentation/opentelemetry-api/opentelemetry-api-1.0/testing/build.gradle.kts b/instrumentation/opentelemetry-api/opentelemetry-api-1.0/testing/build.gradle.kts new file mode 100644 index 000000000000..a5cdafa34246 --- /dev/null +++ b/instrumentation/opentelemetry-api/opentelemetry-api-1.0/testing/build.gradle.kts @@ -0,0 +1,9 @@ +plugins { + id("otel.javaagent-testing") +} + +dependencies { + compileOnly(project(":opentelemetry-api-shaded-for-instrumenting", configuration = "shadow")) + implementation(project(":instrumentation:opentelemetry-api:opentelemetry-api-1.0:javaagent")) + testInstrumentation(project(":instrumentation:opentelemetry-api:opentelemetry-api-1.0:javaagent")) +} diff --git a/instrumentation/opentelemetry-api/opentelemetry-api-1.0/testing/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/TestInstrumentation.java b/instrumentation/opentelemetry-api/opentelemetry-api-1.0/testing/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/TestInstrumentation.java new file mode 100644 index 000000000000..46d5ec170e2d --- /dev/null +++ b/instrumentation/opentelemetry-api/opentelemetry-api-1.0/testing/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/TestInstrumentation.java @@ -0,0 +1,42 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.opentelemetryapi; + +import static net.bytebuddy.matcher.ElementMatchers.named; + +import application.io.opentelemetry.context.Context; +import io.opentelemetry.api.internal.InstrumentationUtil; +import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; +import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; +import io.opentelemetry.javaagent.instrumentation.opentelemetryapi.context.AgentContextStorage; +import net.bytebuddy.asm.Advice; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; + +public class TestInstrumentation implements TypeInstrumentation { + @Override + public ElementMatcher typeMatcher() { + return named("io.opentelemetry.javaagent.instrumentation.opentelemetryapi.TestClass"); + } + + @Override + public void transform(TypeTransformer transformer) { + transformer.applyAdviceToMethod( + named("shouldSuppressInstrumentation"), this.getClass().getName() + "$TestAdvice"); + } + + @SuppressWarnings("unused") + public static class TestAdvice { + + @Advice.OnMethodExit(suppress = Throwable.class) + public static void onExit( + @Advice.Argument(0) Context context, @Advice.Return(readOnly = false) boolean result) { + result = + InstrumentationUtil.shouldSuppressInstrumentation( + AgentContextStorage.getAgentContext(context)); + } + } +} diff --git a/instrumentation/opentelemetry-api/opentelemetry-api-1.0/testing/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/TestInstrumentationModule.java b/instrumentation/opentelemetry-api/opentelemetry-api-1.0/testing/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/TestInstrumentationModule.java new file mode 100644 index 000000000000..c3a158a059b6 --- /dev/null +++ b/instrumentation/opentelemetry-api/opentelemetry-api-1.0/testing/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/TestInstrumentationModule.java @@ -0,0 +1,32 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.opentelemetryapi; + +import static java.util.Collections.singletonList; + +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 TestInstrumentationModule extends InstrumentationModule + implements ExperimentalInstrumentationModule { + public TestInstrumentationModule() { + super("test"); + } + + @Override + public String getModuleGroup() { + return "opentelemetry-api-bridge"; + } + + @Override + public List typeInstrumentations() { + return singletonList(new TestInstrumentation()); + } +} diff --git a/instrumentation/opentelemetry-api/opentelemetry-api-1.0/testing/src/test/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/InstrumentationUtilTest.java b/instrumentation/opentelemetry-api/opentelemetry-api-1.0/testing/src/test/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/InstrumentationUtilTest.java new file mode 100644 index 000000000000..199979130805 --- /dev/null +++ b/instrumentation/opentelemetry-api/opentelemetry-api-1.0/testing/src/test/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/InstrumentationUtilTest.java @@ -0,0 +1,24 @@ +/* + * 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.internal.InstrumentationUtil; +import io.opentelemetry.context.Context; +import org.junit.jupiter.api.Test; + +class InstrumentationUtilTest { + + @Test + void instrumentationSuppression() { + Context[] contexts = new Context[1]; + InstrumentationUtil.suppressInstrumentation(() -> contexts[0] = Context.current()); + + assertThat(InstrumentationUtil.shouldSuppressInstrumentation(contexts[0])).isTrue(); + assertThat(TestClass.shouldSuppressInstrumentation(contexts[0])).isTrue(); + } +} diff --git a/instrumentation/opentelemetry-api/opentelemetry-api-1.0/testing/src/test/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/TestClass.java b/instrumentation/opentelemetry-api/opentelemetry-api-1.0/testing/src/test/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/TestClass.java new file mode 100644 index 000000000000..29725af0b144 --- /dev/null +++ b/instrumentation/opentelemetry-api/opentelemetry-api-1.0/testing/src/test/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/TestClass.java @@ -0,0 +1,19 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.opentelemetryapi; + +import io.opentelemetry.context.Context; + +class TestClass { + static boolean shouldSuppressInstrumentation(Context context) { + // this method is instrumented to call + // InstrumentationUtil.shouldSuppressInstrumentation(context) to simulate agent code calling + // that method + return false; + } + + private TestClass() {} +} diff --git a/instrumentation/opentelemetry-instrumentation-api/javaagent/build.gradle.kts b/instrumentation/opentelemetry-instrumentation-api/javaagent/build.gradle.kts index 57a75a08004b..7e2f7a7bd899 100644 --- a/instrumentation/opentelemetry-instrumentation-api/javaagent/build.gradle.kts +++ b/instrumentation/opentelemetry-instrumentation-api/javaagent/build.gradle.kts @@ -68,16 +68,18 @@ configurations.configureEach { tasks { val testStableSemconv by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath jvmArgs("-Dotel.semconv-stability.opt-in=code") } val testBothSemconv by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath jvmArgs("-Dotel.semconv-stability.opt-in=code/dup") } check { - dependsOn(testing.suites) - dependsOn(testStableSemconv) - dependsOn(testBothSemconv) + dependsOn(testing.suites, testStableSemconv, testBothSemconv) } } diff --git a/instrumentation/oracle-ucp-11.2/javaagent/build.gradle.kts b/instrumentation/oracle-ucp-11.2/javaagent/build.gradle.kts index 9af1f2963c2b..0d8afc2bafdc 100644 --- a/instrumentation/oracle-ucp-11.2/javaagent/build.gradle.kts +++ b/instrumentation/oracle-ucp-11.2/javaagent/build.gradle.kts @@ -29,8 +29,9 @@ tasks { } val testStableSemconv by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath jvmArgs("-Dotel.semconv-stability.opt-in=database") - systemProperty("collectMetadata", collectMetadata) systemProperty("metadataConfig", "otel.semconv-stability.opt-in=database") } diff --git a/instrumentation/oracle-ucp-11.2/library/README.md b/instrumentation/oracle-ucp-11.2/library/README.md index cbc3eefa4335..4f8d5ec15c73 100644 --- a/instrumentation/oracle-ucp-11.2/library/README.md +++ b/instrumentation/oracle-ucp-11.2/library/README.md @@ -7,7 +7,7 @@ Provides OpenTelemetry instrumentation for [Oracle UCP](https://docs.oracle.com/ ### Add these dependencies to your project Replace `OPENTELEMETRY_VERSION` with the [latest -release](https://search.maven.org/search?q=g:io.opentelemetry.instrumentation%20AND%20a:opentelemetry-oracle-ucp-11.2). +release]( https://central.sonatype.com/artifact/io.opentelemetry.instrumentation/opentelemetry-oracle-ucp-11.2). For Maven, add to your `pom.xml` dependencies: diff --git a/instrumentation/oracle-ucp-11.2/library/build.gradle.kts b/instrumentation/oracle-ucp-11.2/library/build.gradle.kts index b9b6020e3e2d..cadfa239b2db 100644 --- a/instrumentation/oracle-ucp-11.2/library/build.gradle.kts +++ b/instrumentation/oracle-ucp-11.2/library/build.gradle.kts @@ -16,6 +16,8 @@ tasks { } val testStableSemconv by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath jvmArgs("-Dotel.semconv-stability.opt-in=database") } diff --git a/instrumentation/oracle-ucp-11.2/metadata.yaml b/instrumentation/oracle-ucp-11.2/metadata.yaml index b44ec44abf52..e7e186bcb828 100644 --- a/instrumentation/oracle-ucp-11.2/metadata.yaml +++ b/instrumentation/oracle-ucp-11.2/metadata.yaml @@ -1 +1,2 @@ description: The Oracle Universal Connection Pool (UCP) instrumentation generates connection pool metrics. +library_link: https://docs.oracle.com/database/121/JJUCP/ diff --git a/instrumentation/oshi/README.md b/instrumentation/oshi/README.md index 165f43d35236..490f23ca650b 100644 --- a/instrumentation/oshi/README.md +++ b/instrumentation/oshi/README.md @@ -6,4 +6,4 @@ # Using OSHI with OpenTelemetry Java agent -Download oshi-core jar from https://search.maven.org/search?q=g:com.github.oshi%20AND%20a:oshi-core and place it on the class path. OpenTelemetry Java agent uses system class loader to load classes from the oshi-core jar that are used for the metrics. +Download oshi-core jar from https://central.sonatype.com/artifact/com.github.oshi/oshi-core and place it on the class path. OpenTelemetry Java agent uses system class loader to load classes from the oshi-core jar that are used for the metrics. diff --git a/instrumentation/oshi/javaagent/build.gradle.kts b/instrumentation/oshi/javaagent/build.gradle.kts index b8bf11ee38bd..6b517888ea5b 100644 --- a/instrumentation/oshi/javaagent/build.gradle.kts +++ b/instrumentation/oshi/javaagent/build.gradle.kts @@ -28,6 +28,8 @@ tasks { } val testExperimental by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath jvmArgs("-Dotel.instrumentation.oshi.experimental-metrics.enabled=true") systemProperty("testExperimental", "true") systemProperty("metadataConfig", "otel.instrumentation.oshi.experimental-metrics.enabled=true") diff --git a/instrumentation/oshi/metadata.yaml b/instrumentation/oshi/metadata.yaml index 7abda9582c21..fc034b5ae85d 100644 --- a/instrumentation/oshi/metadata.yaml +++ b/instrumentation/oshi/metadata.yaml @@ -4,3 +4,4 @@ configurations: description: Enable the OSHI process runtime metrics. type: boolean default: false +library_link: https://github.com/oshi/oshi/ diff --git a/instrumentation/pulsar/pulsar-2.8/javaagent/build.gradle.kts b/instrumentation/pulsar/pulsar-2.8/javaagent/build.gradle.kts index a0f01abb60b3..688006a62213 100644 --- a/instrumentation/pulsar/pulsar-2.8/javaagent/build.gradle.kts +++ b/instrumentation/pulsar/pulsar-2.8/javaagent/build.gradle.kts @@ -21,6 +21,8 @@ dependencies { tasks { val testReceiveSpanDisabled by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath filter { includeTestsMatching("PulsarClientSuppressReceiveSpansTest") } 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 993e53536f8b..4adcbfd4c081 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 @@ -363,7 +363,7 @@ static List sendAttributes( if (testHeaders) { assertions.add( equalTo( - AttributeKey.stringArrayKey("messaging.header.test_message_header"), + AttributeKey.stringArrayKey("messaging.header.Test_Message_Header"), Collections.singletonList("test"))); } int partitionIndex = TopicName.getPartitionIndex(destination); @@ -399,7 +399,7 @@ static List receiveAttributes( if (testHeaders) { assertions.add( equalTo( - AttributeKey.stringArrayKey("messaging.header.test_message_header"), + AttributeKey.stringArrayKey("messaging.header.Test_Message_Header"), Collections.singletonList("test"))); } if (isBatch) { @@ -426,7 +426,7 @@ static List processAttributes( if (testHeaders) { assertions.add( equalTo( - AttributeKey.stringArrayKey("messaging.header.test_message_header"), + AttributeKey.stringArrayKey("messaging.header.Test_Message_Header"), Collections.singletonList("test"))); } int partitionIndex = TopicName.getPartitionIndex(destination); diff --git a/instrumentation/pulsar/pulsar-2.8/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/pulsar/v2_8/PulsarClientSuppressReceiveSpansTest.java b/instrumentation/pulsar/pulsar-2.8/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/pulsar/v2_8/PulsarClientSuppressReceiveSpansTest.java index 13d41145c141..cb2bd2126eaf 100644 --- a/instrumentation/pulsar/pulsar-2.8/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/pulsar/v2_8/PulsarClientSuppressReceiveSpansTest.java +++ b/instrumentation/pulsar/pulsar-2.8/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/pulsar/v2_8/PulsarClientSuppressReceiveSpansTest.java @@ -215,7 +215,7 @@ void captureMessageHeaderAsSpanAttribute() throws Exception { MessageId msgId = testing.runWithSpan( "parent", - () -> producer.newMessage().value(msg).property("test-message-header", "test").send()); + () -> producer.newMessage().value(msg).property("Test-Message-Header", "test").send()); latch.await(1, TimeUnit.MINUTES); 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 a2c4c94b69bc..c704c4f07468 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 @@ -431,7 +431,7 @@ void captureMessageHeaderAsSpanAttribute() throws Exception { MessageId msgId = testing.runWithSpan( "parent", - () -> producer.newMessage().value(msg).property("test-message-header", "test").send()); + () -> producer.newMessage().value(msg).property("Test-Message-Header", "test").send()); latch.await(1, TimeUnit.MINUTES); diff --git a/instrumentation/quarkus-resteasy-reactive/quarkus2-plugin/build.gradle.kts b/instrumentation/quarkus-resteasy-reactive/quarkus2-plugin/build.gradle.kts new file mode 100644 index 000000000000..96daf6507b26 --- /dev/null +++ b/instrumentation/quarkus-resteasy-reactive/quarkus2-plugin/build.gradle.kts @@ -0,0 +1,22 @@ +plugins { + `java-gradle-plugin` +} + +repositories { + mavenCentral() + gradlePluginPortal() +} + +dependencies { + implementation(gradleApi()) + implementation("io.quarkus:quarkus-gradle-model:2.16.7.Final") +} + +gradlePlugin { + plugins { + create("quarkus2Plugin") { + id = "io.opentelemetry.instrumentation.quarkus2" + implementationClass = "io.opentelemetry.instrumentation.quarkus2plugin.Quarkus2Plugin" + } + } +} diff --git a/instrumentation/quarkus-resteasy-reactive/quarkus2-plugin/src/main/java/io/opentelemetry/instrumentation/quarkus2plugin/Quarkus2Plugin.java b/instrumentation/quarkus-resteasy-reactive/quarkus2-plugin/src/main/java/io/opentelemetry/instrumentation/quarkus2plugin/Quarkus2Plugin.java new file mode 100644 index 000000000000..8ca0a40f5631 --- /dev/null +++ b/instrumentation/quarkus-resteasy-reactive/quarkus2-plugin/src/main/java/io/opentelemetry/instrumentation/quarkus2plugin/Quarkus2Plugin.java @@ -0,0 +1,20 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.quarkus2plugin; + +import org.gradle.api.Plugin; +import org.gradle.api.Project; + +@SuppressWarnings("unused") +public class Quarkus2Plugin implements Plugin { + + @Override + public void apply(Project project) { + // we use this plugin with apply false and call its classes directly from the build script + throw new IllegalStateException("this plugin is not meant to be applied"); + } +} + diff --git a/instrumentation/quarkus-resteasy-reactive/quarkus2-plugin/src/main/java/io/quarkus/gradle/dependency/ConditionalDependenciesEnabler.java b/instrumentation/quarkus-resteasy-reactive/quarkus2-plugin/src/main/java/io/quarkus/gradle/dependency/ConditionalDependenciesEnabler.java new file mode 100644 index 000000000000..01690e33986a --- /dev/null +++ b/instrumentation/quarkus-resteasy-reactive/quarkus2-plugin/src/main/java/io/quarkus/gradle/dependency/ConditionalDependenciesEnabler.java @@ -0,0 +1,245 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +// Includes work from: +/* + * Copyright Quarkus Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.quarkus.gradle.dependency; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.gradle.api.Project; +import org.gradle.api.artifacts.Configuration; +import org.gradle.api.artifacts.Dependency; +import org.gradle.api.artifacts.ModuleVersionIdentifier; +import org.gradle.api.artifacts.ResolvedArtifact; + +import io.quarkus.gradle.tooling.dependency.DependencyUtils; +import io.quarkus.gradle.tooling.dependency.ExtensionDependency; +import io.quarkus.maven.dependency.ArtifactCoords; +import io.quarkus.maven.dependency.ArtifactKey; +import io.quarkus.maven.dependency.GACT; +import io.quarkus.runtime.LaunchMode; + +public class ConditionalDependenciesEnabler { + + /** + * Links dependencies to extensions + */ + private final Map> featureVariants = new HashMap<>(); + /** + * Despite its name, only contains extensions which have no conditional dependencies, or have + * resolved their conditional dependencies. + */ + private final Map allExtensions = new HashMap<>(); + private final Project project; + private final Configuration enforcedPlatforms; + private final Set existingArtifacts = new HashSet<>(); + private final List unsatisfiedConditionalDeps = new ArrayList<>(); + + public ConditionalDependenciesEnabler(Project project, LaunchMode mode, + Configuration platforms) { + this.project = project; + this.enforcedPlatforms = platforms; + + // Get runtimeClasspath (quarkusProdBaseRuntimeClasspathConfiguration to be exact) + Configuration baseRuntimeConfig = project.getConfigurations() + .getByName(ApplicationDeploymentClasspathBuilder.getBaseRuntimeConfigName(mode)); + + if (!baseRuntimeConfig.getIncoming().getDependencies().isEmpty()) { + // Gather all extensions from the full resolved dependency tree + collectConditionalDependencies(baseRuntimeConfig.getResolvedConfiguration().getResolvedArtifacts()); + // If there are any extensions which had unresolved conditional dependencies: + while (!unsatisfiedConditionalDeps.isEmpty()) { + boolean satisfiedConditionalDeps = false; + final int originalUnsatisfiedCount = unsatisfiedConditionalDeps.size(); + int i = 0; + // Go through each unsatisfied/unresolved dependency once: + while (i < unsatisfiedConditionalDeps.size()) { + final Dependency conditionalDep = unsatisfiedConditionalDeps.get(i); + // Try to resolve it with the latest evolved graph available + if (resolveConditionalDependency(conditionalDep)) { + // Mark the resolution as a success so we know the graph evolved + satisfiedConditionalDeps = true; + unsatisfiedConditionalDeps.remove(i); + } else { + // No resolution (yet) or graph evolution; move on to the next + ++i; + } + } + // If we didn't resolve any dependencies and the graph did not evolve, give up. + if (!satisfiedConditionalDeps && unsatisfiedConditionalDeps.size() == originalUnsatisfiedCount) { + break; + } + } + reset(); + } + + } + + public Collection getAllExtensions() { + return allExtensions.values(); + } + + private void reset() { + featureVariants.clear(); + existingArtifacts.clear(); + unsatisfiedConditionalDeps.clear(); + } + + private void collectConditionalDependencies(Set runtimeArtifacts) { + // For every artifact in the dependency graph: + for (ResolvedArtifact artifact : runtimeArtifacts) { + // Add to master list of artifacts: + existingArtifacts.add(getKey(artifact)); + ExtensionDependency extension = DependencyUtils.getExtensionInfoOrNull(project, artifact); + // If this artifact represents an extension: + if (extension != null) { + // Add to master list of accepted extensions: + allExtensions.put(extension.getExtensionId(), extension); + for (Dependency conditionalDep : extension.getConditionalDependencies()) { + // If the dependency is not present yet in the graph, queue it for resolution later + if (!exists(conditionalDep)) { + queueConditionalDependency(extension, conditionalDep); + } + } + } + } + } + + private boolean resolveConditionalDependency(Dependency conditionalDep) { + + final Configuration conditionalDeps = createConditionalDependenciesConfiguration(project, conditionalDep); + Set resolvedArtifacts = conditionalDeps.getResolvedConfiguration().getResolvedArtifacts(); + + boolean satisfied = false; + // Resolved artifacts don't have great linking back to the original artifact, so I think + // this loop is trying to find the artifact that represents the original conditional + // dependency + for (ResolvedArtifact artifact : resolvedArtifacts) { + if (conditionalDep.getName().equals(artifact.getName()) + && conditionalDep.getVersion().equals(artifact.getModuleVersion().getId().getVersion()) + && artifact.getModuleVersion().getId().getGroup().equals(conditionalDep.getGroup())) { + // Once the dependency is found, reload the extension info from within + final ExtensionDependency extensionDependency = DependencyUtils.getExtensionInfoOrNull(project, artifact); + // Now check if this conditional dependency is resolved given the latest graph evolution + if (extensionDependency != null && (extensionDependency.getDependencyConditions().isEmpty() + || exist(extensionDependency.getDependencyConditions()))) { + satisfied = true; + enableConditionalDependency(extensionDependency.getExtensionId()); + break; + } + } + } + + // No resolution (yet); give up. + if (!satisfied) { + return false; + } + + // The conditional dependency resolved! Let's now add all of /its/ dependencies + for (ResolvedArtifact artifact : resolvedArtifacts) { + // First add the artifact to the master list + existingArtifacts.add(getKey(artifact)); + ExtensionDependency extensionDependency = DependencyUtils.getExtensionInfoOrNull(project, artifact); + if (extensionDependency == null) { + continue; + } + // If this artifact represents an extension, mark this one as a conditional extension + extensionDependency.setConditional(true); + // Add to the master list of accepted extensions + allExtensions.put(extensionDependency.getExtensionId(), extensionDependency); + for (Dependency cd : extensionDependency.getConditionalDependencies()) { + // Add any unsatisfied/unresolved conditional dependencies of this dependency to the queue + if (!exists(cd)) { + queueConditionalDependency(extensionDependency, cd); + } + } + } + return satisfied; + } + + private void queueConditionalDependency(ExtensionDependency extension, Dependency conditionalDep) { + // 1. Add to master list of unresolved/unsatisfied dependencies + // 2. Add map entry to link dependency to extension + featureVariants.computeIfAbsent(getFeatureKey(conditionalDep), k -> { + unsatisfiedConditionalDeps.add(conditionalDep); + return new HashSet<>(); + }).add(extension); + } + + private Configuration createConditionalDependenciesConfiguration(Project project, Dependency conditionalDep) { + /* + Configuration conditionalDepConfiguration = project.getConfigurations() + .detachedConfiguration() + .extendsFrom(enforcedPlatforms); + */ + Configuration conditionalDepConfiguration = project.getConfigurations().detachedConfiguration(); + enforcedPlatforms.getExcludeRules().forEach(rule -> { + conditionalDepConfiguration.exclude(Map.of( + "group", rule.getGroup(), + "module", rule.getModule())); + }); + enforcedPlatforms.getAllDependencies().forEach(dependency -> { + conditionalDepConfiguration.getDependencies().add(dependency); + }); + conditionalDepConfiguration.getDependencies().add(conditionalDep); + return conditionalDepConfiguration; + } + + private void enableConditionalDependency(ModuleVersionIdentifier dependency) { + final Set extensions = featureVariants.remove(getFeatureKey(dependency)); + if (extensions == null) { + return; + } + extensions.forEach(e -> e.importConditionalDependency(project.getDependencies(), dependency)); + } + + private boolean exist(List dependencies) { + return existingArtifacts.containsAll(dependencies); + } + + private boolean exists(Dependency dependency) { + return existingArtifacts + .contains(ArtifactKey.of(dependency.getGroup(), dependency.getName(), null, ArtifactCoords.TYPE_JAR)); + } + + public boolean exists(ExtensionDependency dependency) { + return existingArtifacts + .contains(ArtifactKey.of(dependency.getGroup(), dependency.getName(), null, ArtifactCoords.TYPE_JAR)); + } + + private static GACT getFeatureKey(ModuleVersionIdentifier version) { + return new GACT(version.getGroup(), version.getName()); + } + + private static GACT getFeatureKey(Dependency version) { + return new GACT(version.getGroup(), version.getName()); + } + + private static ArtifactKey getKey(ResolvedArtifact a) { + return ArtifactKey.of(a.getModuleVersion().getId().getGroup(), a.getName(), a.getClassifier(), a.getType()); + } +} diff --git a/instrumentation/quarkus-resteasy-reactive/quarkus2-plugin/src/main/java/io/quarkus/gradle/tooling/GradleApplicationModelBuilder.java b/instrumentation/quarkus-resteasy-reactive/quarkus2-plugin/src/main/java/io/quarkus/gradle/tooling/GradleApplicationModelBuilder.java new file mode 100644 index 000000000000..5f2b35b16019 --- /dev/null +++ b/instrumentation/quarkus-resteasy-reactive/quarkus2-plugin/src/main/java/io/quarkus/gradle/tooling/GradleApplicationModelBuilder.java @@ -0,0 +1,674 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +// Includes work from: +/* + * Copyright Quarkus Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.quarkus.gradle.tooling; + +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.file.FileSystem; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Properties; +import java.util.Set; + +import org.gradle.api.Project; +import org.gradle.api.artifacts.Configuration; +import org.gradle.api.artifacts.ResolvableDependencies; +import org.gradle.api.artifacts.ResolvedArtifact; +import org.gradle.api.artifacts.ResolvedConfiguration; +import org.gradle.api.artifacts.component.ProjectComponentIdentifier; +import org.gradle.api.file.FileCollection; +import org.gradle.api.file.FileTree; +import org.gradle.api.initialization.IncludedBuild; +import org.gradle.api.tasks.SourceSet; +import org.gradle.api.tasks.SourceSetContainer; +import org.gradle.api.tasks.TaskCollection; +import org.gradle.api.tasks.compile.AbstractCompile; +import org.gradle.api.tasks.testing.Test; +import org.gradle.internal.composite.IncludedBuildInternal; +import org.gradle.language.jvm.tasks.ProcessResources; +import org.gradle.tooling.provider.model.ParameterizedToolingModelBuilder; + +import io.quarkus.bootstrap.BootstrapConstants; +import io.quarkus.bootstrap.model.ApplicationModel; +import io.quarkus.bootstrap.model.ApplicationModelBuilder; +import io.quarkus.bootstrap.model.CapabilityContract; +import io.quarkus.bootstrap.model.PlatformImports; +import io.quarkus.bootstrap.model.gradle.ModelParameter; +import io.quarkus.bootstrap.model.gradle.impl.ModelParameterImpl; +import io.quarkus.bootstrap.workspace.ArtifactSources; +import io.quarkus.bootstrap.workspace.DefaultArtifactSources; +import io.quarkus.bootstrap.workspace.DefaultSourceDir; +import io.quarkus.bootstrap.workspace.DefaultWorkspaceModule; +import io.quarkus.bootstrap.workspace.SourceDir; +import io.quarkus.bootstrap.workspace.WorkspaceModule; +import io.quarkus.fs.util.ZipUtils; +import io.quarkus.gradle.dependency.ApplicationDeploymentClasspathBuilder; +import io.quarkus.maven.dependency.ArtifactCoords; +import io.quarkus.maven.dependency.ArtifactDependency; +import io.quarkus.maven.dependency.ArtifactKey; +import io.quarkus.maven.dependency.DependencyFlags; +import io.quarkus.maven.dependency.GACT; +import io.quarkus.maven.dependency.GACTV; +import io.quarkus.maven.dependency.GAV; +import io.quarkus.maven.dependency.ResolvedDependency; +import io.quarkus.maven.dependency.ResolvedDependencyBuilder; +import io.quarkus.paths.PathCollection; +import io.quarkus.paths.PathList; +import io.quarkus.runtime.LaunchMode; +import io.quarkus.runtime.util.HashUtil; + +public class GradleApplicationModelBuilder implements ParameterizedToolingModelBuilder { + + private static final String MAIN_RESOURCES_OUTPUT = "build/resources/main"; + private static final String CLASSES_OUTPUT = "build/classes"; + + /* @formatter:off */ + private static final byte COLLECT_TOP_EXTENSION_RUNTIME_NODES = 0b001; + private static final byte COLLECT_DIRECT_DEPS = 0b010; + private static final byte COLLECT_RELOADABLE_MODULES = 0b100; + /* @formatter:on */ + + @Override + public boolean canBuild(String modelName) { + return modelName.equals(ApplicationModel.class.getName()); + } + + @Override + public Class getParameterType() { + return ModelParameter.class; + } + + @Override + public Object buildAll(String modelName, Project project) { + final ModelParameterImpl modelParameter = new ModelParameterImpl(); + modelParameter.setMode(LaunchMode.DEVELOPMENT.toString()); + return buildAll(modelName, modelParameter, project); + } + + @Override + public Object buildAll(String modelName, ModelParameter parameter, Project project) { + final LaunchMode mode = LaunchMode.valueOf(parameter.getMode()); + + final ApplicationDeploymentClasspathBuilder classpathBuilder = new ApplicationDeploymentClasspathBuilder(project, + mode); + final Configuration classpathConfig = classpathBuilder.getRuntimeConfiguration(); + final Configuration deploymentConfig = classpathBuilder.getDeploymentConfiguration(); + final PlatformImports platformImports = classpathBuilder.getPlatformImports(); + + boolean workspaceDiscovery = LaunchMode.DEVELOPMENT.equals(mode) || LaunchMode.TEST.equals(mode) + || Boolean.parseBoolean(System.getProperty(BootstrapConstants.QUARKUS_BOOTSTRAP_WORKSPACE_DISCOVERY)); + if (!workspaceDiscovery) { + Object o = project.getProperties().get(BootstrapConstants.QUARKUS_BOOTSTRAP_WORKSPACE_DISCOVERY); + if (o != null) { + workspaceDiscovery = Boolean.parseBoolean(o.toString()); + } + } + + final ResolvedDependency appArtifact = getProjectArtifact(project, workspaceDiscovery); + final ApplicationModelBuilder modelBuilder = new ApplicationModelBuilder() + .setAppArtifact(appArtifact) + .addReloadableWorkspaceModule(appArtifact.getKey()) + .setPlatformImports(platformImports); + + collectDependencies(classpathConfig.getResolvedConfiguration(), classpathConfig.getIncoming(), workspaceDiscovery, + project, modelBuilder, appArtifact.getWorkspaceModule().mutable()); + collectExtensionDependencies(project, deploymentConfig, modelBuilder); + + return modelBuilder.build(); + } + + public static ResolvedDependency getProjectArtifact(Project project, boolean workspaceDiscovery) { + final ResolvedDependencyBuilder appArtifact = ResolvedDependencyBuilder.newInstance() + .setGroupId(project.getGroup().toString()) + .setArtifactId(project.getName()) + .setVersion(project.getVersion().toString()); + + final SourceSetContainer sourceSets = project.getExtensions().getByType(SourceSetContainer.class); + final WorkspaceModule.Mutable mainModule = WorkspaceModule.builder() + .setModuleId(new GAV(appArtifact.getGroupId(), appArtifact.getArtifactId(), appArtifact.getVersion())) + .setModuleDir(project.getProjectDir().toPath()) + .setBuildDir(project.getBuildDir().toPath()) + .setBuildFile(project.getBuildFile().toPath()); + + initProjectModule(project, mainModule, sourceSets.getByName(SourceSet.MAIN_SOURCE_SET_NAME), ArtifactSources.MAIN); + if (workspaceDiscovery) { + final TaskCollection testTasks = project.getTasks().withType(Test.class); + if (!testTasks.isEmpty()) { + final Map sourceSetsByClassesDir = new HashMap<>(); + sourceSets.forEach(s -> { + s.getOutput().getClassesDirs().forEach(d -> { + if (d.exists()) { + sourceSetsByClassesDir.put(d, s); + } + }); + }); + testTasks.forEach(t -> { + if (t.getEnabled()) { + t.getTestClassesDirs().forEach(d -> { + if (d.exists()) { + final SourceSet sourceSet = sourceSetsByClassesDir.remove(d); + if (sourceSet != null) { + initProjectModule(project, mainModule, sourceSet, + sourceSet.getName().equals(SourceSet.TEST_SOURCE_SET_NAME) + ? ArtifactSources.TEST + : sourceSet.getName()); + } + } + }); + } + }); + } + } + + final PathList.Builder paths = PathList.builder(); + collectDestinationDirs(mainModule.getMainSources().getSourceDirs(), paths); + collectDestinationDirs(mainModule.getMainSources().getResourceDirs(), paths); + + return appArtifact.setWorkspaceModule(mainModule).setResolvedPaths(paths.build()).build(); + } + + private static void collectDestinationDirs(Collection sources, final PathList.Builder paths) { + for (SourceDir src : sources) { + final Path path = src.getOutputDir(); + if (paths.contains(path) || !Files.exists(path)) { + continue; + } + paths.add(path); + } + } + + private void collectExtensionDependencies(Project project, Configuration deploymentConfiguration, + ApplicationModelBuilder modelBuilder) { + final ResolvedConfiguration rc = deploymentConfiguration.getResolvedConfiguration(); + for (ResolvedArtifact a : rc.getResolvedArtifacts()) { + if (a.getId().getComponentIdentifier() instanceof ProjectComponentIdentifier) { + ProjectComponentIdentifier projectComponentIdentifier = (ProjectComponentIdentifier) a.getId() + .getComponentIdentifier(); + var includedBuild = ToolingUtils.includedBuild(project, projectComponentIdentifier); + Project projectDep = null; + if (includedBuild != null) { + projectDep = ToolingUtils.includedBuildProject((IncludedBuildInternal) includedBuild, + projectComponentIdentifier); + } else { + projectDep = project.getRootProject().findProject(projectComponentIdentifier.getProjectPath()); + } + Objects.requireNonNull(projectDep, "project " + projectComponentIdentifier.getProjectPath() + " should exist"); + SourceSetContainer sourceSets = projectDep.getExtensions().getByType(SourceSetContainer.class); + + SourceSet mainSourceSet = sourceSets.getByName(SourceSet.MAIN_SOURCE_SET_NAME); + ResolvedDependencyBuilder dep = modelBuilder.getDependency( + toAppDependenciesKey(a.getModuleVersion().getId().getGroup(), a.getName(), a.getClassifier())); + if (dep == null) { + dep = toDependency(a, mainSourceSet); + modelBuilder.addDependency(dep); + } + dep.setDeploymentCp(); + dep.clearFlag(DependencyFlags.RELOADABLE); + } else if (isDependency(a)) { + ResolvedDependencyBuilder dep = modelBuilder.getDependency( + toAppDependenciesKey(a.getModuleVersion().getId().getGroup(), a.getName(), a.getClassifier())); + if (dep == null) { + dep = toDependency(a); + modelBuilder.addDependency(dep); + } + dep.setDeploymentCp(); + dep.clearFlag(DependencyFlags.RELOADABLE); + } + } + } + + private void collectDependencies(ResolvedConfiguration configuration, ResolvableDependencies dependencies, + boolean workspaceDiscovery, Project project, ApplicationModelBuilder modelBuilder, + WorkspaceModule.Mutable wsModule) { + + final Set resolvedArtifacts = configuration.getResolvedArtifacts(); + // if the number of artifacts is less than the number of files then probably + // the project includes direct file dependencies + // final Set artifactFiles = resolvedArtifacts.size() < configuration.getFiles().size() + final Set artifactFiles = resolvedArtifacts.size() < dependencies.getFiles().getFiles().size() + ? new HashSet<>(resolvedArtifacts.size()) + : null; + + configuration.getFirstLevelModuleDependencies() + .forEach(d -> { + collectDependencies(d, workspaceDiscovery, project, artifactFiles, new HashSet<>(), + modelBuilder, + wsModule, + (byte) (COLLECT_TOP_EXTENSION_RUNTIME_NODES | COLLECT_DIRECT_DEPS | COLLECT_RELOADABLE_MODULES)); + }); + + if (artifactFiles != null) { + // detect FS paths that aren't provided by the resolved artifacts + // for (File f : configuration.getFiles()) { + for (File f : dependencies.getFiles().getFiles()) { + if (artifactFiles.contains(f) || !f.exists()) { + continue; + } + // here we are trying to represent a direct FS path dependency + // as an artifact dependency + // SHA1 hash is used to avoid long file names in the lib dir + final String parentPath = f.getParent(); + final String group = HashUtil.sha1(parentPath == null ? f.getName() : parentPath); + String name = f.getName(); + String type = ArtifactCoords.TYPE_JAR; + if (!f.isDirectory()) { + final int dot = f.getName().lastIndexOf('.'); + if (dot > 0) { + name = f.getName().substring(0, dot); + type = f.getName().substring(dot + 1); + } + } + // hash could be a better way to represent the version + final String version = String.valueOf(f.lastModified()); + final ResolvedDependencyBuilder artifactBuilder = ResolvedDependencyBuilder.newInstance() + .setGroupId(group) + .setArtifactId(name) + .setType(type) + .setVersion(version) + .setResolvedPath(f.toPath()) + .setDirect(true) + .setRuntimeCp(); + processQuarkusDependency(artifactBuilder, modelBuilder); + modelBuilder.addDependency(artifactBuilder); + } + } + } + + private void collectDependencies(org.gradle.api.artifacts.ResolvedDependency resolvedDep, boolean workspaceDiscovery, + Project project, Set artifactFiles, Set processedModules, ApplicationModelBuilder modelBuilder, + WorkspaceModule.Mutable parentModule, + byte flags) { + WorkspaceModule.Mutable projectModule = null; + for (ResolvedArtifact a : resolvedDep.getModuleArtifacts()) { + final ArtifactKey artifactKey = toAppDependenciesKey(a.getModuleVersion().getId().getGroup(), a.getName(), + a.getClassifier()); + if (!isDependency(a) || modelBuilder.getDependency(artifactKey) != null) { + continue; + } + final ArtifactCoords depCoords = toArtifactCoords(a); + final ResolvedDependencyBuilder depBuilder = ResolvedDependencyBuilder.newInstance() + .setCoords(depCoords) + .setRuntimeCp(); + if (isFlagOn(flags, COLLECT_DIRECT_DEPS)) { + depBuilder.setDirect(true); + flags = clearFlag(flags, COLLECT_DIRECT_DEPS); + } + if (parentModule != null) { + parentModule.addDependency(new ArtifactDependency(depCoords)); + } + + PathCollection paths = null; + if (workspaceDiscovery && a.getId().getComponentIdentifier() instanceof ProjectComponentIdentifier) { + + Project projectDep = project.getRootProject().findProject( + ((ProjectComponentIdentifier) a.getId().getComponentIdentifier()).getProjectPath()); + SourceSetContainer sourceSets = projectDep == null ? null + : projectDep.getExtensions().findByType(SourceSetContainer.class); + + final String classifier = a.getClassifier(); + if (classifier == null || classifier.isEmpty()) { + final IncludedBuild includedBuild = ToolingUtils.includedBuild(project.getRootProject(), + (ProjectComponentIdentifier) a.getId().getComponentIdentifier()); + if (includedBuild != null) { + final PathList.Builder pathBuilder = PathList.builder(); + + if (includedBuild instanceof IncludedBuildInternal) { + projectDep = ToolingUtils.includedBuildProject((IncludedBuildInternal) includedBuild, + (ProjectComponentIdentifier) a.getId().getComponentIdentifier()); + } + if (projectDep != null) { + projectModule = initProjectModuleAndBuildPaths(projectDep, a, modelBuilder, depBuilder, + pathBuilder, SourceSet.MAIN_SOURCE_SET_NAME, false); + addSubstitutedProject(pathBuilder, projectDep.getProjectDir()); + } else { + addSubstitutedProject(pathBuilder, includedBuild.getProjectDir()); + } + paths = pathBuilder.build(); + } else if (sourceSets != null) { + final PathList.Builder pathBuilder = PathList.builder(); + projectModule = initProjectModuleAndBuildPaths(projectDep, a, modelBuilder, depBuilder, + pathBuilder, SourceSet.MAIN_SOURCE_SET_NAME, false); + paths = pathBuilder.build(); + } + } else if (sourceSets != null) { + if (SourceSet.TEST_SOURCE_SET_NAME.equals(classifier)) { + final PathList.Builder pathBuilder = PathList.builder(); + projectModule = initProjectModuleAndBuildPaths(projectDep, a, modelBuilder, depBuilder, + pathBuilder, SourceSet.TEST_SOURCE_SET_NAME, true); + paths = pathBuilder.build(); + } else if ("test-fixtures".equals(classifier)) { + final PathList.Builder pathBuilder = PathList.builder(); + projectModule = initProjectModuleAndBuildPaths(projectDep, a, modelBuilder, depBuilder, + pathBuilder, "testFixtures", true); + paths = pathBuilder.build(); + } + } + } + + depBuilder.setResolvedPaths(paths == null ? PathList.of(a.getFile().toPath()) : paths) + .setWorkspaceModule(projectModule); + if (processQuarkusDependency(depBuilder, modelBuilder)) { + if (isFlagOn(flags, COLLECT_TOP_EXTENSION_RUNTIME_NODES)) { + depBuilder.setFlags(DependencyFlags.TOP_LEVEL_RUNTIME_EXTENSION_ARTIFACT); + flags = clearFlag(flags, COLLECT_TOP_EXTENSION_RUNTIME_NODES); + } + flags = clearFlag(flags, COLLECT_RELOADABLE_MODULES); + } + if (!isFlagOn(flags, COLLECT_RELOADABLE_MODULES)) { + depBuilder.clearFlag(DependencyFlags.RELOADABLE); + } + modelBuilder.addDependency(depBuilder); + + if (artifactFiles != null) { + artifactFiles.add(a.getFile()); + } + } + + processedModules.add(ArtifactKey.ga(resolvedDep.getModuleGroup(), resolvedDep.getModuleName())); + for (org.gradle.api.artifacts.ResolvedDependency child : resolvedDep.getChildren()) { + if (!processedModules.contains(new GACT(child.getModuleGroup(), child.getModuleName()))) { + collectDependencies(child, workspaceDiscovery, project, artifactFiles, processedModules, + modelBuilder, projectModule, flags); + } + } + } + + private static String toNonNullClassifier(String resolvedClassifier) { + return resolvedClassifier == null ? ArtifactCoords.DEFAULT_CLASSIFIER : resolvedClassifier; + } + + private WorkspaceModule.Mutable initProjectModuleAndBuildPaths(final Project project, + ResolvedArtifact resolvedArtifact, ApplicationModelBuilder appModel, final ResolvedDependencyBuilder appDep, + PathList.Builder buildPaths, String sourceName, boolean test) { + + appDep.setWorkspaceModule().setReloadable(); + + final WorkspaceModule.Mutable projectModule = appModel.getOrCreateProjectModule( + new GAV(resolvedArtifact.getModuleVersion().getId().getGroup(), resolvedArtifact.getName(), + resolvedArtifact.getModuleVersion().getId().getVersion()), + project.getProjectDir(), + project.getBuildDir()) + .setBuildFile(project.getBuildFile().toPath()); + + final String classifier = toNonNullClassifier(resolvedArtifact.getClassifier()); + SourceSetContainer sourceSets = project.getExtensions().getByType(SourceSetContainer.class); + initProjectModule(project, projectModule, sourceSets.findByName(sourceName), classifier); + + collectDestinationDirs(projectModule.getSources(classifier).getSourceDirs(), buildPaths); + collectDestinationDirs(projectModule.getSources(classifier).getResourceDirs(), buildPaths); + + appModel.addReloadableWorkspaceModule( + ArtifactKey.of(resolvedArtifact.getModuleVersion().getId().getGroup(), resolvedArtifact.getName(), classifier, + ArtifactCoords.TYPE_JAR)); + return projectModule; + } + + private boolean processQuarkusDependency(ResolvedDependencyBuilder artifactBuilder, ApplicationModelBuilder modelBuilder) { + for (Path artifactPath : artifactBuilder.getResolvedPaths()) { + if (!Files.exists(artifactPath) || !artifactBuilder.getType().equals(ArtifactCoords.TYPE_JAR)) { + break; + } + if (Files.isDirectory(artifactPath)) { + return processQuarkusDir(artifactBuilder, artifactPath.resolve(BootstrapConstants.META_INF), modelBuilder); + } else { + try (FileSystem artifactFs = ZipUtils.newFileSystem(artifactPath)) { + return processQuarkusDir(artifactBuilder, artifactFs.getPath(BootstrapConstants.META_INF), modelBuilder); + } catch (IOException e) { + throw new RuntimeException("Failed to process " + artifactPath, e); + } + } + } + return false; + } + + private static boolean processQuarkusDir(ResolvedDependencyBuilder artifactBuilder, Path quarkusDir, + ApplicationModelBuilder modelBuilder) { + if (!Files.exists(quarkusDir)) { + return false; + } + final Path quarkusDescr = quarkusDir.resolve(BootstrapConstants.DESCRIPTOR_FILE_NAME); + if (!Files.exists(quarkusDescr)) { + return false; + } + final Properties extProps = readDescriptor(quarkusDescr); + if (extProps == null) { + return false; + } + artifactBuilder.setRuntimeExtensionArtifact(); + final String extensionCoords = artifactBuilder.toGACTVString(); + modelBuilder.handleExtensionProperties(extProps, extensionCoords); + + final String providesCapabilities = extProps.getProperty(BootstrapConstants.PROP_PROVIDES_CAPABILITIES); + if (providesCapabilities != null) { + modelBuilder + .addExtensionCapabilities(CapabilityContract.of(extensionCoords, providesCapabilities, null)); + } + return true; + } + + private static Properties readDescriptor(final Path path) { + final Properties rtProps; + if (!Files.exists(path)) { + // not a platform artifact + return null; + } + rtProps = new Properties(); + try (BufferedReader reader = Files.newBufferedReader(path)) { + rtProps.load(reader); + } catch (IOException e) { + throw new UncheckedIOException("Failed to load extension description " + path, e); + } + return rtProps; + } + + private static void initProjectModule(Project project, WorkspaceModule.Mutable module, SourceSet sourceSet, + String classifier) { + + if (sourceSet == null) { + return; + } + + final FileCollection allClassesDirs = sourceSet.getOutput().getClassesDirs(); + // some plugins do not add source directories to source sets and they may be missing from sourceSet.getAllJava() + // see https://github.com/quarkusio/quarkus/issues/20755 + + final List sourceDirs = new ArrayList<>(1); + project.getTasks().withType(AbstractCompile.class, t -> { + if (!t.getEnabled()) { + return; + } + final FileTree source = t.getSource(); + if (source.isEmpty()) { + return; + } + final File destDir = t.getDestinationDirectory().getAsFile().get(); + if (!allClassesDirs.contains(destDir)) { + return; + } + source.visit(a -> { + // we are looking for the root dirs containing sources + if (a.getRelativePath().getSegments().length == 1) { + final File srcDir = a.getFile().getParentFile(); + sourceDirs.add(new DefaultSourceDir(srcDir.toPath(), destDir.toPath(), Map.of("compiler", t.getName()))); + } + }); + }); + + // This "try/catch" is needed because of the way the "quarkus-cli" Gradle tests work. Without it, the tests fail. + /* + try { + Class.forName("org.jetbrains.kotlin.gradle.tasks.KotlinJvmCompile"); + project.getTasks().withType(KotlinJvmCompile.class, t -> { + if (!t.getEnabled()) { + return; + } + final FileTree source = t.getSources().getAsFileTree(); + if (source.isEmpty()) { + return; + } + final File destDir = t.getDestinationDirectory().getAsFile().get(); + if (!allClassesDirs.contains(destDir)) { + return; + } + source.visit(a -> { + // we are looking for the root dirs containing sources + if (a.getRelativePath().getSegments().length == 1) { + final File srcDir = a.getFile().getParentFile(); + sourceDirs + .add(new DefaultSourceDir(srcDir.toPath(), destDir.toPath(), Map.of("compiler", t.getName()))); + } + }); + }); + } catch (ClassNotFoundException e) { + // ignore + } + */ + + final LinkedHashMap resourceDirs = new LinkedHashMap<>(1); + final File resourcesOutputDir = sourceSet.getOutput().getResourcesDir(); + project.getTasks().withType(ProcessResources.class, t -> { + if (!t.getEnabled()) { + return; + } + final FileCollection source = t.getSource(); + if (source.isEmpty()) { + return; + } + if (!t.getDestinationDir().equals(resourcesOutputDir)) { + return; + } + final Path destDir = t.getDestinationDir().toPath(); + source.getAsFileTree().visit(a -> { + // we are looking for the root dirs containing sources + if (a.getRelativePath().getSegments().length == 1) { + final File srcDir = a.getFile().getParentFile(); + resourceDirs.put(srcDir, destDir); + } + }); + }); + // there could be a task generating resources + if (resourcesOutputDir.exists() && resourceDirs.isEmpty()) { + sourceSet.getResources().getSrcDirs() + .forEach(srcDir -> resourceDirs.put(srcDir, resourcesOutputDir.toPath())); + } + final List resources = new ArrayList<>(resourceDirs.size()); + for (Map.Entry e : resourceDirs.entrySet()) { + resources.add(new DefaultSourceDir(e.getKey().toPath(), e.getValue())); + } + module.addArtifactSources(new DefaultArtifactSources(classifier, sourceDirs, resources)); + } + + private void addSubstitutedProject(PathList.Builder paths, File projectFile) { + File mainResourceDirectory = new File(projectFile, MAIN_RESOURCES_OUTPUT); + if (mainResourceDirectory.exists()) { + paths.add(mainResourceDirectory.toPath()); + } + File classesOutput = new File(projectFile, CLASSES_OUTPUT); + File[] languageDirectories = classesOutput.listFiles(); + if (languageDirectories != null) { + for (File languageDirectory : languageDirectories) { + if (languageDirectory.isDirectory()) { + for (File sourceSet : languageDirectory.listFiles()) { + if (sourceSet.isDirectory() && sourceSet.getName().equals(SourceSet.MAIN_SOURCE_SET_NAME)) { + paths.add(sourceSet.toPath()); + } + } + } + } + } + } + + private static boolean isFlagOn(byte walkingFlags, byte flag) { + return (walkingFlags & flag) > 0; + } + + private static byte clearFlag(byte flags, byte flag) { + if ((flags & flag) > 0) { + flags ^= flag; + } + return flags; + } + + private static boolean isDependency(ResolvedArtifact a) { + return ArtifactCoords.TYPE_JAR.equalsIgnoreCase(a.getExtension()) || "exe".equalsIgnoreCase(a.getExtension()) || + a.getFile().isDirectory(); + } + + /** + * Creates an instance of Dependency and associates it with the ResolvedArtifact's path + */ + static ResolvedDependencyBuilder toDependency(ResolvedArtifact a, int... flags) { + return toDependency(a, PathList.of(a.getFile().toPath()), null, flags); + } + + static ResolvedDependencyBuilder toDependency(ResolvedArtifact a, SourceSet s) { + PathList.Builder resolvedPathBuilder = PathList.builder(); + + for (File classesDir : s.getOutput().getClassesDirs()) { + if (classesDir.exists()) { + resolvedPathBuilder.add(classesDir.toPath()); + } + } + File resourceDir = s.getOutput().getResourcesDir(); + if (resourceDir != null && resourceDir.exists()) { + resolvedPathBuilder.add(resourceDir.toPath()); + } + + return ResolvedDependencyBuilder + .newInstance() + .setResolvedPaths(resolvedPathBuilder.build()) + .setCoords(toArtifactCoords(a)); + } + + static ResolvedDependencyBuilder toDependency(ResolvedArtifact a, PathCollection paths, DefaultWorkspaceModule module, + int... flags) { + int allFlags = 0; + for (int f : flags) { + allFlags |= f; + } + return ResolvedDependencyBuilder.newInstance() + .setCoords(toArtifactCoords(a)) + .setResolvedPaths(paths) + .setWorkspaceModule(module) + .setFlags(allFlags); + } + + private static ArtifactCoords toArtifactCoords(ResolvedArtifact a) { + final String[] split = a.getModuleVersion().toString().split(":"); + return new GACTV(split[0], split[1], a.getClassifier(), a.getType(), split.length > 2 ? split[2] : null); + } + + private static ArtifactKey toAppDependenciesKey(String groupId, String artifactId, String classifier) { + return new GACT(groupId, artifactId, classifier, ArtifactCoords.TYPE_JAR); + } +} diff --git a/instrumentation/quarkus-resteasy-reactive/quarkus2-plugin/src/main/java/io/quarkus/gradle/tooling/ToolingUtils.java b/instrumentation/quarkus-resteasy-reactive/quarkus2-plugin/src/main/java/io/quarkus/gradle/tooling/ToolingUtils.java new file mode 100644 index 000000000000..f4cddb7bdc13 --- /dev/null +++ b/instrumentation/quarkus-resteasy-reactive/quarkus2-plugin/src/main/java/io/quarkus/gradle/tooling/ToolingUtils.java @@ -0,0 +1,108 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +// Includes work from: +/* + * Copyright Quarkus Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.quarkus.gradle.tooling; + +import java.io.IOException; +import java.io.ObjectOutputStream; +import java.nio.file.Files; +import java.nio.file.Path; + +import org.gradle.api.Project; +import org.gradle.api.Task; +import org.gradle.api.artifacts.ModuleDependency; +import org.gradle.api.artifacts.component.ProjectComponentIdentifier; +import org.gradle.api.attributes.Category; +import org.gradle.api.initialization.IncludedBuild; +import org.gradle.internal.composite.IncludedBuildInternal; + +import io.quarkus.bootstrap.model.ApplicationModel; +import io.quarkus.bootstrap.model.gradle.ModelParameter; +import io.quarkus.bootstrap.model.gradle.impl.ModelParameterImpl; +import io.quarkus.runtime.LaunchMode; + +public class ToolingUtils { + + private static final String DEPLOYMENT_CONFIGURATION_SUFFIX = "Deployment"; + private static final String PLATFORM_CONFIGURATION_SUFFIX = "Platform"; + public static final String DEV_MODE_CONFIGURATION_NAME = "quarkusDev"; + + public static String toDeploymentConfigurationName(String baseConfigurationName) { + return baseConfigurationName + DEPLOYMENT_CONFIGURATION_SUFFIX; + } + + public static String toPlatformConfigurationName(String baseConfigurationName) { + return baseConfigurationName + PLATFORM_CONFIGURATION_SUFFIX; + } + + public static boolean isEnforcedPlatform(ModuleDependency module) { + final Category category = module.getAttributes().getAttribute(Category.CATEGORY_ATTRIBUTE); + return category != null && (Category.ENFORCED_PLATFORM.equals(category.getName()) + || Category.REGULAR_PLATFORM.equals(category.getName())); + } + + public static IncludedBuild includedBuild(final Project project, + final ProjectComponentIdentifier projectComponentIdentifier) { +/* + final String name = projectComponentIdentifier.getBuild().getName(); + for (IncludedBuild ib : project.getRootProject().getGradle().getIncludedBuilds()) { + if (ib.getName().equals(name)) { + return ib; + } + } +*/ + final String buildPath = projectComponentIdentifier.getBuild().getBuildPath(); + for (IncludedBuild ib : project.getRootProject().getGradle().getIncludedBuilds()) { + if (((IncludedBuildInternal) ib).getTarget().getBuildIdentifier().getBuildPath().equals(buildPath)) { + return ib; + } + } + return null; + } + + public static Project includedBuildProject(IncludedBuildInternal includedBuild, + final ProjectComponentIdentifier componentIdentifier) { + return includedBuild.getTarget().getMutableModel().getRootProject().findProject( + componentIdentifier.getProjectPath()); + } + + public static Path serializeAppModel(ApplicationModel appModel, Task context, boolean test) throws IOException { + final Path serializedModel = context.getTemporaryDir().toPath() + .resolve("quarkus-app" + (test ? "-test" : "") + "-model.dat"); + try (ObjectOutputStream out = new ObjectOutputStream(Files.newOutputStream(serializedModel))) { + out.writeObject(appModel); + } + return serializedModel; + } + + public static ApplicationModel create(Project project, LaunchMode mode) { + final ModelParameter params = new ModelParameterImpl(); + params.setMode(mode.toString()); + return create(project, params); + } + + public static ApplicationModel create(Project project, ModelParameter params) { + return (ApplicationModel) new GradleApplicationModelBuilder().buildAll(ApplicationModel.class.getName(), params, + project); + } + +} diff --git a/instrumentation/quarkus-resteasy-reactive/quarkus2-plugin/src/main/java/io/quarkus/gradle/tooling/dependency/DependencyUtils.java b/instrumentation/quarkus-resteasy-reactive/quarkus2-plugin/src/main/java/io/quarkus/gradle/tooling/dependency/DependencyUtils.java new file mode 100644 index 000000000000..68008babd8b6 --- /dev/null +++ b/instrumentation/quarkus-resteasy-reactive/quarkus2-plugin/src/main/java/io/quarkus/gradle/tooling/dependency/DependencyUtils.java @@ -0,0 +1,212 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +// Includes work from: +/* + * Copyright Quarkus Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.quarkus.gradle.tooling.dependency; + +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.nio.file.FileSystem; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Properties; + +import org.gradle.api.GradleException; +import org.gradle.api.Project; +import org.gradle.api.artifacts.Configuration; +import org.gradle.api.artifacts.Dependency; +import org.gradle.api.artifacts.ModuleDependency; +import org.gradle.api.artifacts.ModuleVersionIdentifier; +import org.gradle.api.artifacts.ResolvedArtifact; +import org.gradle.api.artifacts.component.ProjectComponentIdentifier; +import org.gradle.api.artifacts.dsl.DependencyHandler; +import org.gradle.api.capabilities.Capability; +import org.gradle.api.tasks.SourceSet; +import org.gradle.api.tasks.SourceSetContainer; +import org.gradle.internal.composite.IncludedBuildInternal; + +import io.quarkus.bootstrap.BootstrapConstants; +import io.quarkus.bootstrap.util.BootstrapUtils; +import io.quarkus.fs.util.ZipUtils; +import io.quarkus.gradle.tooling.ToolingUtils; +import io.quarkus.maven.dependency.ArtifactCoords; +import io.quarkus.maven.dependency.ArtifactKey; +import io.quarkus.maven.dependency.GACTV; + +public class DependencyUtils { + + private static final String COPY_CONFIGURATION_NAME = "quarkusDependency"; + private static final String TEST_FIXTURE_SUFFIX = "-test-fixtures"; + + public static Configuration duplicateConfiguration(Project project, Configuration toDuplicate) { + Configuration configurationCopy = project.getConfigurations().findByName(COPY_CONFIGURATION_NAME); + if (configurationCopy != null) { + project.getConfigurations().remove(configurationCopy); + } + return duplicateConfiguration(project, COPY_CONFIGURATION_NAME, toDuplicate); + } + + public static Configuration duplicateConfiguration(Project project, String name, Configuration toDuplicate) { + final Configuration configurationCopy = project.getConfigurations().create(name); + configurationCopy.getDependencies().addAll(toDuplicate.getAllDependencies()); + configurationCopy.getDependencyConstraints().addAll(toDuplicate.getAllDependencyConstraints()); + return configurationCopy; + } + + public static boolean isTestFixtureDependency(Dependency dependency) { + if (!(dependency instanceof ModuleDependency)) { + return false; + } + ModuleDependency module = (ModuleDependency) dependency; + for (Capability requestedCapability : module.getRequestedCapabilities()) { + if (requestedCapability.getName().endsWith(TEST_FIXTURE_SUFFIX)) { + return true; + } + } + return false; + } + + public static String asDependencyNotation(Dependency dependency) { + return String.join(":", dependency.getGroup(), dependency.getName(), dependency.getVersion()); + } + + public static String asDependencyNotation(ArtifactCoords artifactCoords) { + return String.join(":", artifactCoords.getGroupId(), artifactCoords.getArtifactId(), artifactCoords.getVersion()); + } + + public static ExtensionDependency getExtensionInfoOrNull(Project project, ResolvedArtifact artifact) { + ModuleVersionIdentifier artifactId = artifact.getModuleVersion().getId(); + File artifactFile = artifact.getFile(); + + if (artifact.getId().getComponentIdentifier() instanceof ProjectComponentIdentifier) { + ProjectComponentIdentifier componentIdentifier = ((ProjectComponentIdentifier) artifact.getId() + .getComponentIdentifier()); + Project projectDep = project.getRootProject().findProject( + componentIdentifier.getProjectPath()); + SourceSetContainer sourceSets = projectDep == null ? null + : projectDep.getExtensions().findByType(SourceSetContainer.class); + final String classifier = artifact.getClassifier(); + boolean isIncludedBuild = false; + /* + if ((!componentIdentifier.getBuild().isCurrentBuild() || sourceSets == null) + && (classifier == null || classifier.isEmpty())) { + var includedBuild = ToolingUtils.includedBuild(project, componentIdentifier); + if (includedBuild instanceof IncludedBuildInternal) { + projectDep = ToolingUtils.includedBuildProject((IncludedBuildInternal) includedBuild, componentIdentifier); + sourceSets = projectDep == null ? null : projectDep.getExtensions().findByType(SourceSetContainer.class); + isIncludedBuild = true; + } + } + */ + if (sourceSets != null) { + SourceSet mainSourceSet = sourceSets.findByName(SourceSet.MAIN_SOURCE_SET_NAME); + if (mainSourceSet == null) { + return null; + } + File resourcesDir = mainSourceSet.getOutput().getResourcesDir(); + Path descriptorPath = resourcesDir.toPath().resolve(BootstrapConstants.DESCRIPTOR_PATH); + if (Files.exists(descriptorPath)) { + return loadExtensionInfo(project, descriptorPath, artifactId, projectDep, isIncludedBuild); + } + } + } + + if (!artifactFile.exists()) { + return null; + } + if (artifactFile.isDirectory()) { + Path descriptorPath = artifactFile.toPath().resolve(BootstrapConstants.DESCRIPTOR_PATH); + if (Files.exists(descriptorPath)) { + return loadExtensionInfo(project, descriptorPath, artifactId, null, false); + } + } else if (ArtifactCoords.TYPE_JAR.equals(artifact.getExtension())) { + try (FileSystem artifactFs = ZipUtils.newFileSystem(artifactFile.toPath())) { + Path descriptorPath = artifactFs.getPath(BootstrapConstants.DESCRIPTOR_PATH); + if (Files.exists(descriptorPath)) { + return loadExtensionInfo(project, descriptorPath, artifactId, null, false); + } + } catch (IOException e) { + throw new GradleException("Failed to read " + artifactFile, e); + } + } + return null; + } + + private static ExtensionDependency loadExtensionInfo(Project project, Path descriptorPath, + ModuleVersionIdentifier exentionId, Project extensionProject, boolean isIncludedBuild) { + final Properties extensionProperties = new Properties(); + try (BufferedReader reader = Files.newBufferedReader(descriptorPath)) { + extensionProperties.load(reader); + } catch (IOException e) { + throw new GradleException("Failed to load " + descriptorPath, e); + } + ArtifactCoords deploymentModule = GACTV + .fromString(extensionProperties.getProperty(BootstrapConstants.PROP_DEPLOYMENT_ARTIFACT)); + final List conditionalDependencies; + if (extensionProperties.containsKey(BootstrapConstants.CONDITIONAL_DEPENDENCIES)) { + final String[] deps = BootstrapUtils + .splitByWhitespace(extensionProperties.getProperty(BootstrapConstants.CONDITIONAL_DEPENDENCIES)); + conditionalDependencies = new ArrayList<>(deps.length); + for (String conditionalDep : deps) { + conditionalDependencies.add(create(project.getDependencies(), conditionalDep)); + } + } else { + conditionalDependencies = Collections.emptyList(); + } + + final ArtifactKey[] constraints = BootstrapUtils + .parseDependencyCondition(extensionProperties.getProperty(BootstrapConstants.DEPENDENCY_CONDITION)); + if (isIncludedBuild) { + return new IncludedBuildExtensionDependency(extensionProject, exentionId, deploymentModule, conditionalDependencies, + constraints == null ? Collections.emptyList() : Arrays.asList(constraints)); + } + if (extensionProject != null) { + return new LocalExtensionDependency(extensionProject, exentionId, deploymentModule, conditionalDependencies, + constraints == null ? Collections.emptyList() : Arrays.asList(constraints)); + } + return new ExtensionDependency(exentionId, deploymentModule, conditionalDependencies, + constraints == null ? Collections.emptyList() : Arrays.asList(constraints)); + } + + public static Dependency create(DependencyHandler dependencies, String conditionalDependency) { + final ArtifactCoords dependencyCoords = GACTV.fromString(conditionalDependency); + return dependencies.create(String.join(":", dependencyCoords.getGroupId(), dependencyCoords.getArtifactId(), + dependencyCoords.getVersion())); + } + + public static void addLocalDeploymentDependency(String deploymentConfigurationName, LocalExtensionDependency extension, + DependencyHandler dependencies) { + dependencies.add(deploymentConfigurationName, + dependencies.project(Collections.singletonMap("path", extension.findDeploymentModulePath()))); + } + + public static void requireDeploymentDependency(String deploymentConfigurationName, ExtensionDependency extension, + DependencyHandler dependencies) { + dependencies.add(deploymentConfigurationName, + extension.getDeploymentModule().getGroupId() + ":" + extension.getDeploymentModule().getArtifactId() + ":" + + extension.getDeploymentModule().getVersion()); + } +} diff --git a/instrumentation/quarkus-resteasy-reactive/quarkus2-testing/build.gradle.kts b/instrumentation/quarkus-resteasy-reactive/quarkus2-testing/build.gradle.kts index ff63fb854165..ef6a0332bef2 100644 --- a/instrumentation/quarkus-resteasy-reactive/quarkus2-testing/build.gradle.kts +++ b/instrumentation/quarkus-resteasy-reactive/quarkus2-testing/build.gradle.kts @@ -1,7 +1,14 @@ +import io.quarkus.bootstrap.model.ApplicationModel +import io.quarkus.bootstrap.model.gradle.impl.ModelParameterImpl +import io.quarkus.bootstrap.util.BootstrapUtils +import io.quarkus.gradle.tooling.GradleApplicationModelBuilder +import io.quarkus.runtime.LaunchMode +import kotlin.io.path.notExists + plugins { id("otel.javaagent-testing") - id("io.quarkus") version "2.16.7.Final" + id("io.opentelemetry.instrumentation.quarkus2") apply false } otelJava { @@ -22,15 +29,39 @@ dependencies { testImplementation("io.quarkus:quarkus-junit5") } -tasks.named("compileJava").configure { - dependsOn(tasks.named("compileQuarkusGeneratedSourcesJava")) +tasks.register("integrationTestClasses") {} + +val quarkusTestBaseRuntimeClasspathConfiguration by configurations.creating { + extendsFrom(configurations["testRuntimeClasspath"]) } -tasks.named("sourcesJar").configure { - dependsOn(tasks.named("compileQuarkusGeneratedSourcesJava")) + +val quarkusTestCompileOnlyConfiguration by configurations.creating { } -tasks.named("checkstyleTest").configure { - dependsOn(tasks.named("compileQuarkusGeneratedSourcesJava")) + +val testModelPath = layout.buildDirectory.file("quarkus-app-test-model.dat").get().asFile.toPath() + +val buildModel = tasks.register("buildModel") { + dependsOn(configurations.named("testRuntimeClasspath")) + + if (testModelPath.notExists()) { + doLast { + val modelParameter = ModelParameterImpl() + modelParameter.mode = LaunchMode.TEST.toString() + val model = GradleApplicationModelBuilder().buildAll( + ApplicationModel::class.java.getName(), + modelParameter, + project + ) + BootstrapUtils.serializeAppModel(model as ApplicationModel?, testModelPath) + } + } + outputs.file(testModelPath) } -tasks.named("compileTestJava").configure { - dependsOn(tasks.named("compileQuarkusTestGeneratedSourcesJava")) + +tasks { + test { + dependsOn(buildModel) + + systemProperty("quarkus-internal-test.serialized-app-model.path", testModelPath.toString()) + } } diff --git a/instrumentation/quarkus-resteasy-reactive/quarkus3-plugin/build.gradle.kts b/instrumentation/quarkus-resteasy-reactive/quarkus3-plugin/build.gradle.kts new file mode 100644 index 000000000000..9fd5a36fcc32 --- /dev/null +++ b/instrumentation/quarkus-resteasy-reactive/quarkus3-plugin/build.gradle.kts @@ -0,0 +1,22 @@ +plugins { + `java-gradle-plugin` +} + +repositories { + mavenCentral() + gradlePluginPortal() +} + +dependencies { + implementation(gradleApi()) + implementation("io.quarkus:quarkus-gradle-model:3.0.0.Final") +} + +gradlePlugin { + plugins { + create("quarkus3Plugin") { + id = "io.opentelemetry.instrumentation.quarkus3" + implementationClass = "io.opentelemetry.instrumentation.quarkus3plugin.Quarkus3Plugin" + } + } +} diff --git a/instrumentation/quarkus-resteasy-reactive/quarkus3-plugin/src/main/java/io/opentelemetry/instrumentation/quarkus3plugin/Quarkus3Plugin.java b/instrumentation/quarkus-resteasy-reactive/quarkus3-plugin/src/main/java/io/opentelemetry/instrumentation/quarkus3plugin/Quarkus3Plugin.java new file mode 100644 index 000000000000..ea717f0dc466 --- /dev/null +++ b/instrumentation/quarkus-resteasy-reactive/quarkus3-plugin/src/main/java/io/opentelemetry/instrumentation/quarkus3plugin/Quarkus3Plugin.java @@ -0,0 +1,20 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.quarkus3plugin; + +import org.gradle.api.Plugin; +import org.gradle.api.Project; + +@SuppressWarnings("unused") +public class Quarkus3Plugin implements Plugin { + + @Override + public void apply(Project project) { + // we use this plugin with apply false and call its classes directly from the build script + throw new IllegalStateException("this plugin is not meant to be applied"); + } +} + diff --git a/instrumentation/quarkus-resteasy-reactive/quarkus3-plugin/src/main/java/io/quarkus/gradle/dependency/ConditionalDependenciesEnabler.java b/instrumentation/quarkus-resteasy-reactive/quarkus3-plugin/src/main/java/io/quarkus/gradle/dependency/ConditionalDependenciesEnabler.java new file mode 100644 index 000000000000..01690e33986a --- /dev/null +++ b/instrumentation/quarkus-resteasy-reactive/quarkus3-plugin/src/main/java/io/quarkus/gradle/dependency/ConditionalDependenciesEnabler.java @@ -0,0 +1,245 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +// Includes work from: +/* + * Copyright Quarkus Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.quarkus.gradle.dependency; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.gradle.api.Project; +import org.gradle.api.artifacts.Configuration; +import org.gradle.api.artifacts.Dependency; +import org.gradle.api.artifacts.ModuleVersionIdentifier; +import org.gradle.api.artifacts.ResolvedArtifact; + +import io.quarkus.gradle.tooling.dependency.DependencyUtils; +import io.quarkus.gradle.tooling.dependency.ExtensionDependency; +import io.quarkus.maven.dependency.ArtifactCoords; +import io.quarkus.maven.dependency.ArtifactKey; +import io.quarkus.maven.dependency.GACT; +import io.quarkus.runtime.LaunchMode; + +public class ConditionalDependenciesEnabler { + + /** + * Links dependencies to extensions + */ + private final Map> featureVariants = new HashMap<>(); + /** + * Despite its name, only contains extensions which have no conditional dependencies, or have + * resolved their conditional dependencies. + */ + private final Map allExtensions = new HashMap<>(); + private final Project project; + private final Configuration enforcedPlatforms; + private final Set existingArtifacts = new HashSet<>(); + private final List unsatisfiedConditionalDeps = new ArrayList<>(); + + public ConditionalDependenciesEnabler(Project project, LaunchMode mode, + Configuration platforms) { + this.project = project; + this.enforcedPlatforms = platforms; + + // Get runtimeClasspath (quarkusProdBaseRuntimeClasspathConfiguration to be exact) + Configuration baseRuntimeConfig = project.getConfigurations() + .getByName(ApplicationDeploymentClasspathBuilder.getBaseRuntimeConfigName(mode)); + + if (!baseRuntimeConfig.getIncoming().getDependencies().isEmpty()) { + // Gather all extensions from the full resolved dependency tree + collectConditionalDependencies(baseRuntimeConfig.getResolvedConfiguration().getResolvedArtifacts()); + // If there are any extensions which had unresolved conditional dependencies: + while (!unsatisfiedConditionalDeps.isEmpty()) { + boolean satisfiedConditionalDeps = false; + final int originalUnsatisfiedCount = unsatisfiedConditionalDeps.size(); + int i = 0; + // Go through each unsatisfied/unresolved dependency once: + while (i < unsatisfiedConditionalDeps.size()) { + final Dependency conditionalDep = unsatisfiedConditionalDeps.get(i); + // Try to resolve it with the latest evolved graph available + if (resolveConditionalDependency(conditionalDep)) { + // Mark the resolution as a success so we know the graph evolved + satisfiedConditionalDeps = true; + unsatisfiedConditionalDeps.remove(i); + } else { + // No resolution (yet) or graph evolution; move on to the next + ++i; + } + } + // If we didn't resolve any dependencies and the graph did not evolve, give up. + if (!satisfiedConditionalDeps && unsatisfiedConditionalDeps.size() == originalUnsatisfiedCount) { + break; + } + } + reset(); + } + + } + + public Collection getAllExtensions() { + return allExtensions.values(); + } + + private void reset() { + featureVariants.clear(); + existingArtifacts.clear(); + unsatisfiedConditionalDeps.clear(); + } + + private void collectConditionalDependencies(Set runtimeArtifacts) { + // For every artifact in the dependency graph: + for (ResolvedArtifact artifact : runtimeArtifacts) { + // Add to master list of artifacts: + existingArtifacts.add(getKey(artifact)); + ExtensionDependency extension = DependencyUtils.getExtensionInfoOrNull(project, artifact); + // If this artifact represents an extension: + if (extension != null) { + // Add to master list of accepted extensions: + allExtensions.put(extension.getExtensionId(), extension); + for (Dependency conditionalDep : extension.getConditionalDependencies()) { + // If the dependency is not present yet in the graph, queue it for resolution later + if (!exists(conditionalDep)) { + queueConditionalDependency(extension, conditionalDep); + } + } + } + } + } + + private boolean resolveConditionalDependency(Dependency conditionalDep) { + + final Configuration conditionalDeps = createConditionalDependenciesConfiguration(project, conditionalDep); + Set resolvedArtifacts = conditionalDeps.getResolvedConfiguration().getResolvedArtifacts(); + + boolean satisfied = false; + // Resolved artifacts don't have great linking back to the original artifact, so I think + // this loop is trying to find the artifact that represents the original conditional + // dependency + for (ResolvedArtifact artifact : resolvedArtifacts) { + if (conditionalDep.getName().equals(artifact.getName()) + && conditionalDep.getVersion().equals(artifact.getModuleVersion().getId().getVersion()) + && artifact.getModuleVersion().getId().getGroup().equals(conditionalDep.getGroup())) { + // Once the dependency is found, reload the extension info from within + final ExtensionDependency extensionDependency = DependencyUtils.getExtensionInfoOrNull(project, artifact); + // Now check if this conditional dependency is resolved given the latest graph evolution + if (extensionDependency != null && (extensionDependency.getDependencyConditions().isEmpty() + || exist(extensionDependency.getDependencyConditions()))) { + satisfied = true; + enableConditionalDependency(extensionDependency.getExtensionId()); + break; + } + } + } + + // No resolution (yet); give up. + if (!satisfied) { + return false; + } + + // The conditional dependency resolved! Let's now add all of /its/ dependencies + for (ResolvedArtifact artifact : resolvedArtifacts) { + // First add the artifact to the master list + existingArtifacts.add(getKey(artifact)); + ExtensionDependency extensionDependency = DependencyUtils.getExtensionInfoOrNull(project, artifact); + if (extensionDependency == null) { + continue; + } + // If this artifact represents an extension, mark this one as a conditional extension + extensionDependency.setConditional(true); + // Add to the master list of accepted extensions + allExtensions.put(extensionDependency.getExtensionId(), extensionDependency); + for (Dependency cd : extensionDependency.getConditionalDependencies()) { + // Add any unsatisfied/unresolved conditional dependencies of this dependency to the queue + if (!exists(cd)) { + queueConditionalDependency(extensionDependency, cd); + } + } + } + return satisfied; + } + + private void queueConditionalDependency(ExtensionDependency extension, Dependency conditionalDep) { + // 1. Add to master list of unresolved/unsatisfied dependencies + // 2. Add map entry to link dependency to extension + featureVariants.computeIfAbsent(getFeatureKey(conditionalDep), k -> { + unsatisfiedConditionalDeps.add(conditionalDep); + return new HashSet<>(); + }).add(extension); + } + + private Configuration createConditionalDependenciesConfiguration(Project project, Dependency conditionalDep) { + /* + Configuration conditionalDepConfiguration = project.getConfigurations() + .detachedConfiguration() + .extendsFrom(enforcedPlatforms); + */ + Configuration conditionalDepConfiguration = project.getConfigurations().detachedConfiguration(); + enforcedPlatforms.getExcludeRules().forEach(rule -> { + conditionalDepConfiguration.exclude(Map.of( + "group", rule.getGroup(), + "module", rule.getModule())); + }); + enforcedPlatforms.getAllDependencies().forEach(dependency -> { + conditionalDepConfiguration.getDependencies().add(dependency); + }); + conditionalDepConfiguration.getDependencies().add(conditionalDep); + return conditionalDepConfiguration; + } + + private void enableConditionalDependency(ModuleVersionIdentifier dependency) { + final Set extensions = featureVariants.remove(getFeatureKey(dependency)); + if (extensions == null) { + return; + } + extensions.forEach(e -> e.importConditionalDependency(project.getDependencies(), dependency)); + } + + private boolean exist(List dependencies) { + return existingArtifacts.containsAll(dependencies); + } + + private boolean exists(Dependency dependency) { + return existingArtifacts + .contains(ArtifactKey.of(dependency.getGroup(), dependency.getName(), null, ArtifactCoords.TYPE_JAR)); + } + + public boolean exists(ExtensionDependency dependency) { + return existingArtifacts + .contains(ArtifactKey.of(dependency.getGroup(), dependency.getName(), null, ArtifactCoords.TYPE_JAR)); + } + + private static GACT getFeatureKey(ModuleVersionIdentifier version) { + return new GACT(version.getGroup(), version.getName()); + } + + private static GACT getFeatureKey(Dependency version) { + return new GACT(version.getGroup(), version.getName()); + } + + private static ArtifactKey getKey(ResolvedArtifact a) { + return ArtifactKey.of(a.getModuleVersion().getId().getGroup(), a.getName(), a.getClassifier(), a.getType()); + } +} diff --git a/instrumentation/quarkus-resteasy-reactive/quarkus3-plugin/src/main/java/io/quarkus/gradle/tooling/GradleApplicationModelBuilder.java b/instrumentation/quarkus-resteasy-reactive/quarkus3-plugin/src/main/java/io/quarkus/gradle/tooling/GradleApplicationModelBuilder.java new file mode 100644 index 000000000000..5f2b35b16019 --- /dev/null +++ b/instrumentation/quarkus-resteasy-reactive/quarkus3-plugin/src/main/java/io/quarkus/gradle/tooling/GradleApplicationModelBuilder.java @@ -0,0 +1,674 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +// Includes work from: +/* + * Copyright Quarkus Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.quarkus.gradle.tooling; + +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.file.FileSystem; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Properties; +import java.util.Set; + +import org.gradle.api.Project; +import org.gradle.api.artifacts.Configuration; +import org.gradle.api.artifacts.ResolvableDependencies; +import org.gradle.api.artifacts.ResolvedArtifact; +import org.gradle.api.artifacts.ResolvedConfiguration; +import org.gradle.api.artifacts.component.ProjectComponentIdentifier; +import org.gradle.api.file.FileCollection; +import org.gradle.api.file.FileTree; +import org.gradle.api.initialization.IncludedBuild; +import org.gradle.api.tasks.SourceSet; +import org.gradle.api.tasks.SourceSetContainer; +import org.gradle.api.tasks.TaskCollection; +import org.gradle.api.tasks.compile.AbstractCompile; +import org.gradle.api.tasks.testing.Test; +import org.gradle.internal.composite.IncludedBuildInternal; +import org.gradle.language.jvm.tasks.ProcessResources; +import org.gradle.tooling.provider.model.ParameterizedToolingModelBuilder; + +import io.quarkus.bootstrap.BootstrapConstants; +import io.quarkus.bootstrap.model.ApplicationModel; +import io.quarkus.bootstrap.model.ApplicationModelBuilder; +import io.quarkus.bootstrap.model.CapabilityContract; +import io.quarkus.bootstrap.model.PlatformImports; +import io.quarkus.bootstrap.model.gradle.ModelParameter; +import io.quarkus.bootstrap.model.gradle.impl.ModelParameterImpl; +import io.quarkus.bootstrap.workspace.ArtifactSources; +import io.quarkus.bootstrap.workspace.DefaultArtifactSources; +import io.quarkus.bootstrap.workspace.DefaultSourceDir; +import io.quarkus.bootstrap.workspace.DefaultWorkspaceModule; +import io.quarkus.bootstrap.workspace.SourceDir; +import io.quarkus.bootstrap.workspace.WorkspaceModule; +import io.quarkus.fs.util.ZipUtils; +import io.quarkus.gradle.dependency.ApplicationDeploymentClasspathBuilder; +import io.quarkus.maven.dependency.ArtifactCoords; +import io.quarkus.maven.dependency.ArtifactDependency; +import io.quarkus.maven.dependency.ArtifactKey; +import io.quarkus.maven.dependency.DependencyFlags; +import io.quarkus.maven.dependency.GACT; +import io.quarkus.maven.dependency.GACTV; +import io.quarkus.maven.dependency.GAV; +import io.quarkus.maven.dependency.ResolvedDependency; +import io.quarkus.maven.dependency.ResolvedDependencyBuilder; +import io.quarkus.paths.PathCollection; +import io.quarkus.paths.PathList; +import io.quarkus.runtime.LaunchMode; +import io.quarkus.runtime.util.HashUtil; + +public class GradleApplicationModelBuilder implements ParameterizedToolingModelBuilder { + + private static final String MAIN_RESOURCES_OUTPUT = "build/resources/main"; + private static final String CLASSES_OUTPUT = "build/classes"; + + /* @formatter:off */ + private static final byte COLLECT_TOP_EXTENSION_RUNTIME_NODES = 0b001; + private static final byte COLLECT_DIRECT_DEPS = 0b010; + private static final byte COLLECT_RELOADABLE_MODULES = 0b100; + /* @formatter:on */ + + @Override + public boolean canBuild(String modelName) { + return modelName.equals(ApplicationModel.class.getName()); + } + + @Override + public Class getParameterType() { + return ModelParameter.class; + } + + @Override + public Object buildAll(String modelName, Project project) { + final ModelParameterImpl modelParameter = new ModelParameterImpl(); + modelParameter.setMode(LaunchMode.DEVELOPMENT.toString()); + return buildAll(modelName, modelParameter, project); + } + + @Override + public Object buildAll(String modelName, ModelParameter parameter, Project project) { + final LaunchMode mode = LaunchMode.valueOf(parameter.getMode()); + + final ApplicationDeploymentClasspathBuilder classpathBuilder = new ApplicationDeploymentClasspathBuilder(project, + mode); + final Configuration classpathConfig = classpathBuilder.getRuntimeConfiguration(); + final Configuration deploymentConfig = classpathBuilder.getDeploymentConfiguration(); + final PlatformImports platformImports = classpathBuilder.getPlatformImports(); + + boolean workspaceDiscovery = LaunchMode.DEVELOPMENT.equals(mode) || LaunchMode.TEST.equals(mode) + || Boolean.parseBoolean(System.getProperty(BootstrapConstants.QUARKUS_BOOTSTRAP_WORKSPACE_DISCOVERY)); + if (!workspaceDiscovery) { + Object o = project.getProperties().get(BootstrapConstants.QUARKUS_BOOTSTRAP_WORKSPACE_DISCOVERY); + if (o != null) { + workspaceDiscovery = Boolean.parseBoolean(o.toString()); + } + } + + final ResolvedDependency appArtifact = getProjectArtifact(project, workspaceDiscovery); + final ApplicationModelBuilder modelBuilder = new ApplicationModelBuilder() + .setAppArtifact(appArtifact) + .addReloadableWorkspaceModule(appArtifact.getKey()) + .setPlatformImports(platformImports); + + collectDependencies(classpathConfig.getResolvedConfiguration(), classpathConfig.getIncoming(), workspaceDiscovery, + project, modelBuilder, appArtifact.getWorkspaceModule().mutable()); + collectExtensionDependencies(project, deploymentConfig, modelBuilder); + + return modelBuilder.build(); + } + + public static ResolvedDependency getProjectArtifact(Project project, boolean workspaceDiscovery) { + final ResolvedDependencyBuilder appArtifact = ResolvedDependencyBuilder.newInstance() + .setGroupId(project.getGroup().toString()) + .setArtifactId(project.getName()) + .setVersion(project.getVersion().toString()); + + final SourceSetContainer sourceSets = project.getExtensions().getByType(SourceSetContainer.class); + final WorkspaceModule.Mutable mainModule = WorkspaceModule.builder() + .setModuleId(new GAV(appArtifact.getGroupId(), appArtifact.getArtifactId(), appArtifact.getVersion())) + .setModuleDir(project.getProjectDir().toPath()) + .setBuildDir(project.getBuildDir().toPath()) + .setBuildFile(project.getBuildFile().toPath()); + + initProjectModule(project, mainModule, sourceSets.getByName(SourceSet.MAIN_SOURCE_SET_NAME), ArtifactSources.MAIN); + if (workspaceDiscovery) { + final TaskCollection testTasks = project.getTasks().withType(Test.class); + if (!testTasks.isEmpty()) { + final Map sourceSetsByClassesDir = new HashMap<>(); + sourceSets.forEach(s -> { + s.getOutput().getClassesDirs().forEach(d -> { + if (d.exists()) { + sourceSetsByClassesDir.put(d, s); + } + }); + }); + testTasks.forEach(t -> { + if (t.getEnabled()) { + t.getTestClassesDirs().forEach(d -> { + if (d.exists()) { + final SourceSet sourceSet = sourceSetsByClassesDir.remove(d); + if (sourceSet != null) { + initProjectModule(project, mainModule, sourceSet, + sourceSet.getName().equals(SourceSet.TEST_SOURCE_SET_NAME) + ? ArtifactSources.TEST + : sourceSet.getName()); + } + } + }); + } + }); + } + } + + final PathList.Builder paths = PathList.builder(); + collectDestinationDirs(mainModule.getMainSources().getSourceDirs(), paths); + collectDestinationDirs(mainModule.getMainSources().getResourceDirs(), paths); + + return appArtifact.setWorkspaceModule(mainModule).setResolvedPaths(paths.build()).build(); + } + + private static void collectDestinationDirs(Collection sources, final PathList.Builder paths) { + for (SourceDir src : sources) { + final Path path = src.getOutputDir(); + if (paths.contains(path) || !Files.exists(path)) { + continue; + } + paths.add(path); + } + } + + private void collectExtensionDependencies(Project project, Configuration deploymentConfiguration, + ApplicationModelBuilder modelBuilder) { + final ResolvedConfiguration rc = deploymentConfiguration.getResolvedConfiguration(); + for (ResolvedArtifact a : rc.getResolvedArtifacts()) { + if (a.getId().getComponentIdentifier() instanceof ProjectComponentIdentifier) { + ProjectComponentIdentifier projectComponentIdentifier = (ProjectComponentIdentifier) a.getId() + .getComponentIdentifier(); + var includedBuild = ToolingUtils.includedBuild(project, projectComponentIdentifier); + Project projectDep = null; + if (includedBuild != null) { + projectDep = ToolingUtils.includedBuildProject((IncludedBuildInternal) includedBuild, + projectComponentIdentifier); + } else { + projectDep = project.getRootProject().findProject(projectComponentIdentifier.getProjectPath()); + } + Objects.requireNonNull(projectDep, "project " + projectComponentIdentifier.getProjectPath() + " should exist"); + SourceSetContainer sourceSets = projectDep.getExtensions().getByType(SourceSetContainer.class); + + SourceSet mainSourceSet = sourceSets.getByName(SourceSet.MAIN_SOURCE_SET_NAME); + ResolvedDependencyBuilder dep = modelBuilder.getDependency( + toAppDependenciesKey(a.getModuleVersion().getId().getGroup(), a.getName(), a.getClassifier())); + if (dep == null) { + dep = toDependency(a, mainSourceSet); + modelBuilder.addDependency(dep); + } + dep.setDeploymentCp(); + dep.clearFlag(DependencyFlags.RELOADABLE); + } else if (isDependency(a)) { + ResolvedDependencyBuilder dep = modelBuilder.getDependency( + toAppDependenciesKey(a.getModuleVersion().getId().getGroup(), a.getName(), a.getClassifier())); + if (dep == null) { + dep = toDependency(a); + modelBuilder.addDependency(dep); + } + dep.setDeploymentCp(); + dep.clearFlag(DependencyFlags.RELOADABLE); + } + } + } + + private void collectDependencies(ResolvedConfiguration configuration, ResolvableDependencies dependencies, + boolean workspaceDiscovery, Project project, ApplicationModelBuilder modelBuilder, + WorkspaceModule.Mutable wsModule) { + + final Set resolvedArtifacts = configuration.getResolvedArtifacts(); + // if the number of artifacts is less than the number of files then probably + // the project includes direct file dependencies + // final Set artifactFiles = resolvedArtifacts.size() < configuration.getFiles().size() + final Set artifactFiles = resolvedArtifacts.size() < dependencies.getFiles().getFiles().size() + ? new HashSet<>(resolvedArtifacts.size()) + : null; + + configuration.getFirstLevelModuleDependencies() + .forEach(d -> { + collectDependencies(d, workspaceDiscovery, project, artifactFiles, new HashSet<>(), + modelBuilder, + wsModule, + (byte) (COLLECT_TOP_EXTENSION_RUNTIME_NODES | COLLECT_DIRECT_DEPS | COLLECT_RELOADABLE_MODULES)); + }); + + if (artifactFiles != null) { + // detect FS paths that aren't provided by the resolved artifacts + // for (File f : configuration.getFiles()) { + for (File f : dependencies.getFiles().getFiles()) { + if (artifactFiles.contains(f) || !f.exists()) { + continue; + } + // here we are trying to represent a direct FS path dependency + // as an artifact dependency + // SHA1 hash is used to avoid long file names in the lib dir + final String parentPath = f.getParent(); + final String group = HashUtil.sha1(parentPath == null ? f.getName() : parentPath); + String name = f.getName(); + String type = ArtifactCoords.TYPE_JAR; + if (!f.isDirectory()) { + final int dot = f.getName().lastIndexOf('.'); + if (dot > 0) { + name = f.getName().substring(0, dot); + type = f.getName().substring(dot + 1); + } + } + // hash could be a better way to represent the version + final String version = String.valueOf(f.lastModified()); + final ResolvedDependencyBuilder artifactBuilder = ResolvedDependencyBuilder.newInstance() + .setGroupId(group) + .setArtifactId(name) + .setType(type) + .setVersion(version) + .setResolvedPath(f.toPath()) + .setDirect(true) + .setRuntimeCp(); + processQuarkusDependency(artifactBuilder, modelBuilder); + modelBuilder.addDependency(artifactBuilder); + } + } + } + + private void collectDependencies(org.gradle.api.artifacts.ResolvedDependency resolvedDep, boolean workspaceDiscovery, + Project project, Set artifactFiles, Set processedModules, ApplicationModelBuilder modelBuilder, + WorkspaceModule.Mutable parentModule, + byte flags) { + WorkspaceModule.Mutable projectModule = null; + for (ResolvedArtifact a : resolvedDep.getModuleArtifacts()) { + final ArtifactKey artifactKey = toAppDependenciesKey(a.getModuleVersion().getId().getGroup(), a.getName(), + a.getClassifier()); + if (!isDependency(a) || modelBuilder.getDependency(artifactKey) != null) { + continue; + } + final ArtifactCoords depCoords = toArtifactCoords(a); + final ResolvedDependencyBuilder depBuilder = ResolvedDependencyBuilder.newInstance() + .setCoords(depCoords) + .setRuntimeCp(); + if (isFlagOn(flags, COLLECT_DIRECT_DEPS)) { + depBuilder.setDirect(true); + flags = clearFlag(flags, COLLECT_DIRECT_DEPS); + } + if (parentModule != null) { + parentModule.addDependency(new ArtifactDependency(depCoords)); + } + + PathCollection paths = null; + if (workspaceDiscovery && a.getId().getComponentIdentifier() instanceof ProjectComponentIdentifier) { + + Project projectDep = project.getRootProject().findProject( + ((ProjectComponentIdentifier) a.getId().getComponentIdentifier()).getProjectPath()); + SourceSetContainer sourceSets = projectDep == null ? null + : projectDep.getExtensions().findByType(SourceSetContainer.class); + + final String classifier = a.getClassifier(); + if (classifier == null || classifier.isEmpty()) { + final IncludedBuild includedBuild = ToolingUtils.includedBuild(project.getRootProject(), + (ProjectComponentIdentifier) a.getId().getComponentIdentifier()); + if (includedBuild != null) { + final PathList.Builder pathBuilder = PathList.builder(); + + if (includedBuild instanceof IncludedBuildInternal) { + projectDep = ToolingUtils.includedBuildProject((IncludedBuildInternal) includedBuild, + (ProjectComponentIdentifier) a.getId().getComponentIdentifier()); + } + if (projectDep != null) { + projectModule = initProjectModuleAndBuildPaths(projectDep, a, modelBuilder, depBuilder, + pathBuilder, SourceSet.MAIN_SOURCE_SET_NAME, false); + addSubstitutedProject(pathBuilder, projectDep.getProjectDir()); + } else { + addSubstitutedProject(pathBuilder, includedBuild.getProjectDir()); + } + paths = pathBuilder.build(); + } else if (sourceSets != null) { + final PathList.Builder pathBuilder = PathList.builder(); + projectModule = initProjectModuleAndBuildPaths(projectDep, a, modelBuilder, depBuilder, + pathBuilder, SourceSet.MAIN_SOURCE_SET_NAME, false); + paths = pathBuilder.build(); + } + } else if (sourceSets != null) { + if (SourceSet.TEST_SOURCE_SET_NAME.equals(classifier)) { + final PathList.Builder pathBuilder = PathList.builder(); + projectModule = initProjectModuleAndBuildPaths(projectDep, a, modelBuilder, depBuilder, + pathBuilder, SourceSet.TEST_SOURCE_SET_NAME, true); + paths = pathBuilder.build(); + } else if ("test-fixtures".equals(classifier)) { + final PathList.Builder pathBuilder = PathList.builder(); + projectModule = initProjectModuleAndBuildPaths(projectDep, a, modelBuilder, depBuilder, + pathBuilder, "testFixtures", true); + paths = pathBuilder.build(); + } + } + } + + depBuilder.setResolvedPaths(paths == null ? PathList.of(a.getFile().toPath()) : paths) + .setWorkspaceModule(projectModule); + if (processQuarkusDependency(depBuilder, modelBuilder)) { + if (isFlagOn(flags, COLLECT_TOP_EXTENSION_RUNTIME_NODES)) { + depBuilder.setFlags(DependencyFlags.TOP_LEVEL_RUNTIME_EXTENSION_ARTIFACT); + flags = clearFlag(flags, COLLECT_TOP_EXTENSION_RUNTIME_NODES); + } + flags = clearFlag(flags, COLLECT_RELOADABLE_MODULES); + } + if (!isFlagOn(flags, COLLECT_RELOADABLE_MODULES)) { + depBuilder.clearFlag(DependencyFlags.RELOADABLE); + } + modelBuilder.addDependency(depBuilder); + + if (artifactFiles != null) { + artifactFiles.add(a.getFile()); + } + } + + processedModules.add(ArtifactKey.ga(resolvedDep.getModuleGroup(), resolvedDep.getModuleName())); + for (org.gradle.api.artifacts.ResolvedDependency child : resolvedDep.getChildren()) { + if (!processedModules.contains(new GACT(child.getModuleGroup(), child.getModuleName()))) { + collectDependencies(child, workspaceDiscovery, project, artifactFiles, processedModules, + modelBuilder, projectModule, flags); + } + } + } + + private static String toNonNullClassifier(String resolvedClassifier) { + return resolvedClassifier == null ? ArtifactCoords.DEFAULT_CLASSIFIER : resolvedClassifier; + } + + private WorkspaceModule.Mutable initProjectModuleAndBuildPaths(final Project project, + ResolvedArtifact resolvedArtifact, ApplicationModelBuilder appModel, final ResolvedDependencyBuilder appDep, + PathList.Builder buildPaths, String sourceName, boolean test) { + + appDep.setWorkspaceModule().setReloadable(); + + final WorkspaceModule.Mutable projectModule = appModel.getOrCreateProjectModule( + new GAV(resolvedArtifact.getModuleVersion().getId().getGroup(), resolvedArtifact.getName(), + resolvedArtifact.getModuleVersion().getId().getVersion()), + project.getProjectDir(), + project.getBuildDir()) + .setBuildFile(project.getBuildFile().toPath()); + + final String classifier = toNonNullClassifier(resolvedArtifact.getClassifier()); + SourceSetContainer sourceSets = project.getExtensions().getByType(SourceSetContainer.class); + initProjectModule(project, projectModule, sourceSets.findByName(sourceName), classifier); + + collectDestinationDirs(projectModule.getSources(classifier).getSourceDirs(), buildPaths); + collectDestinationDirs(projectModule.getSources(classifier).getResourceDirs(), buildPaths); + + appModel.addReloadableWorkspaceModule( + ArtifactKey.of(resolvedArtifact.getModuleVersion().getId().getGroup(), resolvedArtifact.getName(), classifier, + ArtifactCoords.TYPE_JAR)); + return projectModule; + } + + private boolean processQuarkusDependency(ResolvedDependencyBuilder artifactBuilder, ApplicationModelBuilder modelBuilder) { + for (Path artifactPath : artifactBuilder.getResolvedPaths()) { + if (!Files.exists(artifactPath) || !artifactBuilder.getType().equals(ArtifactCoords.TYPE_JAR)) { + break; + } + if (Files.isDirectory(artifactPath)) { + return processQuarkusDir(artifactBuilder, artifactPath.resolve(BootstrapConstants.META_INF), modelBuilder); + } else { + try (FileSystem artifactFs = ZipUtils.newFileSystem(artifactPath)) { + return processQuarkusDir(artifactBuilder, artifactFs.getPath(BootstrapConstants.META_INF), modelBuilder); + } catch (IOException e) { + throw new RuntimeException("Failed to process " + artifactPath, e); + } + } + } + return false; + } + + private static boolean processQuarkusDir(ResolvedDependencyBuilder artifactBuilder, Path quarkusDir, + ApplicationModelBuilder modelBuilder) { + if (!Files.exists(quarkusDir)) { + return false; + } + final Path quarkusDescr = quarkusDir.resolve(BootstrapConstants.DESCRIPTOR_FILE_NAME); + if (!Files.exists(quarkusDescr)) { + return false; + } + final Properties extProps = readDescriptor(quarkusDescr); + if (extProps == null) { + return false; + } + artifactBuilder.setRuntimeExtensionArtifact(); + final String extensionCoords = artifactBuilder.toGACTVString(); + modelBuilder.handleExtensionProperties(extProps, extensionCoords); + + final String providesCapabilities = extProps.getProperty(BootstrapConstants.PROP_PROVIDES_CAPABILITIES); + if (providesCapabilities != null) { + modelBuilder + .addExtensionCapabilities(CapabilityContract.of(extensionCoords, providesCapabilities, null)); + } + return true; + } + + private static Properties readDescriptor(final Path path) { + final Properties rtProps; + if (!Files.exists(path)) { + // not a platform artifact + return null; + } + rtProps = new Properties(); + try (BufferedReader reader = Files.newBufferedReader(path)) { + rtProps.load(reader); + } catch (IOException e) { + throw new UncheckedIOException("Failed to load extension description " + path, e); + } + return rtProps; + } + + private static void initProjectModule(Project project, WorkspaceModule.Mutable module, SourceSet sourceSet, + String classifier) { + + if (sourceSet == null) { + return; + } + + final FileCollection allClassesDirs = sourceSet.getOutput().getClassesDirs(); + // some plugins do not add source directories to source sets and they may be missing from sourceSet.getAllJava() + // see https://github.com/quarkusio/quarkus/issues/20755 + + final List sourceDirs = new ArrayList<>(1); + project.getTasks().withType(AbstractCompile.class, t -> { + if (!t.getEnabled()) { + return; + } + final FileTree source = t.getSource(); + if (source.isEmpty()) { + return; + } + final File destDir = t.getDestinationDirectory().getAsFile().get(); + if (!allClassesDirs.contains(destDir)) { + return; + } + source.visit(a -> { + // we are looking for the root dirs containing sources + if (a.getRelativePath().getSegments().length == 1) { + final File srcDir = a.getFile().getParentFile(); + sourceDirs.add(new DefaultSourceDir(srcDir.toPath(), destDir.toPath(), Map.of("compiler", t.getName()))); + } + }); + }); + + // This "try/catch" is needed because of the way the "quarkus-cli" Gradle tests work. Without it, the tests fail. + /* + try { + Class.forName("org.jetbrains.kotlin.gradle.tasks.KotlinJvmCompile"); + project.getTasks().withType(KotlinJvmCompile.class, t -> { + if (!t.getEnabled()) { + return; + } + final FileTree source = t.getSources().getAsFileTree(); + if (source.isEmpty()) { + return; + } + final File destDir = t.getDestinationDirectory().getAsFile().get(); + if (!allClassesDirs.contains(destDir)) { + return; + } + source.visit(a -> { + // we are looking for the root dirs containing sources + if (a.getRelativePath().getSegments().length == 1) { + final File srcDir = a.getFile().getParentFile(); + sourceDirs + .add(new DefaultSourceDir(srcDir.toPath(), destDir.toPath(), Map.of("compiler", t.getName()))); + } + }); + }); + } catch (ClassNotFoundException e) { + // ignore + } + */ + + final LinkedHashMap resourceDirs = new LinkedHashMap<>(1); + final File resourcesOutputDir = sourceSet.getOutput().getResourcesDir(); + project.getTasks().withType(ProcessResources.class, t -> { + if (!t.getEnabled()) { + return; + } + final FileCollection source = t.getSource(); + if (source.isEmpty()) { + return; + } + if (!t.getDestinationDir().equals(resourcesOutputDir)) { + return; + } + final Path destDir = t.getDestinationDir().toPath(); + source.getAsFileTree().visit(a -> { + // we are looking for the root dirs containing sources + if (a.getRelativePath().getSegments().length == 1) { + final File srcDir = a.getFile().getParentFile(); + resourceDirs.put(srcDir, destDir); + } + }); + }); + // there could be a task generating resources + if (resourcesOutputDir.exists() && resourceDirs.isEmpty()) { + sourceSet.getResources().getSrcDirs() + .forEach(srcDir -> resourceDirs.put(srcDir, resourcesOutputDir.toPath())); + } + final List resources = new ArrayList<>(resourceDirs.size()); + for (Map.Entry e : resourceDirs.entrySet()) { + resources.add(new DefaultSourceDir(e.getKey().toPath(), e.getValue())); + } + module.addArtifactSources(new DefaultArtifactSources(classifier, sourceDirs, resources)); + } + + private void addSubstitutedProject(PathList.Builder paths, File projectFile) { + File mainResourceDirectory = new File(projectFile, MAIN_RESOURCES_OUTPUT); + if (mainResourceDirectory.exists()) { + paths.add(mainResourceDirectory.toPath()); + } + File classesOutput = new File(projectFile, CLASSES_OUTPUT); + File[] languageDirectories = classesOutput.listFiles(); + if (languageDirectories != null) { + for (File languageDirectory : languageDirectories) { + if (languageDirectory.isDirectory()) { + for (File sourceSet : languageDirectory.listFiles()) { + if (sourceSet.isDirectory() && sourceSet.getName().equals(SourceSet.MAIN_SOURCE_SET_NAME)) { + paths.add(sourceSet.toPath()); + } + } + } + } + } + } + + private static boolean isFlagOn(byte walkingFlags, byte flag) { + return (walkingFlags & flag) > 0; + } + + private static byte clearFlag(byte flags, byte flag) { + if ((flags & flag) > 0) { + flags ^= flag; + } + return flags; + } + + private static boolean isDependency(ResolvedArtifact a) { + return ArtifactCoords.TYPE_JAR.equalsIgnoreCase(a.getExtension()) || "exe".equalsIgnoreCase(a.getExtension()) || + a.getFile().isDirectory(); + } + + /** + * Creates an instance of Dependency and associates it with the ResolvedArtifact's path + */ + static ResolvedDependencyBuilder toDependency(ResolvedArtifact a, int... flags) { + return toDependency(a, PathList.of(a.getFile().toPath()), null, flags); + } + + static ResolvedDependencyBuilder toDependency(ResolvedArtifact a, SourceSet s) { + PathList.Builder resolvedPathBuilder = PathList.builder(); + + for (File classesDir : s.getOutput().getClassesDirs()) { + if (classesDir.exists()) { + resolvedPathBuilder.add(classesDir.toPath()); + } + } + File resourceDir = s.getOutput().getResourcesDir(); + if (resourceDir != null && resourceDir.exists()) { + resolvedPathBuilder.add(resourceDir.toPath()); + } + + return ResolvedDependencyBuilder + .newInstance() + .setResolvedPaths(resolvedPathBuilder.build()) + .setCoords(toArtifactCoords(a)); + } + + static ResolvedDependencyBuilder toDependency(ResolvedArtifact a, PathCollection paths, DefaultWorkspaceModule module, + int... flags) { + int allFlags = 0; + for (int f : flags) { + allFlags |= f; + } + return ResolvedDependencyBuilder.newInstance() + .setCoords(toArtifactCoords(a)) + .setResolvedPaths(paths) + .setWorkspaceModule(module) + .setFlags(allFlags); + } + + private static ArtifactCoords toArtifactCoords(ResolvedArtifact a) { + final String[] split = a.getModuleVersion().toString().split(":"); + return new GACTV(split[0], split[1], a.getClassifier(), a.getType(), split.length > 2 ? split[2] : null); + } + + private static ArtifactKey toAppDependenciesKey(String groupId, String artifactId, String classifier) { + return new GACT(groupId, artifactId, classifier, ArtifactCoords.TYPE_JAR); + } +} diff --git a/instrumentation/quarkus-resteasy-reactive/quarkus3-plugin/src/main/java/io/quarkus/gradle/tooling/ToolingUtils.java b/instrumentation/quarkus-resteasy-reactive/quarkus3-plugin/src/main/java/io/quarkus/gradle/tooling/ToolingUtils.java new file mode 100644 index 000000000000..6993416b2d71 --- /dev/null +++ b/instrumentation/quarkus-resteasy-reactive/quarkus3-plugin/src/main/java/io/quarkus/gradle/tooling/ToolingUtils.java @@ -0,0 +1,108 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +// Includes work from: +/* + * Copyright Quarkus Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.quarkus.gradle.tooling; + +import java.io.IOException; +import java.io.ObjectOutputStream; +import java.nio.file.Files; +import java.nio.file.Path; + +import org.gradle.api.Project; +import org.gradle.api.Task; +import org.gradle.api.artifacts.ModuleDependency; +import org.gradle.api.artifacts.component.ProjectComponentIdentifier; +import org.gradle.api.attributes.Category; +import org.gradle.api.initialization.IncludedBuild; +import org.gradle.internal.composite.IncludedBuildInternal; + +import io.quarkus.bootstrap.model.ApplicationModel; +import io.quarkus.bootstrap.model.gradle.ModelParameter; +import io.quarkus.bootstrap.model.gradle.impl.ModelParameterImpl; +import io.quarkus.runtime.LaunchMode; + +public class ToolingUtils { + + private static final String DEPLOYMENT_CONFIGURATION_SUFFIX = "Deployment"; + private static final String PLATFORM_CONFIGURATION_SUFFIX = "Platform"; + public static final String DEV_MODE_CONFIGURATION_NAME = "quarkusDev"; + + public static String toDeploymentConfigurationName(String baseConfigurationName) { + return baseConfigurationName + DEPLOYMENT_CONFIGURATION_SUFFIX; + } + + public static String toPlatformConfigurationName(String baseConfigurationName) { + return baseConfigurationName + PLATFORM_CONFIGURATION_SUFFIX; + } + + public static boolean isEnforcedPlatform(ModuleDependency module) { + final Category category = module.getAttributes().getAttribute(Category.CATEGORY_ATTRIBUTE); + return category != null && (Category.ENFORCED_PLATFORM.equals(category.getName()) + || Category.REGULAR_PLATFORM.equals(category.getName())); + } + + public static IncludedBuild includedBuild(final Project project, + final ProjectComponentIdentifier projectComponentIdentifier) { + /* + final String name = projectComponentIdentifier.getBuild().getName(); + for (IncludedBuild ib : project.getRootProject().getGradle().getIncludedBuilds()) { + if (ib.getName().equals(name)) { + return ib; + } + } + */ + final String buildPath = projectComponentIdentifier.getBuild().getBuildPath(); + for (IncludedBuild ib : project.getRootProject().getGradle().getIncludedBuilds()) { + if (((IncludedBuildInternal) ib).getTarget().getBuildIdentifier().getBuildPath().equals(buildPath)) { + return ib; + } + } + return null; + } + + public static Project includedBuildProject(IncludedBuildInternal includedBuild, + final ProjectComponentIdentifier componentIdentifier) { + return includedBuild.getTarget().getMutableModel().getRootProject().findProject( + componentIdentifier.getProjectPath()); + } + + public static Path serializeAppModel(ApplicationModel appModel, Task context, boolean test) throws IOException { + final Path serializedModel = context.getTemporaryDir().toPath() + .resolve("quarkus-app" + (test ? "-test" : "") + "-model.dat"); + try (ObjectOutputStream out = new ObjectOutputStream(Files.newOutputStream(serializedModel))) { + out.writeObject(appModel); + } + return serializedModel; + } + + public static ApplicationModel create(Project project, LaunchMode mode) { + final ModelParameter params = new ModelParameterImpl(); + params.setMode(mode.toString()); + return create(project, params); + } + + public static ApplicationModel create(Project project, ModelParameter params) { + return (ApplicationModel) new GradleApplicationModelBuilder().buildAll(ApplicationModel.class.getName(), params, + project); + } + +} diff --git a/instrumentation/quarkus-resteasy-reactive/quarkus3-plugin/src/main/java/io/quarkus/gradle/tooling/dependency/DependencyUtils.java b/instrumentation/quarkus-resteasy-reactive/quarkus3-plugin/src/main/java/io/quarkus/gradle/tooling/dependency/DependencyUtils.java new file mode 100644 index 000000000000..68008babd8b6 --- /dev/null +++ b/instrumentation/quarkus-resteasy-reactive/quarkus3-plugin/src/main/java/io/quarkus/gradle/tooling/dependency/DependencyUtils.java @@ -0,0 +1,212 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +// Includes work from: +/* + * Copyright Quarkus Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.quarkus.gradle.tooling.dependency; + +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.nio.file.FileSystem; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Properties; + +import org.gradle.api.GradleException; +import org.gradle.api.Project; +import org.gradle.api.artifacts.Configuration; +import org.gradle.api.artifacts.Dependency; +import org.gradle.api.artifacts.ModuleDependency; +import org.gradle.api.artifacts.ModuleVersionIdentifier; +import org.gradle.api.artifacts.ResolvedArtifact; +import org.gradle.api.artifacts.component.ProjectComponentIdentifier; +import org.gradle.api.artifacts.dsl.DependencyHandler; +import org.gradle.api.capabilities.Capability; +import org.gradle.api.tasks.SourceSet; +import org.gradle.api.tasks.SourceSetContainer; +import org.gradle.internal.composite.IncludedBuildInternal; + +import io.quarkus.bootstrap.BootstrapConstants; +import io.quarkus.bootstrap.util.BootstrapUtils; +import io.quarkus.fs.util.ZipUtils; +import io.quarkus.gradle.tooling.ToolingUtils; +import io.quarkus.maven.dependency.ArtifactCoords; +import io.quarkus.maven.dependency.ArtifactKey; +import io.quarkus.maven.dependency.GACTV; + +public class DependencyUtils { + + private static final String COPY_CONFIGURATION_NAME = "quarkusDependency"; + private static final String TEST_FIXTURE_SUFFIX = "-test-fixtures"; + + public static Configuration duplicateConfiguration(Project project, Configuration toDuplicate) { + Configuration configurationCopy = project.getConfigurations().findByName(COPY_CONFIGURATION_NAME); + if (configurationCopy != null) { + project.getConfigurations().remove(configurationCopy); + } + return duplicateConfiguration(project, COPY_CONFIGURATION_NAME, toDuplicate); + } + + public static Configuration duplicateConfiguration(Project project, String name, Configuration toDuplicate) { + final Configuration configurationCopy = project.getConfigurations().create(name); + configurationCopy.getDependencies().addAll(toDuplicate.getAllDependencies()); + configurationCopy.getDependencyConstraints().addAll(toDuplicate.getAllDependencyConstraints()); + return configurationCopy; + } + + public static boolean isTestFixtureDependency(Dependency dependency) { + if (!(dependency instanceof ModuleDependency)) { + return false; + } + ModuleDependency module = (ModuleDependency) dependency; + for (Capability requestedCapability : module.getRequestedCapabilities()) { + if (requestedCapability.getName().endsWith(TEST_FIXTURE_SUFFIX)) { + return true; + } + } + return false; + } + + public static String asDependencyNotation(Dependency dependency) { + return String.join(":", dependency.getGroup(), dependency.getName(), dependency.getVersion()); + } + + public static String asDependencyNotation(ArtifactCoords artifactCoords) { + return String.join(":", artifactCoords.getGroupId(), artifactCoords.getArtifactId(), artifactCoords.getVersion()); + } + + public static ExtensionDependency getExtensionInfoOrNull(Project project, ResolvedArtifact artifact) { + ModuleVersionIdentifier artifactId = artifact.getModuleVersion().getId(); + File artifactFile = artifact.getFile(); + + if (artifact.getId().getComponentIdentifier() instanceof ProjectComponentIdentifier) { + ProjectComponentIdentifier componentIdentifier = ((ProjectComponentIdentifier) artifact.getId() + .getComponentIdentifier()); + Project projectDep = project.getRootProject().findProject( + componentIdentifier.getProjectPath()); + SourceSetContainer sourceSets = projectDep == null ? null + : projectDep.getExtensions().findByType(SourceSetContainer.class); + final String classifier = artifact.getClassifier(); + boolean isIncludedBuild = false; + /* + if ((!componentIdentifier.getBuild().isCurrentBuild() || sourceSets == null) + && (classifier == null || classifier.isEmpty())) { + var includedBuild = ToolingUtils.includedBuild(project, componentIdentifier); + if (includedBuild instanceof IncludedBuildInternal) { + projectDep = ToolingUtils.includedBuildProject((IncludedBuildInternal) includedBuild, componentIdentifier); + sourceSets = projectDep == null ? null : projectDep.getExtensions().findByType(SourceSetContainer.class); + isIncludedBuild = true; + } + } + */ + if (sourceSets != null) { + SourceSet mainSourceSet = sourceSets.findByName(SourceSet.MAIN_SOURCE_SET_NAME); + if (mainSourceSet == null) { + return null; + } + File resourcesDir = mainSourceSet.getOutput().getResourcesDir(); + Path descriptorPath = resourcesDir.toPath().resolve(BootstrapConstants.DESCRIPTOR_PATH); + if (Files.exists(descriptorPath)) { + return loadExtensionInfo(project, descriptorPath, artifactId, projectDep, isIncludedBuild); + } + } + } + + if (!artifactFile.exists()) { + return null; + } + if (artifactFile.isDirectory()) { + Path descriptorPath = artifactFile.toPath().resolve(BootstrapConstants.DESCRIPTOR_PATH); + if (Files.exists(descriptorPath)) { + return loadExtensionInfo(project, descriptorPath, artifactId, null, false); + } + } else if (ArtifactCoords.TYPE_JAR.equals(artifact.getExtension())) { + try (FileSystem artifactFs = ZipUtils.newFileSystem(artifactFile.toPath())) { + Path descriptorPath = artifactFs.getPath(BootstrapConstants.DESCRIPTOR_PATH); + if (Files.exists(descriptorPath)) { + return loadExtensionInfo(project, descriptorPath, artifactId, null, false); + } + } catch (IOException e) { + throw new GradleException("Failed to read " + artifactFile, e); + } + } + return null; + } + + private static ExtensionDependency loadExtensionInfo(Project project, Path descriptorPath, + ModuleVersionIdentifier exentionId, Project extensionProject, boolean isIncludedBuild) { + final Properties extensionProperties = new Properties(); + try (BufferedReader reader = Files.newBufferedReader(descriptorPath)) { + extensionProperties.load(reader); + } catch (IOException e) { + throw new GradleException("Failed to load " + descriptorPath, e); + } + ArtifactCoords deploymentModule = GACTV + .fromString(extensionProperties.getProperty(BootstrapConstants.PROP_DEPLOYMENT_ARTIFACT)); + final List conditionalDependencies; + if (extensionProperties.containsKey(BootstrapConstants.CONDITIONAL_DEPENDENCIES)) { + final String[] deps = BootstrapUtils + .splitByWhitespace(extensionProperties.getProperty(BootstrapConstants.CONDITIONAL_DEPENDENCIES)); + conditionalDependencies = new ArrayList<>(deps.length); + for (String conditionalDep : deps) { + conditionalDependencies.add(create(project.getDependencies(), conditionalDep)); + } + } else { + conditionalDependencies = Collections.emptyList(); + } + + final ArtifactKey[] constraints = BootstrapUtils + .parseDependencyCondition(extensionProperties.getProperty(BootstrapConstants.DEPENDENCY_CONDITION)); + if (isIncludedBuild) { + return new IncludedBuildExtensionDependency(extensionProject, exentionId, deploymentModule, conditionalDependencies, + constraints == null ? Collections.emptyList() : Arrays.asList(constraints)); + } + if (extensionProject != null) { + return new LocalExtensionDependency(extensionProject, exentionId, deploymentModule, conditionalDependencies, + constraints == null ? Collections.emptyList() : Arrays.asList(constraints)); + } + return new ExtensionDependency(exentionId, deploymentModule, conditionalDependencies, + constraints == null ? Collections.emptyList() : Arrays.asList(constraints)); + } + + public static Dependency create(DependencyHandler dependencies, String conditionalDependency) { + final ArtifactCoords dependencyCoords = GACTV.fromString(conditionalDependency); + return dependencies.create(String.join(":", dependencyCoords.getGroupId(), dependencyCoords.getArtifactId(), + dependencyCoords.getVersion())); + } + + public static void addLocalDeploymentDependency(String deploymentConfigurationName, LocalExtensionDependency extension, + DependencyHandler dependencies) { + dependencies.add(deploymentConfigurationName, + dependencies.project(Collections.singletonMap("path", extension.findDeploymentModulePath()))); + } + + public static void requireDeploymentDependency(String deploymentConfigurationName, ExtensionDependency extension, + DependencyHandler dependencies) { + dependencies.add(deploymentConfigurationName, + extension.getDeploymentModule().getGroupId() + ":" + extension.getDeploymentModule().getArtifactId() + ":" + + extension.getDeploymentModule().getVersion()); + } +} diff --git a/instrumentation/quarkus-resteasy-reactive/quarkus3-testing/build.gradle.kts b/instrumentation/quarkus-resteasy-reactive/quarkus3-testing/build.gradle.kts index afd93f30ed41..6623f8f6b3df 100644 --- a/instrumentation/quarkus-resteasy-reactive/quarkus3-testing/build.gradle.kts +++ b/instrumentation/quarkus-resteasy-reactive/quarkus3-testing/build.gradle.kts @@ -1,7 +1,15 @@ +import io.quarkus.bootstrap.model.ApplicationModel +import io.quarkus.bootstrap.model.gradle.impl.ModelParameterImpl +import io.quarkus.bootstrap.util.BootstrapUtils +import io.quarkus.gradle.tooling.GradleApplicationModelBuilder +import io.quarkus.runtime.LaunchMode +import kotlin.io.path.notExists +import kotlin.jvm.java + plugins { id("otel.javaagent-testing") - id("io.quarkus") version "3.0.0.Final" + id("io.opentelemetry.instrumentation.quarkus3") apply false } otelJava { @@ -28,9 +36,39 @@ dependencies { testImplementation("io.quarkus:quarkus-junit5") } -tasks.named("compileJava").configure { - dependsOn(tasks.named("compileQuarkusGeneratedSourcesJava")) +tasks.register("integrationTestClasses") {} + +val quarkusTestBaseRuntimeClasspathConfiguration by configurations.creating { + extendsFrom(configurations["testRuntimeClasspath"]) +} + +val quarkusTestCompileOnlyConfiguration by configurations.creating { +} + +val testModelPath = layout.buildDirectory.file("quarkus-app-test-model.dat").get().asFile.toPath() + +val buildModel = tasks.register("buildModel") { + dependsOn(configurations.named("testRuntimeClasspath")) + + if (testModelPath.notExists()) { + doLast { + val modelParameter = ModelParameterImpl() + modelParameter.mode = LaunchMode.TEST.toString() + val model = GradleApplicationModelBuilder().buildAll( + ApplicationModel::class.java.getName(), + modelParameter, + project + ) + BootstrapUtils.serializeAppModel(model as ApplicationModel?, testModelPath) + } + } + outputs.file(testModelPath) } -tasks.named("sourcesJar").configure { - dependsOn(tasks.named("compileQuarkusGeneratedSourcesJava")) + +tasks { + test { + dependsOn(buildModel) + + systemProperty("quarkus-internal-test.serialized-app-model.path", testModelPath.toString()) + } } diff --git a/instrumentation/r2dbc-1.0/javaagent/build.gradle.kts b/instrumentation/r2dbc-1.0/javaagent/build.gradle.kts index 6c80b8f7aa1c..2e587611db7c 100644 --- a/instrumentation/r2dbc-1.0/javaagent/build.gradle.kts +++ b/instrumentation/r2dbc-1.0/javaagent/build.gradle.kts @@ -36,6 +36,8 @@ tasks { } val testStableSemconv by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath jvmArgs("-Dotel.semconv-stability.opt-in=database") } diff --git a/instrumentation/r2dbc-1.0/library/README.md b/instrumentation/r2dbc-1.0/library/README.md index 361823ee8e4a..b9e17a09bedb 100644 --- a/instrumentation/r2dbc-1.0/library/README.md +++ b/instrumentation/r2dbc-1.0/library/README.md @@ -7,7 +7,7 @@ Provides OpenTelemetry instrumentation for [R2dbc](https://r2dbc.io/). ### Add these dependencies to your project Replace `OPENTELEMETRY_VERSION` with the [latest -release](https://search.maven.org/search?q=g:io.opentelemetry.instrumentation%20AND%20a:opentelemetry-r2dbc-1.0). +release]( https://central.sonatype.com/artifact/io.opentelemetry.instrumentation/opentelemetry-r2dbc-1.0). For Maven, add to your `pom.xml` dependencies: diff --git a/instrumentation/r2dbc-1.0/library/build.gradle.kts b/instrumentation/r2dbc-1.0/library/build.gradle.kts index 729385252749..76592a4ec28b 100644 --- a/instrumentation/r2dbc-1.0/library/build.gradle.kts +++ b/instrumentation/r2dbc-1.0/library/build.gradle.kts @@ -16,6 +16,8 @@ tasks { } val testStableSemconv by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath jvmArgs("-Dotel.semconv-stability.opt-in=database") } diff --git a/instrumentation/rabbitmq-2.7/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/rabbitmq/RabbitMqTest.java b/instrumentation/rabbitmq-2.7/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/rabbitmq/RabbitMqTest.java index 85f8deafe500..4e5b74c2cc0d 100644 --- a/instrumentation/rabbitmq-2.7/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/rabbitmq/RabbitMqTest.java +++ b/instrumentation/rabbitmq-2.7/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/rabbitmq/RabbitMqTest.java @@ -513,7 +513,7 @@ void testSpringRabbit() { void captureMessageHeaderAsSpanAttributes() throws IOException, InterruptedException { String queueName = channel.queueDeclare().getQueue(); Map headers = new java.util.HashMap<>(); - headers.put("test-message-header", "test"); + headers.put("Test_Message_Header", "test"); AMQP.BasicProperties properties = new AMQP.BasicProperties.Builder().headers(headers).build(); channel.basicPublish( "", queueName, properties, "Hello, world!".getBytes(Charset.defaultCharset())); @@ -555,7 +555,7 @@ public void handleDelivery( List verifyHeaders = attrs.get( AttributeKey.stringArrayKey( - "messaging.header.test_message_header")); + "messaging.header.Test_Message_Header")); assertNotNull(verifyHeaders); assertTrue(verifyHeaders.contains("test")); }); @@ -579,7 +579,7 @@ public void handleDelivery( List verifyHeaders = attrs.get( AttributeKey.stringArrayKey( - "messaging.header.test_message_header")); + "messaging.header.Test_Message_Header")); assertNotNull(verifyHeaders); assertTrue(verifyHeaders.contains("test")); }); diff --git a/instrumentation/reactor/reactor-3.1/javaagent/build.gradle.kts b/instrumentation/reactor/reactor-3.1/javaagent/build.gradle.kts index 0fa3c63fd407..e53ec2d92031 100644 --- a/instrumentation/reactor/reactor-3.1/javaagent/build.gradle.kts +++ b/instrumentation/reactor/reactor-3.1/javaagent/build.gradle.kts @@ -59,16 +59,18 @@ testing { tasks { val testStableSemconv by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath jvmArgs("-Dotel.semconv-stability.opt-in=code") } val testBothSemconv by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath jvmArgs("-Dotel.semconv-stability.opt-in=code/dup") } check { - dependsOn(testing.suites) - dependsOn(testStableSemconv) - dependsOn(testBothSemconv) + dependsOn(testing.suites, testStableSemconv, testBothSemconv) } } diff --git a/instrumentation/reactor/reactor-netty/reactor-netty-0.9/javaagent/build.gradle.kts b/instrumentation/reactor/reactor-netty/reactor-netty-0.9/javaagent/build.gradle.kts index a5d9f62bc544..56e96b72fb0f 100644 --- a/instrumentation/reactor/reactor-netty/reactor-netty-0.9/javaagent/build.gradle.kts +++ b/instrumentation/reactor/reactor-netty/reactor-netty-0.9/javaagent/build.gradle.kts @@ -33,6 +33,8 @@ dependencies { tasks { val testConnectionSpan by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath filter { includeTestsMatching("ReactorNettyConnectionSpanTest") } diff --git a/instrumentation/reactor/reactor-netty/reactor-netty-1.0/javaagent/build.gradle.kts b/instrumentation/reactor/reactor-netty/reactor-netty-1.0/javaagent/build.gradle.kts index c54a3abed42e..78829717fe5d 100644 --- a/instrumentation/reactor/reactor-netty/reactor-netty-1.0/javaagent/build.gradle.kts +++ b/instrumentation/reactor/reactor-netty/reactor-netty-1.0/javaagent/build.gradle.kts @@ -45,6 +45,8 @@ dependencies { tasks { val testConnectionSpan by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath filter { includeTestsMatching("ReactorNettyConnectionSpanTest") includeTestsMatching("ReactorNettyClientSslTest") diff --git a/instrumentation/redisson/redisson-3.17/javaagent/build.gradle.kts b/instrumentation/redisson/redisson-3.17/javaagent/build.gradle.kts index 3eabc34c913b..d9e4b881b60d 100644 --- a/instrumentation/redisson/redisson-3.17/javaagent/build.gradle.kts +++ b/instrumentation/redisson/redisson-3.17/javaagent/build.gradle.kts @@ -31,6 +31,8 @@ tasks { } val testStableSemconv by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath jvmArgs("-Dotel.semconv-stability.opt-in=database") } diff --git a/instrumentation/resources/library/build.gradle.kts b/instrumentation/resources/library/build.gradle.kts index 931468c7d94c..bb41a8b31dd9 100644 --- a/instrumentation/resources/library/build.gradle.kts +++ b/instrumentation/resources/library/build.gradle.kts @@ -8,6 +8,7 @@ dependencies { compileOnly("io.opentelemetry:opentelemetry-api-incubator") implementation("io.opentelemetry:opentelemetry-sdk-common") implementation("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure-spi") + compileOnly("io.opentelemetry:opentelemetry-sdk-extension-incubator") implementation("io.opentelemetry.semconv:opentelemetry-semconv") annotationProcessor("com.google.auto.service:auto-service") diff --git a/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/AttributeResourceProvider.java b/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/AttributeResourceProvider.java index 594b69cad2cb..31f4d18c2ec5 100644 --- a/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/AttributeResourceProvider.java +++ b/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/AttributeResourceProvider.java @@ -45,7 +45,6 @@ public AttributeBuilder add(AttributeKey key, Function> ge } private Set> filteredKeys; - private final Map, Function>> attributeGetters = new HashMap<>(); diff --git a/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/JarServiceNameDetector.java b/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/JarServiceNameDetector.java index 7ffaeaa2d170..b661d12917ae 100644 --- a/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/JarServiceNameDetector.java +++ b/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/JarServiceNameDetector.java @@ -5,57 +5,25 @@ package io.opentelemetry.instrumentation.resources; -import static java.util.logging.Level.FINE; - import com.google.auto.service.AutoService; -import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.instrumentation.resources.internal.JarServiceNameResourceExtractor; import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import io.opentelemetry.sdk.autoconfigure.spi.ResourceProvider; import io.opentelemetry.sdk.autoconfigure.spi.internal.ConditionalResourceProvider; import io.opentelemetry.sdk.resources.Resource; import io.opentelemetry.semconv.ServiceAttributes; -import java.nio.file.Path; import java.util.Map; -import java.util.Optional; -import java.util.function.Supplier; -import java.util.logging.Logger; /** - * A {@link ResourceProvider} that will attempt to detect the application name from the jar name. + * A {@link ResourceProvider} that will attempt to detect the service.name from the + * main jar file name. */ @AutoService(ResourceProvider.class) public final class JarServiceNameDetector implements ConditionalResourceProvider { - private static final Logger logger = Logger.getLogger(JarServiceNameDetector.class.getName()); - - private final Supplier> jarPathSupplier; - - @SuppressWarnings("unused") // SPI - public JarServiceNameDetector() { - this(MainJarPathHolder::getJarPath); - } - - private JarServiceNameDetector(Supplier> jarPathSupplier) { - this.jarPathSupplier = jarPathSupplier; - } - - // visible for tests - JarServiceNameDetector(MainJarPathFinder jarPathFinder) { - this(() -> Optional.ofNullable(jarPathFinder.detectJarPath())); - } - @Override public Resource createResource(ConfigProperties config) { - return jarPathSupplier - .get() - .map( - jarPath -> { - String serviceName = getServiceName(jarPath); - logger.log( - FINE, "Auto-detected service name from the jar file name: {0}", serviceName); - return Resource.create(Attributes.of(ServiceAttributes.SERVICE_NAME, serviceName)); - }) - .orElseGet(Resource::empty); + return new JarServiceNameResourceExtractor().extract(); } @Override @@ -67,12 +35,6 @@ public boolean shouldApply(ConfigProperties config, Resource existing) { && "unknown_service:java".equals(existing.getAttribute(ServiceAttributes.SERVICE_NAME)); } - private static String getServiceName(Path jarPath) { - String jarName = jarPath.getFileName().toString(); - int dotIndex = jarName.lastIndexOf("."); - return dotIndex == -1 ? jarName : jarName.substring(0, dotIndex); - } - @Override public int order() { // make it run later than the SpringBootServiceNameDetector diff --git a/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/ManifestResourceProvider.java b/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/ManifestResourceProvider.java index c5cb94af8de6..927f85e8fe60 100644 --- a/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/ManifestResourceProvider.java +++ b/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/ManifestResourceProvider.java @@ -5,79 +5,48 @@ package io.opentelemetry.instrumentation.resources; -import static java.util.logging.Level.WARNING; - import com.google.auto.service.AutoService; +import io.opentelemetry.instrumentation.resources.internal.ManifestResourceExtractor; import io.opentelemetry.sdk.autoconfigure.spi.ResourceProvider; +import io.opentelemetry.sdk.resources.Resource; import io.opentelemetry.semconv.ServiceAttributes; -import java.io.IOException; -import java.nio.file.Path; import java.util.Optional; -import java.util.function.Function; import java.util.function.Supplier; -import java.util.jar.JarFile; -import java.util.jar.Manifest; -import java.util.logging.Logger; /** * A {@link ResourceProvider} that will attempt to detect the service.name and * service.version from META-INF/MANIFEST.MF. */ @AutoService(ResourceProvider.class) -public final class ManifestResourceProvider extends AttributeResourceProvider { - - private static final Logger logger = Logger.getLogger(ManifestResourceProvider.class.getName()); +public final class ManifestResourceProvider extends AttributeResourceProvider { @SuppressWarnings("unused") // SPI public ManifestResourceProvider() { - this(MainJarPathHolder::getJarPath, ManifestResourceProvider::readManifest); + this(() -> new ManifestResourceExtractor().extract()); } - private ManifestResourceProvider( - Supplier> jarPathSupplier, Function> manifestReader) { + // Visible for testing + ManifestResourceProvider(Supplier resourceSupplier) { super( - new AttributeProvider() { + new AttributeProvider() { @Override - public Optional readData() { - return jarPathSupplier.get().flatMap(manifestReader); + public Optional readData() { + return Optional.of(resourceSupplier.get()); } @Override - public void registerAttributes(Builder builder) { + public void registerAttributes(Builder builder) { builder .add( ServiceAttributes.SERVICE_NAME, - manifest -> { - String serviceName = - manifest.getMainAttributes().getValue("Implementation-Title"); - return Optional.ofNullable(serviceName); - }) + r -> Optional.ofNullable(r.getAttribute(ServiceAttributes.SERVICE_NAME))) .add( ServiceAttributes.SERVICE_VERSION, - manifest -> { - String serviceVersion = - manifest.getMainAttributes().getValue("Implementation-Version"); - return Optional.ofNullable(serviceVersion); - }); + r -> Optional.ofNullable(r.getAttribute(ServiceAttributes.SERVICE_VERSION))); } }); } - // Visible for testing - ManifestResourceProvider( - MainJarPathFinder jarPathFinder, Function> manifestReader) { - this(() -> Optional.ofNullable(jarPathFinder.detectJarPath()), manifestReader); - } - - private static Optional readManifest(Path jarPath) { - try (JarFile jarFile = new JarFile(jarPath.toFile(), false)) { - return Optional.of(jarFile.getManifest()); - } catch (IOException exception) { - logger.log(WARNING, "Error reading manifest", exception); - return Optional.empty(); - } - } - @Override public int order() { // make it run later than SpringBootServiceNameDetector diff --git a/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/OsResource.java b/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/OsResource.java index 3de866869cfa..162a8b008dc4 100644 --- a/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/OsResource.java +++ b/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/OsResource.java @@ -89,7 +89,7 @@ private static String getOs(String os) { } else if (os.startsWith("solaris")) { return OsTypeValues.SOLARIS; } else if (os.startsWith("z/os")) { - return OsTypeValues.Z_OS; + return OsTypeValues.ZOS; } return null; } @@ -108,7 +108,7 @@ private static final class OsTypeValues { static final String HPUX = "hpux"; static final String AIX = "aix"; static final String SOLARIS = "solaris"; - static final String Z_OS = "z_os"; + static final String ZOS = "zos"; private OsTypeValues() {} } diff --git a/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/ProcessArguments.java b/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/ProcessArguments.java deleted file mode 100644 index c835f876965c..000000000000 --- a/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/ProcessArguments.java +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.instrumentation.resources; - -final class ProcessArguments { - - static String[] getProcessArguments() { - return new String[0]; - } - - private ProcessArguments() {} -} diff --git a/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/ProcessResource.java b/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/ProcessResource.java index 666c2964d8e2..20470efbfa92 100644 --- a/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/ProcessResource.java +++ b/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/ProcessResource.java @@ -8,6 +8,7 @@ import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.common.AttributesBuilder; +import io.opentelemetry.instrumentation.resources.internal.ProcessArguments; import io.opentelemetry.sdk.resources.Resource; import io.opentelemetry.semconv.SchemaUrls; import java.io.File; diff --git a/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/internal/ContainerResourceComponentProvider.java b/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/internal/ContainerResourceComponentProvider.java index 426f3c2ec9b4..141e4bd91386 100644 --- a/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/internal/ContainerResourceComponentProvider.java +++ b/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/internal/ContainerResourceComponentProvider.java @@ -19,6 +19,6 @@ @AutoService(ComponentProvider.class) public class ContainerResourceComponentProvider extends ResourceComponentProvider { public ContainerResourceComponentProvider() { - super("container", ContainerResource::get); + super("container", p -> ContainerResource.get()); } } diff --git a/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/internal/HostResourceComponentProvider.java b/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/internal/HostResourceComponentProvider.java index dd0b0f519cfd..a65382557539 100644 --- a/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/internal/HostResourceComponentProvider.java +++ b/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/internal/HostResourceComponentProvider.java @@ -21,6 +21,6 @@ @AutoService(ComponentProvider.class) public class HostResourceComponentProvider extends ResourceComponentProvider { public HostResourceComponentProvider() { - super("host", () -> HostResource.get().merge(HostIdResource.get()).merge(OsResource.get())); + super("host", p -> HostResource.get().merge(HostIdResource.get()).merge(OsResource.get())); } } diff --git a/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/internal/JarResourceComponentProvider.java b/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/internal/JarResourceComponentProvider.java new file mode 100644 index 000000000000..938dae452199 --- /dev/null +++ b/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/internal/JarResourceComponentProvider.java @@ -0,0 +1,23 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.resources.internal; + +import com.google.auto.service.AutoService; +import io.opentelemetry.sdk.autoconfigure.spi.internal.ComponentProvider; + +/** + * Declarative config jar resource provider. + * + *

This class is internal and is hence not for public use. Its APIs are unstable and can change + * at any time. + */ +@SuppressWarnings("rawtypes") +@AutoService(ComponentProvider.class) +public class JarResourceComponentProvider extends ResourceComponentProvider { + public JarResourceComponentProvider() { + super("jar", p -> new JarServiceNameResourceExtractor().extract()); + } +} diff --git a/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/internal/JarServiceNameResourceExtractor.java b/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/internal/JarServiceNameResourceExtractor.java new file mode 100644 index 000000000000..d9c09b9a4073 --- /dev/null +++ b/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/internal/JarServiceNameResourceExtractor.java @@ -0,0 +1,63 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.resources.internal; + +import static java.util.logging.Level.FINE; + +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.sdk.resources.Resource; +import io.opentelemetry.semconv.ServiceAttributes; +import java.nio.file.Path; +import java.util.Optional; +import java.util.function.Supplier; +import java.util.logging.Logger; + +/** + * A resource extractor that will attempt to detect the service.name from the main jar + * file name. + * + *

This class is internal and is hence not for public use. Its APIs are unstable and can change + * at any time. + */ +public final class JarServiceNameResourceExtractor { + + private static final Logger logger = + Logger.getLogger(JarServiceNameResourceExtractor.class.getName()); + + private final Supplier> jarPathSupplier; + + public JarServiceNameResourceExtractor() { + this(MainJarPathHolder::getJarPath); + } + + // visible for tests + JarServiceNameResourceExtractor(MainJarPathFinder jarPathFinder) { + this(() -> Optional.ofNullable(jarPathFinder.detectJarPath())); + } + + private JarServiceNameResourceExtractor(Supplier> jarPathSupplier) { + this.jarPathSupplier = jarPathSupplier; + } + + public Resource extract() { + return jarPathSupplier + .get() + .map( + jarPath -> { + String serviceName = getServiceName(jarPath); + logger.log( + FINE, "Auto-detected service name from the jar file name: {0}", serviceName); + return Resource.create(Attributes.of(ServiceAttributes.SERVICE_NAME, serviceName)); + }) + .orElseGet(Resource::empty); + } + + private static String getServiceName(Path jarPath) { + String jarName = jarPath.getFileName().toString(); + int dotIndex = jarName.lastIndexOf("."); + return dotIndex == -1 ? jarName : jarName.substring(0, dotIndex); + } +} diff --git a/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/MainJarPathFinder.java b/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/internal/MainJarPathFinder.java similarity index 97% rename from instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/MainJarPathFinder.java rename to instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/internal/MainJarPathFinder.java index c0c7588698f2..dec6f642deb4 100644 --- a/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/MainJarPathFinder.java +++ b/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/internal/MainJarPathFinder.java @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.instrumentation.resources; +package io.opentelemetry.instrumentation.resources.internal; import java.nio.file.Files; import java.nio.file.InvalidPathException; diff --git a/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/MainJarPathHolder.java b/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/internal/MainJarPathHolder.java similarity index 86% rename from instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/MainJarPathHolder.java rename to instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/internal/MainJarPathHolder.java index c6948d3eca39..ee6df00ca345 100644 --- a/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/MainJarPathHolder.java +++ b/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/internal/MainJarPathHolder.java @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.instrumentation.resources; +package io.opentelemetry.instrumentation.resources.internal; import java.nio.file.Path; import java.util.Optional; diff --git a/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/internal/ManifestResourceComponentProvider.java b/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/internal/ManifestResourceComponentProvider.java new file mode 100644 index 000000000000..0d290adef0a2 --- /dev/null +++ b/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/internal/ManifestResourceComponentProvider.java @@ -0,0 +1,23 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.resources.internal; + +import com.google.auto.service.AutoService; +import io.opentelemetry.sdk.autoconfigure.spi.internal.ComponentProvider; + +/** + * Declarative config manifest resource provider. + * + *

This class is internal and is hence not for public use. Its APIs are unstable and can change + * at any time. + */ +@SuppressWarnings("rawtypes") +@AutoService(ComponentProvider.class) +public class ManifestResourceComponentProvider extends ResourceComponentProvider { + public ManifestResourceComponentProvider() { + super("manifest", p -> new ManifestResourceExtractor().extract()); + } +} diff --git a/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/internal/ManifestResourceExtractor.java b/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/internal/ManifestResourceExtractor.java new file mode 100644 index 000000000000..2fede180f443 --- /dev/null +++ b/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/internal/ManifestResourceExtractor.java @@ -0,0 +1,84 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.resources.internal; + +import static java.util.logging.Level.FINE; + +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.common.AttributesBuilder; +import io.opentelemetry.sdk.resources.Resource; +import io.opentelemetry.semconv.ServiceAttributes; +import java.io.IOException; +import java.nio.file.Path; +import java.util.Optional; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.jar.JarFile; +import java.util.jar.Manifest; +import java.util.logging.Logger; + +/** + * A resource extractor that will attempt to detect the service.name and + * service.version from META-INF/MANIFEST.MF. + * + *

This class is internal and is hence not for public use. Its APIs are unstable and can change + * at any time. + */ +public final class ManifestResourceExtractor { + + private static final Logger logger = Logger.getLogger(ManifestResourceExtractor.class.getName()); + + private final Supplier> jarPathSupplier; + + private final Function> manifestReader; + + public ManifestResourceExtractor() { + this(MainJarPathHolder::getJarPath, ManifestResourceExtractor::readManifest); + } + + // Visible for testing + ManifestResourceExtractor( + MainJarPathFinder jarPathFinder, Function> manifestReader) { + this(() -> Optional.ofNullable(jarPathFinder.detectJarPath()), manifestReader); + } + + private ManifestResourceExtractor( + Supplier> jarPathSupplier, Function> manifestReader) { + this.jarPathSupplier = jarPathSupplier; + this.manifestReader = manifestReader; + } + + public Resource extract() { + return jarPathSupplier + .get() + .flatMap(manifestReader) + .map(manifest -> extract(manifest)) + .orElseGet(Resource::empty); + } + + private static Resource extract(Manifest manifest) { + String serviceName = manifest.getMainAttributes().getValue("Implementation-Title"); + AttributesBuilder builder = Attributes.builder(); + if (serviceName != null) { + builder.put(ServiceAttributes.SERVICE_NAME, serviceName); + } + + String serviceVersion = manifest.getMainAttributes().getValue("Implementation-Version"); + if (serviceVersion != null) { + builder.put(ServiceAttributes.SERVICE_VERSION, serviceVersion); + } + return Resource.create(builder.build()); + } + + private static Optional readManifest(Path jarPath) { + try (JarFile jarFile = new JarFile(jarPath.toFile(), false)) { + return Optional.of(jarFile.getManifest()); + } catch (IOException exception) { + logger.log(FINE, "Error reading manifest", exception); + return Optional.empty(); + } + } +} diff --git a/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/internal/ProcessArguments.java b/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/internal/ProcessArguments.java new file mode 100644 index 000000000000..24ebce1fc1cb --- /dev/null +++ b/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/internal/ProcessArguments.java @@ -0,0 +1,19 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.resources.internal; + +/** + * This class is internal and is hence not for public use. Its APIs are unstable and can change at + * any time. + */ +public final class ProcessArguments { + + public static String[] getProcessArguments() { + return new String[0]; + } + + private ProcessArguments() {} +} diff --git a/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/internal/ProcessResourceComponentProvider.java b/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/internal/ProcessResourceComponentProvider.java index cbdb53ad2c6f..13d26c85503b 100644 --- a/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/internal/ProcessResourceComponentProvider.java +++ b/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/internal/ProcessResourceComponentProvider.java @@ -20,6 +20,6 @@ @AutoService(ComponentProvider.class) public class ProcessResourceComponentProvider extends ResourceComponentProvider { public ProcessResourceComponentProvider() { - super("process", () -> ProcessResource.get().merge(ProcessRuntimeResource.get())); + super("process", p -> ProcessResource.get().merge(ProcessRuntimeResource.get())); } } diff --git a/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/internal/ResourceComponentProvider.java b/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/internal/ResourceComponentProvider.java index 04508560f883..841839ccd03f 100644 --- a/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/internal/ResourceComponentProvider.java +++ b/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/internal/ResourceComponentProvider.java @@ -8,15 +8,15 @@ import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; import io.opentelemetry.sdk.autoconfigure.spi.internal.ComponentProvider; import io.opentelemetry.sdk.resources.Resource; -import java.util.function.Supplier; +import java.util.function.Function; /** Abstract class to simply {@link Resource} {@link ComponentProvider} implementations. */ abstract class ResourceComponentProvider implements ComponentProvider { private final String name; - private final Supplier supplier; + private final Function supplier; - ResourceComponentProvider(String name, Supplier supplier) { + ResourceComponentProvider(String name, Function supplier) { this.name = name; this.supplier = supplier; } @@ -33,6 +33,6 @@ public String getName() { @Override public Resource create(DeclarativeConfigProperties declarativeConfigProperties) { - return supplier.get(); + return supplier.apply(declarativeConfigProperties); } } diff --git a/instrumentation/resources/library/src/main/java9/io/opentelemetry/instrumentation/resources/ProcessArguments.java b/instrumentation/resources/library/src/main/java9/io/opentelemetry/instrumentation/resources/ProcessArguments.java deleted file mode 100644 index b32f6c361524..000000000000 --- a/instrumentation/resources/library/src/main/java9/io/opentelemetry/instrumentation/resources/ProcessArguments.java +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.instrumentation.resources; - -final class ProcessArguments { - - static String[] getProcessArguments() { - return ProcessHandle.current().info().arguments().orElseGet(() -> new String[0]); - } - - private ProcessArguments() {} -} diff --git a/instrumentation/resources/library/src/main/java9/io/opentelemetry/instrumentation/resources/internal/ProcessArguments.java b/instrumentation/resources/library/src/main/java9/io/opentelemetry/instrumentation/resources/internal/ProcessArguments.java new file mode 100644 index 000000000000..2361d1f14080 --- /dev/null +++ b/instrumentation/resources/library/src/main/java9/io/opentelemetry/instrumentation/resources/internal/ProcessArguments.java @@ -0,0 +1,19 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.resources.internal; + +/** + * This class is internal and is hence not for public use. Its APIs are unstable and can change at + * any time. + */ +public final class ProcessArguments { + + public static String[] getProcessArguments() { + return ProcessHandle.current().info().arguments().orElseGet(() -> new String[0]); + } + + private ProcessArguments() {} +} diff --git a/instrumentation/resources/library/src/test/java/io/opentelemetry/instrumentation/resources/ManifestResourceProviderTest.java b/instrumentation/resources/library/src/test/java/io/opentelemetry/instrumentation/resources/ManifestResourceProviderTest.java index 2986337d892a..336850c8e78e 100644 --- a/instrumentation/resources/library/src/test/java/io/opentelemetry/instrumentation/resources/ManifestResourceProviderTest.java +++ b/instrumentation/resources/library/src/test/java/io/opentelemetry/instrumentation/resources/ManifestResourceProviderTest.java @@ -13,11 +13,8 @@ import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import io.opentelemetry.sdk.autoconfigure.spi.internal.DefaultConfigProperties; import io.opentelemetry.sdk.resources.Resource; -import java.io.InputStream; import java.util.Collection; import java.util.Collections; -import java.util.Optional; -import java.util.jar.Manifest; import java.util.stream.Collectors; import java.util.stream.Stream; import org.junit.jupiter.api.DynamicTest; @@ -29,14 +26,14 @@ private static class TestCase { private final String name; private final String expectedName; private final String expectedVersion; - private final InputStream input; + private final Resource input; private final Resource existing; TestCase( String name, String expectedName, String expectedVersion, - InputStream input, + Resource input, Resource existing) { this.name = name; this.expectedName = expectedName; @@ -55,20 +52,17 @@ Collection createResource() { "name ok", "demo", "0.0.1-SNAPSHOT", - openClasspathResource("MANIFEST.MF"), + Resource.create( + Attributes.of(SERVICE_NAME, "demo", SERVICE_VERSION, "0.0.1-SNAPSHOT")), Resource.getDefault()), - new TestCase("name - no resource", null, null, null, Resource.getDefault()), new TestCase( - "name - empty resource", - null, - null, - openClasspathResource("empty-MANIFEST.MF"), - Resource.getDefault()), + "name - empty resource", null, null, Resource.empty(), Resource.getDefault()), new TestCase( "name already detected", null, "0.0.1-SNAPSHOT", - openClasspathResource("MANIFEST.MF"), + Resource.create( + Attributes.of(SERVICE_NAME, "demo", SERVICE_VERSION, "0.0.1-SNAPSHOT")), Resource.create(Attributes.of(SERVICE_NAME, "old")))) .map( t -> @@ -76,20 +70,7 @@ Collection createResource() { t.name, () -> { ManifestResourceProvider provider = - new ManifestResourceProvider( - new MainJarPathFinder( - () -> JarServiceNameDetectorTest.getArgs("app.jar"), - prop -> null, - JarServiceNameDetectorTest::failPath), - p -> { - try { - Manifest manifest = new Manifest(); - manifest.read(t.input); - return Optional.of(manifest); - } catch (Exception e) { - return Optional.empty(); - } - }); + new ManifestResourceProvider(() -> t.input); provider.shouldApply(config, t.existing); Resource resource = provider.createResource(config); @@ -99,8 +80,4 @@ Collection createResource() { })) .collect(Collectors.toList()); } - - private static InputStream openClasspathResource(String resource) { - return ManifestResourceProviderTest.class.getClassLoader().getResourceAsStream(resource); - } } diff --git a/instrumentation/resources/library/src/test/java/io/opentelemetry/instrumentation/resources/OsResourceTest.java b/instrumentation/resources/library/src/test/java/io/opentelemetry/instrumentation/resources/OsResourceTest.java index 27bbcf293500..02f1127621b1 100644 --- a/instrumentation/resources/library/src/test/java/io/opentelemetry/instrumentation/resources/OsResourceTest.java +++ b/instrumentation/resources/library/src/test/java/io/opentelemetry/instrumentation/resources/OsResourceTest.java @@ -121,7 +121,7 @@ void zos() { Resource resource = OsResource.buildResource(); assertThat(resource.getSchemaUrl()).isEqualTo(SchemaUrls.V1_24_0); assertThat(resource.getAttribute(OsIncubatingAttributes.OS_TYPE)) - .isEqualTo(OsIncubatingAttributes.OsTypeIncubatingValues.Z_OS); + .isEqualTo(OsIncubatingAttributes.OsTypeIncubatingValues.ZOS); assertThat(resource.getAttribute(OsIncubatingAttributes.OS_DESCRIPTION)).isNotEmpty(); } diff --git a/instrumentation/resources/library/src/test/java/io/opentelemetry/instrumentation/resources/JarServiceNameDetectorTest.java b/instrumentation/resources/library/src/test/java/io/opentelemetry/instrumentation/resources/internal/JarServiceNameResourceExtractorTest.java similarity index 62% rename from instrumentation/resources/library/src/test/java/io/opentelemetry/instrumentation/resources/JarServiceNameDetectorTest.java rename to instrumentation/resources/library/src/test/java/io/opentelemetry/instrumentation/resources/internal/JarServiceNameResourceExtractorTest.java index ff8ce66848ef..0a9596b0f1ce 100644 --- a/instrumentation/resources/library/src/test/java/io/opentelemetry/instrumentation/resources/JarServiceNameDetectorTest.java +++ b/instrumentation/resources/library/src/test/java/io/opentelemetry/instrumentation/resources/internal/JarServiceNameResourceExtractorTest.java @@ -3,13 +3,12 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.instrumentation.resources; +package io.opentelemetry.instrumentation.resources.internal; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; import static io.opentelemetry.semconv.ServiceAttributes.SERVICE_NAME; import static org.junit.jupiter.params.provider.Arguments.arguments; -import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import io.opentelemetry.sdk.resources.Resource; import java.nio.file.Path; import java.nio.file.Paths; @@ -21,74 +20,74 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; -import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; @ExtendWith(MockitoExtension.class) // todo split JarFileDetectorTest and JarServiceNameDetectorTest -class JarServiceNameDetectorTest { - - @Mock ConfigProperties config; +class JarServiceNameResourceExtractorTest { @Test - void createResource_empty() { + void extractResource_empty() { String[] processArgs = new String[0]; Function getProperty = prop -> null; - Predicate fileExists = JarServiceNameDetectorTest::failPath; - JarServiceNameDetector serviceNameProvider = getDetector(processArgs, getProperty, fileExists); + Predicate fileExists = JarServiceNameResourceExtractorTest::failPath; + JarServiceNameResourceExtractor serviceNameProvider = + getExtractor(processArgs, getProperty, fileExists); - Resource resource = serviceNameProvider.createResource(config); + Resource resource = serviceNameProvider.extract(); assertThat(resource.getAttributes()).isEmpty(); } - private static JarServiceNameDetector getDetector( + private static JarServiceNameResourceExtractor getExtractor( String[] processArgs, Function getProperty, Predicate fileExists) { - return new JarServiceNameDetector( + return new JarServiceNameResourceExtractor( new MainJarPathFinder(() -> processArgs, getProperty, fileExists)); } @Test - void createResource_noJarFileInArgs() { + void extractResource_noJarFileInArgs() { String[] args = new String[] {"-Dtest=42", "-Xmx666m", "-jar"}; - JarServiceNameDetector serviceNameProvider = - getDetector(args, prop -> null, JarServiceNameDetectorTest::failPath); + JarServiceNameResourceExtractor serviceNameProvider = + getExtractor(args, prop -> null, JarServiceNameResourceExtractorTest::failPath); - Resource resource = serviceNameProvider.createResource(config); + Resource resource = serviceNameProvider.extract(); assertThat(resource.getAttributes()).isEmpty(); } @Test - void createResource_processHandleJar() { - JarServiceNameDetector serviceNameProvider = - getDetector(getArgs("my-service.jar"), prop -> null, JarServiceNameDetectorTest::failPath); + void extractResource_processHandleJar() { + JarServiceNameResourceExtractor serviceNameProvider = + getExtractor( + getArgs("my-service.jar"), prop -> null, JarServiceNameResourceExtractorTest::failPath); - Resource resource = serviceNameProvider.createResource(config); + Resource resource = serviceNameProvider.extract(); assertThat(resource.getAttributes()).hasSize(1).containsEntry(SERVICE_NAME, "my-service"); } @Test - void createResource_processHandleJarExtraFlag() { + void extractResource_processHandleJarExtraFlag() { String path = Paths.get("path", "to", "app", "my-service.jar").toString(); - JarServiceNameDetector serviceNameProvider = - getDetector( + JarServiceNameResourceExtractor serviceNameProvider = + getExtractor( new String[] {"-Dtest=42", "-jar", "-Xmx512m", path, "abc", "def"}, prop -> null, - JarServiceNameDetectorTest::failPath); + JarServiceNameResourceExtractorTest::failPath); - Resource resource = serviceNameProvider.createResource(config); + Resource resource = serviceNameProvider.extract(); assertThat(resource.getAttributes()).hasSize(1).containsEntry(SERVICE_NAME, "my-service"); } @Test - void createResource_processHandleJarWithoutExtension() { - JarServiceNameDetector serviceNameProvider = - getDetector(getArgs("my-service"), prop -> null, JarServiceNameDetectorTest::failPath); + void extractResource_processHandleJarWithoutExtension() { + JarServiceNameResourceExtractor serviceNameProvider = + getExtractor( + getArgs("my-service"), prop -> null, JarServiceNameResourceExtractorTest::failPath); - Resource resource = serviceNameProvider.createResource(config); + Resource resource = serviceNameProvider.extract(); assertThat(resource.getAttributes()).hasSize(1).containsEntry(SERVICE_NAME, "my-service"); } @@ -100,15 +99,15 @@ static String[] getArgs(String jarName) { @ParameterizedTest @MethodSource("sunCommandLineArguments") - void createResource_sunCommandLine(String commandLine, Path jarPath) { + void extractResource_sunCommandLine(String commandLine, Path jarPath) { Function getProperty = key -> "sun.java.command".equals(key) ? commandLine : null; Predicate fileExists = jarPath::equals; - JarServiceNameDetector serviceNameProvider = - getDetector(new String[0], getProperty, fileExists); + JarServiceNameResourceExtractor serviceNameProvider = + getExtractor(new String[0], getProperty, fileExists); - Resource resource = serviceNameProvider.createResource(config); + Resource resource = serviceNameProvider.extract(); assertThat(resource.getAttributes()).hasSize(1).containsEntry(SERVICE_NAME, "my-service"); } @@ -116,15 +115,15 @@ void createResource_sunCommandLine(String commandLine, Path jarPath) { // regression test for // https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/7567 @Test - void createResource_sunCommandLineProblematicArgs() { + void extractResource_sunCommandLineProblematicArgs() { Function getProperty = key -> key.equals("sun.java.command") ? "one C:/two" : null; Predicate fileExists = path -> false; - JarServiceNameDetector serviceNameProvider = - getDetector(new String[0], getProperty, fileExists); + JarServiceNameResourceExtractor serviceNameProvider = + getExtractor(new String[0], getProperty, fileExists); - Resource resource = serviceNameProvider.createResource(config); + Resource resource = serviceNameProvider.extract(); assertThat(resource.getAttributes()).isEmpty(); } diff --git a/instrumentation/resources/library/src/test/java/io/opentelemetry/instrumentation/resources/internal/ManifestResourceExtractorTest.java b/instrumentation/resources/library/src/test/java/io/opentelemetry/instrumentation/resources/internal/ManifestResourceExtractorTest.java new file mode 100644 index 000000000000..0aa8544a96cd --- /dev/null +++ b/instrumentation/resources/library/src/test/java/io/opentelemetry/instrumentation/resources/internal/ManifestResourceExtractorTest.java @@ -0,0 +1,76 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.resources.internal; + +import static io.opentelemetry.semconv.ServiceAttributes.SERVICE_NAME; +import static io.opentelemetry.semconv.ServiceAttributes.SERVICE_VERSION; +import static org.assertj.core.api.Assertions.assertThat; + +import io.opentelemetry.sdk.resources.Resource; +import java.io.InputStream; +import java.util.Collection; +import java.util.Optional; +import java.util.jar.Manifest; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import org.junit.jupiter.api.DynamicTest; +import org.junit.jupiter.api.TestFactory; + +class ManifestResourceExtractorTest { + + private static class TestCase { + private final String name; + private final String expectedName; + private final String expectedVersion; + private final InputStream input; + + TestCase(String name, String expectedName, String expectedVersion, InputStream input) { + this.name = name; + this.expectedName = expectedName; + this.expectedVersion = expectedVersion; + this.input = input; + } + } + + @TestFactory + Collection extractResource() { + return Stream.of( + new TestCase("name ok", "demo", "0.0.1-SNAPSHOT", openClasspathResource("MANIFEST.MF")), + new TestCase("name - no resource", null, null, null), + new TestCase( + "name - empty resource", null, null, openClasspathResource("empty-MANIFEST.MF"))) + .map( + t -> + DynamicTest.dynamicTest( + t.name, + () -> { + Resource resource = + new ManifestResourceExtractor( + new MainJarPathFinder( + () -> JarServiceNameResourceExtractorTest.getArgs("app.jar"), + prop -> null, + JarServiceNameResourceExtractorTest::failPath), + p -> { + try { + Manifest manifest = new Manifest(); + manifest.read(t.input); + return Optional.of(manifest); + } catch (Exception e) { + return Optional.empty(); + } + }) + .extract(); + assertThat(resource.getAttribute(SERVICE_NAME)).isEqualTo(t.expectedName); + assertThat(resource.getAttribute(SERVICE_VERSION)) + .isEqualTo(t.expectedVersion); + })) + .collect(Collectors.toList()); + } + + private static InputStream openClasspathResource(String resource) { + return ManifestResourceExtractorTest.class.getClassLoader().getResourceAsStream(resource); + } +} diff --git a/instrumentation/resources/library/src/test/java/io/opentelemetry/instrumentation/resources/internal/DeclarativeConfigTest.java b/instrumentation/resources/library/src/test/java/io/opentelemetry/instrumentation/resources/internal/ResourceDeclarativeConfigTest.java similarity index 97% rename from instrumentation/resources/library/src/test/java/io/opentelemetry/instrumentation/resources/internal/DeclarativeConfigTest.java rename to instrumentation/resources/library/src/test/java/io/opentelemetry/instrumentation/resources/internal/ResourceDeclarativeConfigTest.java index 870c4ec72a52..882dc9af7539 100644 --- a/instrumentation/resources/library/src/test/java/io/opentelemetry/instrumentation/resources/internal/DeclarativeConfigTest.java +++ b/instrumentation/resources/library/src/test/java/io/opentelemetry/instrumentation/resources/internal/ResourceDeclarativeConfigTest.java @@ -22,7 +22,7 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; -class DeclarativeConfigTest { +class ResourceDeclarativeConfigTest { // just to ensure that the test exporters are registered @RegisterExtension @@ -31,7 +31,7 @@ class DeclarativeConfigTest { @Test void endToEnd() { String yaml = - "file_format: \"0.4\"\n" + "file_format: \"1.0-rc.1\"\n" + "tracer_provider:\n" + "resource:\n" + " attributes:\n" diff --git a/instrumentation/rmi/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/rmi/RmiTest.java b/instrumentation/rmi/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/rmi/RmiTest.java index 3ea0b017985a..37db40732287 100644 --- a/instrumentation/rmi/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/rmi/RmiTest.java +++ b/instrumentation/rmi/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/rmi/RmiTest.java @@ -24,7 +24,6 @@ import java.rmi.registry.LocateRegistry; import java.rmi.registry.Registry; import java.rmi.server.UnicastRemoteObject; -import org.assertj.core.api.AbstractAssert; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; @@ -157,8 +156,7 @@ void serviceThrownException() throws Exception { thrown.getClass().getCanonicalName()), equalTo(EXCEPTION_MESSAGE, thrown.getMessage()), satisfies( - EXCEPTION_STACKTRACE, - AbstractAssert::isNotNull))) + EXCEPTION_STACKTRACE, val -> val.isNotNull()))) .hasAttributesSatisfyingExactly( equalTo(RPC_SYSTEM, "java_rmi"), equalTo(RPC_SERVICE, "rmi.app.Greeter"), @@ -177,8 +175,7 @@ void serviceThrownException() throws Exception { thrown.getClass().getCanonicalName()), equalTo(EXCEPTION_MESSAGE, thrown.getMessage()), satisfies( - EXCEPTION_STACKTRACE, - AbstractAssert::isNotNull))) + EXCEPTION_STACKTRACE, val -> val.isNotNull()))) .hasAttributesSatisfyingExactly( equalTo(RPC_SYSTEM, "java_rmi"), equalTo(RPC_SERVICE, "rmi.app.Server"), diff --git a/instrumentation/rocketmq/rocketmq-client/rocketmq-client-4.8/library/README.md b/instrumentation/rocketmq/rocketmq-client/rocketmq-client-4.8/library/README.md index 7844d4d0d286..83c3292802a3 100644 --- a/instrumentation/rocketmq/rocketmq-client/rocketmq-client-4.8/library/README.md +++ b/instrumentation/rocketmq/rocketmq-client/rocketmq-client-4.8/library/README.md @@ -6,7 +6,7 @@ Provides OpenTelemetry instrumentation for [Apache RocketMQ](https://rocketmq.ap ### Add the following dependencies to your project -Replace `OPENTELEMETRY_VERSION` with the [latest release](https://search.maven.org/search?q=g:io.opentelemetry.instrumentation%20AND%20a:opentelemetry-rocketmq-client-4.8). +Replace `OPENTELEMETRY_VERSION` with the [latest release]( https://central.sonatype.com/artifact/io.opentelemetry.instrumentation/opentelemetry-rocketmq-client-4.8). For Maven, add the following to your `pom.xml` dependencies: diff --git a/instrumentation/rocketmq/rocketmq-client/rocketmq-client-4.8/library/src/test/java/io/opentelemetry/instrumentation/rocketmqclient/v4_8/RocketMqClientTest.java b/instrumentation/rocketmq/rocketmq-client/rocketmq-client-4.8/library/src/test/java/io/opentelemetry/instrumentation/rocketmqclient/v4_8/RocketMqClientTest.java index 28b6fcd3c63d..231c3ae76312 100644 --- a/instrumentation/rocketmq/rocketmq-client/rocketmq-client-4.8/library/src/test/java/io/opentelemetry/instrumentation/rocketmqclient/v4_8/RocketMqClientTest.java +++ b/instrumentation/rocketmq/rocketmq-client/rocketmq-client-4.8/library/src/test/java/io/opentelemetry/instrumentation/rocketmqclient/v4_8/RocketMqClientTest.java @@ -31,7 +31,7 @@ void configureMqProducer(DefaultMQProducer producer) { .getDefaultMQProducerImpl() .registerSendMessageHook( RocketMqTelemetry.builder(testing.getOpenTelemetry()) - .setCapturedHeaders(singletonList("test-message-header")) + .setCapturedHeaders(singletonList("Test-Message-Header")) .setCaptureExperimentalSpanAttributes(true) .build() .newTracingSendMessageHook()); @@ -45,7 +45,7 @@ void configureMqPushConsumer(DefaultMQPushConsumer consumer) { .getDefaultMQPushConsumerImpl() .registerConsumeMessageHook( RocketMqTelemetry.builder(testing.getOpenTelemetry()) - .setCapturedHeaders(singletonList("test-message-header")) + .setCapturedHeaders(singletonList("Test-Message-Header")) .setCaptureExperimentalSpanAttributes(true) .build() .newTracingConsumeMessageHook()); diff --git a/instrumentation/rocketmq/rocketmq-client/rocketmq-client-4.8/testing/src/main/java/io/opentelemetry/instrumentation/rocketmqclient/v4_8/AbstractRocketMqClientTest.java b/instrumentation/rocketmq/rocketmq-client/rocketmq-client-4.8/testing/src/main/java/io/opentelemetry/instrumentation/rocketmqclient/v4_8/AbstractRocketMqClientTest.java index 8530f54adba7..d1a40a16f6ff 100644 --- a/instrumentation/rocketmq/rocketmq-client/rocketmq-client-4.8/testing/src/main/java/io/opentelemetry/instrumentation/rocketmqclient/v4_8/AbstractRocketMqClientTest.java +++ b/instrumentation/rocketmq/rocketmq-client/rocketmq-client-4.8/testing/src/main/java/io/opentelemetry/instrumentation/rocketmqclient/v4_8/AbstractRocketMqClientTest.java @@ -377,7 +377,7 @@ void captureMessageHeaderAsSpanAttributes() throws Exception { Message msg = new Message( sharedTopic, "TagA", "Hello RocketMQ".getBytes(Charset.defaultCharset())); - msg.putUserProperty("test-message-header", "test"); + msg.putUserProperty("Test-Message-Header", "test"); SendResult sendResult = producer.send(msg); assertEquals( SendStatus.SEND_OK, sendResult.getSendStatus(), "Send status should be SEND_OK"); @@ -409,7 +409,7 @@ void captureMessageHeaderAsSpanAttributes() throws Exception { "SEND_OK"), equalTo( AttributeKey.stringArrayKey( - "messaging.header.test_message_header"), + "messaging.header.Test_Message_Header"), singletonList("test"))), span -> span.hasName(sharedTopic + " process") @@ -436,7 +436,7 @@ void captureMessageHeaderAsSpanAttributes() throws Exception { val -> val.isInstanceOf(Long.class)), equalTo( AttributeKey.stringArrayKey( - "messaging.header.test_message_header"), + "messaging.header.Test_Message_Header"), singletonList("test"))), span -> span.hasName("messageListener") diff --git a/instrumentation/rocketmq/rocketmq-client/rocketmq-client-5.0/javaagent/build.gradle.kts b/instrumentation/rocketmq/rocketmq-client/rocketmq-client-5.0/javaagent/build.gradle.kts index bb76d29aa456..9948b881a7f7 100644 --- a/instrumentation/rocketmq/rocketmq-client/rocketmq-client-5.0/javaagent/build.gradle.kts +++ b/instrumentation/rocketmq/rocketmq-client/rocketmq-client-5.0/javaagent/build.gradle.kts @@ -19,6 +19,8 @@ dependencies { tasks { val testReceiveSpanDisabled by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath filter { includeTestsMatching("RocketMqClientSuppressReceiveSpanTest") } diff --git a/instrumentation/rocketmq/rocketmq-client/rocketmq-client-5.0/testing/src/main/java/io/opentelemetry/instrumentation/rocketmqclient/v5_0/AbstractRocketMqClientTest.java b/instrumentation/rocketmq/rocketmq-client/rocketmq-client-5.0/testing/src/main/java/io/opentelemetry/instrumentation/rocketmqclient/v5_0/AbstractRocketMqClientTest.java index 541f042aecc9..e036d98ef91c 100644 --- a/instrumentation/rocketmq/rocketmq-client/rocketmq-client-5.0/testing/src/main/java/io/opentelemetry/instrumentation/rocketmqclient/v5_0/AbstractRocketMqClientTest.java +++ b/instrumentation/rocketmq/rocketmq-client/rocketmq-client-5.0/testing/src/main/java/io/opentelemetry/instrumentation/rocketmqclient/v5_0/AbstractRocketMqClientTest.java @@ -348,7 +348,7 @@ public void testCapturedMessageHeaders() throws Throwable { .setTag(tag) .setKeys(keys) .setBody(body) - .addProperty("test-message-header", "test") + .addProperty("Test-Message-Header", "test") .build(); SendReceipt sendReceipt = @@ -372,7 +372,7 @@ public void testCapturedMessageHeaders() throws Throwable { sendReceipt, equalTo( AttributeKey.stringArrayKey( - "messaging.header.test_message_header"), + "messaging.header.Test_Message_Header"), Collections.singletonList("test"))) .hasParent(trace.getSpan(0))); sendSpanData.set(trace.getSpan(1)); @@ -392,7 +392,7 @@ public void testCapturedMessageHeaders() throws Throwable { sendReceipt, equalTo( AttributeKey.stringArrayKey( - "messaging.header.test_message_header"), + "messaging.header.Test_Message_Header"), Collections.singletonList("test"))) // As the child of receive span. .hasParent(trace.getSpan(0)), diff --git a/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/build.gradle.kts b/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/build.gradle.kts index 71360a6a8ba7..a261bb7e75d8 100644 --- a/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/build.gradle.kts +++ b/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/build.gradle.kts @@ -21,6 +21,8 @@ tasks.register("generateDocs", JavaExec::class) { tasks { val testG1 by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath filter { includeTestsMatching("*G1GcMemoryMetricTest*") } @@ -29,6 +31,8 @@ tasks { } val testPS by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath filter { includeTestsMatching("*PsGcMemoryMetricTest*") } @@ -37,6 +41,8 @@ tasks { } val testSerial by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath filter { includeTestsMatching("*SerialGcMemoryMetricTest*") } @@ -53,9 +59,7 @@ tasks { } check { - dependsOn(testG1) - dependsOn(testPS) - dependsOn(testSerial) + dependsOn(testG1, testPS, testSerial) } compileJava { diff --git a/instrumentation/runtime-telemetry/runtime-telemetry-java8/library/README.md b/instrumentation/runtime-telemetry/runtime-telemetry-java8/library/README.md index 4f846856d0e8..3d68c3679aad 100644 --- a/instrumentation/runtime-telemetry/runtime-telemetry-java8/library/README.md +++ b/instrumentation/runtime-telemetry/runtime-telemetry-java8/library/README.md @@ -7,7 +7,7 @@ This module provides JVM runtime metrics as documented in the [semantic conventi ### Add these dependencies to your project Replace `OPENTELEMETRY_VERSION` with the [latest -release](https://search.maven.org/search?q=g:io.opentelemetry.instrumentation%20AND%20a:opentelemetry-runtime-telemetry-java8). +release]( https://central.sonatype.com/artifact/io.opentelemetry.instrumentation/opentelemetry-runtime-telemetry-java8). For Maven, add to your `pom.xml` dependencies: diff --git a/instrumentation/spark-2.3/metadata.yaml b/instrumentation/spark-2.3/metadata.yaml index e4523dffa326..a51d1eaab275 100644 --- a/instrumentation/spark-2.3/metadata.yaml +++ b/instrumentation/spark-2.3/metadata.yaml @@ -1,3 +1,4 @@ description: > This instrumentation does not emit telemetry on its own. Instead, it extracts the HTTP route and - attaches it to SERVER spans and HTTP server metrics. + attaches it to HTTP server spans and HTTP server metrics. +library_link: https://sparkjava.com/ diff --git a/instrumentation/spring/spring-batch-3.0/javaagent/build.gradle.kts b/instrumentation/spring/spring-batch-3.0/javaagent/build.gradle.kts index 521afc23385b..7841db566744 100644 --- a/instrumentation/spring/spring-batch-3.0/javaagent/build.gradle.kts +++ b/instrumentation/spring/spring-batch-3.0/javaagent/build.gradle.kts @@ -26,6 +26,8 @@ dependencies { tasks { val testChunkRootSpan by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath filter { includeTestsMatching("*ChunkRootSpanTest") } @@ -34,6 +36,8 @@ tasks { } val testItemLevelSpan by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath filter { includeTestsMatching("*ItemLevelSpanTest") includeTestsMatching("*CustomSpanEventTest") @@ -54,8 +58,7 @@ tasks { } check { - dependsOn(testChunkRootSpan) - dependsOn(testItemLevelSpan) + dependsOn(testChunkRootSpan, testItemLevelSpan) } withType().configureEach { diff --git a/instrumentation/spring/spring-batch-3.0/metadata.yaml b/instrumentation/spring/spring-batch-3.0/metadata.yaml index ee9d0349927c..011d79a36165 100644 --- a/instrumentation/spring/spring-batch-3.0/metadata.yaml +++ b/instrumentation/spring/spring-batch-3.0/metadata.yaml @@ -1,5 +1,6 @@ disabled_by_default: true description: This instrumentation enables INTERNAL spans for jobs run by the Spring Batch framework. +library_link: https://spring.io/projects/spring-batch configurations: - name: otel.instrumentation.spring-batch.experimental-span-attributes type: boolean diff --git a/instrumentation/spring/spring-boot-actuator-autoconfigure-2.0/metadata.yaml b/instrumentation/spring/spring-boot-actuator-autoconfigure-2.0/metadata.yaml index d0985f49b067..7734bf7b09f2 100644 --- a/instrumentation/spring/spring-boot-actuator-autoconfigure-2.0/metadata.yaml +++ b/instrumentation/spring/spring-boot-actuator-autoconfigure-2.0/metadata.yaml @@ -2,3 +2,4 @@ disabled_by_default: true description: > This instrumentation configures the OpenTelemetry Micrometer bridge to receive metrics from Spring Boot Actuator. It does not produce telemetry on its own. +library_link: https://spring.io/projects/spring-boot diff --git a/instrumentation/spring/spring-boot-autoconfigure/build.gradle.kts b/instrumentation/spring/spring-boot-autoconfigure/build.gradle.kts index 8ac5e0c57289..513b8d89488b 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/build.gradle.kts +++ b/instrumentation/spring/spring-boot-autoconfigure/build.gradle.kts @@ -221,11 +221,12 @@ tasks { } val testStableSemconv by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath jvmArgs("-Dotel.semconv-stability.opt-in=database") } check { - dependsOn(testing.suites) - dependsOn(testStableSemconv) + dependsOn(testing.suites, testStableSemconv) } } diff --git a/instrumentation/spring/spring-boot-autoconfigure/metadata.yaml b/instrumentation/spring/spring-boot-autoconfigure/metadata.yaml index 5a9cc7af0fc6..df014199ac42 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/metadata.yaml +++ b/instrumentation/spring/spring-boot-autoconfigure/metadata.yaml @@ -2,3 +2,4 @@ description: > This instrumentation auto-configures OpenTelemetry instrumentation for spring-web, spring-webmvc, and spring-webflux to instrument Spring Boot applications. It does not produce telemetry on its own. This instrumentation is mostly used as part of the Spring Boot starter. +library_link: https://spring.io/projects/spring-boot diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/thread/ThreadDetailsAutoConfiguration.java b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/thread/ThreadDetailsAutoConfiguration.java new file mode 100644 index 000000000000..91a0049a7dc8 --- /dev/null +++ b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/thread/ThreadDetailsAutoConfiguration.java @@ -0,0 +1,31 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.thread; + +import io.opentelemetry.instrumentation.spring.autoconfigure.internal.ConditionalOnEnabledInstrumentation; +import io.opentelemetry.instrumentation.thread.internal.AddThreadDetailsSpanProcessor; +import io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizerProvider; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * This class is internal and is hence not for public use. Its APIs are unstable and can change at + * any time. + */ +@ConditionalOnEnabledInstrumentation(module = "common.thread-details", enabledByDefault = false) +@Configuration +public class ThreadDetailsAutoConfiguration { + + @Bean + public AutoConfigurationCustomizerProvider threadDetailOtelCustomizer() { + return p -> + p.addTracerProviderCustomizer( + (builder, config) -> { + builder.addSpanProcessor(new AddThreadDetailsSpanProcessor()); + return builder; + }); + } +} diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/properties/SpringConfigProperties.java b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/properties/SpringConfigProperties.java index 05efa521d59a..4f0412ef023b 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/properties/SpringConfigProperties.java +++ b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/properties/SpringConfigProperties.java @@ -7,7 +7,7 @@ import io.opentelemetry.api.internal.ConfigUtil; import io.opentelemetry.exporter.otlp.internal.OtlpConfigUtil; -import io.opentelemetry.instrumentation.resources.ResourceProviderPropertiesCustomizer; +import io.opentelemetry.instrumentation.resources.internal.ResourceProviderPropertiesCustomizer; import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import io.opentelemetry.sdk.autoconfigure.spi.internal.DefaultConfigProperties; import java.time.Duration; diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories b/instrumentation/spring/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories index cb026785d354..aa3f1300ec31 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories +++ b/instrumentation/spring/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories @@ -13,7 +13,8 @@ io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.w io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.scheduling.SpringSchedulingInstrumentationAutoConfiguration,\ io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.runtimemetrics.RuntimeMetricsAutoConfiguration,\ io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.runtimemetrics.Java8RuntimeMetricsProvider,\ -io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.runtimemetrics.Java17RuntimeMetricsProvider +io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.runtimemetrics.Java17RuntimeMetricsProvider,\ +io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.thread.ThreadDetailsAutoConfiguration org.springframework.context.ApplicationListener=\ io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.logging.LogbackAppenderApplicationListener diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/instrumentation/spring/spring-boot-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports index 2392e0c35592..e8c5e8a83d7a 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports +++ b/instrumentation/spring/spring-boot-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -14,3 +14,4 @@ io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.s io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.runtimemetrics.RuntimeMetricsAutoConfiguration io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.runtimemetrics.Java8RuntimeMetricsProvider io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.runtimemetrics.Java17RuntimeMetricsProvider +io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.thread.ThreadDetailsAutoConfiguration diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/OpenTelemetryAutoConfigurationTest.java b/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/OpenTelemetryAutoConfigurationTest.java index b37f074c34ac..1e1a04bb5f10 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/OpenTelemetryAutoConfigurationTest.java +++ b/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/OpenTelemetryAutoConfigurationTest.java @@ -100,7 +100,7 @@ void specialListProperties() { "io.opentelemetry.contrib.aws.resource.LambdaResourceProvider", "io.opentelemetry.contrib.gcp.resource.GCPResourceProvider", "io.opentelemetry.contrib.cloudfoundry.resources.CloudFoundryResourceProvider", - "io.opentelemetry.instrumentation.resources.ResourceProviderPropertiesCustomizerTest$Provider"); + "io.opentelemetry.instrumentation.resources.internal.ResourceProviderPropertiesCustomizerTest$Provider"); })); } diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/properties/SpringConfigPropertiesTest.java b/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/properties/SpringConfigPropertiesTest.java index 9754ccb3b7ad..02433342b1ab 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/properties/SpringConfigPropertiesTest.java +++ b/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/properties/SpringConfigPropertiesTest.java @@ -105,7 +105,6 @@ void mapObjectHeaders(String key) { public static Stream listProperties() { return Stream.of( - Arguments.of("otel.experimental.metrics.view.config", Arrays.asList("a", "b")), Arguments.of("otel.experimental.resource.disabled.keys", Arrays.asList("a", "b")), Arguments.of("otel.propagators", Arrays.asList("baggage", "b3")), Arguments.of("otel.logs.exporter", Collections.singletonList("console")), diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/test/resources/application.yaml b/instrumentation/spring/spring-boot-autoconfigure/src/test/resources/application.yaml index c9485005a6a7..bfbad4f868be 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/src/test/resources/application.yaml +++ b/instrumentation/spring/spring-boot-autoconfigure/src/test/resources/application.yaml @@ -1,8 +1,5 @@ otel: experimental: - metrics: - view: - config: [ a,b ] resource: disabled: keys: [ a,b ] diff --git a/instrumentation/spring/spring-boot-resources/javaagent/src/main/java/io/opentelemetry/instrumentation/spring/resources/SpringBootServiceNameDetector.java b/instrumentation/spring/spring-boot-resources/javaagent/src/main/java/io/opentelemetry/instrumentation/spring/resources/SpringBootServiceNameDetector.java index 0bcdb527095a..c54542fdf9f7 100644 --- a/instrumentation/spring/spring-boot-resources/javaagent/src/main/java/io/opentelemetry/instrumentation/spring/resources/SpringBootServiceNameDetector.java +++ b/instrumentation/spring/spring-boot-resources/javaagent/src/main/java/io/opentelemetry/instrumentation/spring/resources/SpringBootServiceNameDetector.java @@ -9,6 +9,7 @@ import static java.util.logging.Level.FINER; import com.google.auto.service.AutoService; +import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import io.opentelemetry.sdk.autoconfigure.spi.ResourceProvider; import io.opentelemetry.sdk.autoconfigure.spi.internal.ConditionalResourceProvider; @@ -71,7 +72,14 @@ public SpringBootServiceNameDetector() { @Override public Resource createResource(ConfigProperties config) { + return create(); + } + + Resource createResource(DeclarativeConfigProperties config) { + return create(); + } + private Resource create() { logger.log(FINER, "Performing Spring Boot service name auto-detection..."); // Note: The order should be consistent with the order of Spring matching, but noting // that we have "first one wins" while Spring has "last one wins". diff --git a/instrumentation/spring/spring-boot-resources/javaagent/src/main/java/io/opentelemetry/instrumentation/spring/resources/SpringBootServiceVersionDetector.java b/instrumentation/spring/spring-boot-resources/javaagent/src/main/java/io/opentelemetry/instrumentation/spring/resources/SpringBootServiceVersionDetector.java index 865279de3064..f888deda2d3f 100644 --- a/instrumentation/spring/spring-boot-resources/javaagent/src/main/java/io/opentelemetry/instrumentation/spring/resources/SpringBootServiceVersionDetector.java +++ b/instrumentation/spring/spring-boot-resources/javaagent/src/main/java/io/opentelemetry/instrumentation/spring/resources/SpringBootServiceVersionDetector.java @@ -8,6 +8,7 @@ import static java.util.logging.Level.FINE; import com.google.auto.service.AutoService; +import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import io.opentelemetry.sdk.autoconfigure.spi.ResourceProvider; import io.opentelemetry.sdk.resources.Resource; @@ -41,6 +42,14 @@ public SpringBootServiceVersionDetector() { @Override public Resource createResource(ConfigProperties config) { + return create(); + } + + Resource createResource(DeclarativeConfigProperties config) { + return create(); + } + + private Resource create() { return getServiceVersionFromBuildInfo() .map( version -> { diff --git a/instrumentation/spring/spring-boot-resources/javaagent/src/main/java/io/opentelemetry/instrumentation/spring/resources/SpringResourceComponentProvider.java b/instrumentation/spring/spring-boot-resources/javaagent/src/main/java/io/opentelemetry/instrumentation/spring/resources/SpringResourceComponentProvider.java new file mode 100644 index 000000000000..a6eff7234e59 --- /dev/null +++ b/instrumentation/spring/spring-boot-resources/javaagent/src/main/java/io/opentelemetry/instrumentation/spring/resources/SpringResourceComponentProvider.java @@ -0,0 +1,39 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.spring.resources; + +import com.google.auto.service.AutoService; +import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; +import io.opentelemetry.sdk.autoconfigure.spi.internal.ComponentProvider; +import io.opentelemetry.sdk.resources.Resource; + +/** + * Declarative config spring resource provider. + * + *

This class is internal and is hence not for public use. Its APIs are unstable and can change + * at any time. + */ +@SuppressWarnings("rawtypes") +@AutoService(ComponentProvider.class) +public class SpringResourceComponentProvider implements ComponentProvider { + + @Override + public Class getType() { + return Resource.class; + } + + @Override + public String getName() { + return "spring"; + } + + @Override + public Resource create(DeclarativeConfigProperties config) { + return new SpringBootServiceVersionDetector() + .createResource(config) + .merge(new SpringBootServiceNameDetector().createResource(config)); + } +} diff --git a/instrumentation/spring/spring-boot-resources/metadata.yaml b/instrumentation/spring/spring-boot-resources/metadata.yaml index daab5c47f19e..633fc39d1ecd 100644 --- a/instrumentation/spring/spring-boot-resources/metadata.yaml +++ b/instrumentation/spring/spring-boot-resources/metadata.yaml @@ -11,4 +11,4 @@ description: > - Check for application.yml in the current working dir - Check for --spring.application.name program argument (not jvm arg) via ProcessHandle - Check for --spring.application.name program argument via sun.java.command system property - +library_link: https://spring.io/projects/spring-boot diff --git a/instrumentation/spring/spring-cloud-aws-3.0/metadata.yaml b/instrumentation/spring/spring-cloud-aws-3.0/metadata.yaml index e7ea9fa15697..7067687d4dcf 100644 --- a/instrumentation/spring/spring-cloud-aws-3.0/metadata.yaml +++ b/instrumentation/spring/spring-cloud-aws-3.0/metadata.yaml @@ -2,3 +2,4 @@ description: > This instrumentation enhances tracing for Spring Cloud AWS. It augments the existing AWS SDK instrumentation by providing higher-level tracing for SQS operations, capturing details specific to Spring Cloud AWS SQS usage and linking them to the underlying AWS SDK traces. +library_link: https://spring.io/projects/spring-cloud-aws diff --git a/instrumentation/spring/spring-cloud-gateway/spring-cloud-gateway-2.0/metadata.yaml b/instrumentation/spring/spring-cloud-gateway/spring-cloud-gateway-2.0/metadata.yaml index 49a20769279a..768b671f387a 100644 --- a/instrumentation/spring/spring-cloud-gateway/spring-cloud-gateway-2.0/metadata.yaml +++ b/instrumentation/spring/spring-cloud-gateway/spring-cloud-gateway-2.0/metadata.yaml @@ -2,6 +2,7 @@ description: > This instrumentation enhances tracing for Spring Cloud Gateway. It does not generate new telemetry on its own, but rather enriches existing traces produced by other instrumentations like Netty and Spring WebFlux with Spring Cloud Gateway-specific attributes. +library_link: https://github.com/spring-cloud/spring-cloud-gateway configurations: - name: otel.instrumentation.spring-cloud-gateway.experimental-span-attributes type: boolean diff --git a/instrumentation/spring/spring-cloud-gateway/spring-cloud-gateway-2.2/metadata.yaml b/instrumentation/spring/spring-cloud-gateway/spring-cloud-gateway-2.2/metadata.yaml index 3dd1f70808ce..5eeffa941736 100644 --- a/instrumentation/spring/spring-cloud-gateway/spring-cloud-gateway-2.2/metadata.yaml +++ b/instrumentation/spring/spring-cloud-gateway/spring-cloud-gateway-2.2/metadata.yaml @@ -2,6 +2,7 @@ description: > This instrumentation enhances tracing for Spring Cloud Gateway. It does not generate new telemetry on its own, but rather enriches existing traces produced by other instrumentations like Netty and Spring WebFlux with Spring Cloud Gateway-specific attributes. +library_link: https://github.com/spring-cloud/spring-cloud-gateway configurations: - name: otel.instrumentation.spring-cloud-gateway.experimental-span-attributes type: boolean diff --git a/instrumentation/spring/spring-core-2.0/metadata.yaml b/instrumentation/spring/spring-core-2.0/metadata.yaml index 9203a45df486..da3c23a9ab05 100644 --- a/instrumentation/spring/spring-core-2.0/metadata.yaml +++ b/instrumentation/spring/spring-core-2.0/metadata.yaml @@ -3,3 +3,4 @@ description: > within Spring Core. It modifies how tasks are submitted and executed to ensure that spans created by other instrumentations are correctly linked across thread boundaries, rather than generating any new telemetry itself. +library_link: https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/core/package-summary.html diff --git a/instrumentation/spring/spring-data/spring-data-1.8/javaagent/build.gradle.kts b/instrumentation/spring/spring-data/spring-data-1.8/javaagent/build.gradle.kts index c6b29a675883..6f342906acae 100644 --- a/instrumentation/spring/spring-data/spring-data-1.8/javaagent/build.gradle.kts +++ b/instrumentation/spring/spring-data/spring-data-1.8/javaagent/build.gradle.kts @@ -56,6 +56,8 @@ tasks { } val testStableSemconv by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath jvmArgs("-Dotel.semconv-stability.opt-in=database") systemProperty("metadataConfig", "otel.semconv-stability.opt-in=database") } diff --git a/instrumentation/spring/spring-data/spring-data-1.8/metadata.yaml b/instrumentation/spring/spring-data/spring-data-1.8/metadata.yaml index 9d4014d548d8..20ca05bee456 100644 --- a/instrumentation/spring/spring-data/spring-data-1.8/metadata.yaml +++ b/instrumentation/spring/spring-data/spring-data-1.8/metadata.yaml @@ -2,3 +2,4 @@ description: > This instrumentation enhances tracing for Spring Data operations. It works in conjunction with other instrumentations, such as JDBC, to provide additional context and details for database interactions initiated through Spring Data. +library_link: https://spring.io/projects/spring-data diff --git a/instrumentation/spring/spring-data/spring-data-3.0/metadata.yaml b/instrumentation/spring/spring-data/spring-data-3.0/metadata.yaml new file mode 100644 index 000000000000..7b746062f0d1 --- /dev/null +++ b/instrumentation/spring/spring-data/spring-data-3.0/metadata.yaml @@ -0,0 +1 @@ +library_link: https://spring.io/projects/spring-data diff --git a/instrumentation/spring/spring-data/spring-data-3.0/testing/build.gradle.kts b/instrumentation/spring/spring-data/spring-data-3.0/testing/build.gradle.kts index ae7755f0fca2..3460a828ab34 100644 --- a/instrumentation/spring/spring-data/spring-data-3.0/testing/build.gradle.kts +++ b/instrumentation/spring/spring-data/spring-data-3.0/testing/build.gradle.kts @@ -48,11 +48,12 @@ tasks { } val testStableSemconv by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath jvmArgs("-Dotel.semconv-stability.opt-in=database") } check { - dependsOn(testing.suites) - dependsOn(testStableSemconv) + dependsOn(testing.suites, testStableSemconv) } } diff --git a/instrumentation/spring/spring-integration-4.1/javaagent/build.gradle.kts b/instrumentation/spring/spring-integration-4.1/javaagent/build.gradle.kts index a08a119ae605..ad76d268a5a9 100644 --- a/instrumentation/spring/spring-integration-4.1/javaagent/build.gradle.kts +++ b/instrumentation/spring/spring-integration-4.1/javaagent/build.gradle.kts @@ -42,6 +42,8 @@ dependencies { tasks { val testWithRabbitInstrumentation by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath filter { includeTestsMatching("SpringIntegrationAndRabbitTest") } @@ -52,6 +54,8 @@ tasks { } val testWithProducerInstrumentation by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath filter { includeTestsMatching("SpringCloudStreamProducerTest") } @@ -72,8 +76,7 @@ tasks { } check { - dependsOn(testWithRabbitInstrumentation) - dependsOn(testWithProducerInstrumentation) + dependsOn(testWithRabbitInstrumentation, testWithProducerInstrumentation) } withType().configureEach { 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 index 49e7c8c04e22..0976584e6aec 100644 --- 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 @@ -21,7 +21,7 @@ class GlobalInterceptorSpringConfig { @Bean ChannelInterceptor otelInterceptor() { return SpringIntegrationTelemetry.builder(GlobalOpenTelemetry.get()) - .setCapturedHeaders(singletonList("test-message-header")) + .setCapturedHeaders(singletonList("Test-Message-Header")) .build() .newChannelInterceptor(); } diff --git a/instrumentation/spring/spring-integration-4.1/metadata.yaml b/instrumentation/spring/spring-integration-4.1/metadata.yaml index 1e2e86e878f3..d36d6c9a8fe2 100644 --- a/instrumentation/spring/spring-integration-4.1/metadata.yaml +++ b/instrumentation/spring/spring-integration-4.1/metadata.yaml @@ -1,4 +1,5 @@ -description: This instrumentation enables PRODUCER and CONSUMER spans for Spring Integration. +description: This instrumentation enables producer and consumer messaging spans for Spring Integration. +library_link: https://spring.io/projects/spring-integration configurations: - name: otel.instrumentation.spring-integration.producer.enabled type: boolean 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 index 3bc3ae5a5eec..b181d1ed7edc 100644 --- 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 @@ -182,7 +182,7 @@ void captureMessageHeader() { channel.subscribe(messageHandler); channel.send( - MessageBuilder.withPayload("test").setHeader("test-message-header", "test").build()); + MessageBuilder.withPayload("test").setHeader("Test-Message-Header", "test").build()); Message capturedMessage = messageHandler.join(); diff --git a/instrumentation/spring/spring-jms/spring-jms-2.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/jms/v2_0/SpringTemplateTest.java b/instrumentation/spring/spring-jms/spring-jms-2.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/jms/v2_0/SpringTemplateTest.java index ecd8c3391090..517752c3d281 100644 --- a/instrumentation/spring/spring-jms/spring-jms-2.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/jms/v2_0/SpringTemplateTest.java +++ b/instrumentation/spring/spring-jms/spring-jms-2.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/jms/v2_0/SpringTemplateTest.java @@ -224,8 +224,8 @@ void captureMessageHeaderAsSpanAttribute() throws JMSException { new MessagePostProcessor() { @Override public @NotNull Message postProcessMessage(@NotNull Message message) throws JMSException { - message.setStringProperty("test_message_header", "test"); - message.setIntProperty("test_message_int_header", 1234); + message.setStringProperty("Test_Message_Header", "test"); + message.setIntProperty("Test_Message_Int_Header", 1234); return message; } }); diff --git a/instrumentation/spring/spring-jms/spring-jms-2.0/metadata.yaml b/instrumentation/spring/spring-jms/spring-jms-2.0/metadata.yaml index a589b17881a5..843712dc2553 100644 --- a/instrumentation/spring/spring-jms/spring-jms-2.0/metadata.yaml +++ b/instrumentation/spring/spring-jms/spring-jms-2.0/metadata.yaml @@ -1,4 +1,5 @@ -description: This instrumentation enables the generation of CONSUMER spans for Spring JMS. +description: This instrumentation enables the generation of consumer messaging spans for Spring JMS. +library_link: https://docs.spring.io/spring-framework/reference/integration/jms.html configurations: - name: otel.instrumentation.messaging.experimental.receive-telemetry.enabled description: > diff --git a/instrumentation/spring/spring-jms/spring-jms-2.0/testing/src/main/java/io/opentelemetry/instrumentation/spring/jms/v2_0/AbstractJmsTest.java b/instrumentation/spring/spring-jms/spring-jms-2.0/testing/src/main/java/io/opentelemetry/instrumentation/spring/jms/v2_0/AbstractJmsTest.java index 8db8726f79dd..3ffc869e35f7 100644 --- a/instrumentation/spring/spring-jms/spring-jms-2.0/testing/src/main/java/io/opentelemetry/instrumentation/spring/jms/v2_0/AbstractJmsTest.java +++ b/instrumentation/spring/spring-jms/spring-jms-2.0/testing/src/main/java/io/opentelemetry/instrumentation/spring/jms/v2_0/AbstractJmsTest.java @@ -53,11 +53,11 @@ protected List producerAttributeAssertions( if (testHeaders) { attributeAssertions.add( equalTo( - stringArrayKey("messaging.header.test_message_header"), + stringArrayKey("messaging.header.Test_Message_Header"), Collections.singletonList("test"))); attributeAssertions.add( equalTo( - stringArrayKey("messaging.header.test_message_int_header"), + stringArrayKey("messaging.header.Test_Message_Int_Header"), Collections.singletonList("1234"))); } return attributeAssertions; @@ -105,11 +105,11 @@ protected List consumerAttributeAssertions( if (testHeaders) { attributeAssertions.add( equalTo( - stringArrayKey("messaging.header.test_message_header"), + stringArrayKey("messaging.header.Test_Message_Header"), Collections.singletonList("test"))); attributeAssertions.add( equalTo( - stringArrayKey("messaging.header.test_message_int_header"), + stringArrayKey("messaging.header.Test_Message_Int_Header"), Collections.singletonList("1234"))); } return attributeAssertions; diff --git a/instrumentation/spring/spring-jms/spring-jms-6.0/javaagent/build.gradle.kts b/instrumentation/spring/spring-jms/spring-jms-6.0/javaagent/build.gradle.kts index fb2af9e1cc42..3ef0f27fc81f 100644 --- a/instrumentation/spring/spring-jms/spring-jms-6.0/javaagent/build.gradle.kts +++ b/instrumentation/spring/spring-jms/spring-jms-6.0/javaagent/build.gradle.kts @@ -41,6 +41,8 @@ tasks { } val testReceiveSpansDisabled by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath filter { includeTestsMatching("SpringListenerSuppressReceiveSpansTest") } diff --git a/instrumentation/spring/spring-jms/spring-jms-6.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/jms/v6_0/SpringJmsListenerTest.java b/instrumentation/spring/spring-jms/spring-jms-6.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/jms/v6_0/SpringJmsListenerTest.java index 21bc58219857..880911560e7f 100644 --- a/instrumentation/spring/spring-jms/spring-jms-6.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/jms/v6_0/SpringJmsListenerTest.java +++ b/instrumentation/spring/spring-jms/spring-jms-6.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/jms/v6_0/SpringJmsListenerTest.java @@ -104,8 +104,8 @@ void shouldCaptureHeaders(Class configClass) "spring-jms-listener", message, jmsMessage -> { - jmsMessage.setStringProperty("test_message_header", "test"); - jmsMessage.setIntProperty("test_message_int_header", 1234); + jmsMessage.setStringProperty("Test_Message_Header", "test"); + jmsMessage.setIntProperty("Test_Message_Int_Header", 1234); return jmsMessage; })); @@ -129,10 +129,10 @@ void shouldCaptureHeaders(Class configClass) equalTo(MESSAGING_OPERATION, "publish"), satisfies(MESSAGING_MESSAGE_ID, AbstractStringAssert::isNotBlank), equalTo( - stringArrayKey("messaging.header.test_message_header"), + stringArrayKey("messaging.header.Test_Message_Header"), singletonList("test")), equalTo( - stringArrayKey("messaging.header.test_message_int_header"), + stringArrayKey("messaging.header.Test_Message_Int_Header"), singletonList("1234")))), trace -> trace.hasSpansSatisfyingExactly( @@ -146,10 +146,10 @@ void shouldCaptureHeaders(Class configClass) equalTo(MESSAGING_OPERATION, "receive"), satisfies(MESSAGING_MESSAGE_ID, AbstractStringAssert::isNotBlank), equalTo( - stringArrayKey("messaging.header.test_message_header"), + stringArrayKey("messaging.header.Test_Message_Header"), singletonList("test")), equalTo( - stringArrayKey("messaging.header.test_message_int_header"), + stringArrayKey("messaging.header.Test_Message_Int_Header"), singletonList("1234"))), span -> span.hasName("spring-jms-listener process") @@ -161,10 +161,10 @@ void shouldCaptureHeaders(Class configClass) equalTo(MESSAGING_OPERATION, "process"), satisfies(MESSAGING_MESSAGE_ID, AbstractStringAssert::isNotBlank), equalTo( - stringArrayKey("messaging.header.test_message_header"), + stringArrayKey("messaging.header.Test_Message_Header"), singletonList("test")), equalTo( - stringArrayKey("messaging.header.test_message_int_header"), + stringArrayKey("messaging.header.Test_Message_Int_Header"), singletonList("1234"))), span -> span.hasName("consumer").hasParent(trace.getSpan(1)))); } diff --git a/instrumentation/spring/spring-jms/spring-jms-6.0/metadata.yaml b/instrumentation/spring/spring-jms/spring-jms-6.0/metadata.yaml index a589b17881a5..843712dc2553 100644 --- a/instrumentation/spring/spring-jms/spring-jms-6.0/metadata.yaml +++ b/instrumentation/spring/spring-jms/spring-jms-6.0/metadata.yaml @@ -1,4 +1,5 @@ -description: This instrumentation enables the generation of CONSUMER spans for Spring JMS. +description: This instrumentation enables the generation of consumer messaging spans for Spring JMS. +library_link: https://docs.spring.io/spring-framework/reference/integration/jms.html configurations: - name: otel.instrumentation.messaging.experimental.receive-telemetry.enabled description: > diff --git a/instrumentation/spring/spring-kafka-2.7/metadata.yaml b/instrumentation/spring/spring-kafka-2.7/metadata.yaml index a5ad7b76bd14..d1d79df47610 100644 --- a/instrumentation/spring/spring-kafka-2.7/metadata.yaml +++ b/instrumentation/spring/spring-kafka-2.7/metadata.yaml @@ -1,4 +1,5 @@ -description: This instrumentation enables CONSUMER spans for Spring Kafka listeners. +description: This instrumentation enables consumer messaging spans for Spring Kafka listeners. +library_link: https://spring.io/projects/spring-kafka configurations: - name: otel.instrumentation.messaging.experimental.receive-telemetry.enabled description: > diff --git a/instrumentation/spring/spring-pulsar-1.0/metadata.yaml b/instrumentation/spring/spring-pulsar-1.0/metadata.yaml index 284ded7953bf..434d81f4c054 100644 --- a/instrumentation/spring/spring-pulsar-1.0/metadata.yaml +++ b/instrumentation/spring/spring-pulsar-1.0/metadata.yaml @@ -1,4 +1,5 @@ -description: This instrumentation enables CONSUMER spans for Spring Pulsar listeners. +description: This instrumentation enables consumer messaging spans for Spring Pulsar listeners. +library_link: https://spring.io/projects/spring-pulsar configurations: - name: otel.instrumentation.messaging.experimental.receive-telemetry.enabled description: > diff --git a/instrumentation/spring/spring-rabbit-1.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/rabbit/v1_0/SpringRabbitMqTest.java b/instrumentation/spring/spring-rabbit-1.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/rabbit/v1_0/SpringRabbitMqTest.java index 38b05dcb9be0..9dcf62864caf 100644 --- a/instrumentation/spring/spring-rabbit-1.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/rabbit/v1_0/SpringRabbitMqTest.java +++ b/instrumentation/spring/spring-rabbit-1.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/rabbit/v1_0/SpringRabbitMqTest.java @@ -130,7 +130,7 @@ private static List getAssertions( if (testHeaders) { assertions.add( equalTo( - AttributeKey.stringArrayKey("messaging.header.test_message_header"), + AttributeKey.stringArrayKey("messaging.header.Test_Message_Header"), Collections.singletonList("test"))); } return assertions; @@ -151,7 +151,7 @@ public void testContextPropagation(boolean testHeaders) throws Exception { ConsumerConfig.TEST_QUEUE, "test", message -> { - message.getMessageProperties().setHeader("test-message-header", "test"); + message.getMessageProperties().setHeader("Test-Message-Header", "test"); return message; }); } else { diff --git a/instrumentation/spring/spring-rabbit-1.0/metadata.yaml b/instrumentation/spring/spring-rabbit-1.0/metadata.yaml index d2f998d11b51..8e9386a684d9 100644 --- a/instrumentation/spring/spring-rabbit-1.0/metadata.yaml +++ b/instrumentation/spring/spring-rabbit-1.0/metadata.yaml @@ -1,4 +1,5 @@ -description: This instrumentation enables CONSUMER spans for Spring RabbitMQ listeners. +description: This instrumentation enables consumer messaging spans for Spring RabbitMQ listeners. +library_link: https://spring.io/projects/spring-amqp configurations: - name: otel.instrumentation.messaging.experimental.capture-headers description: A comma-separated list of header names to capture as span attributes. diff --git a/instrumentation/spring/spring-rmi-4.0/metadata.yaml b/instrumentation/spring/spring-rmi-4.0/metadata.yaml index 9c6f21ea242d..f26c22bf4483 100644 --- a/instrumentation/spring/spring-rmi-4.0/metadata.yaml +++ b/instrumentation/spring/spring-rmi-4.0/metadata.yaml @@ -1 +1,2 @@ -description: This instrumentation enables CLIENT and SERVER spans for Spring RMI applications. +description: This instrumentation enables RPC client and RPC server spans for Spring RMI applications. +library_link: https://docs.spring.io/spring-framework/docs/4.0.x/javadoc-api/org/springframework/remoting/rmi/package-summary.html diff --git a/instrumentation/spring/spring-scheduling-3.1/javaagent/build.gradle.kts b/instrumentation/spring/spring-scheduling-3.1/javaagent/build.gradle.kts index c120df885619..f8d00346318f 100644 --- a/instrumentation/spring/spring-scheduling-3.1/javaagent/build.gradle.kts +++ b/instrumentation/spring/spring-scheduling-3.1/javaagent/build.gradle.kts @@ -30,9 +30,15 @@ tasks { } val testExperimental by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath jvmArgs("-Dotel.instrumentation.spring-scheduling.experimental-span-attributes=true") systemProperty("metadataConfig", "otel.instrumentation.spring-scheduling.experimental-span-attributes=true") } + + check { + dependsOn(testExperimental) + } } val latestDepTest = findProperty("testLatestDeps") as Boolean diff --git a/instrumentation/spring/spring-scheduling-3.1/metadata.yaml b/instrumentation/spring/spring-scheduling-3.1/metadata.yaml index d246e781446e..61a4a8331f06 100644 --- a/instrumentation/spring/spring-scheduling-3.1/metadata.yaml +++ b/instrumentation/spring/spring-scheduling-3.1/metadata.yaml @@ -1,4 +1,5 @@ description: This instrumentation enables tracing for Spring Scheduling tasks. +library_link: https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/scheduling/package-summary.html configurations: - name: otel.instrumentation.spring-scheduling.experimental-span-attributes description: Adds the experimental span attribute `job.system` with the value `spring_scheduling`. diff --git a/instrumentation/spring/spring-security-config-6.0/metadata.yaml b/instrumentation/spring/spring-security-config-6.0/metadata.yaml index a009f50b527a..01f796b4a337 100644 --- a/instrumentation/spring/spring-security-config-6.0/metadata.yaml +++ b/instrumentation/spring/spring-security-config-6.0/metadata.yaml @@ -3,6 +3,7 @@ description: > attributes, and is only enabled when at least one of the `enduser` configurations is enabled. NOTE: The `enduser` attributes have been deprecated and will be removed in 3.0+ of the java agent. +library_link: https://spring.io/projects/spring-security configurations: - name: otel.instrumentation.common.enduser.id.enabled description: Enables capturing the enduser.id attribute. diff --git a/instrumentation/spring/spring-web/spring-web-3.1/library/README.md b/instrumentation/spring/spring-web/spring-web-3.1/library/README.md index c2145fbc05a1..7176cbab5837 100644 --- a/instrumentation/spring/spring-web/spring-web-3.1/library/README.md +++ b/instrumentation/spring/spring-web/spring-web-3.1/library/README.md @@ -10,7 +10,7 @@ Replace `SPRING_VERSION` with the version of spring you're using. `Minimum version: 3.1` Replace `OPENTELEMETRY_VERSION` with the [latest -release](https://search.maven.org/search?q=g:io.opentelemetry.instrumentation%20AND%20a:opentelemetry-spring-web-3.1). +release]( https://central.sonatype.com/artifact/io.opentelemetry.instrumentation/opentelemetry-spring-web-3.1). For Maven, add to your `pom.xml` dependencies: diff --git a/instrumentation/spring/spring-web/spring-web-3.1/metadata.yaml b/instrumentation/spring/spring-web/spring-web-3.1/metadata.yaml index 969b249906f4..605729f25d17 100644 --- a/instrumentation/spring/spring-web/spring-web-3.1/metadata.yaml +++ b/instrumentation/spring/spring-web/spring-web-3.1/metadata.yaml @@ -2,3 +2,4 @@ description: > This instrumentation provides a library integration that enables capturing HTTP client spans and metrics for Spring's RestTemplate. The agent integration enriches HTTP server spans and metrics with route information. +library_link: https://github.com/spring-projects/spring-framework diff --git a/instrumentation/spring/spring-web/spring-web-6.0/javaagent/build.gradle.kts b/instrumentation/spring/spring-web/spring-web-6.0/javaagent/build.gradle.kts index 6b9afcb97b30..04ec70b85d5a 100644 --- a/instrumentation/spring/spring-web/spring-web-6.0/javaagent/build.gradle.kts +++ b/instrumentation/spring/spring-web/spring-web-6.0/javaagent/build.gradle.kts @@ -14,5 +14,16 @@ muzzle { } dependencies { - compileOnly("org.springframework:spring-web:6.0.0") + library("org.springframework:spring-web:6.0.0") + + testInstrumentation(project(":instrumentation:http-url-connection:javaagent")) +} + +// spring 6 requires java 17 +otelJava { + minJavaVersionSupported.set(JavaVersion.VERSION_17) +} + +tasks.withType().configureEach { + jvmArgs("-Dotel.instrumentation.http.client.emit-experimental-telemetry=true") } diff --git a/instrumentation/spring/spring-web/spring-web-6.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/web/v6_0/RestTemplateInstrumentation.java b/instrumentation/spring/spring-web/spring-web-6.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/web/v6_0/RestTemplateInstrumentation.java new file mode 100644 index 000000000000..1fb1923f69d6 --- /dev/null +++ b/instrumentation/spring/spring-web/spring-web-6.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/web/v6_0/RestTemplateInstrumentation.java @@ -0,0 +1,57 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.spring.web.v6_0; + +import static net.bytebuddy.matcher.ElementMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.takesArgument; + +import io.opentelemetry.context.Scope; +import io.opentelemetry.instrumentation.api.incubator.semconv.http.HttpClientUrlTemplate; +import io.opentelemetry.instrumentation.api.incubator.semconv.net.internal.UrlParser; +import io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge; +import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; +import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; +import net.bytebuddy.asm.Advice; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; +import org.springframework.lang.Nullable; + +public class RestTemplateInstrumentation implements TypeInstrumentation { + + @Override + public ElementMatcher typeMatcher() { + return named("org.springframework.web.client.RestTemplate"); + } + + @Override + public void transform(TypeTransformer transformer) { + transformer.applyAdviceToMethod( + named("doExecute").and(takesArgument(1, String.class)), + this.getClass().getName() + "$UrlTemplateAdvice"); + } + + @SuppressWarnings("unused") + public static class UrlTemplateAdvice { + + @Advice.OnMethodEnter(suppress = Throwable.class) + public static Scope onEnter(@Advice.Argument(1) String uriTemplate) { + if (uriTemplate != null) { + String path = UrlParser.getPath(uriTemplate); + if (path != null) { + return HttpClientUrlTemplate.with(Java8BytecodeBridge.currentContext(), path); + } + } + return null; + } + + @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class) + public static void onExit(@Advice.Enter @Nullable Scope scope) { + if (scope != null) { + scope.close(); + } + } + } +} diff --git a/instrumentation/spring/spring-web/spring-web-6.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/web/v6_0/SpringWebInstrumentationModule.java b/instrumentation/spring/spring-web/spring-web-6.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/web/v6_0/SpringWebInstrumentationModule.java index 6a66e70cd866..09dfb405ee94 100644 --- a/instrumentation/spring/spring-web/spring-web-6.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/web/v6_0/SpringWebInstrumentationModule.java +++ b/instrumentation/spring/spring-web/spring-web-6.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/web/v6_0/SpringWebInstrumentationModule.java @@ -6,7 +6,7 @@ package io.opentelemetry.javaagent.instrumentation.spring.web.v6_0; import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed; -import static java.util.Collections.singletonList; +import static java.util.Arrays.asList; import com.google.auto.service.AutoService; import io.opentelemetry.javaagent.extension.instrumentation.HelperResourceBuilder; @@ -53,6 +53,6 @@ public String getModuleGroup() { @Override public List typeInstrumentations() { - return singletonList(new WebApplicationContextInstrumentation()); + return asList(new WebApplicationContextInstrumentation(), new RestTemplateInstrumentation()); } } diff --git a/instrumentation/spring/spring-web/spring-web-6.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/web/v6_0/WebApplicationContextInstrumentation.java b/instrumentation/spring/spring-web/spring-web-6.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/web/v6_0/WebApplicationContextInstrumentation.java index 69fbc36e1e70..4a944cfa884c 100644 --- a/instrumentation/spring/spring-web/spring-web-6.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/web/v6_0/WebApplicationContextInstrumentation.java +++ b/instrumentation/spring/spring-web/spring-web-6.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/web/v6_0/WebApplicationContextInstrumentation.java @@ -59,7 +59,7 @@ public static class FilterInjectingAdvice { @Advice.OnMethodEnter(suppress = Throwable.class) public static void onEnter(@Advice.Argument(0) ConfigurableListableBeanFactory beanFactory) { - if (beanFactory instanceof BeanDefinitionRegistry + if (beanFactory instanceof BeanDefinitionRegistry beanDefinitionRegistry && !beanFactory.containsBean("otelAutoDispatcherFilter")) { // Explicitly loading classes allows to catch any class-loading issue or deal with cases // where the class is not visible. @@ -83,8 +83,7 @@ public static void onEnter(@Advice.Argument(0) ConfigurableListableBeanFactory b beanDefinition.setScope(SCOPE_SINGLETON); beanDefinition.setBeanClass(clazz); - ((BeanDefinitionRegistry) beanFactory) - .registerBeanDefinition("otelAutoDispatcherFilter", beanDefinition); + beanDefinitionRegistry.registerBeanDefinition("otelAutoDispatcherFilter", beanDefinition); } catch (ClassNotFoundException ignored) { // Ignore } diff --git a/instrumentation/spring/spring-web/spring-web-6.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/web/v6_0/SpringRestTemplateTest.java b/instrumentation/spring/spring-web/spring-web-6.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/web/v6_0/SpringRestTemplateTest.java new file mode 100644 index 000000000000..b0aa2401385a --- /dev/null +++ b/instrumentation/spring/spring-web/spring-web-6.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/web/v6_0/SpringRestTemplateTest.java @@ -0,0 +1,143 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.spring.web.v6_0; + +import static java.util.Collections.singletonList; +import static org.assertj.core.api.Assertions.assertThat; + +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.http.AbstractHttpClientTest; +import io.opentelemetry.instrumentation.testing.junit.http.HttpClientInstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.http.HttpClientResult; +import io.opentelemetry.instrumentation.testing.junit.http.HttpClientTestOptions; +import io.opentelemetry.sdk.trace.data.StatusData; +import java.io.InputStream; +import java.net.URI; +import java.util.Map; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.client.SimpleClientHttpRequestFactory; +import org.springframework.web.client.ResourceAccessException; +import org.springframework.web.client.RestTemplate; + +@SuppressWarnings("PreferJavaTimeOverload") +class SpringRestTemplateTest extends AbstractHttpClientTest> { + + @RegisterExtension + static final InstrumentationExtension testing = HttpClientInstrumentationExtension.forAgent(); + + static RestTemplate restTemplate = buildClient(false); + static RestTemplate restTemplateWithReadTimeout = buildClient(true); + + private static RestTemplate buildClient(boolean readTimeout) { + SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory(); + factory.setConnectTimeout((int) CONNECTION_TIMEOUT.toMillis()); + if (readTimeout) { + factory.setReadTimeout((int) READ_TIMEOUT.toMillis()); + } + return new RestTemplate(factory); + } + + private static RestTemplate getClient(URI uri) { + if (uri.toString().contains("/read-timeout")) { + return restTemplateWithReadTimeout; + } + return restTemplate; + } + + @Override + public HttpEntity buildRequest(String method, URI uri, Map headers) { + HttpHeaders httpHeaders = new HttpHeaders(); + headers.forEach((k, v) -> httpHeaders.put(k, singletonList(v))); + return new HttpEntity<>(httpHeaders); + } + + @Override + public int sendRequest( + HttpEntity request, String method, URI uri, Map headers) + throws Exception { + try { + return getClient(uri) + .exchange(uri.toString(), HttpMethod.valueOf(method), request, String.class) + .getStatusCode() + .value(); + } catch (ResourceAccessException exception) { + throw (Exception) exception.getCause(); + } + } + + @Override + public void sendRequestWithCallback( + HttpEntity request, + String method, + URI uri, + Map headers, + HttpClientResult httpClientResult) { + try { + restTemplate.execute( + uri.toString(), + HttpMethod.valueOf(method), + req -> headers.forEach(req.getHeaders()::add), + response -> { + byte[] buffer = new byte[1024]; + try (InputStream inputStream = response.getBody()) { + while (inputStream.read(buffer) >= 0) {} + } + httpClientResult.complete(response.getStatusCode().value()); + return null; + }); + } catch (ResourceAccessException exception) { + httpClientResult.complete(exception.getCause()); + } + } + + @Override + protected void configure(HttpClientTestOptions.Builder optionsBuilder) { + optionsBuilder.setMaxRedirects(20); + + // no enum value for TEST + optionsBuilder.disableTestNonStandardHttpMethod(); + optionsBuilder.setExpectedClientSpanNameMapper( + (uri, method) -> method + " " + getTemplate(uri)); + optionsBuilder.setExpectedUrlTemplateMapper(SpringRestTemplateTest::getTemplate); + optionsBuilder.setHasUrlTemplate(true); + } + + private static String getTemplate(URI uri) { + String path = uri.getPath(); + if (path.startsWith("/hello/")) { + return "/hello/{name}"; + } + return path; + } + + @Test + void requestWithTemplate() { + URI rootUri = resolveAddress("/"); + URI uri = resolveAddress("/hello/world"); + String method = "GET"; + int responseCode = + restTemplate + .exchange( + rootUri + "hello/{name}", HttpMethod.valueOf(method), null, String.class, "world") + .getStatusCode() + .value(); + + assertThat(responseCode).isEqualTo(200); + + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> + assertClientSpan(span, uri, method, responseCode, null) + .hasNoParent() + .hasStatus(StatusData.unset()), + span -> assertServerSpan(span).hasParent(trace.getSpan(0)))); + } +} diff --git a/instrumentation/spring/spring-web/spring-web-6.0/metadata.yaml b/instrumentation/spring/spring-web/spring-web-6.0/metadata.yaml index 4b044e2b1fdb..03ec81331a22 100644 --- a/instrumentation/spring/spring-web/spring-web-6.0/metadata.yaml +++ b/instrumentation/spring/spring-web/spring-web-6.0/metadata.yaml @@ -1 +1,2 @@ description: This instrumentation enriches HTTP server spans and metrics with route information. +library_link: https://github.com/spring-projects/spring-framework diff --git a/instrumentation/spring/spring-webflux/spring-webflux-5.0/metadata.yaml b/instrumentation/spring/spring-webflux/spring-webflux-5.0/metadata.yaml index 630264ea798f..776f06d5655b 100644 --- a/instrumentation/spring/spring-webflux/spring-webflux-5.0/metadata.yaml +++ b/instrumentation/spring/spring-webflux/spring-webflux-5.0/metadata.yaml @@ -1,6 +1,7 @@ description: > This instrumentation enables HTTP client spans and metrics for Spring WebFlux 5.0. It also optionally enables experimental controller (INTERNAL) spans. +library_link: https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/reactive/package-summary.html configurations: - name: otel.instrumentation.common.experimental.controller-telemetry.enabled description: Enables the creation of experimental controller (INTERNAL) spans. diff --git a/instrumentation/spring/spring-webflux/spring-webflux-5.3/metadata.yaml b/instrumentation/spring/spring-webflux/spring-webflux-5.3/metadata.yaml index 4879f014a113..5dcb53a29913 100644 --- a/instrumentation/spring/spring-webflux/spring-webflux-5.3/metadata.yaml +++ b/instrumentation/spring/spring-webflux/spring-webflux-5.3/metadata.yaml @@ -1,3 +1,4 @@ description: > This instrumentation provides a library integration for the Spring WebFlux WebClient and Webflux server versions 5.3+ that enables HTTP client and server spans and metrics. +library_link: https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/reactive/package-summary.html diff --git a/instrumentation/spring/spring-webmvc/spring-webmvc-3.1/javaagent/build.gradle.kts b/instrumentation/spring/spring-webmvc/spring-webmvc-3.1/javaagent/build.gradle.kts index 0c6ae9682ea2..fbcefb4bd6be 100644 --- a/instrumentation/spring/spring-webmvc/spring-webmvc-3.1/javaagent/build.gradle.kts +++ b/instrumentation/spring/spring-webmvc/spring-webmvc-3.1/javaagent/build.gradle.kts @@ -57,6 +57,8 @@ tasks { } val testExperimental by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath systemProperty("metadataConfig", "otel.instrumentation.spring-webmvc.experimental-span-attributes=true") jvmArgs("-Dotel.instrumentation.spring-webmvc.experimental-span-attributes=true") } diff --git a/instrumentation/spring/spring-webmvc/spring-webmvc-3.1/metadata.yaml b/instrumentation/spring/spring-webmvc/spring-webmvc-3.1/metadata.yaml index 44d7d559e3ed..d5bebf7ccbc6 100644 --- a/instrumentation/spring/spring-webmvc/spring-webmvc-3.1/metadata.yaml +++ b/instrumentation/spring/spring-webmvc/spring-webmvc-3.1/metadata.yaml @@ -1,5 +1,6 @@ description: > This instrumentation enables optional Controller and View (INTERNAL) spans for Spring WebMVC 3.1+. +library_link: https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/servlet/mvc/package-summary.html configurations: - name: otel.instrumentation.spring-webmvc.experimental-span-attributes type: boolean diff --git a/instrumentation/spring/spring-webmvc/spring-webmvc-5.3/library/README.md b/instrumentation/spring/spring-webmvc/spring-webmvc-5.3/library/README.md index 5be13f86ac0e..b724e63e374b 100644 --- a/instrumentation/spring/spring-webmvc/spring-webmvc-5.3/library/README.md +++ b/instrumentation/spring/spring-webmvc/spring-webmvc-5.3/library/README.md @@ -11,7 +11,7 @@ Replace `SPRING_VERSION` with the version of spring you're using. - `Minimum version: 5.3` Replace `OPENTELEMETRY_VERSION` with the [latest -release](https://search.maven.org/search?q=g:io.opentelemetry.instrumentation%20AND%20a:opentelemetry-spring-webmvc-5.3). +release]( https://central.sonatype.com/artifact/io.opentelemetry.instrumentation/opentelemetry-spring-webmvc-5.3). For Maven add the following to your `pom.xml`: diff --git a/instrumentation/spring/spring-webmvc/spring-webmvc-5.3/metadata.yaml b/instrumentation/spring/spring-webmvc/spring-webmvc-5.3/metadata.yaml index 4beb4df459f4..b9d4aabccbf8 100644 --- a/instrumentation/spring/spring-webmvc/spring-webmvc-5.3/metadata.yaml +++ b/instrumentation/spring/spring-webmvc/spring-webmvc-5.3/metadata.yaml @@ -2,3 +2,4 @@ description: > This instrumentation provides a library integration for Spring WebMVC controllers, that enables the creation of HTTP server spans and metrics for requests processed by the Spring servlet container. +library_link: https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/servlet/mvc/package-summary.html diff --git a/instrumentation/spring/spring-webmvc/spring-webmvc-6.0/javaagent/build.gradle.kts b/instrumentation/spring/spring-webmvc/spring-webmvc-6.0/javaagent/build.gradle.kts index 640dc885e125..cf6d5fb378f4 100644 --- a/instrumentation/spring/spring-webmvc/spring-webmvc-6.0/javaagent/build.gradle.kts +++ b/instrumentation/spring/spring-webmvc/spring-webmvc-6.0/javaagent/build.gradle.kts @@ -57,6 +57,8 @@ tasks { } val testExperimental by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath systemProperty("metadataConfig", "otel.instrumentation.spring-webmvc.experimental-span-attributes=true") jvmArgs("-Dotel.instrumentation.spring-webmvc.experimental-span-attributes=true") } diff --git a/instrumentation/spring/spring-webmvc/spring-webmvc-6.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/webmvc/v6_0/boot/SpringBootBasedTest.java b/instrumentation/spring/spring-webmvc/spring-webmvc-6.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/webmvc/v6_0/boot/SpringBootBasedTest.java index 95379349fc46..1ed5e35b629e 100644 --- a/instrumentation/spring/spring-webmvc/spring-webmvc-6.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/webmvc/v6_0/boot/SpringBootBasedTest.java +++ b/instrumentation/spring/spring-webmvc/spring-webmvc-6.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/webmvc/v6_0/boot/SpringBootBasedTest.java @@ -12,7 +12,6 @@ import static io.opentelemetry.semconv.ExceptionAttributes.EXCEPTION_MESSAGE; import static io.opentelemetry.semconv.ExceptionAttributes.EXCEPTION_STACKTRACE; import static io.opentelemetry.semconv.ExceptionAttributes.EXCEPTION_TYPE; -import static org.assertj.core.api.Assertions.assertThat; import io.opentelemetry.api.trace.SpanKind; import io.opentelemetry.instrumentation.spring.webmvc.boot.AbstractSpringBootBasedTest; @@ -86,7 +85,7 @@ protected SpanDataAssert assertHandlerSpan( equalTo( EXCEPTION_TYPE, "org.springframework.web.servlet.resource.NoResourceFoundException"), - satisfies(EXCEPTION_MESSAGE, val -> assertThat(val).isNotNull()), + satisfies(EXCEPTION_MESSAGE, val -> val.isNotNull()), satisfies(EXCEPTION_STACKTRACE, val -> val.isInstanceOf(String.class)))); return span; } else { diff --git a/instrumentation/spring/spring-webmvc/spring-webmvc-6.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/webmvc/v6_0/filter/ServletFilterTest.java b/instrumentation/spring/spring-webmvc/spring-webmvc-6.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/webmvc/v6_0/filter/ServletFilterTest.java index 97f86d80f83b..6af866606400 100644 --- a/instrumentation/spring/spring-webmvc/spring-webmvc-6.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/webmvc/v6_0/filter/ServletFilterTest.java +++ b/instrumentation/spring/spring-webmvc/spring-webmvc-6.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/webmvc/v6_0/filter/ServletFilterTest.java @@ -70,7 +70,7 @@ protected SpanDataAssert assertHandlerSpan( equalTo( EXCEPTION_TYPE, "org.springframework.web.servlet.resource.NoResourceFoundException"), - satisfies(EXCEPTION_MESSAGE, val -> assertThat(val).isNotNull()), + satisfies(EXCEPTION_MESSAGE, val -> val.isNotNull()), satisfies(EXCEPTION_STACKTRACE, val -> val.isInstanceOf(String.class)))); return span; } else { diff --git a/instrumentation/spring/spring-webmvc/spring-webmvc-6.0/library/README.md b/instrumentation/spring/spring-webmvc/spring-webmvc-6.0/library/README.md index f6f43aabb813..52b1a33ee592 100644 --- a/instrumentation/spring/spring-webmvc/spring-webmvc-6.0/library/README.md +++ b/instrumentation/spring/spring-webmvc/spring-webmvc-6.0/library/README.md @@ -11,7 +11,7 @@ Replace `SPRING_VERSION` with the version of spring you're using. - `Minimum version: 6.0.0` Replace `OPENTELEMETRY_VERSION` with the [latest -release](https://search.maven.org/search?q=g:io.opentelemetry.instrumentation%20AND%20a:opentelemetry-spring-webmvc-6.0). +release]( https://central.sonatype.com/artifact/io.opentelemetry.instrumentation/opentelemetry-spring-webmvc-6.0). For Maven add the following to your `pom.xml`: diff --git a/instrumentation/spring/spring-webmvc/spring-webmvc-6.0/metadata.yaml b/instrumentation/spring/spring-webmvc/spring-webmvc-6.0/metadata.yaml index a38d74b6f05d..d83156806bc2 100644 --- a/instrumentation/spring/spring-webmvc/spring-webmvc-6.0/metadata.yaml +++ b/instrumentation/spring/spring-webmvc/spring-webmvc-6.0/metadata.yaml @@ -15,3 +15,4 @@ configurations: description: Enables the creation of experimental view (INTERNAL) spans. type: boolean default: false +library_link: https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/servlet/mvc/package-summary.html diff --git a/instrumentation/spring/spring-ws-2.0/javaagent/build.gradle.kts b/instrumentation/spring/spring-ws-2.0/javaagent/build.gradle.kts index 47cdcc7e70c4..f1a063be7190 100644 --- a/instrumentation/spring/spring-ws-2.0/javaagent/build.gradle.kts +++ b/instrumentation/spring/spring-ws-2.0/javaagent/build.gradle.kts @@ -1,5 +1,4 @@ plugins { - id("org.unbroken-dome.xjc") id("otel.javaagent-instrumentation") } @@ -14,21 +13,6 @@ muzzle { } } -sourceSets { - test { - resources { - srcDirs("src/test/schema") - } - } -} - -tasks { - named("checkstyleTest") { - // exclude generated web service classes - exclude("**/hello_web_service/**") - } -} - dependencies { compileOnly("org.springframework.ws:spring-ws-core:2.0.0.RELEASE") @@ -46,7 +30,9 @@ dependencies { testImplementation("javax.xml.bind:jaxb-api:2.2.11") testImplementation("com.sun.xml.bind:jaxb-core:2.2.11") testImplementation("com.sun.xml.bind:jaxb-impl:2.2.11") + testImplementation("javax.activation:activation:1.1.1") testImplementation("com.google.guava:guava") + testImplementation(project(":instrumentation:spring:spring-ws-2.0:testing")) testInstrumentation(project(":instrumentation:servlet:servlet-3.0:javaagent")) } diff --git a/instrumentation/spring/spring-ws-2.0/metadata.yaml b/instrumentation/spring/spring-ws-2.0/metadata.yaml index 1aee203e711b..f31c0d39d62d 100644 --- a/instrumentation/spring/spring-ws-2.0/metadata.yaml +++ b/instrumentation/spring/spring-ws-2.0/metadata.yaml @@ -1 +1,2 @@ disabled_by_default: true +library_link: https://spring.io/projects/spring-ws diff --git a/instrumentation/spring/spring-ws-2.0/testing/build.gradle.kts b/instrumentation/spring/spring-ws-2.0/testing/build.gradle.kts new file mode 100644 index 000000000000..9cdbed4dc9bf --- /dev/null +++ b/instrumentation/spring/spring-ws-2.0/testing/build.gradle.kts @@ -0,0 +1,27 @@ +plugins { + id("com.github.bjornvester.xjc") + id("otel.java-conventions") +} + +sourceSets { + main { + resources { + srcDirs("src/main/schema") + } + } +} + +tasks { + named("checkstyleMain") { + // exclude generated web service classes + exclude("**/hello_web_service/**") + } +} + +xjc { + xsdDir.set(layout.projectDirectory.dir("src/main/schema")) + useJakarta.set(false) +} + +dependencies { +} diff --git a/instrumentation/spring/spring-ws-2.0/javaagent/src/test/schema/hello.xsd b/instrumentation/spring/spring-ws-2.0/testing/src/main/schema/hello.xsd similarity index 100% rename from instrumentation/spring/spring-ws-2.0/javaagent/src/test/schema/hello.xsd rename to instrumentation/spring/spring-ws-2.0/testing/src/main/schema/hello.xsd diff --git a/instrumentation/spymemcached-2.12/javaagent/build.gradle.kts b/instrumentation/spymemcached-2.12/javaagent/build.gradle.kts index 0f3c5a4ff953..5ee157c6cce0 100644 --- a/instrumentation/spymemcached-2.12/javaagent/build.gradle.kts +++ b/instrumentation/spymemcached-2.12/javaagent/build.gradle.kts @@ -28,6 +28,8 @@ tasks { } val testStableSemconv by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath jvmArgs("-Dotel.semconv-stability.opt-in=database") } diff --git a/instrumentation/tomcat/tomcat-jdbc/javaagent/build.gradle.kts b/instrumentation/tomcat/tomcat-jdbc/javaagent/build.gradle.kts index 0bc199140cd1..7351efd94bad 100644 --- a/instrumentation/tomcat/tomcat-jdbc/javaagent/build.gradle.kts +++ b/instrumentation/tomcat/tomcat-jdbc/javaagent/build.gradle.kts @@ -20,8 +20,9 @@ val collectMetadata = findProperty("collectMetadata")?.toString() ?: "false" tasks { val testStableSemconv by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath jvmArgs("-Dotel.semconv-stability.opt-in=database") - systemProperty("collectMetadata", collectMetadata) systemProperty("metadataConfig", "otel.semconv-stability.opt-in=database") } diff --git a/instrumentation/vertx/vertx-http-client/vertx-http-client-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/v3_0/client/HttpClientImplInstrumentation.java b/instrumentation/vertx/vertx-http-client/vertx-http-client-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/v3_0/client/HttpClientImplInstrumentation.java index f36c3fdd4474..dd20f8eee754 100644 --- a/instrumentation/vertx/vertx-http-client/vertx-http-client-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/v3_0/client/HttpClientImplInstrumentation.java +++ b/instrumentation/vertx/vertx-http-client/vertx-http-client-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/v3_0/client/HttpClientImplInstrumentation.java @@ -5,10 +5,10 @@ package io.opentelemetry.javaagent.instrumentation.vertx.v3_0.client; +import static io.opentelemetry.javaagent.instrumentation.vertx.v3_0.client.VertxClientSingletons.HTTP_CLIENT_OPTIONS; import static net.bytebuddy.matcher.ElementMatchers.isConstructor; import static net.bytebuddy.matcher.ElementMatchers.named; -import io.opentelemetry.instrumentation.api.util.VirtualField; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; import io.vertx.core.http.HttpClientOptions; @@ -36,7 +36,7 @@ public static class AttachStateAdvice { public static void attachHttpClientOptions( @Advice.This HttpClientImpl client, @Advice.FieldValue("options") HttpClientOptions options) { - VirtualField.find(HttpClientImpl.class, HttpClientOptions.class).set(client, options); + HTTP_CLIENT_OPTIONS.set(client, options); } } } diff --git a/instrumentation/vertx/vertx-http-client/vertx-http-client-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/v3_0/client/HttpRequestImplInstrumentation.java b/instrumentation/vertx/vertx-http-client/vertx-http-client-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/v3_0/client/HttpRequestImplInstrumentation.java index 1b720657ecc6..c65b8136d376 100644 --- a/instrumentation/vertx/vertx-http-client/vertx-http-client-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/v3_0/client/HttpRequestImplInstrumentation.java +++ b/instrumentation/vertx/vertx-http-client/vertx-http-client-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/v3_0/client/HttpRequestImplInstrumentation.java @@ -5,11 +5,12 @@ package io.opentelemetry.javaagent.instrumentation.vertx.v3_0.client; +import static io.opentelemetry.javaagent.instrumentation.vertx.v3_0.client.VertxClientSingletons.HTTP_CLIENT_OPTIONS; +import static io.opentelemetry.javaagent.instrumentation.vertx.v3_0.client.VertxClientSingletons.REQUEST_INFO; import static net.bytebuddy.matcher.ElementMatchers.isConstructor; import static net.bytebuddy.matcher.ElementMatchers.named; import static net.bytebuddy.matcher.ElementMatchers.takesArgument; -import io.opentelemetry.instrumentation.api.util.VirtualField; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; import io.vertx.core.http.HttpClientOptions; @@ -53,13 +54,11 @@ public static void attachRequestInfo( @Advice.Argument(0) HttpClientImpl client, @Advice.Argument(2) String host, @Advice.Argument(3) int port) { - HttpClientOptions httpClientOptions = - VirtualField.find(HttpClientImpl.class, HttpClientOptions.class).get(client); - VirtualField.find(HttpClientRequest.class, VertxRequestInfo.class) - .set( - request, - VertxRequestInfo.create( - httpClientOptions != null && httpClientOptions.isSsl(), host, port)); + HttpClientOptions httpClientOptions = HTTP_CLIENT_OPTIONS.get(client); + REQUEST_INFO.set( + request, + VertxRequestInfo.create( + httpClientOptions != null && httpClientOptions.isSsl(), host, port)); } } @@ -71,8 +70,7 @@ public static void attachRequestInfo( @Advice.Argument(1) boolean ssl, @Advice.Argument(3) String host, @Advice.Argument(4) int port) { - VirtualField.find(HttpClientRequest.class, VertxRequestInfo.class) - .set(request, VertxRequestInfo.create(ssl, host, port)); + REQUEST_INFO.set(request, VertxRequestInfo.create(ssl, host, port)); } } @@ -84,8 +82,7 @@ public static void attachRequestInfo( @Advice.Argument(1) boolean ssl, @Advice.Argument(4) String host, @Advice.Argument(5) int port) { - VirtualField.find(HttpClientRequest.class, VertxRequestInfo.class) - .set(request, VertxRequestInfo.create(ssl, host, port)); + REQUEST_INFO.set(request, VertxRequestInfo.create(ssl, host, port)); } } } diff --git a/instrumentation/vertx/vertx-http-client/vertx-http-client-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/v3_0/client/HttpRequestInstrumentation.java b/instrumentation/vertx/vertx-http-client/vertx-http-client-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/v3_0/client/HttpRequestInstrumentation.java index 5590b393ec35..a8cfb91486a5 100644 --- a/instrumentation/vertx/vertx-http-client/vertx-http-client-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/v3_0/client/HttpRequestInstrumentation.java +++ b/instrumentation/vertx/vertx-http-client/vertx-http-client-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/v3_0/client/HttpRequestInstrumentation.java @@ -7,6 +7,8 @@ import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed; import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.implementsInterface; +import static io.opentelemetry.javaagent.instrumentation.vertx.v3_0.client.VertxClientSingletons.CONTEXTS; +import static io.opentelemetry.javaagent.instrumentation.vertx.v3_0.client.VertxClientSingletons.REQUEST_INFO; import static io.opentelemetry.javaagent.instrumentation.vertx.v3_0.client.VertxClientSingletons.instrumenter; import static net.bytebuddy.matcher.ElementMatchers.isMethod; import static net.bytebuddy.matcher.ElementMatchers.isPrivate; @@ -16,8 +18,6 @@ import io.opentelemetry.context.Context; import io.opentelemetry.context.Scope; -import io.opentelemetry.instrumentation.api.util.VirtualField; -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.vertx.client.Contexts; @@ -25,7 +25,10 @@ import io.vertx.core.Handler; import io.vertx.core.http.HttpClientRequest; import io.vertx.core.http.HttpClientResponse; +import javax.annotation.Nullable; import net.bytebuddy.asm.Advice; +import net.bytebuddy.asm.Advice.AssignReturned; +import net.bytebuddy.asm.Advice.AssignReturned.ToArguments.ToArgument; import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.matcher.ElementMatcher; @@ -83,41 +86,52 @@ public void transform(TypeTransformer transformer) { @SuppressWarnings("unused") public static class EndRequestAdvice { - @Advice.OnMethodEnter(suppress = Throwable.class) - public static void attachContext( - @Advice.This HttpClientRequest request, - @Advice.Local("otelContext") Context context, - @Advice.Local("otelScope") Scope scope) { + public static class AdviceScope { + private final Context context; + private final Scope scope; - VertxRequestInfo requestInfo = - VirtualField.find(HttpClientRequest.class, VertxRequestInfo.class).get(request); - if (requestInfo == null) { - return; + private AdviceScope(Context context, Scope scope) { + this.context = context; + this.scope = scope; } - Context parentContext = Java8BytecodeBridge.currentContext(); - if (!instrumenter().shouldStart(parentContext, request)) { - return; + @Nullable + public static AdviceScope startAndAttachContext(HttpClientRequest request) { + VertxRequestInfo requestInfo = REQUEST_INFO.get(request); + if (requestInfo == null) { + return null; + } + + Context parentContext = Context.current(); + if (!instrumenter().shouldStart(parentContext, request)) { + return null; + } + Context context = instrumenter().start(parentContext, request); + CONTEXTS.set(request, new Contexts(parentContext, context)); + return new AdviceScope(context, context.makeCurrent()); } - context = instrumenter().start(parentContext, request); - Contexts contexts = new Contexts(parentContext, context); - VirtualField.find(HttpClientRequest.class, Contexts.class).set(request, contexts); + public void end(@Nullable Throwable throwable, HttpClientRequest request) { + scope.close(); + if (throwable != null) { + instrumenter().end(context, request, null, throwable); + } + } + } - scope = context.makeCurrent(); + @Nullable + @Advice.OnMethodEnter(suppress = Throwable.class) + public static AdviceScope attachContext(@Advice.This HttpClientRequest request) { + return AdviceScope.startAndAttachContext(request); } @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) public static void endScope( @Advice.This HttpClientRequest request, - @Advice.Local("otelContext") Context context, - @Advice.Local("otelScope") Scope scope, - @Advice.Thrown Throwable throwable) { - if (scope != null) { - scope.close(); - } - if (throwable != null) { - instrumenter().end(context, request, null, throwable); + @Advice.Thrown @Nullable Throwable throwable, + @Advice.Enter @Nullable AdviceScope adviceScope) { + if (adviceScope != null) { + adviceScope.end(throwable, request); } } } @@ -125,25 +139,23 @@ public static void endScope( @SuppressWarnings("unused") public static class HandleExceptionAdvice { + @Nullable @Advice.OnMethodEnter(suppress = Throwable.class) - public static void handleException( - @Advice.This HttpClientRequest request, - @Advice.Argument(0) Throwable t, - @Advice.Local("otelScope") Scope scope) { - Contexts contexts = VirtualField.find(HttpClientRequest.class, Contexts.class).get(request); + public static Scope handleException( + @Advice.This HttpClientRequest request, @Advice.Argument(0) Throwable t) { + Contexts contexts = CONTEXTS.get(request); if (contexts == null) { - return; + return null; } - instrumenter().end(contexts.context, request, null, t); // Scoping all potential callbacks etc to the parent context - scope = contexts.parentContext.makeCurrent(); + return contexts.parentContext.makeCurrent(); } @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) - public static void handleResponseExit(@Advice.Local("otelScope") Scope scope) { + public static void handleResponseExit(@Advice.Enter @Nullable Scope scope) { if (scope != null) { scope.close(); } @@ -153,25 +165,23 @@ public static void handleResponseExit(@Advice.Local("otelScope") Scope scope) { @SuppressWarnings("unused") public static class HandleResponseAdvice { + @Nullable @Advice.OnMethodEnter(suppress = Throwable.class) - public static void handleResponseEnter( - @Advice.This HttpClientRequest request, - @Advice.Argument(0) HttpClientResponse response, - @Advice.Local("otelScope") Scope scope) { - Contexts contexts = VirtualField.find(HttpClientRequest.class, Contexts.class).get(request); + public static Scope handleResponseEnter( + @Advice.This HttpClientRequest request, @Advice.Argument(0) HttpClientResponse response) { + Contexts contexts = CONTEXTS.get(request); if (contexts == null) { - return; + return null; } - instrumenter().end(contexts.context, request, response, null); // Scoping all potential callbacks etc to the parent context - scope = contexts.parentContext.makeCurrent(); + return contexts.parentContext.makeCurrent(); } @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) - public static void handleResponseExit(@Advice.Local("otelScope") Scope scope) { + public static void handleResponseExit(@Advice.Enter @Nullable Scope scope) { if (scope != null) { scope.close(); } @@ -181,19 +191,18 @@ public static void handleResponseExit(@Advice.Local("otelScope") Scope scope) { @SuppressWarnings("unused") public static class MountContextAdvice { + @Nullable @Advice.OnMethodEnter(suppress = Throwable.class) - public static void mountContext( - @Advice.This HttpClientRequest request, @Advice.Local("otelScope") Scope scope) { - Contexts contexts = VirtualField.find(HttpClientRequest.class, Contexts.class).get(request); + public static Scope mountContext(@Advice.This HttpClientRequest request) { + Contexts contexts = CONTEXTS.get(request); if (contexts == null) { - return; + return null; } - - scope = contexts.context.makeCurrent(); + return contexts.context.makeCurrent(); } @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) - public static void unmountContext(@Advice.Local("otelScope") Scope scope) { + public static void unmountContext(@Advice.Enter @Nullable Scope scope) { if (scope != null) { scope.close(); } @@ -203,15 +212,16 @@ public static void unmountContext(@Advice.Local("otelScope") Scope scope) { @SuppressWarnings("unused") public static class ExceptionHandlerAdvice { + @Nullable + @AssignReturned.ToArguments(@ToArgument(0)) @Advice.OnMethodEnter(suppress = Throwable.class) - public static void wrapExceptionHandler( + public static Handler wrapExceptionHandler( @Advice.This HttpClientRequest request, - @Advice.Argument(value = 0, readOnly = false) Handler handler) { - if (handler != null) { - VirtualField virtualField = - VirtualField.find(HttpClientRequest.class, Contexts.class); - handler = ExceptionHandlerWrapper.wrap(instrumenter(), request, virtualField, handler); + @Advice.Argument(0) @Nullable Handler handler) { + if (handler == null) { + return null; } + return ExceptionHandlerWrapper.wrap(instrumenter(), request, CONTEXTS, handler); } } } diff --git a/instrumentation/vertx/vertx-http-client/vertx-http-client-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/v3_0/client/VertxClientInstrumentationModule.java b/instrumentation/vertx/vertx-http-client/vertx-http-client-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/v3_0/client/VertxClientInstrumentationModule.java index ecbb82a9bf4a..e36e65b91c43 100644 --- a/instrumentation/vertx/vertx-http-client/vertx-http-client-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/v3_0/client/VertxClientInstrumentationModule.java +++ b/instrumentation/vertx/vertx-http-client/vertx-http-client-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/v3_0/client/VertxClientInstrumentationModule.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 VertxClientInstrumentationModule extends InstrumentationModule { +public class VertxClientInstrumentationModule extends InstrumentationModule + implements ExperimentalInstrumentationModule { public VertxClientInstrumentationModule() { super("vertx-http-client", "vertx-http-client-3.0", "vertx"); @@ -34,4 +36,9 @@ public List typeInstrumentations() { new HttpRequestImplInstrumentation(), new HttpRequestInstrumentation()); } + + @Override + public boolean isIndyReady() { + return true; + } } diff --git a/instrumentation/vertx/vertx-http-client/vertx-http-client-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/v3_0/client/VertxClientSingletons.java b/instrumentation/vertx/vertx-http-client/vertx-http-client-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/v3_0/client/VertxClientSingletons.java index 9153de8e39b7..ec46f53ba669 100644 --- a/instrumentation/vertx/vertx-http-client/vertx-http-client-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/v3_0/client/VertxClientSingletons.java +++ b/instrumentation/vertx/vertx-http-client/vertx-http-client-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/v3_0/client/VertxClientSingletons.java @@ -6,9 +6,13 @@ package io.opentelemetry.javaagent.instrumentation.vertx.v3_0.client; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; +import io.opentelemetry.instrumentation.api.util.VirtualField; +import io.opentelemetry.javaagent.instrumentation.vertx.client.Contexts; import io.opentelemetry.javaagent.instrumentation.vertx.client.VertxClientInstrumenterFactory; +import io.vertx.core.http.HttpClientOptions; import io.vertx.core.http.HttpClientRequest; import io.vertx.core.http.HttpClientResponse; +import io.vertx.core.http.impl.HttpClientImpl; public final class VertxClientSingletons { @@ -16,6 +20,15 @@ public final class VertxClientSingletons { VertxClientInstrumenterFactory.create( "io.opentelemetry.vertx-http-client-3.0", new Vertx3HttpAttributesGetter()); + public static final VirtualField CONTEXTS = + VirtualField.find(HttpClientRequest.class, Contexts.class); + + public static final VirtualField REQUEST_INFO = + VirtualField.find(HttpClientRequest.class, VertxRequestInfo.class); + + public static final VirtualField HTTP_CLIENT_OPTIONS = + VirtualField.find(HttpClientImpl.class, HttpClientOptions.class); + public static Instrumenter instrumenter() { return INSTRUMENTER; } diff --git a/instrumentation/vertx/vertx-http-client/vertx-http-client-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/v4_0/client/ConnectionManagerInstrumentation.java b/instrumentation/vertx/vertx-http-client/vertx-http-client-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/v4_0/client/ConnectionManagerInstrumentation.java index 9b51318b2b78..67fbb9702035 100644 --- a/instrumentation/vertx/vertx-http-client/vertx-http-client-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/v4_0/client/ConnectionManagerInstrumentation.java +++ b/instrumentation/vertx/vertx-http-client/vertx-http-client-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/v4_0/client/ConnectionManagerInstrumentation.java @@ -12,7 +12,10 @@ import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; import io.vertx.core.Handler; +import javax.annotation.Nullable; import net.bytebuddy.asm.Advice; +import net.bytebuddy.asm.Advice.AssignReturned; +import net.bytebuddy.asm.Advice.AssignReturned.ToArguments.ToArgument; import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.matcher.ElementMatcher; @@ -42,28 +45,31 @@ public void transform(TypeTransformer transformer) { @SuppressWarnings("unused") public static class GetConnectionArg2Advice { + @Nullable + @AssignReturned.ToArguments(@ToArgument(2)) @Advice.OnMethodEnter(suppress = Throwable.class) - public static void wrapHandler( - @Advice.Argument(value = 2, readOnly = false) Handler handler) { - handler = HandlerWrapper.wrap(handler); + public static Handler wrapHandler(@Advice.Argument(2) @Nullable Handler handler) { + return HandlerWrapper.wrap(handler); } } @SuppressWarnings("unused") public static class GetConnectionArg3Advice { + @Nullable + @AssignReturned.ToArguments(@ToArgument(3)) @Advice.OnMethodEnter(suppress = Throwable.class) - public static void wrapHandler( - @Advice.Argument(value = 3, readOnly = false) Handler handler) { - handler = HandlerWrapper.wrap(handler); + public static Handler wrapHandler(@Advice.Argument(3) @Nullable Handler handler) { + return HandlerWrapper.wrap(handler); } } @SuppressWarnings("unused") public static class GetConnectionArg4Advice { + @Nullable + @AssignReturned.ToArguments(@ToArgument(4)) @Advice.OnMethodEnter(suppress = Throwable.class) - public static void wrapHandler( - @Advice.Argument(value = 4, readOnly = false) Handler handler) { - handler = HandlerWrapper.wrap(handler); + public static Handler wrapHandler(@Advice.Argument(4) @Nullable Handler handler) { + return HandlerWrapper.wrap(handler); } } } diff --git a/instrumentation/vertx/vertx-http-client/vertx-http-client-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/v4_0/client/HandlerWrapper.java b/instrumentation/vertx/vertx-http-client/vertx-http-client-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/v4_0/client/HandlerWrapper.java index a2938d0e8e68..794ad982a31b 100644 --- a/instrumentation/vertx/vertx-http-client/vertx-http-client-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/v4_0/client/HandlerWrapper.java +++ b/instrumentation/vertx/vertx-http-client/vertx-http-client-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/v4_0/client/HandlerWrapper.java @@ -8,6 +8,7 @@ import io.opentelemetry.context.Context; import io.opentelemetry.context.Scope; import io.vertx.core.Handler; +import javax.annotation.Nullable; public class HandlerWrapper implements Handler { private final Handler delegate; @@ -18,7 +19,8 @@ private HandlerWrapper(Handler delegate, Context context) { this.context = context; } - public static Handler wrap(Handler handler) { + @Nullable + public static Handler wrap(@Nullable Handler handler) { Context current = Context.current(); if (handler != null && !(handler instanceof HandlerWrapper) && current != Context.root()) { handler = new HandlerWrapper<>(handler, current); diff --git a/instrumentation/vertx/vertx-http-client/vertx-http-client-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/v4_0/client/HttpClientConnectionInstrumentation.java b/instrumentation/vertx/vertx-http-client/vertx-http-client-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/v4_0/client/HttpClientConnectionInstrumentation.java index b310f747f38d..636b40d7d713 100644 --- a/instrumentation/vertx/vertx-http-client/vertx-http-client-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/v4_0/client/HttpClientConnectionInstrumentation.java +++ b/instrumentation/vertx/vertx-http-client/vertx-http-client-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/v4_0/client/HttpClientConnectionInstrumentation.java @@ -14,6 +14,8 @@ import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; import io.vertx.core.Handler; import net.bytebuddy.asm.Advice; +import net.bytebuddy.asm.Advice.AssignReturned; +import net.bytebuddy.asm.Advice.AssignReturned.ToArguments.ToArgument; import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.matcher.ElementMatcher; @@ -39,10 +41,10 @@ public void transform(TypeTransformer transformer) { @SuppressWarnings("unused") public static class CreateStreamAdvice { + @AssignReturned.ToArguments(@ToArgument(1)) @Advice.OnMethodEnter(suppress = Throwable.class) - public static void wrapHandler( - @Advice.Argument(value = 1, readOnly = false) Handler handler) { - handler = HandlerWrapper.wrap(handler); + public static Handler wrapHandler(@Advice.Argument(1) Handler handler) { + return HandlerWrapper.wrap(handler); } } } diff --git a/instrumentation/vertx/vertx-http-client/vertx-http-client-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/v4_0/client/HttpRequestInstrumentation.java b/instrumentation/vertx/vertx-http-client/vertx-http-client-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/v4_0/client/HttpRequestInstrumentation.java index fb2b8a0895f4..8a3e147a8af1 100644 --- a/instrumentation/vertx/vertx-http-client/vertx-http-client-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/v4_0/client/HttpRequestInstrumentation.java +++ b/instrumentation/vertx/vertx-http-client/vertx-http-client-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/v4_0/client/HttpRequestInstrumentation.java @@ -7,6 +7,7 @@ import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed; import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.implementsInterface; +import static io.opentelemetry.javaagent.instrumentation.vertx.v4_0.client.VertxClientSingletons.CONTEXTS; import static io.opentelemetry.javaagent.instrumentation.vertx.v4_0.client.VertxClientSingletons.instrumenter; import static net.bytebuddy.matcher.ElementMatchers.isMethod; import static net.bytebuddy.matcher.ElementMatchers.isPrivate; @@ -16,8 +17,6 @@ import io.opentelemetry.context.Context; import io.opentelemetry.context.Scope; -import io.opentelemetry.instrumentation.api.util.VirtualField; -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.vertx.client.Contexts; @@ -25,7 +24,10 @@ import io.vertx.core.Handler; import io.vertx.core.http.HttpClientRequest; import io.vertx.core.http.HttpClientResponse; +import javax.annotation.Nullable; import net.bytebuddy.asm.Advice; +import net.bytebuddy.asm.Advice.AssignReturned; +import net.bytebuddy.asm.Advice.AssignReturned.ToArguments.ToArgument; import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.matcher.ElementMatcher; @@ -85,35 +87,50 @@ public void transform(TypeTransformer transformer) { @SuppressWarnings("unused") public static class EndRequestAdvice { - @Advice.OnMethodEnter(suppress = Throwable.class) - public static void attachContext( - @Advice.This HttpClientRequest request, - @Advice.Local("otelContext") Context context, - @Advice.Local("otelScope") Scope scope) { - Context parentContext = Java8BytecodeBridge.currentContext(); + public static class AdviceScope { + private final Context context; + private final Scope scope; - if (!instrumenter().shouldStart(parentContext, request)) { - return; + private AdviceScope(Context context, Scope scope) { + this.context = context; + this.scope = scope; } - context = instrumenter().start(parentContext, request); - Contexts contexts = new Contexts(parentContext, context); - VirtualField.find(HttpClientRequest.class, Contexts.class).set(request, contexts); + @Nullable + public static AdviceScope startAndAttachContext(HttpClientRequest request) { + Context parentContext = Context.current(); + if (!instrumenter().shouldStart(parentContext, request)) { + return null; + } + + Context context = instrumenter().start(parentContext, request); + Contexts contexts = new Contexts(parentContext, context); + CONTEXTS.set(request, contexts); + + return new AdviceScope(context, context.makeCurrent()); + } - scope = context.makeCurrent(); + public void end(HttpClientRequest request, Throwable throwable) { + scope.close(); + if (throwable != null) { + instrumenter().end(context, request, null, throwable); + } + } + } + + @Nullable + @Advice.OnMethodEnter(suppress = Throwable.class) + public static AdviceScope attachContext(@Advice.This HttpClientRequest request) { + return AdviceScope.startAndAttachContext(request); } @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) public static void endScope( @Advice.This HttpClientRequest request, - @Advice.Local("otelContext") Context context, - @Advice.Local("otelScope") Scope scope, - @Advice.Thrown Throwable throwable) { - if (scope != null) { - scope.close(); - } - if (throwable != null) { - instrumenter().end(context, request, null, throwable); + @Advice.Thrown Throwable throwable, + @Advice.Enter @Nullable AdviceScope adviceScope) { + if (adviceScope != null) { + adviceScope.end(request, throwable); } } } @@ -121,25 +138,24 @@ public static void endScope( @SuppressWarnings("unused") public static class HandleExceptionAdvice { + @Nullable @Advice.OnMethodEnter(suppress = Throwable.class) - public static void handleException( - @Advice.This HttpClientRequest request, - @Advice.Argument(0) Throwable t, - @Advice.Local("otelScope") Scope scope) { - Contexts contexts = VirtualField.find(HttpClientRequest.class, Contexts.class).get(request); + public static Scope handleException( + @Advice.This HttpClientRequest request, @Advice.Argument(0) Throwable t) { + Contexts contexts = CONTEXTS.get(request); if (contexts == null) { - return; + return null; } instrumenter().end(contexts.context, request, null, t); // Scoping all potential callbacks etc to the parent context - scope = contexts.parentContext.makeCurrent(); + return contexts.parentContext.makeCurrent(); } @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) - public static void handleResponseExit(@Advice.Local("otelScope") Scope scope) { + public static void handleResponseExit(@Advice.Enter @Nullable Scope scope) { if (scope != null) { scope.close(); } @@ -149,25 +165,24 @@ public static void handleResponseExit(@Advice.Local("otelScope") Scope scope) { @SuppressWarnings("unused") public static class HandleResponseAdvice { + @Nullable @Advice.OnMethodEnter(suppress = Throwable.class) - public static void handleResponseEnter( - @Advice.This HttpClientRequest request, - @Advice.Argument(1) HttpClientResponse response, - @Advice.Local("otelScope") Scope scope) { - Contexts contexts = VirtualField.find(HttpClientRequest.class, Contexts.class).get(request); + public static Scope handleResponseEnter( + @Advice.This HttpClientRequest request, @Advice.Argument(1) HttpClientResponse response) { + Contexts contexts = CONTEXTS.get(request); if (contexts == null) { - return; + return null; } instrumenter().end(contexts.context, request, response, null); // Scoping all potential callbacks etc to the parent context - scope = contexts.parentContext.makeCurrent(); + return contexts.parentContext.makeCurrent(); } @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) - public static void handleResponseExit(@Advice.Local("otelScope") Scope scope) { + public static void handleResponseExit(@Advice.Enter @Nullable Scope scope) { if (scope != null) { scope.close(); } @@ -177,19 +192,19 @@ public static void handleResponseExit(@Advice.Local("otelScope") Scope scope) { @SuppressWarnings("unused") public static class MountContextAdvice { + @Nullable @Advice.OnMethodEnter(suppress = Throwable.class) - public static void mountContext( - @Advice.This HttpClientRequest request, @Advice.Local("otelScope") Scope scope) { - Contexts contexts = VirtualField.find(HttpClientRequest.class, Contexts.class).get(request); + public static Scope mountContext(@Advice.This HttpClientRequest request) { + Contexts contexts = CONTEXTS.get(request); if (contexts == null) { - return; + return null; } - scope = contexts.context.makeCurrent(); + return contexts.context.makeCurrent(); } @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) - public static void unmountContext(@Advice.Local("otelScope") Scope scope) { + public static void unmountContext(@Advice.Enter @Nullable Scope scope) { if (scope != null) { scope.close(); } @@ -199,15 +214,16 @@ public static void unmountContext(@Advice.Local("otelScope") Scope scope) { @SuppressWarnings("unused") public static class ExceptionHandlerAdvice { + @Nullable + @AssignReturned.ToArguments(@ToArgument(0)) @Advice.OnMethodEnter(suppress = Throwable.class) - public static void wrapExceptionHandler( + public static Handler wrapExceptionHandler( @Advice.This HttpClientRequest request, - @Advice.Argument(value = 0, readOnly = false) Handler handler) { - if (handler != null) { - VirtualField virtualField = - VirtualField.find(HttpClientRequest.class, Contexts.class); - handler = ExceptionHandlerWrapper.wrap(instrumenter(), request, virtualField, handler); + @Advice.Argument(0) @Nullable Handler handler) { + if (handler == null) { + return null; } + return ExceptionHandlerWrapper.wrap(instrumenter(), request, CONTEXTS, handler); } } } diff --git a/instrumentation/vertx/vertx-http-client/vertx-http-client-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/v4_0/client/VertxClientInstrumentationModule.java b/instrumentation/vertx/vertx-http-client/vertx-http-client-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/v4_0/client/VertxClientInstrumentationModule.java index c18687d452a9..a3b4c24e8a25 100644 --- a/instrumentation/vertx/vertx-http-client/vertx-http-client-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/v4_0/client/VertxClientInstrumentationModule.java +++ b/instrumentation/vertx/vertx-http-client/vertx-http-client-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/v4_0/client/VertxClientInstrumentationModule.java @@ -12,11 +12,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 VertxClientInstrumentationModule extends InstrumentationModule { +public class VertxClientInstrumentationModule extends InstrumentationModule + implements ExperimentalInstrumentationModule { public VertxClientInstrumentationModule() { super("vertx-http-client", "vertx-http-client-4.0", "vertx"); @@ -37,4 +39,9 @@ public List typeInstrumentations() { new HttpClientConnectionInstrumentation(), new HttpRequestInstrumentation()); } + + @Override + public boolean isIndyReady() { + return true; + } } diff --git a/instrumentation/vertx/vertx-http-client/vertx-http-client-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/v4_0/client/VertxClientSingletons.java b/instrumentation/vertx/vertx-http-client/vertx-http-client-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/v4_0/client/VertxClientSingletons.java index 1e14fa9f4f44..8960551fb916 100644 --- a/instrumentation/vertx/vertx-http-client/vertx-http-client-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/v4_0/client/VertxClientSingletons.java +++ b/instrumentation/vertx/vertx-http-client/vertx-http-client-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/v4_0/client/VertxClientSingletons.java @@ -6,6 +6,8 @@ package io.opentelemetry.javaagent.instrumentation.vertx.v4_0.client; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; +import io.opentelemetry.instrumentation.api.util.VirtualField; +import io.opentelemetry.javaagent.instrumentation.vertx.client.Contexts; import io.opentelemetry.javaagent.instrumentation.vertx.client.VertxClientInstrumenterFactory; import io.vertx.core.http.HttpClientRequest; import io.vertx.core.http.HttpClientResponse; @@ -16,6 +18,9 @@ public final class VertxClientSingletons { VertxClientInstrumenterFactory.create( "io.opentelemetry.vertx-http-client-4.0", new Vertx4HttpAttributesGetter()); + public static final VirtualField CONTEXTS = + VirtualField.find(HttpClientRequest.class, Contexts.class); + public static Instrumenter instrumenter() { return INSTRUMENTER; } diff --git a/instrumentation/vertx/vertx-http-client/vertx-http-client-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/v5_0/client/HttpRequestInstrumentation.java b/instrumentation/vertx/vertx-http-client/vertx-http-client-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/v5_0/client/HttpRequestInstrumentation.java index c1d579a8c5bf..9b04f6c8d331 100644 --- a/instrumentation/vertx/vertx-http-client/vertx-http-client-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/v5_0/client/HttpRequestInstrumentation.java +++ b/instrumentation/vertx/vertx-http-client/vertx-http-client-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/v5_0/client/HttpRequestInstrumentation.java @@ -7,6 +7,7 @@ import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed; import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.implementsInterface; +import static io.opentelemetry.javaagent.instrumentation.vertx.v5_0.client.VertxClientSingletons.CONTEXTS; import static io.opentelemetry.javaagent.instrumentation.vertx.v5_0.client.VertxClientSingletons.instrumenter; import static net.bytebuddy.matcher.ElementMatchers.isMethod; import static net.bytebuddy.matcher.ElementMatchers.isPrivate; @@ -16,8 +17,6 @@ import io.opentelemetry.context.Context; import io.opentelemetry.context.Scope; -import io.opentelemetry.instrumentation.api.util.VirtualField; -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.vertx.client.Contexts; @@ -25,7 +24,10 @@ import io.vertx.core.Handler; import io.vertx.core.http.HttpClientRequest; import io.vertx.core.http.HttpClientResponse; +import javax.annotation.Nullable; import net.bytebuddy.asm.Advice; +import net.bytebuddy.asm.Advice.AssignReturned; +import net.bytebuddy.asm.Advice.AssignReturned.ToArguments.ToArgument; import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.matcher.ElementMatcher; @@ -85,35 +87,47 @@ public void transform(TypeTransformer transformer) { @SuppressWarnings("unused") public static class EndRequestAdvice { - @Advice.OnMethodEnter(suppress = Throwable.class) - public static void attachContext( - @Advice.This HttpClientRequest request, - @Advice.Local("otelContext") Context context, - @Advice.Local("otelScope") Scope scope) { - Context parentContext = Java8BytecodeBridge.currentContext(); + public static class AdviceScope { + private final Context context; + private final Scope scope; + + private AdviceScope(Context context, Scope scope) { + this.context = context; + this.scope = scope; + } - if (!instrumenter().shouldStart(parentContext, request)) { - return; + @Nullable + public static AdviceScope startAndAttachContext(HttpClientRequest request) { + Context parentContext = Context.current(); + if (!instrumenter().shouldStart(parentContext, request)) { + return null; + } + Context context = instrumenter().start(parentContext, request); + CONTEXTS.set(request, new Contexts(parentContext, context)); + return new AdviceScope(context, context.makeCurrent()); } - context = instrumenter().start(parentContext, request); - Contexts contexts = new Contexts(parentContext, context); - VirtualField.find(HttpClientRequest.class, Contexts.class).set(request, contexts); + public void end(HttpClientRequest request, @Nullable Throwable throwable) { + scope.close(); + if (throwable != null) { + instrumenter().end(context, request, null, throwable); + } + } + } - scope = context.makeCurrent(); + @Nullable + @Advice.OnMethodEnter(suppress = Throwable.class) + public static AdviceScope attachContext(@Advice.This HttpClientRequest request) { + return AdviceScope.startAndAttachContext(request); } @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) public static void endScope( @Advice.This HttpClientRequest request, - @Advice.Local("otelContext") Context context, - @Advice.Local("otelScope") Scope scope, - @Advice.Thrown Throwable throwable) { - if (scope != null) { - scope.close(); - } - if (throwable != null) { - instrumenter().end(context, request, null, throwable); + @Advice.Thrown @Nullable Throwable throwable, + @Advice.Enter @Nullable AdviceScope adviceScope) { + if (adviceScope != null) { + adviceScope.end(request, throwable); } } } @@ -122,24 +136,21 @@ public static void endScope( public static class HandleExceptionAdvice { @Advice.OnMethodEnter(suppress = Throwable.class) - public static void handleException( - @Advice.This HttpClientRequest request, - @Advice.Argument(0) Throwable t, - @Advice.Local("otelScope") Scope scope) { - Contexts contexts = VirtualField.find(HttpClientRequest.class, Contexts.class).get(request); - + public static Scope handleException( + @Advice.This HttpClientRequest request, @Advice.Argument(0) Throwable t) { + Contexts contexts = CONTEXTS.get(request); if (contexts == null) { - return; + return null; } instrumenter().end(contexts.context, request, null, t); // Scoping all potential callbacks etc to the parent context - scope = contexts.parentContext.makeCurrent(); + return contexts.parentContext.makeCurrent(); } @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) - public static void handleResponseExit(@Advice.Local("otelScope") Scope scope) { + public static void handleResponseExit(@Advice.Enter Scope scope) { if (scope != null) { scope.close(); } @@ -150,24 +161,22 @@ public static void handleResponseExit(@Advice.Local("otelScope") Scope scope) { public static class HandleResponseAdvice { @Advice.OnMethodEnter(suppress = Throwable.class) - public static void handleResponseEnter( - @Advice.This HttpClientRequest request, - @Advice.Argument(1) HttpClientResponse response, - @Advice.Local("otelScope") Scope scope) { - Contexts contexts = VirtualField.find(HttpClientRequest.class, Contexts.class).get(request); + public static Scope handleResponseEnter( + @Advice.This HttpClientRequest request, @Advice.Argument(1) HttpClientResponse response) { + Contexts contexts = CONTEXTS.get(request); if (contexts == null) { - return; + return null; } instrumenter().end(contexts.context, request, response, null); // Scoping all potential callbacks etc to the parent context - scope = contexts.parentContext.makeCurrent(); + return contexts.parentContext.makeCurrent(); } @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) - public static void handleResponseExit(@Advice.Local("otelScope") Scope scope) { + public static void handleResponseExit(@Advice.Enter Scope scope) { if (scope != null) { scope.close(); } @@ -178,18 +187,17 @@ public static void handleResponseExit(@Advice.Local("otelScope") Scope scope) { public static class MountContextAdvice { @Advice.OnMethodEnter(suppress = Throwable.class) - public static void mountContext( - @Advice.This HttpClientRequest request, @Advice.Local("otelScope") Scope scope) { - Contexts contexts = VirtualField.find(HttpClientRequest.class, Contexts.class).get(request); + public static Scope mountContext(@Advice.This HttpClientRequest request) { + Contexts contexts = CONTEXTS.get(request); if (contexts == null) { - return; + return null; } - scope = contexts.context.makeCurrent(); + return contexts.context.makeCurrent(); } @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) - public static void unmountContext(@Advice.Local("otelScope") Scope scope) { + public static void unmountContext(@Advice.Enter Scope scope) { if (scope != null) { scope.close(); } @@ -199,15 +207,16 @@ public static void unmountContext(@Advice.Local("otelScope") Scope scope) { @SuppressWarnings("unused") public static class ExceptionHandlerAdvice { + @AssignReturned.ToArguments(@ToArgument(0)) @Advice.OnMethodEnter(suppress = Throwable.class) - public static void wrapExceptionHandler( + public static Handler wrapExceptionHandler( @Advice.This HttpClientRequest request, - @Advice.Argument(value = 0, readOnly = false) Handler handler) { + @Advice.Argument(0) Handler originalHandler) { + Handler handler = originalHandler; if (handler != null) { - VirtualField virtualField = - VirtualField.find(HttpClientRequest.class, Contexts.class); - handler = ExceptionHandlerWrapper.wrap(instrumenter(), request, virtualField, handler); + handler = ExceptionHandlerWrapper.wrap(instrumenter(), request, CONTEXTS, handler); } + return handler; } } } diff --git a/instrumentation/vertx/vertx-http-client/vertx-http-client-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/v5_0/client/ResourceManagerInstrumentation.java b/instrumentation/vertx/vertx-http-client/vertx-http-client-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/v5_0/client/ResourceManagerInstrumentation.java index 9f7d18efbf25..79a6e3d472e2 100644 --- a/instrumentation/vertx/vertx-http-client/vertx-http-client-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/v5_0/client/ResourceManagerInstrumentation.java +++ b/instrumentation/vertx/vertx-http-client/vertx-http-client-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/v5_0/client/ResourceManagerInstrumentation.java @@ -12,6 +12,7 @@ import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; import io.vertx.core.Future; import net.bytebuddy.asm.Advice; +import net.bytebuddy.asm.Advice.AssignReturned; import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.matcher.ElementMatcher; @@ -32,9 +33,10 @@ public void transform(TypeTransformer transformer) { @SuppressWarnings("unused") public static class WithResourceAsyncAdvice { + @AssignReturned.ToReturned @Advice.OnMethodExit(suppress = Throwable.class) - public static void wrapFuture(@Advice.Return(readOnly = false) Future future) { - future = VertxClientSingletons.wrapFuture(future); + public static Future wrapFuture(@Advice.Return Future future) { + return VertxClientSingletons.wrapFuture(future); } } } diff --git a/instrumentation/vertx/vertx-http-client/vertx-http-client-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/v5_0/client/VertxClientInstrumentationModule.java b/instrumentation/vertx/vertx-http-client/vertx-http-client-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/v5_0/client/VertxClientInstrumentationModule.java index 03df65695bee..737a176f26d3 100644 --- a/instrumentation/vertx/vertx-http-client/vertx-http-client-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/v5_0/client/VertxClientInstrumentationModule.java +++ b/instrumentation/vertx/vertx-http-client/vertx-http-client-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/v5_0/client/VertxClientInstrumentationModule.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 VertxClientInstrumentationModule extends InstrumentationModule { +public class VertxClientInstrumentationModule extends InstrumentationModule + implements ExperimentalInstrumentationModule { public VertxClientInstrumentationModule() { super("vertx-http-client", "vertx-http-client-5.0", "vertx"); @@ -34,4 +36,9 @@ public List typeInstrumentations() { new HttpClientRequestBaseInstrumentation(), new ResourceManagerInstrumentation()); } + + @Override + public boolean isIndyReady() { + return true; + } } diff --git a/instrumentation/vertx/vertx-http-client/vertx-http-client-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/v5_0/client/VertxClientSingletons.java b/instrumentation/vertx/vertx-http-client/vertx-http-client-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/v5_0/client/VertxClientSingletons.java index e7deee794716..215f28b6daf7 100644 --- a/instrumentation/vertx/vertx-http-client/vertx-http-client-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/v5_0/client/VertxClientSingletons.java +++ b/instrumentation/vertx/vertx-http-client/vertx-http-client-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/v5_0/client/VertxClientSingletons.java @@ -9,6 +9,7 @@ import io.opentelemetry.context.Scope; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; import io.opentelemetry.instrumentation.api.util.VirtualField; +import io.opentelemetry.javaagent.instrumentation.vertx.client.Contexts; import io.opentelemetry.javaagent.instrumentation.vertx.client.VertxClientInstrumenterFactory; import io.vertx.core.Future; import io.vertx.core.http.HttpClientRequest; @@ -29,6 +30,9 @@ public static Instrumenter instrumenter() private static final VirtualField authorityField = VirtualField.find(HttpClientRequest.class, HostAndPort.class); + public static final VirtualField CONTEXTS = + VirtualField.find(HttpClientRequest.class, Contexts.class); + public static void setAuthority(HttpClientRequest request, HostAndPort authority) { authorityField.set(request, authority); } diff --git a/instrumentation/vertx/vertx-kafka-client-3.6/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/kafka/v3_6/KafkaReadStreamImplInstrumentation.java b/instrumentation/vertx/vertx-kafka-client-3.6/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/kafka/v3_6/KafkaReadStreamImplInstrumentation.java index 24d396cad2bb..fd996020f1bc 100644 --- a/instrumentation/vertx/vertx-kafka-client-3.6/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/kafka/v3_6/KafkaReadStreamImplInstrumentation.java +++ b/instrumentation/vertx/vertx-kafka-client-3.6/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/kafka/v3_6/KafkaReadStreamImplInstrumentation.java @@ -16,6 +16,8 @@ import io.vertx.core.Handler; import io.vertx.kafka.client.consumer.impl.KafkaReadStreamImpl; import net.bytebuddy.asm.Advice; +import net.bytebuddy.asm.Advice.AssignReturned; +import net.bytebuddy.asm.Advice.AssignReturned.ToArguments.ToArgument; import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.matcher.ElementMatcher; import org.apache.kafka.clients.consumer.ConsumerRecord; @@ -47,24 +49,26 @@ public void transform(TypeTransformer transformer) { @SuppressWarnings("unused") public static class HandlerAdvice { + @AssignReturned.ToArguments(@ToArgument(0)) @Advice.OnMethodEnter(suppress = Throwable.class) - public static void onEnter( + public static Handler> onEnter( @Advice.This KafkaReadStreamImpl readStream, - @Advice.Argument(value = 0, readOnly = false) Handler> handler) { + @Advice.Argument(0) Handler> handler) { - handler = new InstrumentedSingleRecordHandler<>(handler); + return new InstrumentedSingleRecordHandler<>(handler); } } @SuppressWarnings("unused") public static class BatchHandlerAdvice { + @AssignReturned.ToArguments(@ToArgument(0)) @Advice.OnMethodEnter(suppress = Throwable.class) - public static void onEnter( + public static Handler> onEnter( @Advice.This KafkaReadStreamImpl readStream, - @Advice.Argument(value = 0, readOnly = false) Handler> handler) { + @Advice.Argument(0) Handler> handler) { - handler = new InstrumentedBatchRecordsHandler<>(handler); + return new InstrumentedBatchRecordsHandler<>(handler); } } diff --git a/instrumentation/vertx/vertx-kafka-client-3.6/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/kafka/v3_6/VertxKafkaInstrumentationModule.java b/instrumentation/vertx/vertx-kafka-client-3.6/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/kafka/v3_6/VertxKafkaInstrumentationModule.java index 605351f87ee6..7e2fd2bbb874 100644 --- a/instrumentation/vertx/vertx-kafka-client-3.6/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/kafka/v3_6/VertxKafkaInstrumentationModule.java +++ b/instrumentation/vertx/vertx-kafka-client-3.6/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/kafka/v3_6/VertxKafkaInstrumentationModule.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 VertxKafkaInstrumentationModule extends InstrumentationModule { +public class VertxKafkaInstrumentationModule extends InstrumentationModule + implements ExperimentalInstrumentationModule { public VertxKafkaInstrumentationModule() { super("vertx-kafka-client", "vertx-kafka-client-3.6", "vertx"); @@ -24,4 +26,9 @@ public List typeInstrumentations() { return asList( new KafkaReadStreamImplInstrumentation(), new KafkaConsumerRecordsImplInstrumentation()); } + + @Override + public boolean isIndyReady() { + return true; + } } diff --git a/instrumentation/vertx/vertx-redis-client-4.0/javaagent/build.gradle.kts b/instrumentation/vertx/vertx-redis-client-4.0/javaagent/build.gradle.kts index 64fe65fce14b..db170f370492 100644 --- a/instrumentation/vertx/vertx-redis-client-4.0/javaagent/build.gradle.kts +++ b/instrumentation/vertx/vertx-redis-client-4.0/javaagent/build.gradle.kts @@ -29,8 +29,9 @@ tasks { } val testStableSemconv by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath jvmArgs("-Dotel.semconv-stability.opt-in=database") - systemProperty("collectMetadata", collectMetadata) systemProperty("metadataConfig", "otel.semconv-stability.opt-in=database") } diff --git a/instrumentation/vertx/vertx-redis-client-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/v4_0/redis/RedisStandaloneConnectionInstrumentation.java b/instrumentation/vertx/vertx-redis-client-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/v4_0/redis/RedisStandaloneConnectionInstrumentation.java index 157a7b2e3677..897a768504a5 100644 --- a/instrumentation/vertx/vertx-redis-client-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/v4_0/redis/RedisStandaloneConnectionInstrumentation.java +++ b/instrumentation/vertx/vertx-redis-client-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/v4_0/redis/RedisStandaloneConnectionInstrumentation.java @@ -5,7 +5,6 @@ package io.opentelemetry.javaagent.instrumentation.vertx.v4_0.redis; -import static io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge.currentContext; import static io.opentelemetry.javaagent.instrumentation.vertx.v4_0.redis.VertxRedisClientSingletons.instrumenter; import static net.bytebuddy.matcher.ElementMatchers.isConstructor; import static net.bytebuddy.matcher.ElementMatchers.named; @@ -21,7 +20,9 @@ import io.vertx.redis.client.impl.RedisStandaloneConnection; import io.vertx.redis.client.impl.RedisURI; import io.vertx.redis.client.impl.RequestUtil; +import javax.annotation.Nullable; import net.bytebuddy.asm.Advice; +import net.bytebuddy.asm.Advice.AssignReturned; import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.matcher.ElementMatcher; @@ -40,54 +41,83 @@ public void transform(TypeTransformer transformer) { @SuppressWarnings("unused") public static class SendAdvice { - @Advice.OnMethodEnter(suppress = Throwable.class) - public static void onEnter( - @Advice.This RedisStandaloneConnection connection, - @Advice.Argument(0) Request request, - @Advice.FieldValue("netSocket") NetSocket netSocket, - @Advice.Local("otelRequest") VertxRedisClientRequest otelRequest, - @Advice.Local("otelContext") Context context, - @Advice.Local("otelScope") Scope scope) { - if (request == null) { - return; + public static class AdviceScope { + private final VertxRedisClientRequest otelRequest; + private final Context context; + private final Scope scope; + + private AdviceScope(VertxRedisClientRequest otelRequest, Context context, Scope scope) { + this.otelRequest = otelRequest; + this.context = context; + this.scope = scope; } - String commandName = VertxRedisClientSingletons.getCommandName(request.command()); - RedisURI redisUri = VertxRedisClientSingletons.getRedisUri(connection); - if (commandName == null || redisUri == null) { - return; + @Nullable + public static AdviceScope start( + RedisStandaloneConnection connection, @Nullable Request request, NetSocket netSocket) { + + if (request == null) { + return null; + } + + String commandName = VertxRedisClientSingletons.getCommandName(request.command()); + RedisURI redisUri = VertxRedisClientSingletons.getRedisUri(connection); + if (commandName == null || redisUri == null) { + return null; + } + + VertxRedisClientRequest otelRequest = + new VertxRedisClientRequest( + commandName, RequestUtil.getArgs(request), redisUri, netSocket); + Context parentContext = Context.current(); + if (!instrumenter().shouldStart(parentContext, otelRequest)) { + return null; + } + Context context = instrumenter().start(parentContext, otelRequest); + return new AdviceScope(otelRequest, context, context.makeCurrent()); } - otelRequest = - new VertxRedisClientRequest( - commandName, RequestUtil.getArgs(request), redisUri, netSocket); - Context parentContext = currentContext(); - if (!instrumenter().shouldStart(parentContext, otelRequest)) { - return; + @Nullable + public Future end( + @Nullable Future responseFuture, @Nullable Throwable throwable) { + if (scope == null) { + return responseFuture; + } + + scope.close(); + if (throwable != null) { + instrumenter().end(context, otelRequest, null, throwable); + } else { + responseFuture = + VertxRedisClientSingletons.wrapEndSpan(responseFuture, context, otelRequest); + } + return responseFuture; } + } + + @Nullable + @Advice.OnMethodEnter(suppress = Throwable.class) + public static AdviceScope onEnter( + @Advice.This RedisStandaloneConnection connection, + @Advice.Argument(0) @Nullable Request request, + @Advice.FieldValue("netSocket") NetSocket netSocket) { - context = instrumenter().start(parentContext, otelRequest); - scope = context.makeCurrent(); + return AdviceScope.start(connection, request, netSocket); } + @Nullable + @AssignReturned.ToReturned @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) - public static void onExit( + public static Future onExit( @Advice.Thrown Throwable throwable, - @Advice.Return(readOnly = false) Future responseFuture, - @Advice.Local("otelRequest") VertxRedisClientRequest otelRequest, - @Advice.Local("otelContext") Context context, - @Advice.Local("otelScope") Scope scope) { - if (scope == null) { - return; - } + @Advice.Return @Nullable Future responseFuture, + @Advice.Enter @Nullable AdviceScope adviceScope) { - scope.close(); - if (throwable != null) { - instrumenter().end(context, otelRequest, null, throwable); - } else { - responseFuture = - VertxRedisClientSingletons.wrapEndSpan(responseFuture, context, otelRequest); + if (adviceScope != null) { + return adviceScope.end(responseFuture, throwable); } + + return responseFuture; } } diff --git a/instrumentation/vertx/vertx-redis-client-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/v4_0/redis/VertxRedisClientInstrumentationModule.java b/instrumentation/vertx/vertx-redis-client-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/v4_0/redis/VertxRedisClientInstrumentationModule.java index e3cab6a83ce5..9d7dc5300965 100644 --- a/instrumentation/vertx/vertx-redis-client-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/v4_0/redis/VertxRedisClientInstrumentationModule.java +++ b/instrumentation/vertx/vertx-redis-client-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/v4_0/redis/VertxRedisClientInstrumentationModule.java @@ -39,4 +39,9 @@ public List typeInstrumentations() { new RedisConnectionProviderInstrumentation(), new CommandImplInstrumentation()); } + + @Override + public boolean isIndyReady() { + return true; + } } diff --git a/instrumentation/vertx/vertx-rx-java-3.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/reactive/AsyncResultSingleInstrumentation.java b/instrumentation/vertx/vertx-rx-java-3.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/reactive/AsyncResultSingleInstrumentation.java index af81b85ffba1..4717f5a9d25c 100644 --- a/instrumentation/vertx/vertx-rx-java-3.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/reactive/AsyncResultSingleInstrumentation.java +++ b/instrumentation/vertx/vertx-rx-java-3.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/reactive/AsyncResultSingleInstrumentation.java @@ -18,6 +18,8 @@ import io.vertx.core.Handler; import java.util.function.Consumer; import net.bytebuddy.asm.Advice; +import net.bytebuddy.asm.Advice.AssignReturned; +import net.bytebuddy.asm.Advice.AssignReturned.ToArguments.ToArgument; import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.matcher.ElementMatcher; @@ -50,22 +52,22 @@ public void transform(TypeTransformer transformer) { @SuppressWarnings("unused") public static class ConstructorWithHandlerAdvice { + @AssignReturned.ToArguments(@ToArgument(0)) @Advice.OnMethodEnter(suppress = Throwable.class) - public static void wrapHandler( - @Advice.Argument(value = 0, readOnly = false) Handler>> handler) { - handler = - AsyncResultHandlerWrapper.wrapIfNeeded(handler, Java8BytecodeBridge.currentContext()); + public static Handler>> wrapHandler( + @Advice.Argument(0) Handler>> handler) { + return AsyncResultHandlerWrapper.wrapIfNeeded(handler, Java8BytecodeBridge.currentContext()); } } @SuppressWarnings("unused") public static class ConstructorWithConsumerAdvice { + @AssignReturned.ToArguments(@ToArgument(0)) @Advice.OnMethodEnter(suppress = Throwable.class) - public static void wrapHandler( - @Advice.Argument(value = 0, readOnly = false) Consumer>> handler) { - handler = - AsyncResultConsumerWrapper.wrapIfNeeded(handler, Java8BytecodeBridge.currentContext()); + public static Consumer>> wrapHandler( + @Advice.Argument(0) Consumer>> handler) { + return AsyncResultConsumerWrapper.wrapIfNeeded(handler, Java8BytecodeBridge.currentContext()); } } } diff --git a/instrumentation/vertx/vertx-rx-java-3.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/reactive/VertxRxInstrumentationModule.java b/instrumentation/vertx/vertx-rx-java-3.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/reactive/VertxRxInstrumentationModule.java index aef5154385cd..b31dc3df8880 100644 --- a/instrumentation/vertx/vertx-rx-java-3.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/reactive/VertxRxInstrumentationModule.java +++ b/instrumentation/vertx/vertx-rx-java-3.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/reactive/VertxRxInstrumentationModule.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 VertxRxInstrumentationModule extends InstrumentationModule { +public class VertxRxInstrumentationModule extends InstrumentationModule + implements ExperimentalInstrumentationModule { public VertxRxInstrumentationModule() { super("vertx-rx-java", "vertx-rx-java-3.5", "vertx"); @@ -23,4 +25,9 @@ public VertxRxInstrumentationModule() { public List typeInstrumentations() { return singletonList(new AsyncResultSingleInstrumentation()); } + + @Override + public boolean isIndyReady() { + return true; + } } diff --git a/instrumentation/vertx/vertx-sql-client/vertx-sql-client-4.0/javaagent/build.gradle.kts b/instrumentation/vertx/vertx-sql-client/vertx-sql-client-4.0/javaagent/build.gradle.kts index 082cd49cadd7..43a920dc0334 100644 --- a/instrumentation/vertx/vertx-sql-client/vertx-sql-client-4.0/javaagent/build.gradle.kts +++ b/instrumentation/vertx/vertx-sql-client/vertx-sql-client-4.0/javaagent/build.gradle.kts @@ -37,8 +37,9 @@ tasks { } val testStableSemconv by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath jvmArgs("-Dotel.semconv-stability.opt-in=database") - systemProperty("metadataConfig", "otel.semconv-stability.opt-in=database") } diff --git a/instrumentation/vertx/vertx-sql-client/vertx-sql-client-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/v4_0/sql/PoolInstrumentation.java b/instrumentation/vertx/vertx-sql-client/vertx-sql-client-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/v4_0/sql/PoolInstrumentation.java index 1a1f62f52a29..27f73b7a2d0e 100644 --- a/instrumentation/vertx/vertx-sql-client/vertx-sql-client-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/v4_0/sql/PoolInstrumentation.java +++ b/instrumentation/vertx/vertx-sql-client/vertx-sql-client-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/v4_0/sql/PoolInstrumentation.java @@ -27,6 +27,7 @@ import io.vertx.sqlclient.SqlConnectOptions; import io.vertx.sqlclient.SqlConnection; import net.bytebuddy.asm.Advice; +import net.bytebuddy.asm.Advice.AssignReturned; import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.matcher.ElementMatcher; @@ -60,23 +61,22 @@ public void transform(TypeTransformer transformer) { @SuppressWarnings("unused") public static class PoolAdvice { @Advice.OnMethodEnter(suppress = Throwable.class) - public static void onEnter( - @Advice.Argument(1) SqlConnectOptions sqlConnectOptions, - @Advice.Local("otelCallDepth") CallDepth callDepth) { - callDepth = CallDepth.forClass(Pool.class); + public static CallDepth onEnter(@Advice.Argument(1) SqlConnectOptions sqlConnectOptions) { + CallDepth callDepth = CallDepth.forClass(Pool.class); if (callDepth.getAndIncrement() > 0) { - return; + return callDepth; } // set connection options to ThreadLocal, they will be read in SqlClientBase constructor setSqlConnectOptions(sqlConnectOptions); + return callDepth; } @Advice.OnMethodExit(suppress = Throwable.class) public static void onExit( @Advice.Return Pool pool, @Advice.Argument(1) SqlConnectOptions sqlConnectOptions, - @Advice.Local("otelCallDepth") CallDepth callDepth) { + @Advice.Enter CallDepth callDepth) { if (callDepth.decrementAndGet() > 0) { return; } @@ -88,14 +88,13 @@ public static void onExit( @SuppressWarnings("unused") public static class GetConnectionAdvice { + @AssignReturned.ToReturned @Advice.OnMethodExit(suppress = Throwable.class) - public static void onExit( - @Advice.This Pool pool, @Advice.Return(readOnly = false) Future future) { + public static Future onExit( + @Advice.This Pool pool, @Advice.Return Future future) { // copy connect options stored on pool to new connection SqlConnectOptions sqlConnectOptions = getPoolSqlConnectOptions(pool); - - future = attachConnectOptions(future, sqlConnectOptions); - future = wrapContext(future); + return wrapContext(attachConnectOptions(future, sqlConnectOptions)); } } } diff --git a/instrumentation/vertx/vertx-sql-client/vertx-sql-client-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/v4_0/sql/QueryExecutorInstrumentation.java b/instrumentation/vertx/vertx-sql-client/vertx-sql-client-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/v4_0/sql/QueryExecutorInstrumentation.java index c724abbe1192..b7970e8cb949 100644 --- a/instrumentation/vertx/vertx-sql-client/vertx-sql-client-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/v4_0/sql/QueryExecutorInstrumentation.java +++ b/instrumentation/vertx/vertx-sql-client/vertx-sql-client-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/v4_0/sql/QueryExecutorInstrumentation.java @@ -5,7 +5,6 @@ package io.opentelemetry.javaagent.instrumentation.vertx.v4_0.sql; -import static io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge.currentContext; import static io.opentelemetry.javaagent.instrumentation.vertx.sql.VertxSqlClientUtil.getSqlConnectOptions; import static io.opentelemetry.javaagent.instrumentation.vertx.v4_0.sql.VertxSqlClientSingletons.instrumenter; import static net.bytebuddy.matcher.ElementMatchers.isConstructor; @@ -22,6 +21,7 @@ import io.vertx.core.impl.future.PromiseInternal; import io.vertx.sqlclient.impl.PreparedStatement; import io.vertx.sqlclient.impl.QueryExecutorUtil; +import javax.annotation.Nullable; import net.bytebuddy.asm.Advice; import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.matcher.ElementMatcher; @@ -54,71 +54,94 @@ public static void onExit(@Advice.This Object queryExecutor) { @SuppressWarnings("unused") public static class QueryAdvice { - @Advice.OnMethodEnter(suppress = Throwable.class) - public static void onEnter( - @Advice.This Object queryExecutor, - @Advice.AllArguments Object[] arguments, - @Advice.Local("otelCallDepth") CallDepth callDepth, - @Advice.Local("otelRequest") VertxSqlClientRequest otelRequest, - @Advice.Local("otelContext") Context context, - @Advice.Local("otelScope") Scope scope) { - callDepth = CallDepth.forClass(queryExecutor.getClass()); - if (callDepth.getAndIncrement() > 0) { - return; + + public static class AdviceScope { + private final CallDepth callDepth; + @Nullable private final VertxSqlClientRequest otelRequest; + @Nullable private final Context context; + @Nullable private final Scope scope; + + private AdviceScope(CallDepth callDepth) { + this(callDepth, null, null, null); + } + + private AdviceScope( + CallDepth callDepth, VertxSqlClientRequest otelRequest, Context context, Scope scope) { + this.callDepth = callDepth; + this.otelRequest = otelRequest; + this.context = context; + this.scope = scope; } - // The parameter we need are in different positions, we are not going to have separate - // advices for all of them. The method gets the statement either as String or - // PreparedStatement, use the first argument that is either of these. PromiseInternal is - // always at the end of the argument list. - String sql = null; - PromiseInternal promiseInternal = null; - for (Object argument : arguments) { - if (sql == null) { - if (argument instanceof String) { - sql = (String) argument; - } else if (argument instanceof PreparedStatement) { - sql = ((PreparedStatement) argument).sql(); + public static AdviceScope start(Object queryExecutor, Object[] arguments) { + CallDepth callDepth = CallDepth.forClass(queryExecutor.getClass()); + if (callDepth.getAndIncrement() > 0) { + return new AdviceScope(callDepth); + } + + // The parameter we need are in different positions, we are not going to have separate + // advices for all of them. The method gets the statement either as String or + // PreparedStatement, use the first argument that is either of these. PromiseInternal is + // always at the end of the argument list. + String sql = null; + PromiseInternal promiseInternal = null; + for (Object argument : arguments) { + if (sql == null) { + if (argument instanceof String) { + sql = (String) argument; + } else if (argument instanceof PreparedStatement) { + sql = ((PreparedStatement) argument).sql(); + } + } else if (argument instanceof PromiseInternal) { + promiseInternal = (PromiseInternal) argument; } - } else if (argument instanceof PromiseInternal) { - promiseInternal = (PromiseInternal) argument; } + if (sql == null || promiseInternal == null) { + return new AdviceScope(callDepth); + } + + VertxSqlClientRequest otelRequest = + new VertxSqlClientRequest(sql, QueryExecutorUtil.getConnectOptions(queryExecutor)); + Context parentContext = Context.current(); + if (!instrumenter().shouldStart(parentContext, otelRequest)) { + return new AdviceScope(callDepth, null, null, null); + } + + Context context = instrumenter().start(parentContext, otelRequest); + VertxSqlClientUtil.attachRequest(promiseInternal, otelRequest, context, parentContext); + return new AdviceScope(callDepth, otelRequest, context, context.makeCurrent()); } - if (sql == null || promiseInternal == null) { - return; - } - otelRequest = - new VertxSqlClientRequest(sql, QueryExecutorUtil.getConnectOptions(queryExecutor)); - Context parentContext = currentContext(); - if (!instrumenter().shouldStart(parentContext, otelRequest)) { - return; + public void end(@Nullable Throwable throwable) { + if (callDepth.decrementAndGet() > 0) { + return; + } + if (scope == null || context == null || otelRequest == null) { + return; + } + + scope.close(); + if (throwable != null) { + instrumenter().end(context, otelRequest, null, throwable); + } + // span will be ended in QueryResultBuilderInstrumentation } + } - context = instrumenter().start(parentContext, otelRequest); - scope = context.makeCurrent(); - VertxSqlClientUtil.attachRequest(promiseInternal, otelRequest, context, parentContext); + @Advice.OnMethodEnter(suppress = Throwable.class) + public static AdviceScope onEnter( + @Advice.This Object queryExecutor, @Advice.AllArguments Object[] arguments) { + return AdviceScope.start(queryExecutor, arguments); } @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) public static void onExit( - @Advice.Thrown Throwable throwable, - @Advice.Local("otelCallDepth") CallDepth callDepth, - @Advice.Local("otelRequest") VertxSqlClientRequest otelRequest, - @Advice.Local("otelContext") Context context, - @Advice.Local("otelScope") Scope scope) { - if (callDepth.decrementAndGet() > 0) { - return; - } - if (scope == null) { - return; - } + @Advice.Thrown @Nullable Throwable throwable, + @Advice.Enter @Nullable AdviceScope adviceScope) { - scope.close(); - if (throwable != null) { - instrumenter().end(context, otelRequest, null, throwable); + if (adviceScope != null) { + adviceScope.end(throwable); } - // span will be ended in QueryResultBuilderInstrumentation } } } diff --git a/instrumentation/vertx/vertx-sql-client/vertx-sql-client-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/v4_0/sql/SqlClientBaseInstrumentation.java b/instrumentation/vertx/vertx-sql-client/vertx-sql-client-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/v4_0/sql/SqlClientBaseInstrumentation.java index af80451e7800..8ca141b75a90 100644 --- a/instrumentation/vertx/vertx-sql-client/vertx-sql-client-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/v4_0/sql/SqlClientBaseInstrumentation.java +++ b/instrumentation/vertx/vertx-sql-client/vertx-sql-client-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/v4_0/sql/SqlClientBaseInstrumentation.java @@ -50,22 +50,21 @@ public static void onExit(@Advice.This SqlClientBase sqlClientBase) { @SuppressWarnings("unused") public static class QueryAdvice { @Advice.OnMethodEnter(suppress = Throwable.class) - public static void onEnter( - @Advice.This SqlClientBase sqlClientBase, - @Advice.Local("otelCallDepth") CallDepth callDepth) { - callDepth = CallDepth.forClass(SqlClientBase.class); + public static CallDepth onEnter(@Advice.This SqlClientBase sqlClientBase) { + CallDepth callDepth = CallDepth.forClass(SqlClientBase.class); if (callDepth.getAndIncrement() > 0) { - return; + return callDepth; } // set connection options to ThreadLocal, they will be read in QueryExecutor constructor SqlConnectOptions sqlConnectOptions = getSqlConnectOptions(sqlClientBase); setSqlConnectOptions(sqlConnectOptions); + return callDepth; } @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) public static void onExit( - @Advice.Thrown Throwable throwable, @Advice.Local("otelCallDepth") CallDepth callDepth) { + @Advice.Thrown Throwable throwable, @Advice.Enter CallDepth callDepth) { if (callDepth.decrementAndGet() > 0) { return; } diff --git a/instrumentation/vertx/vertx-sql-client/vertx-sql-client-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/v4_0/sql/TransactionImplInstrumentation.java b/instrumentation/vertx/vertx-sql-client/vertx-sql-client-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/v4_0/sql/TransactionImplInstrumentation.java index 464479dd63d6..44263741cf1e 100644 --- a/instrumentation/vertx/vertx-sql-client/vertx-sql-client-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/v4_0/sql/TransactionImplInstrumentation.java +++ b/instrumentation/vertx/vertx-sql-client/vertx-sql-client-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/v4_0/sql/TransactionImplInstrumentation.java @@ -12,6 +12,7 @@ import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; import io.vertx.core.Handler; import net.bytebuddy.asm.Advice; +import net.bytebuddy.asm.Advice.AssignReturned; import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.matcher.ElementMatcher; @@ -31,9 +32,10 @@ public void transform(TypeTransformer transformer) { @SuppressWarnings("unused") public static class WrapHandlerAdvice { + @AssignReturned.ToReturned @Advice.OnMethodExit(suppress = Throwable.class) - public static void wrapHandler(@Advice.Return(readOnly = false) Handler handler) { - handler = HandlerWrapper.wrap(handler); + public static Handler wrapHandler(@Advice.Return Handler handler) { + return HandlerWrapper.wrap(handler); } } } diff --git a/instrumentation/vertx/vertx-sql-client/vertx-sql-client-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/v4_0/sql/VertxSqlClientInstrumentationModule.java b/instrumentation/vertx/vertx-sql-client/vertx-sql-client-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/v4_0/sql/VertxSqlClientInstrumentationModule.java index 00523bba06cc..e6f0a5d90670 100644 --- a/instrumentation/vertx/vertx-sql-client/vertx-sql-client-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/v4_0/sql/VertxSqlClientInstrumentationModule.java +++ b/instrumentation/vertx/vertx-sql-client/vertx-sql-client-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/v4_0/sql/VertxSqlClientInstrumentationModule.java @@ -49,4 +49,9 @@ public List typeInstrumentations() { new QueryResultBuilderInstrumentation(), new TransactionImplInstrumentation()); } + + @Override + public boolean isIndyReady() { + return true; + } } diff --git a/instrumentation/vertx/vertx-sql-client/vertx-sql-client-5.0/javaagent/build.gradle.kts b/instrumentation/vertx/vertx-sql-client/vertx-sql-client-5.0/javaagent/build.gradle.kts index 4fa02754e0df..20707eb61410 100644 --- a/instrumentation/vertx/vertx-sql-client/vertx-sql-client-5.0/javaagent/build.gradle.kts +++ b/instrumentation/vertx/vertx-sql-client/vertx-sql-client-5.0/javaagent/build.gradle.kts @@ -37,8 +37,9 @@ tasks { } val testStableSemconv by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath jvmArgs("-Dotel.semconv-stability.opt-in=database") - systemProperty("metadataConfig", "otel.semconv-stability.opt-in=database") } diff --git a/instrumentation/vertx/vertx-sql-client/vertx-sql-client-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/v5_0/sql/PoolInstrumentation.java b/instrumentation/vertx/vertx-sql-client/vertx-sql-client-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/v5_0/sql/PoolInstrumentation.java index 4ff4b22b259d..bb560548a3f4 100644 --- a/instrumentation/vertx/vertx-sql-client/vertx-sql-client-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/v5_0/sql/PoolInstrumentation.java +++ b/instrumentation/vertx/vertx-sql-client/vertx-sql-client-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/v5_0/sql/PoolInstrumentation.java @@ -19,7 +19,6 @@ import static net.bytebuddy.matcher.ElementMatchers.takesArguments; import static net.bytebuddy.matcher.ElementMatchers.takesNoArguments; -import io.opentelemetry.instrumentation.api.util.VirtualField; import io.opentelemetry.javaagent.bootstrap.CallDepth; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; @@ -28,6 +27,7 @@ import io.vertx.sqlclient.SqlConnectOptions; import io.vertx.sqlclient.SqlConnection; import net.bytebuddy.asm.Advice; +import net.bytebuddy.asm.Advice.AssignReturned; import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.matcher.ElementMatcher; @@ -60,32 +60,28 @@ public void transform(TypeTransformer transformer) { @SuppressWarnings("unused") public static class PoolAdvice { + @Advice.OnMethodEnter(suppress = Throwable.class) - public static void onEnter( - @Advice.Argument(1) SqlConnectOptions sqlConnectOptions, - @Advice.Local("otelCallDepth") CallDepth callDepth) { - callDepth = CallDepth.forClass(Pool.class); + public static CallDepth onEnter(@Advice.Argument(1) SqlConnectOptions sqlConnectOptions) { + CallDepth callDepth = CallDepth.forClass(Pool.class); if (callDepth.getAndIncrement() > 0) { - return; + return callDepth; } // set connection options to ThreadLocal, they will be read in SqlClientBase constructor setSqlConnectOptions(sqlConnectOptions); + return callDepth; } @Advice.OnMethodExit(suppress = Throwable.class) public static void onExit( @Advice.Return Pool pool, @Advice.Argument(1) SqlConnectOptions sqlConnectOptions, - @Advice.Local("otelCallDepth") CallDepth callDepth) { + @Advice.Enter CallDepth callDepth) { if (callDepth.decrementAndGet() > 0) { return; } - VirtualField virtualField = - VirtualField.find(Pool.class, SqlConnectOptions.class); - virtualField.set(pool, sqlConnectOptions); - setPoolConnectOptions(pool, sqlConnectOptions); setSqlConnectOptions(null); } @@ -93,14 +89,14 @@ public static void onExit( @SuppressWarnings("unused") public static class GetConnectionAdvice { + @AssignReturned.ToReturned @Advice.OnMethodExit(suppress = Throwable.class) - public static void onExit( - @Advice.This Pool pool, @Advice.Return(readOnly = false) Future future) { + public static Future onExit( + @Advice.This Pool pool, @Advice.Return Future future) { // copy connect options stored on pool to new connection SqlConnectOptions sqlConnectOptions = getPoolSqlConnectOptions(pool); - future = attachConnectOptions(future, sqlConnectOptions); - future = wrapContext(future); + return wrapContext(attachConnectOptions(future, sqlConnectOptions)); } } } diff --git a/instrumentation/vertx/vertx-sql-client/vertx-sql-client-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/v5_0/sql/QueryExecutorInstrumentation.java b/instrumentation/vertx/vertx-sql-client/vertx-sql-client-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/v5_0/sql/QueryExecutorInstrumentation.java index 0e78cb8e099f..ce532b34ad8e 100644 --- a/instrumentation/vertx/vertx-sql-client/vertx-sql-client-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/v5_0/sql/QueryExecutorInstrumentation.java +++ b/instrumentation/vertx/vertx-sql-client/vertx-sql-client-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/v5_0/sql/QueryExecutorInstrumentation.java @@ -5,7 +5,6 @@ package io.opentelemetry.javaagent.instrumentation.vertx.v5_0.sql; -import static io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge.currentContext; import static io.opentelemetry.javaagent.instrumentation.vertx.sql.VertxSqlClientUtil.getSqlConnectOptions; import static io.opentelemetry.javaagent.instrumentation.vertx.v5_0.sql.VertxSqlClientSingletons.instrumenter; import static net.bytebuddy.matcher.ElementMatchers.isConstructor; @@ -22,6 +21,7 @@ import io.vertx.core.internal.PromiseInternal; import io.vertx.sqlclient.impl.QueryExecutorUtil; import io.vertx.sqlclient.internal.PreparedStatement; +import javax.annotation.Nullable; import net.bytebuddy.asm.Advice; import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.matcher.ElementMatcher; @@ -54,71 +54,89 @@ public static void onExit(@Advice.This Object queryExecutor) { @SuppressWarnings("unused") public static class QueryAdvice { - @Advice.OnMethodEnter(suppress = Throwable.class) - public static void onEnter( - @Advice.This Object queryExecutor, - @Advice.AllArguments Object[] arguments, - @Advice.Local("otelCallDepth") CallDepth callDepth, - @Advice.Local("otelRequest") VertxSqlClientRequest otelRequest, - @Advice.Local("otelContext") Context context, - @Advice.Local("otelScope") Scope scope) { - callDepth = CallDepth.forClass(queryExecutor.getClass()); - if (callDepth.getAndIncrement() > 0) { - return; + public static class AdviceScope { + private final CallDepth callDepth; + @Nullable private final VertxSqlClientRequest otelRequest; + @Nullable private final Context context; + @Nullable private final Scope scope; + + private AdviceScope(CallDepth callDepth) { + this(callDepth, null, null, null); + } + + private AdviceScope( + CallDepth callDepth, VertxSqlClientRequest otelRequest, Context context, Scope scope) { + this.callDepth = callDepth; + this.otelRequest = otelRequest; + this.context = context; + this.scope = scope; } - // The parameter we need are in different positions, we are not going to have separate - // advices for all of them. The method gets the statement either as String or - // PreparedStatement, use the first argument that is either of these. PromiseInternal is - // always at the end of the argument list. - String sql = null; - PromiseInternal promiseInternal = null; - for (Object argument : arguments) { - if (sql == null) { - if (argument instanceof String) { - sql = (String) argument; - } else if (argument instanceof PreparedStatement) { - sql = ((PreparedStatement) argument).sql(); + public static AdviceScope start(Object queryExecutor, Object[] arguments) { + CallDepth callDepth = CallDepth.forClass(queryExecutor.getClass()); + if (callDepth.getAndIncrement() > 0) { + return new AdviceScope(callDepth); + } + + // The parameter we need are in different positions, we are not going to have separate + // advices for all of them. The method gets the statement either as String or + // PreparedStatement, use the first argument that is either of these. PromiseInternal is + // always at the end of the argument list. + String sql = null; + PromiseInternal promiseInternal = null; + for (Object argument : arguments) { + if (sql == null) { + if (argument instanceof String) { + sql = (String) argument; + } else if (argument instanceof PreparedStatement) { + sql = ((PreparedStatement) argument).sql(); + } + } else if (argument instanceof PromiseInternal) { + promiseInternal = (PromiseInternal) argument; } - } else if (argument instanceof PromiseInternal) { - promiseInternal = (PromiseInternal) argument; } - } - if (sql == null || promiseInternal == null) { - return; + if (sql == null || promiseInternal == null) { + return new AdviceScope(callDepth); + } + + VertxSqlClientRequest otelRequest = + new VertxSqlClientRequest(sql, QueryExecutorUtil.getConnectOptions(queryExecutor)); + Context parentContext = Context.current(); + if (!instrumenter().shouldStart(parentContext, otelRequest)) { + return new AdviceScope(callDepth); + } + + Context context = instrumenter().start(parentContext, otelRequest); + VertxSqlClientUtil.attachRequest(promiseInternal, otelRequest, context, parentContext); + return new AdviceScope(callDepth, otelRequest, context, context.makeCurrent()); } - otelRequest = - new VertxSqlClientRequest(sql, QueryExecutorUtil.getConnectOptions(queryExecutor)); - Context parentContext = currentContext(); - if (!instrumenter().shouldStart(parentContext, otelRequest)) { - return; + public void end(Throwable throwable) { + if (callDepth.decrementAndGet() > 0) { + return; + } + if (scope == null || context == null || otelRequest == null) { + return; + } + + scope.close(); + if (throwable != null) { + instrumenter().end(context, otelRequest, null, throwable); + } + // span will be ended in QueryResultBuilderInstrumentation } + } - context = instrumenter().start(parentContext, otelRequest); - scope = context.makeCurrent(); - VertxSqlClientUtil.attachRequest(promiseInternal, otelRequest, context, parentContext); + @Advice.OnMethodEnter(suppress = Throwable.class) + public static AdviceScope onEnter( + @Advice.This Object queryExecutor, @Advice.AllArguments Object[] arguments) { + return AdviceScope.start(queryExecutor, arguments); } @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) public static void onExit( - @Advice.Thrown Throwable throwable, - @Advice.Local("otelCallDepth") CallDepth callDepth, - @Advice.Local("otelRequest") VertxSqlClientRequest otelRequest, - @Advice.Local("otelContext") Context context, - @Advice.Local("otelScope") Scope scope) { - if (callDepth.decrementAndGet() > 0) { - return; - } - if (scope == null) { - return; - } - - scope.close(); - if (throwable != null) { - instrumenter().end(context, otelRequest, null, throwable); - } - // span will be ended in QueryResultBuilderInstrumentation + @Advice.Thrown @Nullable Throwable throwable, @Advice.Enter AdviceScope adviceScope) { + adviceScope.end(throwable); } } } diff --git a/instrumentation/vertx/vertx-sql-client/vertx-sql-client-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/v5_0/sql/SqlClientBaseInstrumentation.java b/instrumentation/vertx/vertx-sql-client/vertx-sql-client-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/v5_0/sql/SqlClientBaseInstrumentation.java index 894d1ba10e67..bb0b37e7877d 100644 --- a/instrumentation/vertx/vertx-sql-client/vertx-sql-client-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/v5_0/sql/SqlClientBaseInstrumentation.java +++ b/instrumentation/vertx/vertx-sql-client/vertx-sql-client-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/v5_0/sql/SqlClientBaseInstrumentation.java @@ -50,22 +50,21 @@ public static void onExit(@Advice.This SqlClientBase sqlClientBase) { @SuppressWarnings("unused") public static class QueryAdvice { @Advice.OnMethodEnter(suppress = Throwable.class) - public static void onEnter( - @Advice.This SqlClientBase sqlClientBase, - @Advice.Local("otelCallDepth") CallDepth callDepth) { - callDepth = CallDepth.forClass(SqlClientBase.class); + public static CallDepth onEnter(@Advice.This SqlClientBase sqlClientBase) { + CallDepth callDepth = CallDepth.forClass(SqlClientBase.class); if (callDepth.getAndIncrement() > 0) { - return; + return callDepth; } // set connection options to ThreadLocal, they will be read in QueryExecutor constructor SqlConnectOptions sqlConnectOptions = getSqlConnectOptions(sqlClientBase); setSqlConnectOptions(sqlConnectOptions); + return callDepth; } @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) public static void onExit( - @Advice.Thrown Throwable throwable, @Advice.Local("otelCallDepth") CallDepth callDepth) { + @Advice.Thrown Throwable throwable, @Advice.Enter CallDepth callDepth) { if (callDepth.decrementAndGet() > 0) { return; } diff --git a/instrumentation/vertx/vertx-sql-client/vertx-sql-client-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/v5_0/sql/TransactionImplInstrumentation.java b/instrumentation/vertx/vertx-sql-client/vertx-sql-client-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/v5_0/sql/TransactionImplInstrumentation.java index 627e58f4420f..e44755492b69 100644 --- a/instrumentation/vertx/vertx-sql-client/vertx-sql-client-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/v5_0/sql/TransactionImplInstrumentation.java +++ b/instrumentation/vertx/vertx-sql-client/vertx-sql-client-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/v5_0/sql/TransactionImplInstrumentation.java @@ -12,6 +12,7 @@ import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; import io.vertx.core.Completable; import net.bytebuddy.asm.Advice; +import net.bytebuddy.asm.Advice.AssignReturned; import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.matcher.ElementMatcher; @@ -31,9 +32,10 @@ public void transform(TypeTransformer transformer) { @SuppressWarnings("unused") public static class WrapHandlerAdvice { + @AssignReturned.ToReturned @Advice.OnMethodExit(suppress = Throwable.class) - public static void wrapHandler(@Advice.Return(readOnly = false) Completable handler) { - handler = CompletableWrapper.wrap(handler); + public static Completable wrapHandler(@Advice.Return Completable handler) { + return CompletableWrapper.wrap(handler); } } } diff --git a/instrumentation/vertx/vertx-sql-client/vertx-sql-client-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/v5_0/sql/VertxSqlClientInstrumentationModule.java b/instrumentation/vertx/vertx-sql-client/vertx-sql-client-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/v5_0/sql/VertxSqlClientInstrumentationModule.java index 5f02f9522e6d..25c2c7da6b4f 100644 --- a/instrumentation/vertx/vertx-sql-client/vertx-sql-client-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/v5_0/sql/VertxSqlClientInstrumentationModule.java +++ b/instrumentation/vertx/vertx-sql-client/vertx-sql-client-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/v5_0/sql/VertxSqlClientInstrumentationModule.java @@ -49,4 +49,9 @@ public List typeInstrumentations() { new QueryResultBuilderInstrumentation(), new TransactionImplInstrumentation()); } + + @Override + public boolean isIndyReady() { + return true; + } } diff --git a/instrumentation/vertx/vertx-web-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/RouteInstrumentation.java b/instrumentation/vertx/vertx-web-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/RouteInstrumentation.java index c01649b8c713..8eb5e301669d 100644 --- a/instrumentation/vertx/vertx-web-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/RouteInstrumentation.java +++ b/instrumentation/vertx/vertx-web-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/RouteInstrumentation.java @@ -16,6 +16,8 @@ import io.vertx.core.Handler; import io.vertx.ext.web.RoutingContext; import net.bytebuddy.asm.Advice; +import net.bytebuddy.asm.Advice.AssignReturned; +import net.bytebuddy.asm.Advice.AssignReturned.ToArguments.ToArgument; import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.matcher.ElementMatcher; @@ -40,10 +42,11 @@ public void transform(TypeTransformer transformer) { @SuppressWarnings("unused") public static class HandlerAdvice { + @AssignReturned.ToArguments(@ToArgument(0)) @Advice.OnMethodEnter(suppress = Throwable.class) - public static void wrapHandler( - @Advice.Argument(value = 0, readOnly = false) Handler handler) { - handler = new RoutingContextHandlerWrapper(handler); + public static Handler wrapHandler( + @Advice.Argument(0) Handler handler) { + return new RoutingContextHandlerWrapper(handler); } } } diff --git a/instrumentation/vertx/vertx-web-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/VertxWebInstrumentationModule.java b/instrumentation/vertx/vertx-web-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/VertxWebInstrumentationModule.java index 659c2dfe507a..392bd6ce67cc 100644 --- a/instrumentation/vertx/vertx-web-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/VertxWebInstrumentationModule.java +++ b/instrumentation/vertx/vertx-web-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/VertxWebInstrumentationModule.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 VertxWebInstrumentationModule extends InstrumentationModule { +public class VertxWebInstrumentationModule extends InstrumentationModule + implements ExperimentalInstrumentationModule { public VertxWebInstrumentationModule() { super("vertx-web", "vertx-web-3.0", "vertx"); @@ -23,4 +25,9 @@ public VertxWebInstrumentationModule() { public List typeInstrumentations() { return asList(new RouteInstrumentation(), new RoutingContextInstrumentation()); } + + @Override + public boolean isIndyReady() { + return true; + } } diff --git a/instrumentation/vibur-dbcp-11.0/javaagent/build.gradle.kts b/instrumentation/vibur-dbcp-11.0/javaagent/build.gradle.kts index f2011616fb2d..838f41b82854 100644 --- a/instrumentation/vibur-dbcp-11.0/javaagent/build.gradle.kts +++ b/instrumentation/vibur-dbcp-11.0/javaagent/build.gradle.kts @@ -23,8 +23,9 @@ val collectMetadata = findProperty("collectMetadata")?.toString() ?: "false" tasks { val testStableSemconv by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath jvmArgs("-Dotel.semconv-stability.opt-in=database") - systemProperty("collectMetadata", collectMetadata) systemProperty("metadataConfig", "otel.semconv-stability.opt-in=database") } diff --git a/instrumentation/vibur-dbcp-11.0/library/README.md b/instrumentation/vibur-dbcp-11.0/library/README.md index f911e0e39e87..149d8da48a18 100644 --- a/instrumentation/vibur-dbcp-11.0/library/README.md +++ b/instrumentation/vibur-dbcp-11.0/library/README.md @@ -7,7 +7,7 @@ Provides OpenTelemetry instrumentation for [Vibur DBCP](https://www.vibur.org/). ### Add these dependencies to your project Replace `OPENTELEMETRY_VERSION` with the [latest -release](https://search.maven.org/search?q=g:io.opentelemetry.instrumentation%20AND%20a:opentelemetry-vibur-dbcp-11.0). +release]( https://central.sonatype.com/artifact/io.opentelemetry.instrumentation/opentelemetry-vibur-dbcp-11.0). For Maven, add to your `pom.xml` dependencies: diff --git a/instrumentation/vibur-dbcp-11.0/library/build.gradle.kts b/instrumentation/vibur-dbcp-11.0/library/build.gradle.kts index 9f0a4b44cb9c..41cd2789b194 100644 --- a/instrumentation/vibur-dbcp-11.0/library/build.gradle.kts +++ b/instrumentation/vibur-dbcp-11.0/library/build.gradle.kts @@ -11,6 +11,8 @@ dependencies { tasks { val testStableSemconv by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath jvmArgs("-Dotel.semconv-stability.opt-in=database") } diff --git a/instrumentation/vibur-dbcp-11.0/metadata.yaml b/instrumentation/vibur-dbcp-11.0/metadata.yaml index 991cdddecce9..bd49143610d0 100644 --- a/instrumentation/vibur-dbcp-11.0/metadata.yaml +++ b/instrumentation/vibur-dbcp-11.0/metadata.yaml @@ -1 +1,2 @@ description: Instrumentation for the vibur-dbcp library, which provides connection pool metrics. +library_link: https://www.vibur.org/ diff --git a/instrumentation/xxl-job/xxl-job-1.9.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/xxljob/v1_9_2/GlueJobHandlerInstrumentation.java b/instrumentation/xxl-job/xxl-job-1.9.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/xxljob/v1_9_2/GlueJobHandlerInstrumentation.java index 5c737335ebcb..f43f5b39310a 100644 --- a/instrumentation/xxl-job/xxl-job-1.9.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/xxljob/v1_9_2/GlueJobHandlerInstrumentation.java +++ b/instrumentation/xxl-job/xxl-job-1.9.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/xxljob/v1_9_2/GlueJobHandlerInstrumentation.java @@ -5,18 +5,17 @@ package io.opentelemetry.javaagent.instrumentation.xxljob.v1_9_2; -import static io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge.currentContext; import static io.opentelemetry.javaagent.instrumentation.xxljob.v1_9_2.XxlJobSingletons.helper; import static net.bytebuddy.matcher.ElementMatchers.isPublic; import static net.bytebuddy.matcher.ElementMatchers.named; import static net.bytebuddy.matcher.ElementMatchers.takesArguments; import com.xxl.job.core.handler.IJobHandler; -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.xxljob.common.XxlJobHelper; import io.opentelemetry.javaagent.instrumentation.xxljob.common.XxlJobProcessRequest; +import javax.annotation.Nullable; import net.bytebuddy.asm.Advice; import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.implementation.bytecode.assign.Assigner; @@ -39,29 +38,20 @@ public void transform(TypeTransformer transformer) { @SuppressWarnings("unused") public static class ScheduleAdvice { + @Nullable @Advice.OnMethodEnter(suppress = Throwable.class) - public static void onSchedule( - @Advice.FieldValue("jobHandler") IJobHandler handler, - @Advice.Local("otelRequest") XxlJobProcessRequest request, - @Advice.Local("otelContext") Context context, - @Advice.Local("otelScope") Scope scope) { - Context parentContext = currentContext(); - request = XxlJobProcessRequest.createGlueJobRequest(handler); - context = helper().startSpan(parentContext, request); - if (context == null) { - return; - } - scope = context.makeCurrent(); + public static XxlJobHelper.XxlJobScope onSchedule( + @Advice.FieldValue("jobHandler") IJobHandler handler) { + return helper().startSpan(XxlJobProcessRequest.createGlueJobRequest(handler)); } @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) public static void stopSpan( - @Advice.Return(typing = Assigner.Typing.DYNAMIC) Object result, - @Advice.Thrown Throwable throwable, - @Advice.Local("otelRequest") XxlJobProcessRequest request, - @Advice.Local("otelContext") Context context, - @Advice.Local("otelScope") Scope scope) { - helper().stopSpan(result, request, throwable, scope, context); + @Advice.Return(typing = Assigner.Typing.DYNAMIC) @Nullable Object result, + @Advice.Thrown @Nullable Throwable throwable, + @Advice.Enter @Nullable XxlJobHelper.XxlJobScope scope) { + + helper().endSpan(scope, result, throwable); } } } diff --git a/instrumentation/xxl-job/xxl-job-1.9.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/xxljob/v1_9_2/ScriptJobHandlerInstrumentation.java b/instrumentation/xxl-job/xxl-job-1.9.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/xxljob/v1_9_2/ScriptJobHandlerInstrumentation.java index 7e947a24ffec..eaa87c0445ce 100644 --- a/instrumentation/xxl-job/xxl-job-1.9.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/xxljob/v1_9_2/ScriptJobHandlerInstrumentation.java +++ b/instrumentation/xxl-job/xxl-job-1.9.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/xxljob/v1_9_2/ScriptJobHandlerInstrumentation.java @@ -5,7 +5,6 @@ package io.opentelemetry.javaagent.instrumentation.xxljob.v1_9_2; -import static io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge.currentContext; import static io.opentelemetry.javaagent.instrumentation.xxljob.v1_9_2.XxlJobSingletons.helper; import static net.bytebuddy.matcher.ElementMatchers.isPublic; import static net.bytebuddy.matcher.ElementMatchers.named; @@ -13,11 +12,11 @@ import static net.bytebuddy.matcher.ElementMatchers.takesArguments; import com.xxl.job.core.glue.GlueTypeEnum; -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.xxljob.common.XxlJobHelper; import io.opentelemetry.javaagent.instrumentation.xxljob.common.XxlJobProcessRequest; +import javax.annotation.Nullable; import net.bytebuddy.asm.Advice; import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.implementation.bytecode.assign.Assigner; @@ -40,30 +39,20 @@ public void transform(TypeTransformer transformer) { @SuppressWarnings("unused") public static class ScheduleAdvice { + @Nullable @Advice.OnMethodEnter(suppress = Throwable.class) - public static void onSchedule( + public static XxlJobHelper.XxlJobScope onSchedule( @Advice.FieldValue("glueType") GlueTypeEnum glueType, - @Advice.FieldValue("jobId") int jobId, - @Advice.Local("otelRequest") XxlJobProcessRequest request, - @Advice.Local("otelContext") Context context, - @Advice.Local("otelScope") Scope scope) { - Context parentContext = currentContext(); - request = XxlJobProcessRequest.createScriptJobRequest(glueType, jobId); - context = helper().startSpan(parentContext, request); - if (context == null) { - return; - } - scope = context.makeCurrent(); + @Advice.FieldValue("jobId") int jobId) { + return helper().startSpan(XxlJobProcessRequest.createScriptJobRequest(glueType, jobId)); } @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) public static void stopSpan( - @Advice.Return(typing = Assigner.Typing.DYNAMIC) Object result, - @Advice.Thrown Throwable throwable, - @Advice.Local("otelRequest") XxlJobProcessRequest request, - @Advice.Local("otelContext") Context context, - @Advice.Local("otelScope") Scope scope) { - helper().stopSpan(result, request, throwable, scope, context); + @Advice.Return(typing = Assigner.Typing.DYNAMIC) @Nullable Object result, + @Advice.Thrown @Nullable Throwable throwable, + @Advice.Enter @Nullable XxlJobHelper.XxlJobScope scope) { + helper().endSpan(scope, result, throwable); } } } diff --git a/instrumentation/xxl-job/xxl-job-1.9.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/xxljob/v1_9_2/SimpleJobHandlerInstrumentation.java b/instrumentation/xxl-job/xxl-job-1.9.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/xxljob/v1_9_2/SimpleJobHandlerInstrumentation.java index 076c90d7c6b5..16f0879bdba6 100644 --- a/instrumentation/xxl-job/xxl-job-1.9.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/xxljob/v1_9_2/SimpleJobHandlerInstrumentation.java +++ b/instrumentation/xxl-job/xxl-job-1.9.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/xxljob/v1_9_2/SimpleJobHandlerInstrumentation.java @@ -5,7 +5,6 @@ package io.opentelemetry.javaagent.instrumentation.xxljob.v1_9_2; -import static io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge.currentContext; import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasSuperType; import static io.opentelemetry.javaagent.instrumentation.xxljob.common.XxlJobConstants.XXL_GLUE_JOB_HANDLER; import static io.opentelemetry.javaagent.instrumentation.xxljob.common.XxlJobConstants.XXL_METHOD_JOB_HANDLER; @@ -19,11 +18,11 @@ import static net.bytebuddy.matcher.ElementMatchers.takesArguments; import com.xxl.job.core.handler.IJobHandler; -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.xxljob.common.XxlJobHelper; import io.opentelemetry.javaagent.instrumentation.xxljob.common.XxlJobProcessRequest; +import javax.annotation.Nullable; import net.bytebuddy.asm.Advice; import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.implementation.bytecode.assign.Assigner; @@ -44,33 +43,21 @@ public void transform(TypeTransformer transformer) { SimpleJobHandlerInstrumentation.class.getName() + "$ScheduleAdvice"); } + @SuppressWarnings("unused") public static class ScheduleAdvice { - @SuppressWarnings("unused") + @Nullable @Advice.OnMethodEnter(suppress = Throwable.class) - public static void onSchedule( - @Advice.This IJobHandler handler, - @Advice.Local("otelRequest") XxlJobProcessRequest request, - @Advice.Local("otelContext") Context context, - @Advice.Local("otelScope") Scope scope) { - Context parentContext = currentContext(); - request = XxlJobProcessRequest.createSimpleJobRequest(handler); - context = helper().startSpan(parentContext, request); - if (context == null) { - return; - } - scope = context.makeCurrent(); + public static XxlJobHelper.XxlJobScope onSchedule(@Advice.This IJobHandler handler) { + return helper().startSpan(XxlJobProcessRequest.createSimpleJobRequest(handler)); } - @SuppressWarnings("unused") @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) public static void stopSpan( - @Advice.Return(typing = Assigner.Typing.DYNAMIC) Object result, - @Advice.Thrown Throwable throwable, - @Advice.Local("otelRequest") XxlJobProcessRequest request, - @Advice.Local("otelContext") Context context, - @Advice.Local("otelScope") Scope scope) { - helper().stopSpan(result, request, throwable, scope, context); + @Advice.Return(typing = Assigner.Typing.DYNAMIC) @Nullable Object result, + @Advice.Thrown @Nullable Throwable throwable, + @Advice.Enter @Nullable XxlJobHelper.XxlJobScope scope) { + helper().endSpan(scope, result, throwable); } } } diff --git a/instrumentation/xxl-job/xxl-job-1.9.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/xxljob/v1_9_2/XxlJobInstrumentationModule.java b/instrumentation/xxl-job/xxl-job-1.9.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/xxljob/v1_9_2/XxlJobInstrumentationModule.java index fd72669efb73..5a8262cc9710 100644 --- a/instrumentation/xxl-job/xxl-job-1.9.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/xxljob/v1_9_2/XxlJobInstrumentationModule.java +++ b/instrumentation/xxl-job/xxl-job-1.9.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/xxljob/v1_9_2/XxlJobInstrumentationModule.java @@ -12,11 +12,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 XxlJobInstrumentationModule extends InstrumentationModule { +public class XxlJobInstrumentationModule extends InstrumentationModule + implements ExperimentalInstrumentationModule { public XxlJobInstrumentationModule() { super("xxl-job", "xxl-job-1.9.2"); @@ -35,4 +37,9 @@ public List typeInstrumentations() { new SimpleJobHandlerInstrumentation(), new GlueJobHandlerInstrumentation()); } + + @Override + public boolean isIndyReady() { + return true; + } } diff --git a/instrumentation/xxl-job/xxl-job-2.1.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/xxljob/v2_1_2/GlueJobHandlerInstrumentation.java b/instrumentation/xxl-job/xxl-job-2.1.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/xxljob/v2_1_2/GlueJobHandlerInstrumentation.java index 2e3ddaca3ce8..8582fd975cb7 100644 --- a/instrumentation/xxl-job/xxl-job-2.1.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/xxljob/v2_1_2/GlueJobHandlerInstrumentation.java +++ b/instrumentation/xxl-job/xxl-job-2.1.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/xxljob/v2_1_2/GlueJobHandlerInstrumentation.java @@ -5,17 +5,16 @@ package io.opentelemetry.javaagent.instrumentation.xxljob.v2_1_2; -import static io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge.currentContext; import static io.opentelemetry.javaagent.instrumentation.xxljob.v2_1_2.XxlJobSingletons.helper; import static net.bytebuddy.matcher.ElementMatchers.isPublic; import static net.bytebuddy.matcher.ElementMatchers.named; import com.xxl.job.core.handler.IJobHandler; -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.xxljob.common.XxlJobHelper; import io.opentelemetry.javaagent.instrumentation.xxljob.common.XxlJobProcessRequest; +import javax.annotation.Nullable; import net.bytebuddy.asm.Advice; import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.implementation.bytecode.assign.Assigner; @@ -37,29 +36,19 @@ public void transform(TypeTransformer transformer) { @SuppressWarnings("unused") public static class ScheduleAdvice { + @Nullable @Advice.OnMethodEnter(suppress = Throwable.class) - public static void onSchedule( - @Advice.FieldValue("jobHandler") IJobHandler handler, - @Advice.Local("otelRequest") XxlJobProcessRequest request, - @Advice.Local("otelContext") Context context, - @Advice.Local("otelScope") Scope scope) { - Context parentContext = currentContext(); - request = XxlJobProcessRequest.createGlueJobRequest(handler); - context = helper().startSpan(parentContext, request); - if (context == null) { - return; - } - scope = context.makeCurrent(); + public static XxlJobHelper.XxlJobScope onSchedule( + @Advice.FieldValue("jobHandler") IJobHandler handler) { + return helper().startSpan(XxlJobProcessRequest.createGlueJobRequest(handler)); } @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) public static void stopSpan( - @Advice.Return(typing = Assigner.Typing.DYNAMIC) Object result, - @Advice.Thrown Throwable throwable, - @Advice.Local("otelRequest") XxlJobProcessRequest request, - @Advice.Local("otelContext") Context context, - @Advice.Local("otelScope") Scope scope) { - helper().stopSpan(result, request, throwable, scope, context); + @Advice.Return(typing = Assigner.Typing.DYNAMIC) @Nullable Object result, + @Advice.Thrown @Nullable Throwable throwable, + @Advice.Enter @Nullable XxlJobHelper.XxlJobScope scope) { + helper().endSpan(scope, result, throwable); } } } diff --git a/instrumentation/xxl-job/xxl-job-2.1.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/xxljob/v2_1_2/MethodJobHandlerInstrumentation.java b/instrumentation/xxl-job/xxl-job-2.1.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/xxljob/v2_1_2/MethodJobHandlerInstrumentation.java index 021c3092682e..71165ec6e8c8 100644 --- a/instrumentation/xxl-job/xxl-job-2.1.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/xxljob/v2_1_2/MethodJobHandlerInstrumentation.java +++ b/instrumentation/xxl-job/xxl-job-2.1.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/xxljob/v2_1_2/MethodJobHandlerInstrumentation.java @@ -5,17 +5,16 @@ package io.opentelemetry.javaagent.instrumentation.xxljob.v2_1_2; -import static io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge.currentContext; import static io.opentelemetry.javaagent.instrumentation.xxljob.v2_1_2.XxlJobSingletons.helper; import static net.bytebuddy.matcher.ElementMatchers.isPublic; 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.xxljob.common.XxlJobHelper; import io.opentelemetry.javaagent.instrumentation.xxljob.common.XxlJobProcessRequest; import java.lang.reflect.Method; +import javax.annotation.Nullable; import net.bytebuddy.asm.Advice; import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.implementation.bytecode.assign.Assigner; @@ -38,29 +37,17 @@ public void transform(TypeTransformer transformer) { @SuppressWarnings("unused") public static class ScheduleAdvice { @Advice.OnMethodEnter(suppress = Throwable.class) - public static void onSchedule( - @Advice.FieldValue("target") Object target, - @Advice.FieldValue("method") Method method, - @Advice.Local("otelRequest") XxlJobProcessRequest request, - @Advice.Local("otelContext") Context context, - @Advice.Local("otelScope") Scope scope) { - Context parentContext = currentContext(); - request = XxlJobProcessRequest.createMethodJobRequest(target, method); - context = helper().startSpan(parentContext, request); - if (context == null) { - return; - } - scope = context.makeCurrent(); + public static XxlJobHelper.XxlJobScope onSchedule( + @Advice.FieldValue("target") Object target, @Advice.FieldValue("method") Method method) { + return helper().startSpan(XxlJobProcessRequest.createMethodJobRequest(target, method)); } @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) public static void stopSpan( - @Advice.Return(typing = Assigner.Typing.DYNAMIC) Object result, - @Advice.Thrown Throwable throwable, - @Advice.Local("otelRequest") XxlJobProcessRequest request, - @Advice.Local("otelContext") Context context, - @Advice.Local("otelScope") Scope scope) { - helper().stopSpan(result, request, throwable, scope, context); + @Advice.Return(typing = Assigner.Typing.DYNAMIC) @Nullable Object result, + @Advice.Thrown @Nullable Throwable throwable, + @Advice.Enter @Nullable XxlJobHelper.XxlJobScope scope) { + helper().endSpan(scope, result, throwable); } } } diff --git a/instrumentation/xxl-job/xxl-job-2.1.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/xxljob/v2_1_2/ScriptJobHandlerInstrumentation.java b/instrumentation/xxl-job/xxl-job-2.1.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/xxljob/v2_1_2/ScriptJobHandlerInstrumentation.java index 8ab021d4e2df..584a951df141 100644 --- a/instrumentation/xxl-job/xxl-job-2.1.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/xxljob/v2_1_2/ScriptJobHandlerInstrumentation.java +++ b/instrumentation/xxl-job/xxl-job-2.1.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/xxljob/v2_1_2/ScriptJobHandlerInstrumentation.java @@ -5,17 +5,16 @@ package io.opentelemetry.javaagent.instrumentation.xxljob.v2_1_2; -import static io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge.currentContext; import static io.opentelemetry.javaagent.instrumentation.xxljob.v2_1_2.XxlJobSingletons.helper; import static net.bytebuddy.matcher.ElementMatchers.isPublic; import static net.bytebuddy.matcher.ElementMatchers.named; import com.xxl.job.core.glue.GlueTypeEnum; -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.xxljob.common.XxlJobHelper; import io.opentelemetry.javaagent.instrumentation.xxljob.common.XxlJobProcessRequest; +import javax.annotation.Nullable; import net.bytebuddy.asm.Advice; import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.implementation.bytecode.assign.Assigner; @@ -37,30 +36,21 @@ public void transform(TypeTransformer transformer) { @SuppressWarnings("unused") public static class ScheduleAdvice { + + @Nullable @Advice.OnMethodEnter(suppress = Throwable.class) - public static void onSchedule( + public static XxlJobHelper.XxlJobScope onSchedule( @Advice.FieldValue("glueType") GlueTypeEnum glueType, - @Advice.FieldValue("jobId") int jobId, - @Advice.Local("otelRequest") XxlJobProcessRequest request, - @Advice.Local("otelContext") Context context, - @Advice.Local("otelScope") Scope scope) { - Context parentContext = currentContext(); - request = XxlJobProcessRequest.createScriptJobRequest(glueType, jobId); - context = helper().startSpan(parentContext, request); - if (context == null) { - return; - } - scope = context.makeCurrent(); + @Advice.FieldValue("jobId") int jobId) { + return helper().startSpan(XxlJobProcessRequest.createScriptJobRequest(glueType, jobId)); } @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) public static void stopSpan( - @Advice.Return(typing = Assigner.Typing.DYNAMIC) Object result, - @Advice.Thrown Throwable throwable, - @Advice.Local("otelRequest") XxlJobProcessRequest request, - @Advice.Local("otelContext") Context context, - @Advice.Local("otelScope") Scope scope) { - helper().stopSpan(result, request, throwable, scope, context); + @Advice.Return(typing = Assigner.Typing.DYNAMIC) @Nullable Object result, + @Advice.Thrown @Nullable Throwable throwable, + @Advice.Enter @Nullable XxlJobHelper.XxlJobScope scope) { + helper().endSpan(scope, result, throwable); } } } diff --git a/instrumentation/xxl-job/xxl-job-2.1.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/xxljob/v2_1_2/SimpleJobHandlerInstrumentation.java b/instrumentation/xxl-job/xxl-job-2.1.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/xxljob/v2_1_2/SimpleJobHandlerInstrumentation.java index 132f732eb150..8329d781dcd3 100644 --- a/instrumentation/xxl-job/xxl-job-2.1.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/xxljob/v2_1_2/SimpleJobHandlerInstrumentation.java +++ b/instrumentation/xxl-job/xxl-job-2.1.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/xxljob/v2_1_2/SimpleJobHandlerInstrumentation.java @@ -5,7 +5,6 @@ package io.opentelemetry.javaagent.instrumentation.xxljob.v2_1_2; -import static io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge.currentContext; import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasSuperType; import static io.opentelemetry.javaagent.instrumentation.xxljob.common.XxlJobConstants.XXL_GLUE_JOB_HANDLER; import static io.opentelemetry.javaagent.instrumentation.xxljob.common.XxlJobConstants.XXL_METHOD_JOB_HANDLER; @@ -17,11 +16,11 @@ import static net.bytebuddy.matcher.ElementMatchers.not; import com.xxl.job.core.handler.IJobHandler; -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.xxljob.common.XxlJobHelper; import io.opentelemetry.javaagent.instrumentation.xxljob.common.XxlJobProcessRequest; +import javax.annotation.Nullable; import net.bytebuddy.asm.Advice; import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.implementation.bytecode.assign.Assigner; @@ -42,33 +41,20 @@ public void transform(TypeTransformer transformer) { SimpleJobHandlerInstrumentation.class.getName() + "$ScheduleAdvice"); } + @SuppressWarnings("unused") public static class ScheduleAdvice { - @SuppressWarnings("unused") @Advice.OnMethodEnter(suppress = Throwable.class) - public static void onSchedule( - @Advice.This IJobHandler handler, - @Advice.Local("otelRequest") XxlJobProcessRequest request, - @Advice.Local("otelContext") Context context, - @Advice.Local("otelScope") Scope scope) { - Context parentContext = currentContext(); - request = XxlJobProcessRequest.createSimpleJobRequest(handler); - context = helper().startSpan(parentContext, request); - if (context == null) { - return; - } - scope = context.makeCurrent(); + public static XxlJobHelper.XxlJobScope onSchedule(@Advice.This IJobHandler handler) { + return helper().startSpan(XxlJobProcessRequest.createSimpleJobRequest(handler)); } - @SuppressWarnings("unused") @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) public static void stopSpan( - @Advice.Return(typing = Assigner.Typing.DYNAMIC) Object result, - @Advice.Thrown Throwable throwable, - @Advice.Local("otelRequest") XxlJobProcessRequest request, - @Advice.Local("otelContext") Context context, - @Advice.Local("otelScope") Scope scope) { - helper().stopSpan(result, request, throwable, scope, context); + @Advice.Return(typing = Assigner.Typing.DYNAMIC) @Nullable Object result, + @Advice.Thrown @Nullable Throwable throwable, + @Advice.Enter @Nullable XxlJobHelper.XxlJobScope scope) { + helper().endSpan(scope, result, throwable); } } } diff --git a/instrumentation/xxl-job/xxl-job-2.1.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/xxljob/v2_1_2/XxlJobInstrumentationModule.java b/instrumentation/xxl-job/xxl-job-2.1.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/xxljob/v2_1_2/XxlJobInstrumentationModule.java index f32251f71f3f..b0f97f29ca41 100644 --- a/instrumentation/xxl-job/xxl-job-2.1.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/xxljob/v2_1_2/XxlJobInstrumentationModule.java +++ b/instrumentation/xxl-job/xxl-job-2.1.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/xxljob/v2_1_2/XxlJobInstrumentationModule.java @@ -12,11 +12,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 XxlJobInstrumentationModule extends InstrumentationModule { +public class XxlJobInstrumentationModule extends InstrumentationModule + implements ExperimentalInstrumentationModule { public XxlJobInstrumentationModule() { super("xxl-job", "xxl-job-2.1.2"); @@ -37,4 +39,9 @@ public List typeInstrumentations() { new SimpleJobHandlerInstrumentation(), new GlueJobHandlerInstrumentation()); } + + @Override + public boolean isIndyReady() { + return true; + } } diff --git a/instrumentation/xxl-job/xxl-job-2.3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/xxljob/v2_3_0/GlueJobHandlerInstrumentation.java b/instrumentation/xxl-job/xxl-job-2.3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/xxljob/v2_3_0/GlueJobHandlerInstrumentation.java index 7e67cd14e066..c5ad141519e7 100644 --- a/instrumentation/xxl-job/xxl-job-2.3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/xxljob/v2_3_0/GlueJobHandlerInstrumentation.java +++ b/instrumentation/xxl-job/xxl-job-2.3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/xxljob/v2_3_0/GlueJobHandlerInstrumentation.java @@ -5,20 +5,20 @@ package io.opentelemetry.javaagent.instrumentation.xxljob.v2_3_0; -import static io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge.currentContext; import static io.opentelemetry.javaagent.instrumentation.xxljob.v2_3_0.XxlJobSingletons.helper; import static net.bytebuddy.matcher.ElementMatchers.isPublic; import static net.bytebuddy.matcher.ElementMatchers.named; import static net.bytebuddy.matcher.ElementMatchers.takesNoArguments; import com.xxl.job.core.handler.IJobHandler; -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.xxljob.common.XxlJobHelper; import io.opentelemetry.javaagent.instrumentation.xxljob.common.XxlJobProcessRequest; +import javax.annotation.Nullable; import net.bytebuddy.asm.Advice; import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.implementation.bytecode.assign.Assigner; import net.bytebuddy.matcher.ElementMatcher; public class GlueJobHandlerInstrumentation implements TypeInstrumentation { @@ -38,28 +38,19 @@ public void transform(TypeTransformer transformer) { @SuppressWarnings("unused") public static class ScheduleAdvice { + @Nullable @Advice.OnMethodEnter(suppress = Throwable.class) - public static void onSchedule( - @Advice.FieldValue("jobHandler") IJobHandler handler, - @Advice.Local("otelRequest") XxlJobProcessRequest request, - @Advice.Local("otelContext") Context context, - @Advice.Local("otelScope") Scope scope) { - Context parentContext = currentContext(); - request = XxlJobProcessRequest.createGlueJobRequest(handler); - context = helper().startSpan(parentContext, request); - if (context == null) { - return; - } - scope = context.makeCurrent(); + public static XxlJobHelper.XxlJobScope onSchedule( + @Advice.FieldValue("jobHandler") IJobHandler handler) { + return helper().startSpan(XxlJobProcessRequest.createGlueJobRequest(handler)); } @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) public static void stopSpan( - @Advice.Thrown Throwable throwable, - @Advice.Local("otelRequest") XxlJobProcessRequest request, - @Advice.Local("otelContext") Context context, - @Advice.Local("otelScope") Scope scope) { - helper().stopSpan(request, throwable, scope, context); + @Advice.Return(typing = Assigner.Typing.DYNAMIC) @Nullable Object result, + @Advice.Thrown @Nullable Throwable throwable, + @Advice.Enter @Nullable XxlJobHelper.XxlJobScope scope) { + helper().endSpan(scope, result, throwable); } } } diff --git a/instrumentation/xxl-job/xxl-job-2.3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/xxljob/v2_3_0/MethodJobHandlerInstrumentation.java b/instrumentation/xxl-job/xxl-job-2.3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/xxljob/v2_3_0/MethodJobHandlerInstrumentation.java index 04d39252acaa..117636c89639 100644 --- a/instrumentation/xxl-job/xxl-job-2.3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/xxljob/v2_3_0/MethodJobHandlerInstrumentation.java +++ b/instrumentation/xxl-job/xxl-job-2.3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/xxljob/v2_3_0/MethodJobHandlerInstrumentation.java @@ -5,20 +5,20 @@ package io.opentelemetry.javaagent.instrumentation.xxljob.v2_3_0; -import static io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge.currentContext; import static io.opentelemetry.javaagent.instrumentation.xxljob.v2_3_0.XxlJobSingletons.helper; import static net.bytebuddy.matcher.ElementMatchers.isPublic; import static net.bytebuddy.matcher.ElementMatchers.named; import static net.bytebuddy.matcher.ElementMatchers.takesNoArguments; -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.xxljob.common.XxlJobHelper; import io.opentelemetry.javaagent.instrumentation.xxljob.common.XxlJobProcessRequest; import java.lang.reflect.Method; +import javax.annotation.Nullable; import net.bytebuddy.asm.Advice; import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.implementation.bytecode.assign.Assigner; import net.bytebuddy.matcher.ElementMatcher; public class MethodJobHandlerInstrumentation implements TypeInstrumentation { @@ -38,28 +38,17 @@ public void transform(TypeTransformer transformer) { @SuppressWarnings("unused") public static class ScheduleAdvice { @Advice.OnMethodEnter(suppress = Throwable.class) - public static void onSchedule( - @Advice.FieldValue("target") Object target, - @Advice.FieldValue("method") Method method, - @Advice.Local("otelRequest") XxlJobProcessRequest request, - @Advice.Local("otelContext") Context context, - @Advice.Local("otelScope") Scope scope) { - Context parentContext = currentContext(); - request = XxlJobProcessRequest.createMethodJobRequest(target, method); - context = helper().startSpan(parentContext, request); - if (context == null) { - return; - } - scope = context.makeCurrent(); + public static XxlJobHelper.XxlJobScope onSchedule( + @Advice.FieldValue("target") Object target, @Advice.FieldValue("method") Method method) { + return helper().startSpan(XxlJobProcessRequest.createMethodJobRequest(target, method)); } @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) public static void stopSpan( - @Advice.Thrown Throwable throwable, - @Advice.Local("otelRequest") XxlJobProcessRequest request, - @Advice.Local("otelContext") Context context, - @Advice.Local("otelScope") Scope scope) { - helper().stopSpan(request, throwable, scope, context); + @Advice.Return(typing = Assigner.Typing.DYNAMIC) @Nullable Object result, + @Advice.Thrown @Nullable Throwable throwable, + @Advice.Enter @Nullable XxlJobHelper.XxlJobScope scope) { + helper().endSpan(scope, result, throwable); } } } diff --git a/instrumentation/xxl-job/xxl-job-2.3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/xxljob/v2_3_0/ScriptJobHandlerInstrumentation.java b/instrumentation/xxl-job/xxl-job-2.3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/xxljob/v2_3_0/ScriptJobHandlerInstrumentation.java index bf5fa3fa793a..5113c312ed6c 100644 --- a/instrumentation/xxl-job/xxl-job-2.3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/xxljob/v2_3_0/ScriptJobHandlerInstrumentation.java +++ b/instrumentation/xxl-job/xxl-job-2.3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/xxljob/v2_3_0/ScriptJobHandlerInstrumentation.java @@ -5,20 +5,20 @@ package io.opentelemetry.javaagent.instrumentation.xxljob.v2_3_0; -import static io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge.currentContext; import static io.opentelemetry.javaagent.instrumentation.xxljob.v2_3_0.XxlJobSingletons.helper; import static net.bytebuddy.matcher.ElementMatchers.isPublic; import static net.bytebuddy.matcher.ElementMatchers.named; import static net.bytebuddy.matcher.ElementMatchers.takesNoArguments; import com.xxl.job.core.glue.GlueTypeEnum; -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.xxljob.common.XxlJobHelper; import io.opentelemetry.javaagent.instrumentation.xxljob.common.XxlJobProcessRequest; +import javax.annotation.Nullable; import net.bytebuddy.asm.Advice; import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.implementation.bytecode.assign.Assigner; import net.bytebuddy.matcher.ElementMatcher; public class ScriptJobHandlerInstrumentation implements TypeInstrumentation { @@ -39,28 +39,18 @@ public void transform(TypeTransformer transformer) { public static class ScheduleAdvice { @Advice.OnMethodEnter(suppress = Throwable.class) - public static void onSchedule( + public static XxlJobHelper.XxlJobScope onSchedule( @Advice.FieldValue("glueType") GlueTypeEnum glueType, - @Advice.FieldValue("jobId") int jobId, - @Advice.Local("otelRequest") XxlJobProcessRequest request, - @Advice.Local("otelContext") Context context, - @Advice.Local("otelScope") Scope scope) { - Context parentContext = currentContext(); - request = XxlJobProcessRequest.createScriptJobRequest(glueType, jobId); - context = helper().startSpan(parentContext, request); - if (context == null) { - return; - } - scope = context.makeCurrent(); + @Advice.FieldValue("jobId") int jobId) { + return helper().startSpan(XxlJobProcessRequest.createScriptJobRequest(glueType, jobId)); } @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) public static void stopSpan( - @Advice.Thrown Throwable throwable, - @Advice.Local("otelRequest") XxlJobProcessRequest request, - @Advice.Local("otelContext") Context context, - @Advice.Local("otelScope") Scope scope) { - helper().stopSpan(request, throwable, scope, context); + @Advice.Return(typing = Assigner.Typing.DYNAMIC) Object result, + @Advice.Thrown @Nullable Throwable throwable, + @Advice.Enter @Nullable XxlJobHelper.XxlJobScope scope) { + helper().endSpan(scope, result, throwable); } } } diff --git a/instrumentation/xxl-job/xxl-job-2.3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/xxljob/v2_3_0/SimpleJobHandlerInstrumentation.java b/instrumentation/xxl-job/xxl-job-2.3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/xxljob/v2_3_0/SimpleJobHandlerInstrumentation.java index 896da6b7136e..507392a15b94 100644 --- a/instrumentation/xxl-job/xxl-job-2.3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/xxljob/v2_3_0/SimpleJobHandlerInstrumentation.java +++ b/instrumentation/xxl-job/xxl-job-2.3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/xxljob/v2_3_0/SimpleJobHandlerInstrumentation.java @@ -5,7 +5,6 @@ package io.opentelemetry.javaagent.instrumentation.xxljob.v2_3_0; -import static io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge.currentContext; import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasSuperType; import static io.opentelemetry.javaagent.instrumentation.xxljob.common.XxlJobConstants.XXL_GLUE_JOB_HANDLER; import static io.opentelemetry.javaagent.instrumentation.xxljob.common.XxlJobConstants.XXL_METHOD_JOB_HANDLER; @@ -18,13 +17,14 @@ import static net.bytebuddy.matcher.ElementMatchers.takesNoArguments; import com.xxl.job.core.handler.IJobHandler; -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.xxljob.common.XxlJobHelper; import io.opentelemetry.javaagent.instrumentation.xxljob.common.XxlJobProcessRequest; +import javax.annotation.Nullable; import net.bytebuddy.asm.Advice; import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.implementation.bytecode.assign.Assigner; import net.bytebuddy.matcher.ElementMatcher; public class SimpleJobHandlerInstrumentation implements TypeInstrumentation { @@ -42,32 +42,20 @@ public void transform(TypeTransformer transformer) { SimpleJobHandlerInstrumentation.class.getName() + "$ScheduleAdvice"); } + @SuppressWarnings("unused") public static class ScheduleAdvice { - @SuppressWarnings("unused") @Advice.OnMethodEnter(suppress = Throwable.class) - public static void onSchedule( - @Advice.This IJobHandler handler, - @Advice.Local("otelRequest") XxlJobProcessRequest request, - @Advice.Local("otelContext") Context context, - @Advice.Local("otelScope") Scope scope) { - Context parentContext = currentContext(); - request = XxlJobProcessRequest.createSimpleJobRequest(handler); - context = helper().startSpan(parentContext, request); - if (context == null) { - return; - } - scope = context.makeCurrent(); + public static XxlJobHelper.XxlJobScope onSchedule(@Advice.This IJobHandler handler) { + return helper().startSpan(XxlJobProcessRequest.createSimpleJobRequest(handler)); } - @SuppressWarnings("unused") @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) public static void stopSpan( - @Advice.Thrown Throwable throwable, - @Advice.Local("otelRequest") XxlJobProcessRequest request, - @Advice.Local("otelContext") Context context, - @Advice.Local("otelScope") Scope scope) { - helper().stopSpan(request, throwable, scope, context); + @Advice.Return(typing = Assigner.Typing.DYNAMIC) @Nullable Object result, + @Advice.Thrown @Nullable Throwable throwable, + @Advice.Enter @Nullable XxlJobHelper.XxlJobScope scope) { + helper().endSpan(scope, result, throwable); } } } diff --git a/instrumentation/xxl-job/xxl-job-2.3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/xxljob/v2_3_0/XxlJobInstrumentationModule.java b/instrumentation/xxl-job/xxl-job-2.3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/xxljob/v2_3_0/XxlJobInstrumentationModule.java index 3b17798a47ea..066f5e5df8f5 100644 --- a/instrumentation/xxl-job/xxl-job-2.3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/xxljob/v2_3_0/XxlJobInstrumentationModule.java +++ b/instrumentation/xxl-job/xxl-job-2.3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/xxljob/v2_3_0/XxlJobInstrumentationModule.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 XxlJobInstrumentationModule extends InstrumentationModule { +public class XxlJobInstrumentationModule extends InstrumentationModule + implements ExperimentalInstrumentationModule { public XxlJobInstrumentationModule() { super("xxl-job", "xxl-job-2.3.0"); @@ -35,4 +37,9 @@ public List typeInstrumentations() { new SimpleJobHandlerInstrumentation(), new GlueJobHandlerInstrumentation()); } + + @Override + public boolean isIndyReady() { + return true; + } } diff --git a/instrumentation/xxl-job/xxl-job-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/xxljob/common/XxlJobHelper.java b/instrumentation/xxl-job/xxl-job-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/xxljob/common/XxlJobHelper.java index 3b90e1d3ad44..92165fdc99f7 100644 --- a/instrumentation/xxl-job/xxl-job-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/xxljob/common/XxlJobHelper.java +++ b/instrumentation/xxl-job/xxl-job-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/xxljob/common/XxlJobHelper.java @@ -9,11 +9,24 @@ import io.opentelemetry.context.Scope; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; import java.util.function.Predicate; +import javax.annotation.Nullable; public final class XxlJobHelper { private final Instrumenter instrumenter; private final Predicate failedStatusPredicate; + public static class XxlJobScope { + private final XxlJobProcessRequest request; + private final Context context; + private final Scope scope; + + private XxlJobScope(XxlJobProcessRequest request, Context context, Scope scope) { + this.request = request; + this.context = context; + this.scope = scope; + } + } + private XxlJobHelper( Instrumenter instrumenter, Predicate failedStatusPredicate) { @@ -27,31 +40,25 @@ public static XxlJobHelper create( return new XxlJobHelper(instrumenter, failedStatusPredicate); } - public Context startSpan(Context parentContext, XxlJobProcessRequest request) { + @Nullable + public XxlJobScope startSpan(XxlJobProcessRequest request) { + Context parentContext = Context.current(); if (!instrumenter.shouldStart(parentContext, request)) { return null; } - return instrumenter.start(parentContext, request); + Context context = instrumenter.start(parentContext, request); + return new XxlJobScope(request, context, context.makeCurrent()); } - public void stopSpan( - Object result, - XxlJobProcessRequest request, - Throwable throwable, - Scope scope, - Context context) { + public void endSpan( + @Nullable XxlJobScope scope, @Nullable Object result, @Nullable Throwable throwable) { if (scope == null) { return; } if (failedStatusPredicate.test(result)) { - request.setFailed(); + scope.request.setFailed(); } - scope.close(); - instrumenter.end(context, request, null, throwable); - } - - public void stopSpan( - XxlJobProcessRequest request, Throwable throwable, Scope scope, Context context) { - stopSpan(null, request, throwable, scope, context); + scope.scope.close(); + instrumenter.end(scope.context, scope.request, null, throwable); } } diff --git a/instrumentation/xxl-job/xxl-job-common/testing/src/main/java/io/opentelemetry/instrumentation/xxljob/ReflectiveMethodsFactory.java b/instrumentation/xxl-job/xxl-job-common/testing/src/main/java/io/opentelemetry/instrumentation/xxljob/ReflectiveMethodsFactory.java index fd772a4611d3..45613f82416e 100644 --- a/instrumentation/xxl-job/xxl-job-common/testing/src/main/java/io/opentelemetry/instrumentation/xxljob/ReflectiveMethodsFactory.java +++ b/instrumentation/xxl-job/xxl-job-common/testing/src/main/java/io/opentelemetry/instrumentation/xxljob/ReflectiveMethodsFactory.java @@ -21,7 +21,9 @@ public void initMethod() {} public void destroyMethod() {} public ReturnT echo(String param) { - return new ReturnT<>("echo: " + param); + ReturnT result = new ReturnT<>(); + result.setContent("echo: " + param); + return result; } } diff --git a/javaagent-bootstrap/src/main/java/io/opentelemetry/javaagent/bootstrap/AgentClassLoader.java b/javaagent-bootstrap/src/main/java/io/opentelemetry/javaagent/bootstrap/AgentClassLoader.java index 340809bc90ff..4baba3e19085 100644 --- a/javaagent-bootstrap/src/main/java/io/opentelemetry/javaagent/bootstrap/AgentClassLoader.java +++ b/javaagent-bootstrap/src/main/java/io/opentelemetry/javaagent/bootstrap/AgentClassLoader.java @@ -84,7 +84,27 @@ public AgentClassLoader(File javaagentFile) { */ public AgentClassLoader( File javaagentFile, String internalJarFileName, boolean isSecurityManagerSupportEnabled) { - super(new URL[] {}, getParentClassLoader()); + this(javaagentFile, internalJarFileName, isSecurityManagerSupportEnabled, null); + } + + /** + * Construct a new AgentClassLoader with a custom parent ClassLoader. This is used by some 3rd + * party command-line utilities in order to reuse classes that are bundled as classdata files + * under `inst/`. + * + * @param javaagentFile Used for resource lookups. + * @param internalJarFileName File name of the internal jar + * @param isSecurityManagerSupportEnabled Whether this class loader should define classes with all + * permissions + * @param parentClassLoader Custom parent ClassLoader to use. If null, the default parent will be + * used. + */ + public AgentClassLoader( + File javaagentFile, + String internalJarFileName, + boolean isSecurityManagerSupportEnabled, + @Nullable ClassLoader parentClassLoader) { + super(new URL[] {}, parentClassLoader != null ? parentClassLoader : getParentClassLoader()); if (javaagentFile == null) { throw new IllegalArgumentException("Agent jar location should be set"); } diff --git a/javaagent-bootstrap/src/main/java/io/opentelemetry/javaagent/bootstrap/PatchLogger.java b/javaagent-bootstrap/src/main/java/io/opentelemetry/javaagent/bootstrap/PatchLogger.java index 50cde71479c8..0d1d52e1d02f 100644 --- a/javaagent-bootstrap/src/main/java/io/opentelemetry/javaagent/bootstrap/PatchLogger.java +++ b/javaagent-bootstrap/src/main/java/io/opentelemetry/javaagent/bootstrap/PatchLogger.java @@ -344,15 +344,15 @@ public static PatchLogger getGlobal() { return global; } - public static void setFilter(Filter filter) {} + public void setFilter(Filter filter) {} - public static Filter getFilter() { + public Filter getFilter() { return null; } - public static void setUseParentHandlers(boolean useParentHandlers) {} + public void setUseParentHandlers(boolean useParentHandlers) {} - public static boolean getUseParentHandlers() { + public boolean getUseParentHandlers() { return true; } diff --git a/javaagent-bootstrap/src/test/groovy/io/opentelemetry/javaagent/bootstrap/AgentClassLoaderTest.groovy b/javaagent-bootstrap/src/test/groovy/io/opentelemetry/javaagent/bootstrap/AgentClassLoaderTest.groovy index a0a73390d036..27300475fd6e 100644 --- a/javaagent-bootstrap/src/test/groovy/io/opentelemetry/javaagent/bootstrap/AgentClassLoaderTest.groovy +++ b/javaagent-bootstrap/src/test/groovy/io/opentelemetry/javaagent/bootstrap/AgentClassLoaderTest.groovy @@ -56,7 +56,7 @@ class AgentClassLoaderTest extends Specification { def "multi release jar"() { setup: boolean jdk8 = "1.8" == System.getProperty("java.specification.version") - def mrJarClass = Class.forName("io.opentelemetry.instrumentation.resources.ProcessArguments") + def mrJarClass = Class.forName("io.opentelemetry.instrumentation.resources.internal.ProcessArguments") // sdk is a multi release jar URL multiReleaseJar = mrJarClass.getProtectionDomain().getCodeSource().getLocation() AgentClassLoader loader = new AgentClassLoader(new File(multiReleaseJar.toURI())) { @@ -67,7 +67,7 @@ class AgentClassLoaderTest extends Specification { } when: - URL url = loader.findResource("io/opentelemetry/instrumentation/resources/ProcessArguments.class") + URL url = loader.findResource("io/opentelemetry/instrumentation/resources/internal/ProcessArguments.class") then: url != null @@ -75,7 +75,7 @@ class AgentClassLoaderTest extends Specification { jdk8 != url.toString().contains("META-INF/versions/9/") and: - Class clazz = loader.loadClass("io.opentelemetry.instrumentation.resources.ProcessArguments") + Class clazz = loader.loadClass("io.opentelemetry.instrumentation.resources.internal.ProcessArguments") // class was loaded by agent loader used in this test clazz.getClassLoader() == loader Method method = clazz.getDeclaredMethod("getProcessArguments") diff --git a/javaagent-bootstrap/src/test/java/io/opentelemetry/javaagent/bootstrap/PatchLoggerTest.java b/javaagent-bootstrap/src/test/java/io/opentelemetry/javaagent/bootstrap/PatchLoggerTest.java index a14f4adcd6fb..99357056d8d0 100644 --- a/javaagent-bootstrap/src/test/java/io/opentelemetry/javaagent/bootstrap/PatchLoggerTest.java +++ b/javaagent-bootstrap/src/test/java/io/opentelemetry/javaagent/bootstrap/PatchLoggerTest.java @@ -12,6 +12,7 @@ import static org.mockito.Mockito.when; import java.lang.reflect.Method; +import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.HashSet; import java.util.List; @@ -31,6 +32,7 @@ void testImplementsAllMethods() { for (Method method : PatchLogger.class.getMethods()) { MethodSignature methodSignature = new MethodSignature(); methodSignature.name = method.getName(); + methodSignature.modifiers = method.getModifiers(); for (Class clazz : method.getParameterTypes()) { String parameterType = clazz.getName(); methodSignature.parameterTypes.add( @@ -50,6 +52,7 @@ void testImplementsAllMethods() { String methodName = method.getName(); MethodSignature builder = new MethodSignature(); builder.name = methodName; + builder.modifiers = method.getModifiers(); List parameterTypes = new ArrayList<>(); for (Class clazz : method.getParameterTypes()) { parameterTypes.add(clazz.getName()); @@ -792,6 +795,7 @@ static class MethodSignature { String name; List parameterTypes = new ArrayList<>(); String returnType; + int modifiers; @Override public boolean equals(@Nullable Object obj) { @@ -804,18 +808,19 @@ public boolean equals(@Nullable Object obj) { MethodSignature other = (MethodSignature) obj; return Objects.equals(name, other.name) && Objects.equals(parameterTypes, other.parameterTypes) - && Objects.equals(returnType, other.returnType); + && Objects.equals(returnType, other.returnType) + && Modifier.isStatic(modifiers) == Modifier.isStatic(other.modifiers); } @Override public int hashCode() { - return Objects.hash(name, parameterTypes, returnType); + return Objects.hash(name, parameterTypes, returnType, Modifier.isStatic(modifiers)); } @Override public String toString() { String params = parameterTypes.stream().reduce((a, b) -> a + ", " + b).orElse(""); - return name + "(" + params + ")" + returnType; + return Modifier.toString(modifiers) + " " + name + "(" + params + ")" + returnType; } } } diff --git a/javaagent-internal-logging-simple/build.gradle.kts b/javaagent-internal-logging-simple/build.gradle.kts index b87f59ec43cf..55d10faa87b3 100644 --- a/javaagent-internal-logging-simple/build.gradle.kts +++ b/javaagent-internal-logging-simple/build.gradle.kts @@ -24,6 +24,10 @@ tasks { val shadowJar by existing(ShadowJar::class) { // required for META-INF/services files relocation mergeServiceFiles() + // mergeServiceFiles requires that duplicate strategy is set to include + filesMatching("META-INF/services/**") { + duplicatesStrategy = DuplicatesStrategy.INCLUDE + } // Prevents configuration naming conflict with other SLF4J instances relocate("org.slf4j", "io.opentelemetry.javaagent.slf4j") diff --git a/javaagent-tooling/build.gradle.kts b/javaagent-tooling/build.gradle.kts index c44a7c4c0a65..848c87e7da82 100644 --- a/javaagent-tooling/build.gradle.kts +++ b/javaagent-tooling/build.gradle.kts @@ -17,6 +17,7 @@ dependencies { implementation(project(":instrumentation-annotations-support")) implementation(project(":muzzle")) implementation(project(":sdk-autoconfigure-support")) + implementation(project(":declarative-config-bridge")) implementation("io.opentelemetry:opentelemetry-api") implementation("io.opentelemetry:opentelemetry-sdk") @@ -61,6 +62,7 @@ dependencies { testImplementation(project(":testing-common")) testImplementation("com.google.guava:guava") testImplementation("org.junit-pioneer:junit-pioneer") + testImplementation("com.fasterxml.jackson.core:jackson-databind") } testing { diff --git a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/AgentTracerProviderConfigurer.java b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/AgentTracerProviderConfigurer.java index 71223c79573c..4f4009038a6b 100644 --- a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/AgentTracerProviderConfigurer.java +++ b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/AgentTracerProviderConfigurer.java @@ -11,6 +11,7 @@ import com.google.auto.service.AutoService; import com.google.errorprone.annotations.CanIgnoreReturnValue; import io.opentelemetry.exporter.logging.LoggingSpanExporter; +import io.opentelemetry.instrumentation.thread.internal.AddThreadDetailsSpanProcessor; import io.opentelemetry.javaagent.tooling.config.AgentConfig; import io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizer; import io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizerProvider; diff --git a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/OpenTelemetryInstaller.java b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/OpenTelemetryInstaller.java index 29c08e7dbe75..ff7147ef44bb 100644 --- a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/OpenTelemetryInstaller.java +++ b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/OpenTelemetryInstaller.java @@ -5,14 +5,19 @@ package io.opentelemetry.javaagent.tooling; +import static io.opentelemetry.api.incubator.config.DeclarativeConfigProperties.empty; + import io.opentelemetry.api.incubator.config.ConfigProvider; +import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; +import io.opentelemetry.instrumentation.config.bridge.DeclarativeConfigPropertiesBridgeBuilder; import io.opentelemetry.javaagent.bootstrap.OpenTelemetrySdkAccess; -import io.opentelemetry.javaagent.extension.internal.DeclarativeConfigPropertiesBridgeBuilder; import io.opentelemetry.javaagent.tooling.config.EarlyInitAgentConfig; import io.opentelemetry.sdk.OpenTelemetrySdk; import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk; import io.opentelemetry.sdk.autoconfigure.SdkAutoconfigureAccess; import io.opentelemetry.sdk.autoconfigure.internal.AutoConfigureUtil; +import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; +import io.opentelemetry.sdk.autoconfigure.spi.ConfigurationException; import io.opentelemetry.sdk.common.CompletableResultCode; import java.util.Arrays; @@ -44,22 +49,49 @@ public static AutoConfiguredOpenTelemetrySdk installOpenTelemetrySdk( return SdkAutoconfigureAccess.create( sdk, SdkAutoconfigureAccess.getResource(autoConfiguredSdk), - new DeclarativeConfigPropertiesBridgeBuilder() - .addMapping("otel.javaagent", "agent") - // these properties are used to initialize the SDK before the configuration file - // is loaded for consistency, we pass them to the bridge, so that they can be read - // later with the same value from the {@link DeclarativeConfigPropertiesBridge} - .addOverride( - "otel.javaagent.debug", earlyConfig.getBoolean("otel.javaagent.debug", false)) - .addOverride( - "otel.javaagent.logging", earlyConfig.getString("otel.javaagent.logging")) - .buildFromInstrumentationConfig(configProvider.getInstrumentationConfig()), + getDeclarativeConfigBridgedProperties(earlyConfig, configProvider), configProvider); } return autoConfiguredSdk; } + // Visible for testing + static ConfigProperties getDeclarativeConfigBridgedProperties( + EarlyInitAgentConfig earlyConfig, ConfigProvider configProvider) { + return new DeclarativeConfigPropertiesBridgeBuilder() + .addMapping("otel.javaagent", "agent") + .addOverride("otel.instrumentation.common.default-enabled", defaultEnabled(configProvider)) + // these properties are used to initialize the SDK before the configuration file + // is loaded for consistency, we pass them to the bridge, so that they can be read + // later with the same value from the {@link DeclarativeConfigPropertiesBridge} + .addOverride("otel.javaagent.debug", earlyConfig.getBoolean("otel.javaagent.debug", false)) + .addOverride("otel.javaagent.logging", earlyConfig.getString("otel.javaagent.logging")) + .buildFromInstrumentationConfig(configProvider.getInstrumentationConfig()); + } + + private static boolean defaultEnabled(ConfigProvider configProvider) { + DeclarativeConfigProperties instrumentationConfig = configProvider.getInstrumentationConfig(); + if (instrumentationConfig == null) { + return true; + } + + String mode = + instrumentationConfig + .getStructured("java", empty()) + .getStructured("agent", empty()) + .getString("instrumentation_mode", "default"); + + switch (mode) { + case "none": + return false; + case "default": + return true; + default: + throw new ConfigurationException("Unknown instrumentation mode: " + mode); + } + } + private static void setForceFlush(OpenTelemetrySdk sdk) { OpenTelemetrySdkAccess.internalSetForceFlush( (timeout, unit) -> { diff --git a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/ignore/AdditionalLibraryIgnoredTypesConfigurer.java b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/ignore/AdditionalLibraryIgnoredTypesConfigurer.java index 54b613c64042..5bc4e05babd4 100644 --- a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/ignore/AdditionalLibraryIgnoredTypesConfigurer.java +++ b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/ignore/AdditionalLibraryIgnoredTypesConfigurer.java @@ -192,6 +192,7 @@ public void configure(IgnoredTypesBuilder builder) { builder .ignoreClass("org.springframework.web.") + .allowClass("org.springframework.web.client.RestTemplate") .allowClass("org.springframework.web.servlet.") .allowClass("org.springframework.web.filter.") .allowClass("org.springframework.web.multipart.") diff --git a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/resources/DistroComponentProvider.java b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/resources/DistroComponentProvider.java new file mode 100644 index 000000000000..498b0474863e --- /dev/null +++ b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/resources/DistroComponentProvider.java @@ -0,0 +1,31 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.tooling.resources; + +import com.google.auto.service.AutoService; +import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; +import io.opentelemetry.sdk.autoconfigure.spi.internal.ComponentProvider; +import io.opentelemetry.sdk.resources.Resource; + +@SuppressWarnings("rawtypes") +@AutoService(ComponentProvider.class) +public class DistroComponentProvider implements ComponentProvider { + + @Override + public Class getType() { + return Resource.class; + } + + @Override + public String getName() { + return "opentelemetry-javaagent-distribution"; + } + + @Override + public Resource create(DeclarativeConfigProperties config) { + return DistroResourceProvider.get("opentelemetry-javaagent"); + } +} diff --git a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/DistroVersionResourceProvider.java b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/resources/DistroResourceProvider.java similarity index 67% rename from javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/DistroVersionResourceProvider.java rename to javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/resources/DistroResourceProvider.java index 53fe1d65a5d1..782878082ea7 100644 --- a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/DistroVersionResourceProvider.java +++ b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/resources/DistroResourceProvider.java @@ -3,29 +3,31 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.javaagent.tooling; +package io.opentelemetry.javaagent.tooling.resources; import static io.opentelemetry.semconv.incubating.TelemetryIncubatingAttributes.TELEMETRY_DISTRO_NAME; import static io.opentelemetry.semconv.incubating.TelemetryIncubatingAttributes.TELEMETRY_DISTRO_VERSION; import com.google.auto.service.AutoService; import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.javaagent.tooling.AgentVersion; import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import io.opentelemetry.sdk.autoconfigure.spi.ResourceProvider; import io.opentelemetry.sdk.resources.Resource; @AutoService(ResourceProvider.class) -public class DistroVersionResourceProvider implements ResourceProvider { +public class DistroResourceProvider implements ResourceProvider { @Override public Resource createResource(ConfigProperties config) { + return get("opentelemetry-java-instrumentation"); + } + + static Resource get(String distroName) { return AgentVersion.VERSION == null ? Resource.empty() : Resource.create( Attributes.of( - TELEMETRY_DISTRO_NAME, - "opentelemetry-java-instrumentation", - TELEMETRY_DISTRO_VERSION, - AgentVersion.VERSION)); + TELEMETRY_DISTRO_NAME, distroName, TELEMETRY_DISTRO_VERSION, AgentVersion.VERSION)); } } diff --git a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/resources/ResourceCustomizerProvider.java b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/resources/ResourceCustomizerProvider.java new file mode 100644 index 000000000000..0beebe01d112 --- /dev/null +++ b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/resources/ResourceCustomizerProvider.java @@ -0,0 +1,65 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.tooling.resources; + +import com.google.auto.service.AutoService; +import io.opentelemetry.sdk.extension.incubator.fileconfig.DeclarativeConfigurationCustomizer; +import io.opentelemetry.sdk.extension.incubator.fileconfig.DeclarativeConfigurationCustomizerProvider; +import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.ExperimentalResourceDetectionModel; +import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.ExperimentalResourceDetectorModel; +import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.ResourceModel; +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * Adds essential resource detectors to the resource model in declarative configuration, if they are + * not already present. + */ +@AutoService(DeclarativeConfigurationCustomizerProvider.class) +public class ResourceCustomizerProvider implements DeclarativeConfigurationCustomizerProvider { + + // opentelemetry-javaagent-distribution: adds "distro.name" and "distro.version" attributes + // (DistroComponentProvider in this package) + private static final List REQUIRED_DETECTORS = + Collections.singletonList("opentelemetry-javaagent-distribution"); + + @Override + public void customize(DeclarativeConfigurationCustomizer customizer) { + customizer.addModelCustomizer( + model -> { + ResourceModel resource = model.getResource(); + if (resource == null) { + resource = new ResourceModel(); + model.withResource(resource); + } + ExperimentalResourceDetectionModel detectionModel = resource.getDetectionDevelopment(); + if (detectionModel == null) { + detectionModel = new ExperimentalResourceDetectionModel(); + resource.withDetectionDevelopment(detectionModel); + } + List detectors = + Objects.requireNonNull(detectionModel.getDetectors()); + Set names = + detectors.stream() + .flatMap(detector -> detector.getAdditionalProperties().keySet().stream()) + .collect(Collectors.toSet()); + + for (String name : REQUIRED_DETECTORS) { + if (!names.contains(name)) { + ExperimentalResourceDetectorModel detector = new ExperimentalResourceDetectorModel(); + detector.getAdditionalProperties().put(name, null); + // add first (the least precedence) + // so that the user can add a differently named detector that takes precedence + detectors.add(0, detector); + } + } + return model; + }); + } +} diff --git a/javaagent-tooling/src/test/groovy/io/opentelemetry/javaagent/tooling/AddThreadDetailsSpanProcessorTest.groovy b/javaagent-tooling/src/test/groovy/io/opentelemetry/javaagent/tooling/AddThreadDetailsSpanProcessorTest.groovy deleted file mode 100644 index d3a8a8c933db..000000000000 --- a/javaagent-tooling/src/test/groovy/io/opentelemetry/javaagent/tooling/AddThreadDetailsSpanProcessorTest.groovy +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.javaagent.tooling - -import io.opentelemetry.context.Context -import io.opentelemetry.sdk.trace.ReadWriteSpan -import io.opentelemetry.semconv.incubating.ThreadIncubatingAttributes -import spock.lang.Specification - -class AddThreadDetailsSpanProcessorTest extends Specification { - def span = Mock(ReadWriteSpan) - - def processor = new AddThreadDetailsSpanProcessor() - - def "should require onStart call"() { - expect: - processor.isStartRequired() - } - - def "should set thread attributes on span start"() { - given: - def currentThreadName = Thread.currentThread().name - def currentThreadId = Thread.currentThread().id - - when: - processor.onStart(Context.root(), span) - - then: - 1 * span.setAttribute(ThreadIncubatingAttributes.THREAD_ID, currentThreadId) - 1 * span.setAttribute(ThreadIncubatingAttributes.THREAD_NAME, currentThreadName) - } -} diff --git a/javaagent-tooling/src/test/java/io/opentelemetry/javaagent/tooling/OpenTelemetryInstallerTest.java b/javaagent-tooling/src/test/java/io/opentelemetry/javaagent/tooling/OpenTelemetryInstallerTest.java index 268f4b40ccc7..c522bb4ecb23 100644 --- a/javaagent-tooling/src/test/java/io/opentelemetry/javaagent/tooling/OpenTelemetryInstallerTest.java +++ b/javaagent-tooling/src/test/java/io/opentelemetry/javaagent/tooling/OpenTelemetryInstallerTest.java @@ -6,14 +6,24 @@ package io.opentelemetry.javaagent.tooling; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatCode; import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.api.OpenTelemetry; import io.opentelemetry.javaagent.tooling.config.EarlyInitAgentConfig; import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk; +import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; +import io.opentelemetry.sdk.autoconfigure.spi.ConfigurationException; +import io.opentelemetry.sdk.extension.incubator.fileconfig.DeclarativeConfiguration; +import io.opentelemetry.sdk.extension.incubator.fileconfig.SdkConfigProvider; +import java.io.ByteArrayInputStream; +import java.nio.charset.StandardCharsets; +import java.util.function.Supplier; 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; class OpenTelemetryInstallerTest { @@ -31,4 +41,43 @@ void globalOpenTelemetry() { assertThat(sdk).isNotNull().isNotEqualTo(OpenTelemetry.noop()); } + + @ParameterizedTest + @CsvSource({ + "default, true, false", + "none, false, false", + ", true, false", // empty value means property is not set + "invalid, false, true", + }) + void defaultEnabledInDeclarativeConfigPropertiesBridge( + String propertyValue, boolean expected, boolean fail) { + String mode = propertyValue == null ? "" : "instrumentation_mode: \"" + propertyValue + "\""; + String yaml = + "file_format: \"1.0-rc.1\"\n" + + "instrumentation/development:\n" + + " java:\n" + + " agent:\n" + + " " + + mode; + + Supplier configPropertiesSupplier = + () -> + OpenTelemetryInstaller.getDeclarativeConfigBridgedProperties( + EarlyInitAgentConfig.create(), + SdkConfigProvider.create( + DeclarativeConfiguration.parse( + new ByteArrayInputStream(yaml.getBytes(StandardCharsets.UTF_8))))); + + if (fail) { + assertThatCode(() -> configPropertiesSupplier.get()) + .isInstanceOf(ConfigurationException.class) + .hasMessage("Unknown instrumentation mode: invalid"); + } else { + assertThat( + configPropertiesSupplier + .get() + .getBoolean("otel.instrumentation.common.default-enabled")) + .isEqualTo(expected); + } + } } diff --git a/javaagent-tooling/src/test/java/io/opentelemetry/javaagent/tooling/resources/ResourceCustomizerProviderTest.java b/javaagent-tooling/src/test/java/io/opentelemetry/javaagent/tooling/resources/ResourceCustomizerProviderTest.java new file mode 100644 index 000000000000..5b50cc913437 --- /dev/null +++ b/javaagent-tooling/src/test/java/io/opentelemetry/javaagent/tooling/resources/ResourceCustomizerProviderTest.java @@ -0,0 +1,36 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.tooling.resources; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.OpenTelemetryConfigurationModel; +import org.junit.jupiter.api.Test; + +class ResourceCustomizerProviderTest { + + private final ObjectMapper objectMapper = new ObjectMapper(); + + @Test + void customize() { + new ResourceCustomizerProvider() + .customize( + customizer -> { + OpenTelemetryConfigurationModel configurationModel = + customizer.apply(new OpenTelemetryConfigurationModel()); + + try { + assertThat(objectMapper.writeValueAsString(configurationModel.getResource())) + .isEqualTo( + "{\"attributes\":[],\"detection/development\":{\"detectors\":[{\"opentelemetry-javaagent-distribution\":null}]}}"); + } catch (JsonProcessingException e) { + throw new AssertionError(e); + } + }); + } +} diff --git a/javaagent/build.gradle.kts b/javaagent/build.gradle.kts index c91211d935f9..fb3ebe8cf449 100644 --- a/javaagent/build.gradle.kts +++ b/javaagent/build.gradle.kts @@ -150,7 +150,7 @@ tasks { // exclude the agent part of the javaagent-extension-api; these classes will be added in relocate tasks exclude("io/opentelemetry/javaagent/extension/**") - duplicatesStrategy = DuplicatesStrategy.EXCLUDE + duplicatesStrategy = DuplicatesStrategy.FAIL archiveFileName.set("bootstrapLibs.jar") } @@ -161,6 +161,13 @@ tasks { excludeBootstrapClasses() duplicatesStrategy = DuplicatesStrategy.FAIL + // TODO: remove after updating contrib to 1.50.0 + filesMatching("io/opentelemetry/contrib/gcp/resource/version.properties") { + duplicatesStrategy = DuplicatesStrategy.EXCLUDE + } + exclude("META-INF/LICENSE") + exclude("META-INF/NOTICE") + exclude("META-INF/maven/**") archiveFileName.set("baseJavaagentLibs-relocated-tmp.jar") } @@ -183,6 +190,16 @@ tasks { exclude("okhttp3/internal/publicsuffix/PublicSuffixDatabase.list") duplicatesStrategy = DuplicatesStrategy.FAIL + // TODO: remove after updating contrib to 1.50.0 + filesMatching("io/opentelemetry/contrib/gcp/resource/version.properties") { + duplicatesStrategy = DuplicatesStrategy.EXCLUDE + } + filesMatching("META-INF/io/opentelemetry/instrumentation/**") { + duplicatesStrategy = DuplicatesStrategy.EXCLUDE + } + exclude("META-INF/LICENSE") + exclude("META-INF/NOTICE") + exclude("META-INF/maven/**") archiveFileName.set("javaagentLibs-relocated-tmp.jar") } @@ -286,7 +303,8 @@ tasks { doLast { val filePath = rootDir.toPath().resolve("licenses").resolve("licenses.md") if (Files.exists(filePath)) { - val datePattern = Pattern.compile("^_[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2} .*_$") + val datePattern = + Pattern.compile("^_[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2} .*_$") val lines = Files.readAllLines(filePath) // 4th line contains the timestamp of when the license report was generated, replace it with // an empty line @@ -394,8 +412,8 @@ fun CopySpec.isolateClasses(jar: Provider) { // important to keep prefix "inst" short, as it is prefixed to lots of strings in runtime mem into("inst") rename("(^.*)\\.class\$", "\$1.classdata") - // Rename LICENSE file since it clashes with license dir on non-case sensitive FSs (i.e. Mac) - rename("""^LICENSE$""", "LICENSE.renamed") + exclude("""^LICENSE$""") + exclude("META-INF/LICENSE.txt") exclude("META-INF/INDEX.LIST") exclude("META-INF/*.DSA") exclude("META-INF/*.SF") @@ -412,7 +430,8 @@ fun CopySpec.copyByteBuddy(jar: Provider) { eachFile { if (path.startsWith("net/bytebuddy/") && // this is our class that we have placed in the byte buddy package, need to preserve it - !path.startsWith("net/bytebuddy/agent/builder/AgentBuilderUtil")) { + !path.startsWith("net/bytebuddy/agent/builder/AgentBuilderUtil") + ) { exclude() } else if (path.startsWith("META-INF/versions/9/net/bytebuddy/")) { path = path.removePrefix("META-INF/versions/9/") diff --git a/licenses/byte-buddy-dep-1.17.6.jar/META-INF/LICENSE b/licenses/byte-buddy-dep-1.17.7.jar/META-INF/LICENSE similarity index 100% rename from licenses/byte-buddy-dep-1.17.6.jar/META-INF/LICENSE rename to licenses/byte-buddy-dep-1.17.7.jar/META-INF/LICENSE diff --git a/licenses/byte-buddy-dep-1.17.6.jar/META-INF/NOTICE b/licenses/byte-buddy-dep-1.17.7.jar/META-INF/NOTICE similarity index 100% rename from licenses/byte-buddy-dep-1.17.6.jar/META-INF/NOTICE rename to licenses/byte-buddy-dep-1.17.7.jar/META-INF/NOTICE diff --git a/licenses/jackson-annotations-2.19.2.jar/META-INF/LICENSE b/licenses/jackson-annotations-2.20.jar/META-INF/LICENSE similarity index 100% rename from licenses/jackson-annotations-2.19.2.jar/META-INF/LICENSE rename to licenses/jackson-annotations-2.20.jar/META-INF/LICENSE diff --git a/licenses/jackson-annotations-2.19.2.jar/META-INF/NOTICE b/licenses/jackson-annotations-2.20.jar/META-INF/NOTICE similarity index 100% rename from licenses/jackson-annotations-2.19.2.jar/META-INF/NOTICE rename to licenses/jackson-annotations-2.20.jar/META-INF/NOTICE diff --git a/licenses/jackson-core-2.19.2.jar/META-INF/LICENSE b/licenses/jackson-core-2.20.0.jar/META-INF/LICENSE similarity index 100% rename from licenses/jackson-core-2.19.2.jar/META-INF/LICENSE rename to licenses/jackson-core-2.20.0.jar/META-INF/LICENSE diff --git a/licenses/jackson-core-2.19.2.jar/META-INF/NOTICE b/licenses/jackson-core-2.20.0.jar/META-INF/NOTICE similarity index 100% rename from licenses/jackson-core-2.19.2.jar/META-INF/NOTICE rename to licenses/jackson-core-2.20.0.jar/META-INF/NOTICE diff --git a/licenses/jackson-databind-2.19.2.jar/META-INF/LICENSE b/licenses/jackson-databind-2.20.0.jar/META-INF/LICENSE similarity index 100% rename from licenses/jackson-databind-2.19.2.jar/META-INF/LICENSE rename to licenses/jackson-databind-2.20.0.jar/META-INF/LICENSE diff --git a/licenses/jackson-databind-2.19.2.jar/META-INF/NOTICE b/licenses/jackson-databind-2.20.0.jar/META-INF/NOTICE similarity index 100% rename from licenses/jackson-databind-2.19.2.jar/META-INF/NOTICE rename to licenses/jackson-databind-2.20.0.jar/META-INF/NOTICE diff --git a/licenses/jackson-dataformat-yaml-2.19.2.jar/META-INF/LICENSE b/licenses/jackson-dataformat-yaml-2.20.0.jar/META-INF/LICENSE similarity index 100% rename from licenses/jackson-dataformat-yaml-2.19.2.jar/META-INF/LICENSE rename to licenses/jackson-dataformat-yaml-2.20.0.jar/META-INF/LICENSE diff --git a/licenses/jackson-dataformat-yaml-2.19.2.jar/META-INF/NOTICE b/licenses/jackson-dataformat-yaml-2.20.0.jar/META-INF/NOTICE similarity index 100% rename from licenses/jackson-dataformat-yaml-2.19.2.jar/META-INF/NOTICE rename to licenses/jackson-dataformat-yaml-2.20.0.jar/META-INF/NOTICE diff --git a/licenses/licenses.md b/licenses/licenses.md index 7cda22cd36ba..9cbfb3c860d9 100644 --- a/licenses/licenses.md +++ b/licenses/licenses.md @@ -12,33 +12,33 @@ > - **POM Project URL**: [https://github.com/raphw/weak-lock-free](https://github.com/raphw/weak-lock-free) > - **POM License**: Apache License, Version 2.0 - [http://www.apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0) -**3** **Group:** `com.fasterxml.jackson.core` **Name:** `jackson-annotations` **Version:** `2.19.2` +**3** **Group:** `com.fasterxml.jackson.core` **Name:** `jackson-annotations` **Version:** `2.20` > - **Project URL**: [https://github.com/FasterXML/jackson](https://github.com/FasterXML/jackson) > - **Manifest License**: Apache License, Version 2.0 (Not Packaged) > - **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**: [jackson-annotations-2.19.2.jar/META-INF/LICENSE](jackson-annotations-2.19.2.jar/META-INF/LICENSE) - - [jackson-annotations-2.19.2.jar/META-INF/NOTICE](jackson-annotations-2.19.2.jar/META-INF/NOTICE) +> - **Embedded license files**: [jackson-annotations-2.20.jar/META-INF/LICENSE](jackson-annotations-2.20.jar/META-INF/LICENSE) + - [jackson-annotations-2.20.jar/META-INF/NOTICE](jackson-annotations-2.20.jar/META-INF/NOTICE) -**4** **Group:** `com.fasterxml.jackson.core` **Name:** `jackson-core` **Version:** `2.19.2` +**4** **Group:** `com.fasterxml.jackson.core` **Name:** `jackson-core` **Version:** `2.20.0` > - **Project URL**: [https://github.com/FasterXML/jackson-core](https://github.com/FasterXML/jackson-core) > - **Manifest License**: Apache License, Version 2.0 (Not Packaged) > - **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**: [jackson-core-2.19.2.jar/META-INF/LICENSE](jackson-core-2.19.2.jar/META-INF/LICENSE) - - [jackson-core-2.19.2.jar/META-INF/NOTICE](jackson-core-2.19.2.jar/META-INF/NOTICE) +> - **Embedded license files**: [jackson-core-2.20.0.jar/META-INF/LICENSE](jackson-core-2.20.0.jar/META-INF/LICENSE) + - [jackson-core-2.20.0.jar/META-INF/NOTICE](jackson-core-2.20.0.jar/META-INF/NOTICE) -**5** **Group:** `com.fasterxml.jackson.core` **Name:** `jackson-databind` **Version:** `2.19.2` +**5** **Group:** `com.fasterxml.jackson.core` **Name:** `jackson-databind` **Version:** `2.20.0` > - **Project URL**: [https://github.com/FasterXML/jackson](https://github.com/FasterXML/jackson) > - **Manifest License**: Apache License, Version 2.0 (Not Packaged) > - **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**: [jackson-databind-2.19.2.jar/META-INF/LICENSE](jackson-databind-2.19.2.jar/META-INF/LICENSE) - - [jackson-databind-2.19.2.jar/META-INF/NOTICE](jackson-databind-2.19.2.jar/META-INF/NOTICE) +> - **Embedded license files**: [jackson-databind-2.20.0.jar/META-INF/LICENSE](jackson-databind-2.20.0.jar/META-INF/LICENSE) + - [jackson-databind-2.20.0.jar/META-INF/NOTICE](jackson-databind-2.20.0.jar/META-INF/NOTICE) -**6** **Group:** `com.fasterxml.jackson.dataformat` **Name:** `jackson-dataformat-yaml` **Version:** `2.19.2` +**6** **Group:** `com.fasterxml.jackson.dataformat` **Name:** `jackson-dataformat-yaml` **Version:** `2.20.0` > - **Project URL**: [https://github.com/FasterXML/jackson-dataformats-text](https://github.com/FasterXML/jackson-dataformats-text) > - **Manifest License**: Apache License, Version 2.0 (Not Packaged) > - **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**: [jackson-dataformat-yaml-2.19.2.jar/META-INF/LICENSE](jackson-dataformat-yaml-2.19.2.jar/META-INF/LICENSE) - - [jackson-dataformat-yaml-2.19.2.jar/META-INF/NOTICE](jackson-dataformat-yaml-2.19.2.jar/META-INF/NOTICE) +> - **Embedded license files**: [jackson-dataformat-yaml-2.20.0.jar/META-INF/LICENSE](jackson-dataformat-yaml-2.20.0.jar/META-INF/LICENSE) + - [jackson-dataformat-yaml-2.20.0.jar/META-INF/NOTICE](jackson-dataformat-yaml-2.20.0.jar/META-INF/NOTICE) **7** **Group:** `com.google.cloud.opentelemetry` **Name:** `detector-resources-support` **Version:** `0.36.0` > - **POM Project URL**: [https://github.com/GoogleCloudPlatform/opentelemetry-operations-java](https://github.com/GoogleCloudPlatform/opentelemetry-operations-java) @@ -65,127 +65,127 @@ > - **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) -**13** **Group:** `io.opentelemetry` **Name:** `opentelemetry-api` **Version:** `1.53.0` +**13** **Group:** `io.opentelemetry` **Name:** `opentelemetry-api` **Version:** `1.54.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-api-incubator` **Version:** `1.53.0-alpha` +**14** **Group:** `io.opentelemetry` **Name:** `opentelemetry-api-incubator` **Version:** `1.54.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) -**15** **Group:** `io.opentelemetry` **Name:** `opentelemetry-common` **Version:** `1.53.0` +**15** **Group:** `io.opentelemetry` **Name:** `opentelemetry-common` **Version:** `1.54.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) -**16** **Group:** `io.opentelemetry` **Name:** `opentelemetry-context` **Version:** `1.53.0` +**16** **Group:** `io.opentelemetry` **Name:** `opentelemetry-context` **Version:** `1.54.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) -**17** **Group:** `io.opentelemetry` **Name:** `opentelemetry-exporter-common` **Version:** `1.53.0` +**17** **Group:** `io.opentelemetry` **Name:** `opentelemetry-exporter-common` **Version:** `1.54.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-logging` **Version:** `1.53.0` +**18** **Group:** `io.opentelemetry` **Name:** `opentelemetry-exporter-logging` **Version:** `1.54.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-logging-otlp` **Version:** `1.53.0` +**19** **Group:** `io.opentelemetry` **Name:** `opentelemetry-exporter-logging-otlp` **Version:** `1.54.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) -**20** **Group:** `io.opentelemetry` **Name:** `opentelemetry-exporter-otlp` **Version:** `1.53.0` +**20** **Group:** `io.opentelemetry` **Name:** `opentelemetry-exporter-otlp` **Version:** `1.54.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-otlp-common` **Version:** `1.53.0` +**21** **Group:** `io.opentelemetry` **Name:** `opentelemetry-exporter-otlp-common` **Version:** `1.54.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-exporter-prometheus` **Version:** `1.53.0-alpha` +**22** **Group:** `io.opentelemetry` **Name:** `opentelemetry-exporter-prometheus` **Version:** `1.54.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) -**23** **Group:** `io.opentelemetry` **Name:** `opentelemetry-exporter-sender-okhttp` **Version:** `1.53.0` +**23** **Group:** `io.opentelemetry` **Name:** `opentelemetry-exporter-sender-okhttp` **Version:** `1.54.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-exporter-zipkin` **Version:** `1.53.0` +**24** **Group:** `io.opentelemetry` **Name:** `opentelemetry-exporter-zipkin` **Version:** `1.54.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-extension-kotlin` **Version:** `1.53.0` +**25** **Group:** `io.opentelemetry` **Name:** `opentelemetry-extension-kotlin` **Version:** `1.54.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-extension-trace-propagators` **Version:** `1.53.0` +**26** **Group:** `io.opentelemetry` **Name:** `opentelemetry-extension-trace-propagators` **Version:** `1.54.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` **Version:** `1.53.0` +**27** **Group:** `io.opentelemetry` **Name:** `opentelemetry-sdk` **Version:** `1.54.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-common` **Version:** `1.53.0` +**28** **Group:** `io.opentelemetry` **Name:** `opentelemetry-sdk-common` **Version:** `1.54.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) -**29** **Group:** `io.opentelemetry` **Name:** `opentelemetry-sdk-extension-autoconfigure` **Version:** `1.53.0` +**29** **Group:** `io.opentelemetry` **Name:** `opentelemetry-sdk-extension-autoconfigure` **Version:** `1.54.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-extension-autoconfigure-spi` **Version:** `1.53.0` +**30** **Group:** `io.opentelemetry` **Name:** `opentelemetry-sdk-extension-autoconfigure-spi` **Version:** `1.54.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-extension-incubator` **Version:** `1.53.0-alpha` +**31** **Group:** `io.opentelemetry` **Name:** `opentelemetry-sdk-extension-incubator` **Version:** `1.54.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) -**32** **Group:** `io.opentelemetry` **Name:** `opentelemetry-sdk-extension-jaeger-remote-sampler` **Version:** `1.53.0` +**32** **Group:** `io.opentelemetry` **Name:** `opentelemetry-sdk-extension-jaeger-remote-sampler` **Version:** `1.54.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` **Name:** `opentelemetry-sdk-logs` **Version:** `1.53.0` +**33** **Group:** `io.opentelemetry` **Name:** `opentelemetry-sdk-logs` **Version:** `1.54.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) -**34** **Group:** `io.opentelemetry` **Name:** `opentelemetry-sdk-metrics` **Version:** `1.53.0` +**34** **Group:** `io.opentelemetry` **Name:** `opentelemetry-sdk-metrics` **Version:** `1.54.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) -**35** **Group:** `io.opentelemetry` **Name:** `opentelemetry-sdk-trace` **Version:** `1.53.0` +**35** **Group:** `io.opentelemetry` **Name:** `opentelemetry-sdk-trace` **Version:** `1.54.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) -**36** **Group:** `io.opentelemetry.contrib` **Name:** `opentelemetry-aws-resources` **Version:** `1.48.0-alpha` +**36** **Group:** `io.opentelemetry.contrib` **Name:** `opentelemetry-aws-resources` **Version:** `1.49.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) -**37** **Group:** `io.opentelemetry.contrib` **Name:** `opentelemetry-aws-xray-propagator` **Version:** `1.48.0-alpha` +**37** **Group:** `io.opentelemetry.contrib` **Name:** `opentelemetry-aws-xray-propagator` **Version:** `1.49.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) -**38** **Group:** `io.opentelemetry.contrib` **Name:** `opentelemetry-azure-resources` **Version:** `1.48.0-alpha` +**38** **Group:** `io.opentelemetry.contrib` **Name:** `opentelemetry-azure-resources` **Version:** `1.49.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) -**39** **Group:** `io.opentelemetry.contrib` **Name:** `opentelemetry-baggage-processor` **Version:** `1.48.0-alpha` +**39** **Group:** `io.opentelemetry.contrib` **Name:** `opentelemetry-baggage-processor` **Version:** `1.49.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) -**40** **Group:** `io.opentelemetry.contrib` **Name:** `opentelemetry-cloudfoundry-resources` **Version:** `1.48.0-alpha` +**40** **Group:** `io.opentelemetry.contrib` **Name:** `opentelemetry-cloudfoundry-resources` **Version:** `1.49.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) -**41** **Group:** `io.opentelemetry.contrib` **Name:** `opentelemetry-gcp-resources` **Version:** `1.48.0-alpha` +**41** **Group:** `io.opentelemetry.contrib` **Name:** `opentelemetry-gcp-resources` **Version:** `1.49.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) -**42** **Group:** `io.opentelemetry.semconv` **Name:** `opentelemetry-semconv` **Version:** `1.34.0` +**42** **Group:** `io.opentelemetry.semconv` **Name:** `opentelemetry-semconv` **Version:** `1.37.0` > - **POM Project URL**: [https://github.com/open-telemetry/semantic-conventions-java](https://github.com/open-telemetry/semantic-conventions-java) > - **POM License**: Apache License, Version 2.0 - [http://www.apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0) -**43** **Group:** `io.opentelemetry.semconv` **Name:** `opentelemetry-semconv-incubating` **Version:** `1.34.0-alpha` +**43** **Group:** `io.opentelemetry.semconv` **Name:** `opentelemetry-semconv-incubating` **Version:** `1.37.0-alpha` > - **POM Project URL**: [https://github.com/open-telemetry/semantic-conventions-java](https://github.com/open-telemetry/semantic-conventions-java) > - **POM License**: Apache License, Version 2.0 - [http://www.apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0) @@ -227,16 +227,16 @@ > - **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) -**52** **Group:** `net.bytebuddy` **Name:** `byte-buddy-dep` **Version:** `1.17.6` +**52** **Group:** `net.bytebuddy` **Name:** `byte-buddy-dep` **Version:** `1.17.7` > - **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.17.6.jar/META-INF/LICENSE](byte-buddy-dep-1.17.6.jar/META-INF/LICENSE) - - [byte-buddy-dep-1.17.6.jar/META-INF/NOTICE](byte-buddy-dep-1.17.6.jar/META-INF/NOTICE) +> - **Embedded license files**: [byte-buddy-dep-1.17.7.jar/META-INF/LICENSE](byte-buddy-dep-1.17.7.jar/META-INF/LICENSE) + - [byte-buddy-dep-1.17.7.jar/META-INF/NOTICE](byte-buddy-dep-1.17.7.jar/META-INF/NOTICE) **53** **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) -**54** **Group:** `org.jetbrains.kotlin` **Name:** `kotlin-stdlib` **Version:** `2.2.0` +**54** **Group:** `org.jetbrains.kotlin` **Name:** `kotlin-stdlib` **Version:** `2.2.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/sdk-autoconfigure-support/build.gradle.kts b/sdk-autoconfigure-support/build.gradle.kts index 7a2e5e185912..c2948406f7e1 100644 --- a/sdk-autoconfigure-support/build.gradle.kts +++ b/sdk-autoconfigure-support/build.gradle.kts @@ -10,4 +10,5 @@ dependencies { compileOnly("com.google.code.findbugs:annotations") testCompileOnly("com.google.code.findbugs:annotations") + testImplementation(project(":testing-common")) } diff --git a/sdk-autoconfigure-support/src/main/java/io/opentelemetry/instrumentation/resources/ResourceProviderPropertiesCustomizer.java b/sdk-autoconfigure-support/src/main/java/io/opentelemetry/instrumentation/resources/internal/ResourceProviderPropertiesCustomizer.java similarity index 93% rename from sdk-autoconfigure-support/src/main/java/io/opentelemetry/instrumentation/resources/ResourceProviderPropertiesCustomizer.java rename to sdk-autoconfigure-support/src/main/java/io/opentelemetry/instrumentation/resources/internal/ResourceProviderPropertiesCustomizer.java index d9242de19f79..7dfabf0a7655 100644 --- a/sdk-autoconfigure-support/src/main/java/io/opentelemetry/instrumentation/resources/ResourceProviderPropertiesCustomizer.java +++ b/sdk-autoconfigure-support/src/main/java/io/opentelemetry/instrumentation/resources/internal/ResourceProviderPropertiesCustomizer.java @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.instrumentation.resources; +package io.opentelemetry.instrumentation.resources.internal; import io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizer; import io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizerProvider; @@ -17,6 +17,10 @@ import java.util.Set; 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 ResourceProviderPropertiesCustomizer implements AutoConfigurationCustomizerProvider { private static final Map DISABLED_BY_DEFAULT_RESOURCE_PROVIDERS = new HashMap<>(); @@ -49,7 +53,7 @@ public class ResourceProviderPropertiesCustomizer implements AutoConfigurationCu "cloudfoundry"); // for testing DISABLED_BY_DEFAULT_RESOURCE_PROVIDERS.put( - "io.opentelemetry.instrumentation.resources.ResourceProviderPropertiesCustomizerTest$Provider", + "io.opentelemetry.instrumentation.resources.internal.ResourceProviderPropertiesCustomizerTest$Provider", "test"); } diff --git a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/AddThreadDetailsSpanProcessor.java b/sdk-autoconfigure-support/src/main/java/io/opentelemetry/instrumentation/thread/internal/AddThreadDetailsSpanProcessor.java similarity index 58% rename from javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/AddThreadDetailsSpanProcessor.java rename to sdk-autoconfigure-support/src/main/java/io/opentelemetry/instrumentation/thread/internal/AddThreadDetailsSpanProcessor.java index 496d8580ff79..b0036384eebc 100644 --- a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/AddThreadDetailsSpanProcessor.java +++ b/sdk-autoconfigure-support/src/main/java/io/opentelemetry/instrumentation/thread/internal/AddThreadDetailsSpanProcessor.java @@ -3,22 +3,33 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.javaagent.tooling; +package io.opentelemetry.instrumentation.thread.internal; +import static io.opentelemetry.api.common.AttributeKey.longKey; +import static io.opentelemetry.api.common.AttributeKey.stringKey; + +import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.context.Context; import io.opentelemetry.sdk.common.CompletableResultCode; import io.opentelemetry.sdk.trace.ReadWriteSpan; import io.opentelemetry.sdk.trace.ReadableSpan; import io.opentelemetry.sdk.trace.SpanProcessor; -import io.opentelemetry.semconv.incubating.ThreadIncubatingAttributes; +/** + * This class is internal and is hence not for public use. Its APIs are unstable and can change at + * any time. + */ public class AddThreadDetailsSpanProcessor implements SpanProcessor { + // attributes are not stable yet + public static final AttributeKey THREAD_ID = longKey("thread.id"); + public static final AttributeKey THREAD_NAME = stringKey("thread.name"); + @Override public void onStart(Context context, ReadWriteSpan span) { Thread currentThread = Thread.currentThread(); - span.setAttribute(ThreadIncubatingAttributes.THREAD_ID, currentThread.getId()); - span.setAttribute(ThreadIncubatingAttributes.THREAD_NAME, currentThread.getName()); + span.setAttribute(THREAD_ID, currentThread.getId()); + span.setAttribute(THREAD_NAME, currentThread.getName()); } @Override diff --git a/sdk-autoconfigure-support/src/main/resources/META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizerProvider b/sdk-autoconfigure-support/src/main/resources/META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizerProvider index 3f8716ab23aa..caf1b52f87f7 100644 --- a/sdk-autoconfigure-support/src/main/resources/META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizerProvider +++ b/sdk-autoconfigure-support/src/main/resources/META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizerProvider @@ -1 +1 @@ -io.opentelemetry.instrumentation.resources.ResourceProviderPropertiesCustomizer +io.opentelemetry.instrumentation.resources.internal.ResourceProviderPropertiesCustomizer diff --git a/sdk-autoconfigure-support/src/test/java/io/opentelemetry/instrumentation/resources/ResourceProviderPropertiesCustomizerTest.java b/sdk-autoconfigure-support/src/test/java/io/opentelemetry/instrumentation/resources/internal/ResourceProviderPropertiesCustomizerTest.java similarity index 96% rename from sdk-autoconfigure-support/src/test/java/io/opentelemetry/instrumentation/resources/ResourceProviderPropertiesCustomizerTest.java rename to sdk-autoconfigure-support/src/test/java/io/opentelemetry/instrumentation/resources/internal/ResourceProviderPropertiesCustomizerTest.java index 18928d915917..1850a2e746ba 100644 --- a/sdk-autoconfigure-support/src/test/java/io/opentelemetry/instrumentation/resources/ResourceProviderPropertiesCustomizerTest.java +++ b/sdk-autoconfigure-support/src/test/java/io/opentelemetry/instrumentation/resources/internal/ResourceProviderPropertiesCustomizerTest.java @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.instrumentation.resources; +package io.opentelemetry.instrumentation.resources.internal; import static org.assertj.core.api.Assertions.assertThat; @@ -58,7 +58,7 @@ private EnabledTestCase( @TestFactory Stream enabledTestCases() { String className = - "io.opentelemetry.instrumentation.resources.ResourceProviderPropertiesCustomizerTest$Provider"; + "io.opentelemetry.instrumentation.resources.internal.ResourceProviderPropertiesCustomizerTest$Provider"; return Stream.of( new EnabledTestCase( "explicitEnabled", true, Collections.emptySet(), Collections.emptySet(), true), diff --git a/sdk-autoconfigure-support/src/test/java/io/opentelemetry/instrumentation/thread/internal/AddThreadDetailsSpanProcessorTest.java b/sdk-autoconfigure-support/src/test/java/io/opentelemetry/instrumentation/thread/internal/AddThreadDetailsSpanProcessorTest.java new file mode 100644 index 000000000000..81353695c87c --- /dev/null +++ b/sdk-autoconfigure-support/src/test/java/io/opentelemetry/instrumentation/thread/internal/AddThreadDetailsSpanProcessorTest.java @@ -0,0 +1,39 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.thread.internal; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; + +import io.opentelemetry.context.Context; +import io.opentelemetry.sdk.trace.ReadWriteSpan; +import io.opentelemetry.sdk.trace.SpanProcessor; +import io.opentelemetry.semconv.incubating.ThreadIncubatingAttributes; +import org.junit.jupiter.api.Test; + +class AddThreadDetailsSpanProcessorTest { + + private final ReadWriteSpan span = mock(ReadWriteSpan.class); + + private final SpanProcessor spanProcessor = new AddThreadDetailsSpanProcessor(); + + @Test + void onStart() { + assertThat(spanProcessor.isStartRequired()).isTrue(); + } + + @Test + void setThreadAttributes() { + Thread thread = Thread.currentThread(); + spanProcessor.onStart(Context.root(), span); + + verify(span).setAttribute(ThreadIncubatingAttributes.THREAD_ID, thread.getId()); + verify(span).setAttribute(ThreadIncubatingAttributes.THREAD_NAME, thread.getName()); + verifyNoMoreInteractions(span); + } +} diff --git a/sdk-autoconfigure-support/src/test/resources/META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.ResourceProvider b/sdk-autoconfigure-support/src/test/resources/META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.ResourceProvider index 243cb5615b57..b6ee551c801f 100644 --- a/sdk-autoconfigure-support/src/test/resources/META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.ResourceProvider +++ b/sdk-autoconfigure-support/src/test/resources/META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.ResourceProvider @@ -1 +1 @@ -io.opentelemetry.instrumentation.resources.ResourceProviderPropertiesCustomizerTest$Provider +io.opentelemetry.instrumentation.resources.internal.ResourceProviderPropertiesCustomizerTest$Provider diff --git a/settings.gradle.kts b/settings.gradle.kts index ea6ccc60a6f7..0baae4bb6962 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -2,12 +2,14 @@ pluginManagement { plugins { id("com.github.jk1.dependency-license-report") version "2.9" id("com.google.cloud.tools.jib") version "3.4.5" - id("com.gradle.plugin-publish") version "1.3.1" + id("com.gradle.plugin-publish") version "2.0.0" id("io.github.gradle-nexus.publish-plugin") version "2.0.0" - id("org.jetbrains.kotlin.jvm") version "2.2.10" + id("org.jetbrains.kotlin.jvm") version "2.2.20" id("org.xbib.gradle.plugin.jflex") version "3.0.2" - id("org.unbroken-dome.xjc") version "2.0.0" + id("com.github.bjornvester.xjc") version "1.8.2" id("org.graalvm.buildtools.native") version "0.11.0" + id("com.google.osdetector") version "1.7.3" + id("com.google.protobuf") version "0.9.5" } } @@ -20,7 +22,7 @@ plugins { // ./gradlew :smoke-tests:images:servlet:buildLinuxTestImages pushMatrix -PsmokeTestServer=jetty // ./gradlew :smoke-tests:images:servlet:buildWindowsTestImages pushMatrix -PsmokeTestServer=jetty id("com.bmuschko.docker-remote-api") version "9.4.0" apply false - id("com.gradle.develocity") version "4.1" + id("com.gradle.develocity") version "4.1.1" } dependencyResolutionManagement { @@ -39,8 +41,6 @@ dependencyResolutionManagement { plugin("versions", "org.springframework.boot").version(version) } } - // r2dbc is not compatible with earlier versions - addSpringBootCatalog("springBoot2", "2.6.15", "2.+") // spring boot 3.0 is not compatible with graalvm native image addSpringBootCatalog("springBoot31", "3.1.0", "3.+") addSpringBootCatalog("springBoot32", "3.2.0", "3.+") @@ -86,6 +86,7 @@ include(":javaagent-internal-logging-application") include(":javaagent-internal-logging-simple") include(":javaagent") include(":sdk-autoconfigure-support") +include(":declarative-config-bridge") include(":bom") include(":bom-alpha") @@ -164,6 +165,8 @@ include(":instrumentation:aws-lambda:aws-lambda-core-1.0:testing") include(":instrumentation:aws-lambda:aws-lambda-events-2.2:javaagent") include(":instrumentation:aws-lambda:aws-lambda-events-2.2:library") include(":instrumentation:aws-lambda:aws-lambda-events-2.2:testing") +include(":instrumentation:aws-lambda:aws-lambda-events-3.11:library") +include(":instrumentation:aws-lambda:aws-lambda-events-common-2.2:library") include(":instrumentation:aws-sdk:aws-sdk-1.11:javaagent") include(":instrumentation:aws-sdk:aws-sdk-1.11:library") include(":instrumentation:aws-sdk:aws-sdk-1.11:library-autoconfigure") @@ -190,7 +193,9 @@ include(":instrumentation:cassandra:cassandra-4.4:library") include(":instrumentation:cassandra:cassandra-4.4:testing") include(":instrumentation:cassandra:cassandra-4-common:testing") include(":instrumentation:cdi-testing") -include(":instrumentation:clickhouse-client-0.5:javaagent") +include(":instrumentation:clickhouse:clickhouse-client-common:javaagent") +include(":instrumentation:clickhouse:clickhouse-client-v1-0.5:javaagent") +include(":instrumentation:clickhouse:clickhouse-client-v2-0.8:javaagent") include(":instrumentation:couchbase:couchbase-2.0:javaagent") include(":instrumentation:couchbase:couchbase-2.6:javaagent") include(":instrumentation:couchbase:couchbase-2-common:javaagent") @@ -247,6 +252,7 @@ include(":instrumentation:hibernate:hibernate-3.3:javaagent") include(":instrumentation:hibernate:hibernate-4.0:javaagent") include(":instrumentation:hibernate:hibernate-6.0:javaagent") include(":instrumentation:hibernate:hibernate-6.0:spring-testing") +include(":instrumentation:hibernate:testing") include(":instrumentation:hibernate:hibernate-common:javaagent") include(":instrumentation:hibernate:hibernate-procedure-call-4.3:javaagent") include(":instrumentation:hibernate:hibernate-reactive-1.0:javaagent") @@ -432,6 +438,7 @@ include(":instrumentation:opensearch:opensearch-rest-3.0:javaagent") include(":instrumentation:opensearch:opensearch-rest-common:javaagent") include(":instrumentation:opensearch:opensearch-rest-common:testing") include(":instrumentation:opentelemetry-api:opentelemetry-api-1.0:javaagent") +include(":instrumentation:opentelemetry-api:opentelemetry-api-1.0:testing") include(":instrumentation:opentelemetry-api:opentelemetry-api-1.4:javaagent") include(":instrumentation:opentelemetry-api:opentelemetry-api-1.10:javaagent") include(":instrumentation:opentelemetry-api:opentelemetry-api-1.15:javaagent") @@ -471,7 +478,9 @@ include(":instrumentation:pulsar:pulsar-2.8:javaagent") include(":instrumentation:pulsar:pulsar-2.8:javaagent-unit-tests") include(":instrumentation:quarkus-resteasy-reactive:common-testing") include(":instrumentation:quarkus-resteasy-reactive:javaagent") +includeBuild("instrumentation/quarkus-resteasy-reactive/quarkus2-plugin") include(":instrumentation:quarkus-resteasy-reactive:quarkus2-testing") +includeBuild("instrumentation/quarkus-resteasy-reactive/quarkus3-plugin") include(":instrumentation:quarkus-resteasy-reactive:quarkus3-testing") include(":instrumentation:quartz-2.0:javaagent") include(":instrumentation:quartz-2.0:library") @@ -590,6 +599,7 @@ include(":instrumentation:spring:spring-webmvc:spring-webmvc-6.0:library") include(":instrumentation:spring:spring-webmvc:spring-webmvc-common:javaagent") include(":instrumentation:spring:spring-webmvc:spring-webmvc-common:testing") include(":instrumentation:spring:spring-ws-2.0:javaagent") +include(":instrumentation:spring:spring-ws-2.0:testing") include(":instrumentation:spring:starters:spring-boot-starter") include(":instrumentation:spring:starters:zipkin-spring-boot-starter") include(":instrumentation:spymemcached-2.12:javaagent") diff --git a/smoke-tests-otel-starter/spring-boot-2/build.gradle.kts b/smoke-tests-otel-starter/spring-boot-2/build.gradle.kts index 9fe628dd698b..788a8042bd30 100644 --- a/smoke-tests-otel-starter/spring-boot-2/build.gradle.kts +++ b/smoke-tests-otel-starter/spring-boot-2/build.gradle.kts @@ -1,6 +1,5 @@ plugins { id("otel.java-conventions") - alias(springBoot2.plugins.versions) } description = "smoke-tests-otel-starter-spring-boot-2" @@ -13,7 +12,8 @@ dependencies { implementation("org.springframework.kafka:spring-kafka") implementation("org.springframework.boot:spring-boot-starter-data-mongodb") implementation("org.springframework.boot:spring-boot-starter-aop") - implementation(platform(org.springframework.boot.gradle.plugin.SpringBootPlugin.BOM_COORDINATES)) + val testLatestDeps = gradle.startParameter.projectProperties["testLatestDeps"] == "true" + implementation(platform("org.springframework.boot:spring-boot-dependencies:" + if (testLatestDeps) "2.+" else "2.6.15")) implementation(project(":smoke-tests-otel-starter:spring-boot-common")) @@ -23,10 +23,6 @@ dependencies { testImplementation("org.springframework.boot:spring-boot-starter-test") } -springBoot { - mainClass = "io.opentelemetry.spring.smoketest.OtelSpringStarterSmokeTestApplication" -} - configurations.configureEach { resolutionStrategy { // our dependency management pins to a version that is not compatible with spring boot 2.7 diff --git a/smoke-tests-otel-starter/spring-boot-2/src/test/java/io/opentelemetry/spring/smoketest/OtelSpringStarterSmokeTest.java b/smoke-tests-otel-starter/spring-boot-2/src/test/java/io/opentelemetry/spring/smoketest/OtelSpringStarterSmokeTest.java index 65315b926d5b..f911cc5aea78 100644 --- a/smoke-tests-otel-starter/spring-boot-2/src/test/java/io/opentelemetry/spring/smoketest/OtelSpringStarterSmokeTest.java +++ b/smoke-tests-otel-starter/spring-boot-2/src/test/java/io/opentelemetry/spring/smoketest/OtelSpringStarterSmokeTest.java @@ -18,5 +18,6 @@ // The headers are simply set here to make sure that headers can be parsed "otel.exporter.otlp.headers.c=3", "otel.instrumentation.runtime-telemetry.emit-experimental-telemetry=true", + "otel.instrumentation.common.thread_details.enabled=true", }) class OtelSpringStarterSmokeTest extends AbstractOtelSpringStarterSmokeTest {} diff --git a/smoke-tests-otel-starter/spring-boot-3.2/build.gradle.kts b/smoke-tests-otel-starter/spring-boot-3.2/build.gradle.kts index 0f1ccf155b0a..0ae46cf424f0 100644 --- a/smoke-tests-otel-starter/spring-boot-3.2/build.gradle.kts +++ b/smoke-tests-otel-starter/spring-boot-3.2/build.gradle.kts @@ -56,6 +56,9 @@ tasks { checkstyleAotTest { isEnabled = false } + bootJar { + enabled = false + } } // To be able to execute the tests as GraalVM native executables @@ -76,3 +79,9 @@ graalvmNative { setForkEvery(1) } } + +// Disable collectReachabilityMetadata task to avoid configuration isolation issues +// See https://github.com/gradle/gradle/issues/17559 +tasks.named("collectReachabilityMetadata").configure { + enabled = false +} diff --git a/smoke-tests-otel-starter/spring-boot-3/build.gradle.kts b/smoke-tests-otel-starter/spring-boot-3/build.gradle.kts index 1b7b865e7b7d..b4b541e3b135 100644 --- a/smoke-tests-otel-starter/spring-boot-3/build.gradle.kts +++ b/smoke-tests-otel-starter/spring-boot-3/build.gradle.kts @@ -59,6 +59,9 @@ tasks { checkstyleAotTest { isEnabled = false } + bootJar { + enabled = false + } } // To be able to execute the tests as GraalVM native executables @@ -79,3 +82,9 @@ graalvmNative { setForkEvery(1) } } + +// Disable collectReachabilityMetadata task to avoid configuration isolation issues +// See https://github.com/gradle/gradle/issues/17559 +tasks.named("collectReachabilityMetadata").configure { + enabled = false +} diff --git a/smoke-tests-otel-starter/spring-boot-3/src/test/java/io/opentelemetry/spring/smoketest/GraalVmNativeKafkaSpringStarterSmokeTest.java b/smoke-tests-otel-starter/spring-boot-3/src/test/java/io/opentelemetry/spring/smoketest/GraalVmNativeKafkaSpringStarterSmokeTest.java index b64e19ce8c8c..fc53907b729a 100644 --- a/smoke-tests-otel-starter/spring-boot-3/src/test/java/io/opentelemetry/spring/smoketest/GraalVmNativeKafkaSpringStarterSmokeTest.java +++ b/smoke-tests-otel-starter/spring-boot-3/src/test/java/io/opentelemetry/spring/smoketest/GraalVmNativeKafkaSpringStarterSmokeTest.java @@ -27,6 +27,6 @@ AbstractKafkaSpringStarterSmokeTest.KafkaConfig.class }, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) -@EnabledInNativeImage // see JvmMongodbSpringStarterSmokeTest for the JVM test +@EnabledInNativeImage // see AbstractJvmKafkaSpringStarterSmokeTest for the JVM test @RequiresDockerCompose public class GraalVmNativeKafkaSpringStarterSmokeTest extends AbstractKafkaSpringStarterSmokeTest {} diff --git a/smoke-tests-otel-starter/spring-boot-3/src/test/java/io/opentelemetry/spring/smoketest/GraalVmNativeMongodbSpringStarterSmokeTest.java b/smoke-tests-otel-starter/spring-boot-3/src/test/java/io/opentelemetry/spring/smoketest/GraalVmNativeMongodbSpringStarterSmokeTest.java index 18a8a46dda0a..4c26d4f82a56 100644 --- a/smoke-tests-otel-starter/spring-boot-3/src/test/java/io/opentelemetry/spring/smoketest/GraalVmNativeMongodbSpringStarterSmokeTest.java +++ b/smoke-tests-otel-starter/spring-boot-3/src/test/java/io/opentelemetry/spring/smoketest/GraalVmNativeMongodbSpringStarterSmokeTest.java @@ -23,7 +23,7 @@ @SpringBootTest( classes = {OtelSpringStarterSmokeTestApplication.class, SpringSmokeOtelConfiguration.class}, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) -@EnabledInNativeImage // see JvmMongodbSpringStarterSmokeTest for the JVM test +@EnabledInNativeImage // see AbstractJvmMongodbSpringStarterSmokeTest for the JVM test @RequiresDockerCompose public class GraalVmNativeMongodbSpringStarterSmokeTest extends AbstractMongodbSpringStarterSmokeTest {} diff --git a/smoke-tests-otel-starter/spring-boot-3/src/test/java/io/opentelemetry/spring/smoketest/OtelSpringStarterSmokeTest.java b/smoke-tests-otel-starter/spring-boot-3/src/test/java/io/opentelemetry/spring/smoketest/OtelSpringStarterSmokeTest.java index 9fe9916312b2..538e68d3ffc3 100644 --- a/smoke-tests-otel-starter/spring-boot-3/src/test/java/io/opentelemetry/spring/smoketest/OtelSpringStarterSmokeTest.java +++ b/smoke-tests-otel-starter/spring-boot-3/src/test/java/io/opentelemetry/spring/smoketest/OtelSpringStarterSmokeTest.java @@ -21,6 +21,7 @@ "otel.exporter.otlp.headers.c=3", "otel.instrumentation.runtime-telemetry.emit-experimental-telemetry=true", "otel.instrumentation.runtime-telemetry-java17.enable-all=true", + "otel.instrumentation.common.thread_details.enabled=true", }) class OtelSpringStarterSmokeTest extends AbstractOtelSpringStarterSmokeTest { diff --git a/smoke-tests-otel-starter/spring-boot-common/build.gradle.kts b/smoke-tests-otel-starter/spring-boot-common/build.gradle.kts index 1e4df320a25f..7c03ffe3df1d 100644 --- a/smoke-tests-otel-starter/spring-boot-common/build.gradle.kts +++ b/smoke-tests-otel-starter/spring-boot-common/build.gradle.kts @@ -1,15 +1,12 @@ -import org.springframework.boot.gradle.tasks.bundling.BootJar - plugins { id("otel.java-conventions") - id("org.springframework.boot") version "2.6.15" } description = "smoke-tests-otel-starter-spring-boot-common" dependencies { // spring dependencies are compile only to enable testing against different versions of spring - compileOnly(platform(org.springframework.boot.gradle.plugin.SpringBootPlugin.BOM_COORDINATES)) + compileOnly(platform("org.springframework.boot:spring-boot-dependencies:2.6.15")) compileOnly("org.springframework.boot:spring-boot-starter-web") compileOnly("org.springframework.boot:spring-boot-starter-test") compileOnly("org.springframework.boot:spring-boot-starter-data-jdbc") @@ -27,6 +24,11 @@ dependencies { implementation(project(":instrumentation:spring:starters:spring-boot-starter")) } -tasks.withType { - enabled = false +tasks { + compileJava { + with(options) { + // preserve parameter names for @SpanAttribute + compilerArgs.add("-parameters") + } + } } diff --git a/smoke-tests-otel-starter/spring-boot-common/src/main/java/io/opentelemetry/spring/smoketest/AbstractJvmKafkaSpringStarterSmokeTest.java b/smoke-tests-otel-starter/spring-boot-common/src/main/java/io/opentelemetry/spring/smoketest/AbstractJvmKafkaSpringStarterSmokeTest.java index 2441e61d2cb1..7052b6eef4a8 100644 --- a/smoke-tests-otel-starter/spring-boot-common/src/main/java/io/opentelemetry/spring/smoketest/AbstractJvmKafkaSpringStarterSmokeTest.java +++ b/smoke-tests-otel-starter/spring-boot-common/src/main/java/io/opentelemetry/spring/smoketest/AbstractJvmKafkaSpringStarterSmokeTest.java @@ -8,6 +8,7 @@ import io.opentelemetry.api.OpenTelemetry; import io.opentelemetry.instrumentation.spring.autoconfigure.OpenTelemetryAutoConfiguration; import io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.kafka.KafkaInstrumentationAutoConfiguration; +import io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.thread.ThreadDetailsAutoConfiguration; import java.time.Duration; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; @@ -50,6 +51,7 @@ void setUpContext() { .withConfiguration( AutoConfigurations.of( OpenTelemetryAutoConfiguration.class, + ThreadDetailsAutoConfiguration.class, SpringSmokeOtelConfiguration.class, KafkaAutoConfiguration.class, KafkaInstrumentationAutoConfiguration.class, diff --git a/smoke-tests-otel-starter/spring-boot-common/src/main/java/io/opentelemetry/spring/smoketest/AbstractOtelSpringStarterSmokeTest.java b/smoke-tests-otel-starter/spring-boot-common/src/main/java/io/opentelemetry/spring/smoketest/AbstractOtelSpringStarterSmokeTest.java index 7395ed49f0a4..c35322169344 100644 --- a/smoke-tests-otel-starter/spring-boot-common/src/main/java/io/opentelemetry/spring/smoketest/AbstractOtelSpringStarterSmokeTest.java +++ b/smoke-tests-otel-starter/spring-boot-common/src/main/java/io/opentelemetry/spring/smoketest/AbstractOtelSpringStarterSmokeTest.java @@ -30,12 +30,12 @@ import io.opentelemetry.semconv.incubating.CodeIncubatingAttributes; import io.opentelemetry.semconv.incubating.DbIncubatingAttributes; import io.opentelemetry.semconv.incubating.ServiceIncubatingAttributes; +import io.opentelemetry.semconv.incubating.ThreadIncubatingAttributes; import java.net.URI; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; -import org.assertj.core.api.AbstractCharSequenceAssert; import org.assertj.core.api.AbstractIterableAssert; import org.assertj.core.api.MapAssert; import org.junit.jupiter.api.MethodOrderer; @@ -180,9 +180,7 @@ void shouldSendTelemetry() { UrlAttributes.URL_FULL, stringAssert -> stringAssert.endsWith("/ping")), equalTo(ServerAttributes.SERVER_ADDRESS, "localhost"), - satisfies( - ServerAttributes.SERVER_PORT, - integerAssert -> integerAssert.isNotZero())), + satisfies(ServerAttributes.SERVER_PORT, val -> val.isNotZero())), serverSpan -> HttpSpanDataAssert.create(serverSpan) .assertServerGetRequest("/ping") @@ -195,7 +193,7 @@ void shouldSendTelemetry() { .hasAttribute( satisfies( ServiceIncubatingAttributes.SERVICE_INSTANCE_ID, - AbstractCharSequenceAssert::isNotBlank))) + val -> val.isNotBlank()))) .hasAttributesSatisfying( equalTo(HttpAttributes.HTTP_REQUEST_METHOD, "GET"), equalTo(HttpAttributes.HTTP_RESPONSE_STATUS_CODE, 200L), @@ -207,10 +205,11 @@ void shouldSendTelemetry() { equalTo( AttributeKey.stringArrayKey("http.request.header.key"), Collections.singletonList("value")), + satisfies(ServerAttributes.SERVER_PORT, val -> val.isNotZero()), + satisfies(ThreadIncubatingAttributes.THREAD_ID, val -> val.isNotZero()), satisfies( - ServerAttributes.SERVER_PORT, - integerAssert -> integerAssert.isNotZero())), - span -> withSpanAssert(span))); + ThreadIncubatingAttributes.THREAD_NAME, val -> val.isNotBlank())), + val -> withSpanAssert(val))); // Metric testing.waitAndAssertMetrics( diff --git a/smoke-tests-otel-starter/spring-boot-reactive-2/build.gradle.kts b/smoke-tests-otel-starter/spring-boot-reactive-2/build.gradle.kts index 83a9d245567c..e0969fc4d44b 100644 --- a/smoke-tests-otel-starter/spring-boot-reactive-2/build.gradle.kts +++ b/smoke-tests-otel-starter/spring-boot-reactive-2/build.gradle.kts @@ -1,13 +1,13 @@ plugins { id("otel.java-conventions") - alias(springBoot2.plugins.versions) } description = "smoke-tests-otel-starter-spring-boot-reactive-2" dependencies { implementation(project(":instrumentation:spring:starters:spring-boot-starter")) - implementation(platform(org.springframework.boot.gradle.plugin.SpringBootPlugin.BOM_COORDINATES)) + val testLatestDeps = gradle.startParameter.projectProperties["testLatestDeps"] == "true" + implementation(platform("org.springframework.boot:spring-boot-dependencies:" + if (testLatestDeps) "2.+" else "2.6.15")) implementation(project(":smoke-tests-otel-starter:spring-boot-reactive-common")) implementation("org.springframework.boot:spring-boot-starter-webflux") @@ -20,10 +20,6 @@ dependencies { testImplementation("io.projectreactor:reactor-test") } -springBoot { - mainClass = "io.opentelemetry.spring.smoketest.OtelReactiveSpringStarterSmokeTestApplication" -} - configurations.configureEach { resolutionStrategy { // our dependency management pins to a version that is not compatible with spring boot 2.7 diff --git a/smoke-tests-otel-starter/spring-boot-reactive-3/build.gradle.kts b/smoke-tests-otel-starter/spring-boot-reactive-3/build.gradle.kts index 64049fbe61fb..33dd3e20c860 100644 --- a/smoke-tests-otel-starter/spring-boot-reactive-3/build.gradle.kts +++ b/smoke-tests-otel-starter/spring-boot-reactive-3/build.gradle.kts @@ -50,6 +50,9 @@ tasks { checkstyleAotTest { isEnabled = false } + bootJar { + enabled = false + } } // To be able to execute the tests as GraalVM native executables @@ -70,3 +73,9 @@ graalvmNative { setForkEvery(1) } } + +// Disable collectReachabilityMetadata task to avoid configuration isolation issues +// See https://github.com/gradle/gradle/issues/17559 +tasks.named("collectReachabilityMetadata").configure { + enabled = false +} diff --git a/smoke-tests-otel-starter/spring-boot-reactive-common/build.gradle.kts b/smoke-tests-otel-starter/spring-boot-reactive-common/build.gradle.kts index 77eeb8ea3d0d..2950d0ab1f7d 100644 --- a/smoke-tests-otel-starter/spring-boot-reactive-common/build.gradle.kts +++ b/smoke-tests-otel-starter/spring-boot-reactive-common/build.gradle.kts @@ -1,22 +1,15 @@ -import org.springframework.boot.gradle.tasks.bundling.BootJar - plugins { id("otel.java-conventions") - id("org.springframework.boot") version "2.6.15" } description = "smoke-tests-otel-starter-spring-boot-reactive-common" dependencies { // spring dependencies are compile only to enable testing against different versions of spring - compileOnly(platform(org.springframework.boot.gradle.plugin.SpringBootPlugin.BOM_COORDINATES)) + compileOnly(platform("org.springframework.boot:spring-boot-dependencies:2.6.15")) compileOnly("org.springframework.boot:spring-boot-starter-web") compileOnly("org.springframework.boot:spring-boot-starter-webflux") compileOnly("org.springframework.boot:spring-boot-starter-test") compileOnly("org.springframework.boot:spring-boot-starter-data-r2dbc") api(project(":smoke-tests-otel-starter:spring-smoke-testing")) } - -tasks.withType { - enabled = false -} diff --git a/smoke-tests-otel-starter/spring-smoke-testing/build.gradle.kts b/smoke-tests-otel-starter/spring-smoke-testing/build.gradle.kts index fbbb80bd6478..0ffd2f38de95 100644 --- a/smoke-tests-otel-starter/spring-smoke-testing/build.gradle.kts +++ b/smoke-tests-otel-starter/spring-smoke-testing/build.gradle.kts @@ -1,22 +1,15 @@ -import org.springframework.boot.gradle.tasks.bundling.BootJar - plugins { id("otel.java-conventions") - id("org.springframework.boot") version "2.6.15" } description = "smoke-tests-otel-starter-spring-smoke-testing" dependencies { // spring dependencies are compile only to enable testing against different versions of spring - compileOnly(platform(org.springframework.boot.gradle.plugin.SpringBootPlugin.BOM_COORDINATES)) + compileOnly(platform("org.springframework.boot:spring-boot-dependencies:2.6.15")) compileOnly("org.springframework.boot:spring-boot-starter") compileOnly("org.springframework.boot:spring-boot-starter-test") api(project(":testing-common")) api("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure-spi") implementation("com.google.guava:guava") } - -tasks.withType { - enabled = false -} diff --git a/smoke-tests/build.gradle.kts b/smoke-tests/build.gradle.kts index a812eeecffc5..4a14fe7ca22b 100644 --- a/smoke-tests/build.gradle.kts +++ b/smoke-tests/build.gradle.kts @@ -15,7 +15,7 @@ otelJava { maxJavaVersionForTests.set(JavaVersion.VERSION_11) } -val dockerJavaVersion = "3.5.3" +val dockerJavaVersion = "3.6.0" dependencies { testCompileOnly("com.google.auto.value:auto-value-annotations") testAnnotationProcessor("com.google.auto.value:auto-value") @@ -23,13 +23,13 @@ dependencies { api("org.spockframework:spock-core") api(project(":testing-common")) - implementation(platform("io.grpc:grpc-bom:1.74.0")) + implementation(platform("io.grpc:grpc-bom:1.75.0")) implementation("org.slf4j:slf4j-api") implementation("io.opentelemetry:opentelemetry-api") implementation("io.opentelemetry.proto:opentelemetry-proto") implementation("org.testcontainers:testcontainers") implementation("com.fasterxml.jackson.core:jackson-databind") - implementation("com.google.protobuf:protobuf-java-util:4.32.0") + implementation("com.google.protobuf:protobuf-java-util:4.32.1") implementation("io.grpc:grpc-netty-shaded") implementation("io.grpc:grpc-protobuf") implementation("io.grpc:grpc-stub") diff --git a/smoke-tests/images/early-jdk8/Dockerfile b/smoke-tests/images/early-jdk8/Dockerfile index 969664549afa..7fa04d420271 100644 --- a/smoke-tests/images/early-jdk8/Dockerfile +++ b/smoke-tests/images/early-jdk8/Dockerfile @@ -1,5 +1,5 @@ # https://github.com/zulu-openjdk/zulu-openjdk/blob/master/ubuntu/8u412-8.78/Dockerfile -FROM ubuntu:noble-20250714@sha256:a08e551cb33850e4740772b38217fc1796a66da2506d312abe51acda354ff061 +FROM ubuntu:noble-20250805@sha256:9cbed754112939e914291337b5e554b07ad7c392491dba6daf25eef1332a22e8 ENV LANG='en_US.UTF-8' LANGUAGE='en_US:en' LC_ALL='en_US.UTF-8' diff --git a/smoke-tests/images/fake-backend/build.gradle.kts b/smoke-tests/images/fake-backend/build.gradle.kts index 6b9206ca6a58..286c8e8ee57e 100644 --- a/smoke-tests/images/fake-backend/build.gradle.kts +++ b/smoke-tests/images/fake-backend/build.gradle.kts @@ -11,7 +11,7 @@ plugins { } dependencies { - implementation("com.linecorp.armeria:armeria-grpc:1.33.1") + implementation("com.linecorp.armeria:armeria-grpc:1.33.2") implementation("io.opentelemetry.proto:opentelemetry-proto") runtimeOnly("org.slf4j:slf4j-simple") } diff --git a/smoke-tests/images/fake-backend/src/docker/backend/windows.dockerfile b/smoke-tests/images/fake-backend/src/docker/backend/windows.dockerfile index 97e7dc7b7fde..a26a6bf07e6f 100644 --- a/smoke-tests/images/fake-backend/src/docker/backend/windows.dockerfile +++ b/smoke-tests/images/fake-backend/src/docker/backend/windows.dockerfile @@ -1,3 +1,3 @@ -FROM eclipse-temurin:21.0.8_9-jdk-windowsservercore-ltsc2022@sha256:87e4af970a21c3a1eb37b39c42621308f71e16cf95bbfbc8e66ad77d6582b1a3 +FROM eclipse-temurin:21.0.8_9-jdk-windowsservercore-ltsc2022@sha256:4c08cf81232e0278ca08cb6a267e94c06dbe4f4d705967f7f4bce9c62b4f60c9 COPY fake-backend.jar /fake-backend.jar CMD ["java", "-jar", "/fake-backend.jar"] diff --git a/smoke-tests/images/grpc/build.gradle.kts b/smoke-tests/images/grpc/build.gradle.kts index 4f0057ae2f2b..d84019b816ea 100644 --- a/smoke-tests/images/grpc/build.gradle.kts +++ b/smoke-tests/images/grpc/build.gradle.kts @@ -8,7 +8,7 @@ plugins { } dependencies { - implementation(platform("io.grpc:grpc-bom:1.74.0")) + implementation(platform("io.grpc:grpc-bom:1.75.0")) implementation(platform("io.opentelemetry:opentelemetry-bom:1.0.0")) implementation(platform("io.opentelemetry:opentelemetry-bom-alpha:1.0.0-alpha")) implementation(platform("org.apache.logging.log4j:log4j-bom:2.25.1")) diff --git a/smoke-tests/images/quarkus/build.gradle.kts b/smoke-tests/images/quarkus/build.gradle.kts index b5e92a2aa300..e6db5d2db8d4 100644 --- a/smoke-tests/images/quarkus/build.gradle.kts +++ b/smoke-tests/images/quarkus/build.gradle.kts @@ -16,7 +16,7 @@ plugins { } dependencies { - implementation(enforcedPlatform("io.quarkus:quarkus-bom:3.25.0")) + implementation(enforcedPlatform("io.quarkus:quarkus-bom:3.26.0")) implementation("io.quarkus:quarkus-rest") } @@ -62,6 +62,10 @@ tasks { dependsOn(quarkusBuild) } + compileJava { + dependsOn(compileQuarkusGeneratedSourcesJava) + } + sourcesJar { dependsOn(quarkusGenerateCode, compileQuarkusGeneratedSourcesJava) } diff --git a/smoke-tests/images/servlet/build.gradle.kts b/smoke-tests/images/servlet/build.gradle.kts index a8d7627a0bd7..1a52c535d360 100644 --- a/smoke-tests/images/servlet/build.gradle.kts +++ b/smoke-tests/images/servlet/build.gradle.kts @@ -1,6 +1,5 @@ import com.bmuschko.gradle.docker.tasks.image.DockerBuildImage import com.bmuschko.gradle.docker.tasks.image.DockerPushImage -import org.apache.commons.lang.StringUtils plugins { id("otel.spotless-conventions") @@ -105,7 +104,7 @@ tasks { continue } println(server) - val serverName = StringUtils.capitalize(server) + val serverName = server.replaceFirstChar(Char::uppercase) for (entry in matrices) { for (version in entry.version) { val dotIndex = version.indexOf('.') diff --git a/smoke-tests/images/servlet/src/jetty.windows.dockerfile b/smoke-tests/images/servlet/src/jetty.windows.dockerfile index 307baf799329..cb4b92440d81 100644 --- a/smoke-tests/images/servlet/src/jetty.windows.dockerfile +++ b/smoke-tests/images/servlet/src/jetty.windows.dockerfile @@ -1,7 +1,7 @@ ARG jdkImage # Unzip in a separate container so that zip file layer is not part of final image -FROM mcr.microsoft.com/windows/servercore:ltsc2022@sha256:3281482945016cdaefbe417edd8338de8119e077b6941f74e78b050da1b7bd97 as builder +FROM mcr.microsoft.com/windows/servercore:ltsc2022@sha256:d9e1a220c13cf25c7b213fbd96df2b63671e2dba0de3909003d4bb23a8bc8a1c as builder ARG sourceVersion ADD https://repo1.maven.org/maven2/org/eclipse/jetty/jetty-home/${sourceVersion}/jetty-home-${sourceVersion}.zip /server.zip diff --git a/smoke-tests/images/servlet/src/liberty.windows.dockerfile b/smoke-tests/images/servlet/src/liberty.windows.dockerfile index 00a3b9d2984a..ccaa3e31ba47 100644 --- a/smoke-tests/images/servlet/src/liberty.windows.dockerfile +++ b/smoke-tests/images/servlet/src/liberty.windows.dockerfile @@ -1,7 +1,7 @@ ARG jdkImage # Unzip in a separate container so that zip file layer is not part of final image -FROM mcr.microsoft.com/windows/servercore:ltsc2022@sha256:3281482945016cdaefbe417edd8338de8119e077b6941f74e78b050da1b7bd97 as builder +FROM mcr.microsoft.com/windows/servercore:ltsc2022@sha256:d9e1a220c13cf25c7b213fbd96df2b63671e2dba0de3909003d4bb23a8bc8a1c as builder ARG version ARG release diff --git a/smoke-tests/images/servlet/src/payara.windows.dockerfile b/smoke-tests/images/servlet/src/payara.windows.dockerfile index 3e3123a902f2..c9e2ad9cff33 100644 --- a/smoke-tests/images/servlet/src/payara.windows.dockerfile +++ b/smoke-tests/images/servlet/src/payara.windows.dockerfile @@ -1,7 +1,7 @@ ARG jdkImage # Unzip in a separate container so that zip file layer is not part of final image -FROM mcr.microsoft.com/windows/servercore:ltsc2022@sha256:3281482945016cdaefbe417edd8338de8119e077b6941f74e78b050da1b7bd97 as builder +FROM mcr.microsoft.com/windows/servercore:ltsc2022@sha256:d9e1a220c13cf25c7b213fbd96df2b63671e2dba0de3909003d4bb23a8bc8a1c as builder ARG version ADD https://nexus.payara.fish/repository/payara-community/fish/payara/distributions/payara/${version}/payara-${version}.zip /server.zip diff --git a/smoke-tests/images/servlet/src/tomcat.windows.dockerfile b/smoke-tests/images/servlet/src/tomcat.windows.dockerfile index 638aff3183b5..97bbfca1cf93 100644 --- a/smoke-tests/images/servlet/src/tomcat.windows.dockerfile +++ b/smoke-tests/images/servlet/src/tomcat.windows.dockerfile @@ -1,7 +1,7 @@ ARG jdkImage # Unzip in a separate container so that zip file layer is not part of final image -FROM mcr.microsoft.com/windows/servercore:ltsc2022@sha256:3281482945016cdaefbe417edd8338de8119e077b6941f74e78b050da1b7bd97 as builder +FROM mcr.microsoft.com/windows/servercore:ltsc2022@sha256:d9e1a220c13cf25c7b213fbd96df2b63671e2dba0de3909003d4bb23a8bc8a1c as builder ARG majorVersion ARG version diff --git a/smoke-tests/images/servlet/src/tomee.windows.dockerfile b/smoke-tests/images/servlet/src/tomee.windows.dockerfile index 388ef3cbcc78..83ba30752c87 100644 --- a/smoke-tests/images/servlet/src/tomee.windows.dockerfile +++ b/smoke-tests/images/servlet/src/tomee.windows.dockerfile @@ -1,7 +1,7 @@ ARG jdkImage # Unzip in a separate container so that zip file layer is not part of final image -FROM mcr.microsoft.com/windows/servercore:ltsc2022@sha256:3281482945016cdaefbe417edd8338de8119e077b6941f74e78b050da1b7bd97 as builder +FROM mcr.microsoft.com/windows/servercore:ltsc2022@sha256:d9e1a220c13cf25c7b213fbd96df2b63671e2dba0de3909003d4bb23a8bc8a1c as builder ARG version ADD https://archive.apache.org/dist/tomee/tomee-${version}/apache-tomee-${version}-webprofile.zip /server.zip diff --git a/smoke-tests/images/servlet/src/wildfly.windows.dockerfile b/smoke-tests/images/servlet/src/wildfly.windows.dockerfile index 53427c947ee6..1757684baf75 100644 --- a/smoke-tests/images/servlet/src/wildfly.windows.dockerfile +++ b/smoke-tests/images/servlet/src/wildfly.windows.dockerfile @@ -1,7 +1,7 @@ ARG jdkImage # Unzip in a separate container so that zip file layer is not part of final image -FROM mcr.microsoft.com/windows/servercore:ltsc2022@sha256:3281482945016cdaefbe417edd8338de8119e077b6941f74e78b050da1b7bd97 as builder +FROM mcr.microsoft.com/windows/servercore:ltsc2022@sha256:d9e1a220c13cf25c7b213fbd96df2b63671e2dba0de3909003d4bb23a8bc8a1c as builder ARG version ARG baseDownloadUrl diff --git a/smoke-tests/images/spring-boot/build.gradle.kts b/smoke-tests/images/spring-boot/build.gradle.kts index 0ee7f321aa5d..495aa605735c 100644 --- a/smoke-tests/images/spring-boot/build.gradle.kts +++ b/smoke-tests/images/spring-boot/build.gradle.kts @@ -5,11 +5,11 @@ plugins { id("otel.java-conventions") id("com.google.cloud.tools.jib") - id("org.springframework.boot") version "2.6.15" + id("org.springframework.boot") version "3.5.5" } dependencies { - implementation(platform("io.opentelemetry:opentelemetry-bom:1.0.0")) + implementation(platform("io.opentelemetry:opentelemetry-bom")) implementation(platform("org.springframework.boot:spring-boot-dependencies:2.6.15")) implementation("io.opentelemetry:opentelemetry-api") @@ -17,6 +17,10 @@ dependencies { implementation("org.springframework.boot:spring-boot-starter-web") } +otelJava { + minJavaVersionSupported = JavaVersion.VERSION_17 +} + configurations.runtimeClasspath { resolutionStrategy { // requires old logback (and therefore also old slf4j) @@ -25,18 +29,11 @@ configurations.runtimeClasspath { } } -val targetJDK = project.findProperty("targetJDK") ?: "11" +val targetJDK = project.findProperty("targetJDK") ?: "17" val tag = findProperty("tag") ?: DateTimeFormatter.ofPattern("yyyyMMdd.HHmmSS").format(LocalDateTime.now()) -java { - // needed by jib to detect java version used in project - // for jdk9+ jib uses an entrypoint that doesn't work with jdk8 - sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 -} - springBoot { buildInfo { } diff --git a/smoke-tests/images/spring-boot/src/main/resources/application.properties b/smoke-tests/images/spring-boot/src/main/resources/application.properties index e15fa6f1a715..4c84ee7ad726 100644 --- a/smoke-tests/images/spring-boot/src/main/resources/application.properties +++ b/smoke-tests/images/spring-boot/src/main/resources/application.properties @@ -2,4 +2,4 @@ spring.application.name=otel-spring-test-app logging.level.root=WARN logging.level.io.opentelemetry=INFO -logging.pattern.console=%-5level [%t] %C{1.}: %msg trace_id=%X{trace_id}%n +logging.pattern.console=%-5level [%t] %C: %msg trace_id=%X{trace_id}%n diff --git a/smoke-tests/src/test/groovy/io/opentelemetry/smoketest/DeclarativeConfigurationSmokeTest.groovy b/smoke-tests/src/test/groovy/io/opentelemetry/smoketest/DeclarativeConfigurationSmokeTest.groovy new file mode 100644 index 000000000000..0e111ceaa442 --- /dev/null +++ b/smoke-tests/src/test/groovy/io/opentelemetry/smoketest/DeclarativeConfigurationSmokeTest.groovy @@ -0,0 +1,78 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.smoketest + + +import io.opentelemetry.proto.collector.trace.v1.ExportTraceServiceRequest +import spock.lang.IgnoreIf +import spock.lang.Unroll + +import java.time.Duration + +import static io.opentelemetry.smoketest.TestContainerManager.useWindowsContainers + +@IgnoreIf({ useWindowsContainers() }) +class DeclarativeConfigurationSmokeTest extends SmokeTest { + + protected String getTargetImage(String jdk) { + "ghcr.io/open-telemetry/opentelemetry-java-instrumentation/smoke-test-spring-boot:jdk$jdk-20241021.11448062567" + } + + @Override + protected List getExtraResources() { + [ + ResourceMapping.of("declarative-config.yaml", "/declarative-config.yaml"), + ] + } + + @Override + protected Map getExtraEnv() { + return ["OTEL_EXPERIMENTAL_CONFIG_FILE": "declarative-config.yaml"] + } + + @Override + protected TargetWaitStrategy getWaitStrategy() { + return new TargetWaitStrategy.Log(Duration.ofMinutes(1), ".*Started SpringbootApplication in.*") + } + + @Unroll + def "spring boot smoke test on JDK #jdk"(int jdk) { + setup: + startTarget(jdk) + + when: + client().get("/greeting").aggregate().join() + Collection traces = waitForTraces() + + then: "There is one trace" + traces.size() == 1 + + then: "There is one span (io.opentelemetry.opentelemetry-instrumentation-annotations-1.16 " + + "is not used, because instrumentation_mode=none)" + getSpanStream(traces).count() == 1 + + then: "explicitly set attribute is present" + hasResourceAttribute(traces, "service.name", "declarative-config-smoke-test") + + then: "explicitly set container detector is used" + findResourceAttribute(traces, "container.id").findAny().isPresent() + + then: "explicitly set container process detector is used" + findResourceAttribute(traces, "process.executable.path").findAny().isPresent() + + then: "explicitly set container host detector is used" + findResourceAttribute(traces, "host.name").findAny().isPresent() + + then: "distro detector is added by customizer" + hasResourceAttribute(traces, "telemetry.distro.name", "opentelemetry-javaagent") + + cleanup: + stopTarget() + + where: + jdk << [8, 11, 17] + } +} diff --git a/smoke-tests/src/test/groovy/io/opentelemetry/smoketest/SmokeTest.groovy b/smoke-tests/src/test/groovy/io/opentelemetry/smoketest/SmokeTest.groovy index d8bdee7dfa3b..d2f6f53e613f 100644 --- a/smoke-tests/src/test/groovy/io/opentelemetry/smoketest/SmokeTest.groovy +++ b/smoke-tests/src/test/groovy/io/opentelemetry/smoketest/SmokeTest.groovy @@ -118,6 +118,15 @@ abstract class SmokeTest extends Specification { .map { it.value } } + protected static boolean hasResourceAttribute(Collection traces, + String attributeKey, + String attributeValue) { + return findResourceAttribute(traces, attributeKey) + .filter { it.stringValue == attributeValue } + .findAny() + .isPresent() + } + protected static int countSpansByName(Collection traces, String spanName) { return getSpanStream(traces).filter { it.name == spanName }.count() } diff --git a/smoke-tests/src/test/resources/declarative-config.yaml b/smoke-tests/src/test/resources/declarative-config.yaml new file mode 100644 index 000000000000..86091dc4d39f --- /dev/null +++ b/smoke-tests/src/test/resources/declarative-config.yaml @@ -0,0 +1,28 @@ +file_format: 1.0-rc.1 + +tracer_provider: + processors: + - batch: + schedule_delay: 10 + max_export_batch_size: 1 + exporter: + otlp_grpc: + endpoint: ${OTEL_EXPORTER_OTLP_ENDPOINT} + +# add extra resource attributes to verify that declarative config is picked up +resource: + detection/development: + detectors: + - container: + - host: + - process: + attributes: + - name: service.name + value: declarative-config-smoke-test + +instrumentation/development: + java: + agent: + instrumentation_mode: none + tomcat: + enabled: true diff --git a/test-report/build.gradle.kts b/test-report/build.gradle.kts index a0582658059c..76f74ef5a70d 100644 --- a/test-report/build.gradle.kts +++ b/test-report/build.gradle.kts @@ -5,7 +5,7 @@ plugins { dependencies { implementation("com.google.api-client:google-api-client:2.8.1") implementation("com.google.apis:google-api-services-sheets:v4-rev20250616-2.0.0") - implementation("com.google.auth:google-auth-library-oauth2-http:1.37.1") + implementation("com.google.auth:google-auth-library-oauth2-http:1.39.0") } otelJava { diff --git a/testing-common/integration-tests/build.gradle.kts b/testing-common/integration-tests/build.gradle.kts index 067d71082b13..01a39a95ae75 100644 --- a/testing-common/integration-tests/build.gradle.kts +++ b/testing-common/integration-tests/build.gradle.kts @@ -29,6 +29,8 @@ dependencies { tasks { val testFieldInjectionDisabled by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath filter { includeTestsMatching("context.FieldInjectionDisabledTest") } @@ -37,6 +39,8 @@ tasks { } val testFieldBackedImplementation by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath filter { includeTestsMatching("context.FieldBackedImplementationTest") } @@ -59,7 +63,6 @@ tasks { } check { - dependsOn(testFieldInjectionDisabled) - dependsOn(testFieldBackedImplementation) + dependsOn(testFieldInjectionDisabled, testFieldBackedImplementation) } } diff --git a/testing-common/integration-tests/src/test/java/instrumentation/AgentInstrumentationTest.java b/testing-common/integration-tests/src/test/java/instrumentation/AgentInstrumentationTest.java index c83a2161a53a..fa0a44bc5dd5 100644 --- a/testing-common/integration-tests/src/test/java/instrumentation/AgentInstrumentationTest.java +++ b/testing-common/integration-tests/src/test/java/instrumentation/AgentInstrumentationTest.java @@ -50,10 +50,15 @@ void classPathSetUp() throws ClassNotFoundException { for (ClassPath.ClassInfo info : getTestClasspath().getAllClasses()) { for (String bootstrapPrefix : BOOTSTRAP_PACKAGE_PREFIXES) { if (info.getName().startsWith(bootstrapPrefix)) { - Class bootstrapClass = Class.forName(info.getName()); - ClassLoader loader = bootstrapClass.getClassLoader(); - if (loader != BOOTSTRAP_CLASSLOADER) { - bootstrapClassesIncorrectlyLoaded.add(bootstrapClass); + try { + Class bootstrapClass = Class.forName(info.getName()); + ClassLoader loader = bootstrapClass.getClassLoader(); + if (loader != BOOTSTRAP_CLASSLOADER) { + bootstrapClassesIncorrectlyLoaded.add(bootstrapClass); + } + } catch (ClassNotFoundException | NoClassDefFoundError e) { + throw new RuntimeException( + "Failed to load bootstrap class: " + info.getName() + " in " + bootstrapPrefix, e); } } } 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 d7652e3e907b..eec43d3a290e 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 @@ -30,6 +30,7 @@ import io.opentelemetry.semconv.ServerAttributes; import io.opentelemetry.semconv.UrlAttributes; import io.opentelemetry.semconv.UserAgentAttributes; +import io.opentelemetry.semconv.incubating.UrlIncubatingAttributes; import java.net.URI; import java.time.Duration; import java.util.ArrayList; @@ -1058,8 +1059,7 @@ void spanEndsAfterHeadersReceived() throws Exception { }); } - // Visible for spock bridge. - SpanDataAssert assertClientSpan( + protected SpanDataAssert assertClientSpan( SpanDataAssert span, URI uri, String method, @@ -1119,6 +1119,12 @@ SpanDataAssert assertClientSpan( if (httpClientAttributes.contains(UrlAttributes.URL_FULL)) { assertThat(attrs).containsEntry(UrlAttributes.URL_FULL, uri.toString()); } + if (options.getHasUrlTemplate()) { + assertThat(attrs) + .containsEntry( + UrlIncubatingAttributes.URL_TEMPLATE, + options.getExpectedUrlTemplateMapper().apply(uri)); + } if (httpClientAttributes.contains(HttpAttributes.HTTP_REQUEST_METHOD)) { assertThat(attrs).containsEntry(HttpAttributes.HTTP_REQUEST_METHOD, method); } @@ -1148,8 +1154,7 @@ SpanDataAssert assertClientSpan( }); } - // Visible for spock bridge. - static SpanDataAssert assertServerSpan(SpanDataAssert span) { + protected static SpanDataAssert assertServerSpan(SpanDataAssert span) { return span.hasName("test-http-server").hasKind(SpanKind.SERVER); } diff --git a/testing-common/src/main/java/io/opentelemetry/instrumentation/testing/junit/http/HttpClientTestOptions.java b/testing-common/src/main/java/io/opentelemetry/instrumentation/testing/junit/http/HttpClientTestOptions.java index 6880e39ee193..d8438864488d 100644 --- a/testing-common/src/main/java/io/opentelemetry/instrumentation/testing/junit/http/HttpClientTestOptions.java +++ b/testing-common/src/main/java/io/opentelemetry/instrumentation/testing/junit/http/HttpClientTestOptions.java @@ -39,6 +39,8 @@ public abstract class HttpClientTestOptions { public static final BiFunction DEFAULT_EXPECTED_CLIENT_SPAN_NAME_MAPPER = (uri, method) -> HttpConstants._OTHER.equals(method) ? "HTTP" : method; + public static final Function DEFAULT_EXPECTED_URL_TEMPLATE_MAPPER = URI::getPath; + public static final int FOUND_STATUS_CODE = HttpStatus.FOUND.code(); public abstract Function>> getHttpAttributes(); @@ -56,6 +58,8 @@ public abstract class HttpClientTestOptions { public abstract BiFunction getExpectedClientSpanNameMapper(); + public abstract Function getExpectedUrlTemplateMapper(); + abstract HttpClientInstrumentationType getInstrumentationType(); public boolean isLowLevelInstrumentation() { @@ -95,6 +99,8 @@ public boolean isLowLevelInstrumentation() { public abstract Function getHttpProtocolVersion(); + public abstract boolean getHasUrlTemplate(); + @Nullable abstract SpanEndsAfterType getSpanEndsAfterType(); @@ -120,6 +126,7 @@ default Builder withDefaults() { .setClientSpanErrorMapper((uri, exception) -> exception) .setSingleConnectionFactory((host, port) -> null) .setExpectedClientSpanNameMapper(DEFAULT_EXPECTED_CLIENT_SPAN_NAME_MAPPER) + .setExpectedUrlTemplateMapper(DEFAULT_EXPECTED_URL_TEMPLATE_MAPPER) .setInstrumentationType(HttpClientInstrumentationType.HIGH_LEVEL) .setSpanEndsAfterType(SpanEndsAfterType.HEADERS) .setTestWithClientParent(true) @@ -137,6 +144,7 @@ default Builder withDefaults() { .setTestNonStandardHttpMethod(true) .setTestCaptureHttpHeaders(true) .setHasSendRequest(true) + .setHasUrlTemplate(false) .setHttpProtocolVersion(uri -> "1.1"); } @@ -152,6 +160,8 @@ default Builder withDefaults() { Builder setExpectedClientSpanNameMapper(BiFunction value); + Builder setExpectedUrlTemplateMapper(Function value); + Builder setInstrumentationType(HttpClientInstrumentationType instrumentationType); Builder setSpanEndsAfterType(SpanEndsAfterType spanEndsAfterType); @@ -184,6 +194,8 @@ default Builder withDefaults() { Builder setHasSendRequest(boolean value); + Builder setHasUrlTemplate(boolean value); + Builder setHttpProtocolVersion(Function value); @CanIgnoreReturnValue @@ -261,6 +273,11 @@ default Builder spanEndsAfterBody() { return setSpanEndsAfterType(SpanEndsAfterType.BODY); } + @CanIgnoreReturnValue + default Builder enableUrlTemplate() { + return setHasUrlTemplate(true); + } + HttpClientTestOptions build(); } 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 014d03acbf68..6add5e753b42 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 @@ -124,6 +124,12 @@ protected void configure(ServerBuilder sb) throws Exception { return writer; }) + .service( + "/hello/{name}", + (ctx, req) -> { + String name = ctx.pathParam("name"); + return HttpResponse.of("Hello, %s!", name != null ? name : "unknown"); + }) .decorator( (delegate, ctx, req) -> { for (String field : openTelemetry.getPropagators().getTextMapPropagator().fields()) { diff --git a/testing-common/src/main/java/io/opentelemetry/instrumentation/testing/recording/YamlFileMappingsSource.java b/testing-common/src/main/java/io/opentelemetry/instrumentation/testing/recording/YamlFileMappingsSource.java index 4090ff8a766a..6ee382d89b88 100644 --- a/testing-common/src/main/java/io/opentelemetry/instrumentation/testing/recording/YamlFileMappingsSource.java +++ b/testing-common/src/main/java/io/opentelemetry/instrumentation/testing/recording/YamlFileMappingsSource.java @@ -53,7 +53,7 @@ final class YamlFileMappingsSource implements MappingsSource { .enable(YAMLGenerator.Feature.ALWAYS_QUOTE_NUMBERS_AS_STRINGS) // For non-YAML, follow // https://github.com/wiremock/wiremock/blob/master/src/main/java/com/github/tomakehurst/wiremock/common/Json.java#L41 - .setSerializationInclusion(Include.NON_NULL) + .setDefaultPropertyInclusion(Include.NON_NULL) .configure(JsonNodeFeature.STRIP_TRAILING_BIGDECIMAL_ZEROES, false) .configure(JsonParser.Feature.ALLOW_COMMENTS, true) .configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true) diff --git a/testing/agent-exporter/src/main/java/io/opentelemetry/javaagent/testing/messaging/CapturedMessagingHeadersTestConfigSupplier.java b/testing/agent-exporter/src/main/java/io/opentelemetry/javaagent/testing/messaging/CapturedMessagingHeadersTestConfigSupplier.java index 231c9cca9168..9bb2b7813e29 100644 --- a/testing/agent-exporter/src/main/java/io/opentelemetry/javaagent/testing/messaging/CapturedMessagingHeadersTestConfigSupplier.java +++ b/testing/agent-exporter/src/main/java/io/opentelemetry/javaagent/testing/messaging/CapturedMessagingHeadersTestConfigSupplier.java @@ -25,10 +25,10 @@ private static Map getTestProperties() { Map testConfig = new HashMap<>(); testConfig.put( "otel.instrumentation.messaging.experimental.capture-headers", - // most tests use "test-message-header", "test_message_header" is used for JMS2+ because + // most tests use "Test-Message-Header". "Test_Message_Header" is used for JMS2+ because // '-' is not allowed in a JMS property name. JMS property name should be a valid java // identifier. - "test-message-header, test-message-int-header, test_message_header, test_message_int_header"); + "Test-Message-Header, Test-Message-Int-Header, Test_Message_Header, Test_Message_Int_Header"); return testConfig; } } diff --git a/testing/armeria-shaded-for-testing/build.gradle.kts b/testing/armeria-shaded-for-testing/build.gradle.kts index 373c60addec8..0debc7a805a1 100644 --- a/testing/armeria-shaded-for-testing/build.gradle.kts +++ b/testing/armeria-shaded-for-testing/build.gradle.kts @@ -4,7 +4,7 @@ plugins { } dependencies { - implementation("com.linecorp.armeria:armeria-junit5:1.33.1") + implementation("com.linecorp.armeria:armeria-junit5:1.33.2") implementation("com.google.errorprone:error_prone_annotations") } @@ -38,6 +38,10 @@ tasks { relocate("org.LatencyUtils", "io.opentelemetry.testing.internal.org.latencyutils") mergeServiceFiles() + // mergeServiceFiles requires that duplicate strategy is set to include + filesMatching("META-INF/services/**") { + duplicatesStrategy = DuplicatesStrategy.INCLUDE + } } val extractShadowJar by registering(Copy::class) { diff --git a/testing/wiremock-shaded-for-testing/build.gradle.kts b/testing/wiremock-shaded-for-testing/build.gradle.kts index 21a9512ed2c5..fa381efc41c2 100644 --- a/testing/wiremock-shaded-for-testing/build.gradle.kts +++ b/testing/wiremock-shaded-for-testing/build.gradle.kts @@ -8,7 +8,7 @@ dependencies { // class conflict reported in the failure. implementation("com.github.tomakehurst:wiremock-jre8:2.35.2") implementation("com.google.errorprone:error_prone_annotations") - implementation("com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.19.2") + implementation("com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.20.0") } tasks { @@ -36,6 +36,10 @@ tasks { relocate("org.yaml", "io.opentelemetry.testing.internal.yaml") mergeServiceFiles() + // mergeServiceFiles requires that duplicate strategy is set to include + filesMatching("META-INF/services/**") { + duplicatesStrategy = DuplicatesStrategy.INCLUDE + } } val extractShadowJar by registering(Copy::class) { diff --git a/version.gradle.kts b/version.gradle.kts index 3bbcba5538f2..d174793c0d5f 100644 --- a/version.gradle.kts +++ b/version.gradle.kts @@ -1,5 +1,5 @@ -val stableVersion = "2.19.0-SNAPSHOT" -val alphaVersion = "2.19.0-alpha-SNAPSHOT" +val stableVersion = "2.20.0" +val alphaVersion = "2.20.0-alpha" allprojects { if (findProperty("otel.stable") != "true") {