diff --git a/.github/component_owners.yml b/.github/component_owners.yml new file mode 100644 index 000000000000..95098193e8f8 --- /dev/null +++ b/.github/component_owners.yml @@ -0,0 +1,4 @@ +# this file is used by .github/workflows/assign-reviewers.yml +components: + "*.md": + - theletterf diff --git a/.github/dependabot-symlinks/README.md b/.github/dependabot-symlinks/README.md new file mode 100644 index 000000000000..52b0beb6c496 --- /dev/null +++ b/.github/dependabot-symlinks/README.md @@ -0,0 +1,6 @@ +This directory and the two symlinks in it are used by the +[dependabot configuration](../dependabot.yml), because we can't include the root directory +in the dependabot scanning since then it will pick up all of the old library versions that we +intentionally compile and test against. + +See https://github.com/dependabot/dependabot-core/issues/4364. diff --git a/.github/dependabot-symlinks/build.gradle.kts b/.github/dependabot-symlinks/build.gradle.kts new file mode 120000 index 000000000000..d9ef3a86cb28 --- /dev/null +++ b/.github/dependabot-symlinks/build.gradle.kts @@ -0,0 +1 @@ +../../build.gradle.kts \ No newline at end of file diff --git a/.github/dependabot-symlinks/settings.gradle.kts b/.github/dependabot-symlinks/settings.gradle.kts new file mode 120000 index 000000000000..976ab1cc3678 --- /dev/null +++ b/.github/dependabot-symlinks/settings.gradle.kts @@ -0,0 +1 @@ +../../settings.gradle.kts \ No newline at end of file diff --git a/.github/dependabot.yml b/.github/dependabot.yml index e332a1489e8b..c6f20944d27e 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -8,13 +8,52 @@ registries: updates: - package-ecosystem: "github-actions" directory: "/" + rebase-strategy: "disabled" schedule: interval: "daily" + + - package-ecosystem: "gradle" + # need to scope gradle dependency updates down in this repo because most of the instrumentations + # intentionally test against old library versions + directory: "/dependencyManagement" + rebase-strategy: "disabled" + schedule: + interval: "daily" + open-pull-requests-limit: 10 + + - package-ecosystem: "gradle" + # need to scope gradle dependency updates down in this repo because most of the instrumentations + # intentionally test against old library versions + directory: "/conventions" + rebase-strategy: "disabled" + schedule: + interval: "daily" + open-pull-requests-limit: 10 + + - package-ecosystem: "gradle" + # need to scope gradle dependency updates down in this repo because most of the instrumentations + # intentionally test against old library versions + directory: "/gradle-plugins" + rebase-strategy: "disabled" + schedule: + interval: "daily" + open-pull-requests-limit: 10 + + - package-ecosystem: "gradle" + # need to scope gradle dependency updates down in this repo because most of the instrumentations + # intentionally test against old library versions + directory: "/.github/dependabot-symlinks" + rebase-strategy: "disabled" + schedule: + interval: "daily" + open-pull-requests-limit: 10 + - package-ecosystem: "gradle" directory: "/" allow: - dependency-name: "com.gradle*" registries: - gradle-plugin-portal + rebase-strategy: "disabled" schedule: - interval: "weekly" + interval: "daily" diff --git a/.github/scripts/diff-suppression-keys-with-docs.sh b/.github/scripts/diff-suppression-keys-with-docs.sh old mode 100644 new mode 100755 diff --git a/.github/scripts/find-all-config-properties.sh b/.github/scripts/find-all-config-properties.sh new file mode 100755 index 000000000000..e492e1af50c5 --- /dev/null +++ b/.github/scripts/find-all-config-properties.sh @@ -0,0 +1,5 @@ +#!/bin/bash -e + +grep -Pohr --include '*.java' --exclude-dir=test \"otel.instrumentation.[^\"]+\" \ + | grep -v otel.instrumentation.internal \ + | sort -u diff --git a/.github/scripts/find-instrumentation-with-upper-version-limits.sh b/.github/scripts/find-instrumentation-with-upper-version-limits.sh new file mode 100644 index 000000000000..a1072992cb7b --- /dev/null +++ b/.github/scripts/find-instrumentation-with-upper-version-limits.sh @@ -0,0 +1,6 @@ +#!/bin/bash -e + +grep -r --include build.gradle.kts latestDepTestLibrary instrumentation \ + | grep -v :+\" \ + | grep -v "// see .* module" \ + | grep -v "// documented limitation" diff --git a/.github/scripts/generate-release-contributors.sh b/.github/scripts/generate-release-contributors.sh index 6ab2ccd31a18..99317bcddfcd 100755 --- a/.github/scripts/generate-release-contributors.sh +++ b/.github/scripts/generate-release-contributors.sh @@ -72,7 +72,7 @@ query($q: String!, $endCursor: String) { } } ' --jq '.data.search.edges.[].node.body' \ - | grep -oE "#[0-9]{4,}|$GITHUB_REPOSITORY/issues/[0-9]{4,}" \ + | grep -oE "#[0-9]{4,}$|#[0-9]{4,}[^0-9<]|$GITHUB_REPOSITORY/issues/[0-9]{4,}" \ | grep -oE "[0-9]{4,}" \ | xargs -I{} gh issue view {} --json 'author,url' --jq '[.author.login,.url]' \ | grep -v '/pull/' \ diff --git a/.github/scripts/get-version.sh b/.github/scripts/get-version.sh index 0609c38a58c7..c2780c78427c 100755 --- a/.github/scripts/get-version.sh +++ b/.github/scripts/get-version.sh @@ -1,3 +1,3 @@ #!/bin/bash -e -grep -Po "val stableVersion = \"\K[0-9]+.[0-9]+.0" version.gradle.kts +grep -Po "val stableVersion = \"\K[0-9]+.[0-9]+.[0-9]" version.gradle.kts diff --git a/.github/scripts/markdown-link-check-config.json b/.github/scripts/markdown-link-check-config.json index a458029d5d96..39048c89a9b0 100644 --- a/.github/scripts/markdown-link-check-config.json +++ b/.github/scripts/markdown-link-check-config.json @@ -1,6 +1,11 @@ { - "retryOn429": true, - "aliveStatusCodes": [ + "ignorePatterns" : [ + { + "pattern" : "^https://kotlinlang\\.org/docs/coroutines-overview\\.html$" + } + ], + "retryOn429" : true, + "aliveStatusCodes" : [ 200, 403 ] diff --git a/.github/scripts/update-sdk-version.sh b/.github/scripts/update-sdk-version.sh new file mode 100755 index 000000000000..994b6a806c45 --- /dev/null +++ b/.github/scripts/update-sdk-version.sh @@ -0,0 +1,13 @@ +#!/bin/bash -e + +version=$1 + +alpha_version=${version}-alpha + +sed -Ei "s/val otelVersion = \"[^\"]*\"/val otelVersion = \"$version\"/" dependencyManagement/build.gradle.kts + +sed -Ei "s/(opentelemetry *: )\"[^\"]*\"/\1\"$version\"/" examples/distro/build.gradle +sed -Ei "s/(opentelemetryAlpha *: )\"[^\"]*\"/\1\"$alpha_version\"/" examples/distro/build.gradle + +sed -Ei "s/(opentelemetry *: )\"[^\"]*\"/\1\"$version\"/" examples/extension/build.gradle +sed -Ei "s/(opentelemetryAlpha *: )\"[^\"]*\"/\1\"$alpha_version\"/" examples/extension/build.gradle diff --git a/.github/scripts/update-version.sh b/.github/scripts/update-version.sh index 66c6cac52a5a..e74dbd09c991 100755 --- a/.github/scripts/update-version.sh +++ b/.github/scripts/update-version.sh @@ -5,7 +5,7 @@ version=$1 if [[ $version == *-SNAPSHOT ]]; then alpha_version=${version//-SNAPSHOT/-alpha-SNAPSHOT} else - alpha_version=$version-alpha + alpha_version=${version}-alpha fi sed -Ei "s/val stableVersion = \"[^\"]*\"/val stableVersion = \"$version\"/" version.gradle.kts diff --git a/.github/scripts/use-cla-approved-github-bot.sh b/.github/scripts/use-cla-approved-github-bot.sh old mode 100644 new mode 100755 diff --git a/.github/workflows/assign-reviewers.yml b/.github/workflows/assign-reviewers.yml new file mode 100644 index 000000000000..267fd7c8af43 --- /dev/null +++ b/.github/workflows/assign-reviewers.yml @@ -0,0 +1,18 @@ +# assigns reviewers to pull requests in a similar way as CODEOWNERS, but doesn't require reviewers +# to have write access to the repository +# see .github/component_owners.yaml for the list of components and their owners +name: Assign reviewers + +on: + # pull_request_target is needed instead of just pull_request + # because repository write permission is needed to assign reviewers + pull_request_target: + +jobs: + assign-reviewers: + runs-on: ubuntu-latest + steps: + - uses: trask/component-owners@main + with: + # this repository is using this action to request doc review + assign-owners: false diff --git a/.github/workflows/backport.yml b/.github/workflows/backport.yml deleted file mode 100644 index 7cabc7ec6c6f..000000000000 --- a/.github/workflows/backport.yml +++ /dev/null @@ -1,43 +0,0 @@ -name: Backport -on: - workflow_dispatch: - inputs: - number: - description: "The pull request # to backport" - required: true - -jobs: - backport: - runs-on: ubuntu-latest - steps: - - run: | - if [[ ! $GITHUB_REF_NAME =~ ^release/v[0-9]+\.[0-9]+\.x$ ]]; then - echo this workflow should only be run against release branches - exit 1 - fi - - - uses: actions/checkout@v3 - with: - # history is needed to run git cherry-pick below - fetch-depth: 0 - - - name: Use CLA approved github bot - run: .github/scripts/use-cla-approved-github-bot.sh - - - name: Create pull request - env: - NUMBER: ${{ github.event.inputs.number }} - # not using secrets.GITHUB_TOKEN since pull requests from that token do not run workflows - GITHUB_TOKEN: ${{ secrets.BOT_TOKEN }} - run: | - commit=$(gh pr view $NUMBER --json mergeCommit --jq .mergeCommit.oid) - title=$(gh pr view $NUMBER --json title --jq .title) - - branch="backport-${NUMBER}-to-${GITHUB_REF_NAME//\//-}" - - git cherry-pick $commit - git push origin HEAD:$branch - gh pr create --title "[$GITHUB_REF_NAME] $title" \ - --body "Clean cherry-pick of #$NUMBER to the \`$GITHUB_REF_NAME\` branch." \ - --head $branch \ - --base $GITHUB_REF_NAME diff --git a/.github/workflows/build-common.yml b/.github/workflows/build-common.yml new file mode 100644 index 000000000000..0138783b6494 --- /dev/null +++ b/.github/workflows/build-common.yml @@ -0,0 +1,208 @@ +name: Reusable - Common + +on: + workflow_call: + inputs: + cache-read-only: + type: boolean + required: false + no-build-cache: + type: boolean + required: false + skip-windows-smoke-tests: + type: boolean + required: false + secrets: + GRADLE_ENTERPRISE_ACCESS_KEY: + required: false + GE_CACHE_USERNAME: + required: false + GE_CACHE_PASSWORD: + required: false + +jobs: + spotless: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - name: Set up JDK for running Gradle + uses: actions/setup-java@v3 + with: + distribution: temurin + java-version: 17 + + - name: Spotless + uses: gradle/gradle-build-action@v2 + env: + GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_ACCESS_KEY }} + GE_CACHE_USERNAME: ${{ secrets.GE_CACHE_USERNAME }} + GE_CACHE_PASSWORD: ${{ secrets.GE_CACHE_PASSWORD }} + with: + arguments: spotlessCheck ${{ inputs.no-build-cache && '--no-build-cache' || '' }} + cache-read-only: ${{ inputs.cache-read-only }} + # gradle enterprise is used for the build cache + gradle-home-cache-excludes: caches/build-cache-1 + + gradle-wrapper-validation: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - uses: gradle/wrapper-validation-action@v1.0.5 + + assemble: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - name: Set up JDK for running Gradle + uses: actions/setup-java@v3 + with: + distribution: temurin + java-version: 17 + + - name: Assemble + uses: gradle/gradle-build-action@v2 + env: + GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_ACCESS_KEY }} + GE_CACHE_USERNAME: ${{ secrets.GE_CACHE_USERNAME }} + GE_CACHE_PASSWORD: ${{ secrets.GE_CACHE_PASSWORD }} + with: + # javadoc task fails sporadically fetching https://docs.oracle.com/javase/8/docs/api/ + arguments: assemble -x javadoc ${{ inputs.no-build-cache && '--no-build-cache' || '' }} + cache-read-only: ${{ inputs.cache-read-only }} + # gradle enterprise is used for the build cache + gradle-home-cache-excludes: caches/build-cache-1 + + - name: Check for jApiCmp diffs + run: | + if git diff --quiet + then + echo "No diff detected." + else + echo "Diff detected - did you run './gradlew jApiCmp'?" + echo $(git diff --name-only) + echo $(git diff) + exit 1 + fi + + test: + runs-on: ubuntu-latest + strategy: + matrix: + test-java-version: + - 11 + vm: + - hotspot + exclude: + - test-java-version: 19 + vm: openj9 + fail-fast: false + steps: + - uses: actions/checkout@v3 + + - id: setup-test-java + name: Set up JDK ${{ matrix.test-java-version }}-${{ matrix.vm }} for running tests + uses: actions/setup-java@v3 + with: + # using zulu because new releases get published quickly + distribution: ${{ matrix.vm == 'hotspot' && 'zulu' || 'adopt-openj9'}} + java-version: ${{ matrix.test-java-version }} + + # vaadin tests use pnpm + - name: Cache pnpm modules + uses: actions/cache@v3 + with: + path: ~/.pnpm-store + key: ${{ runner.os }}-test-cache-pnpm-modules + + - name: Start deadlock detector + run: .github/scripts/deadlock-detector.sh + + - name: Test + env: + GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_ACCESS_KEY }} + GE_CACHE_USERNAME: ${{ secrets.GE_CACHE_USERNAME }} + GE_CACHE_PASSWORD: ${{ secrets.GE_CACHE_PASSWORD }} + uses: gradle/gradle-build-action@v2 + with: + # spotless is checked separately since it's a common source of failure + arguments: > + check + -x spotlessCheck + -PtestJavaVersion=${{ matrix.test-java-version }} + -PtestJavaVM=${{ matrix.vm }} + -Porg.gradle.java.installations.paths=${{ steps.setup-test-java.outputs.path }} + -Porg.gradle.java.installations.auto-download=false + ${{ inputs.no-build-cache && ' --no-build-cache' || '' }} + -x :instrumentation:finatra-2.9:javaagent:test + -x :instrumentation:hibernate:hibernate-4.0:javaagent:compileLatestDepTestJava + -x :instrumentation:hibernate:hibernate-4.0:javaagent:compileLatestDepTestGroovy + # only push cache for one matrix option since github action cache space is limited + cache-read-only: ${{ inputs.cache-read-only || matrix.test-java-version != 11 || matrix.vm != 'hotspot' }} + # gradle enterprise is used for the build cache + gradle-home-cache-excludes: caches/build-cache-1 + + - name: Upload deadlock detector artifacts if any + if: always() + uses: actions/upload-artifact@v3 + with: + name: deadlock-detector-test-${{ matrix.test-java-version }}-${{ matrix.vm }} + path: /tmp/deadlock-detector-* + if-no-files-found: ignore + + - name: Upload jvm crash dump files if any + if: always() + uses: actions/upload-artifact@v3 + with: + name: javacore-test-${{ matrix.test-java-version }} + path: | + **/hs_err_pid*.log + **/javacore.*.txt + **/Snap.*.trc + **/core.*.dmp + **/jitdump.*.dmp + if-no-files-found: ignore + + - name: Set up Gradle cache + uses: gradle/gradle-build-action@v2 + with: + # only push cache for one matrix option per OS since github action cache space is limited + cache-read-only: ${{ inputs.cache-read-only || matrix.smoke-test-suite != 'tomcat' }} + # gradle enterprise is used for the build cache + gradle-home-cache-excludes: caches/build-cache-1 + + - name: Build + env: + GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_ACCESS_KEY }} + GE_CACHE_USERNAME: ${{ secrets.GE_CACHE_USERNAME }} + GE_CACHE_PASSWORD: ${{ secrets.GE_CACHE_PASSWORD }} + # running suite "none" compiles everything needed by smoke tests without executing any tests + run: ./gradlew --no-daemon ${{ inputs.no-build-cache && ' --no-build-cache' || '' }} -x :instrumentation:finatra-2.9:javaagent:test + + - name: Test + env: + GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_ACCESS_KEY }} + GE_CACHE_USERNAME: ${{ secrets.GE_CACHE_USERNAME }} + GE_CACHE_PASSWORD: ${{ secrets.GE_CACHE_PASSWORD }} + run: ./gradlew ${{ inputs.no-build-cache && ' --no-build-cache' || '' }} -x :instrumentation:finatra-2.9:javaagent:test -x :instrumentation:hibernate:hibernate-4.0:javaagent:compileLatestDepTestJava + + + gradle-plugins: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - name: Set up JDK 11 for running Gradle + uses: actions/setup-java@v3 + with: + distribution: temurin + java-version: 11 + + - name: Build + uses: gradle/gradle-build-action@v2 + with: + arguments: build ${{ inputs.no-build-cache && '--no-build-cache' || '' }} + build-root-directory: gradle-plugins + cache-read-only: ${{ inputs.cache-read-only }} diff --git a/.github/workflows/build-daily-no-build-cache.yml b/.github/workflows/build-daily-no-build-cache.yml deleted file mode 100644 index 92ea69865903..000000000000 --- a/.github/workflows/build-daily-no-build-cache.yml +++ /dev/null @@ -1,63 +0,0 @@ -name: Build (daily, --no-build-cache) - -on: - schedule: - # strange schedule to reduce the risk of DDOS GitHub infra - - cron: "48 4 * * *" - workflow_dispatch: - -jobs: - assemble: - uses: ./.github/workflows/reusable-assemble.yml - with: - no-build-cache: true - secrets: - GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_ACCESS_KEY }} - - test: - uses: ./.github/workflows/reusable-test.yml - with: - no-build-cache: true - secrets: - GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_ACCESS_KEY }} - - test-latest-deps: - uses: ./.github/workflows/reusable-test-latest-deps.yml - with: - no-build-cache: true - secrets: - GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_ACCESS_KEY }} - - smoke-test: - uses: ./.github/workflows/reusable-smoke-test.yml - with: - no-build-cache: true - secrets: - GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_ACCESS_KEY }} - - # muzzle is not included here because it doesn't use gradle cache anyway and so is already covered - # by the normal daily build - - gradle-plugins: - uses: ./.github/workflows/reusable-gradle-plugins.yml - with: - no-build-cache: true - - examples: - uses: ./.github/workflows/reusable-examples.yml - with: - no-build-cache: true - - # markdown-link-check and misspell-check are not included here because they don't use gradle cache - # anyway and so are already covered by the normal daily build - - open-issue-on-failure: - needs: - - assemble - - test - - test-latest-deps - - smoke-test - - gradle-plugins - - examples - if: failure() - uses: ./.github/workflows/reusable-open-issue-on-failure.yml diff --git a/.github/workflows/build-daily.yml b/.github/workflows/build-daily.yml deleted file mode 100644 index c395a7f032dc..000000000000 --- a/.github/workflows/build-daily.yml +++ /dev/null @@ -1,65 +0,0 @@ -name: Build (daily) - -on: - schedule: - # strange schedule to reduce the risk of DDOS GitHub infra - - cron: "24 3 * * *" - workflow_dispatch: - -jobs: - assemble: - uses: ./.github/workflows/reusable-assemble.yml - secrets: - GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_ACCESS_KEY }} - GE_CACHE_USERNAME: ${{ secrets.GE_CACHE_USERNAME }} - GE_CACHE_PASSWORD: ${{ secrets.GE_CACHE_PASSWORD }} - - test: - uses: ./.github/workflows/reusable-test.yml - secrets: - GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_ACCESS_KEY }} - GE_CACHE_USERNAME: ${{ secrets.GE_CACHE_USERNAME }} - GE_CACHE_PASSWORD: ${{ secrets.GE_CACHE_PASSWORD }} - - test-latest-deps: - uses: ./.github/workflows/reusable-test-latest-deps.yml - secrets: - GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_ACCESS_KEY }} - GE_CACHE_USERNAME: ${{ secrets.GE_CACHE_USERNAME }} - GE_CACHE_PASSWORD: ${{ secrets.GE_CACHE_PASSWORD }} - - smoke-test: - uses: ./.github/workflows/reusable-smoke-test.yml - secrets: - GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_ACCESS_KEY }} - GE_CACHE_USERNAME: ${{ secrets.GE_CACHE_USERNAME }} - GE_CACHE_PASSWORD: ${{ secrets.GE_CACHE_PASSWORD }} - - muzzle: - uses: ./.github/workflows/reusable-muzzle.yml - - gradle-plugins: - uses: ./.github/workflows/reusable-gradle-plugins.yml - - examples: - uses: ./.github/workflows/reusable-examples.yml - - markdown-link-check: - uses: ./.github/workflows/reusable-markdown-link-check.yml - - misspell-check: - uses: ./.github/workflows/reusable-misspell-check.yml - - open-issue-on-failure: - needs: - - assemble - - test - - test-latest-deps - - smoke-test - - muzzle - - gradle-plugins - - examples - - markdown-link-check - - misspell-check - if: failure() - uses: ./.github/workflows/reusable-open-issue-on-failure.yml diff --git a/.github/workflows/build-pull-request.yml b/.github/workflows/build-pull-request.yml index 3ddd8023902b..b6040cf43a39 100644 --- a/.github/workflows/build-pull-request.yml +++ b/.github/workflows/build-pull-request.yml @@ -8,40 +8,11 @@ concurrency: cancel-in-progress: true jobs: - assemble: - uses: ./.github/workflows/reusable-assemble.yml - with: - cache-read-only: true - - test: - uses: ./.github/workflows/reusable-test.yml - with: - cache-read-only: true - - test-latest-deps: - # test-latest-deps is not included in the PR workflow by default - # because any time a new library version is released to maven central - # it can fail due to test code incompatibility with the new library version, - # or due to slight changes in emitted telemetry, which can be confusing for contributors - # (muzzle can also fail when a new library version is released to maven central - # but that happens much less often) - # - # the condition is on the steps below instead of here on the job, because skipping the job - # causes the job to show up as canceled in the GitHub UI which prevents the build section from - # collapsing when everything (else) is green - # - # and the name is updated when the steps below are skipped which makes what's happening clearer - # in the GitHub UI - uses: ./.github/workflows/reusable-test-latest-deps.yml - with: - skip: ${{ !contains(github.event.pull_request.labels.*.name, 'test latest deps') }} - cache-read-only: true - - smoke-test: - uses: ./.github/workflows/reusable-smoke-test.yml + common: + uses: ./.github/workflows/build-common.yml with: # windows smoke tests are slower, and it's rare for only the windows smoke tests to break - skip-windows: true + skip-windows-smoke-tests: ${{ !contains(github.event.pull_request.labels.*.name, 'test windows') }} cache-read-only: true muzzle: @@ -53,22 +24,6 @@ jobs: with: cache-read-only: true - gradle-plugins: - uses: ./.github/workflows/reusable-gradle-plugins.yml - with: - cache-read-only: true - - examples: - uses: ./.github/workflows/reusable-examples.yml - with: - cache-read-only: true - - # this is not a required check to avoid blocking pull requests if external links break - markdown-link-check: - # release branches are excluded to avoid unnecessary maintenance - if: ${{ !startsWith(github.ref_name, 'release/') }} - uses: ./.github/workflows/reusable-markdown-link-check.yml - # this is not a required check to avoid blocking pull requests if new misspellings are added # to the misspell dictionary misspell-check: @@ -77,21 +32,19 @@ jobs: uses: ./.github/workflows/reusable-misspell-check.yml required-status-check: + # test-latest-deps is not included in the required status checks + # because any time a new library version is released to maven central + # it can fail due to test code incompatibility with the new library version, + # or due to slight changes in emitted telemetry + # (muzzle can also fail when a new library version is released to maven central + # but that happens much less often) needs: - - assemble - - test - - smoke-test + - common - muzzle - - gradle-plugins - - examples runs-on: ubuntu-latest if: always() steps: - if: | - needs.assemble.result != 'success' || - needs.test.result != 'success' || - needs.smoke-test.result != 'success' || - (!startsWith(github.base_ref, 'release/') && needs.muzzle.result != 'success') || - needs.gradle-plugins.result != 'success' || - needs.examples.result != 'success' + needs.common.result != 'success' || + (!startsWith(github.base_ref, 'release/') && needs.muzzle.result != 'success') run: exit 1 diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index be0ad1ec657d..b1b929d0f62c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -8,33 +8,8 @@ on: workflow_dispatch: jobs: - assemble: - uses: ./.github/workflows/reusable-assemble.yml - secrets: - GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_ACCESS_KEY }} - GE_CACHE_USERNAME: ${{ secrets.GE_CACHE_USERNAME }} - GE_CACHE_PASSWORD: ${{ secrets.GE_CACHE_PASSWORD }} - - test: - uses: ./.github/workflows/reusable-test.yml - secrets: - GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_ACCESS_KEY }} - GE_CACHE_USERNAME: ${{ secrets.GE_CACHE_USERNAME }} - GE_CACHE_PASSWORD: ${{ secrets.GE_CACHE_PASSWORD }} - - test-latest-deps: - # release branches are excluded - # because any time a new library version is released to maven central it can fail - # which requires unnecessary release branch maintenance, especially for patches - if: ${{ !startsWith(github.ref_name, 'release/') }} - uses: ./.github/workflows/reusable-test-latest-deps.yml - secrets: - GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_ACCESS_KEY }} - GE_CACHE_USERNAME: ${{ secrets.GE_CACHE_USERNAME }} - GE_CACHE_PASSWORD: ${{ secrets.GE_CACHE_PASSWORD }} - - smoke-test: - uses: ./.github/workflows/reusable-smoke-test.yml + common: + uses: ./.github/workflows/build-common.yml secrets: GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_ACCESS_KEY }} GE_CACHE_USERNAME: ${{ secrets.GE_CACHE_USERNAME }} @@ -50,66 +25,8 @@ jobs: gradle-plugins: uses: ./.github/workflows/reusable-gradle-plugins.yml - examples: - uses: ./.github/workflows/reusable-examples.yml - - markdown-link-check: - # release branches are excluded to avoid unnecessary maintenance if external links break - # (and also because the README.md javaagent download link has to be updated on release branches - # before the release download has been published) - if: ${{ !startsWith(github.ref_name, 'release/') }} - uses: ./.github/workflows/reusable-markdown-link-check.yml - misspell-check: # release branches are excluded to avoid unnecessary maintenance if new misspellings are added # to the misspell dictionary if: ${{ !startsWith(github.ref_name, 'release/') }} uses: ./.github/workflows/reusable-misspell-check.yml - - publish-snapshots: - needs: - # intentionally not blocking snapshot publishing on test-latest-deps, muzzle, - # markdown-link-check, or misspell-check - - assemble - - test - - smoke-test - - gradle-plugins - - examples - runs-on: ubuntu-latest - # 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@v3 - - - name: Set up JDK for running Gradle - uses: actions/setup-java@v3 - with: - distribution: temurin - java-version: 17 - - - name: Build and publish artifact snapshots - env: - GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_ACCESS_KEY }} - SONATYPE_USER: ${{ secrets.SONATYPE_USER }} - SONATYPE_KEY: ${{ secrets.SONATYPE_KEY }} - GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }} - GPG_PASSWORD: ${{ secrets.GPG_PASSWORD }} - uses: gradle/gradle-build-action@v2 - with: - arguments: assemble publishToSonatype - # gradle enterprise is used for the build cache - gradle-home-cache-excludes: caches/build-cache-1 - - - name: Build and publish gradle plugin snapshots - env: - GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_ACCESS_KEY }} - SONATYPE_USER: ${{ secrets.SONATYPE_USER }} - SONATYPE_KEY: ${{ secrets.SONATYPE_KEY }} - GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }} - GPG_PASSWORD: ${{ secrets.GPG_PASSWORD }} - uses: gradle/gradle-build-action@v2 - with: - build-root-directory: gradle-plugins - arguments: build publishToSonatype - # gradle enterprise is used for the build cache - gradle-home-cache-excludes: caches/build-cache-1 diff --git a/.github/workflows/codeql-daily.yml b/.github/workflows/codeql-daily.yml deleted file mode 100644 index f3d79ac2e73a..000000000000 --- a/.github/workflows/codeql-daily.yml +++ /dev/null @@ -1,41 +0,0 @@ -name: CodeQL (daily) - -on: - schedule: - - cron: '30 1 * * *' - workflow_dispatch: - -jobs: - analyze: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v3 - - - name: Set up Java 17 - uses: actions/setup-java@v3 - with: - distribution: temurin - java-version: 17 - - - name: Initialize CodeQL - uses: github/codeql-action/init@v2 - with: - languages: java - - - uses: gradle/gradle-build-action@v2 - env: - # see https://github.com/github/codeql-action/issues/972 - JAVA_TOOL_OPTIONS: "--add-opens=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED" - with: - # skipping build cache is needed so that all modules will be analyzed - arguments: assemble --no-build-cache - - - name: Perform CodeQL analysis - uses: github/codeql-action/analyze@v2 - - open-issue-on-failure: - # open an issue on failure because it can be easy to miss CI failure notifications - needs: analyze - if: failure() - uses: ./.github/workflows/reusable-open-issue-on-failure.yml diff --git a/.github/workflows/gradle-wrapper-validation.yml b/.github/workflows/gradle-wrapper-validation.yml deleted file mode 100644 index e407efce70ec..000000000000 --- a/.github/workflows/gradle-wrapper-validation.yml +++ /dev/null @@ -1,16 +0,0 @@ -name: Gradle wrapper validation -on: - pull_request: - paths: - - '**/gradle/wrapper/**' - push: - paths: - - '**/gradle/wrapper/**' - -jobs: - validation: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - - uses: gradle/wrapper-validation-action@v1.0.4 diff --git a/.github/workflows/overhead-benchmark-daily.yml b/.github/workflows/overhead-benchmark-daily.yml deleted file mode 100644 index 18b3bcf88487..000000000000 --- a/.github/workflows/overhead-benchmark-daily.yml +++ /dev/null @@ -1,51 +0,0 @@ -name: Overhead benchmark (daily) -on: - schedule: - # 5am GMT - - cron: '0 5 * * *' - workflow_dispatch: - -jobs: - run-overhead-tests: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - - uses: actions/checkout@v3 - with: - ref: gh-pages - path: gh-pages - - - name: Copy results from gh-pages branch - run: | - rsync -avv gh-pages/benchmark-overhead/results/ benchmark-overhead/results/ - - - name: Run tests - uses: gradle/gradle-build-action@v2 - with: - arguments: test - build-root-directory: benchmark-overhead - - - name: Inspect the results dir - working-directory: benchmark-overhead - run: ls -lR results - - - name: Copy results back to gh-pages branch - run: rsync -avv benchmark-overhead/results/ gh-pages/benchmark-overhead/results/ && rm -rf benchmark-overhead/results - - - name: Commit updated results - uses: EndBug/add-and-commit@v9 - with: - add: 'benchmark-overhead/results' - cwd: './gh-pages' - branch: 'gh-pages' - message: 'update test result data' - author_name: opentelemetrybot - author_email: 107717825+opentelemetrybot@users.noreply.github.com - committer_name: opentelemetrybot - committer_email: 107717825+opentelemetrybot@users.noreply.github.com - - open-issue-on-failure: - needs: run-overhead-tests - if: failure() - uses: ./.github/workflows/reusable-open-issue-on-failure.yml diff --git a/.github/workflows/owasp-dependency-check-daily.yml b/.github/workflows/owasp-dependency-check-daily.yml new file mode 100644 index 000000000000..ef87eb271977 --- /dev/null +++ b/.github/workflows/owasp-dependency-check-daily.yml @@ -0,0 +1,31 @@ +# the benefit of this over dependabot is that this also analyzes transitive dependencies +# while dependabot (at least currently) only analyzes top-level dependencies +name: OWASP dependency check (daily) + +on: + schedule: + - cron: '30 1 * * *' + workflow_dispatch: + +jobs: + analyze: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + + - name: Set up Java 11 + uses: actions/setup-java@v3 + with: + distribution: temurin + java-version: 11 + + - uses: gradle/gradle-build-action@v2 + with: + arguments: ":javaagent:dependencyCheckAnalyze" + + - name: Upload report + if: always() + uses: actions/upload-artifact@v3 + with: + path: javaagent/build/reports diff --git a/.github/workflows/pr-smoke-test-fake-backend-images.yml b/.github/workflows/pr-smoke-test-fake-backend-images.yml deleted file mode 100644 index a8d4b26a1b21..000000000000 --- a/.github/workflows/pr-smoke-test-fake-backend-images.yml +++ /dev/null @@ -1,50 +0,0 @@ -name: PR build fake backend images for smoke tests - -on: - pull_request: - paths: - - 'smoke-tests/images/fake-backend/**' - - '.github/workflows/pr-smoke-test-fake-backend-images.yml' - -jobs: - buildLinux: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - - name: Set up JDK for running Gradle - uses: actions/setup-java@v3 - with: - distribution: temurin - java-version: 17 - - - name: Build Docker image - uses: gradle/gradle-build-action@v2 - with: - arguments: ":smoke-tests:images:fake-backend:jibDockerBuild -Djib.httpTimeout=120000 -Djib.console=plain" - cache-read-only: true - # gradle enterprise is used for the build cache - gradle-home-cache-excludes: caches/build-cache-1 - - buildWindows: - runs-on: windows-latest - defaults: - run: - shell: bash - steps: - - name: Support long paths - run: git config --system core.longpaths true - - - uses: actions/checkout@v3 - - - name: Set up JDK for running Gradle - uses: actions/setup-java@v3 - with: - distribution: temurin - java-version: 17 - - - name: Build Docker image - uses: gradle/gradle-build-action@v2 - with: - arguments: ":smoke-tests:images:fake-backend:windowsBackendImageBuild" - cache-read-only: true diff --git a/.github/workflows/pr-smoke-test-grpc-images.yml b/.github/workflows/pr-smoke-test-grpc-images.yml deleted file mode 100644 index 5a22dcaef144..000000000000 --- a/.github/workflows/pr-smoke-test-grpc-images.yml +++ /dev/null @@ -1,14 +0,0 @@ -name: PR build gRPC images for smoke tests - -on: - pull_request: - paths: - - 'smoke-tests/images/grpc/**' - - '.github/workflows/pr-smoke-test-grpc-images.yml' - -jobs: - build: - uses: ./.github/workflows/reusable-smoke-test-images.yml - with: - project: ":smoke-tests:images:grpc" - cache-read-only: true diff --git a/.github/workflows/pr-smoke-test-play-images.yml b/.github/workflows/pr-smoke-test-play-images.yml deleted file mode 100644 index 6e8b2ff37075..000000000000 --- a/.github/workflows/pr-smoke-test-play-images.yml +++ /dev/null @@ -1,39 +0,0 @@ -name: PR build Play images for smoke tests - -on: - pull_request: - paths: - - 'smoke-tests/images/play/**' - - '.github/workflows/pr-smoke-test-play-images.yml' - -jobs: - build: - uses: ./.github/workflows/reusable-smoke-test-images.yml - with: - project: ":smoke-tests:images:play" - cache-read-only: true - # Play doesn't support Java 16+ yet - # https://github.com/playframework/playframework/pull/10819 - skip-java-17: true - skip-java-18: true - - build-java-15: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - - name: Set up JDK for running Gradle - uses: actions/setup-java@v3 - with: - distribution: temurin - java-version: 17 - - - name: Set up Gradle cache - uses: gradle/gradle-build-action@v2 - with: - cache-read-only: true - - # Play doesn't support Java 16 (or 17) yet - # https://github.com/playframework/playframework/pull/10819 - - name: Build Java 15 Docker image - run: ./gradlew :smoke-tests:images:play:jibDockerBuild -PtargetJDK=15 -Djib.httpTimeout=120000 -Djib.console=plain diff --git a/.github/workflows/pr-smoke-test-quarkus-images.yml b/.github/workflows/pr-smoke-test-quarkus-images.yml deleted file mode 100644 index c064bc721586..000000000000 --- a/.github/workflows/pr-smoke-test-quarkus-images.yml +++ /dev/null @@ -1,16 +0,0 @@ -name: PR build Quarkus images for smoke tests - -on: - pull_request: - paths: - - 'smoke-tests/images/quarkus/**' - - '.github/workflows/pr-smoke-test-quarkus-images.yml' - -jobs: - build: - uses: ./.github/workflows/reusable-smoke-test-images.yml - with: - project: ":smoke-tests:images:quarkus" - cache-read-only: true - # Quarkus 2.0+ does not support Java 8 - skip-java-8: true diff --git a/.github/workflows/pr-smoke-test-servlet-images.yml b/.github/workflows/pr-smoke-test-servlet-images.yml deleted file mode 100644 index 3b586a6ba93c..000000000000 --- a/.github/workflows/pr-smoke-test-servlet-images.yml +++ /dev/null @@ -1,53 +0,0 @@ -name: PR build Servlet images for smoke tests - -on: - pull_request: - paths: - - 'smoke-tests/images/servlet/**' - - '.github/workflows/pr-smoke-test-servlet-images.yml' - -jobs: - build: - runs-on: ${{ matrix.os }} - strategy: - matrix: - os: - - windows-2019 - - ubuntu-latest - smoke-test-server: - - jetty - - liberty - - payara - - tomcat - - tomee - - websphere - - wildfly - exclude: - - os: windows-2019 - smoke-test-server: websphere - fail-fast: false - steps: - - name: Support long paths - run: git config --system core.longpaths true - if: matrix.os == 'windows-2019' - - - uses: actions/checkout@v3 - - - name: Set up JDK for running Gradle - uses: actions/setup-java@v3 - with: - distribution: temurin - java-version: 17 - - - name: Set up Gradle cache - uses: gradle/gradle-build-action@v2 - with: - cache-read-only: true - - - name: Build Linux docker images - if: matrix.os != 'windows-2019' - run: ./gradlew :smoke-tests:images:servlet:buildLinuxTestImages -PsmokeTestServer=${{ matrix.smoke-test-server }} - - - name: Build Windows docker images - if: matrix.os == 'windows-2019' - run: ./gradlew :smoke-tests:images:servlet:buildWindowsTestImages -PsmokeTestServer=${{ matrix.smoke-test-server }} diff --git a/.github/workflows/pr-smoke-test-spring-boot-images.yml b/.github/workflows/pr-smoke-test-spring-boot-images.yml deleted file mode 100644 index 78362a5e82ef..000000000000 --- a/.github/workflows/pr-smoke-test-spring-boot-images.yml +++ /dev/null @@ -1,14 +0,0 @@ -name: PR build Spring Boot images for smoke tests - -on: - pull_request: - paths: - - 'smoke-tests/images/spring-boot/**' - - '.github/workflows/pr-smoke-test-spring-boot-images.yml' - -jobs: - build: - uses: ./.github/workflows/reusable-smoke-test-images.yml - with: - project: ":smoke-tests:images:spring-boot" - cache-read-only: true diff --git a/.github/workflows/prepare-patch-release.yml b/.github/workflows/prepare-patch-release.yml deleted file mode 100644 index 1c7e8174f3f4..000000000000 --- a/.github/workflows/prepare-patch-release.yml +++ /dev/null @@ -1,62 +0,0 @@ -name: Prepare patch release -on: - workflow_dispatch: - -jobs: - prepare-patch-release: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - - run: | - if [[ ! $GITHUB_REF_NAME =~ ^release/v[0-9]+\.[0-9]+\.x$ ]]; then - echo this workflow should only be run against release branches - exit 1 - fi - - if ! grep --quiet "^## Unreleased$" CHANGELOG.md; then - echo the change log is missing an \"Unreleased\" section - exit 1 - fi - - - name: Set environment variables - run: | - version=$(.github/scripts/get-version.sh) - if [[ $version =~ ^([0-9]+\.[0-9]+)\.([0-9]+)$ ]]; then - major_minor="${BASH_REMATCH[1]}" - patch="${BASH_REMATCH[2]}" - else - echo "unexpected version: $version" - exit 1 - fi - echo "VERSION=$major_minor.$((patch + 1))" >> $GITHUB_ENV - - - name: Update version - run: .github/scripts/update-version.sh $VERSION - - - name: Update download link version - run: | - sed -Ei "s,https://github\.com/open-telemetry/opentelemetry-java-instrumentation/releases/download/v[0-9]+\.[0-9]+\.[0-9]+/,https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/download/v$VERSION/," README.md - - - name: Update the change log with the approximate release date - run: | - date=$(date "+%Y-%m-%d") - sed -Ei "s/^## Unreleased$/## Version $VERSION ($date)/" CHANGELOG.md - - - name: Use CLA approved github bot - run: .github/scripts/use-cla-approved-github-bot.sh - - - name: Create pull request - env: - # not using secrets.GITHUB_TOKEN since pull requests from that token do not run workflows - GITHUB_TOKEN: ${{ secrets.BOT_TOKEN }} - run: | - message="Prepare release $VERSION" - branch="prepare-release-${VERSION}" - - git commit -a -m "$message" - git push origin HEAD:$branch - gh pr create --title "[$GITHUB_REF_NAME] $message" \ - --body "$message." \ - --head $branch \ - --base $GITHUB_REF_NAME diff --git a/.github/workflows/prepare-release-branch.yml b/.github/workflows/prepare-release-branch.yml deleted file mode 100644 index ef2fb76a6810..000000000000 --- a/.github/workflows/prepare-release-branch.yml +++ /dev/null @@ -1,121 +0,0 @@ -name: Prepare release branch -on: - workflow_dispatch: - -jobs: - prereqs: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - - run: | - if [[ $GITHUB_REF_NAME != main ]]; then - echo this workflow should only be run against main - exit 1 - fi - - if ! grep --quiet "^## Unreleased$" CHANGELOG.md; then - echo the change log is missing an \"Unreleased\" section - exit 1 - fi - - create-pull-request-against-release-branch: - runs-on: ubuntu-latest - needs: prereqs - steps: - - uses: actions/checkout@v3 - - - name: Create release branch - run: | - version=$(.github/scripts/get-version.sh) - version=${version//-SNAPSHOT/} - if [[ $version =~ ^([0-9]+)\.([0-9]+)\.0$ ]]; then - release_branch_name=$(echo $version | sed -E 's/([0-9]+)\.([0-9]+)\.0/release\/v\1.\2.x/') - else - echo "unexpected version: $version" - exit 1 - fi - - git push origin HEAD:$release_branch_name - - echo "VERSION=$version" >> $GITHUB_ENV - echo "RELEASE_BRANCH_NAME=$release_branch_name" >> $GITHUB_ENV - - - name: Update version - run: .github/scripts/update-version.sh $VERSION - - - name: Update download link version - run: | - sed -Ei "s,https://github\.com/open-telemetry/opentelemetry-java-instrumentation/releases/latest/download/,https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/download/v$VERSION/," README.md - - - name: Update the change log with the approximate release date - run: | - date=$(date "+%Y-%m-%d") - sed -Ei "s/^## Unreleased$/## Version $VERSION ($date)/" CHANGELOG.md - - - name: Use CLA approved github bot - run: .github/scripts/use-cla-approved-github-bot.sh - - - name: Create pull request against the release branch - env: - # not using secrets.GITHUB_TOKEN since pull requests from that token do not run workflows - GITHUB_TOKEN: ${{ secrets.BOT_TOKEN }} - run: | - message="Prepare release $VERSION" - branch="prepare-release-${VERSION}" - - git commit -a -m "$message" - git push origin HEAD:$branch - gh pr create --title "[$RELEASE_BRANCH_NAME] $message" \ - --body "$message." \ - --head $branch \ - --base $RELEASE_BRANCH_NAME - - create-pull-request-against-main: - runs-on: ubuntu-latest - needs: prereqs - steps: - - uses: actions/checkout@v3 - - - name: Set environment variables - run: | - version=$(.github/scripts/get-version.sh) - version=${version//-SNAPSHOT/} - if [[ $version =~ ^([0-9]+)\.([0-9]+)\.0$ ]]; then - major="${BASH_REMATCH[1]}" - minor="${BASH_REMATCH[2]}" - next_version="$major.$((minor + 1)).0" - else - echo "unexpected version: $version" - exit 1 - fi - echo "NEXT_VERSION=${next_version}-SNAPSHOT" >> $GITHUB_ENV - echo "VERSION=$version" >> $GITHUB_ENV - - - name: Update version - run: .github/scripts/update-version.sh $NEXT_VERSION - - - name: Update the change log on main - run: | - # the actual release date on main will be updated at the end of the release workflow - date=$(date "+%Y-%m-%d") - sed -Ei "s/^## Unreleased$/## Unreleased\n\n## Version $VERSION ($date)/" CHANGELOG.md - - - name: Use CLA approved github bot - run: .github/scripts/use-cla-approved-github-bot.sh - - - name: Create pull request against main - env: - # not using secrets.GITHUB_TOKEN since pull requests from that token do not run workflows - GITHUB_TOKEN: ${{ secrets.BOT_TOKEN }} - run: | - message="Update version to $NEXT_VERSION" - body="Update version to \`$NEXT_VERSION\`." - branch="update-version-to-${NEXT_VERSION}" - - git commit -a -m "$message" - git push origin HEAD:$branch - gh pr create --title "$message" \ - --body "$body" \ - --head $branch \ - --base main diff --git a/.github/workflows/publish-petclinic-benchmark-image.yml b/.github/workflows/publish-petclinic-benchmark-image.yml deleted file mode 100644 index 2f262af66442..000000000000 --- a/.github/workflows/publish-petclinic-benchmark-image.yml +++ /dev/null @@ -1,36 +0,0 @@ -name: Publish PetClinic benchmark image -on: - push: - paths: - - 'benchmark-overhead/Dockerfile-petclinic-base' - branches: - - main - workflow_dispatch: - -jobs: - publish: - runs-on: ubuntu-latest - permissions: - packages: write - contents: read - steps: - - uses: actions/checkout@v3 - - - uses: docker/setup-buildx-action@v2 - - - name: Login to GitHub container registry - uses: docker/login-action@v2 - with: - registry: ghcr.io - username: ${{ github.repository_owner }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Create timestamp for docker image tag - run: echo "TS=$(date +'%Y%m%d%H%M%S')" >> $GITHUB_ENV - - - name: Push to GitHub packages - uses: docker/build-push-action@v3 - with: - push: true - file: benchmark-overhead/Dockerfile-petclinic-base - tags: ghcr.io/open-telemetry/opentelemetry-java-instrumentation/petclinic-rest-base:${{ env.TS }} diff --git a/.github/workflows/publish-smoke-test-fake-backend-images.yml b/.github/workflows/publish-smoke-test-fake-backend-images.yml deleted file mode 100644 index c0679c7d8f8a..000000000000 --- a/.github/workflows/publish-smoke-test-fake-backend-images.yml +++ /dev/null @@ -1,76 +0,0 @@ -name: Publish fake backend images for smoke tests - -on: - push: - paths: - - 'smoke-tests/images/fake-backend/**' - - '.github/workflows/publish-smoke-test-fake-backend-images.yml' - branches: - - main - workflow_dispatch: - -jobs: - publishLinux: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - - name: Set up JDK for running Gradle - uses: actions/setup-java@v3 - with: - distribution: temurin - java-version: 17 - - - name: Login to GitHub package registry - uses: docker/login-action@v2 - with: - registry: ghcr.io - username: ${{ github.repository_owner }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Set tag - run: echo "TAG=$(date '+%Y%m%d').$GITHUB_RUN_ID" >> $GITHUB_ENV - - - name: Build Docker image - uses: gradle/gradle-build-action@v2 - with: - arguments: ":smoke-tests:images:fake-backend:jib -Djib.httpTimeout=120000 -Djib.console=plain -PextraTag=${{ env.TAG }}" - - publishWindows: - runs-on: windows-latest - defaults: - run: - shell: bash - steps: - - name: Support long paths - run: git config --system core.longpaths true - - - uses: actions/checkout@v3 - - - name: Set up JDK 11 for running Gradle - uses: actions/setup-java@v3 - with: - distribution: temurin - java-version: 11 - - - name: Login to GitHub package registry - uses: azure/docker-login@v1 - with: - login-server: ghcr.io - username: ${{ github.repository_owner }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Set tag - run: echo "TAG=$(date '+%Y%m%d').$GITHUB_RUN_ID" >> $GITHUB_ENV - - - name: Build Docker image - uses: gradle/gradle-build-action@v2 - with: - arguments: ":smoke-tests:images:fake-backend:dockerPush -PextraTag=${{ env.TAG }}" - - open-issue-on-failure: - needs: - - publishLinux - - publishWindows - if: failure() - uses: ./.github/workflows/reusable-open-issue-on-failure.yml diff --git a/.github/workflows/publish-smoke-test-grpc-images.yml b/.github/workflows/publish-smoke-test-grpc-images.yml deleted file mode 100644 index 5faff4ea1a93..000000000000 --- a/.github/workflows/publish-smoke-test-grpc-images.yml +++ /dev/null @@ -1,22 +0,0 @@ -name: Publish gRPC images for smoke tests - -on: - push: - paths: - - 'smoke-tests/images/grpc/**' - - '.github/workflows/publish-smoke-test-grpc-images.yml' - branches: - - main - workflow_dispatch: - -jobs: - publish: - uses: ./.github/workflows/reusable-smoke-test-images.yml - with: - project: ":smoke-tests:images:grpc" - publish: true - - open-issue-on-failure: - needs: publish - if: failure() - uses: ./.github/workflows/reusable-open-issue-on-failure.yml diff --git a/.github/workflows/publish-smoke-test-play-images.yml b/.github/workflows/publish-smoke-test-play-images.yml deleted file mode 100644 index 56e6ba59f72e..000000000000 --- a/.github/workflows/publish-smoke-test-play-images.yml +++ /dev/null @@ -1,57 +0,0 @@ -name: Publish Play images for smoke tests - -on: - push: - paths: - - 'smoke-tests/images/play/**' - - '.github/workflows/publish-smoke-test-play-images.yml' - branches: - - main - workflow_dispatch: - -jobs: - publish: - uses: ./.github/workflows/reusable-smoke-test-images.yml - with: - project: ":smoke-tests:images:play" - publish: true - # Play doesn't support Java 16+ yet - # https://github.com/playframework/playframework/pull/10819 - skip-java-17: true - skip-java-18: true - - publish-java-15: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - - name: Set up JDK for running Gradle - uses: actions/setup-java@v3 - with: - distribution: temurin - java-version: 17 - - - name: Login to GitHub package registry - uses: docker/login-action@v2 - with: - registry: ghcr.io - username: ${{ github.repository_owner }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Set tag - run: echo "TAG=$(date '+%Y%m%d').$GITHUB_RUN_ID" >> $GITHUB_ENV - - - name: Set up Gradle cache - uses: gradle/gradle-build-action@v2 - - # Play doesn't support Java 16 (or 17) yet - # https://github.com/playframework/playframework/pull/10819 - - name: Build Java 15 Docker image - run: ./gradlew :smoke-tests:images:play:jib -PtargetJDK=15 -Djib.httpTimeout=120000 -Djib.console=plain -Ptag=${{ env.TAG }} - - open-issue-on-failure: - needs: - - publish - - publish-java-15 - if: failure() - uses: ./.github/workflows/reusable-open-issue-on-failure.yml diff --git a/.github/workflows/publish-smoke-test-quarkus-images.yml b/.github/workflows/publish-smoke-test-quarkus-images.yml deleted file mode 100644 index b66564e6cea9..000000000000 --- a/.github/workflows/publish-smoke-test-quarkus-images.yml +++ /dev/null @@ -1,24 +0,0 @@ -name: Publish Quarkus images for smoke tests - -on: - push: - paths: - - 'smoke-tests/images/quarkus/**' - - '.github/workflows/publish-smoke-test-quarkus-images.yml' - branches: - - main - workflow_dispatch: - -jobs: - publish: - uses: ./.github/workflows/reusable-smoke-test-images.yml - with: - project: ":smoke-tests:images:quarkus" - publish: true - # Quarkus 2.0+ does not support Java 8 - skip-java-8: true - - open-issue-on-failure: - needs: publish - if: failure() - uses: ./.github/workflows/reusable-open-issue-on-failure.yml diff --git a/.github/workflows/publish-smoke-test-servlet-images.yml b/.github/workflows/publish-smoke-test-servlet-images.yml deleted file mode 100644 index eedb60cc154c..000000000000 --- a/.github/workflows/publish-smoke-test-servlet-images.yml +++ /dev/null @@ -1,75 +0,0 @@ -name: Publish Servlet images for smoke tests - -on: - push: - paths: - - 'smoke-tests/images/servlet/**' - - '.github/workflows/publish-smoke-test-servlet-images.yml' - branches: - - main - workflow_dispatch: - -jobs: - publish: - runs-on: ${{ matrix.os }} - defaults: - run: - shell: bash # this is needed for TAG construction below on Windows - strategy: - matrix: - os: - - windows-2019 - - ubuntu-latest - smoke-test-server: - - jetty - - liberty - - payara - - tomcat - - tomee - - websphere - - wildfly - exclude: - - os: windows-2019 - smoke-test-server: websphere - fail-fast: false - steps: - - name: Support long paths - run: git config --system core.longpaths true - if: matrix.os == 'windows-2019' - - - uses: actions/checkout@v3 - - - name: Set up JDK for running Gradle - uses: actions/setup-java@v3 - with: - distribution: temurin - java-version: 17 - - - name: Login to GitHub package registry - uses: docker/login-action@v2 - with: - registry: ghcr.io - username: ${{ github.repository_owner }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Set tag - run: echo "TAG=$(date '+%Y%m%d').$GITHUB_RUN_ID" >> $GITHUB_ENV - - - name: Set up Gradle cache - uses: gradle/gradle-build-action@v2 - with: - # only push cache for one matrix option per OS since github action cache space is limited - cache-read-only: ${{ matrix.smoke-test-suite != 'tomcat' }} - - - name: Build Linux docker images - if: matrix.os != 'windows-2019' - run: ./gradlew :smoke-tests:images:servlet:buildLinuxTestImages pushMatrix -PextraTag=${{ env.TAG }} -PsmokeTestServer=${{ matrix.smoke-test-server }} - - - name: Build Windows docker images - if: matrix.os == 'windows-2019' - run: ./gradlew :smoke-tests:images:servlet:buildWindowsTestImages pushMatrix -PextraTag=${{ env.TAG }} -PsmokeTestServer=${{ matrix.smoke-test-server }} - - open-issue-on-failure: - needs: publish - if: failure() - uses: ./.github/workflows/reusable-open-issue-on-failure.yml diff --git a/.github/workflows/publish-smoke-test-spring-boot-images.yml b/.github/workflows/publish-smoke-test-spring-boot-images.yml deleted file mode 100644 index a092b354ed9b..000000000000 --- a/.github/workflows/publish-smoke-test-spring-boot-images.yml +++ /dev/null @@ -1,22 +0,0 @@ -name: Publish Spring Boot images for smoke tests - -on: - push: - paths: - - 'smoke-tests/images/spring-boot/**' - - '.github/workflows/publish-smoke-test-spring-boot-images.yml' - branches: - - main - workflow_dispatch: - -jobs: - publish: - uses: ./.github/workflows/reusable-smoke-test-images.yml - with: - project: ":smoke-tests:images:spring-boot" - publish: true - - open-issue-on-failure: - needs: publish - if: failure() - uses: ./.github/workflows/reusable-open-issue-on-failure.yml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 72927225915f..4fa187633a4c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -3,46 +3,39 @@ on: workflow_dispatch: jobs: - assemble: - uses: ./.github/workflows/reusable-assemble.yml - test: - uses: ./.github/workflows/reusable-test.yml + test-java8: + uses: ./.github/workflows/reusable-test-8.yml - # test-latest-deps is intentionally not included in the release workflows - # because any time a new library version is released to maven central - # it can fail due to test code incompatibility with the new library version, - # or due to slight changes in emitted telemetry + test-java11: + uses: ./.github/workflows/reusable-test-11.yml - smoke-test: - uses: ./.github/workflows/reusable-smoke-test.yml + # test-latest-deps is intentionally not included in the release workflows + # because any time a new library version is released to maven central + # it can fail due to test code incompatibility with the new library version, + # or due to slight changes in emitted telemetry - # muzzle is intentionally not included in the release workflows - # because any time a new library version is released to maven central it can fail, - # and this is not a reason to hold up the release +# smoke-test: +# uses: ./.github/workflows/reusable-smoke-test.yml +# + # muzzle is intentionally not included in the release workflows + # because any time a new library version is released to maven central it can fail, + # and this is not a reason to hold up the release - gradle-plugins: - uses: ./.github/workflows/reusable-gradle-plugins.yml +# gradle-plugins: +# uses: ./.github/workflows/reusable-gradle-plugins.yml - examples: - uses: ./.github/workflows/reusable-examples.yml release: needs: - - assemble - - test - - smoke-test - - gradle-plugins - - examples + - test-java8 + - test-java11 +# - smoke-test +# - gradle-plugins outputs: version: ${{ steps.create-github-release.outputs.version }} runs-on: ubuntu-latest steps: - - run: | - if [[ $GITHUB_REF_NAME != release/* ]]; then - echo this workflow should only be run against release branches - exit 1 - fi - uses: actions/checkout@v3 @@ -77,15 +70,6 @@ jobs: with: ref: main - - run: | - if [[ $VERSION == *.0 ]]; then - # not making a patch release - if ! grep --quiet "^## Version $VERSION " CHANGELOG.md; then - echo the pull request generated by prepare-release-branch.yml needs to be merged first - exit 1 - fi - fi - # back to the release branch - uses: actions/checkout@v3 with: @@ -97,70 +81,11 @@ jobs: distribution: temurin java-version: 17 - - name: Build and publish artifacts - uses: gradle/gradle-build-action@v2 - with: - arguments: assemble publishToSonatype closeAndReleaseSonatypeStagingRepository - env: - SONATYPE_USER: ${{ secrets.SONATYPE_USER }} - SONATYPE_KEY: ${{ secrets.SONATYPE_KEY }} - GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }} - GPG_PASSWORD: ${{ secrets.GPG_PASSWORD }} - - - name: Build and publish gradle plugins + - name: Setup Gradle uses: gradle/gradle-build-action@v2 - env: - SONATYPE_USER: ${{ secrets.SONATYPE_USER }} - SONATYPE_KEY: ${{ secrets.SONATYPE_KEY }} - GRADLE_PUBLISH_KEY: ${{ secrets.GRADLE_PUBLISH_KEY }} - GRADLE_PUBLISH_SECRET: ${{ secrets.GRADLE_PUBLISH_SECRET }} - GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }} - GPG_PASSWORD: ${{ secrets.GPG_PASSWORD }} - with: - # Don't use publishToSonatype since we don't want to publish the marker artifact - arguments: build publishPlugins publishPluginMavenPublicationToSonatypeRepository closeAndReleaseSonatypeStagingRepository - build-root-directory: gradle-plugins - - - name: Generate release notes - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - # conditional blocks not indented because of the heredoc - if [[ $VERSION == *.0 ]]; then - cat > /tmp/release-notes.txt << EOF - This release targets the OpenTelemetry SDK $VERSION. - - Note that all artifacts other than \`io.opentelemetry.javaagent:opentelemetry-javaagent\` have the \`-alpha\` suffix attached to their version number, reflecting that they are still alpha quality and will continue to have breaking changes. Please see the [VERSIONING.md](https://github.com/open-telemetry/opentelemetry-java-instrumentation/blob/main/VERSIONING.md#opentelemetry-java-instrumentation-versioning) for more details. - - EOF - else - cat > /tmp/release-notes.txt << EOF - This is a patch release on the previous $PRIOR_VERSION release, fixing the issue(s) below. - - EOF - fi - - # CHANGELOG_SECTION.md is also used at the end of the release workflow - # for copying the change log updates to main - sed -n "0,/^## Version $VERSION /d;/^## Version /q;p" CHANGELOG.md \ - > /tmp/CHANGELOG_SECTION.md - # the complex perl regex is needed because markdown docs render newlines as soft wraps - # while release notes render them as line breaks - perl -0pe 's/(?> /tmp/release-notes.txt - - # conditional block not indented because of the heredoc - if [[ $VERSION == *.0 ]]; then - cat >> /tmp/release-notes.txt << EOF - ### 🙇 Thank you - - This release was possible thanks to the following contributors who shared their brilliant ideas and awesome pull requests: - - EOF - - .github/scripts/generate-release-contributors.sh v$PRIOR_VERSION >> /tmp/release-notes.txt - fi + - name: Execute Gradle build + run: ./gradlew build -x :instrumentation:finatra-2.9:javaagent:test -x :instrumentation:hibernate:hibernate-4.0:javaagent:compileLatestDepTestJava -x :instrumentation:hibernate:hibernate-4.0:javaagent:compileLatestDepTestGroovy -x :instrumentation:play:play-mvc:play-mvc-2.6:javaagent:test - id: create-github-release name: Create GitHub release @@ -170,94 +95,7 @@ jobs: cp javaagent/build/libs/opentelemetry-javaagent-${VERSION}.jar opentelemetry-javaagent.jar gh release create --target $GITHUB_REF_NAME \ --title "Version $VERSION" \ - --notes-file /tmp/release-notes.txt \ - --discussion-category announcements \ v$VERSION \ opentelemetry-javaagent.jar - echo "::set-output name=version::$VERSION" - - - uses: actions/checkout@v3 - with: - # the step below is creating a pull request against main - ref: main - - - name: Copy change log updates to main - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - if [[ $VERSION == *.0 ]]; then - # this was not a patch release, so the version exists already in the CHANGELOG.md - - # update the release date - date=$(gh release view v$VERSION --json publishedAt --jq .publishedAt | sed 's/T.*//') - sed -Ei "s/## Version $VERSION .*/## Version $VERSION ($date)/" CHANGELOG.md - - # the entries are copied over from the release branch to support workflows - # where change log entries may be updated after preparing the release branch - - # copy the portion above the release, up to and including the heading - sed -n "0,/^## Version $VERSION ($date)/p" CHANGELOG.md > /tmp/CHANGELOG.md - - # copy the release notes - cat /tmp/CHANGELOG_SECTION.md >> /tmp/CHANGELOG.md - - # copy the portion below the release - sed -n "0,/^## Version $VERSION /d;0,/^## Version /{/^## Version/!d};p" CHANGELOG.md \ - >> /tmp/CHANGELOG.md - - # update the real CHANGELOG.md - cp /tmp/CHANGELOG.md CHANGELOG.md - else - # this was a patch release, so the version does not exist already in the CHANGELOG.md - - # copy the portion above the top-most release, not including the heading - sed -n "0,/^## Version /{ /^## Version /!p }" CHANGELOG.md > /tmp/CHANGELOG.md - - # add the heading - date=$(gh release view v$VERSION --json publishedAt --jq .publishedAt | sed 's/T.*//') - echo "## Version $VERSION ($date)" >> /tmp/CHANGELOG.md - - # copy the release notes - cat /tmp/CHANGELOG_SECTION.md >> /tmp/CHANGELOG.md - - # copy the portion starting from the top-most release - sed -n "/^## Version /,\$p" CHANGELOG.md >> /tmp/CHANGELOG.md - - # update the real CHANGELOG.md - cp /tmp/CHANGELOG.md CHANGELOG.md - fi - - - name: Use CLA approved github bot - run: .github/scripts/use-cla-approved-github-bot.sh - - - name: Create pull request against main - env: - # not using secrets.GITHUB_TOKEN since pull requests from that token do not run workflows - GITHUB_TOKEN: ${{ secrets.BOT_TOKEN }} - run: | - message="Copy change log updates from $GITHUB_REF_NAME" - body="Copy log updates from \`$GITHUB_REF_NAME\`." - branch="copy-change-log-updates-from-${GITHUB_REF_NAME//\//-}" - - if [[ $VERSION == *.0 ]]; then - if git diff --quiet; then - echo there are no updates needed to the change log on main, not creating pull request - exit 0 # success - fi - fi - - git commit -a -m "$message" - git push origin HEAD:$branch - gh pr create --title "$message" \ - --body "$body" \ - --head $branch \ - --base main - - create-operator-pull-request: - needs: release - uses: ./.github/workflows/reusable-create-operator-pull-request.yml - with: - javaagent-version: ${{ needs.release.outputs.version }} - secrets: - BOT_TOKEN: ${{ secrets.BOT_TOKEN }} + echo "::set-output name=version::$VERSION" \ No newline at end of file diff --git a/.github/workflows/reusable-assemble.yml b/.github/workflows/reusable-assemble.yml deleted file mode 100644 index dd3bb46798d4..000000000000 --- a/.github/workflows/reusable-assemble.yml +++ /dev/null @@ -1,67 +0,0 @@ -name: Reusable - Assemble - -on: - workflow_call: - inputs: - cache-read-only: - type: boolean - required: false - no-build-cache: - type: boolean - required: false - secrets: - GRADLE_ENTERPRISE_ACCESS_KEY: - required: false - GE_CACHE_USERNAME: - required: false - GE_CACHE_PASSWORD: - required: false - -jobs: - assemble: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - - name: Set up JDK for running Gradle - uses: actions/setup-java@v3 - with: - distribution: temurin - java-version: 17 - - - name: Start deadlock detector - run: .github/scripts/deadlock-detector.sh - - - name: Assemble - uses: gradle/gradle-build-action@v2 - env: - GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_ACCESS_KEY }} - GE_CACHE_USERNAME: ${{ secrets.GE_CACHE_USERNAME }} - GE_CACHE_PASSWORD: ${{ secrets.GE_CACHE_PASSWORD }} - with: - # javadoc task fails sporadically fetching https://docs.oracle.com/javase/8/docs/api/ - arguments: assemble -x javadoc ${{ inputs.no-build-cache && '--no-build-cache' || '' }} - cache-read-only: ${{ inputs.cache-read-only }} - # gradle enterprise is used for the build cache - gradle-home-cache-excludes: caches/build-cache-1 - - - name: Upload deadlock detector artifacts if any - if: always() - uses: actions/upload-artifact@v3 - with: - name: deadlock-detector-assemble - path: /tmp/deadlock-detector-* - if-no-files-found: ignore - - - name: Upload jvm crash dump files if any - if: always() - uses: actions/upload-artifact@v3 - with: - name: javacore-assemble - path: | - **/hs_err_pid*.log - **/javacore.*.txt - **/Snap.*.trc - **/core.*.dmp - **/jitdump.*.dmp - if-no-files-found: ignore diff --git a/.github/workflows/reusable-create-java-contrib-pull-request.yml b/.github/workflows/reusable-create-java-contrib-pull-request.yml new file mode 100644 index 000000000000..1784508121cc --- /dev/null +++ b/.github/workflows/reusable-create-java-contrib-pull-request.yml @@ -0,0 +1,69 @@ +name: Reusable - Create Java contrib pull request + +on: + workflow_call: + inputs: + version: + type: string + required: true + secrets: + BOT_TOKEN: + required: true + # to help with partial release build failures + workflow_dispatch: + inputs: + version: + description: "Version" + required: true + +jobs: + create-java-contrib-pull-request: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + repository: opentelemetrybot/opentelemetry-java-contrib + # this is the personal access token used for "git push" below + token: ${{ secrets.BOT_TOKEN }} + + - name: Initialize pull request branch + env: + VERSION: ${{ inputs.version }} + run: | + git remote add upstream https://github.com/open-telemetry/opentelemetry-java-contrib.git + git fetch upstream + git checkout -b update-opentelemetry-sdk-to-${VERSION} upstream/main + + - name: Update version + env: + VERSION: ${{ inputs.version }} + run: ./.github/scripts/update-sdk-version.sh $VERSION + + - name: Use CLA approved github bot + run: .github/scripts/use-cla-approved-github-bot.sh + + - name: Create pull request against opentelemetry-java-contrib + env: + # this is the personal access token used for "gh pr create" below + GITHUB_TOKEN: ${{ secrets.BOT_TOKEN }} + VERSION: ${{ inputs.version }} + run: | + message="Update the SDK and instrumentation versions to $VERSION" + body=$(cat << EOF + Update the SDK and instrumentation versions to \`$VERSION\`. + + Note: you will likely need to re-run the checks on this PR in an hour or so, once the + updated dependencies are visible in maven central. + EOF + ) + + # gh pr create doesn't have a way to explicitly specify different head and base + # repositories currently, but it will implicitly pick up the head from a different + # repository if you set up a tracking branch + + git commit -a -m "$message" + git push --set-upstream origin HEAD:update-opentelemetry-sdk-to-${VERSION} + gh pr create --title "$message" \ + --body "$body" \ + --repo open-telemetry/opentelemetry-java-contrib \ + --base main diff --git a/.github/workflows/reusable-create-java-docs-pull-request.yml b/.github/workflows/reusable-create-java-docs-pull-request.yml new file mode 100644 index 000000000000..100fe5233704 --- /dev/null +++ b/.github/workflows/reusable-create-java-docs-pull-request.yml @@ -0,0 +1,72 @@ +name: Reusable - Create Java docs pull request + +on: + workflow_call: + inputs: + version: + type: string + required: true + secrets: + BOT_TOKEN: + required: true + # to help with partial release build failures + workflow_dispatch: + inputs: + version: + description: "Version" + required: true + +jobs: + create-java-docs-pull-request: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + repository: opentelemetrybot/opentelemetry-java-docs + # this is the personal access token used for "git push" below + token: ${{ secrets.BOT_TOKEN }} + + - name: Initialize pull request branch + env: + VERSION: ${{ inputs.version }} + run: | + git remote add upstream https://github.com/open-telemetry/opentelemetry-java-docs.git + git fetch upstream + git checkout -b update-opentelemetry-sdk-to-${VERSION} upstream/main + + - name: Update version + env: + VERSION: ${{ inputs.version }} + run: ./.github/scripts/update-sdk-version.sh $VERSION + + - name: Use CLA approved github bot + run: | + # cannot run the use-cla-approved-github-bot.sh script here since in a different repo + git config user.name opentelemetrybot + git config user.email 107717825+opentelemetrybot@users.noreply.github.com + + - name: Create pull request against opentelemetry-java-docs + env: + # this is the personal access token used for "gh pr create" below + GITHUB_TOKEN: ${{ secrets.BOT_TOKEN }} + VERSION: ${{ inputs.version }} + run: | + message="Update the SDK and instrumentation versions to $VERSION" + body=$(cat << EOF + Update the SDK and instrumentation versions to \`$VERSION\`. + + Note: you will likely need to re-run the checks on this PR in an hour or so, once the + updated dependencies are visible in maven central. + EOF + ) + + # gh pr create doesn't have a way to explicitly specify different head and base + # repositories currently, but it will implicitly pick up the head from a different + # repository if you set up a tracking branch + + git commit -a -m "$message" + git push --set-upstream origin HEAD:update-opentelemetry-sdk-to-${VERSION} + gh pr create --title "$message" \ + --body "$body" \ + --repo open-telemetry/opentelemetry-java-docs \ + --base main diff --git a/.github/workflows/reusable-create-operator-pull-request.yml b/.github/workflows/reusable-create-operator-pull-request.yml index fe7a2703053c..af69232582b8 100644 --- a/.github/workflows/reusable-create-operator-pull-request.yml +++ b/.github/workflows/reusable-create-operator-pull-request.yml @@ -3,7 +3,7 @@ name: Reusable - Create operator pull request on: workflow_call: inputs: - javaagent-version: + version: type: string required: true secrets: @@ -12,8 +12,8 @@ on: # to help with partial release build failures workflow_dispatch: inputs: - javaagent-version: - description: "Javaagent version" + version: + description: "Version" required: true jobs: @@ -28,7 +28,7 @@ jobs: - name: Initialize pull request branch env: - VERSION: ${{ inputs.javaagent-version }} + VERSION: ${{ inputs.version }} run: | git remote add upstream https://github.com/open-telemetry/opentelemetry-operator.git git fetch upstream @@ -36,21 +36,30 @@ jobs: - name: Update version env: - VERSION: ${{ inputs.javaagent-version }} + VERSION: ${{ inputs.version }} run: | echo $VERSION > autoinstrumentation/java/version.txt - name: Use CLA approved github bot - run: .github/scripts/use-cla-approved-github-bot.sh + run: | + # cannot run the use-cla-approved-github-bot.sh script here since in a different repo + git config user.name opentelemetrybot + git config user.email 107717825+opentelemetrybot@users.noreply.github.com - name: Create pull request against opentelemetry-operator env: # this is the personal access token used for "gh pr create" below GITHUB_TOKEN: ${{ secrets.BOT_TOKEN }} - VERSION: ${{ inputs.javaagent-version }} + VERSION: ${{ inputs.version }} run: | message="Update the javaagent version to $VERSION" - body="Update the javaagent version to \`$VERSION\`." + # note that @open-telemetry/java-instrumentation-approvers cannot be used below + # because opentelemetrybot is not a member of the OpenTelemetry org, + # and so it cannot @ mention OpenTelemetry org groups + body="Update the javaagent version to \`$VERSION\`. + + cc @laurit @mateuszrzeszutek @trask + " # gh pr create doesn't have a way to explicitly specify different head and base # repositories currently, but it will implicitly pick up the head from a different diff --git a/.github/workflows/reusable-create-website-pull-request.yml b/.github/workflows/reusable-create-website-pull-request.yml new file mode 100644 index 000000000000..e4536f1cff87 --- /dev/null +++ b/.github/workflows/reusable-create-website-pull-request.yml @@ -0,0 +1,69 @@ +name: Reusable - Create website pull request + +on: + workflow_call: + inputs: + version: + type: string + required: true + secrets: + BOT_TOKEN: + required: true + # to help with partial release build failures + workflow_dispatch: + inputs: + version: + description: "Version" + required: true + +jobs: + create-java-website-pull-request: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + repository: opentelemetrybot/opentelemetry.io + # this is the personal access token used for "git push" below + token: ${{ secrets.BOT_TOKEN }} + + - name: Initialize pull request branch + env: + VERSION: ${{ inputs.version }} + run: | + git remote add upstream https://github.com/open-telemetry/opentelemetry.io.git + git fetch upstream + git checkout -b update-opentelemetry-java-instrumentation-to-${VERSION} upstream/main + git submodule update + + - name: Update version + env: + VERSION: ${{ inputs.version }} + run: | + # TODO (trask) this will need to be updated when instrumentation-annotations are stable + sed -Ei "s/^javaAnnotationsVersion: [0-9.]+-alpha$/javaAnnotationsVersion: ${VERSION}-alpha/" content/en/docs/instrumentation/java/automatic/annotations.md + + - name: Use CLA approved github bot + run: | + # cannot run the use-cla-approved-github-bot.sh script here since in a different repo + git config user.name opentelemetrybot + git config user.email 107717825+opentelemetrybot@users.noreply.github.com + + - name: Create pull request against opentelemetry.io + env: + # this is the personal access token used for "gh pr create" below + GITHUB_TOKEN: ${{ secrets.BOT_TOKEN }} + VERSION: ${{ inputs.version }} + run: | + message="Update the Java instrumentation versions to $VERSION" + body="Update the Java instrumentation version to \`$VERSION\`." + + # gh pr create doesn't have a way to explicitly specify different head and base + # repositories currently, but it will implicitly pick up the head from a different + # repository if you set up a tracking branch + + git commit -a -m "$message" + git push --set-upstream origin HEAD:update-opentelemetry-java-instrumentation-to-${VERSION} + gh pr create --title "$message" \ + --body "$body" \ + --repo open-telemetry/opentelemetry.io \ + --base main diff --git a/.github/workflows/reusable-examples.yml b/.github/workflows/reusable-examples.yml deleted file mode 100644 index 5234668c4f69..000000000000 --- a/.github/workflows/reusable-examples.yml +++ /dev/null @@ -1,49 +0,0 @@ -name: Reusable - Examples - -on: - workflow_call: - inputs: - cache-read-only: - type: boolean - required: false - no-build-cache: - type: boolean - required: false - -jobs: - examples: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - - name: Set up JDK for running Gradle - uses: actions/setup-java@v3 - with: - distribution: temurin - java-version: 17 - - - name: Set up Gradle cache - uses: gradle/gradle-build-action@v2 - with: - cache-read-only: ${{ inputs.cache-read-only }} - - - name: Local publish of artifacts - # javadoc task fails sporadically fetching https://docs.oracle.com/javase/8/docs/api/ - run: ./gradlew publishToMavenLocal -x javadoc - - - name: Local publish of gradle plugins - # javadoc task fails sporadically fetching https://docs.oracle.com/javase/8/docs/api/ - run: ./gradlew publishToMavenLocal -x javadoc - working-directory: gradle-plugins - - - name: Build distro - run: ./gradlew build --init-script ../../.github/scripts/local.init.gradle.kts${{ inputs.no-build-cache && ' --no-build-cache' || '' }} - working-directory: examples/distro - - - name: Build extension - run: ./gradlew build --init-script ../../.github/scripts/local.init.gradle.kts${{ inputs.no-build-cache && ' --no-build-cache' || '' }} - working-directory: examples/extension - - - name: Run muzzle check against extension - run: ./gradlew muzzle --init-script ../../.github/scripts/local.init.gradle.kts - working-directory: examples/extension diff --git a/.github/workflows/reusable-gradle-plugins.yml b/.github/workflows/reusable-gradle-plugins.yml deleted file mode 100644 index bf8faa8a9c5b..000000000000 --- a/.github/workflows/reusable-gradle-plugins.yml +++ /dev/null @@ -1,30 +0,0 @@ -name: Reusable - Gradle plugins - -on: - workflow_call: - inputs: - cache-read-only: - type: boolean - required: false - no-build-cache: - type: boolean - required: false - -jobs: - gradle-plugins: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - - name: Set up JDK 11 for running Gradle - uses: actions/setup-java@v3 - with: - distribution: temurin - java-version: 11 - - - name: Build - uses: gradle/gradle-build-action@v2 - with: - arguments: build ${{ inputs.no-build-cache && '--no-build-cache' || '' }} - build-root-directory: gradle-plugins - cache-read-only: ${{ inputs.cache-read-only }} diff --git a/.github/workflows/reusable-markdown-link-check.yml b/.github/workflows/reusable-markdown-link-check.yml deleted file mode 100644 index 2832088d4305..000000000000 --- a/.github/workflows/reusable-markdown-link-check.yml +++ /dev/null @@ -1,21 +0,0 @@ -name: Reusable - Markdown link check - -on: - workflow_call: - -jobs: - markdown-link-check: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - - name: Install markdown-link-check - run: npm install -g markdown-link-check - - - name: Run markdown-link-check - run: | - find . -type f \ - -name '*.md' \ - -not -path './CHANGELOG.md' \ - -not -path './licenses/*' \ - | xargs .github/scripts/markdown-link-check-with-retry.sh diff --git a/.github/workflows/reusable-muzzle.yml b/.github/workflows/reusable-muzzle.yml index e3e400ea89d3..475dd9a3ce18 100644 --- a/.github/workflows/reusable-muzzle.yml +++ b/.github/workflows/reusable-muzzle.yml @@ -1,6 +1,7 @@ name: Reusable - Muzzle on: + workflow_dispatch: workflow_call: inputs: cache-read-only: diff --git a/.github/workflows/reusable-open-issue-on-failure.yml b/.github/workflows/reusable-open-issue-on-failure.yml deleted file mode 100644 index 31f81f979d4e..000000000000 --- a/.github/workflows/reusable-open-issue-on-failure.yml +++ /dev/null @@ -1,18 +0,0 @@ -name: Reusable - Open issue on failure - -on: - workflow_call: - -jobs: - open-issue: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - - name: Open issue - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - gh issue create --title "$GITHUB_WORKFLOW #$GITHUB_RUN_NUMBER failed" \ - --label bug \ - --body "See [$GITHUB_WORKFLOW #$GITHUB_RUN_NUMBER](https://github.com/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID)." diff --git a/.github/workflows/reusable-smoke-test-images.yml b/.github/workflows/reusable-smoke-test-images.yml deleted file mode 100644 index 5f0a689db32d..000000000000 --- a/.github/workflows/reusable-smoke-test-images.yml +++ /dev/null @@ -1,66 +0,0 @@ -name: PR build fake backend images for smoke tests - -on: - workflow_call: - inputs: - project: - type: string - required: true - publish: - type: boolean - required: false - cache-read-only: - type: boolean - required: false - skip-java-8: - type: boolean - required: false - skip-java-17: - type: boolean - required: false - skip-java-18: - type: boolean - required: false - -jobs: - build: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - - name: Set up JDK for running Gradle - uses: actions/setup-java@v3 - with: - distribution: temurin - java-version: 17 - - - name: Login to GitHub package registry - if: ${{ inputs.publish }} - uses: docker/login-action@v2 - with: - registry: ghcr.io - username: ${{ github.repository_owner }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Set tag - run: echo "TAG=$(date '+%Y%m%d').$GITHUB_RUN_ID" >> $GITHUB_ENV - - - name: Set up Gradle cache - uses: gradle/gradle-build-action@v2 - with: - cache-read-only: ${{ inputs.cache-read-only }} - - - name: Build Java 8 Docker image - if: ${{ !inputs.skip-java-8 }} - run: ./gradlew ${{ inputs.project }}:${{ inputs.publish && 'jib' || 'jibDockerBuild' }} -PtargetJDK=8 -Djib.httpTimeout=120000 -Djib.console=plain - - - name: Build Java 11 Docker image - run: ./gradlew ${{ inputs.project }}:${{ inputs.publish && 'jib' || 'jibDockerBuild' }} -PtargetJDK=11 -Djib.httpTimeout=120000 -Djib.console=plain - - - name: Build Java 17 Docker image - if: ${{ !inputs.skip-java-17 }} - run: ./gradlew ${{ inputs.project }}:${{ inputs.publish && 'jib' || 'jibDockerBuild' }} -PtargetJDK=17 -Djib.httpTimeout=120000 -Djib.console=plain - - - name: Build Java 18 Docker image - if: ${{ !inputs.skip-java-18 }} - run: ./gradlew ${{ inputs.project }}:${{ inputs.publish && 'jib' || 'jibDockerBuild' }} -PtargetJDK=18 -Djib.httpTimeout=120000 -Djib.console=plain diff --git a/.github/workflows/reusable-smoke-test.yml b/.github/workflows/reusable-smoke-test.yml deleted file mode 100644 index 6d9ad988b511..000000000000 --- a/.github/workflows/reusable-smoke-test.yml +++ /dev/null @@ -1,99 +0,0 @@ -name: Reusable - Smoke test - -on: - workflow_call: - inputs: - skip-windows: - type: boolean - required: false - cache-read-only: - type: boolean - required: false - no-build-cache: - type: boolean - required: false - secrets: - GRADLE_ENTERPRISE_ACCESS_KEY: - required: false - GE_CACHE_USERNAME: - required: false - GE_CACHE_PASSWORD: - required: false - -jobs: - smoke-test: - runs-on: ${{ matrix.os }} - strategy: - matrix: - os: - - windows-2019 - - ubuntu-latest - smoke-test-suite: - - jetty - - liberty - - payara - - tomcat - - tomee - - websphere - - wildfly - - other - exclude: - - os: ${{ inputs.skip-windows && 'windows-2019' || '' }} - - os: windows-2019 - smoke-test-suite: websphere - fail-fast: false - steps: - - name: Support long paths - run: git config --system core.longpaths true - if: matrix.os == 'windows-2019' - - - uses: actions/checkout@v3 - - - name: Set up JDK for running Gradle - uses: actions/setup-java@v3 - with: - distribution: temurin - java-version: 17 - - - name: Set up Gradle cache - uses: gradle/gradle-build-action@v2 - with: - # only push cache for one matrix option per OS since github action cache space is limited - cache-read-only: ${{ inputs.cache-read-only || matrix.smoke-test-suite != 'tomcat' }} - # gradle enterprise is used for the build cache - gradle-home-cache-excludes: caches/build-cache-1 - - - name: Build - env: - GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_ACCESS_KEY }} - GE_CACHE_USERNAME: ${{ secrets.GE_CACHE_USERNAME }} - GE_CACHE_PASSWORD: ${{ secrets.GE_CACHE_PASSWORD }} - # running suite "none" compiles everything needed by smoke tests without executing any tests - run: ./gradlew :smoke-tests:test -PsmokeTestSuite=none --no-daemon ${{ inputs.no-build-cache && ' --no-build-cache' || '' }} - - - name: Test - env: - GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_ACCESS_KEY }} - GE_CACHE_USERNAME: ${{ secrets.GE_CACHE_USERNAME }} - GE_CACHE_PASSWORD: ${{ secrets.GE_CACHE_PASSWORD }} - run: ./gradlew :smoke-tests:test -PsmokeTestSuite=${{ matrix.smoke-test-suite }}${{ inputs.no-build-cache && ' --no-build-cache' || '' }} - - - name: Upload jvm crash dump files if any - if: always() - uses: actions/upload-artifact@v3 - with: - name: javacore-smoke-test-${{ matrix.smoke-test-suite }}-${{ matrix.os }} - # we expect crash dumps either in root director or in smoke-tests - # not using **/ here because actions/upload-artifact fails with long paths https://github.com/actions/upload-artifact/issues/309 - path: | - hs_err_pid*.log - smoke-tests/hs_err_pid*.log - javacore.*.txt - smoke-tests/javacore.*.txt - Snap.*.trc - smoke-tests/Snap.*.trc - core.*.dmp - smoke-tests/core.*.dmp - jitdump.*.dmp - smoke-tests/jitdump.*.dmp - if-no-files-found: ignore diff --git a/.github/workflows/reusable-test.yml b/.github/workflows/reusable-test-11.yml similarity index 91% rename from .github/workflows/reusable-test.yml rename to .github/workflows/reusable-test-11.yml index e1e58ced2a34..0ef7d7010fed 100644 --- a/.github/workflows/reusable-test.yml +++ b/.github/workflows/reusable-test-11.yml @@ -1,6 +1,7 @@ name: Reusable - Test on: + workflow_dispatch: workflow_call: inputs: cache-read-only: @@ -23,12 +24,10 @@ jobs: strategy: matrix: test-java-version: - - 8 - 11 - 17 vm: - hotspot - - openj9 fail-fast: false steps: - uses: actions/checkout@v3 @@ -63,7 +62,7 @@ jobs: GE_CACHE_PASSWORD: ${{ secrets.GE_CACHE_PASSWORD }} uses: gradle/gradle-build-action@v2 with: - arguments: check -PtestJavaVersion=${{ matrix.test-java-version }} -PtestJavaVM=${{ matrix.vm }} -Porg.gradle.java.installations.paths=${{ steps.setup-test-java.outputs.path }} -Porg.gradle.java.installations.auto-download=false ${{ inputs.no-build-cache && ' --no-build-cache' || '' }} + arguments: check -PtestJavaVersion=${{ matrix.test-java-version }} -PtestJavaVM=${{ matrix.vm }} -Porg.gradle.java.installations.paths=${{ steps.setup-test-java.outputs.path }} -Porg.gradle.java.installations.auto-download=false ${{ inputs.no-build-cache && ' --no-build-cache' || '' }} -x :instrumentation:finatra-2.9:javaagent:test -x :instrumentation:hibernate:hibernate-4.0:javaagent:compileLatestDepTestJava -x :instrumentation:hibernate:hibernate-4.0:javaagent:compileLatestDepTestGroovy # only push cache for one matrix option since github action cache space is limited cache-read-only: ${{ inputs.cache-read-only || matrix.test-java-version != 11 || matrix.vm != 'hotspot' }} # gradle enterprise is used for the build cache diff --git a/.github/workflows/reusable-test-8.yml b/.github/workflows/reusable-test-8.yml new file mode 100644 index 000000000000..1b5d33e12945 --- /dev/null +++ b/.github/workflows/reusable-test-8.yml @@ -0,0 +1,89 @@ +name: Reusable - Test + +on: + workflow_dispatch: + workflow_call: + inputs: + cache-read-only: + type: boolean + required: false + no-build-cache: + type: boolean + required: false + secrets: + GRADLE_ENTERPRISE_ACCESS_KEY: + required: false + GE_CACHE_USERNAME: + required: false + GE_CACHE_PASSWORD: + required: false + +jobs: + test: + runs-on: ubuntu-latest + strategy: + matrix: + test-java-version: + - 8 + vm: + - hotspot + fail-fast: false + steps: + - uses: actions/checkout@v3 + + - id: setup-test-java + name: Set up JDK ${{ matrix.test-java-version }}-${{ matrix.vm }} for running tests + uses: actions/setup-java@v3 + with: + distribution: ${{ matrix.vm == 'hotspot' && 'temurin' || 'adopt-openj9'}} + java-version: ${{ matrix.test-java-version }} + + - name: Set up JDK for running Gradle + uses: actions/setup-java@v3 + with: + distribution: temurin + java-version: 17 + + # vaadin tests use pnpm + - name: Cache pnpm modules + uses: actions/cache@v3 + with: + path: ~/.pnpm-store + key: ${{ runner.os }}-test-cache-pnpm-modules + + - name: Start deadlock detector + run: .github/scripts/deadlock-detector.sh + + - name: Test + env: + GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_ACCESS_KEY }} + GE_CACHE_USERNAME: ${{ secrets.GE_CACHE_USERNAME }} + GE_CACHE_PASSWORD: ${{ secrets.GE_CACHE_PASSWORD }} + uses: gradle/gradle-build-action@v2 + with: + arguments: check -PtestJavaVersion=${{ matrix.test-java-version }} -PtestJavaVM=${{ matrix.vm }} -Porg.gradle.java.installations.paths=${{ steps.setup-test-java.outputs.path }} -Porg.gradle.java.installations.auto-download=false ${{ inputs.no-build-cache && ' --no-build-cache' || '' }} -x :instrumentation:finatra-2.9:javaagent:test -x :instrumentation:play:play-mvc:play-mvc-2.6:javaagent:test -x :instrumentation:hibernate:hibernate-4.0:javaagent:compileLatestDepTestJava -x :instrumentation:hibernate:hibernate-4.0:javaagent:compileLatestDepTestGroovy + # only push cache for one matrix option since github action cache space is limited + cache-read-only: ${{ inputs.cache-read-only || matrix.test-java-version != 11 || matrix.vm != 'hotspot' }} + # gradle enterprise is used for the build cache + gradle-home-cache-excludes: caches/build-cache-1 + + - name: Upload deadlock detector artifacts if any + if: always() + uses: actions/upload-artifact@v3 + with: + name: deadlock-detector-test-${{ matrix.test-java-version }}-${{ matrix.vm }} + path: /tmp/deadlock-detector-* + if-no-files-found: ignore + + - name: Upload jvm crash dump files if any + if: always() + uses: actions/upload-artifact@v3 + with: + name: javacore-test-${{ matrix.test-java-version }} + path: | + **/hs_err_pid*.log + **/javacore.*.txt + **/Snap.*.trc + **/core.*.dmp + **/jitdump.*.dmp + if-no-files-found: ignore diff --git a/.github/workflows/reusable-test-latest-deps.yml b/.github/workflows/reusable-test-latest-deps.yml deleted file mode 100644 index e59e69171641..000000000000 --- a/.github/workflows/reusable-test-latest-deps.yml +++ /dev/null @@ -1,65 +0,0 @@ -name: Reusable - Test latest deps - -on: - workflow_call: - inputs: - skip: - type: boolean - required: false - cache-read-only: - type: boolean - required: false - no-build-cache: - type: boolean - required: false - secrets: - GRADLE_ENTERPRISE_ACCESS_KEY: - required: false - GE_CACHE_USERNAME: - required: false - GE_CACHE_PASSWORD: - required: false - -jobs: - test-latest-deps: - # the condition is on the steps below instead of here on the job, because skipping the job - # causes the job to show up as canceled in the GitHub UI which prevents the build section from - # collapsing when everything (else) is green - # - # and the name is updated when the steps below are skipped which makes what's happening clearer - # in the GitHub UI - name: testLatestDeps${{ inputs.skip && ' (skipped)' || '' }} - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - - name: Set up JDK for running Gradle - if: ${{ !inputs.skip }} - uses: actions/setup-java@v3 - with: - distribution: temurin - java-version: 17 - - # vaadin tests use pnpm - - name: Cache pnpm modules - uses: actions/cache@v3 - with: - path: ~/.pnpm-store - key: ${{ runner.os }}-test-latest-cache-pnpm-modules - - - name: Increase gradle daemon heap size - run: | - sed -i "s/org.gradle.jvmargs=/org.gradle.jvmargs=-Xmx2g /" gradle.properties - - - name: Test - if: ${{ !inputs.skip }} - uses: gradle/gradle-build-action@v2 - env: - GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_ACCESS_KEY }} - GE_CACHE_USERNAME: ${{ secrets.GE_CACHE_USERNAME }} - GE_CACHE_PASSWORD: ${{ secrets.GE_CACHE_PASSWORD }} - with: - arguments: test -PtestLatestDeps=true ${{ inputs.no-build-cache && ' --no-build-cache' || '' }} - cache-read-only: ${{ inputs.cache-read-only }} - # gradle enterprise is used for the build cache - gradle-home-cache-excludes: caches/build-cache-1 diff --git a/CHANGELOG.md b/CHANGELOG.md index 9974cf016264..998d6e7429dc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,271 @@ ## Unreleased +## Version 1.19.1 (2022-10-19) + +### 🛠️ Bug fixes + +- Capture `net.host.name` on netty SERVER spans + ([#6892](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/6892)) + +## Version 1.19.0 (2022-10-13) + +### Migration notes + +- Deprecated `HttpServerAttributesGetter.serverName()`, and removed `http.host` and + `http.server_name` attributes + ([#6709](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/6709)) +- Removed previously deprecated configuration flags (see previous release notes for deprecations) + ([#6771](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/6771)) +- The restlet-1 instrumentation name has changed from `restlet-1.0` to `restlet-1.1` + ([#6106](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/6106)) + +### 🌟 New library instrumentation + +- Netty 4.1 + ([#6820](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/6820)) + +### 📈 Enhancements + +- Move in resource providers from core repo + ([#6574](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/6574)) +- Propagate client span context in doOnRequest + ([#6621](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/6621)) +- Update attribute key of rocketmq's message tag to use name from semantic conventions + (`messaging.rocketmq.message_tag`) + ([#6677](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/6677)) +- Avoid muzzle matcher warning for the spring-boot-actuator-autoconfigure instrumentation + ([#6695](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/6695)) +- Add marker attribute for Log4j 2 + ([#6680](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/6680)) +- Add marker attribute for Logback + ([#6652](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/6652)) +- Add daemon attribute to process.runtime.jvm.threads.count + ([#6635](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/6635)) +- Link JMS receive span with the producer span + ([#6804](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/6804)) +- Link RabbitMQ receive span with the producer span + ([#6808](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/6808)) +- Run context customizers before span start instead of after + ([#6634](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/6634)) +- Strip sensitive data from the url + ([#6417](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/6417)) +- Extract `net.peer.{name,port}` on start for CLIENT spans + ([#6828](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/6828)) + +### 🛠️ Bug fixes + +- Fix scheduled job experimental attributes property + ([#6633](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/6633)) +- Fix AutoConfigurationCustomizer.addPropertiesSupplier not taking into account configuration-file + ([#6697](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/6697)) +- Fix Dubbo NPE and trace propagation issue + ([#6640](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/6640)) +- Fix directory separator in ProcessResource attributes + ([#6716](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/6716)) +- Fix instrumentation for tomcat 10.1.0 + ([#6766](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/6766)) +- Fix instrumentation name for jaxrs-2.0-annotations + ([#6770](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/6770)) +- Fix instrumentation for vert.x 4.3.4 + ([#6809](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/6809)) +- Fix Restlet v2 `Message#getAttributes` calls + ([#6796](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/6796)) +- Guard against null HttpContext + ([#6792](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/6792)) + +## Version 1.18.0 (2022-09-14) + +The `opentelemetry-instrumentation-api` artifact is declared stable in this release. + +### Migration notes + +- There were a few late-breaking changes in `opentelemetry-instrumentation-api`, prior to it being + declared stable: + * `InstrumenterBuilder.addAttributesExtractors(AttributesExtractor...)` was removed, use instead + `addAttributesExtractors(AttributesExtractor)` or + `addAttributesExtractors(Iterable)` + * `SpanLinksExtractor.extractFromRequest()` was removed, use instead manual extraction + * `ErrorCauseExtractor.jdk()` was renamed to `ErrorCauseExtractor.getDefault()` + * `ClassNames` utility was removed with no direct replacement +- The deprecated `io.opentelemetry.instrumentation.api.config.Config` and related classes + have been removed + ([#6501](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/6501)) +- Net attributes getters were updated to reflect latest specification changes + ([#6503](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/6503)) +- The deprecated `Ordered` interface was removed from the `opentelemetry-javaagent-extension-api`, + use instead the `Ordered` interface from `opentelemetry-sdk-extension-autoconfigure-spi` + ([#6589](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/6589)) + +### 📈 Enhancements + +- Add Spring Boot service name guesser / ResourceProvider + ([#6516](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/6516)) +- Move micrometer shim library instrumentation back + ([#6538](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/6538)) +- Add grpc status code to metrics attrs + ([#6556](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/6556)) +- Add mongo sanitization configuration + ([#6541](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/6541)) +- Add kafka client metrics to the javaagent instrumentation + ([#6533](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/6533)) +- Add experimental span attribute job.system + ([#6586](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/6586)) +- Add code attributes for Logback + ([#6591](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/6591)) +- Reactor instrumentation: do not make root context current + ([#6593](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/6593)) + +### 🛠️ Bug fixes + +- Fix default-enabled config + ([#6491](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/6491)) +- Fix new jdbc javaagent config + ([#6492](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/6492)) +- Fix jaxrs async instrumentation race + ([#6523](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/6523)) +- Fix spring webmvc instrumentation name + ([#6557](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/6557)) +- Fix spring boot `@WithSpan` handling + ([#6619](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/6619)) + +## Version 1.17.0 (2022-08-18) + +### Migration notes + +- The `@WithSpan` and `@SpanAttribute` annotations has been moved from the + `io.opentelemetry:opentelemetry-extension-annotations` artifact to the + `io.opentelemetry.instrumentation:opentelemetry-instrumentation-annotations` in order to live and + evolve alongside the instrumentation itself. The instrumentation will continue to support the old + artifact for backwards compatibility, but new annotation-based features will only be built out and + supported with the new annotation artifact. +- `InstrumenterBuilder.newInstrumenter()` is renamed to `InstrumenterBuilder.buildInstrumenter()` + ([#6363](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/6363)) +- `io.opentelemetry.instrumentation.api.config.Config` is deprecated + ([#6360](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/6360)) +- `HttpCommonAttributesGetter.requestContentLengthUncompressed()` and + `responseContentLengthUncompressed` are deprecated + ([#6383](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/6383)) +- Ktor 2.0 instrumentation name is changed from `io.opentelemetry.ktor-1.0` to + `io.opentelemetry.ktor-2.0` + ([#6452](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/6452)) +- `io.opentelemetry.opentelemetry-annotations-1.0` instrumentation name is changed to + `io.opentelemetry.opentelemetry-instrumentation-annotations-1.16` + ([#6450](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/6450)) +- Liberty instrumentation names are changed from `io.opentelemetry.liberty` and + `io.opentelemetry.liberty-dispatcher` to `io.opentelemetry.liberty-20.0` and + `io.opentelemetry.liberty-dispatcher-20.0` + ([#6456](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/6456)) +- The 2-arg variant of HttpCommonAttributesGeter#statusCode() is deprecated + ([#6466](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/6466)) +- The `opentelemetry-spring-starter` artifact has been renamed to + `opentelemetry-spring-boot-starter`, the `opentelemetry-zipkin-exporter-starter` artifact has been + renamed to `opentelemetry-zipkin-spring-boot-starter`, and the + `opentelemetry-zipkin-exporter-starter` artifact has been renamed to + `opentelemetry-zipkin-spring-boot-starter` + ([#6453](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/6453)) +- Update net semantic convention changes based on recent specification changes: + `net.peer.ip` renamed to `net.sock.peer.addr`, `net.host.ip` renamed to `net.sock.host.addr`, + `net.peer.name` renamed to `net.sock.peer.name` for socket-level instrumentation, + and `NetClientAttributesGetter.peerIp()`, `NetServerAttributesGetter.peerIp()`, and + `NetServerAttributesGetter.peerPort()` are deprecated + ([#6268](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/6268)) + +### 📈 Enhancements + +- Move buffer pool metrics out of experimental + ([#6370](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/6370)) +- Add code attributes to several instrumentations + ([#6365](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/6365)) +- Add http.client|server.request|response.size metrics + ([#6376](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/6376)) +- Add Kafka instrumentation to the Spring Boot starter + ([#6371](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/6371)) +- Extract HTTP request & response content length from headers + ([#6415](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/6415)) +- Support DataDirect and Tibco Jdbc URLs + ([#6420](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/6420)) +- Set http.route in spring-autoconfigure webmvc instrumentation + ([#6414](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/6414)) +- Grizzly: capture all matching request & response headers + ([#6463](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/6463)) +- Capture messaging header value as span attribute + ([#6454](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/6454)) +- Add JDBC-specific sanitizer property + ([#6472](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/6472)) + +### 🛠️ Bug fixes + +- Fix duplicate spans for Quarkus gRPC server + ([#6356](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/6356)) +- Update Kafka library instrumentation to support version 3.0.0 and later + ([#6457](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/6457)) +- Mongodb: avoid duplicate tracing + ([#6465](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/6465)) +- Fix netty instrumentation NoSuchElementException + ([#6469](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/6469)) + +## Version 1.16.0 (2022-07-19) + +### Migration notes + +- Config has been replaced by ConfigProperties in Javaagent extensions SPIs +- The deprecated TimeExtractor has been removed +- The `opentelemetry-instrumentation-api-annotation-support` artifact has been renamed to + `opentelemetry-instrumentation-annotation-support` +- The `opentelemetry-annotations` instrumentation suppression key has been renamed to + `opentelemetry-extension-annotations` +- The 'otel.javaagent.experimental.use-noop-api' flag has been removed, as this capability is now + available via the `otel.experimental.sdk.enabled` flag + +### 🌟 New javaagent instrumentation + +- C3P0 connection pool metrics + ([#6174](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/6174)) +- JVM buffer pool metrics + ([#6177](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/6177)) +- Kafka client metrics + ([#6138](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/6138)) +- dropwizard-metrics to OpenTelemetry metrics bridge + ([#6259](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/6259)) + +### 🌟 New library instrumentation + +- C3P0 connection pool metrics + ([#6174](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/6174)) +- JVM buffer pool metrics + ([#6177](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/6177)) +- Kafka client metrics + ([#6138](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/6138)) +- Add metrics & micrometer support to spring-boot-autoconfigure + ([#6270](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/6270)) +- Spring Kafka library instrumentation + ([#6283](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/6283)) + +### 📈 Enhancements + +- Update GraphQL instrumentation to match spec + ([#6179](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/6179)) +- Make rpc.grpc.status_code required + ([#6184](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/6184)) +- Always pass Context when recording HttpServerMetrics + ([#6223](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/6223)) +- Capture enduser.id in servlet instrumentation + ([#6225](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/6225)) +- Support metric view configuration file in the Javaagent + ([#6228](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/6228)) +- Aws sdk2 sqs context propagation + ([#6199](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/6199)) +- More Spring JMS support + ([#6308](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/6308)) +- Hikaricp: Avoid registering duplicate metrics + ([#6325](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/6325)) + +### 🛠️ Bug fixes + +- Fix liberty net.peer.port + ([#6274](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/6274)) + ## Version 1.15.0 (2022-06-16) ### Migration notes diff --git a/README.md b/README.md index 26235fb24d2a..0c32ef51ea0a 100644 --- a/README.md +++ b/README.md @@ -1,168 +1,22 @@ ---- +[Upstream project README](https://github.com/open-telemetry/opentelemetry-java/blob/main/README.md) -

- - Getting Started -   •   - Getting Involved -   •   - Getting In Touch - -

+## Build Locally -

- - Build Status - - - GitHub release (latest by date including pre-releases) - - Beta -

+1. Run: -

- - Contributing -   •   - Scope - -

+ ``` + ./gradlew build + -x :instrumentation:kafka:kafka-streams-0.11:javaagent:testReceiveSpansDisabled + -x :instrumentation:spring:spring-webmvc-3.1:javaagent:codenarcTest + -x :instrumentation:vertx:vertx-kafka-client-3.6:javaagent:testNoReceiveTelemetry + -x test + ``` ---- +2. Pray 🙏 +3. The updated agent jar should be under `javaagent/build/libs`. -# OpenTelemetry Icon OpenTelemetry Instrumentation for Java +## Release -* [About](#about) -* [Getting Started](#getting-started) -* [Configuring the Agent](#configuring-the-agent) -* [Supported libraries, frameworks, and application servers](#supported-libraries-frameworks-and-application-servers) -* [Creating agent extensions](#creating-agent-extensions) -* [Manually instrumenting](#manually-instrumenting) -* [Logger MDC auto-instrumentation](#logger-mdc-mapped-diagnostic-context-auto-instrumentation) -* [Troubleshooting](#troubleshooting) -* [Contributing](#contributing) - -## About - -This project provides a Java agent JAR that can be attached to any Java 8+ -application and dynamically injects bytecode to capture telemetry from a -number of popular libraries and frameworks. -You can export the telemetry data in a variety of formats. -You can also configure the agent and exporter via command line arguments -or environment variables. The net result is the ability to gather telemetry -data from a Java application without code changes. - -This repository also publishes standalone instrumentation for several libraries (and growing) -that can be used if you prefer that over using the Java agent. -Please see [standalone library instrumentation](docs/standalone-library-instrumentation.md) -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). - -This package includes the instrumentation agent as well as -instrumentations for all supported libraries and all available data exporters. -The package provides a completely automatic, out-of-the-box experience. - -Enable the instrumentation agent using the `-javaagent` flag to the JVM. -``` -java -javaagent:path/to/opentelemetry-javaagent.jar \ - -jar myapp.jar -``` -By default, the OpenTelemetry Java agent uses -[OTLP exporter](https://github.com/open-telemetry/opentelemetry-java/tree/main/exporters/otlp) -configured to send data to -[OpenTelemetry collector](https://github.com/open-telemetry/opentelemetry-collector/blob/main/receiver/otlpreceiver/README.md) -at `http://localhost:4317`. - -Configuration parameters are passed as Java system properties (`-D` flags) or -as environment variables. See [the configuration documentation][config] -for the full list of configuration items. For example: -``` -java -javaagent:path/to/opentelemetry-javaagent.jar \ - -Dotel.resource.attributes=service.name=your-service-name \ - -Dotel.traces.exporter=zipkin \ - -jar myapp.jar -``` - -## Configuring the Agent - -The agent is [highly configurable][config]! Many aspects of the agent's behavior can be -configured for your needs, such as exporter choice, exporter config (like where -data is sent), trace context propagation headers, and much more. - -[Click here to see the detailed list of configuration environment variables and system properties][config]. - -*Note: Config parameter names are very likely to change over time, so please check -back here when trying out a new version! Please [report any bugs](https://github.com/open-telemetry/opentelemetry-java-instrumentation/issues) or unexpected -behavior you find.* - -## Supported libraries, frameworks, and application servers - -We support an impressively huge number of [libraries and frameworks](docs/supported-libraries.md#libraries--frameworks) and -a majority of the most popular [application servers](docs/supported-libraries.md#application-servers)...right out of the box! -[Click here to see the full list](docs/supported-libraries.md) and to learn more about -[disabled instrumentation](docs/supported-libraries.md#disabled-instrumentations) -and how to [suppress unwanted instrumentation][suppress]. - -## Creating agent extensions - -[Extensions](examples/extension/README.md) add new features and capabilities to the agent without having to create a separate distribution or to fork this repository. For example, you can create custom samplers or span exporters, set new defaults, and embed it all in the agent to obtain a single jar file. - -## Manually instrumenting - -For most users, the out-of-the-box instrumentation is completely sufficient and nothing more has to -be done. Sometimes, however, users wish to add attributes to the otherwise automatic spans, -or they might want to manually create spans for their own custom code. - -For detailed instructions, see [Manual instrumentation][manual]. - -## Logger MDC (Mapped Diagnostic Context) auto-instrumentation - -It is possible to inject trace information like trace IDs and span IDs into your -custom application logs. For details, see [Logger MDC -auto-instrumentation](docs/logger-mdc-instrumentation.md). - -## Troubleshooting - -To turn on the agent's internal debug logging: - -`-Dotel.javaagent.debug=true` - -**Note**: These logs are extremely verbose. Enable debug logging only when needed. -Debug logging negatively impacts the performance of your application. - -## Contributing - -See [CONTRIBUTING.md](CONTRIBUTING.md). - -Triagers ([@open-telemetry/java-instrumentation-triagers](https://github.com/orgs/open-telemetry/teams/java-instrumentation-triagers)): - -- [Jack Berg](https://github.com/jack-berg), New Relic -- [Jason Plumb](https://github.com/breedx-splk), Splunk - -Approvers ([@open-telemetry/java-instrumentation-approvers](https://github.com/orgs/open-telemetry/teams/java-instrumentation-approvers)): - -- [John Watson](https://github.com/jkwatson), Splunk -- [Pavol Loffay](https://github.com/pavolloffay), Traceable.ai - -Maintainers ([@open-telemetry/java-instrumentation-maintainers](https://github.com/orgs/open-telemetry/teams/java-instrumentation-maintainers)): - -- [Anuraag Agrawal](https://github.com/anuraaga), AWS -- [Lauri Tulmin](https://github.com/laurit), Splunk -- [Mateusz Rzeszutek](https://github.com/mateuszrzeszutek), Splunk -- [Nikita Salnikov-Tarnovski](https://github.com/iNikem), Splunk -- [Trask Stalnaker](https://github.com/trask), Microsoft - -Learn more about roles in the [community repository](https://github.com/open-telemetry/community/blob/main/community-membership.md). - -Thanks to all the people who already contributed! - - - - - -[config]: https://opentelemetry.io/docs/instrumentation/java/automatic/agent-config/ -[manual]: https://opentelemetry.io/docs/instrumentation/java/manual/ -[suppress]: https://opentelemetry.io/docs/instrumentation/java/automatic/agent-config/#suppressing-specific-auto-instrumentation +1. Bump the stable version in the following file: `version.gradle.kts`. +2. Open a PR (pray some more 🙏) and merge to main. +3. Trigger [Release](https://github.com/helios/opentelemetry-java-instrumentation/actions/workflows/release.yml) workflow in github. diff --git a/RELEASING.md b/RELEASING.md index 663d927b15bc..ee6964911b71 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -10,9 +10,16 @@ Every successful CI build of the main branch automatically executes `./gradlew p as the last step, which publishes a snapshot build to [Sonatype OSS snapshots repository](https://oss.sonatype.org/content/repositories/snapshots/io/opentelemetry/). +## Release cadence + +This repository roughly targets monthly minor releases from the `main` branch on the Wednesday after +the second Monday of the month (roughly a few of days after the monthly minor release of +[opentelemetry-java](https://github.com/open-telemetry/opentelemetry-java)). + ## Preparing a new major or minor release -* Close the release milestone if there is one. +* Close the [release milestone](https://github.com/open-telemetry/opentelemetry-java-instrumentation/milestones) + if there is one. * Merge a pull request to `main` updating the `CHANGELOG.md`. * The heading for the unreleased entries should be `## Unreleased`. * Use `.github/scripts/draft-change-log-entries.sh` as a starting point for writing the change log. @@ -34,6 +41,10 @@ and deadlocks. e.g. `release/v1.9.x`, then enter the pull request number that you want to backport, then click the "Run workflow" button below that. * Review and merge the backport pull request that it generates. + * Note: if the PR contains any changes to workflow files, it will have to be manually backported, + because the default `GITHUB_TOKEN` does not have permission to update workflow files (and the + `opentelemetrybot` token doesn't have write permission to this repository at all, so while it + can be used to open a PR, it can't be used to push to a local branch). * Merge a pull request to the release branch updating the `CHANGELOG.md`. * The heading for the unreleased entries should be `## Unreleased`. * Run the [Prepare patch release workflow](https://github.com/open-telemetry/opentelemetry-java-instrumentation/actions/workflows/prepare-patch-release.yml). @@ -52,6 +63,20 @@ and deadlocks. (note that if this is not a patch release then the change log on main may already be up-to-date, in which case no pull request will be created). +## Update release versions in documentations + +After releasing is done, you need to first update the docs. This needs to happen after artifacts have propagated +to Maven Central so should probably be done an hour or two after the release workflow finishes. + +```sh +./gradlew japicmp -PapiBaseVersion=a.b.c -PapiNewVersion=x.y.z +./gradlew --refresh-dependencies japicmp +``` + +Where `x.y.z` is the version just released and `a.b.c` is the previous version. + +Create a PR to mark the new release in docs on the main branch. + ## Credentials Same as the core repo, see [opentelemetry-java/RELEASING.md#credentials](https://github.com/open-telemetry/opentelemetry-java/blob/main/RELEASING.md#credentials). diff --git a/benchmark-overhead-jmh/src/jmh/java/io/opentelemetry/javaagent/benchmark/servlet/ServletWithNoopApiBenchmark.java b/benchmark-overhead-jmh/src/jmh/java/io/opentelemetry/javaagent/benchmark/servlet/ServletWithSdkDisabledBenchmark.java similarity index 56% rename from benchmark-overhead-jmh/src/jmh/java/io/opentelemetry/javaagent/benchmark/servlet/ServletWithNoopApiBenchmark.java rename to benchmark-overhead-jmh/src/jmh/java/io/opentelemetry/javaagent/benchmark/servlet/ServletWithSdkDisabledBenchmark.java index 4d36b97df237..79fc2d56bca6 100644 --- a/benchmark-overhead-jmh/src/jmh/java/io/opentelemetry/javaagent/benchmark/servlet/ServletWithNoopApiBenchmark.java +++ b/benchmark-overhead-jmh/src/jmh/java/io/opentelemetry/javaagent/benchmark/servlet/ServletWithSdkDisabledBenchmark.java @@ -7,5 +7,5 @@ import org.openjdk.jmh.annotations.Fork; -@Fork(jvmArgsAppend = "-Dotel.javaagent.experimental.use-noop-api=true") -public class ServletWithNoopApiBenchmark extends ServletBenchmark {} +@Fork(jvmArgsAppend = "-Dotel.experimental.sdk.enabled=false") +public class ServletWithSdkDisabledBenchmark extends ServletBenchmark {} diff --git a/benchmark-overhead/build.gradle.kts b/benchmark-overhead/build.gradle.kts index 72fd693890a9..7a131453e6c2 100644 --- a/benchmark-overhead/build.gradle.kts +++ b/benchmark-overhead/build.gradle.kts @@ -1,6 +1,6 @@ plugins { id("java") - id("com.diffplug.spotless") version "6.7.2" + id("com.diffplug.spotless") version "6.9.0" } spotless { @@ -18,13 +18,13 @@ repositories { dependencies { testImplementation("org.testcontainers:testcontainers:1.16.2") testImplementation("org.testcontainers:postgresql:1.15.3") - testImplementation("org.junit.jupiter:junit-jupiter-api:5.7.2") - testImplementation("org.junit.jupiter:junit-jupiter-params:5.7.2") + testImplementation("org.junit.jupiter:junit-jupiter-api:5.9.0") + testImplementation("org.junit.jupiter:junit-jupiter-params:5.9.0") testImplementation("com.squareup.okhttp3:okhttp:4.9.1") testImplementation("org.jooq:joox:1.6.2") testImplementation("com.jayway.jsonpath:json-path:2.6.0") - testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.7.2") - testImplementation("org.slf4j:slf4j-simple:1.7.36") + testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.9.0") + testImplementation("org.slf4j:slf4j-simple:2.0.0") } tasks { diff --git a/benchmark-overhead/gradle/wrapper/gradle-wrapper.jar b/benchmark-overhead/gradle/wrapper/gradle-wrapper.jar index 41d9927a4d4f..249e5832f090 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 aa991fceae6e..ae04661ee733 100644 --- a/benchmark-overhead/gradle/wrapper/gradle-wrapper.properties +++ b/benchmark-overhead/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/benchmark-overhead/gradlew b/benchmark-overhead/gradlew index 1b6c787337ff..a69d9cb6c206 100755 --- a/benchmark-overhead/gradlew +++ b/benchmark-overhead/gradlew @@ -205,6 +205,12 @@ set -- \ org.gradle.wrapper.GradleWrapperMain \ "$@" +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + # Use "xargs" to parse quoted args. # # With -n1 it outputs one arg per line, with the quotes and backslashes removed. diff --git a/benchmark-overhead/gradlew.bat b/benchmark-overhead/gradlew.bat index 107acd32c4e6..f127cfd49d40 100644 --- a/benchmark-overhead/gradlew.bat +++ b/benchmark-overhead/gradlew.bat @@ -14,7 +14,7 @@ @rem limitations under the License. @rem -@if "%DEBUG%" == "" @echo off +@if "%DEBUG%"=="" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @@ -25,7 +25,7 @@ if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. +if "%DIRNAME%"=="" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @@ -40,7 +40,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto execute +if %ERRORLEVEL% equ 0 goto execute echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. @@ -75,13 +75,15 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar :end @rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd +if %ERRORLEVEL% equ 0 goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% :mainEnd if "%OS%"=="Windows_NT" endlocal diff --git a/bom-alpha/build.gradle.kts b/bom-alpha/build.gradle.kts index 4f1030cb8d1d..7a6679640b65 100644 --- a/bom-alpha/build.gradle.kts +++ b/bom-alpha/build.gradle.kts @@ -1,7 +1,5 @@ plugins { - id("java-platform") - - id("otel.publish-conventions") + id("otel.bom-conventions") } description = "OpenTelemetry Instrumentation Bill of Materials (Alpha)" @@ -17,17 +15,7 @@ val otelVersion: String by project dependencies { api(platform("io.opentelemetry:opentelemetry-bom:${otelVersion}")) api(platform("io.opentelemetry:opentelemetry-bom-alpha:${otelVersion}-alpha")) + api(platform(project(":bom"))) } -dependencies { - constraints { - rootProject.subprojects { - val proj = this - if (!proj.name.startsWith("bom") && proj.name != "javaagent") { - proj.plugins.withId("maven-publish") { - api(proj) - } - } - } - } -} +otelBom.projectFilter.set { it.findProperty("otel.stable") != "true" } diff --git a/bom/build.gradle.kts b/bom/build.gradle.kts new file mode 100644 index 000000000000..7c5fe1a78f1b --- /dev/null +++ b/bom/build.gradle.kts @@ -0,0 +1,19 @@ +plugins { + id("otel.bom-conventions") +} + +description = "OpenTelemetry Instrumentation Bill of Materials" +group = "io.opentelemetry.instrumentation" +base.archivesName.set("opentelemetry-instrumentation-bom") + +javaPlatform { + allowDependencies() +} + +val otelVersion: String by project + +dependencies { + api(platform("io.opentelemetry:opentelemetry-bom:${otelVersion}")) +} + +otelBom.projectFilter.set { it.findProperty("otel.stable") == "true" } diff --git a/bom/gradle.properties b/bom/gradle.properties new file mode 100644 index 000000000000..45d64bec279d --- /dev/null +++ b/bom/gradle.properties @@ -0,0 +1 @@ +otel.stable=true diff --git a/buildscripts/checkstyle.xml b/buildscripts/checkstyle.xml index 531e77dc2c72..e1a6dc3f6637 100644 --- a/buildscripts/checkstyle.xml +++ b/buildscripts/checkstyle.xml @@ -321,10 +321,12 @@ + @@ -376,15 +378,15 @@ - - - - - - - + + + + + + + + + diff --git a/buildscripts/dependency-check-suppressions.xml b/buildscripts/dependency-check-suppressions.xml new file mode 100644 index 000000000000..694a465537d7 --- /dev/null +++ b/buildscripts/dependency-check-suppressions.xml @@ -0,0 +1,9 @@ + + + + + ^pkg:maven/io\.opentelemetry[./].* + ^CVE-.* + + diff --git a/conventions/build.gradle.kts b/conventions/build.gradle.kts index 6b862e9574e2..bfddbd649d51 100644 --- a/conventions/build.gradle.kts +++ b/conventions/build.gradle.kts @@ -1,7 +1,7 @@ plugins { `kotlin-dsl` // When updating, update below in dependencies too - id("com.diffplug.spotless") version "6.7.2" + id("com.diffplug.spotless") version "6.11.0" } spotless { @@ -11,7 +11,8 @@ spotless { target("src/**/*.java") } kotlinGradle { - ktlint().userData(mapOf("indent_size" to "2", "continuation_indent_size" to "2", "disabled_rules" to "no-wildcard-imports")) + // not sure why it's not using the indent settings from .editorconfig + ktlint().editorConfigOverride(mapOf("indent_size" to "2", "continuation_indent_size" to "2", "disabled_rules" to "no-wildcard-imports")) target("**/*.gradle.kts") } } @@ -19,9 +20,6 @@ spotless { repositories { mavenCentral() gradlePluginPortal() - maven { - url = uri("https://oss.sonatype.org/content/repositories/snapshots") - } } tasks.withType().configureEach { @@ -32,32 +30,34 @@ dependencies { implementation(gradleApi()) implementation(localGroovy()) - implementation("io.opentelemetry.instrumentation:gradle-plugins:1.9.1-alpha") + // dependencySubstitution is applied to this dependency (see seetings.gradle.kts) + implementation("io.opentelemetry.instrumentation:gradle-plugins") implementation("org.eclipse.aether:aether-connector-basic:1.1.0") implementation("org.eclipse.aether:aether-transport-http:1.1.0") implementation("org.apache.maven:maven-aether-provider:3.3.9") // When updating, update above in plugins too - implementation("com.diffplug.spotless:spotless-plugin-gradle:6.7.2") + implementation("com.diffplug.spotless:spotless-plugin-gradle:6.11.0") implementation("com.google.guava:guava:31.1-jre") implementation("gradle.plugin.com.google.protobuf:protobuf-gradle-plugin:0.8.18") implementation("gradle.plugin.com.github.johnrengelman:shadow:7.1.2") - implementation("org.ow2.asm:asm:9.3") - implementation("org.ow2.asm:asm-tree:9.3") + implementation("org.ow2.asm:asm:9.4") + implementation("org.ow2.asm:asm-tree:9.4") implementation("org.apache.httpcomponents:httpclient:4.5.13") - implementation("org.gradle:test-retry-gradle-plugin:1.3.1") - implementation("ru.vyarus:gradle-animalsniffer-plugin:1.5.4") + implementation("org.gradle:test-retry-gradle-plugin:1.4.1") + implementation("org.owasp:dependency-check-gradle:7.3.0") + implementation("ru.vyarus:gradle-animalsniffer-plugin:1.6.0") // When updating, also update dependencyManagement/build.gradle.kts - implementation("net.bytebuddy:byte-buddy-gradle-plugin:1.12.10") + implementation("net.bytebuddy:byte-buddy-gradle-plugin:1.12.18") implementation("gradle.plugin.io.morethan.jmhreport:gradle-jmh-report:0.9.0") - implementation("me.champeau.jmh:jmh-gradle-plugin:0.6.6") - implementation("net.ltgt.gradle:gradle-errorprone-plugin:2.0.2") - implementation("net.ltgt.gradle:gradle-nullaway-plugin:1.3.0") - implementation("me.champeau.gradle:japicmp-gradle-plugin:0.4.0") + implementation("me.champeau.jmh:jmh-gradle-plugin:0.6.8") + implementation("net.ltgt.gradle:gradle-errorprone-plugin:3.0.1") + implementation("net.ltgt.gradle:gradle-nullaway-plugin:1.4.0") + implementation("me.champeau.gradle:japicmp-gradle-plugin:0.4.1") - testImplementation(enforcedPlatform("org.junit:junit-bom:5.8.2")) + testImplementation(enforcedPlatform("org.junit:junit-bom:5.9.1")) testImplementation("org.junit.jupiter:junit-jupiter-api") testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine") - testImplementation("org.assertj:assertj-core:3.22.0") + testImplementation("org.assertj:assertj-core:3.23.1") } diff --git a/conventions/src/main/kotlin/io.opentelemetry.instrumentation.javaagent-instrumentation.gradle.kts b/conventions/src/main/kotlin/io.opentelemetry.instrumentation.javaagent-instrumentation.gradle.kts index 49b5350707c9..24a561e63d9d 100644 --- a/conventions/src/main/kotlin/io.opentelemetry.instrumentation.javaagent-instrumentation.gradle.kts +++ b/conventions/src/main/kotlin/io.opentelemetry.instrumentation.javaagent-instrumentation.gradle.kts @@ -8,7 +8,6 @@ dependencies { add("muzzleBootstrap", "io.opentelemetry.instrumentation:opentelemetry-instrumentation-api") add("muzzleBootstrap", "io.opentelemetry.instrumentation:opentelemetry-instrumentation-api-semconv") add("muzzleBootstrap", "io.opentelemetry.instrumentation:opentelemetry-instrumentation-annotations-support") - add("muzzleBootstrap", "io.opentelemetry.instrumentation:opentelemetry-instrumentation-appender-api-internal") add("muzzleTooling", "io.opentelemetry.javaagent:opentelemetry-javaagent-extension-api") add("muzzleTooling", "io.opentelemetry.javaagent:opentelemetry-javaagent-tooling") 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 106ae96a6d24..04a1af2e5e89 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 @@ -14,8 +14,6 @@ tasks.withType().configureEach { exclude("**/module-info.class") - // Prevents conflict with other SLF4J instances. Important for premain. - relocate("org.slf4j", "io.opentelemetry.javaagent.slf4j") // rewrite dependencies calling Logger.getLogger relocate("java.util.logging.Logger", "io.opentelemetry.javaagent.bootstrap.PatchLogger") diff --git a/conventions/src/main/kotlin/io/opentelemetry/instrumentation/gradle/OtelBomExtension.kt b/conventions/src/main/kotlin/io/opentelemetry/instrumentation/gradle/OtelBomExtension.kt new file mode 100644 index 000000000000..85029753de39 --- /dev/null +++ b/conventions/src/main/kotlin/io/opentelemetry/instrumentation/gradle/OtelBomExtension.kt @@ -0,0 +1,14 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.gradle + +import org.gradle.api.Project +import org.gradle.api.provider.Property +import java.util.function.Predicate + +abstract class OtelBomExtension { + abstract val projectFilter: Property> +} diff --git a/conventions/src/main/kotlin/otel.bom-conventions.gradle.kts b/conventions/src/main/kotlin/otel.bom-conventions.gradle.kts new file mode 100644 index 000000000000..f863bd539e24 --- /dev/null +++ b/conventions/src/main/kotlin/otel.bom-conventions.gradle.kts @@ -0,0 +1,35 @@ +import io.opentelemetry.instrumentation.gradle.OtelBomExtension + +plugins { + id("otel.publish-conventions") + id("java-platform") +} + +if (!project.name.startsWith("bom")) { + throw IllegalStateException("Name of BOM projects must start with 'bom'.") +} + +rootProject.subprojects.forEach { subproject -> + if (!subproject.name.startsWith("bom")) { + evaluationDependsOn(subproject.path) + } +} +val otelBom = extensions.create("otelBom") + +afterEvaluate { + otelBom.projectFilter.finalizeValue() + val bomProjects = rootProject.subprojects + .sortedBy { it.findProperty("archivesName") as String? } + .filter { !it.name.startsWith("bom") } + .filter { !it.name.equals("javaagent") } + .filter(otelBom.projectFilter.get()::test) + .filter { it.plugins.hasPlugin("maven-publish") } + + bomProjects.forEach { project -> + dependencies { + constraints { + api(project) + } + } + } +} diff --git a/conventions/src/main/kotlin/otel.errorprone-conventions.gradle.kts b/conventions/src/main/kotlin/otel.errorprone-conventions.gradle.kts index e87e341bddfe..d66ec3d6ac84 100644 --- a/conventions/src/main/kotlin/otel.errorprone-conventions.gradle.kts +++ b/conventions/src/main/kotlin/otel.errorprone-conventions.gradle.kts @@ -23,15 +23,9 @@ tasks { disableWarningsInGeneratedCode.set(true) allDisabledChecksAsWarnings.set(true) + // Ignore warnings for generated and vendored classes excludedPaths.set(".*/build/generated/.*|.*/concurrentlinkedhashmap/.*") - // Still Java 8 - disable("Varifier") - - // We often override a method returning Iterable which this makes tedious - // for questionable value. - disable("PreferredInterfaceType") - // it's very convenient to debug stuff in the javaagent using System.out.println // and we don't want to conditionally only check this in CI // because then the remote gradle cache won't work for local builds @@ -40,42 +34,63 @@ tasks { disable("BooleanParameter") + // We often override a method returning Iterable which this makes tedious + // for questionable value. + disable("PreferredInterfaceType") + // Doesn't work well with Java 8 disable("FutureReturnValueIgnored") - // Require Guava + // Still Java 8 + disable("Varifier") + + // Doesn't currently use Var annotations. + disable("Var") // "-Xep:Var:OFF" + + // ImmutableRefactoring suggests using com.google.errorprone.annotations.Immutable, + // but currently uses javax.annotation.concurrent.Immutable + disable("ImmutableRefactoring") + + // AutoValueImmutableFields suggests returning Guava types from API methods disable("AutoValueImmutableFields") - disable("StringSplitter") + // Suggests using Guava types for fields but we don't use Guava disable("ImmutableMemberCollection") // Fully qualified names may be necessary when deprecating a class to avoid // deprecation warning. disable("UnnecessarilyFullyQualified") - // Don't currently use this (to indicate a local variable that's mutated) but could - // consider for future. - disable("Var") - - // We use animal sniffer - disable("AndroidJdkLibsChecker") + // TODO (trask) use animal sniffer disable("Java7ApiChecker") disable("Java8ApiChecker") + disable("AndroidJdkLibsChecker") - // Prevents defensive null checks and we have nullaway anyways - disable("ParameterMissingNullable") + // apparently disabling android doesn't disable this + disable("StaticOrDefaultInterfaceMethod") - // javax.annotation.Nullable doesn't support type parameter assertions - disable("VoidMissingNullable") + // We don't depend on Guava so use normal splitting + disable("StringSplitter") + + // Prevents lazy initialization + disable("InitializeInline") + + // Seems to trigger even when a deprecated method isn't called anywhere. + // We don't get much benefit from it anyways. + disable("InlineMeSuggester") + + disable("DoNotCallSuggester") - // Overlaps with nullaway + // We have nullaway so don't need errorprone nullable checks which have more false positives. disable("FieldMissingNullable") + disable("ParameterMissingNullable") disable("ReturnMissingNullable") + disable("VoidMissingNullable") - disable("StaticOrDefaultInterfaceMethod") + // allow UPPERCASE type parameter names + disable("TypeParameterNaming") - // Great check, but for bytecode manipulation it's too common to separate over - // onEnter / onExit - // TODO(anuraaga): Only disable for auto instrumentation project. + // In bytecode instrumentation it's very common to separate across onEnter / onExit + // TODO(anuraaga): Only disable for javaagent instrumentation modules. disable("MustBeClosedChecker") // Common to avoid an allocation. Revisit if it's worth opt-in suppressing instead of @@ -90,20 +105,15 @@ tasks { // some moving. disable("DefaultPackage") - // TODO(anuraaga): Remove this, all our advice classes miss constructors but probably should - // address this. + // we use modified OtelPrivateConstructorForUtilityClass which ignores *Advice classes disable("PrivateConstructorForUtilityClass") // TODO(anuraaga): Remove this, probably after instrumenter API migration instead of dealing // with older APIs. disable("InconsistentOverloads") - disable("TypeParameterNaming") - - // We don't use tools that recognize. - disable("InlineMeSuggester") - disable("DoNotCallSuggester") if (name.contains("Jmh") || name.contains("Test")) { + // Allow underscore in test-type method names disable("MemberName") } } diff --git a/conventions/src/main/kotlin/otel.jacoco-conventions.gradle.kts b/conventions/src/main/kotlin/otel.jacoco-conventions.gradle.kts index b43d25043b9d..2ff70b43ba52 100644 --- a/conventions/src/main/kotlin/otel.jacoco-conventions.gradle.kts +++ b/conventions/src/main/kotlin/otel.jacoco-conventions.gradle.kts @@ -3,7 +3,7 @@ plugins { } jacoco { - toolVersion = "0.8.7" + toolVersion = "0.8.8" } tasks { diff --git a/conventions/src/main/kotlin/otel.japicmp-conventions.gradle.kts b/conventions/src/main/kotlin/otel.japicmp-conventions.gradle.kts index a211767095b5..941ebc40cfee 100644 --- a/conventions/src/main/kotlin/otel.japicmp-conventions.gradle.kts +++ b/conventions/src/main/kotlin/otel.japicmp-conventions.gradle.kts @@ -1,4 +1,8 @@ +import japicmp.model.JApiChangeStatus import me.champeau.gradle.japicmp.JapicmpTask +import me.champeau.gradle.japicmp.report.stdrules.RecordSeenMembersSetup +import me.champeau.gradle.japicmp.report.stdrules.SourceCompatibleRule +import me.champeau.gradle.japicmp.report.stdrules.UnchangedMemberRule plugins { base @@ -43,36 +47,61 @@ fun findArtifact(version: String): File { } } -tasks { - val jApiCmp by registering(JapicmpTask::class) { - dependsOn("jar") +// generate the api diff report for any module that is stable +if (project.findProperty("otel.stable") == "true") { + afterEvaluate { + tasks { + val jApiCmp by registering(JapicmpTask::class) { + dependsOn("jar") - // the japicmp "new" version is either the user-specified one, or the locally built jar. - val apiNewVersion: String? by project - val newArtifact = apiNewVersion?.let { findArtifact(it) } - ?: file(getByName("jar").archiveFile) - newClasspath.from(files(newArtifact)) + // the japicmp "new" version is either the user-specified one, or the locally built jar. + val apiNewVersion: String? by project + val newArtifact = apiNewVersion?.let { findArtifact(it) } + ?: file(getByName("jar").archiveFile) + newClasspath.from(files(newArtifact)) - // only output changes, not everything - onlyModified.set(true) + // only output changes, not everything + onlyModified.set(true) - // the japicmp "old" version is either the user-specified one, or the latest release. - val apiBaseVersion: String? by project - val baselineVersion = apiBaseVersion ?: latestReleasedVersion - oldClasspath.from(try { - files(findArtifact(baselineVersion)) - } catch (e: Exception) { - // if we can't find the baseline artifact, this is probably one that's never been published before, - // so publish the whole API. We do that by flipping this flag, and comparing the current against nothing. - onlyModified.set(false) - files() - }) + // the japicmp "old" version is either the user-specified one, or the latest release. + val apiBaseVersion: String? by project + val baselineVersion = apiBaseVersion ?: latestReleasedVersion + oldClasspath.from( + try { + files(findArtifact(baselineVersion)) + } catch (e: Exception) { + // if we can't find the baseline artifact, this is probably one that's never been published before, + // so publish the whole API. We do that by flipping this flag, and comparing the current against nothing. + onlyModified.set(false) + files() + } + ) - // this is needed so that we only consider the current artifact, and not dependencies - ignoreMissingClasses.set(true) - packageExcludes.addAll("*.internal", "*.internal.*") - val baseVersionString = if (apiBaseVersion == null) "latest" else baselineVersion - val newVersionString = if (apiNewVersion == null) "current" else apiNewVersion - txtOutputFile.set(file("$rootDir/docs/apidiffs/${newVersionString}_vs_$baseVersionString/${base.archivesName.get()}.txt")) + // Reproduce defaults from https://github.com/melix/japicmp-gradle-plugin/blob/09f52739ef1fccda6b4310cf3f4b19dc97377024/src/main/java/me/champeau/gradle/japicmp/report/ViolationsGenerator.java#L130 + // only changing the BinaryIncompatibleRule to our custom one that allows new default methods + // on interfaces, and adding default implementations to interface methods previously + // abstract. + richReport { + addSetupRule(RecordSeenMembersSetup::class.java) + addRule(JApiChangeStatus.NEW, SourceCompatibleRule::class.java) + addRule(JApiChangeStatus.MODIFIED, SourceCompatibleRule::class.java) + addRule(JApiChangeStatus.UNCHANGED, UnchangedMemberRule::class.java) + addRule(SourceCompatibleRule::class.java) + } + + // this is needed so that we only consider the current artifact, and not dependencies + ignoreMissingClasses.set(true) + packageExcludes.addAll("*.internal", "*.internal.*") + val baseVersionString = if (apiBaseVersion == null) "latest" else baselineVersion + txtOutputFile.set( + apiNewVersion?.let { file("$rootDir/docs/apidiffs/${apiNewVersion}_vs_$baselineVersion/${base.archivesName.get()}.txt") } + ?: file("$rootDir/docs/apidiffs/current_vs_$baseVersionString/${base.archivesName.get()}.txt") + ) + } + // have the jApiCmp task run every time the jar task is run, to make it more likely it will get used. + named("jar") { + finalizedBy(jApiCmp) + } + } } } diff --git a/conventions/src/main/kotlin/otel.java-conventions.gradle.kts b/conventions/src/main/kotlin/otel.java-conventions.gradle.kts index b05a41ba9e9f..c68c6795a619 100644 --- a/conventions/src/main/kotlin/otel.java-conventions.gradle.kts +++ b/conventions/src/main/kotlin/otel.java-conventions.gradle.kts @@ -13,6 +13,7 @@ plugins { id("otel.errorprone-conventions") id("otel.spotless-conventions") + id("org.owasp.dependencycheck") } val otelJava = extensions.create("otelJava") @@ -58,7 +59,7 @@ tasks.withType().configureEach { "-Xlint:-options", // Fail build on any warning - "-Werror" +// "-Werror" ) ) } @@ -124,6 +125,7 @@ dependencies { components.all() compileOnly("com.google.code.findbugs:jsr305") + compileOnly("com.google.errorprone:error_prone_annotations") codenarc("org.codenarc:CodeNarc:2.2.0") codenarc(platform("org.codehaus.groovy:groovy-bom:3.0.9")) @@ -136,6 +138,7 @@ testing { implementation("org.junit.jupiter:junit-jupiter-params") runtimeOnly("org.junit.jupiter:junit-jupiter-engine") runtimeOnly("org.junit.vintage:junit-vintage-engine") + implementation("org.junit-pioneer:junit-pioneer") implementation("org.assertj:assertj-core") @@ -201,11 +204,14 @@ tasks { charSet = "UTF-8" breakIterator(true) - links("https://docs.oracle.com/javase/8/docs/api/") + // TODO (trask) revisit to see if url is fixed + // currently broken because https://docs.oracle.com/javase/8/docs/api/element-list is missing + // and redirects + // links("https://docs.oracle.com/javase/8/docs/api/") addStringOption("Xdoclint:none", "-quiet") // non-standard option to fail on warnings, see https://bugs.openjdk.java.net/browse/JDK-8200363 - addStringOption("Xwerror", "-quiet") +// addStringOption("Xwerror", "-quiet") } } @@ -234,7 +240,7 @@ fun isJavaVersionAllowed(version: JavaVersion): Boolean { if (otelJava.minJavaVersionSupported.get().compareTo(version) > 0) { return false } - if (otelJava.maxJavaVersionForTests.isPresent() && otelJava.maxJavaVersionForTests.get().compareTo(version) < 0) { + if (otelJava.maxJavaVersionForTests.isPresent && otelJava.maxJavaVersionForTests.get().compareTo(version) < 0) { return false } return true @@ -350,10 +356,16 @@ checkstyle { maxWarnings = 0 } +dependencyCheck { + skipConfigurations = listOf("errorprone", "checkstyle", "annotationProcessor") + suppressionFile = "buildscripts/dependency-check-suppressions.xml" + failBuildOnCVSS = 7.0f // fail on high or critical CVE +} + idea { module { - setDownloadJavadoc(false) - setDownloadSources(false) + isDownloadJavadoc = false + isDownloadSources = false } } @@ -364,7 +376,7 @@ when (projectDir.name) { // https://github.com/gradle/gradle/issues/847 // In otel.publish-conventions, we set the maven group, which is what matters, to the correct // value. - group = "io.opentelemetry.${projectDir.parentFile.name}" + group = "io.opentelemetry.dummy.${projectDir.parentFile.name}" } } @@ -378,7 +390,6 @@ configurations.configureEach { substitute(module("io.opentelemetry.instrumentation:opentelemetry-instrumentation-api-semconv")).using(project(":instrumentation-api-semconv")) substitute(module("io.opentelemetry.instrumentation:opentelemetry-instrumentation-annotations")).using(project(":instrumentation-annotations")) substitute(module("io.opentelemetry.instrumentation:opentelemetry-instrumentation-annotations-support")).using(project(":instrumentation-annotations-support")) - substitute(module("io.opentelemetry.instrumentation:opentelemetry-instrumentation-appender-api-internal")).using(project(":instrumentation-appender-api-internal")) substitute(module("io.opentelemetry.javaagent:opentelemetry-javaagent-bootstrap")).using(project(":javaagent-bootstrap")) substitute(module("io.opentelemetry.javaagent:opentelemetry-javaagent-extension-api")).using(project(":javaagent-extension-api")) substitute(module("io.opentelemetry.javaagent:opentelemetry-javaagent-tooling")).using(project(":javaagent-tooling")) diff --git a/conventions/src/main/kotlin/otel.protobuf-conventions.gradle.kts b/conventions/src/main/kotlin/otel.protobuf-conventions.gradle.kts index 2e40a53a29f9..1ef7ab8febb5 100644 --- a/conventions/src/main/kotlin/otel.protobuf-conventions.gradle.kts +++ b/conventions/src/main/kotlin/otel.protobuf-conventions.gradle.kts @@ -10,10 +10,18 @@ protobuf { protoc { // The artifact spec for the Protobuf Compiler artifact = "com.google.protobuf:protoc:3.3.0" + if (osdetector.os == "osx") { + // Always use x86_64 version as ARM binary is not available + artifact += ":osx-x86_64" + } } plugins { id("grpc") { artifact = "io.grpc:protoc-gen-grpc-java:1.6.0" + if (osdetector.os == "osx") { + // Always use x86_64 version as ARM binary is not available + artifact += ":osx-x86_64" + } } } generateProtoTasks { diff --git a/conventions/src/main/kotlin/otel.publish-conventions.gradle.kts b/conventions/src/main/kotlin/otel.publish-conventions.gradle.kts index d75b78cc916b..74fa89c13e38 100644 --- a/conventions/src/main/kotlin/otel.publish-conventions.gradle.kts +++ b/conventions/src/main/kotlin/otel.publish-conventions.gradle.kts @@ -26,8 +26,10 @@ publishing { } artifactId = artifactPrefix(project, base.archivesName.get()) + base.archivesName.get() - if (!groupId.startsWith("io.opentelemetry.")) { - throw GradleException("groupId is not set for this project or its parent ${project.parent}") + if (groupId != "io.opentelemetry.instrumentation" + && groupId != "io.opentelemetry.javaagent" + && groupId != "io.opentelemetry.javaagent.instrumentation") { + throw GradleException("Unexpected groupId for this project or its parent ${project.parent}: $groupId") } pom.description.set( diff --git a/conventions/src/main/kotlin/otel.scala-conventions.gradle.kts b/conventions/src/main/kotlin/otel.scala-conventions.gradle.kts index e37235db06c6..e52d1e00d9aa 100644 --- a/conventions/src/main/kotlin/otel.scala-conventions.gradle.kts +++ b/conventions/src/main/kotlin/otel.scala-conventions.gradle.kts @@ -9,7 +9,7 @@ dependencies { } tasks { - // Gradle sets scala compiler version to toolchain vesion, not target version + // Gradle sets scala compiler version to toolchain version, not target version // https://github.com/gradle/gradle/issues/18211 withType().configureEach { scalaCompileOptions.apply { diff --git a/conventions/src/main/kotlin/otel.sdk-extension.gradle.kts b/conventions/src/main/kotlin/otel.sdk-extension.gradle.kts new file mode 100644 index 000000000000..c1390adcf9ea --- /dev/null +++ b/conventions/src/main/kotlin/otel.sdk-extension.gradle.kts @@ -0,0 +1,14 @@ +// SDK extensions are very similar to library instrumentations, they can be used without the javaagent +// but since they depend on the SDK they must be loaded by the agent CL in the javaagent + +plugins { + id("io.opentelemetry.instrumentation.library-instrumentation") + + id("otel.jacoco-conventions") + id("otel.java-conventions") + id("otel.publish-conventions") +} + +extra["mavenGroupId"] = "io.opentelemetry.instrumentation" + +base.archivesName.set(projectDir.parentFile.name) diff --git a/conventions/src/main/kotlin/otel.spotless-conventions.gradle.kts b/conventions/src/main/kotlin/otel.spotless-conventions.gradle.kts index 726f0a12a1e8..d39638112ad0 100644 --- a/conventions/src/main/kotlin/otel.spotless-conventions.gradle.kts +++ b/conventions/src/main/kotlin/otel.spotless-conventions.gradle.kts @@ -25,14 +25,14 @@ spotless { } plugins.withId("org.jetbrains.kotlin.jvm") { kotlin { - ktlint().userData(mapOf("continuation_indent_size" to "2", "disabled_rules" to "no-wildcard-imports")) - .editorConfigOverride(mapOf("indent_size" to "2")) // not sure why it's not using the setting from .editorconfig + // not sure why it's not using the indent settings from .editorconfig + ktlint().editorConfigOverride(mapOf("indent_size" to "2", "continuation_indent_size" to "2", "disabled_rules" to "no-wildcard-imports,package-name")) licenseHeaderFile(rootProject.file("buildscripts/spotless.license.java"), "(package|import|class|// Includes work from:)") } } kotlinGradle { - ktlint().userData(mapOf("continuation_indent_size" to "2", "disabled_rules" to "no-wildcard-imports")) - .editorConfigOverride(mapOf("indent_size" to "2")) // not sure why it's not using the setting from .editorconfig + // not sure why it's not using the indent settings from .editorconfig + ktlint().editorConfigOverride(mapOf("indent_size" to "2", "continuation_indent_size" to "2", "disabled_rules" to "no-wildcard-imports")) } format("misc") { // not using "**/..." to help keep spotless fast diff --git a/custom-checks/build.gradle.kts b/custom-checks/build.gradle.kts index 4c3d7063b6bf..2a3fb92b1447 100644 --- a/custom-checks/build.gradle.kts +++ b/custom-checks/build.gradle.kts @@ -33,10 +33,14 @@ tasks { compilerArgs.addAll( listOf( - "--add-exports", "jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED", - "--add-exports", "jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED", - "--add-exports", "jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED", - "--add-exports", "jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED" + "--add-exports", + "jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED", + "--add-exports", + "jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED", + "--add-exports", + "jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED", + "--add-exports", + "jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED" ) ) } diff --git a/custom-checks/src/main/java/io/opentelemetry/javaagent/customchecks/OtelPrivateConstructorForUtilityClass.java b/custom-checks/src/main/java/io/opentelemetry/javaagent/customchecks/OtelPrivateConstructorForUtilityClass.java new file mode 100644 index 000000000000..de6586ab36f5 --- /dev/null +++ b/custom-checks/src/main/java/io/opentelemetry/javaagent/customchecks/OtelPrivateConstructorForUtilityClass.java @@ -0,0 +1,43 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.customchecks; + +import static com.google.errorprone.BugPattern.SeverityLevel.WARNING; +import static com.google.errorprone.matchers.Description.NO_MATCH; + +import com.google.auto.service.AutoService; +import com.google.errorprone.BugPattern; +import com.google.errorprone.VisitorState; +import com.google.errorprone.bugpatterns.BugChecker; +import com.google.errorprone.bugpatterns.PrivateConstructorForUtilityClass; +import com.google.errorprone.matchers.Description; +import com.sun.source.tree.ClassTree; + +@AutoService(BugChecker.class) +@BugPattern( + summary = + "Classes which are not intended to be instantiated should be made non-instantiable with a private constructor. This includes utility classes (classes with only static members), and the main class.", + severity = WARNING) +public class OtelPrivateConstructorForUtilityClass extends BugChecker + implements BugChecker.ClassTreeMatcher { + + private static final long serialVersionUID = 1L; + + private final PrivateConstructorForUtilityClass delegate = + new PrivateConstructorForUtilityClass(); + + @Override + public Description matchClass(ClassTree tree, VisitorState state) { + if (tree.getSimpleName().toString().endsWith("Advice")) { + return NO_MATCH; + } + Description description = delegate.matchClass(tree, state); + if (description == NO_MATCH) { + return description; + } + return describeMatch(tree); + } +} diff --git a/dependencyManagement/build.gradle.kts b/dependencyManagement/build.gradle.kts index e31f350befab..f22ad859a49f 100644 --- a/dependencyManagement/build.gradle.kts +++ b/dependencyManagement/build.gradle.kts @@ -11,11 +11,13 @@ data class DependencySet(val group: String, val version: String, val modules: Li val dependencyVersions = hashMapOf() rootProject.extra["versions"] = dependencyVersions -val otelVersion = "1.15.0" +// this line is managed by .github/scripts/update-sdk-version.sh +val otelVersion = "1.19.0" + rootProject.extra["otelVersion"] = otelVersion // Need both BOM and groovy jars -val groovyVersion = "4.0.2" +val groovyVersion = "4.0.6" // We don't force libraries we instrument to new versions since we compile and test against specific // old baseline versions but we do try to force those libraries' transitive dependencies to new @@ -29,63 +31,50 @@ val groovyVersion = "4.0.2" // configurations.testRuntimeClasspath.resolutionStrategy.force "com.google.guava:guava:19.0" val DEPENDENCY_BOMS = listOf( - "com.fasterxml.jackson:jackson-bom:2.13.2.20220328", + "com.fasterxml.jackson:jackson-bom:2.13.4.20221013", "com.google.guava:guava-bom:31.1-jre", "org.apache.groovy:groovy-bom:${groovyVersion}", "io.opentelemetry:opentelemetry-bom:${otelVersion}", "io.opentelemetry:opentelemetry-bom-alpha:${otelVersion}-alpha", - "org.junit:junit-bom:5.8.2", - "org.testcontainers:testcontainers-bom:1.17.1", + "org.junit:junit-bom:5.9.1", + "org.testcontainers:testcontainers-bom:1.17.5", + "org.spockframework:spock-bom:2.3-groovy-4.0" ) -val DEPENDENCY_SETS = listOf( - DependencySet( - "com.google.auto.service", - "1.0.1", - listOf("auto-service", "auto-service-annotations") - ), - DependencySet( - "com.google.auto.value", - "1.9", - listOf("auto-value", "auto-value-annotations") - ), - DependencySet( - "com.google.errorprone", - "2.14.0", - listOf("error_prone_annotations", "error_prone_core", "error_prone_test_helpers") - ), - DependencySet( - "net.bytebuddy", - // When updating, also update conventions/build.gradle.kts - "1.12.10", - listOf("byte-buddy", "byte-buddy-dep", "byte-buddy-agent", "byte-buddy-gradle-plugin") - ), - DependencySet( - "org.openjdk.jmh", - "1.35", - listOf("jmh-core", "jmh-generator-bytecode") - ), - DependencySet( - "org.mockito", - "4.5.1", - listOf("mockito-core", "mockito-junit-jupiter", "mockito-inline") - ), - DependencySet( - "org.slf4j", - "1.7.36", - listOf("slf4j-api", "slf4j-simple", "log4j-over-slf4j", "jcl-over-slf4j", "jul-to-slf4j") - ), +val CORE_DEPENDENCIES = listOf( + "com.google.auto.service:auto-service:1.0.1", + "com.google.auto.service:auto-service-annotations:1.0.1", + "com.google.auto.value:auto-value:1.10", + "com.google.auto.value:auto-value-annotations:1.10", + "com.google.errorprone:error_prone_annotations:2.16", + "com.google.errorprone:error_prone_core:2.16", + "com.google.errorprone:error_prone_test_helpers:2.16", + // When updating, also update conventions/build.gradle.kts + "net.bytebuddy:byte-buddy:1.12.18", + "net.bytebuddy:byte-buddy-dep:1.12.18", + "net.bytebuddy:byte-buddy-agent:1.12.18", + "net.bytebuddy:byte-buddy-gradle-plugin:1.12.18", + "org.openjdk.jmh:jmh-core:1.35", + "org.openjdk.jmh:jmh-generator-bytecode:1.35", + "org.mockito:mockito-core:4.8.1", + "org.mockito:mockito-junit-jupiter:4.8.1", + "org.mockito:mockito-inline:4.8.1", + "org.slf4j:slf4j-api:2.0.2", + "org.slf4j:slf4j-simple:2.0.2", + "org.slf4j:log4j-over-slf4j:2.0.2", + "org.slf4j:jcl-over-slf4j:2.0.2", + "org.slf4j:jul-to-slf4j:2.0.2" ) // See the comment above about why we keep this rather large list. // There are dependencies included here that appear to have no usages, but are maintained at // this top level to help consistently satisfy large numbers of transitive dependencies. val DEPENDENCIES = listOf( - "ch.qos.logback:logback-classic:1.2.11", + "ch.qos.logback:logback-classic:1.3.1", // 1.4+ requires Java 11+ "com.github.stefanbirkner:system-lambda:1.2.1", "com.github.stefanbirkner:system-rules:1.19.0", "uk.org.webcompere:system-stubs-jupiter:2.0.1", - "com.uber.nullaway:nullaway:0.9.7", + "com.uber.nullaway:nullaway:0.10.2", "commons-beanutils:commons-beanutils:1.9.4", "commons-cli:commons-cli:1.5.0", "commons-codec:commons-codec:1.15", @@ -97,20 +86,21 @@ val DEPENDENCIES = listOf( "commons-logging:commons-logging:1.2", "commons-validator:commons-validator:1.7", "io.netty:netty:3.10.6.Final", - "io.opentelemetry.proto:opentelemetry-proto:0.17.0-alpha", - "org.assertj:assertj-core:3.22.0", + "io.opentelemetry.proto:opentelemetry-proto:0.19.0-alpha", + "org.assertj:assertj-core:3.23.1", "org.awaitility:awaitility:4.2.0", "com.google.code.findbugs:annotations:3.0.1u2", "com.google.code.findbugs:jsr305:3.0.2", "org.apache.groovy:groovy:${groovyVersion}", "org.apache.groovy:groovy-json:${groovyVersion}", - "org.junit-pioneer:junit-pioneer:1.7.0", - "org.objenesis:objenesis:3.2", - "org.spockframework:spock-core:2.2-M1-groovy-4.0", - "org.spockframework:spock-junit4:2.2-M1-groovy-4.0", + "org.codehaus.mojo:animal-sniffer-annotations:1.22", + "org.junit-pioneer:junit-pioneer:1.7.1", + "org.objenesis:objenesis:3.3", "org.scala-lang:scala-library:2.11.12", // Note that this is only referenced as "org.springframework.boot" in build files, not the artifact name. - "org.springframework.boot:spring-boot-dependencies:2.3.1.RELEASE" + "org.springframework.boot:spring-boot-dependencies:2.7.5", + "javax.validation:validation-api:2.0.1.Final", + "org.yaml:snakeyaml:1.33" ) javaPlatform { @@ -124,11 +114,10 @@ dependencies { dependencyVersions[split[0]] = split[2] } constraints { - for (set in DEPENDENCY_SETS) { - for (module in set.modules) { - api("${set.group}:${module}:${set.version}") - dependencyVersions[set.group] = set.version - } + for (dependency in CORE_DEPENDENCIES) { + api(dependency) + val split = dependency.split(':') + dependencyVersions[split[0]] = split[2] } for (dependency in DEPENDENCIES) { api(dependency) diff --git a/docs/apidiffs/1.18.0_vs_1.17.0/opentelemetry-instrumentation-api.txt b/docs/apidiffs/1.18.0_vs_1.17.0/opentelemetry-instrumentation-api.txt new file mode 100644 index 000000000000..e97761e16a45 --- /dev/null +++ b/docs/apidiffs/1.18.0_vs_1.17.0/opentelemetry-instrumentation-api.txt @@ -0,0 +1,106 @@ +Comparing source compatibility of against ++++ NEW INTERFACE: PUBLIC(+) ABSTRACT(+) io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor (not serializable) + +++ CLASS FILE FORMAT VERSION: 52.0 <- n.a. + +++ NEW SUPERCLASS: java.lang.Object + +++ NEW METHOD: PUBLIC(+) STATIC(+) io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor constant(io.opentelemetry.api.common.AttributeKey, java.lang.Object) + +++ NEW METHOD: PUBLIC(+) ABSTRACT(+) void onEnd(io.opentelemetry.api.common.AttributesBuilder, io.opentelemetry.context.Context, java.lang.Object, java.lang.Object, java.lang.Throwable) + +++ NEW METHOD: PUBLIC(+) ABSTRACT(+) void onStart(io.opentelemetry.api.common.AttributesBuilder, io.opentelemetry.context.Context, java.lang.Object) ++++ NEW INTERFACE: PUBLIC(+) ABSTRACT(+) io.opentelemetry.instrumentation.api.instrumenter.ContextCustomizer (not serializable) + +++ CLASS FILE FORMAT VERSION: 52.0 <- n.a. + +++ NEW SUPERCLASS: java.lang.Object + +++ NEW METHOD: PUBLIC(+) ABSTRACT(+) io.opentelemetry.context.Context onStart(io.opentelemetry.context.Context, java.lang.Object, io.opentelemetry.api.common.Attributes) + +++ NEW ANNOTATION: java.lang.FunctionalInterface ++++ NEW INTERFACE: PUBLIC(+) ABSTRACT(+) io.opentelemetry.instrumentation.api.instrumenter.ErrorCauseExtractor (not serializable) + +++ CLASS FILE FORMAT VERSION: 52.0 <- n.a. + +++ NEW SUPERCLASS: java.lang.Object + +++ NEW METHOD: PUBLIC(+) ABSTRACT(+) java.lang.Throwable extract(java.lang.Throwable) + +++ NEW METHOD: PUBLIC(+) STATIC(+) io.opentelemetry.instrumentation.api.instrumenter.ErrorCauseExtractor getDefault() + +++ NEW ANNOTATION: java.lang.FunctionalInterface ++++ NEW CLASS: PUBLIC(+) io.opentelemetry.instrumentation.api.instrumenter.Instrumenter (not serializable) + +++ CLASS FILE FORMAT VERSION: 52.0 <- n.a. + +++ NEW SUPERCLASS: java.lang.Object + +++ NEW METHOD: PUBLIC(+) STATIC(+) io.opentelemetry.instrumentation.api.instrumenter.InstrumenterBuilder builder(io.opentelemetry.api.OpenTelemetry, java.lang.String, io.opentelemetry.instrumentation.api.instrumenter.SpanNameExtractor) + +++ NEW METHOD: PUBLIC(+) void end(io.opentelemetry.context.Context, java.lang.Object, java.lang.Object, java.lang.Throwable) + +++ NEW METHOD: PUBLIC(+) boolean shouldStart(io.opentelemetry.context.Context, java.lang.Object) + +++ NEW METHOD: PUBLIC(+) io.opentelemetry.context.Context start(io.opentelemetry.context.Context, java.lang.Object) ++++ NEW CLASS: PUBLIC(+) FINAL(+) io.opentelemetry.instrumentation.api.instrumenter.InstrumenterBuilder (not serializable) + +++ CLASS FILE FORMAT VERSION: 52.0 <- n.a. + +++ NEW SUPERCLASS: java.lang.Object + +++ NEW METHOD: PUBLIC(+) io.opentelemetry.instrumentation.api.instrumenter.InstrumenterBuilder addAttributesExtractor(io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor) + +++ NEW METHOD: PUBLIC(+) io.opentelemetry.instrumentation.api.instrumenter.InstrumenterBuilder addAttributesExtractors(java.lang.Iterable) + +++ NEW METHOD: PUBLIC(+) io.opentelemetry.instrumentation.api.instrumenter.InstrumenterBuilder addContextCustomizer(io.opentelemetry.instrumentation.api.instrumenter.ContextCustomizer) + +++ NEW METHOD: PUBLIC(+) io.opentelemetry.instrumentation.api.instrumenter.InstrumenterBuilder addOperationListener(io.opentelemetry.instrumentation.api.instrumenter.OperationListener) + +++ NEW METHOD: PUBLIC(+) io.opentelemetry.instrumentation.api.instrumenter.InstrumenterBuilder addOperationMetrics(io.opentelemetry.instrumentation.api.instrumenter.OperationMetrics) + +++ NEW METHOD: PUBLIC(+) io.opentelemetry.instrumentation.api.instrumenter.InstrumenterBuilder addSpanLinksExtractor(io.opentelemetry.instrumentation.api.instrumenter.SpanLinksExtractor) + +++ NEW METHOD: PUBLIC(+) io.opentelemetry.instrumentation.api.instrumenter.Instrumenter buildClientInstrumenter(io.opentelemetry.context.propagation.TextMapSetter) + +++ NEW METHOD: PUBLIC(+) io.opentelemetry.instrumentation.api.instrumenter.Instrumenter buildConsumerInstrumenter(io.opentelemetry.context.propagation.TextMapGetter) + +++ NEW METHOD: PUBLIC(+) io.opentelemetry.instrumentation.api.instrumenter.Instrumenter buildInstrumenter() + +++ NEW METHOD: PUBLIC(+) io.opentelemetry.instrumentation.api.instrumenter.Instrumenter buildInstrumenter(io.opentelemetry.instrumentation.api.instrumenter.SpanKindExtractor) + +++ NEW METHOD: PUBLIC(+) io.opentelemetry.instrumentation.api.instrumenter.Instrumenter buildProducerInstrumenter(io.opentelemetry.context.propagation.TextMapSetter) + +++ NEW METHOD: PUBLIC(+) io.opentelemetry.instrumentation.api.instrumenter.Instrumenter buildServerInstrumenter(io.opentelemetry.context.propagation.TextMapGetter) + +++ NEW METHOD: PUBLIC(+) io.opentelemetry.instrumentation.api.instrumenter.InstrumenterBuilder setEnabled(boolean) + +++ NEW METHOD: PUBLIC(+) io.opentelemetry.instrumentation.api.instrumenter.InstrumenterBuilder setErrorCauseExtractor(io.opentelemetry.instrumentation.api.instrumenter.ErrorCauseExtractor) + +++ NEW METHOD: PUBLIC(+) io.opentelemetry.instrumentation.api.instrumenter.InstrumenterBuilder setInstrumentationVersion(java.lang.String) + +++ NEW METHOD: PUBLIC(+) io.opentelemetry.instrumentation.api.instrumenter.InstrumenterBuilder setSchemaUrl(java.lang.String) + +++ NEW METHOD: PUBLIC(+) io.opentelemetry.instrumentation.api.instrumenter.InstrumenterBuilder setSpanStatusExtractor(io.opentelemetry.instrumentation.api.instrumenter.SpanStatusExtractor) ++++ NEW CLASS: PUBLIC(+) FINAL(+) io.opentelemetry.instrumentation.api.instrumenter.LocalRootSpan (not serializable) + +++ CLASS FILE FORMAT VERSION: 52.0 <- n.a. + +++ NEW SUPERCLASS: java.lang.Object + +++ NEW METHOD: PUBLIC(+) STATIC(+) io.opentelemetry.api.trace.Span current() + +++ NEW METHOD: PUBLIC(+) STATIC(+) io.opentelemetry.api.trace.Span fromContext(io.opentelemetry.context.Context) + +++ NEW METHOD: PUBLIC(+) STATIC(+) io.opentelemetry.api.trace.Span fromContextOrNull(io.opentelemetry.context.Context) + +++ NEW ANNOTATION: javax.annotation.Nullable ++++ NEW INTERFACE: PUBLIC(+) ABSTRACT(+) io.opentelemetry.instrumentation.api.instrumenter.OperationListener (not serializable) + +++ CLASS FILE FORMAT VERSION: 52.0 <- n.a. + +++ NEW SUPERCLASS: java.lang.Object + +++ NEW METHOD: PUBLIC(+) ABSTRACT(+) void onEnd(io.opentelemetry.context.Context, io.opentelemetry.api.common.Attributes, long) + +++ NEW METHOD: PUBLIC(+) ABSTRACT(+) io.opentelemetry.context.Context onStart(io.opentelemetry.context.Context, io.opentelemetry.api.common.Attributes, long) ++++ NEW INTERFACE: PUBLIC(+) ABSTRACT(+) io.opentelemetry.instrumentation.api.instrumenter.OperationMetrics (not serializable) + +++ CLASS FILE FORMAT VERSION: 52.0 <- n.a. + +++ NEW SUPERCLASS: java.lang.Object + +++ NEW METHOD: PUBLIC(+) ABSTRACT(+) io.opentelemetry.instrumentation.api.instrumenter.OperationListener create(io.opentelemetry.api.metrics.Meter) + +++ NEW ANNOTATION: java.lang.FunctionalInterface ++++ NEW INTERFACE: PUBLIC(+) ABSTRACT(+) io.opentelemetry.instrumentation.api.instrumenter.SpanKindExtractor (not serializable) + +++ CLASS FILE FORMAT VERSION: 52.0 <- n.a. + +++ NEW SUPERCLASS: java.lang.Object + +++ NEW METHOD: PUBLIC(+) STATIC(+) io.opentelemetry.instrumentation.api.instrumenter.SpanKindExtractor alwaysClient() + +++ NEW METHOD: PUBLIC(+) STATIC(+) io.opentelemetry.instrumentation.api.instrumenter.SpanKindExtractor alwaysConsumer() + +++ NEW METHOD: PUBLIC(+) STATIC(+) io.opentelemetry.instrumentation.api.instrumenter.SpanKindExtractor alwaysInternal() + +++ NEW METHOD: PUBLIC(+) STATIC(+) io.opentelemetry.instrumentation.api.instrumenter.SpanKindExtractor alwaysProducer() + +++ NEW METHOD: PUBLIC(+) STATIC(+) io.opentelemetry.instrumentation.api.instrumenter.SpanKindExtractor alwaysServer() + +++ NEW METHOD: PUBLIC(+) ABSTRACT(+) io.opentelemetry.api.trace.SpanKind extract(java.lang.Object) + +++ NEW ANNOTATION: java.lang.FunctionalInterface ++++ NEW INTERFACE: PUBLIC(+) ABSTRACT(+) io.opentelemetry.instrumentation.api.instrumenter.SpanLinksBuilder (not serializable) + +++ CLASS FILE FORMAT VERSION: 52.0 <- n.a. + +++ NEW SUPERCLASS: java.lang.Object + +++ NEW METHOD: PUBLIC(+) ABSTRACT(+) io.opentelemetry.instrumentation.api.instrumenter.SpanLinksBuilder addLink(io.opentelemetry.api.trace.SpanContext) + +++ NEW METHOD: PUBLIC(+) ABSTRACT(+) io.opentelemetry.instrumentation.api.instrumenter.SpanLinksBuilder addLink(io.opentelemetry.api.trace.SpanContext, io.opentelemetry.api.common.Attributes) ++++ NEW INTERFACE: PUBLIC(+) ABSTRACT(+) io.opentelemetry.instrumentation.api.instrumenter.SpanLinksExtractor (not serializable) + +++ CLASS FILE FORMAT VERSION: 52.0 <- n.a. + +++ NEW SUPERCLASS: java.lang.Object + +++ NEW METHOD: PUBLIC(+) ABSTRACT(+) void extract(io.opentelemetry.instrumentation.api.instrumenter.SpanLinksBuilder, io.opentelemetry.context.Context, java.lang.Object) + +++ NEW ANNOTATION: java.lang.FunctionalInterface ++++ NEW INTERFACE: PUBLIC(+) ABSTRACT(+) io.opentelemetry.instrumentation.api.instrumenter.SpanNameExtractor (not serializable) + +++ CLASS FILE FORMAT VERSION: 52.0 <- n.a. + +++ NEW SUPERCLASS: java.lang.Object + +++ NEW METHOD: PUBLIC(+) ABSTRACT(+) java.lang.String extract(java.lang.Object) + +++ NEW ANNOTATION: java.lang.FunctionalInterface ++++ NEW INTERFACE: PUBLIC(+) ABSTRACT(+) io.opentelemetry.instrumentation.api.instrumenter.SpanStatusBuilder (not serializable) + +++ CLASS FILE FORMAT VERSION: 52.0 <- n.a. + +++ NEW SUPERCLASS: java.lang.Object + +++ NEW METHOD: PUBLIC(+) io.opentelemetry.instrumentation.api.instrumenter.SpanStatusBuilder setStatus(io.opentelemetry.api.trace.StatusCode) + +++ NEW METHOD: PUBLIC(+) ABSTRACT(+) io.opentelemetry.instrumentation.api.instrumenter.SpanStatusBuilder setStatus(io.opentelemetry.api.trace.StatusCode, java.lang.String) ++++ NEW INTERFACE: PUBLIC(+) ABSTRACT(+) io.opentelemetry.instrumentation.api.instrumenter.SpanStatusExtractor (not serializable) + +++ CLASS FILE FORMAT VERSION: 52.0 <- n.a. + +++ NEW SUPERCLASS: java.lang.Object + +++ NEW METHOD: PUBLIC(+) ABSTRACT(+) void extract(io.opentelemetry.instrumentation.api.instrumenter.SpanStatusBuilder, java.lang.Object, java.lang.Object, java.lang.Throwable) + +++ NEW METHOD: PUBLIC(+) STATIC(+) io.opentelemetry.instrumentation.api.instrumenter.SpanStatusExtractor getDefault() + +++ NEW ANNOTATION: java.lang.FunctionalInterface ++++ NEW CLASS: PUBLIC(+) ABSTRACT(+) io.opentelemetry.instrumentation.api.util.VirtualField (not serializable) + +++ CLASS FILE FORMAT VERSION: 52.0 <- n.a. + +++ NEW SUPERCLASS: java.lang.Object + +++ NEW CONSTRUCTOR: PUBLIC(+) VirtualField() + +++ NEW METHOD: PUBLIC(+) STATIC(+) io.opentelemetry.instrumentation.api.util.VirtualField find(java.lang.Class, java.lang.Class) + +++ NEW METHOD: PUBLIC(+) ABSTRACT(+) java.lang.Object get(java.lang.Object) + +++ NEW ANNOTATION: javax.annotation.Nullable + +++ NEW METHOD: PUBLIC(+) ABSTRACT(+) void set(java.lang.Object, java.lang.Object) diff --git a/docs/apidiffs/1.19.0_vs_1.18.0/opentelemetry-instrumentation-api.txt b/docs/apidiffs/1.19.0_vs_1.18.0/opentelemetry-instrumentation-api.txt new file mode 100644 index 000000000000..df26146497bf --- /dev/null +++ b/docs/apidiffs/1.19.0_vs_1.18.0/opentelemetry-instrumentation-api.txt @@ -0,0 +1,2 @@ +Comparing source compatibility of against +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 new file mode 100644 index 000000000000..df26146497bf --- /dev/null +++ b/docs/apidiffs/current_vs_latest/opentelemetry-instrumentation-api.txt @@ -0,0 +1,2 @@ +Comparing source compatibility of against +No changes. \ No newline at end of file diff --git a/docs/contributing/intellij-setup-and-troubleshooting.md b/docs/contributing/intellij-setup-and-troubleshooting.md index 270707194d79..f4c03e2403df 100644 --- a/docs/contributing/intellij-setup-and-troubleshooting.md +++ b/docs/contributing/intellij-setup-and-troubleshooting.md @@ -16,16 +16,8 @@ Configuration: ![enable google format](https://user-images.githubusercontent.com/5099946/131759832-36437aa0-a5f7-42c0-9425-8c5b45c16765.png) - -## [Save Actions](https://plugins.jetbrains.com/plugin/7642-save-actions) - -Installation: - -![save action](https://user-images.githubusercontent.com/5099946/131758940-7a1820db-3cf4-4e30-b346-c45c1ff4646e.png) - -Configuration: - -![Recommended Settings](save-actions.png) +Note: If google-java-format generates errors in Intellij, +see https://github.com/google/google-java-format/issues/787#issuecomment-1200762464. ## Troubleshooting diff --git a/docs/contributing/javaagent-structure.md b/docs/contributing/javaagent-structure.md index 007ad8a80265..90271abc0868 100644 --- a/docs/contributing/javaagent-structure.md +++ b/docs/contributing/javaagent-structure.md @@ -38,9 +38,6 @@ The bootstrap class loader contains several modules: * **The `instrumentation-annotations-support` module**: it contains classes that provide support for annotation-based auto-instrumentation, e.g. the `@WithSpan` annotation. This module is internal and its APIs are considered unstable. -* **The `instrumentation-appender-api-internal` module**: - it contains classes that constitute the "appender API", used by logging instrumentations. This - module is internal and its APIs are considered unstable. * **The `io.opentelemetry.javaagent.bootstrap` package from the `javaagent-extension-api` module**: this package contains several instrumentation utilities that are only usable when an application is instrumented with the javaagent; for example, the `Java8BytecodeBridge` that should be used diff --git a/docs/contributing/repository-settings.md b/docs/contributing/repository-settings.md index bd5f83d28853..812f4aad6c49 100644 --- a/docs/contributing/repository-settings.md +++ b/docs/contributing/repository-settings.md @@ -1,12 +1,17 @@ # Repository settings -(In addition to https://github.com/open-telemetry/community/blob/main/docs/how-to-configure-new-repository.md) +(In addition +to https://github.com/open-telemetry/community/blob/main/docs/how-to-configure-new-repository.md) -## General +## General > Pull Requests + +* Allow squash merging > Default to pull request title and description + +* Allow auto-merge * Automatically delete head branches: CHECKED - So that bot PR branches will be deleted. + So automation PR branches will be deleted. ## Actions > General @@ -18,9 +23,10 @@ ## Branch protections -(In addition to https://github.com/open-telemetry/community/blob/main/docs/how-to-configure-new-repository.md) +(In addition +to https://github.com/open-telemetry/community/blob/main/docs/how-to-configure-new-repository.md) -### `main` and `release/*` +### `main` * Require branches to be up to date before merging: UNCHECKED @@ -31,14 +37,13 @@ * EasyCLA * required-status-check -### `v*` (old release branches) +### `release/*` -Same settings as above for new release branches (`release/**`), except: +Same settings as above for `main`, except: -* Status checks that are required: +* Restrict pushes that create matching branches: UNCHECKED - * EasyCLA - * accept-pr + So release automation can create release branches. ### `gh-pages` @@ -48,12 +53,26 @@ Same settings as above for new release branches (`release/**`), except: [Nightly overhead benchmark](https://github.com/open-telemetry/opentelemetry-java-instrumentation/actions/workflows/nightly-benchmark-overhead.yml) job. -### `**/**` +### `dependabot/**/**` and `opentelemetrybot/*` -* Status checks that are required: +* Require status checks to pass before merging: UNCHECKED - EasyCLA + So bots can rebase their PR branches + +* Restrict who can push to matching branches: UNCHECKED + + So bots can create PR branches in the first place + +* Allow force pushes > Everyone + + So bots can rebase their PR branches * Allow deletions: CHECKED - So that bot PR branches can be deleted + So bot PR branches can be deleted after merging + +### `**/**` + +* Status checks that are required: + + EasyCLA diff --git a/docs/contributing/running-tests.md b/docs/contributing/running-tests.md index 323f4866bcda..f95d1c9b9bf7 100644 --- a/docs/contributing/running-tests.md +++ b/docs/contributing/running-tests.md @@ -54,3 +54,9 @@ If you are on Windows and you want to run the tests using linux containers: ``` USE_LINUX_CONTAINERS=1 ./gradlew :smoke-tests:test -PsmokeTestSuite=payara ``` + +## Docker disk space + +Some of the instrumentation tests (and all of the smoke tests) spin up docker containers via +[testcontainers](https://www.testcontainers.org/). If you run out of space, you may wish to prune +old containers, images and volumes using `docker system prune --volumes`. diff --git a/docs/contributing/style-guideline.md b/docs/contributing/style-guideline.md index 8f647164a6c3..2b54b53fa20b 100644 --- a/docs/contributing/style-guideline.md +++ b/docs/contributing/style-guideline.md @@ -126,3 +126,11 @@ plugins { id("otel.nullaway-conventions") } ``` + +## 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. + +It is ok to use `Optional` in places where it does not leak into public API signatures and where +performance is not critical. diff --git a/docs/contributing/using-instrumenter-api.md b/docs/contributing/using-instrumenter-api.md index bd2bd4ce9037..9b3005f9df60 100644 --- a/docs/contributing/using-instrumenter-api.md +++ b/docs/contributing/using-instrumenter-api.md @@ -349,7 +349,7 @@ class MyOperationMetrics implements OperationListener { MyOperationMetrics(Meter meter) { activeRequests = meter .upDownCounterBuilder("mylib.active_requests") - .setUnit("requests") + .setUnit("{requests}") .build(); } diff --git a/docs/misc/inter-thread-context-propagation.md b/docs/misc/inter-thread-context-propagation.md index b12aaf9fa2f9..0c25a569a902 100644 --- a/docs/misc/inter-thread-context-propagation.md +++ b/docs/misc/inter-thread-context-propagation.md @@ -8,10 +8,10 @@ Executor pool = Executors.newFixedThreadPool(10) public void doGet(HttpServletRequest request, HttpServletResponse response) { Future f1 = pool.submit(() -> { - return userRepository.queryShippingAddress(requet) + return userRepository.queryShippingAddress(request) }) Future f2 = pool.submit(() -> { - return warehouse.currentState(requet) + return warehouse.currentState(request) }) writeResponse(response, f1.get(), f2.get()) } @@ -23,7 +23,7 @@ Executor pool = Executors.newFixedThreadPool(10) public void doGet(HttpServletRequest request, HttpServletResponse response) { final AsyncContext acontext = request.startAsync(); acontext.start(() -> { - String address = userRepository.queryShippingAddress(requet) + String address = userRepository.queryShippingAddress(request) HttpServletResponse response = acontext.getResponse(); writeResponse(response, address) acontext.complete(); @@ -52,7 +52,7 @@ on that thread for the duration of the execution. This can be illustrated by the ``` var job = () -> { try(Scope scope = this.context.makeCurrent()) { - return userRepository.queryShippingAddress(requet) + return userRepository.queryShippingAddress(request) }} job.context = Context.current() Future f1 = pool.submit() diff --git a/docs/standalone-library-instrumentation.md b/docs/standalone-library-instrumentation.md index 8372a4e67bbb..a823682856c3 100644 --- a/docs/standalone-library-instrumentation.md +++ b/docs/standalone-library-instrumentation.md @@ -4,6 +4,7 @@ This repository also publishes standalone instrumentation for several libraries that can be used if you prefer that over using the Java agent: * [Apache Dubbo](../instrumentation/apache-dubbo-2.7/library-autoconfigure) +* [Apache Kafka](../instrumentation/kafka/kafka-clients/kafka-clients-2.6/library) * [Armeria](../instrumentation/armeria-1.3/library) * [AWS Lambda](../instrumentation/aws-lambda/aws-lambda-core-1.0/library) * [AWS SDK 1.11](../instrumentation/aws-sdk/aws-sdk-1.11/library) @@ -24,18 +25,12 @@ that can be used if you prefer that over using the Java agent: * [Oracle UCP](../instrumentation/oracle-ucp-11.2/library) * [OSHI](../instrumentation/oshi/library) * [Reactor](../instrumentation/reactor/reactor-3.1/library) -* [RocketMQ](../instrumentation/rocketmq-client-4.8/library) +* [RocketMQ](../instrumentation/rocketmq/rocketmq-client/rocketmq-client-4.8/library) * [Runtime metrics](../instrumentation/runtime-metrics/library) * [RxJava 1.0](../instrumentation/rxjava/rxjava-1.0/library) * [RxJava 2.0](../instrumentation/rxjava/rxjava-2.0/library) * [RxJava 3.0](../instrumentation/rxjava/rxjava-3.0/library) * [Spring RestTemplate](../instrumentation/spring/spring-web-3.1/library) -* [Spring Web MVC](../instrumentation/spring/spring-webmvc-3.1/library) +* [Spring Web MVC](../instrumentation/spring/spring-webmvc-5.3/library) * [Spring WebFlux Client](../instrumentation/spring/spring-webflux-5.0/library) * [Vibur DBCP](../instrumentation/vibur-dbcp-11.0/library) - -And some libraries are publishing their own OpenTelemetry instrumentation (yay!), e.g. - -* [http4k](https://www.http4k.org/guide/reference/opentelemetry/) - -If you know of additional libraries that are publishing their own OpenTelemetry instrumentation, please let us know! diff --git a/docs/supported-libraries.md b/docs/supported-libraries.md index 633aebf0b534..41e3c3694754 100644 --- a/docs/supported-libraries.md +++ b/docs/supported-libraries.md @@ -17,109 +17,111 @@ or [contributing](../CONTRIBUTING.md). These are the supported libraries and frameworks: -| Library/Framework | Versions | -|-----------------------------------------------------------------------------------------------------------------------------------|--------------------------------| -| [Akka Actors](https://doc.akka.io/docs/akka/current/typed/index.html) | 2.5+ | -| [Akka HTTP](https://doc.akka.io/docs/akka-http/current/index.html) | 10.0+ | -| [Apache Axis2](https://axis.apache.org/axis2/java/core/) | 1.6+ | -| [Apache Camel](https://camel.apache.org/) | 2.20+ (not including 3.x yet) | -| [Apache CXF JAX-RS](https://cxf.apache.org/) | 3.2+ | -| [Apache CXF JAX-RS Client](https://cxf.apache.org/) | 3.0+ | -| [Apache CXF JAX-WS](https://cxf.apache.org/) | 3.0+ | -| [Apache Dubbo](https://github.com/apache/dubbo/) | 2.7+ | -| [Apache HttpAsyncClient](https://hc.apache.org/index.html) | 4.1+ | -| [Apache HttpClient](https://hc.apache.org/index.html) | 2.0+ | -| [Apache Kafka Producer/Consumer API](https://kafka.apache.org/documentation/#producerapi) | 0.11+ | -| [Apache Kafka Streams API](https://kafka.apache.org/documentation/streams/) | 0.11+ | -| [Apache MyFaces](https://myfaces.apache.org/) | 1.2+ (not including 3.x yet) | -| [Apache RocketMQ](https://rocketmq.apache.org/) | 4.8+ | -| [Apache Struts 2](https://github.com/apache/struts) | 2.3+ | -| [Apache Tapestry](https://tapestry.apache.org/) | 5.4+ | -| [Apache Wicket](https://wicket.apache.org/) | 8.0+ | -| [Armeria](https://armeria.dev) | 1.3+ | -| [AsyncHttpClient](https://github.com/AsyncHttpClient/async-http-client) | 1.9+ | -| [AWS Lambda](https://docs.aws.amazon.com/lambda/latest/dg/java-handler.html) | 1.0+ | -| [AWS SDK](https://aws.amazon.com/sdk-for-java/) | 1.11.x and 2.2.0+ | -| [Azure Core](https://docs.microsoft.com/en-us/java/api/overview/azure/core-readme) | 1.14+ | -| [Cassandra Driver](https://github.com/datastax/java-driver) | 3.0+ | -| [Couchbase Client](https://github.com/couchbase/couchbase-java-client) | 2.0+ and 3.1+ | -| [Dropwizard Views](https://www.dropwizard.io/en/latest/manual/views.html) | 0.7+ | -| [Eclipse Grizzly](https://javaee.github.io/grizzly/httpserverframework.html) | 2.0+ (disabled by default) | -| [Eclipse Jersey](https://eclipse-ee4j.github.io/jersey/) | 2.0+ (not including 3.x yet) | -| [Eclipse Jetty HTTP Client](https://www.eclipse.org/jetty/javadoc/jetty-9/org/eclipse/jetty/client/HttpClient.html) | 9.2+ (not including 10+ yet) | -| [Eclipse Metro](https://projects.eclipse.org/projects/ee4j.metro) | 2.2+ (not including 3.x yet) | -| [Eclipse Mojarra](https://projects.eclipse.org/projects/ee4j.mojarra) | 1.2+ (not including 3.x yet) | -| [Elasticsearch API](https://www.elastic.co/guide/en/elasticsearch/client/java-api/current/index.html) | 5.0+ | -| [Elasticsearch REST Client](https://www.elastic.co/guide/en/elasticsearch/client/java-rest/current/index.html) | 5.0+ | -| [Finatra](https://github.com/twitter/finatra) | 2.9+ | -| [Geode Client](https://geode.apache.org/) | 1.4+ | -| [Google HTTP Client](https://github.com/googleapis/google-http-java-client) | 1.19+ | -| [Grails](https://grails.org/) | 3.0+ | -| [GraphQL Java](https://www.graphql-java.com/) | 12.0+ | -| [gRPC](https://github.com/grpc/grpc-java) | 1.6+ | -| [Guava ListenableFuture](https://guava.dev/releases/snapshot/api/docs/com/google/common/util/concurrent/ListenableFuture.html) | 10.0+ | -| [GWT](http://www.gwtproject.org/) | 2.0+ | -| [Hibernate](https://github.com/hibernate/hibernate-orm) | 3.3+ | -| [HikariCP](https://github.com/brettwooldridge/HikariCP) | 3.0+ | -| [HttpURLConnection](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/net/HttpURLConnection.html) | Java 8+ | -| [Hystrix](https://github.com/Netflix/Hystrix) | 1.4+ | -| [Java Executors](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/Executor.html) | Java 8+ | -| [Java Http Client](https://docs.oracle.com/en/java/javase/11/docs/api/java.net.http/java/net/http/package-summary.html) | Java 11+ | -| [java.util.logging](https://docs.oracle.com/javase/8/docs/api/java/util/logging/package-summary.html) | Java 8+ | -| [JAX-RS](https://javaee.github.io/javaee-spec/javadocs/javax/ws/rs/package-summary.html) | 0.5+ | -| [JAX-RS Client](https://javaee.github.io/javaee-spec/javadocs/javax/ws/rs/client/package-summary.html) | 1.1+ | -| [JAX-WS](https://jakarta.ee/specifications/xml-web-services/2.3/apidocs/javax/xml/ws/package-summary.html) | 2.0+ (not including 3.x yet) | -| [JDBC](https://docs.oracle.com/javase/8/docs/api/java/sql/package-summary.html) | Java 8+ | -| [Jedis](https://github.com/xetorthio/jedis) | 1.4+ | -| [JMS](https://javaee.github.io/javaee-spec/javadocs/javax/jms/package-summary.html) | 1.1+ | -| [JSP](https://javaee.github.io/javaee-spec/javadocs/javax/servlet/jsp/package-summary.html) | 2.3+ | -| [Kotlin Coroutines](https://kotlinlang.org/docs/coroutines-overview.html) | 1.0+ | -| [Kubernetes Client](https://github.com/kubernetes-client/java) | 7.0+ | -| [Lettuce](https://github.com/lettuce-io/lettuce-core) | 4.0+ | -| [Log4j 1](https://logging.apache.org/log4j/1.2/) | 1.2+ | -| [Log4j 2](https://logging.apache.org/log4j/2.x/) | 2.11+ | -| [Logback](http://logback.qos.ch/) | 1.0+ | -| [Micrometer](https://micrometer.io/) | 1.5+ | -| [MongoDB Driver](https://mongodb.github.io/mongo-java-driver/) | 3.1+ | -| [Netty](https://github.com/netty/netty) | 3.8+ | -| [OkHttp](https://github.com/square/okhttp/) | 2.2+ | -| [Oracle UCP](https://docs.oracle.com/database/121/JJUCP/) | 11.2+ | -| [OSHI](https://github.com/oshi/oshi/) | 5.3.1+ | -| [Play](https://github.com/playframework/playframework) | 2.4+ | -| [Play WS](https://github.com/playframework/play-ws) | 1.0+ | -| [Quartz](https://www.quartz-scheduler.org/) | 2.0+ | -| [RabbitMQ Client](https://github.com/rabbitmq/rabbitmq-java-client) | 2.7+ | -| [Ratpack](https://github.com/ratpack/ratpack) | 1.4+ | -| [Reactor](https://github.com/reactor/reactor-core) | 3.1+ | -| [Reactor Netty](https://github.com/reactor/reactor-netty) | 0.9+ | -| [Rediscala](https://github.com/etaty/rediscala) | 1.8+ | -| [Redisson](https://github.com/redisson/redisson) | 3.0+ | -| [RESTEasy](https://resteasy.github.io/) | 3.0+ | -| [Restlet](https://restlet.talend.com/) | 1.0+ | -| [RMI](https://docs.oracle.com/en/java/javase/11/docs/api/java.rmi/java/rmi/package-summary.html) | Java 8+ | -| [RxJava](https://github.com/ReactiveX/RxJava) | 1.0+ | -| [Scala ForkJoinPool](https://www.scala-lang.org/api/2.12.0/scala/concurrent/forkjoin/package$$ForkJoinPool$.html) | 2.8+ | -| [Servlet](https://javaee.github.io/javaee-spec/javadocs/javax/servlet/package-summary.html) | 2.2+ | -| [Spark Web Framework](https://github.com/perwendel/spark) | 2.3+ | -| [Spring Batch](https://spring.io/projects/spring-batch) | 3.0+ | -| [Spring Data](https://spring.io/projects/spring-data) | 1.8+ | -| [Spring Integration](https://spring.io/projects/spring-integration) | 4.1+ | -| [Spring Kafka](https://spring.io/projects/spring-kafka) | 2.7+ | -| [Spring RabbitMQ](https://spring.io/projects/spring-amqp) | 1.0+ | -| [Spring Scheduling](https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/scheduling/package-summary.html) | 3.1+ | -| [Spring Web MVC](https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/servlet/mvc/package-summary.html) | 3.1+ | -| [Spring Web Services](https://spring.io/projects/spring-ws) | 2.0+ | -| [Spring WebFlux](https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/reactive/package-summary.html) | 5.0+ | -| [Spymemcached](https://github.com/couchbase/spymemcached) | 2.12+ | -| [Tomcat JDBC Pool](https://tomcat.apache.org/tomcat-7.0-doc/jdbc-pool.html) | 8.5.0+ | -| [Twilio](https://github.com/twilio/twilio-java) | 6.6+ (not including 8.x yet) | -| [Undertow](https://undertow.io/) | 1.4+ | -| [Vaadin](https://vaadin.com/) | 14.2+ | -| [Vert.x Web](https://vertx.io/docs/vertx-web/java/) | 3.0+ | -| [Vert.x HttpClient](https://vertx.io/docs/apidocs/io/vertx/core/http/HttpClient.html) | 3.0+ | -| [Vert.x Kafka Client](https://vertx.io/docs/vertx-kafka-client/java/) | 3.6+ | -| [Vert.x RxJava2](https://vertx.io/docs/vertx-rx/java2/) | 3.5+ | -| [Vibur DBCP](https://www.vibur.org/) | 11.0+ | +| Library/Framework | Versions | +|-----------------------------------------------------------------------------------------------------------------------------------|-------------------------------| +| [Akka Actors](https://doc.akka.io/docs/akka/current/typed/index.html) | 2.5+ | +| [Akka HTTP](https://doc.akka.io/docs/akka-http/current/index.html) | 10.0+ | +| [Apache Axis2](https://axis.apache.org/axis2/java/core/) | 1.6+ | +| [Apache Camel](https://camel.apache.org/) | 2.20+ (not including 3.x yet) | +| [Apache CXF JAX-RS](https://cxf.apache.org/) | 3.2+ | +| [Apache CXF JAX-RS Client](https://cxf.apache.org/) | 3.0+ | +| [Apache CXF JAX-WS](https://cxf.apache.org/) | 3.0+ | +| [Apache Dubbo](https://github.com/apache/dubbo/) | 2.7+ | +| [Apache HttpAsyncClient](https://hc.apache.org/index.html) | 4.1+ | +| [Apache HttpClient](https://hc.apache.org/index.html) | 2.0+ | +| [Apache Kafka Producer/Consumer API](https://kafka.apache.org/documentation/#producerapi) | 0.11+ | +| [Apache Kafka Streams API](https://kafka.apache.org/documentation/streams/) | 0.11+ | +| [Apache MyFaces](https://myfaces.apache.org/) | 1.2+ (not including 3.x yet) | +| [Apache RocketMQ](https://rocketmq.apache.org/) | 4.8+ | +| [Apache Struts 2](https://github.com/apache/struts) | 2.3+ | +| [Apache Tapestry](https://tapestry.apache.org/) | 5.4+ | +| [Apache Wicket](https://wicket.apache.org/) | 8.0+ | +| [Armeria](https://armeria.dev) | 1.3+ | +| [AsyncHttpClient](https://github.com/AsyncHttpClient/async-http-client) | 1.9+ | +| [AWS Lambda](https://docs.aws.amazon.com/lambda/latest/dg/java-handler.html) | 1.0+ | +| [AWS SDK](https://aws.amazon.com/sdk-for-java/) | 1.11.x and 2.2.0+ | +| [Azure Core](https://docs.microsoft.com/en-us/java/api/overview/azure/core-readme) | 1.14+ | +| [Cassandra Driver](https://github.com/datastax/java-driver) | 3.0+ | +| [Couchbase Client](https://github.com/couchbase/couchbase-java-client) | 2.0+ and 3.1+ | +| [Dropwizard Metrics](https://metrics.dropwizard.io/) | 4.0+ (disabled by default) | +| [Dropwizard Views](https://www.dropwizard.io/en/latest/manual/views.html) | 0.7+ | +| [Eclipse Grizzly](https://javaee.github.io/grizzly/httpserverframework.html) | 2.0+ (disabled by default) | +| [Eclipse Jersey](https://eclipse-ee4j.github.io/jersey/) | 2.0+ (not including 3.x yet) | +| [Eclipse Jetty HTTP Client](https://www.eclipse.org/jetty/javadoc/jetty-9/org/eclipse/jetty/client/HttpClient.html) | 9.2+ (not including 10+ yet) | +| [Eclipse Metro](https://projects.eclipse.org/projects/ee4j.metro) | 2.2+ (not including 3.x yet) | +| [Eclipse Mojarra](https://projects.eclipse.org/projects/ee4j.mojarra) | 1.2+ (not including 3.x yet) | +| [Elasticsearch API](https://www.elastic.co/guide/en/elasticsearch/client/java-api/current/index.html) | 5.0+ | +| [Elasticsearch REST Client](https://www.elastic.co/guide/en/elasticsearch/client/java-rest/current/index.html) | 5.0+ | +| [Finatra](https://github.com/twitter/finatra) | 2.9+ | +| [Geode Client](https://geode.apache.org/) | 1.4+ | +| [Google HTTP Client](https://github.com/googleapis/google-http-java-client) | 1.19+ | +| [Grails](https://grails.org/) | 3.0+ | +| [GraphQL Java](https://www.graphql-java.com/) | 12.0+ | +| [gRPC](https://github.com/grpc/grpc-java) | 1.6+ | +| [Guava ListenableFuture](https://guava.dev/releases/snapshot/api/docs/com/google/common/util/concurrent/ListenableFuture.html) | 10.0+ | +| [GWT](http://www.gwtproject.org/) | 2.0+ | +| [Hibernate](https://github.com/hibernate/hibernate-orm) | 3.3+ (not including 6.x yet) | +| [HikariCP](https://github.com/brettwooldridge/HikariCP) | 3.0+ | +| [HttpURLConnection](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/net/HttpURLConnection.html) | Java 8+ | +| [Hystrix](https://github.com/Netflix/Hystrix) | 1.4+ | +| [Java Executors](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/Executor.html) | Java 8+ | +| [Java Http Client](https://docs.oracle.com/en/java/javase/11/docs/api/java.net.http/java/net/http/package-summary.html) | Java 11+ | +| [java.util.logging](https://docs.oracle.com/javase/8/docs/api/java/util/logging/package-summary.html) | Java 8+ | +| [JAX-RS](https://javaee.github.io/javaee-spec/javadocs/javax/ws/rs/package-summary.html) | 0.5+ | +| [JAX-RS Client](https://javaee.github.io/javaee-spec/javadocs/javax/ws/rs/client/package-summary.html) | 1.1+ | +| [JAX-WS](https://jakarta.ee/specifications/xml-web-services/2.3/apidocs/javax/xml/ws/package-summary.html) | 2.0+ (not including 3.x yet) | +| [JBoss Log Manager](https://github.com/jboss-logging/jboss-logmanager) | 1.1+ | +| [JDBC](https://docs.oracle.com/javase/8/docs/api/java/sql/package-summary.html) | Java 8+ | +| [Jedis](https://github.com/xetorthio/jedis) | 1.4+ | +| [JMS](https://javaee.github.io/javaee-spec/javadocs/javax/jms/package-summary.html) | 1.1+ | +| [JSP](https://javaee.github.io/javaee-spec/javadocs/javax/servlet/jsp/package-summary.html) | 2.3+ | +| [Kotlin Coroutines](https://kotlinlang.org/docs/coroutines-overview.html) | 1.0+ | +| [Kubernetes Client](https://github.com/kubernetes-client/java) | 7.0+ | +| [Lettuce](https://github.com/lettuce-io/lettuce-core) | 4.0+ | +| [Log4j 1](https://logging.apache.org/log4j/1.2/) | 1.2+ | +| [Log4j 2](https://logging.apache.org/log4j/2.x/) | 2.11+ | +| [Logback](http://logback.qos.ch/) | 1.0+ | +| [Micrometer](https://micrometer.io/) | 1.5+ | +| [MongoDB Driver](https://mongodb.github.io/mongo-java-driver/) | 3.1+ | +| [Netty](https://github.com/netty/netty) | 3.8+ | +| [OkHttp](https://github.com/square/okhttp/) | 2.2+ | +| [Oracle UCP](https://docs.oracle.com/database/121/JJUCP/) | 11.2+ | +| [OSHI](https://github.com/oshi/oshi/) | 5.3.1+ | +| [Play](https://github.com/playframework/playframework) | 2.4+ | +| [Play WS](https://github.com/playframework/play-ws) | 1.0+ | +| [Quartz](https://www.quartz-scheduler.org/) | 2.0+ | +| [RabbitMQ Client](https://github.com/rabbitmq/rabbitmq-java-client) | 2.7+ | +| [Ratpack](https://github.com/ratpack/ratpack) | 1.4+ | +| [Reactor](https://github.com/reactor/reactor-core) | 3.1+ | +| [Reactor Netty](https://github.com/reactor/reactor-netty) | 0.9+ | +| [Rediscala](https://github.com/etaty/rediscala) | 1.8+ | +| [Redisson](https://github.com/redisson/redisson) | 3.0+ | +| [RESTEasy](https://resteasy.github.io/) | 3.0+ | +| [Restlet](https://restlet.talend.com/) | 1.0+ | +| [RMI](https://docs.oracle.com/en/java/javase/11/docs/api/java.rmi/java/rmi/package-summary.html) | Java 8+ | +| [RxJava](https://github.com/ReactiveX/RxJava) | 1.0+ | +| [Scala ForkJoinPool](https://www.scala-lang.org/api/2.12.0/scala/concurrent/forkjoin/package$$ForkJoinPool$.html) | 2.8+ | +| [Servlet](https://javaee.github.io/javaee-spec/javadocs/javax/servlet/package-summary.html) | 2.2+ | +| [Spark Web Framework](https://github.com/perwendel/spark) | 2.3+ | +| [Spring Batch](https://spring.io/projects/spring-batch) | 3.0+ | +| [Spring Data](https://spring.io/projects/spring-data) | 1.8+ | +| [Spring Integration](https://spring.io/projects/spring-integration) | 4.1+ | +| [Spring Kafka](https://spring.io/projects/spring-kafka) | 2.7+ | +| [Spring RabbitMQ](https://spring.io/projects/spring-amqp) | 1.0+ | +| [Spring Scheduling](https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/scheduling/package-summary.html) | 3.1+ | +| [Spring Web MVC](https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/servlet/mvc/package-summary.html) | 3.1+ | +| [Spring Web Services](https://spring.io/projects/spring-ws) | 2.0+ | +| [Spring WebFlux](https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/reactive/package-summary.html) | 5.0+ | +| [Spymemcached](https://github.com/couchbase/spymemcached) | 2.12+ | +| [Tomcat JDBC Pool](https://tomcat.apache.org/tomcat-7.0-doc/jdbc-pool.html) | 8.5.0+ | +| [Twilio](https://github.com/twilio/twilio-java) | 6.6+ (not including 8.x yet) | +| [Undertow](https://undertow.io/) | 1.4+ | +| [Vaadin](https://vaadin.com/) | 14.2+ | +| [Vert.x Web](https://vertx.io/docs/vertx-web/java/) | 3.0+ | +| [Vert.x HttpClient](https://vertx.io/docs/apidocs/io/vertx/core/http/HttpClient.html) | 3.0+ | +| [Vert.x Kafka Client](https://vertx.io/docs/vertx-kafka-client/java/) | 3.6+ | +| [Vert.x RxJava2](https://vertx.io/docs/vertx-rx/java2/) | 3.5+ | +| [Vibur DBCP](https://www.vibur.org/) | 11.0+ | ## Application Servers @@ -133,7 +135,7 @@ These are the application servers that the smoke tests are run against: | [Tomcat](http://tomcat.apache.org/) | 7.0.x, 8.5.x, 9.0.x, 10.0.x | OpenJDK 8, 11, 17 | Ubuntu 18, Windows Server 2019 | | [TomEE](https://tomee.apache.org/) | 7.x, 8.x | OpenJDK 8, 11, 17 | Ubuntu 18, Windows Server 2019 | | [Websphere Liberty Profile](https://www.ibm.com/cloud/websphere-liberty) | 20.x, 21.x | OpenJDK 8 | Ubuntu 18, Windows Server 2019 | -| [Websphere Traditional](https://www.ibm.com/cloud/websphere-application-server) | 8.5.5.x, 9.0.x | IBM JDK 8 | Red Hat Enterprise Linux 8.4 | +| [Websphere Traditional](https://www.ibm.com/uk-en/cloud/websphere-application-server) | 8.5.5.x, 9.0.x | IBM JDK 8 | Red Hat Enterprise Linux 8.4 | | [WildFly](https://www.wildfly.org/) | 13.x | OpenJDK 8 | Ubuntu 18, Windows Server 2019 | | [WildFly](https://www.wildfly.org/) | 17.x, 21.x, 25.x | OpenJDK 8, 11, 17 | Ubuntu 18, Windows Server 2019 | @@ -152,6 +154,8 @@ Some instrumentations can produce too many spans and make traces very noisy. For this reason, the following instrumentations are disabled by default: - `jdbc-datasource` which creates spans whenever the `java.sql.DataSource#getConnection` method is called. +- `dropwizard-metrics` which might create a very low quality metrics data, because of lack of label/attribute support + in the Dropwizard metrics API. To enable them, add the `otel.instrumentation..enabled` system property: `-Dotel.instrumentation.jdbc-datasource.enabled=true` diff --git a/examples/distro/README.md b/examples/distro/README.md index 1e2d7066ac4c..86d1b8c84338 100644 --- a/examples/distro/README.md +++ b/examples/distro/README.md @@ -19,7 +19,6 @@ customize that * [DemoIdGenerator](custom/src/main/java/com/example/javaagent/DemoIdGenerator.java) - custom `IdGenerator` * [DemoPropagator](custom/src/main/java/com/example/javaagent/DemoPropagator.java) - custom `TextMapPropagator` -* [DemoPropertySource](custom/src/main/java/com/example/javaagent/DemoPropertySource.java) - default configuration * [DemoSampler](custom/src/main/java/com/example/javaagent/DemoSampler.java) - custom `Sampler` * [DemoSpanProcessor](custom/src/main/java/com/example/javaagent/DemoSpanProcessor.java) - custom `SpanProcessor` * [DemoSpanExporter](custom/src/main/java/com/example/javaagent/DemoSpanExporter.java) - custom `SpanExporter` diff --git a/examples/distro/agent/build.gradle b/examples/distro/agent/build.gradle index 42b7a2964bf7..d2dae6ac33c3 100644 --- a/examples/distro/agent/build.gradle +++ b/examples/distro/agent/build.gradle @@ -1,5 +1,7 @@ +import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar + plugins { - id("com.github.johnrengelman.shadow") version "7.1.2" + id("com.github.johnrengelman.shadow") } apply from: "$rootDir/gradle/shadow.gradle" @@ -7,32 +9,97 @@ apply from: "$rootDir/gradle/shadow.gradle" def relocatePackages = ext.relocatePackages configurations { - customShadow + // this configuration collects libs that will be placed in the bootstrap classloader + bootstrapLibs { + canBeResolved = true + canBeConsumed = false + } + // this configuration collects libs that will be placed in the agent classloader, isolated from the instrumented application code + javaagentLibs { + canBeResolved = true + canBeConsumed = false + } + // this configuration stores the upstream agent dep that's extended by this project + upstreamAgent { + canBeResolved = true + canBeConsumed = false + } } dependencies { - customShadow project(path: ":custom", configuration: "shadow") - customShadow project(path: ":instrumentation", configuration: "shadow") - implementation "io.opentelemetry.javaagent:opentelemetry-javaagent:${versions.opentelemetryJavaagent}" + bootstrapLibs(project(":bootstrap")) + + javaagentLibs(project(":custom")) + javaagentLibs(project(":instrumentation:servlet-3")) + + upstreamAgent("io.opentelemetry.javaagent:opentelemetry-javaagent:${versions.opentelemetryJavaagent}") } -CopySpec isolateSpec() { +CopySpec isolateClasses(Iterable jars) { return copySpec { - configurations.customShadow.files.each { + jars.forEach { from(zipTree(it)) { into("inst") - rename("(^.*)\\.class\$", "\$1.classdata") + 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("META-INF/INDEX.LIST") + exclude("META-INF/*.DSA") + exclude("META-INF/*.SF") } } } } - tasks { + jar { + enabled = false + } + + // building the final javaagent jar is done in 3 steps: + + // 1. all distro specific javaagent libs are relocated + task relocateJavaagentLibs(type: ShadowJar) { + configurations = [project.configurations.javaagentLibs] + + duplicatesStrategy = DuplicatesStrategy.FAIL + + archiveFileName.set("javaagentLibs-relocated.jar") + + mergeServiceFiles() + exclude("**/module-info.class") + relocatePackages(it) + + // exclude known bootstrap dependencies - they can't appear in the inst/ directory + dependencies { + exclude("org.slf4j:slf4j-api") + exclude("io.opentelemetry:opentelemetry-api") + exclude("io.opentelemetry:opentelemetry-api-logs") + exclude("io.opentelemetry:opentelemetry-context") + exclude("io.opentelemetry:opentelemetry-semconv") + } + } + + // 2. the distro javaagent libs are then isolated - moved to the inst/ directory + // having a separate task for isolating javaagent libs is required to avoid duplicates with the upstream javaagent + // duplicatesStrategy in shadowJar won't be applied when adding files with with(CopySpec) because each CopySpec has + // its own duplicatesStrategy + task isolateJavaagentLibs(type: Copy) { + dependsOn(tasks.relocateJavaagentLibs) + with isolateClasses(tasks.relocateJavaagentLibs.outputs.files) + + into("$buildDir/isolated/javaagentLibs") + } + + // 3. the relocated and isolated javaagent libs are merged together with the bootstrap libs (which undergo relocation + // in this task) and the upstream javaagent jar; duplicates are removed shadowJar { - dependsOn ':custom:shadowJar' - dependsOn ':instrumentation:shadowJar' - with isolateSpec() + configurations = [project.configurations.bootstrapLibs, project.configurations.upstreamAgent] + + dependsOn(tasks.isolateJavaagentLibs) + from(tasks.isolateJavaagentLibs.outputs) + + archiveClassifier.set("all") duplicatesStrategy = DuplicatesStrategy.EXCLUDE @@ -40,7 +107,6 @@ tasks { include("inst/META-INF/services/*") } exclude("**/module-info.class") - relocatePackages(it) manifest { @@ -57,4 +123,4 @@ tasks { assemble { dependsOn(shadowJar) } -} \ No newline at end of file +} diff --git a/examples/distro/bootstrap/build.gradle b/examples/distro/bootstrap/build.gradle new file mode 100644 index 000000000000..1f769508f773 --- /dev/null +++ b/examples/distro/bootstrap/build.gradle @@ -0,0 +1,6 @@ +plugins { + id "java" +} + +dependencies { +} diff --git a/examples/distro/bootstrap/src/main/java/com/example/javaagent/bootstrap/AgentApi.java b/examples/distro/bootstrap/src/main/java/com/example/javaagent/bootstrap/AgentApi.java new file mode 100644 index 000000000000..51877e67799f --- /dev/null +++ b/examples/distro/bootstrap/src/main/java/com/example/javaagent/bootstrap/AgentApi.java @@ -0,0 +1,17 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.example.javaagent.bootstrap; + +/** + * Classes in bootstrap class loader are visible for both the agent classes in agent class loader + * and helper classes that are injected into the class loader that contains the instrumented class. + */ +public final class AgentApi { + + public static void doSomething(int number) {} + + private AgentApi() {} +} diff --git a/examples/distro/build.gradle b/examples/distro/build.gradle index 6396834b1784..4be45564ed01 100644 --- a/examples/distro/build.gradle +++ b/examples/distro/build.gradle @@ -6,9 +6,15 @@ buildscript { maven { url "https://plugins.gradle.org/m2/" } + maven { + name = "sonatype" + url = uri("https://oss.sonatype.org/content/repositories/snapshots") + } } dependencies { classpath "com.diffplug.spotless:spotless-plugin-gradle:6.3.0" + classpath "gradle.plugin.com.github.johnrengelman:shadow:7.1.2" + classpath "io.opentelemetry.instrumentation:gradle-plugins:1.18.0-alpha-SNAPSHOT" } } @@ -20,10 +26,14 @@ subprojects { ext { versions = [ - opentelemetry : "1.15.0", - opentelemetryAlpha : "1.15.0-alpha", - opentelemetryJavaagent : "1.16.0-SNAPSHOT", - opentelemetryJavaagentAlpha: "1.16.0-alpha-SNAPSHOT", + // these lines are managed by .github/scripts/update-sdk-version.sh + opentelemetry : "1.18.0", + opentelemetryAlpha : "1.18.0-alpha", + + // these lines are managed by .github/scripts/update-version.sh + opentelemetryJavaagent : "1.20.0-SNAPSHOT", + opentelemetryJavaagentAlpha: "1.20.0-alpha-SNAPSHOT", + bytebuddy : "1.12.10", autoservice : "1.0.1" ] @@ -55,8 +65,9 @@ subprojects { dependencies { testImplementation("org.mockito:mockito-core:3.3.3") - testImplementation("org.junit.jupiter:junit-jupiter-api:5.6.2") - testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.6.2") + testImplementation(enforcedPlatform("org.junit:junit-bom:5.9.0")) + testImplementation("org.junit.jupiter:junit-jupiter-api:5.9.0") + testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.9.0") } tasks { diff --git a/examples/distro/custom/build.gradle b/examples/distro/custom/build.gradle index 8ea1309d3bb5..34e12811d61d 100644 --- a/examples/distro/custom/build.gradle +++ b/examples/distro/custom/build.gradle @@ -1,24 +1,11 @@ plugins { id "java" - id("com.github.johnrengelman.shadow") version "7.1.2" } -apply from: "$rootDir/gradle/shadow.gradle" - -def relocatePackages = ext.relocatePackages - dependencies { + compileOnly(project(":bootstrap")) compileOnly("io.opentelemetry:opentelemetry-sdk:${versions.opentelemetry}") compileOnly("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure-spi:${versions.opentelemetry}") compileOnly("io.opentelemetry.javaagent:opentelemetry-javaagent-extension-api:${versions.opentelemetryJavaagentAlpha}") -} - -tasks { - shadowJar { - mergeServiceFiles() - - exclude("**/module-info.class") - - relocatePackages(it) - } + compileOnly("io.opentelemetry.javaagent:opentelemetry-javaagent-tooling:${versions.opentelemetryJavaagentAlpha}") } diff --git a/examples/distro/custom/src/main/java/com/example/javaagent/DemoAutoConfigurationCustomizerProvider.java b/examples/distro/custom/src/main/java/com/example/javaagent/DemoAutoConfigurationCustomizerProvider.java index b996f20243e0..c601d088ad99 100644 --- a/examples/distro/custom/src/main/java/com/example/javaagent/DemoAutoConfigurationCustomizerProvider.java +++ b/examples/distro/custom/src/main/java/com/example/javaagent/DemoAutoConfigurationCustomizerProvider.java @@ -11,6 +11,8 @@ import io.opentelemetry.sdk.trace.SdkTracerProviderBuilder; import io.opentelemetry.sdk.trace.SpanLimits; import io.opentelemetry.sdk.trace.export.SimpleSpanProcessor; +import java.util.HashMap; +import java.util.Map; /** * This is one of the main entry points for Instrumentation Agent's customizations. It allows @@ -27,7 +29,9 @@ public class DemoAutoConfigurationCustomizerProvider @Override public void customize(AutoConfigurationCustomizer autoConfiguration) { - autoConfiguration.addTracerProviderCustomizer(this::configureSdkTracerProvider); + autoConfiguration + .addTracerProviderCustomizer(this::configureSdkTracerProvider) + .addPropertiesSupplier(this::getDefaultProperties); } private SdkTracerProviderBuilder configureSdkTracerProvider( @@ -39,4 +43,12 @@ private SdkTracerProviderBuilder configureSdkTracerProvider( .addSpanProcessor(new DemoSpanProcessor()) .addSpanProcessor(SimpleSpanProcessor.create(new DemoSpanExporter())); } + + private Map getDefaultProperties() { + Map properties = new HashMap<>(); + properties.put("otel.exporter.otlp.endpoint", "http://backend:8080"); + properties.put("otel.exporter.otlp.insecure", "true"); + properties.put("otel.config.max.attrs", "16"); + return properties; + } } diff --git a/examples/distro/custom/src/main/java/com/example/javaagent/DemoBootstrapPackagesProvider.java b/examples/distro/custom/src/main/java/com/example/javaagent/DemoBootstrapPackagesProvider.java new file mode 100644 index 000000000000..b83cd153d998 --- /dev/null +++ b/examples/distro/custom/src/main/java/com/example/javaagent/DemoBootstrapPackagesProvider.java @@ -0,0 +1,26 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.example.javaagent; + +import com.example.javaagent.bootstrap.AgentApi; +import io.opentelemetry.javaagent.tooling.bootstrap.BootstrapPackagesBuilder; +import io.opentelemetry.javaagent.tooling.bootstrap.BootstrapPackagesConfigurer; +import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; + +/** + * To ensure that the classes we add to bootstrap class loader are available in class loaders that + * don't delegate all class loading requests to bootstrap class loader e.g. OSGi we need to tell the + * agent which packages we have added. + * + * @see BootstrapPackagesConfigurer + */ +public class DemoBootstrapPackagesProvider implements BootstrapPackagesConfigurer { + + @Override + public void configure(BootstrapPackagesBuilder builder, ConfigProperties config) { + builder.add(AgentApi.class.getPackage().getName()); + } +} diff --git a/examples/distro/custom/src/main/java/com/example/javaagent/DemoPropertySource.java b/examples/distro/custom/src/main/java/com/example/javaagent/DemoPropertySource.java deleted file mode 100644 index 5048960ffa12..000000000000 --- a/examples/distro/custom/src/main/java/com/example/javaagent/DemoPropertySource.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package com.example.javaagent; - -import io.opentelemetry.javaagent.extension.config.ConfigPropertySource; -import java.util.HashMap; -import java.util.Map; - -/** - * {@link ConfigPropertySource} is an SPI provided by OpenTelemetry Java instrumentation agent. By - * implementing it custom distributions can supply their own default configuration. The - * configuration priority, from highest to lowest is: system properties -> environment variables -> - * configuration file -> PropertySource SPI -> hard-coded defaults - */ -public class DemoPropertySource implements ConfigPropertySource { - - @Override - public Map getProperties() { - Map properties = new HashMap<>(); - properties.put("otel.exporter.otlp.endpoint", "http://backend:8080"); - properties.put("otel.exporter.otlp.insecure", "true"); - properties.put("otel.config.max.attrs", "16"); - return properties; - } -} diff --git a/examples/distro/custom/src/main/resources/META-INF/services/io.opentelemetry.javaagent.extension.config.ConfigPropertySource b/examples/distro/custom/src/main/resources/META-INF/services/io.opentelemetry.javaagent.extension.config.ConfigPropertySource deleted file mode 100644 index 1274076e549e..000000000000 --- a/examples/distro/custom/src/main/resources/META-INF/services/io.opentelemetry.javaagent.extension.config.ConfigPropertySource +++ /dev/null @@ -1 +0,0 @@ -com.example.javaagent.DemoPropertySource \ No newline at end of file diff --git a/examples/distro/custom/src/main/resources/META-INF/services/io.opentelemetry.javaagent.tooling.bootstrap.BootstrapPackagesConfigurer b/examples/distro/custom/src/main/resources/META-INF/services/io.opentelemetry.javaagent.tooling.bootstrap.BootstrapPackagesConfigurer new file mode 100644 index 000000000000..4cb7aaa239e5 --- /dev/null +++ b/examples/distro/custom/src/main/resources/META-INF/services/io.opentelemetry.javaagent.tooling.bootstrap.BootstrapPackagesConfigurer @@ -0,0 +1 @@ +com.example.javaagent.DemoBootstrapPackagesProvider \ No newline at end of file diff --git a/examples/distro/gradle/instrumentation.gradle b/examples/distro/gradle/instrumentation.gradle index eef7f23e05c7..b20f9b9174e2 100644 --- a/examples/distro/gradle/instrumentation.gradle +++ b/examples/distro/gradle/instrumentation.gradle @@ -1,5 +1,7 @@ apply plugin: 'java' apply plugin: 'com.github.johnrengelman.shadow' +apply plugin: 'io.opentelemetry.instrumentation.muzzle-generation' +apply plugin: 'io.opentelemetry.instrumentation.muzzle-check' apply from: "$rootDir/gradle/shadow.gradle" @@ -12,6 +14,7 @@ configurations { dependencies { compileOnly("io.opentelemetry:opentelemetry-sdk:${versions.opentelemetry}") + compileOnly("io.opentelemetry.instrumentation:opentelemetry-instrumentation-api:${versions.opentelemetryJavaagent}") compileOnly("io.opentelemetry.javaagent:opentelemetry-javaagent-extension-api:${versions.opentelemetryJavaagentAlpha}") compileOnly deps.bytebuddy @@ -19,11 +22,16 @@ dependencies { compileOnly deps.autoservice // the javaagent that is going to be used when running instrumentation unit tests - testAgent("io.opentelemetry.javaagent:opentelemetry-agent-for-testing:${versions.opentelemetryJavaagentAlpha}") + testAgent(project(path: ":testing:agent-for-testing", configuration: "shadow")) // test dependencies testImplementation("io.opentelemetry.javaagent:opentelemetry-testing-common:${versions.opentelemetryJavaagentAlpha}") testImplementation("io.opentelemetry:opentelemetry-sdk-testing:${versions.opentelemetry}") testImplementation("org.assertj:assertj-core:3.19.0") + + add("codegen", "io.opentelemetry.javaagent:opentelemetry-javaagent-tooling:${versions.opentelemetryJavaagentAlpha}") + add("muzzleBootstrap", "io.opentelemetry.instrumentation:opentelemetry-instrumentation-annotations-support:${versions.opentelemetryJavaagentAlpha}") + add("muzzleTooling", "io.opentelemetry.javaagent:opentelemetry-javaagent-extension-api:${versions.opentelemetryJavaagentAlpha}") + add("muzzleTooling", "io.opentelemetry.javaagent:opentelemetry-javaagent-tooling:${versions.opentelemetryJavaagentAlpha}") } shadowJar { @@ -48,6 +56,7 @@ tasks.withType(Test).configureEach { jvmArgs "-Dotel.metrics.exporter=otlp" dependsOn shadowJar + dependsOn configurations.testAgent.buildDependencies // The sources are packaged into the testing jar so we need to make sure to exclude from the test // classpath, which automatically inherits them, to ensure our shaded versions are used. diff --git a/examples/distro/gradle/shadow.gradle b/examples/distro/gradle/shadow.gradle index fce270eab672..a1dc1b670428 100644 --- a/examples/distro/gradle/shadow.gradle +++ b/examples/distro/gradle/shadow.gradle @@ -1,6 +1,4 @@ ext.relocatePackages = { shadowJar -> - // Prevents conflict with other SLF4J instances. Important for premain. - shadowJar.relocate 'org.slf4j', 'io.opentelemetry.javaagent.slf4j' // rewrite dependencies calling Logger.getLogger shadowJar.relocate 'java.util.logging.Logger', 'io.opentelemetry.javaagent.bootstrap.PatchLogger' diff --git a/examples/distro/gradle/wrapper/gradle-wrapper.jar b/examples/distro/gradle/wrapper/gradle-wrapper.jar index 41d9927a4d4f..249e5832f090 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 aa991fceae6e..ae04661ee733 100644 --- a/examples/distro/gradle/wrapper/gradle-wrapper.properties +++ b/examples/distro/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/examples/distro/gradlew b/examples/distro/gradlew index 1b6c787337ff..a69d9cb6c206 100755 --- a/examples/distro/gradlew +++ b/examples/distro/gradlew @@ -205,6 +205,12 @@ set -- \ org.gradle.wrapper.GradleWrapperMain \ "$@" +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + # Use "xargs" to parse quoted args. # # With -n1 it outputs one arg per line, with the quotes and backslashes removed. diff --git a/examples/distro/gradlew.bat b/examples/distro/gradlew.bat index 107acd32c4e6..f127cfd49d40 100644 --- a/examples/distro/gradlew.bat +++ b/examples/distro/gradlew.bat @@ -14,7 +14,7 @@ @rem limitations under the License. @rem -@if "%DEBUG%" == "" @echo off +@if "%DEBUG%"=="" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @@ -25,7 +25,7 @@ if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. +if "%DIRNAME%"=="" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @@ -40,7 +40,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto execute +if %ERRORLEVEL% equ 0 goto execute echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. @@ -75,13 +75,15 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar :end @rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd +if %ERRORLEVEL% equ 0 goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% :mainEnd if "%OS%"=="Windows_NT" endlocal diff --git a/examples/distro/instrumentation/build.gradle b/examples/distro/instrumentation/build.gradle index 9c9a80761ae4..9d1a69144a12 100644 --- a/examples/distro/instrumentation/build.gradle +++ b/examples/distro/instrumentation/build.gradle @@ -1,11 +1,3 @@ -plugins { - id("com.github.johnrengelman.shadow") version "7.1.2" -} - -apply from: "$rootDir/gradle/shadow.gradle" - -def relocatePackages = ext.relocatePackages - Project instr_project = project subprojects { afterEvaluate { Project subProj -> @@ -19,13 +11,3 @@ subprojects { } } } - -shadowJar { - mergeServiceFiles() - - exclude '**/module-info.class' - - duplicatesStrategy = DuplicatesStrategy.FAIL - - relocatePackages(it) -} \ No newline at end of file diff --git a/examples/distro/instrumentation/servlet-3/build.gradle b/examples/distro/instrumentation/servlet-3/build.gradle index 7c4f6f7a71c5..427fc522a3b7 100644 --- a/examples/distro/instrumentation/servlet-3/build.gradle +++ b/examples/distro/instrumentation/servlet-3/build.gradle @@ -1,6 +1,22 @@ apply from: "$rootDir/gradle/instrumentation.gradle" +muzzle { + pass { + group.set("javax.servlet") + module.set("javax.servlet-api") + versions.set("[3.0,)") + assertInverse.set(true) + } + pass { + group.set("javax.servlet") + module.set("servlet-api") + versions.set("[2.2, 3.0)") + assertInverse.set(true) + } +} + dependencies { + compileOnly project(":bootstrap") compileOnly "javax.servlet:javax.servlet-api:3.0.1" testInstrumentation "io.opentelemetry.javaagent.instrumentation:opentelemetry-javaagent-servlet-common:${versions.opentelemetryJavaagentAlpha}" diff --git a/examples/distro/instrumentation/servlet-3/src/main/java/com/example/javaagent/instrumentation/DemoServlet3HelperClass.java b/examples/distro/instrumentation/servlet-3/src/main/java/com/example/javaagent/instrumentation/DemoServlet3HelperClass.java new file mode 100644 index 000000000000..32519022946d --- /dev/null +++ b/examples/distro/instrumentation/servlet-3/src/main/java/com/example/javaagent/instrumentation/DemoServlet3HelperClass.java @@ -0,0 +1,18 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.example.javaagent.instrumentation; + +import com.example.javaagent.bootstrap.AgentApi; + +public final class DemoServlet3HelperClass { + + public static void doSomething(int number) { + // call the api in bootstrap class loader + AgentApi.doSomething(number); + } + + private DemoServlet3HelperClass() {} +} diff --git a/examples/distro/instrumentation/servlet-3/src/main/java/com/example/javaagent/instrumentation/DemoServlet3Instrumentation.java b/examples/distro/instrumentation/servlet-3/src/main/java/com/example/javaagent/instrumentation/DemoServlet3Instrumentation.java index 330cf2fb2340..fe0c29ba2e74 100644 --- a/examples/distro/instrumentation/servlet-3/src/main/java/com/example/javaagent/instrumentation/DemoServlet3Instrumentation.java +++ b/examples/distro/instrumentation/servlet-3/src/main/java/com/example/javaagent/instrumentation/DemoServlet3Instrumentation.java @@ -11,9 +11,11 @@ import static net.bytebuddy.matcher.ElementMatchers.namedOneOf; import static net.bytebuddy.matcher.ElementMatchers.takesArgument; +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 java.util.concurrent.atomic.AtomicInteger; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletResponse; import net.bytebuddy.asm.Advice; @@ -41,6 +43,17 @@ public static class DemoServlet3Advice { @Advice.OnMethodEnter(suppress = Throwable.class) public static void onEnter(@Advice.Argument(value = 1) ServletResponse response) { + // VirtualField depends on muzzle-generation. Using it here to verify that muzzle-generation + // was set up. + VirtualField virtualField = + VirtualField.find(ServletResponse.class, AtomicInteger.class); + AtomicInteger counter = virtualField.get(response); + if (counter == null) { + counter = new AtomicInteger(); + virtualField.set(response, counter); + } + DemoServlet3HelperClass.doSomething(counter.incrementAndGet()); + if (!(response instanceof HttpServletResponse)) { return; } diff --git a/examples/distro/instrumentation/servlet-3/src/main/java/com/example/javaagent/instrumentation/DemoServlet3InstrumentationModule.java b/examples/distro/instrumentation/servlet-3/src/main/java/com/example/javaagent/instrumentation/DemoServlet3InstrumentationModule.java index 64edaa1efd1e..e2f50fc77f8b 100644 --- a/examples/distro/instrumentation/servlet-3/src/main/java/com/example/javaagent/instrumentation/DemoServlet3InstrumentationModule.java +++ b/examples/distro/instrumentation/servlet-3/src/main/java/com/example/javaagent/instrumentation/DemoServlet3InstrumentationModule.java @@ -43,4 +43,9 @@ public ElementMatcher.Junction classLoaderMatcher() { public List typeInstrumentations() { return singletonList(new DemoServlet3Instrumentation()); } + + @Override + public boolean isHelperClass(String className) { + return className.startsWith("com.example.javaagent.instrumentation"); + } } diff --git a/examples/distro/settings.gradle b/examples/distro/settings.gradle index 20d00c93547a..3b5d2dcef000 100644 --- a/examples/distro/settings.gradle +++ b/examples/distro/settings.gradle @@ -11,7 +11,9 @@ pluginManagement { rootProject.name = 'opentelemetry-java-instrumentation-distro-demo' include "agent" +include "bootstrap" include "custom" include "instrumentation" include "instrumentation:servlet-3" include "smoke-tests" +include "testing:agent-for-testing" diff --git a/examples/distro/smoke-tests/build.gradle b/examples/distro/smoke-tests/build.gradle index 11ce1f220297..9207ebd525d7 100644 --- a/examples/distro/smoke-tests/build.gradle +++ b/examples/distro/smoke-tests/build.gradle @@ -10,7 +10,7 @@ dependencies { testImplementation("io.opentelemetry.proto:opentelemetry-proto:0.16.0-alpha") testImplementation("io.opentelemetry:opentelemetry-api:1.6.0") - testImplementation("ch.qos.logback:logback-classic:1.2.3") + testImplementation("ch.qos.logback:logback-classic:1.2.11") } tasks.test { diff --git a/examples/distro/testing/agent-for-testing/build.gradle b/examples/distro/testing/agent-for-testing/build.gradle new file mode 100644 index 000000000000..5026f88df1b6 --- /dev/null +++ b/examples/distro/testing/agent-for-testing/build.gradle @@ -0,0 +1,123 @@ +import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar + +plugins { + id("com.github.johnrengelman.shadow") +} + +apply from: "$rootDir/gradle/shadow.gradle" + +def relocatePackages = ext.relocatePackages + +configurations { + // this configuration collects libs that will be placed in the bootstrap classloader + bootstrapLibs { + canBeResolved = true + canBeConsumed = false + } + // this configuration collects libs that will be placed in the agent classloader, isolated from the instrumented application code + javaagentLibs { + canBeResolved = true + canBeConsumed = false + } + // this configuration stores the upstream agent dep that's extended by this project + upstreamAgent { + canBeResolved = true + canBeConsumed = false + } +} + +dependencies { + bootstrapLibs(project(":bootstrap")) + // and finally include everything from otel agent for testing + upstreamAgent("io.opentelemetry.javaagent:opentelemetry-agent-for-testing:${versions.opentelemetryJavaagentAlpha}") +} + +CopySpec isolateClasses(Iterable jars) { + return copySpec { + jars.forEach { + 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("META-INF/INDEX.LIST") + exclude("META-INF/*.DSA") + exclude("META-INF/*.SF") + } + } + } +} + +tasks { + jar { + enabled = false + } + + // building the final javaagent jar is done in 3 steps: + + // 1. all distro specific javaagent libs are relocated + task relocateJavaagentLibs(type: ShadowJar) { + configurations = [project.configurations.javaagentLibs] + + duplicatesStrategy = DuplicatesStrategy.FAIL + + archiveFileName.set("javaagentLibs-relocated.jar") + + mergeServiceFiles() + exclude("**/module-info.class") + relocatePackages(it) + + // exclude known bootstrap dependencies - they can't appear in the inst/ directory + dependencies { + exclude("org.slf4j:slf4j-api") + exclude("io.opentelemetry:opentelemetry-api") + exclude("io.opentelemetry:opentelemetry-api-logs") + exclude("io.opentelemetry:opentelemetry-context") + exclude("io.opentelemetry:opentelemetry-semconv") + } + } + + // 2. the distro javaagent libs are then isolated - moved to the inst/ directory + // having a separate task for isolating javaagent libs is required to avoid duplicates with the upstream javaagent + // duplicatesStrategy in shadowJar won't be applied when adding files with with(CopySpec) because each CopySpec has + // its own duplicatesStrategy + task isolateJavaagentLibs(type: Copy) { + dependsOn(tasks.relocateJavaagentLibs) + with isolateClasses(tasks.relocateJavaagentLibs.outputs.files) + + into("$buildDir/isolated/javaagentLibs") + } + + // 3. the relocated and isolated javaagent libs are merged together with the bootstrap libs (which undergo relocation + // in this task) and the upstream javaagent jar; duplicates are removed + shadowJar { + configurations = [project.configurations.bootstrapLibs, project.configurations.upstreamAgent] + + dependsOn(tasks.isolateJavaagentLibs) + from(tasks.isolateJavaagentLibs.outputs) + + archiveClassifier.set("") + + duplicatesStrategy = DuplicatesStrategy.EXCLUDE + + mergeServiceFiles { + include("inst/META-INF/services/*") + } + exclude("**/module-info.class") + relocatePackages(it) + + manifest { + attributes.put("Main-Class", "io.opentelemetry.javaagent.OpenTelemetryAgent") + attributes.put("Agent-Class", "io.opentelemetry.javaagent.OpenTelemetryAgent") + attributes.put("Premain-Class", "io.opentelemetry.javaagent.OpenTelemetryAgent") + attributes.put("Can-Redefine-Classes", "true") + attributes.put("Can-Retransform-Classes", "true") + attributes.put("Implementation-Vendor", "Demo") + attributes.put("Implementation-Version", "demo-${project.version}-otel-${versions.opentelemetryJavaagent}") + } + } + + assemble { + dependsOn(shadowJar) + } +} diff --git a/examples/extension/README.md b/examples/extension/README.md index e4c383c9726d..73cea36d5933 100644 --- a/examples/extension/README.md +++ b/examples/extension/README.md @@ -32,7 +32,6 @@ For more information, see the `extendedAgent` task in [build.gradle](build.gradl * Custom `IdGenerator`: [DemoIdGenerator](src/main/java/com/example/javaagent/DemoIdGenerator.java) * Custom `TextMapPropagator`: [DemoPropagator](src/main/java/com/example/javaagent/DemoPropagator.java) -* New default configuration: [DemoPropertySource](src/main/java/com/example/javaagent/DemoPropertySource.java) * Custom `Sampler`: [DemoSampler](src/main/java/com/example/javaagent/DemoSampler.java) * Custom `SpanProcessor`: [DemoSpanProcessor](src/main/java/com/example/javaagent/DemoSpanProcessor.java) * Custom `SpanExporter`: [DemoSpanExporter](src/main/java/com/example/javaagent/DemoSpanExporter.java) diff --git a/examples/extension/build.gradle b/examples/extension/build.gradle index e3e1d4603844..05233037586c 100644 --- a/examples/extension/build.gradle +++ b/examples/extension/build.gradle @@ -13,8 +13,8 @@ plugins { id "com.github.johnrengelman.shadow" version "7.1.2" id "com.diffplug.spotless" version "6.3.0" - id "io.opentelemetry.instrumentation.muzzle-generation" version "1.16.0-alpha-SNAPSHOT" - id "io.opentelemetry.instrumentation.muzzle-check" version "1.16.0-alpha-SNAPSHOT" + id "io.opentelemetry.instrumentation.muzzle-generation" version "1.20.0-alpha-SNAPSHOT" + id "io.opentelemetry.instrumentation.muzzle-check" version "1.20.0-alpha-SNAPSHOT" } group 'io.opentelemetry.example' @@ -22,10 +22,13 @@ version '1.0' ext { versions = [ - opentelemetry : "1.15.0", - opentelemetryAlpha : "1.15.0-alpha", - opentelemetryJavaagent : "1.16.0-SNAPSHOT", - opentelemetryJavaagentAlpha: "1.16.0-alpha-SNAPSHOT", + // these lines are managed by .github/scripts/update-sdk-version.sh + opentelemetry : "1.18.0", + opentelemetryAlpha : "1.18.0-alpha", + + // these lines are managed by .github/scripts/update-version.sh + opentelemetryJavaagent : "1.20.0-SNAPSHOT", + opentelemetryJavaagentAlpha: "1.20.0-alpha-SNAPSHOT", ] deps = [ @@ -64,7 +67,7 @@ dependencies { runtime all necessary classes are provided by javaagent itself. */ compileOnly("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure-spi:${versions.opentelemetry}") - compileOnly("io.opentelemetry.instrumentation:opentelemetry-instrumentation-api:${versions.opentelemetryJavaagentAlpha}") + compileOnly("io.opentelemetry.instrumentation:opentelemetry-instrumentation-api:${versions.opentelemetryJavaagent}") compileOnly("io.opentelemetry.javaagent:opentelemetry-javaagent-extension-api:${versions.opentelemetryJavaagentAlpha}") //Provides @AutoService annotation that makes registration of our SPI implementations much easier @@ -96,16 +99,15 @@ dependencies { testImplementation("io.opentelemetry:opentelemetry-api:${versions.opentelemetry}") testImplementation("io.opentelemetry.proto:opentelemetry-proto:0.16.0-alpha") - testImplementation("org.junit.jupiter:junit-jupiter-api:5.6.2") - testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.6.2") - testRuntimeOnly("ch.qos.logback:logback-classic:1.2.3") + testImplementation("org.junit.jupiter:junit-jupiter-api:5.9.0") + testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.9.0") + testRuntimeOnly("ch.qos.logback:logback-classic:1.2.11") //Otel Java instrumentation that we use and extend during integration tests otel("io.opentelemetry.javaagent:opentelemetry-javaagent:${versions.opentelemetryJavaagent}") //TODO remove when start using io.opentelemetry.instrumentation.javaagent-instrumentation plugin add("codegen", "io.opentelemetry.javaagent:opentelemetry-javaagent-tooling:${versions.opentelemetryJavaagentAlpha}") - add("codegen", "ch.qos.logback:logback-classic:1.2.3") add("muzzleBootstrap", "io.opentelemetry.instrumentation:opentelemetry-instrumentation-annotations-support:${versions.opentelemetryJavaagentAlpha}") add("muzzleTooling", "io.opentelemetry.javaagent:opentelemetry-javaagent-extension-api:${versions.opentelemetryJavaagentAlpha}") add("muzzleTooling", "io.opentelemetry.javaagent:opentelemetry-javaagent-tooling:${versions.opentelemetryJavaagentAlpha}") diff --git a/examples/extension/gradle/wrapper/gradle-wrapper.jar b/examples/extension/gradle/wrapper/gradle-wrapper.jar index 41d9927a4d4f..249e5832f090 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 aa991fceae6e..ae04661ee733 100644 --- a/examples/extension/gradle/wrapper/gradle-wrapper.properties +++ b/examples/extension/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/examples/extension/gradlew b/examples/extension/gradlew index 1b6c787337ff..a69d9cb6c206 100755 --- a/examples/extension/gradlew +++ b/examples/extension/gradlew @@ -205,6 +205,12 @@ set -- \ org.gradle.wrapper.GradleWrapperMain \ "$@" +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + # Use "xargs" to parse quoted args. # # With -n1 it outputs one arg per line, with the quotes and backslashes removed. diff --git a/examples/extension/gradlew.bat b/examples/extension/gradlew.bat index 107acd32c4e6..f127cfd49d40 100644 --- a/examples/extension/gradlew.bat +++ b/examples/extension/gradlew.bat @@ -14,7 +14,7 @@ @rem limitations under the License. @rem -@if "%DEBUG%" == "" @echo off +@if "%DEBUG%"=="" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @@ -25,7 +25,7 @@ if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. +if "%DIRNAME%"=="" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @@ -40,7 +40,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto execute +if %ERRORLEVEL% equ 0 goto execute echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. @@ -75,13 +75,15 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar :end @rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd +if %ERRORLEVEL% equ 0 goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% :mainEnd if "%OS%"=="Windows_NT" endlocal diff --git a/examples/extension/src/main/java/com/example/javaagent/DemoAutoConfigurationCustomizerProvider.java b/examples/extension/src/main/java/com/example/javaagent/DemoAutoConfigurationCustomizerProvider.java index f7870d555f66..18fbb162e064 100644 --- a/examples/extension/src/main/java/com/example/javaagent/DemoAutoConfigurationCustomizerProvider.java +++ b/examples/extension/src/main/java/com/example/javaagent/DemoAutoConfigurationCustomizerProvider.java @@ -12,6 +12,8 @@ import io.opentelemetry.sdk.trace.SdkTracerProviderBuilder; import io.opentelemetry.sdk.trace.SpanLimits; import io.opentelemetry.sdk.trace.export.SimpleSpanProcessor; +import java.util.HashMap; +import java.util.Map; /** * This is one of the main entry points for Instrumentation Agent's customizations. It allows @@ -29,7 +31,9 @@ public class DemoAutoConfigurationCustomizerProvider @Override public void customize(AutoConfigurationCustomizer autoConfiguration) { - autoConfiguration.addTracerProviderCustomizer(this::configureSdkTracerProvider); + autoConfiguration + .addTracerProviderCustomizer(this::configureSdkTracerProvider) + .addPropertiesSupplier(this::getDefaultProperties); } private SdkTracerProviderBuilder configureSdkTracerProvider( @@ -41,4 +45,13 @@ private SdkTracerProviderBuilder configureSdkTracerProvider( .addSpanProcessor(new DemoSpanProcessor()) .addSpanProcessor(SimpleSpanProcessor.create(new DemoSpanExporter())); } + + private Map getDefaultProperties() { + Map properties = new HashMap<>(); + properties.put("otel.exporter.otlp.endpoint", "http://backend:8080"); + properties.put("otel.exporter.otlp.insecure", "true"); + properties.put("otel.config.max.attrs", "16"); + properties.put("otel.traces.sampler", "demo"); + return properties; + } } diff --git a/examples/extension/src/main/java/com/example/javaagent/DemoIgnoredTypesConfigurer.java b/examples/extension/src/main/java/com/example/javaagent/DemoIgnoredTypesConfigurer.java index 2113bf0ab6f1..353084fb2aba 100644 --- a/examples/extension/src/main/java/com/example/javaagent/DemoIgnoredTypesConfigurer.java +++ b/examples/extension/src/main/java/com/example/javaagent/DemoIgnoredTypesConfigurer.java @@ -6,9 +6,9 @@ package com.example.javaagent; import com.google.auto.service.AutoService; -import io.opentelemetry.instrumentation.api.config.Config; import io.opentelemetry.javaagent.extension.ignore.IgnoredTypesBuilder; import io.opentelemetry.javaagent.extension.ignore.IgnoredTypesConfigurer; +import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; /** * Custom {@link IgnoredTypesConfigurer} which exists currently only to verify correct shading. @@ -19,5 +19,5 @@ public class DemoIgnoredTypesConfigurer implements IgnoredTypesConfigurer { @Override - public void configure(Config config, IgnoredTypesBuilder builder) {} + public void configure(IgnoredTypesBuilder builder, ConfigProperties config) {} } diff --git a/examples/extension/src/main/java/com/example/javaagent/DemoPropertySource.java b/examples/extension/src/main/java/com/example/javaagent/DemoPropertySource.java deleted file mode 100644 index 2d671285b25f..000000000000 --- a/examples/extension/src/main/java/com/example/javaagent/DemoPropertySource.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package com.example.javaagent; - -import com.google.auto.service.AutoService; -import io.opentelemetry.javaagent.extension.config.ConfigPropertySource; -import java.util.HashMap; -import java.util.Map; - -/** - * {@link ConfigPropertySource} is an SPI provided by OpenTelemetry Java instrumentation agent. By - * implementing it custom distributions can supply their own default configuration. The - * configuration priority, from highest to lowest is: system properties -> environment variables -> - * configuration file -> PropertySource SPI -> hard-coded defaults - */ -@AutoService(ConfigPropertySource.class) -public class DemoPropertySource implements ConfigPropertySource { - - @Override - public Map getProperties() { - Map properties = new HashMap<>(); - properties.put("otel.exporter.otlp.endpoint", "http://backend:8080"); - properties.put("otel.exporter.otlp.insecure", "true"); - properties.put("otel.config.max.attrs", "16"); - properties.put("otel.traces.sampler", "demo"); - return properties; - } -} diff --git a/gradle-plugins/build.gradle.kts b/gradle-plugins/build.gradle.kts index 7bfa550fed6a..61eb36301b76 100644 --- a/gradle-plugins/build.gradle.kts +++ b/gradle-plugins/build.gradle.kts @@ -26,10 +26,10 @@ configurations.named("compileOnly") { dependencies { implementation("com.google.guava:guava:31.1-jre") // we need to use byte buddy variant that does not shade asm - implementation("net.bytebuddy:byte-buddy-gradle-plugin:1.12.10") { + implementation("net.bytebuddy:byte-buddy-gradle-plugin:1.12.18") { exclude(group = "net.bytebuddy", module = "byte-buddy") } - implementation("net.bytebuddy:byte-buddy-dep:1.12.10") + implementation("net.bytebuddy:byte-buddy-dep:1.12.18") implementation("org.eclipse.aether:aether-connector-basic:1.1.0") implementation("org.eclipse.aether:aether-transport-http:1.1.0") @@ -37,9 +37,9 @@ dependencies { implementation("gradle.plugin.com.github.johnrengelman:shadow:7.1.2") - testImplementation("org.assertj:assertj-core:3.22.0") + testImplementation("org.assertj:assertj-core:3.23.1") - testImplementation(enforcedPlatform("org.junit:junit-bom:5.8.2")) + testImplementation(enforcedPlatform("org.junit:junit-bom:5.9.1")) testImplementation("org.junit.jupiter:junit-jupiter-api") testImplementation("org.junit.jupiter:junit-jupiter-params") testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine") @@ -137,15 +137,14 @@ afterEvaluate { } } } +} // Sign only if we have a key to do so - val signingKey: String? = System.getenv("GPG_PRIVATE_KEY") -// Stub out entire signing block off of CI since Gradle provides no way of lazy configuration of -// signing tasks. - if (System.getenv("CI") != null && signingKey != null) { - signing { - useInMemoryPgpKeys(signingKey, System.getenv("GPG_PASSWORD")) - sign(publishing.publications["pluginMaven"]) - } - } +val signingKey: String? = System.getenv("GPG_PRIVATE_KEY") +signing { + setRequired({ + // only require signing on CI and when a signing key is present + System.getenv("CI") != null && signingKey != null + }) + useInMemoryPgpKeys(signingKey, System.getenv("GPG_PASSWORD")) } diff --git a/gradle-plugins/gradle/wrapper/gradle-wrapper.jar b/gradle-plugins/gradle/wrapper/gradle-wrapper.jar index 7454180f2ae8..249e5832f090 100644 Binary files a/gradle-plugins/gradle/wrapper/gradle-wrapper.jar and b/gradle-plugins/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle-plugins/gradle/wrapper/gradle-wrapper.properties b/gradle-plugins/gradle/wrapper/gradle-wrapper.properties index aa991fceae6e..ae04661ee733 100644 --- a/gradle-plugins/gradle/wrapper/gradle-wrapper.properties +++ b/gradle-plugins/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradle-plugins/gradlew b/gradle-plugins/gradlew index c53aefaa5fc8..a69d9cb6c206 100755 --- a/gradle-plugins/gradlew +++ b/gradle-plugins/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright 2015-2021 the original authors. +# Copyright © 2015-2021 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. @@ -32,10 +32,10 @@ # Busybox and similar reduced shells will NOT work, because this script # requires all of these POSIX shell features: # * functions; -# * expansions $var, ${var}, ${var:-default}, ${var+SET}, -# ${var#prefix}, ${var%suffix}, and $( cmd ); -# * compound commands having a testable exit status, especially case; -# * various built-in commands including command, set, and ulimit. +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». # # Important for patching: # @@ -205,6 +205,12 @@ set -- \ org.gradle.wrapper.GradleWrapperMain \ "$@" +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + # Use "xargs" to parse quoted args. # # With -n1 it outputs one arg per line, with the quotes and backslashes removed. diff --git a/gradle-plugins/gradlew.bat b/gradle-plugins/gradlew.bat index 107acd32c4e6..f127cfd49d40 100644 --- a/gradle-plugins/gradlew.bat +++ b/gradle-plugins/gradlew.bat @@ -14,7 +14,7 @@ @rem limitations under the License. @rem -@if "%DEBUG%" == "" @echo off +@if "%DEBUG%"=="" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @@ -25,7 +25,7 @@ if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. +if "%DIRNAME%"=="" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @@ -40,7 +40,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto execute +if %ERRORLEVEL% equ 0 goto execute echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. @@ -75,13 +75,15 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar :end @rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd +if %ERRORLEVEL% equ 0 goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% :mainEnd if "%OS%"=="Windows_NT" endlocal diff --git a/gradle-plugins/settings.gradle.kts b/gradle-plugins/settings.gradle.kts index e70327ddbfa1..111f634d8311 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 "0.15.0" + id("com.gradle.plugin-publish") version "1.0.0" id("io.github.gradle-nexus.publish-plugin") version "1.1.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 d91d805a37a0..6856d7adce5a 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 @@ -82,8 +82,6 @@ tasks.withType().configureEach { exclude("**/module-info.class") - // Prevents conflict with other SLF4J instances. Important for premain. - relocate("org.slf4j", "io.opentelemetry.javaagent.slf4j") // rewrite dependencies calling Logger.getLogger relocate("java.util.logging.Logger", "io.opentelemetry.javaagent.bootstrap.PatchLogger") @@ -134,26 +132,6 @@ tasks.register("printMuzzleReferences") { } } -val projectRepositories = mutableListOf().apply { - // Manually add mavenCentral until https://github.com/gradle/gradle/issues/17295 - // Adding mavenLocal is much more complicated but hopefully isn't required for normal usage of - // Muzzle. - add( - RemoteRepository.Builder( - "MavenCentral", "default", "https://repo.maven.apache.org/maven2/") - .build()) - for (repository in repositories) { - if (repository is MavenArtifactRepository) { - add( - RemoteRepository.Builder( - repository.getName(), - "default", - repository.url.toString()) - .build()) - } - } -}.toList() - val hasRelevantTask = gradle.startParameter.taskNames.any { // removing leading ':' if present val taskName = it.removePrefix(":") @@ -169,18 +147,21 @@ if (hasRelevantTask) { afterEvaluate { var runAfter = muzzle + // the project repositories need to be retrieved after evaluation, before that the list is just empty + val projectRepositories = getProjectRepositories(project) + for (muzzleDirective in muzzleConfig.directives.get()) { - logger.info("configured ${muzzleDirective}") + logger.info("configured $muzzleDirective") if (muzzleDirective.coreJdk.get()) { runAfter = addMuzzleTask(muzzleDirective, null, runAfter) } else { - for (singleVersion in muzzleDirectiveToArtifacts(muzzleDirective, system, session)) { + for (singleVersion in muzzleDirectiveToArtifacts(muzzleDirective, system, session, projectRepositories)) { runAfter = addMuzzleTask(muzzleDirective, singleVersion, runAfter) } if (muzzleDirective.assertInverse.get()) { - for (inverseDirective in inverseOf(muzzleDirective, system, session)) { - for (singleVersion in muzzleDirectiveToArtifacts(inverseDirective, system, session)) { + for (inverseDirective in inverseOf(muzzleDirective, system, session, projectRepositories)) { + for (singleVersion in muzzleDirectiveToArtifacts(inverseDirective, system, session, projectRepositories)) { runAfter = addMuzzleTask(inverseDirective, singleVersion, runAfter) } } @@ -190,6 +171,29 @@ if (hasRelevantTask) { } } +fun getProjectRepositories(project: Project): List { + val projectRepositories = project.repositories + .filterIsInstance() + .map { + RemoteRepository.Builder( + it.name, + "default", + it.url.toString()) + .build() + } + // dependencyResolutionManagement.repositories are not being added to project.repositories, + // they need to be queries separately + if (projectRepositories.isEmpty()) { + // Manually add mavenCentral until https://github.com/gradle/gradle/issues/17295 + // Adding mavenLocal is much more complicated but hopefully isn't required for normal usage of + // Muzzle. + return listOf(RemoteRepository.Builder( + "MavenCentral", "default", "https://repo.maven.apache.org/maven2/") + .build()) + } + return projectRepositories +} + fun createInstrumentationClassloader(): ClassLoader { logger.info("Creating instrumentation class loader for: $path") val muzzleShadowJar = shadowModule.get().archiveFile.get() @@ -311,7 +315,7 @@ fun createClassLoaderForTask(muzzleTaskConfiguration: Configuration): ClassLoade return classpathLoader(muzzleTaskConfiguration + files(muzzleBootstrapShadowJar), ClassLoader.getPlatformClassLoader()) } -fun inverseOf(muzzleDirective: MuzzleDirective, system: RepositorySystem, session: RepositorySystemSession): Set { +fun inverseOf(muzzleDirective: MuzzleDirective, system: RepositorySystem, session: RepositorySystemSession, repos: List): Set { val inverseDirectives = mutableSetOf() val allVersionsArtifact = DefaultArtifact( @@ -327,7 +331,6 @@ fun inverseOf(muzzleDirective: MuzzleDirective, system: RepositorySystem, sessio "jar", muzzleDirective.versions.get()) - val repos = projectRepositories val allRangeRequest = VersionRangeRequest().apply { repositories = repos artifact = allVersionsArtifact @@ -376,7 +379,7 @@ fun filterVersions(range: VersionRangeResult, skipVersions: Set) = seque } }.distinct().take(RANGE_COUNT_LIMIT) -fun muzzleDirectiveToArtifacts(muzzleDirective: MuzzleDirective, system: RepositorySystem, session: RepositorySystemSession) = sequence { +fun muzzleDirectiveToArtifacts(muzzleDirective: MuzzleDirective, system: RepositorySystem, session: RepositorySystemSession, repos: List) = sequence { val directiveArtifact: Artifact = DefaultArtifact( muzzleDirective.group.get(), muzzleDirective.module.get(), @@ -385,7 +388,7 @@ fun muzzleDirectiveToArtifacts(muzzleDirective: MuzzleDirective, system: Reposit muzzleDirective.versions.get()) val rangeRequest = VersionRangeRequest().apply { - repositories = projectRepositories + repositories = repos artifact = directiveArtifact } val rangeResult = system.resolveVersionRange(session, rangeRequest) diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 41d9927a4d4f..249e5832f090 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 aa991fceae6e..ae04661ee733 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index 1b6c787337ff..a69d9cb6c206 100755 --- a/gradlew +++ b/gradlew @@ -205,6 +205,12 @@ set -- \ org.gradle.wrapper.GradleWrapperMain \ "$@" +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + # Use "xargs" to parse quoted args. # # With -n1 it outputs one arg per line, with the quotes and backslashes removed. diff --git a/gradlew.bat b/gradlew.bat index 107acd32c4e6..f127cfd49d40 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -14,7 +14,7 @@ @rem limitations under the License. @rem -@if "%DEBUG%" == "" @echo off +@if "%DEBUG%"=="" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @@ -25,7 +25,7 @@ if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. +if "%DIRNAME%"=="" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @@ -40,7 +40,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto execute +if %ERRORLEVEL% equ 0 goto execute echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. @@ -75,13 +75,15 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar :end @rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd +if %ERRORLEVEL% equ 0 goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% :mainEnd if "%OS%"=="Windows_NT" endlocal diff --git a/include-conventions.gradle.kts b/include-conventions.gradle.kts new file mode 100644 index 000000000000..cfa589b7ba32 --- /dev/null +++ b/include-conventions.gradle.kts @@ -0,0 +1,3 @@ +// this file is only split out from settings.gradle.kts due to a dependabot limitation +// for details see .github/project-root-duplicates/README.md +includeBuild("conventions") diff --git a/instrumentation-annotations-support-testing/src/main/java/io/opentelemetry/javaagent/instrumentation/otelannotations/AbstractWithSpanTest.java b/instrumentation-annotations-support-testing/src/main/java/io/opentelemetry/javaagent/instrumentation/otelannotations/AbstractWithSpanTest.java index 5f10e0d889bd..2d1a03aa8193 100644 --- a/instrumentation-annotations-support-testing/src/main/java/io/opentelemetry/javaagent/instrumentation/otelannotations/AbstractWithSpanTest.java +++ b/instrumentation-annotations-support-testing/src/main/java/io/opentelemetry/javaagent/instrumentation/otelannotations/AbstractWithSpanTest.java @@ -5,11 +5,13 @@ package io.opentelemetry.javaagent.instrumentation.otelannotations; -import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.attributeEntry; +import static io.opentelemetry.api.common.AttributeKey.booleanKey; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; +import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.CODE_FUNCTION; +import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.CODE_NAMESPACE; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.catchThrowable; -import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.trace.SpanKind; import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension; import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; @@ -42,7 +44,8 @@ protected final InstrumentationExtension testing() { @Test void success() { - T future = newTraced().completable(); + AbstractTraced traced = newTraced(); + T future = traced.completable(); complete(future, AbstractTraced.SUCCESS_VALUE); assertThat(getCompleted(future)).isEqualTo(AbstractTraced.SUCCESS_VALUE); @@ -54,12 +57,15 @@ void success() { span.hasName("Traced.completable") .hasKind(SpanKind.INTERNAL) .hasNoParent() - .hasAttributes(Attributes.empty()))); + .hasAttributesSatisfyingExactly( + equalTo(CODE_NAMESPACE, traced.getClass().getName()), + equalTo(CODE_FUNCTION, "completable")))); } @Test void failure() { - T future = newTraced().completable(); + AbstractTraced traced = newTraced(); + T future = traced.completable(); fail(future, AbstractTraced.FAILURE); Throwable thrown = catchThrowable(() -> getCompleted(future)); @@ -74,12 +80,15 @@ void failure() { .hasNoParent() .hasStatus(StatusData.error()) .hasException(AbstractTraced.FAILURE) - .hasAttributes(Attributes.empty()))); + .hasAttributesSatisfyingExactly( + equalTo(CODE_NAMESPACE, traced.getClass().getName()), + equalTo(CODE_FUNCTION, "completable")))); } @Test void canceled() { - T future = newTraced().completable(); + AbstractTraced traced = newTraced(); + T future = traced.completable(); cancel(future); testing.waitAndAssertTraces( @@ -89,13 +98,16 @@ void canceled() { span.hasName("Traced.completable") .hasKind(SpanKind.INTERNAL) .hasNoParent() - .hasAttributes(attributeEntry(canceledKey(), true)))); + .hasAttributesSatisfyingExactly( + equalTo(CODE_NAMESPACE, traced.getClass().getName()), + equalTo(CODE_FUNCTION, "completable"), + equalTo(booleanKey(canceledKey()), true)))); } @Test void immediateSuccess() { - assertThat(getCompleted(newTraced().alreadySucceeded())) - .isEqualTo(AbstractTraced.SUCCESS_VALUE); + AbstractTraced traced = newTraced(); + assertThat(getCompleted(traced.alreadySucceeded())).isEqualTo(AbstractTraced.SUCCESS_VALUE); testing.waitAndAssertTraces( trace -> @@ -104,12 +116,15 @@ void immediateSuccess() { span.hasName("Traced.alreadySucceeded") .hasKind(SpanKind.INTERNAL) .hasNoParent() - .hasAttributes(Attributes.empty()))); + .hasAttributesSatisfyingExactly( + equalTo(CODE_NAMESPACE, traced.getClass().getName()), + equalTo(CODE_FUNCTION, "alreadySucceeded")))); } @Test void immediateFailure() { - Throwable error = catchThrowable(() -> getCompleted(newTraced().alreadyFailed())); + AbstractTraced traced = newTraced(); + Throwable error = catchThrowable(() -> getCompleted(traced.alreadyFailed())); assertThat(unwrapError(error)).isEqualTo(AbstractTraced.FAILURE); testing.waitAndAssertTraces( @@ -121,6 +136,8 @@ void immediateFailure() { .hasNoParent() .hasStatus(StatusData.error()) .hasException(AbstractTraced.FAILURE) - .hasAttributes(Attributes.empty()))); + .hasAttributesSatisfyingExactly( + equalTo(CODE_NAMESPACE, traced.getClass().getName()), + equalTo(CODE_FUNCTION, "alreadyFailed")))); } } diff --git a/instrumentation-annotations-support/src/test/java/io/opentelemetry/instrumentation/api/annotation/support/MethodCacheTest.java b/instrumentation-annotations-support/src/test/java/io/opentelemetry/instrumentation/api/annotation/support/MethodCacheTest.java index 9d422001bdc2..13c0ef4fa581 100644 --- a/instrumentation-annotations-support/src/test/java/io/opentelemetry/instrumentation/api/annotation/support/MethodCacheTest.java +++ b/instrumentation-annotations-support/src/test/java/io/opentelemetry/instrumentation/api/annotation/support/MethodCacheTest.java @@ -83,6 +83,9 @@ void doesNotComputeItemIfPresent() throws Exception { } static class TestClass { + public static void method() {} + + private TestClass() {} } } diff --git a/instrumentation-api-semconv/build.gradle.kts b/instrumentation-api-semconv/build.gradle.kts index 852fed4416e1..e060d3e82bdd 100644 --- a/instrumentation-api-semconv/build.gradle.kts +++ b/instrumentation-api-semconv/build.gradle.kts @@ -17,6 +17,9 @@ dependencies { compileOnly("com.google.auto.value:auto-value-annotations") annotationProcessor("com.google.auto.value:auto-value") + implementation("com.fasterxml.jackson.core:jackson-core:2.13.3") + implementation("com.fasterxml.jackson.core:jackson-databind:2.13.3") + testImplementation(project(":testing-common")) testImplementation("io.opentelemetry:opentelemetry-sdk-metrics") testImplementation("io.opentelemetry:opentelemetry-sdk-testing") @@ -38,22 +41,4 @@ tasks { sourcesJar { dependsOn("generateJflex") } - - val testStatementSanitizerConfig by registering(Test::class) { - filter { - includeTestsMatching("StatementSanitizationConfigTest") - } - include("**/StatementSanitizationConfigTest.*") - jvmArgs("-Dotel.instrumentation.common.db-statement-sanitizer.enabled=false") - } - - test { - filter { - excludeTestsMatching("StatementSanitizationConfigTest") - } - } - - check { - dependsOn(testStatementSanitizerConfig) - } } diff --git a/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/db/RedisCommandSanitizer.java b/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/db/RedisCommandSanitizer.java index f729c0c53357..98e94b3c580b 100644 --- a/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/db/RedisCommandSanitizer.java +++ b/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/db/RedisCommandSanitizer.java @@ -8,10 +8,6 @@ import static java.util.Arrays.asList; import static java.util.Collections.unmodifiableMap; -import io.opentelemetry.instrumentation.api.db.RedisCommandSanitizer.CommandSanitizer.CommandAndNumArgs; -import io.opentelemetry.instrumentation.api.db.RedisCommandSanitizer.CommandSanitizer.Eval; -import io.opentelemetry.instrumentation.api.db.RedisCommandSanitizer.CommandSanitizer.KeepAllArgs; -import io.opentelemetry.instrumentation.api.db.RedisCommandSanitizer.CommandSanitizer.MultiKeyValue; import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.List; @@ -353,8 +349,18 @@ public final class RedisCommandSanitizer { SANITIZERS = unmodifiableMap(sanitizers); } - public static String sanitize(String command, List args) { - if (!StatementSanitizationConfig.isStatementSanitizationEnabled()) { + public static RedisCommandSanitizer create(boolean statementSanitizationEnabled) { + return new RedisCommandSanitizer(statementSanitizationEnabled); + } + + private final boolean statementSanitizationEnabled; + + private RedisCommandSanitizer(boolean statementSanitizationEnabled) { + this.statementSanitizationEnabled = statementSanitizationEnabled; + } + + public String sanitize(String command, List args) { + if (!statementSanitizationEnabled) { return KeepAllArgs.INSTANCE.sanitize(command, args); } return SANITIZERS @@ -362,107 +368,105 @@ public static String sanitize(String command, List args) { .sanitize(command, args); } - public interface CommandSanitizer { + interface CommandSanitizer { String sanitize(String command, List args); + } - static String argToString(Object arg) { - if (arg instanceof byte[]) { - return new String((byte[]) arg, StandardCharsets.UTF_8); - } else { - return String.valueOf(arg); + enum KeepAllArgs implements CommandSanitizer { + INSTANCE; + + @Override + public String sanitize(String command, List args) { + StringBuilder sanitized = new StringBuilder(command); + for (Object arg : args) { + sanitized.append(" ").append(argToString(arg)); } + return sanitized.toString(); } + } - enum KeepAllArgs implements CommandSanitizer { - INSTANCE; + // keeps only a chosen number of arguments + // example for num=2: CMD arg1 arg2 ? ? + static final class CommandAndNumArgs implements CommandSanitizer { + private final int numOfArgsToKeep; - @Override - public String sanitize(String command, List args) { - StringBuilder sanitized = new StringBuilder(command); - for (Object arg : args) { - sanitized.append(" ").append(argToString(arg)); - } - return sanitized.toString(); - } + CommandAndNumArgs(int numOfArgsToKeep) { + this.numOfArgsToKeep = numOfArgsToKeep; } - // keeps only a chosen number of arguments - // example for num=2: CMD arg1 arg2 ? ? - class CommandAndNumArgs implements CommandSanitizer { - private final int numOfArgsToKeep; - - public CommandAndNumArgs(int numOfArgsToKeep) { - this.numOfArgsToKeep = numOfArgsToKeep; + @Override + public String sanitize(String command, List args) { + StringBuilder sanitized = new StringBuilder(command); + for (int i = 0; i < numOfArgsToKeep && i < args.size(); ++i) { + sanitized.append(" ").append(argToString(args.get(i))); } - - @Override - public String sanitize(String command, List args) { - StringBuilder sanitized = new StringBuilder(command); - for (int i = 0; i < numOfArgsToKeep && i < args.size(); ++i) { - sanitized.append(" ").append(argToString(args.get(i))); - } - for (int i = numOfArgsToKeep; i < args.size(); ++i) { - sanitized.append(" ?"); - } - return sanitized.toString(); + for (int i = numOfArgsToKeep; i < args.size(); ++i) { + sanitized.append(" ?"); } + return sanitized.toString(); } + } - // keeps only chosen number of arguments and then every second one - // example for num=2: CMD arg1 arg2 key1 ? key2 ? - class MultiKeyValue implements CommandSanitizer { - private final int numOfArgsBeforeKeyValue; + // keeps only chosen number of arguments and then every second one + // example for num=2: CMD arg1 arg2 key1 ? key2 ? + static final class MultiKeyValue implements CommandSanitizer { + private final int numOfArgsBeforeKeyValue; - public MultiKeyValue(int numOfArgsBeforeKeyValue) { - this.numOfArgsBeforeKeyValue = numOfArgsBeforeKeyValue; - } + MultiKeyValue(int numOfArgsBeforeKeyValue) { + this.numOfArgsBeforeKeyValue = numOfArgsBeforeKeyValue; + } - @Override - public String sanitize(String command, List args) { - StringBuilder sanitized = new StringBuilder(command); - // append all "initial" arguments before key-value pairs start - for (int i = 0; i < numOfArgsBeforeKeyValue && i < args.size(); ++i) { - sanitized.append(" ").append(argToString(args.get(i))); - } + @Override + public String sanitize(String command, List args) { + StringBuilder sanitized = new StringBuilder(command); + // append all "initial" arguments before key-value pairs start + for (int i = 0; i < numOfArgsBeforeKeyValue && i < args.size(); ++i) { + sanitized.append(" ").append(argToString(args.get(i))); + } - // loop over keys only - for (int i = numOfArgsBeforeKeyValue; i < args.size(); i += 2) { - sanitized.append(" ").append(argToString(args.get(i))).append(" ?"); - } - return sanitized.toString(); + // loop over keys only + for (int i = numOfArgsBeforeKeyValue; i < args.size(); i += 2) { + sanitized.append(" ").append(argToString(args.get(i))).append(" ?"); } + return sanitized.toString(); } + } - enum Eval implements CommandSanitizer { - INSTANCE; - - @Override - public String sanitize(String command, List args) { - StringBuilder sanitized = new StringBuilder(command); - - // get the number of keys passed from the command itself (second arg) - int numberOfKeys = 0; - if (args.size() > 2) { - try { - numberOfKeys = Integer.parseInt(argToString(args.get(1))); - } catch (NumberFormatException ignored) { - // Ignore - } - } + enum Eval implements CommandSanitizer { + INSTANCE; - int i = 0; - // log the script, number of keys and all keys - for (; i < (numberOfKeys + 2) && i < args.size(); ++i) { - sanitized.append(" ").append(argToString(args.get(i))); - } - // mask the rest - for (; i < args.size(); ++i) { - sanitized.append(" ?"); + @Override + public String sanitize(String command, List args) { + StringBuilder sanitized = new StringBuilder(command); + + // get the number of keys passed from the command itself (second arg) + int numberOfKeys = 0; + if (args.size() > 2) { + try { + numberOfKeys = Integer.parseInt(argToString(args.get(1))); + } catch (NumberFormatException ignored) { + // Ignore } - return sanitized.toString(); } + + int i = 0; + // log the script, number of keys and all keys + for (; i < (numberOfKeys + 2) && i < args.size(); ++i) { + sanitized.append(" ").append(argToString(args.get(i))); + } + // mask the rest + for (; i < args.size(); ++i) { + sanitized.append(" ?"); + } + return sanitized.toString(); } } - private RedisCommandSanitizer() {} + static String argToString(Object arg) { + if (arg instanceof byte[]) { + return new String((byte[]) arg, StandardCharsets.UTF_8); + } else { + return String.valueOf(arg); + } + } } diff --git a/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/db/SqlStatementSanitizer.java b/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/db/SqlStatementSanitizer.java index 077645442c8c..a9175364a897 100644 --- a/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/db/SqlStatementSanitizer.java +++ b/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/db/SqlStatementSanitizer.java @@ -5,7 +5,6 @@ package io.opentelemetry.instrumentation.api.db; -import static io.opentelemetry.instrumentation.api.db.StatementSanitizationConfig.isStatementSanitizationEnabled; import static io.opentelemetry.instrumentation.api.internal.SupportabilityMetrics.CounterNames.SQL_STATEMENT_SANITIZER_CACHE_MISS; import com.google.auto.value.AutoValue; @@ -23,12 +22,22 @@ public final class SqlStatementSanitizer { private static final Cache sqlToStatementInfoCache = Cache.bounded(1000); - public static SqlStatementInfo sanitize(@Nullable String statement) { + public static SqlStatementSanitizer create(boolean statementSanitizationEnabled) { + return new SqlStatementSanitizer(statementSanitizationEnabled); + } + + private final boolean statementSanitizationEnabled; + + private SqlStatementSanitizer(boolean statementSanitizationEnabled) { + this.statementSanitizationEnabled = statementSanitizationEnabled; + } + + public SqlStatementInfo sanitize(@Nullable String statement) { return sanitize(statement, SqlDialect.DEFAULT); } - public static SqlStatementInfo sanitize(@Nullable String statement, SqlDialect dialect) { - if (!isStatementSanitizationEnabled() || statement == null) { + public SqlStatementInfo sanitize(@Nullable String statement, SqlDialect dialect) { + if (!statementSanitizationEnabled || statement == null) { return SqlStatementInfo.create(statement, null, null); } return sqlToStatementInfoCache.computeIfAbsent( @@ -50,6 +59,4 @@ static CacheKey create(String statement, SqlDialect dialect) { abstract SqlDialect getDialect(); } - - private SqlStatementSanitizer() {} } diff --git a/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/db/StatementSanitizationConfig.java b/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/db/StatementSanitizationConfig.java deleted file mode 100644 index 17665024b752..000000000000 --- a/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/db/StatementSanitizationConfig.java +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.instrumentation.api.db; - -import io.opentelemetry.instrumentation.api.config.Config; - -/** DB statement sanitization is always enabled by default, you have to manually disable it. */ -final class StatementSanitizationConfig { - - private static final boolean STATEMENT_SANITIZATION_ENABLED = - Config.get().getBoolean("otel.instrumentation.common.db-statement-sanitizer.enabled", true); - - static boolean isStatementSanitizationEnabled() { - return STATEMENT_SANITIZATION_ENABLED; - } - - private StatementSanitizationConfig() {} -} diff --git a/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/code/CodeSpanNameExtractor.java b/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/code/CodeSpanNameExtractor.java index 949eb8531cb4..cc249381f488 100644 --- a/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/code/CodeSpanNameExtractor.java +++ b/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/code/CodeSpanNameExtractor.java @@ -6,8 +6,7 @@ package io.opentelemetry.instrumentation.api.instrumenter.code; import io.opentelemetry.instrumentation.api.instrumenter.SpanNameExtractor; -import io.opentelemetry.instrumentation.api.util.ClassNames; -import javax.annotation.Nullable; +import io.opentelemetry.instrumentation.api.internal.ClassNames; /** * A helper {@link SpanNameExtractor} implementation for instrumentations that target specific Java @@ -33,11 +32,15 @@ private CodeSpanNameExtractor(CodeAttributesGetter getter) { public String extract(REQUEST request) { Class cls = getter.codeClass(request); String className = cls != null ? ClassNames.simpleName(cls) : ""; - String methodName = defaultString(getter.methodName(request)); + int lambdaIdx = className.indexOf("$$Lambda$"); + if (lambdaIdx > -1) { + // need to produce low-cardinality name, since lambda class names change with each restart + className = className.substring(0, lambdaIdx + "$$Lambda$".length()); + } + String methodName = getter.methodName(request); + if (methodName == null) { + return className; + } return className + "." + methodName; } - - private static String defaultString(@Nullable String s) { - return s == null ? "" : s; - } } diff --git a/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/db/DbClientSpanNameExtractor.java b/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/db/DbClientSpanNameExtractor.java index 661255443661..00afa6d5c5fd 100644 --- a/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/db/DbClientSpanNameExtractor.java +++ b/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/db/DbClientSpanNameExtractor.java @@ -82,6 +82,9 @@ public String extract(REQUEST request) { private static final class SqlClientSpanNameExtractor extends DbClientSpanNameExtractor { + // a dedicated sanitizer just for extracting the operation and table name + private static final SqlStatementSanitizer sanitizer = SqlStatementSanitizer.create(true); + private final SqlClientAttributesGetter getter; private SqlClientSpanNameExtractor(SqlClientAttributesGetter getter) { @@ -91,8 +94,7 @@ private SqlClientSpanNameExtractor(SqlClientAttributesGetter getter) { @Override public String extract(REQUEST request) { String dbName = getter.name(request); - SqlStatementInfo sanitizedStatement = - SqlStatementSanitizer.sanitize(getter.rawStatement(request)); + SqlStatementInfo sanitizedStatement = sanitizer.sanitize(getter.rawStatement(request)); return computeSpanName( dbName, sanitizedStatement.getOperation(), sanitizedStatement.getTable()); } diff --git a/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/db/SqlClientAttributesExtractor.java b/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/db/SqlClientAttributesExtractor.java index d98ef1ea2829..3dc6d76a9a0d 100644 --- a/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/db/SqlClientAttributesExtractor.java +++ b/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/db/SqlClientAttributesExtractor.java @@ -20,7 +20,7 @@ * attributes. This class is designed with SQL (or SQL-like) database clients in mind. * *

It sets the same set of attributes as {@link DbClientAttributesExtractor} plus an additional - * {@linkplain SemanticAttributes#DB_SQL_TABLE db.sql.table} attrubute. The raw SQL + * {@linkplain SemanticAttributes#DB_SQL_TABLE db.sql.table} attribute. The raw SQL * statements returned by the {@link SqlClientAttributesGetter#rawStatement(Object)} method are * sanitized before use, all statement parameters are removed. */ @@ -44,19 +44,22 @@ public static SqlClientAttributesExtractorBuilder dbTableAttribute; + private final SqlStatementSanitizer sanitizer; SqlClientAttributesExtractor( - SqlClientAttributesGetter getter, AttributeKey dbTableAttribute) { + SqlClientAttributesGetter getter, + AttributeKey dbTableAttribute, + SqlStatementSanitizer sanitizer) { super(getter); this.dbTableAttribute = dbTableAttribute; + this.sanitizer = sanitizer; } @Override public void onStart(AttributesBuilder attributes, Context parentContext, REQUEST request) { super.onStart(attributes, parentContext, request); - SqlStatementInfo sanitizedStatement = - SqlStatementSanitizer.sanitize(getter.rawStatement(request)); + SqlStatementInfo sanitizedStatement = sanitizer.sanitize(getter.rawStatement(request)); internalSet(attributes, SemanticAttributes.DB_STATEMENT, sanitizedStatement.getFullStatement()); internalSet(attributes, SemanticAttributes.DB_OPERATION, sanitizedStatement.getOperation()); internalSet(attributes, dbTableAttribute, sanitizedStatement.getTable()); diff --git a/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/db/SqlClientAttributesExtractorBuilder.java b/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/db/SqlClientAttributesExtractorBuilder.java index 9b872f290181..1d0793911cc5 100644 --- a/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/db/SqlClientAttributesExtractorBuilder.java +++ b/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/db/SqlClientAttributesExtractorBuilder.java @@ -7,7 +7,9 @@ import static java.util.Objects.requireNonNull; +import com.google.errorprone.annotations.CanIgnoreReturnValue; import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.instrumentation.api.db.SqlStatementSanitizer; import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; /** A builder of {@link SqlClientAttributesExtractor}. */ @@ -15,6 +17,7 @@ public final class SqlClientAttributesExtractorBuilder { final SqlClientAttributesGetter getter; AttributeKey dbTableAttribute = SemanticAttributes.DB_SQL_TABLE; + boolean statementSanitizationEnabled = true; SqlClientAttributesExtractorBuilder(SqlClientAttributesGetter getter) { this.getter = getter; @@ -28,17 +31,31 @@ public final class SqlClientAttributesExtractorBuilder { * @param dbTableAttribute The {@link AttributeKey} under which the table extracted by the {@link * SqlClientAttributesExtractor} will be stored. */ + @CanIgnoreReturnValue public SqlClientAttributesExtractorBuilder setTableAttribute( AttributeKey dbTableAttribute) { this.dbTableAttribute = requireNonNull(dbTableAttribute); return this; } + /** + * Sets whether the {@code db.statement} attribute extracted by the constructed {@link + * SqlClientAttributesExtractor} should be sanitized. If set to {@code true}, all parameters that + * can potentially contain sensitive information will be masked. Enabled by default. + */ + @CanIgnoreReturnValue + public SqlClientAttributesExtractorBuilder setStatementSanitizationEnabled( + boolean statementSanitizationEnabled) { + this.statementSanitizationEnabled = statementSanitizationEnabled; + return this; + } + /** * Returns a new {@link SqlClientAttributesExtractor} with the settings of this {@link * SqlClientAttributesExtractorBuilder}. */ public SqlClientAttributesExtractor build() { - return new SqlClientAttributesExtractor<>(getter, dbTableAttribute); + return new SqlClientAttributesExtractor<>( + getter, dbTableAttribute, SqlStatementSanitizer.create(statementSanitizationEnabled)); } } diff --git a/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/http/CapturedHttpHeadersUtil.java b/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/http/CapturedHttpHeadersUtil.java index 8ef3a73dca40..682cb3da09b0 100644 --- a/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/http/CapturedHttpHeadersUtil.java +++ b/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/http/CapturedHttpHeadersUtil.java @@ -5,11 +5,9 @@ package io.opentelemetry.instrumentation.api.instrumenter.http; -import static java.util.Collections.emptyList; import static java.util.Collections.unmodifiableList; import io.opentelemetry.api.common.AttributeKey; -import io.opentelemetry.instrumentation.api.config.Config; import java.util.List; import java.util.Locale; import java.util.concurrent.ConcurrentHashMap; @@ -18,28 +16,6 @@ final class CapturedHttpHeadersUtil { - private static final String CLIENT_REQUEST_PROPERTY = - "otel.instrumentation.http.capture-headers.client.request"; - private static final String CLIENT_RESPONSE_PROPERTY = - "otel.instrumentation.http.capture-headers.client.response"; - private static final String SERVER_REQUEST_PROPERTY = - "otel.instrumentation.http.capture-headers.server.request"; - private static final String SERVER_RESPONSE_PROPERTY = - "otel.instrumentation.http.capture-headers.server.response"; - - static final List clientRequestHeaders; - static final List clientResponseHeaders; - static final List serverRequestHeaders; - static final List serverResponseHeaders; - - static { - Config config = Config.get(); - clientRequestHeaders = config.getList(CLIENT_REQUEST_PROPERTY, emptyList()); - clientResponseHeaders = config.getList(CLIENT_RESPONSE_PROPERTY, emptyList()); - serverRequestHeaders = config.getList(SERVER_REQUEST_PROPERTY, emptyList()); - serverResponseHeaders = config.getList(SERVER_RESPONSE_PROPERTY, emptyList()); - } - // these are naturally bounded because they only store keys listed in // otel.instrumentation.http.capture-headers.server.request and // otel.instrumentation.http.capture-headers.server.response diff --git a/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/http/HttpClientAttributesExtractor.java b/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/http/HttpClientAttributesExtractor.java index ae49e32d048b..e5053a40ee1e 100644 --- a/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/http/HttpClientAttributesExtractor.java +++ b/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/http/HttpClientAttributesExtractor.java @@ -54,7 +54,50 @@ public static HttpClientAttributesExtractorBuilder start of path + int index; + int atIndex = -1; + for (index = schemeEndIndex + 3; index < len; index++) { + char c = url.charAt(index); + + if (c == '@') { + atIndex = index; + } + + if (c == '/' || c == '?' || c == '#') { + break; + } + } + + if (atIndex == -1 || atIndex == len - 1) { + return url; + } + return url.substring(0, schemeEndIndex + 3) + url.substring(atIndex + 1); } @Override diff --git a/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/http/HttpClientAttributesExtractorBuilder.java b/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/http/HttpClientAttributesExtractorBuilder.java index 8099eb1be095..56bcab57708b 100644 --- a/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/http/HttpClientAttributesExtractorBuilder.java +++ b/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/http/HttpClientAttributesExtractorBuilder.java @@ -5,14 +5,17 @@ package io.opentelemetry.instrumentation.api.instrumenter.http; +import static java.util.Collections.emptyList; + +import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.util.List; /** A builder of {@link HttpClientAttributesExtractor}. */ public final class HttpClientAttributesExtractorBuilder { final HttpClientAttributesGetter getter; - List capturedRequestHeaders = CapturedHttpHeadersUtil.clientRequestHeaders; - List capturedResponseHeaders = CapturedHttpHeadersUtil.clientResponseHeaders; + List capturedRequestHeaders = emptyList(); + List capturedResponseHeaders = emptyList(); HttpClientAttributesExtractorBuilder(HttpClientAttributesGetter getter) { this.getter = getter; @@ -29,6 +32,7 @@ public final class HttpClientAttributesExtractorBuilder { * * @param requestHeaders A list of HTTP header names. */ + @CanIgnoreReturnValue public HttpClientAttributesExtractorBuilder setCapturedRequestHeaders( List requestHeaders) { this.capturedRequestHeaders = requestHeaders; @@ -47,6 +51,7 @@ public HttpClientAttributesExtractorBuilder setCapturedReques * * @param responseHeaders A list of HTTP header names. */ + @CanIgnoreReturnValue public HttpClientAttributesExtractorBuilder setCapturedResponseHeaders( List responseHeaders) { this.capturedResponseHeaders = responseHeaders; diff --git a/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/http/HttpClientMetrics.java b/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/http/HttpClientMetrics.java index 9323ce8c5c87..ab2aede2a05d 100644 --- a/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/http/HttpClientMetrics.java +++ b/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/http/HttpClientMetrics.java @@ -5,19 +5,23 @@ package io.opentelemetry.instrumentation.api.instrumenter.http; -import static io.opentelemetry.instrumentation.api.instrumenter.http.TemporaryMetricsView.applyClientDurationView; +import static io.opentelemetry.instrumentation.api.instrumenter.http.TemporaryMetricsView.applyClientDurationAndSizeView; import static java.util.logging.Level.FINE; import com.google.auto.value.AutoValue; +import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.metrics.DoubleHistogram; +import io.opentelemetry.api.metrics.LongHistogram; import io.opentelemetry.api.metrics.Meter; import io.opentelemetry.context.Context; import io.opentelemetry.context.ContextKey; import io.opentelemetry.instrumentation.api.instrumenter.OperationListener; import io.opentelemetry.instrumentation.api.instrumenter.OperationMetrics; +import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; import java.util.concurrent.TimeUnit; import java.util.logging.Logger; +import javax.annotation.Nullable; /** * {@link OperationListener} which keeps track of T getAttribute(AttributeKey key, Attributes... attributesList) { + for (Attributes attributes : attributesList) { + T value = attributes.get(key); + if (value != null) { + return value; + } + } + return null; } @AutoValue diff --git a/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/http/HttpCommonAttributesExtractor.java b/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/http/HttpCommonAttributesExtractor.java index 1d3bb659c25b..7aac2dbf80ac 100644 --- a/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/http/HttpCommonAttributesExtractor.java +++ b/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/http/HttpCommonAttributesExtractor.java @@ -8,6 +8,8 @@ import static io.opentelemetry.instrumentation.api.instrumenter.http.CapturedHttpHeadersUtil.lowercase; import static io.opentelemetry.instrumentation.api.instrumenter.http.CapturedHttpHeadersUtil.requestAttributeKey; import static io.opentelemetry.instrumentation.api.instrumenter.http.CapturedHttpHeadersUtil.responseAttributeKey; +import static io.opentelemetry.instrumentation.api.instrumenter.http.SemanticAttributes.HTTP_REQUEST_HEADERS; +import static io.opentelemetry.instrumentation.api.instrumenter.http.SemanticAttributes.HTTP_RESPONSE_HEADERS; import static io.opentelemetry.instrumentation.api.internal.AttributesExtractorUtil.internalSet; import io.opentelemetry.api.common.AttributesBuilder; @@ -42,6 +44,11 @@ public void onStart(AttributesBuilder attributes, Context parentContext, REQUEST internalSet(attributes, SemanticAttributes.HTTP_METHOD, getter.method(request)); internalSet(attributes, SemanticAttributes.HTTP_USER_AGENT, userAgent(request)); + String reqHeaders = requestHeaders(request, null); + if (reqHeaders != null) { + internalSet(attributes, HTTP_REQUEST_HEADERS, reqHeaders); + } + for (String name : capturedRequestHeaders) { List values = getter.requestHeader(request, name); if (!values.isEmpty()) { @@ -59,27 +66,23 @@ public void onEnd( @Nullable Throwable error) { internalSet( - attributes, - SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH, - getter.requestContentLength(request, response)); - internalSet( - attributes, - SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH_UNCOMPRESSED, - getter.requestContentLengthUncompressed(request, response)); + attributes, SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH, requestContentLength(request)); if (response != null) { - Integer statusCode = getter.statusCode(request, response); + Integer statusCode = getter.statusCode(request, response, error); if (statusCode != null && statusCode > 0) { internalSet(attributes, SemanticAttributes.HTTP_STATUS_CODE, (long) statusCode); } + internalSet( attributes, SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH, - getter.responseContentLength(request, response)); - internalSet( - attributes, - SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH_UNCOMPRESSED, - getter.responseContentLengthUncompressed(request, response)); + responseContentLength(request, response)); + + String resHeaders = responseHeaders(request, response); + if (resHeaders != null) { + internalSet(attributes, HTTP_RESPONSE_HEADERS, resHeaders); + } for (String name : capturedResponseHeaders) { List values = getter.responseHeader(request, response, name); @@ -90,13 +93,47 @@ public void onEnd( } } + @Nullable + private String requestHeaders(REQUEST request, @Nullable RESPONSE response) { + return getter.requestHeaders(request, response); + } + + @Nullable + private String responseHeaders(REQUEST request, @Nullable RESPONSE response) { + return getter.responseHeaders(request, response); + } + @Nullable private String userAgent(REQUEST request) { return firstHeaderValue(getter.requestHeader(request, "user-agent")); } + @Nullable + private Long requestContentLength(REQUEST request) { + return parseNumber(firstHeaderValue(getter.requestHeader(request, "content-length"))); + } + + @Nullable + private Long responseContentLength(REQUEST request, RESPONSE response) { + return parseNumber( + firstHeaderValue(getter.responseHeader(request, response, "content-length"))); + } + @Nullable static String firstHeaderValue(List values) { return values.isEmpty() ? null : values.get(0); } + + @Nullable + private static Long parseNumber(@Nullable String number) { + if (number == null) { + return null; + } + try { + return Long.parseLong(number); + } catch (NumberFormatException e) { + // not a number + return null; + } + } } diff --git a/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/http/HttpCommonAttributesGetter.java b/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/http/HttpCommonAttributesGetter.java index 8cf034114934..59f69776b25b 100644 --- a/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/http/HttpCommonAttributesGetter.java +++ b/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/http/HttpCommonAttributesGetter.java @@ -27,25 +27,12 @@ public interface HttpCommonAttributesGetter { */ List requestHeader(REQUEST request, String name); - // Attributes which are not always available when the request is ready. - - /** - * Extracts the {@code http.request_content_length} span attribute. - * - *

This is called from {@link Instrumenter#end(Context, Object, Object, Throwable)}, whether - * {@code response} is {@code null} or not. - */ @Nullable - Long requestContentLength(REQUEST request, @Nullable RESPONSE response); + default String requestHeaders(REQUEST request, @Nullable RESPONSE response) { + return null; + } - /** - * Extracts the {@code http.request_content_length_uncompressed} span attribute. - * - *

This is called from {@link Instrumenter#end(Context, Object, Object, Throwable)}, whether - * {@code response} is {@code null} or not. - */ - @Nullable - Long requestContentLengthUncompressed(REQUEST request, @Nullable RESPONSE response); + // Attributes which are not always available when the request is ready. /** * Extracts the {@code http.status_code} span attribute. @@ -54,25 +41,7 @@ public interface HttpCommonAttributesGetter { * {@code response} is non-{@code null}. */ @Nullable - Integer statusCode(REQUEST request, RESPONSE response); - - /** - * Extracts the {@code http.response_content_length} span attribute. - * - *

This is called from {@link Instrumenter#end(Context, Object, Object, Throwable)}, only when - * {@code response} is non-{@code null}. - */ - @Nullable - Long responseContentLength(REQUEST request, RESPONSE response); - - /** - * Extracts the {@code http.response_content_length_uncompressed} span attribute. - * - *

This is called from {@link Instrumenter#end(Context, Object, Object, Throwable)}, only when - * {@code response} is non-{@code null}. - */ - @Nullable - Long responseContentLengthUncompressed(REQUEST request, RESPONSE response); + Integer statusCode(REQUEST request, RESPONSE response, @Nullable Throwable error); /** * Extracts all values of header named {@code name} from the response, or an empty list if there @@ -85,4 +54,9 @@ public interface HttpCommonAttributesGetter { * returned instead. */ List responseHeader(REQUEST request, RESPONSE response, String name); + + @Nullable + default String responseHeaders(REQUEST request, RESPONSE response) { + return null; + } } diff --git a/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/http/HttpServerAttributesExtractor.java b/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/http/HttpServerAttributesExtractor.java index d2c5bfe764a6..7b23a3fa3757 100644 --- a/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/http/HttpServerAttributesExtractor.java +++ b/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/http/HttpServerAttributesExtractor.java @@ -13,6 +13,8 @@ import io.opentelemetry.api.common.AttributesBuilder; import io.opentelemetry.context.Context; +import io.opentelemetry.instrumentation.api.instrumenter.net.NetServerAttributesGetter; +import io.opentelemetry.instrumentation.api.instrumenter.net.internal.InternalNetServerAttributesExtractor; import io.opentelemetry.instrumentation.api.internal.SpanKey; import io.opentelemetry.instrumentation.api.internal.SpanKeyProvider; import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; @@ -36,8 +38,9 @@ public final class HttpServerAttributesExtractor /** Creates the HTTP server attributes extractor with default configuration. */ public static HttpServerAttributesExtractor create( - HttpServerAttributesGetter getter) { - return builder(getter).build(); + HttpServerAttributesGetter httpAttributesGetter, + NetServerAttributesGetter netAttributesGetter) { + return builder(httpAttributesGetter, netAttributesGetter).build(); } /** @@ -45,26 +48,36 @@ public static HttpServerAttributesExtractor HttpServerAttributesExtractorBuilder builder( - HttpServerAttributesGetter getter) { - return new HttpServerAttributesExtractorBuilder<>(getter); + HttpServerAttributesGetter httpAttributesGetter, + NetServerAttributesGetter netAttributesGetter) { + return new HttpServerAttributesExtractorBuilder<>(httpAttributesGetter, netAttributesGetter); } + private final NetServerAttributesGetter netAttributesGetter; private final Function httpRouteHolderGetter; HttpServerAttributesExtractor( - HttpServerAttributesGetter getter, + HttpServerAttributesGetter httpAttributesGetter, + NetServerAttributesGetter netAttributesGetter, List capturedRequestHeaders, List capturedResponseHeaders) { - this(getter, capturedRequestHeaders, capturedResponseHeaders, HttpRouteHolder::getRoute); + this( + httpAttributesGetter, + netAttributesGetter, + capturedRequestHeaders, + capturedResponseHeaders, + HttpRouteHolder::getRoute); } // visible for tests HttpServerAttributesExtractor( - HttpServerAttributesGetter getter, + HttpServerAttributesGetter httpAttributesGetter, + NetServerAttributesGetter netAttributesGetter, List capturedRequestHeaders, List responseHeaders, Function httpRouteHolderGetter) { - super(getter, capturedRequestHeaders, responseHeaders); + super(httpAttributesGetter, capturedRequestHeaders, responseHeaders); + this.netAttributesGetter = netAttributesGetter; this.httpRouteHolderGetter = httpRouteHolderGetter; } @@ -76,11 +89,12 @@ public void onStart(AttributesBuilder attributes, Context parentContext, REQUEST String forwardedProto = forwardedProto(request); String value = forwardedProto != null ? forwardedProto : getter.scheme(request); internalSet(attributes, SemanticAttributes.HTTP_SCHEME, value); - internalSet(attributes, SemanticAttributes.HTTP_HOST, host(request)); internalSet(attributes, SemanticAttributes.HTTP_TARGET, getter.target(request)); internalSet(attributes, SemanticAttributes.HTTP_ROUTE, getter.route(request)); - internalSet(attributes, SemanticAttributes.HTTP_SERVER_NAME, getter.serverName(request)); internalSet(attributes, SemanticAttributes.HTTP_CLIENT_IP, clientIp(request)); + + InternalNetServerAttributesExtractor.onStart( + netAttributesGetter, attributes, request, host(request)); } @Override diff --git a/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/http/HttpServerAttributesExtractorBuilder.java b/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/http/HttpServerAttributesExtractorBuilder.java index 568ecfee9341..12e7bc5b3368 100644 --- a/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/http/HttpServerAttributesExtractorBuilder.java +++ b/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/http/HttpServerAttributesExtractorBuilder.java @@ -5,17 +5,25 @@ package io.opentelemetry.instrumentation.api.instrumenter.http; +import static java.util.Collections.emptyList; + +import com.google.errorprone.annotations.CanIgnoreReturnValue; +import io.opentelemetry.instrumentation.api.instrumenter.net.NetServerAttributesGetter; import java.util.List; /** A builder of {@link HttpServerAttributesExtractor}. */ public final class HttpServerAttributesExtractorBuilder { - final HttpServerAttributesGetter getter; - List capturedRequestHeaders = CapturedHttpHeadersUtil.serverRequestHeaders; - List capturedResponseHeaders = CapturedHttpHeadersUtil.serverResponseHeaders; + final HttpServerAttributesGetter httpAttributesGetter; + final NetServerAttributesGetter netAttributesGetter; + List capturedRequestHeaders = emptyList(); + List capturedResponseHeaders = emptyList(); - HttpServerAttributesExtractorBuilder(HttpServerAttributesGetter getter) { - this.getter = getter; + HttpServerAttributesExtractorBuilder( + HttpServerAttributesGetter httpAttributesGetter, + NetServerAttributesGetter netAttributesGetter) { + this.httpAttributesGetter = httpAttributesGetter; + this.netAttributesGetter = netAttributesGetter; } /** @@ -29,6 +37,7 @@ public final class HttpServerAttributesExtractorBuilder { * * @param requestHeaders A list of HTTP header names. */ + @CanIgnoreReturnValue public HttpServerAttributesExtractorBuilder setCapturedRequestHeaders( List requestHeaders) { this.capturedRequestHeaders = requestHeaders; @@ -47,6 +56,7 @@ public HttpServerAttributesExtractorBuilder setCapturedReques * * @param responseHeaders A list of HTTP header names. */ + @CanIgnoreReturnValue public HttpServerAttributesExtractorBuilder setCapturedResponseHeaders( List responseHeaders) { this.capturedResponseHeaders = responseHeaders; @@ -59,6 +69,6 @@ public HttpServerAttributesExtractorBuilder setCapturedRespon */ public HttpServerAttributesExtractor build() { return new HttpServerAttributesExtractor<>( - getter, capturedRequestHeaders, capturedResponseHeaders); + httpAttributesGetter, netAttributesGetter, capturedRequestHeaders, capturedResponseHeaders); } } diff --git a/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/http/HttpServerAttributesGetter.java b/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/http/HttpServerAttributesGetter.java index e36795a388ce..b2a94929927b 100644 --- a/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/http/HttpServerAttributesGetter.java +++ b/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/http/HttpServerAttributesGetter.java @@ -35,7 +35,13 @@ public interface HttpServerAttributesGetter * The primary server name of the matched virtual host. This should be obtained via configuration, * not from the Host header. If no such configuration can be obtained, this method should return * {@code null}. + * + * @deprecated This method is deprecated and will be removed in the next release. */ @Nullable - String serverName(REQUEST request); + @Deprecated + default String serverName(REQUEST request) { + throw new UnsupportedOperationException( + "This method is deprecated and will be removed in the next release"); + } } diff --git a/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/http/HttpServerMetrics.java b/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/http/HttpServerMetrics.java index 2c624f9253aa..b27026c5d4a8 100644 --- a/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/http/HttpServerMetrics.java +++ b/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/http/HttpServerMetrics.java @@ -6,20 +6,24 @@ package io.opentelemetry.instrumentation.api.instrumenter.http; import static io.opentelemetry.instrumentation.api.instrumenter.http.TemporaryMetricsView.applyActiveRequestsView; -import static io.opentelemetry.instrumentation.api.instrumenter.http.TemporaryMetricsView.applyServerDurationView; +import static io.opentelemetry.instrumentation.api.instrumenter.http.TemporaryMetricsView.applyServerDurationAndSizeView; import static java.util.logging.Level.FINE; import com.google.auto.value.AutoValue; +import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.metrics.DoubleHistogram; +import io.opentelemetry.api.metrics.LongHistogram; import io.opentelemetry.api.metrics.LongUpDownCounter; import io.opentelemetry.api.metrics.Meter; import io.opentelemetry.context.Context; import io.opentelemetry.context.ContextKey; import io.opentelemetry.instrumentation.api.instrumenter.OperationListener; import io.opentelemetry.instrumentation.api.instrumenter.OperationMetrics; +import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; import java.util.concurrent.TimeUnit; import java.util.logging.Logger; +import javax.annotation.Nullable; /** * {@link OperationListener} which keeps track of T getAttribute(AttributeKey key, Attributes... attributesList) { + for (Attributes attributes : attributesList) { + T value = attributes.get(key); + if (value != null) { + return value; + } + } + return null; } @AutoValue diff --git a/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/http/HttpSpanStatusExtractor.java b/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/http/HttpSpanStatusExtractor.java index 373efa10fe07..1ef9c60598ff 100644 --- a/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/http/HttpSpanStatusExtractor.java +++ b/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/http/HttpSpanStatusExtractor.java @@ -54,9 +54,10 @@ public void extract( SpanStatusBuilder spanStatusBuilder, REQUEST request, @Nullable RESPONSE response, - Throwable error) { + @Nullable Throwable error) { + if (response != null) { - Integer statusCode = getter.statusCode(request, response); + Integer statusCode = getter.statusCode(request, response, error); if (statusCode != null) { StatusCode statusCodeObj = statusConverter.statusFromHttpStatus(statusCode); if (statusCodeObj == StatusCode.ERROR) { diff --git a/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/http/SemanticAttributes.java b/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/http/SemanticAttributes.java new file mode 100644 index 000000000000..d9e14f072812 --- /dev/null +++ b/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/http/SemanticAttributes.java @@ -0,0 +1,21 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.api.instrumenter.http; + +import static io.opentelemetry.api.common.AttributeKey.stringKey; + +import io.opentelemetry.api.common.AttributeKey; + +public final class SemanticAttributes { + + private SemanticAttributes() {} + + public static final AttributeKey HTTP_REQUEST_BODY = stringKey("http.request.body"); + public static final AttributeKey HTTP_REQUEST_HEADERS = stringKey("http.request.headers"); + public static final AttributeKey HTTP_RESPONSE_BODY = stringKey("http.response.body"); + public static final AttributeKey HTTP_RESPONSE_HEADERS = + stringKey("http.response.headers"); +} diff --git a/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/http/TemporaryMetricsView.java b/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/http/TemporaryMetricsView.java index f6432320f5f4..6b3e88928ca0 100644 --- a/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/http/TemporaryMetricsView.java +++ b/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/http/TemporaryMetricsView.java @@ -40,6 +40,7 @@ private static Set buildDurationClientView() { Set view = new HashSet<>(durationAlwaysInclude); view.add(SemanticAttributes.NET_PEER_NAME); view.add(SemanticAttributes.NET_PEER_PORT); + view.add(AttributeKey.stringKey("net.peer.sock.addr")); return view; } @@ -51,7 +52,8 @@ private static Set buildDurationServerView() { // - we prefer http.route (which is scrubbed) over http.target (which is not scrubbed). Set view = new HashSet<>(durationAlwaysInclude); view.add(SemanticAttributes.HTTP_SCHEME); - view.add(SemanticAttributes.HTTP_HOST); + view.add(SemanticAttributes.NET_HOST_NAME); + view.add(SemanticAttributes.NET_HOST_PORT); view.add(SemanticAttributes.HTTP_ROUTE); return view; } @@ -61,21 +63,23 @@ private static Set buildActiveRequestsView() { // https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/semantic_conventions/http-metrics.md#attributes Set view = new HashSet<>(); view.add(SemanticAttributes.HTTP_METHOD); - view.add(SemanticAttributes.HTTP_HOST); view.add(SemanticAttributes.HTTP_SCHEME); view.add(SemanticAttributes.HTTP_FLAVOR); - view.add(SemanticAttributes.HTTP_SERVER_NAME); + view.add(SemanticAttributes.NET_HOST_NAME); + // TODO: net host port? return view; } - static Attributes applyClientDurationView(Attributes startAttributes, Attributes endAttributes) { + static Attributes applyClientDurationAndSizeView( + Attributes startAttributes, Attributes endAttributes) { AttributesBuilder filtered = Attributes.builder(); applyView(filtered, startAttributes, durationClientView); applyView(filtered, endAttributes, durationClientView); return filtered.build(); } - static Attributes applyServerDurationView(Attributes startAttributes, Attributes endAttributes) { + static Attributes applyServerDurationAndSizeView( + Attributes startAttributes, Attributes endAttributes) { AttributesBuilder filtered = Attributes.builder(); applyView(filtered, startAttributes, durationServerView); applyView(filtered, endAttributes, durationServerView); diff --git a/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/messaging/CapturedMessageHeadersUtil.java b/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/messaging/CapturedMessageHeadersUtil.java new file mode 100644 index 000000000000..e1ec7ae780eb --- /dev/null +++ b/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/messaging/CapturedMessageHeadersUtil.java @@ -0,0 +1,38 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.api.instrumenter.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); + } + + private CapturedMessageHeadersUtil() {} +} diff --git a/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/messaging/MessagingAttributesExtractor.java b/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/messaging/MessagingAttributesExtractor.java index 2694f9ea2980..81edc180a841 100644 --- a/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/messaging/MessagingAttributesExtractor.java +++ b/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/messaging/MessagingAttributesExtractor.java @@ -5,16 +5,20 @@ package io.opentelemetry.instrumentation.api.instrumenter.messaging; +import static io.opentelemetry.instrumentation.api.instrumenter.messaging.CapturedMessageHeadersUtil.attributeKey; +import static io.opentelemetry.instrumentation.api.instrumenter.messaging.CapturedMessageHeadersUtil.lowercase; import static io.opentelemetry.instrumentation.api.instrumenter.messaging.MessageOperation.PROCESS; import static io.opentelemetry.instrumentation.api.instrumenter.messaging.MessageOperation.RECEIVE; import static io.opentelemetry.instrumentation.api.internal.AttributesExtractorUtil.internalSet; +import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.api.common.AttributesBuilder; import io.opentelemetry.context.Context; import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor; import io.opentelemetry.instrumentation.api.internal.SpanKey; import io.opentelemetry.instrumentation.api.internal.SpanKeyProvider; import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; +import java.util.List; import javax.annotation.Nullable; /** @@ -29,22 +33,40 @@ public final class MessagingAttributesExtractor implements AttributesExtractor, SpanKeyProvider { static final String TEMP_DESTINATION_NAME = "(temporary)"; + static final long HS_MAX_PAYLOAD_SIZE = 65536; + + public static final AttributeKey MESSAGING_PAYLOAD = + AttributeKey.stringKey("messaging.payload"); /** - * Creates the messaging attributes extractor for the given {@link MessageOperation operation}. + * Creates the messaging attributes extractor for the given {@link MessageOperation operation} + * with default configuration. */ public static MessagingAttributesExtractor create( MessagingAttributesGetter getter, MessageOperation operation) { - return new MessagingAttributesExtractor<>(getter, operation); + return builder(getter, operation).build(); + } + + /** + * Returns a new {@link MessagingAttributesExtractorBuilder} for the given {@link MessageOperation + * operation} that can be used to configure the messaging attributes extractor. + */ + public static MessagingAttributesExtractorBuilder builder( + MessagingAttributesGetter getter, MessageOperation operation) { + return new MessagingAttributesExtractorBuilder<>(getter, operation); } private final MessagingAttributesGetter getter; private final MessageOperation operation; + private final List capturedHeaders; - private MessagingAttributesExtractor( - MessagingAttributesGetter getter, MessageOperation operation) { + MessagingAttributesExtractor( + MessagingAttributesGetter getter, + MessageOperation operation, + List capturedHeaders) { this.getter = getter; this.operation = operation; + this.capturedHeaders = lowercase(capturedHeaders); } @SuppressWarnings("deprecation") // operationName @@ -78,6 +100,11 @@ public void onStart(AttributesBuilder attributes, Context parentContext, REQUEST if (operation == RECEIVE || operation == PROCESS) { internalSet(attributes, SemanticAttributes.MESSAGING_OPERATION, operation.operationName()); } + + String messagePayload = getter.messagePayload(request); + if (messagePayload != null && messagePayload.length() <= HS_MAX_PAYLOAD_SIZE) { + internalSet(attributes, MESSAGING_PAYLOAD, messagePayload); + } } @Override @@ -89,6 +116,13 @@ public void onEnd( @Nullable Throwable error) { internalSet( attributes, SemanticAttributes.MESSAGING_MESSAGE_ID, getter.messageId(request, response)); + + for (String name : capturedHeaders) { + List values = getter.header(request, name); + if (!values.isEmpty()) { + internalSet(attributes, attributeKey(name), values); + } + } } /** diff --git a/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/messaging/MessagingAttributesExtractorBuilder.java b/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/messaging/MessagingAttributesExtractorBuilder.java new file mode 100644 index 000000000000..009ea5a0f26f --- /dev/null +++ b/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/messaging/MessagingAttributesExtractorBuilder.java @@ -0,0 +1,49 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.api.instrumenter.messaging; + +import static java.util.Collections.emptyList; + +import com.google.errorprone.annotations.CanIgnoreReturnValue; +import java.util.List; + +/** A builder of {@link MessagingAttributesExtractor}. */ +public final class MessagingAttributesExtractorBuilder { + + final MessagingAttributesGetter getter; + final MessageOperation operation; + List capturedHeaders = emptyList(); + + MessagingAttributesExtractorBuilder( + MessagingAttributesGetter getter, MessageOperation operation) { + this.getter = getter; + this.operation = operation; + } + + /** + * 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. + * + * @param capturedHeaders A list of messaging header names. + */ + @CanIgnoreReturnValue + public MessagingAttributesExtractorBuilder setCapturedHeaders( + List capturedHeaders) { + this.capturedHeaders = capturedHeaders; + return this; + } + + /** + * Returns a new {@link MessagingAttributesExtractor} with the settings of this {@link + * MessagingAttributesExtractorBuilder}. + */ + public MessagingAttributesExtractor build() { + return new MessagingAttributesExtractor<>(getter, operation, capturedHeaders); + } +} diff --git a/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/messaging/MessagingAttributesGetter.java b/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/messaging/MessagingAttributesGetter.java index 1646ac50f61b..18b4293bf56a 100644 --- a/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/messaging/MessagingAttributesGetter.java +++ b/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/messaging/MessagingAttributesGetter.java @@ -5,6 +5,8 @@ package io.opentelemetry.instrumentation.api.instrumenter.messaging; +import java.util.Collections; +import java.util.List; import javax.annotation.Nullable; /** @@ -47,4 +49,20 @@ public interface MessagingAttributesGetter { @Nullable String messageId(REQUEST request, @Nullable RESPONSE response); + + /** + * Extracts all values of header named {@code name} from the request, or an empty list if there + * were none. + * + *

Implementations of this method must not return a null value; an empty list should be + * returned instead. + */ + default List header(REQUEST request, String name) { + return Collections.emptyList(); + } + + @Nullable + default String messagePayload(REQUEST request) { + return null; + } } diff --git a/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/net/InetSocketAddressNetClientAttributesGetter.java b/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/net/InetSocketAddressNetClientAttributesGetter.java index 019df5343b14..3f212247e521 100644 --- a/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/net/InetSocketAddressNetClientAttributesGetter.java +++ b/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/net/InetSocketAddressNetClientAttributesGetter.java @@ -5,6 +5,7 @@ package io.opentelemetry.instrumentation.api.instrumenter.net; +import java.net.Inet6Address; import java.net.InetAddress; import java.net.InetSocketAddress; import javax.annotation.Nullable; @@ -20,39 +21,54 @@ public abstract class InetSocketAddressNetClientAttributesGetter { @Nullable - public abstract InetSocketAddress getAddress(REQUEST request, @Nullable RESPONSE response); + protected abstract InetSocketAddress getPeerSocketAddress( + REQUEST request, @Nullable RESPONSE response); - @Override @Nullable - public final String peerName(REQUEST request, @Nullable RESPONSE response) { - InetSocketAddress address = getAddress(request, response); + @Override + public String sockFamily(REQUEST request, @Nullable RESPONSE response) { + InetSocketAddress address = getPeerSocketAddress(request, response); if (address == null) { return null; } - return address.getHostString(); + InetAddress remoteAddress = address.getAddress(); + if (remoteAddress instanceof Inet6Address) { + return "inet6"; + } + return null; } @Override @Nullable - public final Integer peerPort(REQUEST request, @Nullable RESPONSE response) { - InetSocketAddress address = getAddress(request, response); + public final String sockPeerAddr(REQUEST request, @Nullable RESPONSE response) { + InetSocketAddress address = getPeerSocketAddress(request, response); if (address == null) { return null; } - return address.getPort(); + InetAddress remoteAddress = address.getAddress(); + if (remoteAddress != null) { + return remoteAddress.getHostAddress(); + } + return null; } @Override @Nullable - public final String peerIp(REQUEST request, @Nullable RESPONSE response) { - InetSocketAddress address = getAddress(request, response); + public String sockPeerName(REQUEST request, @Nullable RESPONSE response) { + InetSocketAddress address = getPeerSocketAddress(request, response); if (address == null) { return null; } - InetAddress remoteAddress = address.getAddress(); - if (remoteAddress != null) { - return remoteAddress.getHostAddress(); + return address.getHostString(); + } + + @Nullable + @Override + public Integer sockPeerPort(REQUEST request, @Nullable RESPONSE response) { + InetSocketAddress address = getPeerSocketAddress(request, response); + if (address == null) { + return null; } - return null; + return address.getPort(); } } diff --git a/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/net/InetSocketAddressNetServerAttributesGetter.java b/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/net/InetSocketAddressNetServerAttributesGetter.java index eb0d22a4b19b..fb2a5e4fa190 100644 --- a/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/net/InetSocketAddressNetServerAttributesGetter.java +++ b/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/net/InetSocketAddressNetServerAttributesGetter.java @@ -5,6 +5,7 @@ package io.opentelemetry.instrumentation.api.instrumenter.net; +import java.net.Inet6Address; import java.net.InetAddress; import java.net.InetSocketAddress; import javax.annotation.Nullable; @@ -20,22 +21,55 @@ public abstract class InetSocketAddressNetServerAttributesGetter implements NetServerAttributesGetter { @Nullable - public abstract InetSocketAddress getAddress(REQUEST request); + protected abstract InetSocketAddress getPeerSocketAddress(REQUEST request); + + // optional + @Nullable + protected abstract InetSocketAddress getHostSocketAddress(REQUEST request); - @Override @Nullable - public final Integer peerPort(REQUEST request) { - InetSocketAddress address = getAddress(request); + @Override + public String sockFamily(REQUEST request) { + InetSocketAddress address = getPeerSocketAddress(request); + if (address == null) { + address = getHostSocketAddress(request); + } if (address == null) { return null; } - return address.getPort(); + InetAddress inetAddress = address.getAddress(); + if (inetAddress instanceof Inet6Address) { + return "inet6"; + } + return null; + } + + @Override + @Nullable + public final String sockPeerAddr(REQUEST request) { + return getAddress(getPeerSocketAddress(request)); } @Override @Nullable - public final String peerIp(REQUEST request) { - InetSocketAddress address = getAddress(request); + public final Integer sockPeerPort(REQUEST request) { + return getPort(getPeerSocketAddress(request)); + } + + @Nullable + @Override + public String sockHostAddr(REQUEST request) { + return getAddress(getHostSocketAddress(request)); + } + + @Nullable + @Override + public Integer sockHostPort(REQUEST request) { + return getPort(getHostSocketAddress(request)); + } + + @Nullable + private static String getAddress(InetSocketAddress address) { if (address == null) { return null; } @@ -45,4 +79,12 @@ public final String peerIp(REQUEST request) { } return null; } + + @Nullable + private static Integer getPort(InetSocketAddress address) { + if (address == null) { + return null; + } + return address.getPort(); + } } diff --git a/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/net/NetClientAttributesExtractor.java b/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/net/NetClientAttributesExtractor.java index d28c7d436fba..aecb892553e7 100644 --- a/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/net/NetClientAttributesExtractor.java +++ b/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/net/NetClientAttributesExtractor.java @@ -37,7 +37,16 @@ private NetClientAttributesExtractor(NetClientAttributesGetter 0) { + internalSet(attributes, SemanticAttributes.NET_PEER_PORT, (long) peerPort); + } + } + } @Override public void onEnd( @@ -49,17 +58,27 @@ public void onEnd( internalSet(attributes, SemanticAttributes.NET_TRANSPORT, getter.transport(request, response)); - String peerIp = getter.peerIp(request, response); - String peerName = getter.peerName(request, response); + String peerName = getter.peerName(request); + Integer peerPort = getter.peerPort(request); - if (peerName != null && !peerName.equals(peerIp)) { - internalSet(attributes, SemanticAttributes.NET_PEER_NAME, peerName); - } - internalSet(attributes, SemanticAttributes.NET_PEER_IP, peerIp); + String sockPeerAddr = getter.sockPeerAddr(request, response); + if (sockPeerAddr != null && !sockPeerAddr.equals(peerName)) { + internalSet(attributes, SemanticAttributes.NET_SOCK_PEER_ADDR, sockPeerAddr); + + Integer sockPeerPort = getter.sockPeerPort(request, response); + if (sockPeerPort != null && sockPeerPort > 0 && !sockPeerPort.equals(peerPort)) { + internalSet(attributes, SemanticAttributes.NET_SOCK_PEER_PORT, (long) sockPeerPort); + } + + String sockFamily = getter.sockFamily(request, response); + if (sockFamily != null && !SemanticAttributes.NetSockFamilyValues.INET.equals(sockFamily)) { + internalSet(attributes, SemanticAttributes.NET_SOCK_FAMILY, sockFamily); + } - Integer peerPort = getter.peerPort(request, response); - if (peerPort != null && peerPort > 0) { - internalSet(attributes, SemanticAttributes.NET_PEER_PORT, (long) peerPort); + String sockPeerName = getter.sockPeerName(request, response); + if (sockPeerName != null && !sockPeerName.equals(peerName)) { + internalSet(attributes, SemanticAttributes.NET_SOCK_PEER_NAME, sockPeerName); + } } } } diff --git a/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/net/NetClientAttributesGetter.java b/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/net/NetClientAttributesGetter.java index 18d05950c165..873d75669562 100644 --- a/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/net/NetClientAttributesGetter.java +++ b/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/net/NetClientAttributesGetter.java @@ -20,12 +20,57 @@ public interface NetClientAttributesGetter { @Nullable String transport(REQUEST request, @Nullable RESPONSE response); + /** + * Logical remote hostname. + * + * @deprecated This method is deprecated and will be removed in the next release. + */ + @Deprecated @Nullable - String peerName(REQUEST request, @Nullable RESPONSE response); + default String peerName(REQUEST request, @Nullable RESPONSE response) { + throw new UnsupportedOperationException( + "This method is deprecated and will be removed in the next release"); + } @Nullable - Integer peerPort(REQUEST request, @Nullable RESPONSE response); + default String peerName(REQUEST request) { + return peerName(request, null); + } + /** + * Logical remote port number. + * + * @deprecated This method is deprecated and will be removed in the next release. + */ + @Deprecated @Nullable - String peerIp(REQUEST request, @Nullable RESPONSE response); + default Integer peerPort(REQUEST request, @Nullable RESPONSE response) { + throw new UnsupportedOperationException( + "This method is deprecated and will be removed in the next release"); + } + + @Nullable + default Integer peerPort(REQUEST request) { + return peerPort(request, null); + } + + @Nullable + default String sockFamily(REQUEST request, @Nullable RESPONSE response) { + return null; + } + + @Nullable + default String sockPeerAddr(REQUEST request, @Nullable RESPONSE response) { + return null; + } + + @Nullable + default String sockPeerName(REQUEST request, @Nullable RESPONSE response) { + return null; + } + + @Nullable + default Integer sockPeerPort(REQUEST request, @Nullable RESPONSE response) { + return null; + } } diff --git a/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/net/NetServerAttributesExtractor.java b/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/net/NetServerAttributesExtractor.java index 9980a1e22511..0a95d6dbdbd3 100644 --- a/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/net/NetServerAttributesExtractor.java +++ b/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/net/NetServerAttributesExtractor.java @@ -5,12 +5,10 @@ package io.opentelemetry.instrumentation.api.instrumenter.net; -import static io.opentelemetry.instrumentation.api.internal.AttributesExtractorUtil.internalSet; - import io.opentelemetry.api.common.AttributesBuilder; import io.opentelemetry.context.Context; import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor; -import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; +import io.opentelemetry.instrumentation.api.instrumenter.net.internal.InternalNetServerAttributesExtractor; import javax.annotation.Nullable; /** @@ -35,16 +33,7 @@ private NetServerAttributesExtractor(NetServerAttributesGetter getter) @Override public void onStart(AttributesBuilder attributes, Context parentContext, REQUEST request) { - internalSet(attributes, SemanticAttributes.NET_TRANSPORT, getter.transport(request)); - - String peerIp = getter.peerIp(request); - - internalSet(attributes, SemanticAttributes.NET_PEER_IP, peerIp); - - Integer peerPort = getter.peerPort(request); - if (peerPort != null && peerPort > 0) { - internalSet(attributes, SemanticAttributes.NET_PEER_PORT, (long) peerPort); - } + InternalNetServerAttributesExtractor.onStart(getter, attributes, request, null); } @Override diff --git a/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/net/NetServerAttributesGetter.java b/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/net/NetServerAttributesGetter.java index 31d1edea002b..72bd87e6b1b8 100644 --- a/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/net/NetServerAttributesGetter.java +++ b/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/net/NetServerAttributesGetter.java @@ -8,8 +8,8 @@ import javax.annotation.Nullable; /** - * An interface for getting server-based network attributes. It adapts a vendor-specific request - * type into the 3 common attributes (transport, peerPort, peerIp). + * An interface for getting server-based network attributes. It adapts an instrumentation-specific + * request type into the 3 common attributes (transport, sockPeerPort, sockPeerAddr). * *

Instrumentation authors will create implementations of this interface for their specific * server library/framework. It will be used by the {@link NetServerAttributesExtractor} to obtain @@ -21,8 +21,33 @@ public interface NetServerAttributesGetter { String transport(REQUEST request); @Nullable - Integer peerPort(REQUEST request); + String hostName(REQUEST request); @Nullable - String peerIp(REQUEST request); + Integer hostPort(REQUEST request); + + @Nullable + default String sockFamily(REQUEST request) { + return null; + } + + @Nullable + default String sockPeerAddr(REQUEST request) { + return null; + } + + @Nullable + default Integer sockPeerPort(REQUEST request) { + return null; + } + + @Nullable + default String sockHostAddr(REQUEST request) { + return null; + } + + @Nullable + default Integer sockHostPort(REQUEST request) { + return null; + } } diff --git a/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/PeerServiceAttributesExtractor.java b/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/net/PeerServiceAttributesExtractor.java similarity index 68% rename from instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/PeerServiceAttributesExtractor.java rename to instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/net/PeerServiceAttributesExtractor.java index 9b4e06b0dfb1..8b7cd2d7fc2c 100644 --- a/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/PeerServiceAttributesExtractor.java +++ b/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/net/PeerServiceAttributesExtractor.java @@ -3,14 +3,11 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.instrumentation.api.instrumenter; - -import static java.util.Collections.emptyMap; +package io.opentelemetry.instrumentation.api.instrumenter.net; import io.opentelemetry.api.common.AttributesBuilder; import io.opentelemetry.context.Context; -import io.opentelemetry.instrumentation.api.config.Config; -import io.opentelemetry.instrumentation.api.instrumenter.net.NetClientAttributesGetter; +import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor; import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; import java.util.Map; import javax.annotation.Nullable; @@ -19,25 +16,19 @@ * Extractor of the {@code peer.service} span attribute, described in the * specification. - * - *

Peer service name mappings can be configured using the {@code - * otel.instrumentation.common.peer-service-mapping} configuration property. The format used is a - * comma-separated list of {@code host=name} pairs. */ public final class PeerServiceAttributesExtractor implements AttributesExtractor { - private static final Map JAVAAGENT_PEER_SERVICE_MAPPING = - Config.get().getMap("otel.instrumentation.common.peer-service-mapping", emptyMap()); - private final Map peerServiceMapping; private final NetClientAttributesGetter attributesGetter; + private final Map peerServiceMapping; // visible for tests PeerServiceAttributesExtractor( - Map peerServiceMapping, - NetClientAttributesGetter attributesGetter) { - this.peerServiceMapping = peerServiceMapping; + NetClientAttributesGetter attributesGetter, + Map peerServiceMapping) { this.attributesGetter = attributesGetter; + this.peerServiceMapping = peerServiceMapping; } /** @@ -45,8 +36,9 @@ public final class PeerServiceAttributesExtractor * netAttributesExtractor} instance to determine the value of the {@code peer.service} attribute. */ public static PeerServiceAttributesExtractor create( - NetClientAttributesGetter attributesGetter) { - return new PeerServiceAttributesExtractor<>(JAVAAGENT_PEER_SERVICE_MAPPING, attributesGetter); + NetClientAttributesGetter attributesGetter, + Map peerServiceMapping) { + return new PeerServiceAttributesExtractor<>(attributesGetter, peerServiceMapping); } @Override @@ -65,12 +57,8 @@ public void onEnd( return; } - String peerName = attributesGetter.peerName(request, response); + String peerName = attributesGetter.peerName(request); String peerService = mapToPeerService(peerName); - if (peerService == null) { - String peerIp = attributesGetter.peerIp(request, response); - peerService = mapToPeerService(peerIp); - } if (peerService != null) { attributes.put(SemanticAttributes.PEER_SERVICE, peerService); } diff --git a/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/net/internal/InternalNetServerAttributesExtractor.java b/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/net/internal/InternalNetServerAttributesExtractor.java new file mode 100644 index 000000000000..5e1c22f9b738 --- /dev/null +++ b/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/net/internal/InternalNetServerAttributesExtractor.java @@ -0,0 +1,95 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.api.instrumenter.net.internal; + +import static io.opentelemetry.instrumentation.api.internal.AttributesExtractorUtil.internalSet; +import static java.util.logging.Level.FINE; + +import io.opentelemetry.api.common.AttributesBuilder; +import io.opentelemetry.instrumentation.api.instrumenter.net.NetServerAttributesGetter; +import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; +import java.util.logging.Logger; +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 InternalNetServerAttributesExtractor { + + private static final Logger logger = + Logger.getLogger(InternalNetServerAttributesExtractor.class.getName()); + + public static void onStart( + NetServerAttributesGetter getter, + AttributesBuilder attributes, + REQUEST request, + @Nullable String hostHeader) { + internalSet(attributes, SemanticAttributes.NET_TRANSPORT, getter.transport(request)); + + boolean setSockFamily = false; + + String sockPeerAddr = getter.sockPeerAddr(request); + if (sockPeerAddr != null) { + setSockFamily = true; + + internalSet(attributes, SemanticAttributes.NET_SOCK_PEER_ADDR, sockPeerAddr); + + Integer sockPeerPort = getter.sockPeerPort(request); + if (sockPeerPort != null && sockPeerPort > 0) { + internalSet(attributes, SemanticAttributes.NET_SOCK_PEER_PORT, (long) sockPeerPort); + } + } + + String hostName = getter.hostName(request); + Integer hostPort = getter.hostPort(request); + + int hostHeaderSeparator = -1; + if (hostHeader != null) { + hostHeaderSeparator = hostHeader.indexOf(':'); + } + if (hostName == null && hostHeader != null) { + hostName = + hostHeaderSeparator == -1 ? hostHeader : hostHeader.substring(0, hostHeaderSeparator); + } + if (hostPort == null && hostHeader != null && hostHeaderSeparator != -1) { + try { + hostPort = Integer.parseInt(hostHeader.substring(hostHeaderSeparator + 1)); + } catch (NumberFormatException e) { + logger.log(FINE, e.getMessage(), e); + } + } + + if (hostName != null) { + internalSet(attributes, SemanticAttributes.NET_HOST_NAME, hostName); + + if (hostPort != null && hostPort > 0) { + internalSet(attributes, SemanticAttributes.NET_HOST_PORT, (long) hostPort); + } + } + + String sockHostAddr = getter.sockHostAddr(request); + if (sockHostAddr != null && !sockHostAddr.equals(hostName)) { + setSockFamily = true; + + internalSet(attributes, SemanticAttributes.NET_SOCK_HOST_ADDR, sockHostAddr); + + Integer sockHostPort = getter.sockHostPort(request); + if (sockHostPort != null && sockHostPort > 0 && !sockHostPort.equals(hostPort)) { + internalSet(attributes, SemanticAttributes.NET_SOCK_HOST_PORT, (long) sockHostPort); + } + } + + if (setSockFamily) { + String sockFamily = getter.sockFamily(request); + if (sockFamily != null && !SemanticAttributes.NetSockFamilyValues.INET.equals(sockFamily)) { + internalSet(attributes, SemanticAttributes.NET_SOCK_FAMILY, sockFamily); + } + } + } + + private InternalNetServerAttributesExtractor() {} +} diff --git a/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/rpc/MetricsView.java b/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/rpc/MetricsView.java index dbf5bd7b5aed..16d427c536b5 100644 --- a/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/rpc/MetricsView.java +++ b/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/rpc/MetricsView.java @@ -20,7 +20,6 @@ final class MetricsView { private static final Set alwaysInclude = buildAlwaysInclude(); private static final Set clientView = buildClientView(); - private static final Set clientFallbackView = buildClientFallbackView(); private static final Set serverView = buildServerView(); private static final Set serverFallbackView = buildServerFallbackView(); @@ -31,6 +30,7 @@ private static Set buildAlwaysInclude() { view.add(SemanticAttributes.RPC_SYSTEM); view.add(SemanticAttributes.RPC_SERVICE); view.add(SemanticAttributes.RPC_METHOD); + view.add(SemanticAttributes.RPC_GRPC_STATUS_CODE); return view; } @@ -44,16 +44,6 @@ private static Set buildClientView() { return view; } - private static Set buildClientFallbackView() { - // the list of rpc client metrics attributes is from - // https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/semantic_conventions/rpc.md#attributes - Set view = new HashSet<>(alwaysInclude); - view.add(SemanticAttributes.NET_PEER_IP); - view.add(SemanticAttributes.NET_PEER_PORT); - view.add(SemanticAttributes.NET_TRANSPORT); - return view; - } - private static Set buildServerView() { // the list of rpc server metrics attributes is from // https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/semantic_conventions/rpc.md#attributes @@ -67,7 +57,7 @@ private static Set buildServerFallbackView() { // the list of rpc server metrics attributes is from // https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/semantic_conventions/rpc.md#attributes Set view = new HashSet<>(alwaysInclude); - view.add(SemanticAttributes.NET_HOST_IP); + view.add(SemanticAttributes.NET_SOCK_HOST_ADDR); view.add(SemanticAttributes.NET_TRANSPORT); return view; } @@ -78,11 +68,7 @@ private static boolean containsAttribute( } static Attributes applyClientView(Attributes startAttributes, Attributes endAttributes) { - Set fullSet = clientView; - if (!containsAttribute(SemanticAttributes.NET_PEER_NAME, startAttributes, endAttributes)) { - fullSet = clientFallbackView; - } - return applyView(fullSet, startAttributes, endAttributes); + return applyView(clientView, startAttributes, endAttributes); } static Attributes applyServerView(Attributes startAttributes, Attributes endAttributes) { @@ -112,4 +98,6 @@ private static void applyView( } }); } + + private MetricsView() {} } diff --git a/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/util/ClassAndMethod.java b/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/util/ClassAndMethod.java similarity index 90% rename from instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/util/ClassAndMethod.java rename to instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/util/ClassAndMethod.java index f2eb170b2c0b..ac9e55098417 100644 --- a/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/util/ClassAndMethod.java +++ b/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/util/ClassAndMethod.java @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.instrumentation.api.util; +package io.opentelemetry.instrumentation.api.instrumenter.util; import com.google.auto.value.AutoValue; import io.opentelemetry.instrumentation.api.instrumenter.code.CodeAttributesGetter; diff --git a/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/util/ClassAndMethodAttributesGetter.java b/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/util/ClassAndMethodAttributesGetter.java similarity index 89% rename from instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/util/ClassAndMethodAttributesGetter.java rename to instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/util/ClassAndMethodAttributesGetter.java index c63f53a18ee9..2166eaa50850 100644 --- a/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/util/ClassAndMethodAttributesGetter.java +++ b/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/util/ClassAndMethodAttributesGetter.java @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.instrumentation.api.util; +package io.opentelemetry.instrumentation.api.instrumenter.util; import io.opentelemetry.instrumentation.api.instrumenter.code.CodeAttributesGetter; import javax.annotation.Nullable; diff --git a/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/util/SpanNames.java b/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/util/SpanNames.java similarity index 67% rename from instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/util/SpanNames.java rename to instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/util/SpanNames.java index 8e1cc31e6b97..87e0ca076e2c 100644 --- a/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/util/SpanNames.java +++ b/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/util/SpanNames.java @@ -3,13 +3,13 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.instrumentation.api.util; +package io.opentelemetry.instrumentation.api.instrumenter.util; +import io.opentelemetry.instrumentation.api.internal.ClassNames; import io.opentelemetry.instrumentation.api.internal.cache.Cache; import java.lang.reflect.Method; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; -import javax.annotation.Nullable; public final class SpanNames { @@ -23,22 +23,6 @@ public static String fromMethod(Method method) { return fromMethod(method.getDeclaringClass(), method.getName()); } - /** - * 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, @Nullable Method method) { - return fromMethod(clazz, method == null ? "" : method.getName()); - } - - /** - * 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(ClassAndMethod classAndMethod) { - return fromMethod(classAndMethod.declaringClass(), classAndMethod.methodName()); - } - /** * This method is used to generate a span name based on a method. Anonymous classes are named * based on their parent. diff --git a/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/log/LoggingContextConstants.java b/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/log/LoggingContextConstants.java index c7d2815b4f62..562b9cedcf9d 100644 --- a/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/log/LoggingContextConstants.java +++ b/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/log/LoggingContextConstants.java @@ -30,6 +30,8 @@ public final class LoggingContextConstants { * @see SpanContext#getTraceFlags() */ public static final String TRACE_FLAGS = "trace_flags"; + /** Helios Key under which the current logger name will be injected into the span attributes. */ + public static final String HELIOS_INSTRUMENTED_INDICATION = "heliosLogInstrumented"; private LoggingContextConstants() {} } diff --git a/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/metrics/db/DbConnectionPoolMetrics.java b/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/metrics/db/DbConnectionPoolMetrics.java index 76b07961e1d6..e398250e6e1b 100644 --- a/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/metrics/db/DbConnectionPoolMetrics.java +++ b/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/metrics/db/DbConnectionPoolMetrics.java @@ -58,7 +58,7 @@ public static DbConnectionPoolMetrics create( public ObservableLongMeasurement connections() { return meter .upDownCounterBuilder("db.client.connections.usage") - .setUnit("connections") + .setUnit("{connections}") .setDescription( "The number of connections that are currently in state described by the state attribute.") .buildObserver(); @@ -67,7 +67,7 @@ public ObservableLongMeasurement connections() { public ObservableLongMeasurement minIdleConnections() { return meter .upDownCounterBuilder("db.client.connections.idle.min") - .setUnit("connections") + .setUnit("{connections}") .setDescription("The minimum number of idle open connections allowed.") .buildObserver(); } @@ -75,7 +75,7 @@ public ObservableLongMeasurement minIdleConnections() { public ObservableLongMeasurement maxIdleConnections() { return meter .upDownCounterBuilder("db.client.connections.idle.max") - .setUnit("connections") + .setUnit("{connections}") .setDescription("The maximum number of idle open connections allowed.") .buildObserver(); } @@ -83,7 +83,7 @@ public ObservableLongMeasurement maxIdleConnections() { public ObservableLongMeasurement maxConnections() { return meter .upDownCounterBuilder("db.client.connections.max") - .setUnit("connections") + .setUnit("{connections}") .setDescription("The maximum number of open connections allowed.") .buildObserver(); } @@ -91,7 +91,7 @@ public ObservableLongMeasurement maxConnections() { public ObservableLongMeasurement pendingRequestsForConnection() { return meter .upDownCounterBuilder("db.client.connections.pending_requests") - .setUnit("requests") + .setUnit("{requests}") .setDescription( "The number of pending requests for an open connection, cumulative for the entire pool.") .buildObserver(); diff --git a/instrumentation-api-semconv/src/test/groovy/io/opentelemetry/instrumentation/api/db/RedisCommandSanitizerTest.groovy b/instrumentation-api-semconv/src/test/groovy/io/opentelemetry/instrumentation/api/db/RedisCommandSanitizerTest.groovy index 9f110e1a0d1c..3a66f3d65a4e 100644 --- a/instrumentation-api-semconv/src/test/groovy/io/opentelemetry/instrumentation/api/db/RedisCommandSanitizerTest.groovy +++ b/instrumentation-api-semconv/src/test/groovy/io/opentelemetry/instrumentation/api/db/RedisCommandSanitizerTest.groovy @@ -12,7 +12,7 @@ class RedisCommandSanitizerTest extends Specification { @Unroll def "should sanitize #expected"() { when: - def sanitized = RedisCommandSanitizer.sanitize(command, args) + def sanitized = RedisCommandSanitizer.create(true).sanitize(command, args) then: sanitized == expected @@ -90,7 +90,7 @@ class RedisCommandSanitizerTest extends Specification { def args = ["arg1", "arg 2"] when: - def sanitized = RedisCommandSanitizer.sanitize(command, args) + def sanitized = RedisCommandSanitizer.create(true).sanitize(command, args) then: sanitized == command + " " + args.join(" ") @@ -140,7 +140,7 @@ class RedisCommandSanitizerTest extends Specification { def "should mask all arguments of an unknown command"() { when: - def sanitized = RedisCommandSanitizer.sanitize("NEWAUTH", ["password", "secret"]) + def sanitized = RedisCommandSanitizer.create(true).sanitize("NEWAUTH", ["password", "secret"]) then: sanitized == "NEWAUTH ? ?" diff --git a/instrumentation-api-semconv/src/test/groovy/io/opentelemetry/instrumentation/api/db/SqlStatementSanitizerTest.groovy b/instrumentation-api-semconv/src/test/groovy/io/opentelemetry/instrumentation/api/db/SqlStatementSanitizerTest.groovy index a126b2a3125d..a21418ef2f82 100644 --- a/instrumentation-api-semconv/src/test/groovy/io/opentelemetry/instrumentation/api/db/SqlStatementSanitizerTest.groovy +++ b/instrumentation-api-semconv/src/test/groovy/io/opentelemetry/instrumentation/api/db/SqlStatementSanitizerTest.groovy @@ -12,7 +12,7 @@ class SqlStatementSanitizerTest extends Specification { def "normalize #originalSql"() { setup: - def actualSanitized = SqlStatementSanitizer.sanitize(originalSql) + def actualSanitized = SqlStatementSanitizer.create(true).sanitize(originalSql) expect: actualSanitized.getFullStatement() == sanitizedSql @@ -79,7 +79,7 @@ class SqlStatementSanitizerTest extends Specification { def "normalize couchbase #originalSql"() { setup: - def actualSanitized = SqlStatementSanitizer.sanitize(originalSql, SqlDialect.COUCHBASE) + def actualSanitized = SqlStatementSanitizer.create(true).sanitize(originalSql, SqlDialect.COUCHBASE) expect: actualSanitized.getFullStatement() == sanitizedSql @@ -99,7 +99,7 @@ class SqlStatementSanitizerTest extends Specification { @Unroll def "should simplify #sql"() { expect: - SqlStatementSanitizer.sanitize(sql) == expected + SqlStatementSanitizer.create(true).sanitize(sql) == expected where: sql | expected @@ -169,14 +169,14 @@ class SqlStatementSanitizerTest extends Specification { expect: def sanitizedQuery = query.replace('=123', '=?').substring(0, AutoSqlSanitizer.LIMIT) - SqlStatementSanitizer.sanitize(query) == SqlStatementInfo.create(sanitizedQuery, "SELECT", "table") + SqlStatementSanitizer.create(true).sanitize(query) == SqlStatementInfo.create(sanitizedQuery, "SELECT", "table") } def "lots and lots of ticks don't cause stack overflow or long runtimes"() { setup: String s = "'" for (int i = 0; i < 10000; i++) { - assert SqlStatementSanitizer.sanitize(s) != null + assert SqlStatementSanitizer.create(true).sanitize(s) != null s += "'" } } @@ -187,7 +187,7 @@ class SqlStatementSanitizerTest extends Specification { for (int i = 0; i < 10000; i++) { s += String.valueOf(i) } - assert "?" == SqlStatementSanitizer.sanitize(s).getFullStatement() + assert "?" == SqlStatementSanitizer.create(true).sanitize(s).getFullStatement() } def "very long numbers at end of table name don't cause problem"() { @@ -196,7 +196,7 @@ class SqlStatementSanitizerTest extends Specification { for (int i = 0; i < 10000; i++) { s += String.valueOf(i) } - assert s.substring(0, AutoSqlSanitizer.LIMIT) == SqlStatementSanitizer.sanitize(s).getFullStatement() + assert s.substring(0, AutoSqlSanitizer.LIMIT) == SqlStatementSanitizer.create(true).sanitize(s).getFullStatement() } def "test 32k truncation"() { @@ -205,7 +205,7 @@ class SqlStatementSanitizerTest extends Specification { for (int i = 0; i < 10000; i++) { s.append("SELECT * FROM TABLE WHERE FIELD = 1234 AND ") } - String sanitized = SqlStatementSanitizer.sanitize(s.toString()).getFullStatement() + String sanitized = SqlStatementSanitizer.create(true).sanitize(s.toString()).getFullStatement() System.out.println(sanitized.length()) assert sanitized.length() <= AutoSqlSanitizer.LIMIT assert !sanitized.contains("1234") @@ -219,7 +219,7 @@ class SqlStatementSanitizerTest extends Specification { for (int c = 0; c < 1000; c++) { sb.append((char) r.nextInt((int) Character.MAX_VALUE)) } - SqlStatementSanitizer.sanitize(sb.toString()) + SqlStatementSanitizer.create(true).sanitize(sb.toString()) } } } diff --git a/instrumentation-api-semconv/src/test/java/io/opentelemetry/instrumentation/api/db/StatementSanitizationConfigTest.java b/instrumentation-api-semconv/src/test/java/io/opentelemetry/instrumentation/api/db/StatementSanitizationConfigTest.java deleted file mode 100644 index 30a6e2049089..000000000000 --- a/instrumentation-api-semconv/src/test/java/io/opentelemetry/instrumentation/api/db/StatementSanitizationConfigTest.java +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.instrumentation.api.db; - -import static org.junit.jupiter.api.Assertions.assertFalse; - -import org.junit.jupiter.api.Test; - -public class StatementSanitizationConfigTest { - - @Test - void shouldGetFalse() { - assertFalse(StatementSanitizationConfig.isStatementSanitizationEnabled()); - } -} diff --git a/instrumentation-api-semconv/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/code/CodeAttributesExtractorTest.java b/instrumentation-api-semconv/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/code/CodeAttributesExtractorTest.java index d82ece6ad6ab..7f96f650041e 100644 --- a/instrumentation-api-semconv/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/code/CodeAttributesExtractorTest.java +++ b/instrumentation-api-semconv/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/code/CodeAttributesExtractorTest.java @@ -19,7 +19,7 @@ class CodeAttributesExtractorTest { - static final class TestAtttributesGetter implements CodeAttributesGetter> { + static final class TestAttributesGetter implements CodeAttributesGetter> { @Override public Class codeClass(Map request) { try { @@ -46,7 +46,7 @@ void shouldExtractAllAttributes() { Context context = Context.root(); CodeAttributesExtractor, Void> underTest = - CodeAttributesExtractor.create(new TestAtttributesGetter()); + CodeAttributesExtractor.create(new TestAttributesGetter()); // when AttributesBuilder startAttributes = Attributes.builder(); @@ -68,7 +68,7 @@ void shouldExtractAllAttributes() { void shouldExtractNoAttributesIfNoneAreAvailable() { // given CodeAttributesExtractor, Void> underTest = - CodeAttributesExtractor.create(new TestAtttributesGetter()); + CodeAttributesExtractor.create(new TestAttributesGetter()); // when AttributesBuilder attributes = Attributes.builder(); diff --git a/instrumentation-api-semconv/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/code/CodeSpanNameExtractorTest.java b/instrumentation-api-semconv/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/code/CodeSpanNameExtractorTest.java index 008a8641a284..5bcf88f892a5 100644 --- a/instrumentation-api-semconv/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/code/CodeSpanNameExtractorTest.java +++ b/instrumentation-api-semconv/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/code/CodeSpanNameExtractorTest.java @@ -6,7 +6,7 @@ package io.opentelemetry.instrumentation.api.instrumenter.code; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.mockito.BDDMockito.willReturn; +import static org.mockito.Mockito.doReturn; import io.opentelemetry.instrumentation.api.instrumenter.SpanNameExtractor; import org.junit.jupiter.api.Test; @@ -23,8 +23,8 @@ void shouldExtractFullSpanName() { // given Object request = new Object(); - willReturn(TestClass.class).given(getter).codeClass(request); - willReturn("doSomething").given(getter).methodName(request); + doReturn(TestClass.class).when(getter).codeClass(request); + doReturn("doSomething").when(getter).methodName(request); SpanNameExtractor underTest = CodeSpanNameExtractor.create(getter); @@ -41,8 +41,8 @@ void shouldExtractFullSpanNameForAnonymousClass() { AnonymousBaseClass anon = new AnonymousBaseClass() {}; Object request = new Object(); - willReturn(anon.getClass()).given(getter).codeClass(request); - willReturn("doSomething").given(getter).methodName(request); + doReturn(anon.getClass()).when(getter).codeClass(request); + doReturn("doSomething").when(getter).methodName(request); SpanNameExtractor underTest = CodeSpanNameExtractor.create(getter); @@ -53,6 +53,24 @@ void shouldExtractFullSpanNameForAnonymousClass() { assertEquals(getClass().getSimpleName() + "$1.doSomething", spanName); } + @Test + void shouldExtractFullSpanNameForLambda() { + // given + Runnable lambda = () -> {}; + Object request = new Object(); + + doReturn(lambda.getClass()).when(getter).codeClass(request); + doReturn("doSomething").when(getter).methodName(request); + + SpanNameExtractor underTest = CodeSpanNameExtractor.create(getter); + + // when + String spanName = underTest.extract(request); + + // then + assertEquals(getClass().getSimpleName() + "$$Lambda$.doSomething", spanName); + } + static class TestClass {} static class AnonymousBaseClass {} diff --git a/instrumentation-api-semconv/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/db/DbClientSpanNameExtractorTest.java b/instrumentation-api-semconv/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/db/DbClientSpanNameExtractorTest.java index 348562a30c7f..e88d82e7ecad 100644 --- a/instrumentation-api-semconv/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/db/DbClientSpanNameExtractorTest.java +++ b/instrumentation-api-semconv/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/db/DbClientSpanNameExtractorTest.java @@ -6,7 +6,7 @@ package io.opentelemetry.instrumentation.api.instrumenter.db; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.when; import io.opentelemetry.instrumentation.api.instrumenter.SpanNameExtractor; import org.junit.jupiter.api.Test; @@ -24,8 +24,8 @@ void shouldExtractFullSpanName() { // given DbRequest dbRequest = new DbRequest(); - given(sqlAttributesGetter.rawStatement(dbRequest)).willReturn("SELECT * from table"); - given(sqlAttributesGetter.name(dbRequest)).willReturn("database"); + when(sqlAttributesGetter.rawStatement(dbRequest)).thenReturn("SELECT * from table"); + when(sqlAttributesGetter.name(dbRequest)).thenReturn("database"); SpanNameExtractor underTest = DbClientSpanNameExtractor.create(sqlAttributesGetter); @@ -41,8 +41,8 @@ void shouldSkipDbNameIfTableAlreadyHasDbNamePrefix() { // given DbRequest dbRequest = new DbRequest(); - given(sqlAttributesGetter.rawStatement(dbRequest)).willReturn("SELECT * from another.table"); - given(sqlAttributesGetter.name(dbRequest)).willReturn("database"); + when(sqlAttributesGetter.rawStatement(dbRequest)).thenReturn("SELECT * from another.table"); + when(sqlAttributesGetter.name(dbRequest)).thenReturn("database"); SpanNameExtractor underTest = DbClientSpanNameExtractor.create(sqlAttributesGetter); @@ -58,7 +58,7 @@ void shouldExtractOperationAndTable() { // given DbRequest dbRequest = new DbRequest(); - given(sqlAttributesGetter.rawStatement(dbRequest)).willReturn("SELECT * from table"); + when(sqlAttributesGetter.rawStatement(dbRequest)).thenReturn("SELECT * from table"); SpanNameExtractor underTest = DbClientSpanNameExtractor.create(sqlAttributesGetter); @@ -74,8 +74,8 @@ void shouldExtractOperationAndName() { // given DbRequest dbRequest = new DbRequest(); - given(dbAttributesGetter.operation(dbRequest)).willReturn("SELECT"); - given(dbAttributesGetter.name(dbRequest)).willReturn("database"); + when(dbAttributesGetter.operation(dbRequest)).thenReturn("SELECT"); + when(dbAttributesGetter.name(dbRequest)).thenReturn("database"); SpanNameExtractor underTest = DbClientSpanNameExtractor.create(dbAttributesGetter); @@ -91,7 +91,7 @@ void shouldExtractOperation() { // given DbRequest dbRequest = new DbRequest(); - given(dbAttributesGetter.operation(dbRequest)).willReturn("SELECT"); + when(dbAttributesGetter.operation(dbRequest)).thenReturn("SELECT"); SpanNameExtractor underTest = DbClientSpanNameExtractor.create(dbAttributesGetter); @@ -107,7 +107,7 @@ void shouldExtractDbName() { // given DbRequest dbRequest = new DbRequest(); - given(dbAttributesGetter.name(dbRequest)).willReturn("database"); + when(dbAttributesGetter.name(dbRequest)).thenReturn("database"); SpanNameExtractor underTest = DbClientSpanNameExtractor.create(dbAttributesGetter); diff --git a/instrumentation-api-semconv/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/http/HttpClientAttributesExtractorTest.java b/instrumentation-api-semconv/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/http/HttpClientAttributesExtractorTest.java index 0e1a0b8456dc..d99052a589e3 100644 --- a/instrumentation-api-semconv/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/http/HttpClientAttributesExtractorTest.java +++ b/instrumentation-api-semconv/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/http/HttpClientAttributesExtractorTest.java @@ -10,6 +10,7 @@ import static java.util.Collections.emptyList; import static java.util.Collections.singletonList; import static org.assertj.core.api.Assertions.entry; +import static org.junit.jupiter.params.provider.Arguments.arguments; import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.api.common.Attributes; @@ -19,7 +20,12 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.stream.Stream; +import javax.annotation.Nullable; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; class HttpClientAttributesExtractorTest { @@ -43,20 +49,8 @@ public List requestHeader(Map request, String name) { } @Override - public Long requestContentLength(Map request, Map response) { - String value = request.get("requestContentLength"); - return value == null ? null : Long.parseLong(value); - } - - @Override - public Long requestContentLengthUncompressed( - Map request, Map response) { - String value = request.get("requestContentLengthUncompressed"); - return value == null ? null : Long.parseLong(value); - } - - @Override - public Integer statusCode(Map request, Map response) { + public Integer statusCode( + Map request, Map response, @Nullable Throwable error) { return Integer.parseInt(response.get("statusCode")); } @@ -65,19 +59,6 @@ public String flavor(Map request, Map response) return request.get("flavor"); } - @Override - public Long responseContentLength(Map request, Map response) { - String value = response.get("responseContentLength"); - return value == null ? null : Long.parseLong(value); - } - - @Override - public Long responseContentLengthUncompressed( - Map request, Map response) { - String value = response.get("responseContentLengthUncompressed"); - return value == null ? null : Long.parseLong(value); - } - @Override public List responseHeader( Map request, Map response, String name) { @@ -91,16 +72,14 @@ void normal() { Map request = new HashMap<>(); request.put("method", "POST"); request.put("url", "http://github.com"); - request.put("requestContentLength", "10"); - request.put("requestContentLengthUncompressed", "11"); + request.put("header.content-length", "10"); request.put("flavor", "http/2"); request.put("header.user-agent", "okhttp 3.x"); request.put("header.custom-request-header", "123,456"); Map response = new HashMap<>(); response.put("statusCode", "202"); - response.put("responseContentLength", "20"); - response.put("responseContentLengthUncompressed", "21"); + response.put("header.content-length", "20"); response.put("header.custom-response-header", "654,321"); HttpClientAttributesExtractor, Map> extractor = @@ -130,16 +109,47 @@ void normal() { AttributeKey.stringArrayKey("http.request.header.custom_request_header"), asList("123", "456")), entry(SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH, 10L), - entry(SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH_UNCOMPRESSED, 11L), entry(SemanticAttributes.HTTP_FLAVOR, "http/2"), entry(SemanticAttributes.HTTP_STATUS_CODE, 202L), entry(SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH, 20L), - entry(SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH_UNCOMPRESSED, 21L), entry( AttributeKey.stringArrayKey("http.response.header.custom_response_header"), asList("654", "321"))); } + @ParameterizedTest + @MethodSource("stripUrlArguments") + void stripBasicAuthTest(String url, String expectedResult) { + Map request = new HashMap<>(); + request.put("url", url); + + stripRequestTest(request, expectedResult); + } + + private static Stream stripUrlArguments() { + return Stream.of( + arguments("https://user1:secret@github.com", "https://github.com"), + arguments("https://user1:secret@github.com/path/", "https://github.com/path/"), + arguments("https://user1:secret@github.com#test.html", "https://github.com#test.html"), + arguments("https://user1:secret@github.com?foo=b@r", "https://github.com?foo=b@r"), + arguments( + "https://user1:secret@github.com/p@th?foo=b@r", "https://github.com/p@th?foo=b@r"), + arguments("https://github.com/p@th?foo=b@r", "https://github.com/p@th?foo=b@r"), + arguments("https://github.com#t@st.html", "https://github.com#t@st.html"), + arguments("user1:secret@github.com", "user1:secret@github.com"), + arguments("https://github.com@", "https://github.com@")); + } + + private static void stripRequestTest(Map request, String expected) { + HttpClientAttributesExtractor, Map> extractor = + HttpClientAttributesExtractor.builder(new TestHttpClientAttributesGetter()).build(); + + AttributesBuilder attributes = Attributes.builder(); + extractor.onStart(attributes, Context.root(), request); + + assertThat(attributes.build()).containsOnly(entry(SemanticAttributes.HTTP_URL, expected)); + } + @Test void invalidStatusCode() { Map request = new HashMap<>(); diff --git a/instrumentation-api-semconv/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/http/HttpClientMetricsTest.java b/instrumentation-api-semconv/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/http/HttpClientMetricsTest.java index 7a8fa75744a8..77bfedd390da 100644 --- a/instrumentation-api-semconv/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/http/HttpClientMetricsTest.java +++ b/instrumentation-api-semconv/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/http/HttpClientMetricsTest.java @@ -5,6 +5,7 @@ package io.opentelemetry.instrumentation.api.instrumenter.http; +import static io.opentelemetry.api.common.AttributeKey.stringKey; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; @@ -35,19 +36,22 @@ void collectsMetrics() { Attributes.builder() .put("http.method", "GET") .put("http.url", "https://localhost:1234/") - .put("http.host", "host") .put("http.target", "/") .put("http.scheme", "https") .put("net.peer.name", "localhost") - .put("net.peer.ip", "0.0.0.0") .put("net.peer.port", 1234) + .put("http.request_content_length", 100) .build(); Attributes responseAttributes = Attributes.builder() .put("http.flavor", "2.0") - .put("http.server_name", "server") .put("http.status_code", 200) + .put("http.response_content_length", 200) + .put("net.sock.family", "inet") + .put("net.peer.sock.addr", "1.2.3.4") + .put("net.peer.sock.name", "somehost20") + .put("net.peer.sock.port", 8080) .build(); Context parent = @@ -83,16 +87,51 @@ void collectsMetrics() { point .hasSum(150 /* millis */) .hasAttributesSatisfying( - equalTo(SemanticAttributes.NET_PEER_NAME, "localhost"), - equalTo(SemanticAttributes.NET_PEER_PORT, 1234), equalTo(SemanticAttributes.HTTP_METHOD, "GET"), + equalTo(SemanticAttributes.HTTP_STATUS_CODE, 200), equalTo(SemanticAttributes.HTTP_FLAVOR, "2.0"), - equalTo(SemanticAttributes.HTTP_STATUS_CODE, 200)) + equalTo(SemanticAttributes.NET_PEER_NAME, "localhost"), + equalTo(SemanticAttributes.NET_PEER_PORT, 1234), + equalTo(stringKey("net.peer.sock.addr"), "1.2.3.4")) .hasExemplarsSatisfying( exemplar -> exemplar .hasTraceId("ff01020304050600ff0a0b0c0d0e0f00") - .hasSpanId("090a0b0c0d0e0f00"))))); + .hasSpanId("090a0b0c0d0e0f00")))), + metric -> + assertThat(metric) + .hasName("http.client.request.size") + .hasUnit("By") + .hasHistogramSatisfying( + histogram -> + histogram.hasPointsSatisfying( + point -> + point + .hasSum(100 /* bytes */) + .hasAttributesSatisfying( + equalTo(SemanticAttributes.HTTP_METHOD, "GET"), + equalTo(SemanticAttributes.HTTP_STATUS_CODE, 200), + equalTo(SemanticAttributes.HTTP_FLAVOR, "2.0"), + equalTo(SemanticAttributes.NET_PEER_NAME, "localhost"), + equalTo(SemanticAttributes.NET_PEER_PORT, 1234), + equalTo(stringKey("net.peer.sock.addr"), "1.2.3.4")))), + metric -> + assertThat(metric) + .hasName("http.client.response.size") + .hasUnit("By") + .hasHistogramSatisfying( + histogram -> + histogram.hasPointsSatisfying( + point -> + point + .hasSum(200 /* bytes */) + .hasAttributesSatisfying( + equalTo(SemanticAttributes.HTTP_METHOD, "GET"), + equalTo(SemanticAttributes.HTTP_STATUS_CODE, 200), + equalTo(SemanticAttributes.HTTP_FLAVOR, "2.0"), + equalTo(SemanticAttributes.NET_PEER_NAME, "localhost"), + equalTo(SemanticAttributes.NET_PEER_PORT, 1234), + equalTo(stringKey("net.peer.sock.addr"), "1.2.3.4"))))); listener.onEnd(context2, responseAttributes, nanos(300)); @@ -103,8 +142,19 @@ void collectsMetrics() { .hasName("http.client.duration") .hasHistogramSatisfying( histogram -> - histogram.hasPointsSatisfying( - point -> point.hasSum(300 /* millis */)))); + histogram.hasPointsSatisfying(point -> point.hasSum(300 /* millis */))), + metric -> + assertThat(metric) + .hasName("http.client.request.size") + .hasHistogramSatisfying( + histogram -> + histogram.hasPointsSatisfying(point -> point.hasSum(200 /* bytes */))), + metric -> + assertThat(metric) + .hasName("http.client.response.size") + .hasHistogramSatisfying( + histogram -> + histogram.hasPointsSatisfying(point -> point.hasSum(400 /* bytes */)))); } private static long nanos(int millis) { diff --git a/instrumentation-api-semconv/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/http/HttpClientSpanStatusExtractorTest.java b/instrumentation-api-semconv/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/http/HttpClientSpanStatusExtractorTest.java deleted file mode 100644 index 3fe7fa16c127..000000000000 --- a/instrumentation-api-semconv/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/http/HttpClientSpanStatusExtractorTest.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.instrumentation.api.instrumenter.http; - -import static org.mockito.ArgumentMatchers.anyMap; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoInteractions; -import static org.mockito.Mockito.when; - -import io.opentelemetry.api.trace.StatusCode; -import io.opentelemetry.instrumentation.api.instrumenter.SpanStatusBuilder; -import java.util.Collections; -import java.util.Map; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.ValueSource; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; - -@ExtendWith(MockitoExtension.class) -class HttpClientSpanStatusExtractorTest { - - @Mock private HttpClientAttributesGetter, Map> getter; - - @Mock private SpanStatusBuilder spanStatusBuilder; - - @ParameterizedTest - @ValueSource(ints = {1, 100, 101, 200, 201, 300, 301, 400, 401, 500, 501, 600, 601}) - void hasStatus(int statusCode) { - StatusCode expectedStatusCode = HttpStatusConverter.CLIENT.statusFromHttpStatus(statusCode); - when(getter.statusCode(anyMap(), anyMap())).thenReturn(statusCode); - - HttpSpanStatusExtractor.create(getter) - .extract(spanStatusBuilder, Collections.emptyMap(), Collections.emptyMap(), null); - - if (expectedStatusCode != StatusCode.UNSET) { - verify(spanStatusBuilder).setStatus(expectedStatusCode); - } else { - verifyNoInteractions(spanStatusBuilder); - } - } - - @ParameterizedTest - @ValueSource(ints = {1, 100, 101, 200, 201, 300, 301, 400, 401, 500, 501, 600, 601}) - void hasStatusAndException(int statusCode) { - StatusCode expectedStatusCode = HttpStatusConverter.CLIENT.statusFromHttpStatus(statusCode); - when(getter.statusCode(anyMap(), anyMap())).thenReturn(statusCode); - - // Presence of exception has no effect. - HttpSpanStatusExtractor.create(getter) - .extract( - spanStatusBuilder, - Collections.emptyMap(), - Collections.emptyMap(), - new IllegalStateException("test")); - - if (expectedStatusCode != StatusCode.UNSET) { - verify(spanStatusBuilder).setStatus(expectedStatusCode); - } else { - verify(spanStatusBuilder).setStatus(StatusCode.ERROR); - } - } - - @Test - void hasNoStatus_fallsBackToDefault_unset() { - when(getter.statusCode(anyMap(), anyMap())).thenReturn(null); - - HttpSpanStatusExtractor.create(getter) - .extract(spanStatusBuilder, Collections.emptyMap(), Collections.emptyMap(), null); - - verifyNoInteractions(spanStatusBuilder); - } - - @Test - void hasNoStatus_fallsBackToDefault_error() { - when(getter.statusCode(anyMap(), anyMap())).thenReturn(null); - - HttpSpanStatusExtractor.create(getter) - .extract( - spanStatusBuilder, - Collections.emptyMap(), - Collections.emptyMap(), - new IllegalStateException("test")); - - verify(spanStatusBuilder).setStatus(StatusCode.ERROR); - } -} diff --git a/instrumentation-api-semconv/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/http/HttpClientStatusConverterTest.groovy b/instrumentation-api-semconv/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/http/HttpClientStatusConverterTest.groovy deleted file mode 100644 index 8cf2f5c079df..000000000000 --- a/instrumentation-api-semconv/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/http/HttpClientStatusConverterTest.groovy +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.instrumentation.api.instrumenter.http - -import io.opentelemetry.api.trace.StatusCode -import spock.lang.Specification - -class HttpClientStatusConverterTest extends Specification { - - def "test HTTP #httpStatus to OTel #expectedStatus"() { - when: - def status = HttpStatusConverter.CLIENT.statusFromHttpStatus(httpStatus) - - then: - status == expectedStatus - - // https://en.wikipedia.org/wiki/List_of_HTTP_status_codes - where: - httpStatus | expectedStatus - 100 | StatusCode.UNSET - 101 | StatusCode.UNSET - 102 | StatusCode.UNSET - 103 | StatusCode.UNSET - - 200 | StatusCode.UNSET - 201 | StatusCode.UNSET - 202 | StatusCode.UNSET - 203 | StatusCode.UNSET - 204 | StatusCode.UNSET - 205 | StatusCode.UNSET - 206 | StatusCode.UNSET - 207 | StatusCode.UNSET - 208 | StatusCode.UNSET - 226 | StatusCode.UNSET - - 300 | StatusCode.UNSET - 301 | StatusCode.UNSET - 302 | StatusCode.UNSET - 303 | StatusCode.UNSET - 304 | StatusCode.UNSET - 305 | StatusCode.UNSET - 306 | StatusCode.UNSET - 307 | StatusCode.UNSET - 308 | StatusCode.UNSET - - 400 | StatusCode.ERROR - 401 | StatusCode.ERROR - 403 | StatusCode.ERROR - 404 | StatusCode.ERROR - 405 | StatusCode.ERROR - 406 | StatusCode.ERROR - 407 | StatusCode.ERROR - 408 | StatusCode.ERROR - 409 | StatusCode.ERROR - 410 | StatusCode.ERROR - 411 | StatusCode.ERROR - 412 | StatusCode.ERROR - 413 | StatusCode.ERROR - 414 | StatusCode.ERROR - 415 | StatusCode.ERROR - 416 | StatusCode.ERROR - 417 | StatusCode.ERROR - 418 | StatusCode.ERROR - 421 | StatusCode.ERROR - 422 | StatusCode.ERROR - 423 | StatusCode.ERROR - 424 | StatusCode.ERROR - 425 | StatusCode.ERROR - 426 | StatusCode.ERROR - 428 | StatusCode.ERROR - 429 | StatusCode.ERROR - 431 | StatusCode.ERROR - 451 | StatusCode.ERROR - - 500 | StatusCode.ERROR - 501 | StatusCode.ERROR - 502 | StatusCode.ERROR - 503 | StatusCode.ERROR - 504 | StatusCode.ERROR - 505 | StatusCode.ERROR - 506 | StatusCode.ERROR - 507 | StatusCode.ERROR - 508 | StatusCode.ERROR - 510 | StatusCode.ERROR - 511 | StatusCode.ERROR - - // Don't exist - 99 | StatusCode.ERROR - 600 | StatusCode.ERROR - } -} diff --git a/instrumentation-api-semconv/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/http/HttpClientStatusConverterTest.java b/instrumentation-api-semconv/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/http/HttpClientStatusConverterTest.java new file mode 100644 index 000000000000..7840ad60ed1e --- /dev/null +++ b/instrumentation-api-semconv/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/http/HttpClientStatusConverterTest.java @@ -0,0 +1,94 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.api.instrumenter.http; + +import static io.opentelemetry.instrumentation.api.instrumenter.http.HttpStatusConverter.CLIENT; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import io.opentelemetry.api.trace.StatusCode; +import java.util.stream.Stream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +public class HttpClientStatusConverterTest { + + @ParameterizedTest + @MethodSource("generateParams") + void httpStatusCodeToOtelStatus(int numeric, StatusCode code) { + assertEquals(code, CLIENT.statusFromHttpStatus(numeric)); + } + + static Stream generateParams() { + return Stream.of( + Arguments.of(100, StatusCode.UNSET), + Arguments.of(101, StatusCode.UNSET), + Arguments.of(102, StatusCode.UNSET), + Arguments.of(103, StatusCode.UNSET), + Arguments.of(200, StatusCode.UNSET), + Arguments.of(201, StatusCode.UNSET), + Arguments.of(202, StatusCode.UNSET), + Arguments.of(203, StatusCode.UNSET), + Arguments.of(204, StatusCode.UNSET), + Arguments.of(205, StatusCode.UNSET), + Arguments.of(206, StatusCode.UNSET), + Arguments.of(207, StatusCode.UNSET), + Arguments.of(208, StatusCode.UNSET), + Arguments.of(226, StatusCode.UNSET), + Arguments.of(300, StatusCode.UNSET), + Arguments.of(301, StatusCode.UNSET), + Arguments.of(302, StatusCode.UNSET), + Arguments.of(303, StatusCode.UNSET), + Arguments.of(304, StatusCode.UNSET), + Arguments.of(305, StatusCode.UNSET), + Arguments.of(306, StatusCode.UNSET), + Arguments.of(307, StatusCode.UNSET), + Arguments.of(308, StatusCode.UNSET), + Arguments.of(400, StatusCode.ERROR), + Arguments.of(401, StatusCode.ERROR), + Arguments.of(403, StatusCode.ERROR), + Arguments.of(404, StatusCode.ERROR), + Arguments.of(405, StatusCode.ERROR), + Arguments.of(406, StatusCode.ERROR), + Arguments.of(407, StatusCode.ERROR), + Arguments.of(408, StatusCode.ERROR), + Arguments.of(409, StatusCode.ERROR), + Arguments.of(410, StatusCode.ERROR), + Arguments.of(411, StatusCode.ERROR), + Arguments.of(412, StatusCode.ERROR), + Arguments.of(413, StatusCode.ERROR), + Arguments.of(414, StatusCode.ERROR), + Arguments.of(415, StatusCode.ERROR), + Arguments.of(416, StatusCode.ERROR), + Arguments.of(417, StatusCode.ERROR), + Arguments.of(418, StatusCode.ERROR), + Arguments.of(421, StatusCode.ERROR), + Arguments.of(422, StatusCode.ERROR), + Arguments.of(423, StatusCode.ERROR), + Arguments.of(424, StatusCode.ERROR), + Arguments.of(425, StatusCode.ERROR), + Arguments.of(426, StatusCode.ERROR), + Arguments.of(428, StatusCode.ERROR), + Arguments.of(429, StatusCode.ERROR), + Arguments.of(431, StatusCode.ERROR), + Arguments.of(451, StatusCode.ERROR), + Arguments.of(500, StatusCode.ERROR), + Arguments.of(501, StatusCode.ERROR), + Arguments.of(502, StatusCode.ERROR), + Arguments.of(503, StatusCode.ERROR), + Arguments.of(504, StatusCode.ERROR), + Arguments.of(505, StatusCode.ERROR), + Arguments.of(506, StatusCode.ERROR), + Arguments.of(507, StatusCode.ERROR), + Arguments.of(508, StatusCode.ERROR), + Arguments.of(510, StatusCode.ERROR), + Arguments.of(511, StatusCode.ERROR), + + // Don't exist + Arguments.of(99, StatusCode.ERROR), + Arguments.of(600, StatusCode.ERROR)); + } +} diff --git a/instrumentation-api-semconv/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/http/HttpRouteHolderTest.java b/instrumentation-api-semconv/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/http/HttpRouteHolderTest.java index 1f8fc4944ffe..a74ad863ce04 100644 --- a/instrumentation-api-semconv/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/http/HttpRouteHolderTest.java +++ b/instrumentation-api-semconv/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/http/HttpRouteHolderTest.java @@ -23,7 +23,7 @@ void shouldSetRouteEvenIfSpanIsNotSampled() { Instrumenter instrumenter = Instrumenter.builder(testing.getOpenTelemetry(), "test", s -> s) .addContextCustomizer(HttpRouteHolder.get()) - .newInstrumenter(); + .buildInstrumenter(); Context context = instrumenter.start(Context.root(), "test"); diff --git a/instrumentation-api-semconv/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/http/HttpServerAttributesExtractorTest.java b/instrumentation-api-semconv/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/http/HttpServerAttributesExtractorTest.java index d815679d4382..b101ce84217b 100644 --- a/instrumentation-api-semconv/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/http/HttpServerAttributesExtractorTest.java +++ b/instrumentation-api-semconv/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/http/HttpServerAttributesExtractorTest.java @@ -15,122 +15,113 @@ import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.common.AttributesBuilder; import io.opentelemetry.context.Context; +import io.opentelemetry.instrumentation.api.instrumenter.net.NetServerAttributesGetter; import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.function.Function; +import javax.annotation.Nullable; import org.junit.jupiter.api.Test; class HttpServerAttributesExtractorTest { - static class TestHttpServerAttributesExtractor - implements HttpServerAttributesGetter, Map> { + static class TestHttpServerAttributesGetter + implements HttpServerAttributesGetter, Map> { @Override - public String method(Map request) { - return request.get("method"); + public String method(Map request) { + return (String) request.get("method"); } @Override - public String target(Map request) { - return request.get("target"); + public String target(Map request) { + return (String) request.get("target"); } @Override - public String route(Map request) { - return request.get("route"); + public String route(Map request) { + return (String) request.get("route"); } @Override - public String scheme(Map request) { - return request.get("scheme"); + public String scheme(Map request) { + return (String) request.get("scheme"); } @Override - public String serverName(Map request) { - return request.get("serverName"); - } - - @Override - public List requestHeader(Map request, String name) { - String values = request.get("header." + name); + public List requestHeader(Map request, String name) { + String values = (String) request.get("header." + name); return values == null ? emptyList() : asList(values.split(",")); } @Override - public Long requestContentLength(Map request, Map response) { - String value = request.get("requestContentLength"); - return value == null ? null : Long.parseLong(value); - } - - @Override - public Long requestContentLengthUncompressed( - Map request, Map response) { - String value = request.get("requestContentLengthUncompressed"); - return value == null ? null : Long.parseLong(value); + public Integer statusCode( + Map request, Map response, @Nullable Throwable error) { + String value = (String) response.get("statusCode"); + return value == null ? null : Integer.parseInt(value); } @Override - public Integer statusCode(Map request, Map response) { - String value = response.get("statusCode"); - return value == null ? null : Integer.parseInt(value); + public String flavor(Map request) { + return (String) request.get("flavor"); } @Override - public String flavor(Map request) { - return request.get("flavor"); + public List responseHeader( + Map request, Map response, String name) { + String values = (String) response.get("header." + name); + return values == null ? emptyList() : asList(values.split(",")); } + } + static class TestNetServerAttributesGetter + implements NetServerAttributesGetter> { + @Nullable @Override - public Long responseContentLength(Map request, Map response) { - String value = response.get("responseContentLength"); - return value == null ? null : Long.parseLong(value); + public String transport(Map request) { + return (String) request.get("transport"); } + @Nullable @Override - public Long responseContentLengthUncompressed( - Map request, Map response) { - String value = response.get("responseContentLengthUncompressed"); - return value == null ? null : Long.parseLong(value); + public String hostName(Map request) { + return (String) request.get("hostName"); } + @Nullable @Override - public List responseHeader( - Map request, Map response, String name) { - String values = response.get("header." + name); - return values == null ? emptyList() : asList(values.split(",")); + public Integer hostPort(Map request) { + return (Integer) request.get("hostPort"); } } @Test void normal() { - Map request = new HashMap<>(); + Map request = new HashMap<>(); request.put("method", "POST"); request.put("url", "http://github.com"); request.put("target", "/repositories/1"); request.put("scheme", "http"); - request.put("requestContentLength", "10"); - request.put("requestContentLengthUncompressed", "11"); + request.put("header.content-length", "10"); request.put("flavor", "http/2"); request.put("route", "/repositories/{id}"); - request.put("serverName", "server"); request.put("header.user-agent", "okhttp 3.x"); request.put("header.host", "github.com"); request.put("header.forwarded", "for=1.1.1.1;proto=https"); request.put("header.custom-request-header", "123,456"); - Map response = new HashMap<>(); + Map response = new HashMap<>(); response.put("statusCode", "202"); - response.put("responseContentLength", "20"); - response.put("responseContentLengthUncompressed", "21"); + response.put("header.content-length", "20"); response.put("header.custom-response-header", "654,321"); Function routeFromContext = ctx -> "/repositories/{repoId}"; - HttpServerAttributesExtractor, Map> extractor = + HttpServerAttributesExtractor, Map> extractor = new HttpServerAttributesExtractor<>( - new TestHttpServerAttributesExtractor(), + new TestHttpServerAttributesGetter(), + new TestNetServerAttributesGetter(), singletonList("Custom-Request-Header"), singletonList("Custom-Response-Header"), routeFromContext); @@ -139,14 +130,13 @@ void normal() { extractor.onStart(attributes, Context.root(), request); assertThat(attributes.build()) .containsOnly( + entry(SemanticAttributes.NET_HOST_NAME, "github.com"), entry(SemanticAttributes.HTTP_FLAVOR, "http/2"), entry(SemanticAttributes.HTTP_METHOD, "POST"), entry(SemanticAttributes.HTTP_SCHEME, "https"), - entry(SemanticAttributes.HTTP_HOST, "github.com"), entry(SemanticAttributes.HTTP_TARGET, "/repositories/1"), entry(SemanticAttributes.HTTP_USER_AGENT, "okhttp 3.x"), entry(SemanticAttributes.HTTP_ROUTE, "/repositories/{id}"), - entry(SemanticAttributes.HTTP_SERVER_NAME, "server"), entry(SemanticAttributes.HTTP_CLIENT_IP, "1.1.1.1"), entry( AttributeKey.stringArrayKey("http.request.header.custom_request_header"), @@ -155,24 +145,20 @@ void normal() { extractor.onEnd(attributes, Context.root(), request, response, null); assertThat(attributes.build()) .containsOnly( + entry(SemanticAttributes.NET_HOST_NAME, "github.com"), entry(SemanticAttributes.HTTP_METHOD, "POST"), entry(SemanticAttributes.HTTP_SCHEME, "https"), - entry(SemanticAttributes.HTTP_HOST, "github.com"), entry(SemanticAttributes.HTTP_TARGET, "/repositories/1"), entry(SemanticAttributes.HTTP_USER_AGENT, "okhttp 3.x"), entry(SemanticAttributes.HTTP_ROUTE, "/repositories/{repoId}"), - entry(SemanticAttributes.HTTP_SERVER_NAME, "server"), entry(SemanticAttributes.HTTP_CLIENT_IP, "1.1.1.1"), + entry(SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH, 10L), entry( AttributeKey.stringArrayKey("http.request.header.custom_request_header"), asList("123", "456")), - entry(SemanticAttributes.HTTP_SERVER_NAME, "server"), - entry(SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH, 10L), - entry(SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH_UNCOMPRESSED, 11L), entry(SemanticAttributes.HTTP_FLAVOR, "http/2"), entry(SemanticAttributes.HTTP_STATUS_CODE, 202L), entry(SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH, 20L), - entry(SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH_UNCOMPRESSED, 21L), entry( AttributeKey.stringArrayKey("http.response.header.custom_response_header"), asList("654", "321"))); @@ -180,11 +166,12 @@ void normal() { @Test void extractClientIpFromX_Forwarded_For() { - Map request = new HashMap<>(); + Map request = new HashMap<>(); request.put("header.x-forwarded-for", "1.1.1.1"); - HttpServerAttributesExtractor, Map> extractor = - HttpServerAttributesExtractor.builder(new TestHttpServerAttributesExtractor()) + HttpServerAttributesExtractor, Map> extractor = + HttpServerAttributesExtractor.builder( + new TestHttpServerAttributesGetter(), new TestNetServerAttributesGetter()) .setCapturedRequestHeaders(emptyList()) .setCapturedResponseHeaders(emptyList()) .build(); @@ -201,11 +188,12 @@ void extractClientIpFromX_Forwarded_For() { @Test void extractClientIpFromX_Forwarded_Proto() { - Map request = new HashMap<>(); + Map request = new HashMap<>(); request.put("header.x-forwarded-proto", "https"); - HttpServerAttributesExtractor, Map> extractor = - HttpServerAttributesExtractor.builder(new TestHttpServerAttributesExtractor()) + HttpServerAttributesExtractor, Map> extractor = + HttpServerAttributesExtractor.builder( + new TestHttpServerAttributesGetter(), new TestNetServerAttributesGetter()) .setCapturedRequestHeaders(emptyList()) .setCapturedResponseHeaders(emptyList()) .build(); @@ -217,4 +205,46 @@ void extractClientIpFromX_Forwarded_Proto() { extractor.onEnd(attributes, Context.root(), request, null, null); assertThat(attributes.build()).containsOnly(entry(SemanticAttributes.HTTP_SCHEME, "https")); } + + @Test + void extractNetHostAndPortFromHostHeader() { + Map request = new HashMap<>(); + request.put("header.host", "thehost:777"); + + HttpServerAttributesExtractor, Map> extractor = + HttpServerAttributesExtractor.builder( + new TestHttpServerAttributesGetter(), new TestNetServerAttributesGetter()) + .setCapturedRequestHeaders(emptyList()) + .setCapturedResponseHeaders(emptyList()) + .build(); + + AttributesBuilder attributes = Attributes.builder(); + extractor.onStart(attributes, Context.root(), request); + assertThat(attributes.build()) + .containsOnly( + entry(SemanticAttributes.NET_HOST_NAME, "thehost"), + entry(SemanticAttributes.NET_HOST_PORT, 777L)); + } + + @Test + void extractNetHostAndPortFromNetAttributesGetter() { + Map request = new HashMap<>(); + request.put("header.host", "notthehost:77777"); // this should have lower precedence + request.put("hostName", "thehost"); + request.put("hostPort", 777); + + HttpServerAttributesExtractor, Map> extractor = + HttpServerAttributesExtractor.builder( + new TestHttpServerAttributesGetter(), new TestNetServerAttributesGetter()) + .setCapturedRequestHeaders(emptyList()) + .setCapturedResponseHeaders(emptyList()) + .build(); + + AttributesBuilder attributes = Attributes.builder(); + extractor.onStart(attributes, Context.root(), request); + assertThat(attributes.build()) + .containsOnly( + entry(SemanticAttributes.NET_HOST_NAME, "thehost"), + entry(SemanticAttributes.NET_HOST_PORT, 777L)); + } } diff --git a/instrumentation-api-semconv/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/http/HttpServerMetricsTest.java b/instrumentation-api-semconv/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/http/HttpServerMetricsTest.java index 5ab895909bc0..b17263bfeb46 100644 --- a/instrumentation-api-semconv/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/http/HttpServerMetricsTest.java +++ b/instrumentation-api-semconv/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/http/HttpServerMetricsTest.java @@ -7,6 +7,8 @@ import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; +import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.HttpFlavorValues.HTTP_2_0; +import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.NetTransportValues.IP_TCP; import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.trace.Span; @@ -34,18 +36,24 @@ void collectsMetrics() { Attributes requestAttributes = Attributes.builder() .put("http.method", "GET") - .put("http.host", "host") + .put("http.flavor", HTTP_2_0) .put("http.target", "/") .put("http.scheme", "https") + .put("net.transport", IP_TCP) .put("net.host.name", "localhost") .put("net.host.port", 1234) + .put("net.sock.family", "inet") + .put("net.peer.sock.addr", "1.2.3.4") + .put("net.peer.sock.port", 8080) + .put("net.host.sock.addr", "4.3.2.1") + .put("net.host.sock.port", 9090) .build(); Attributes responseAttributes = Attributes.builder() - .put("http.flavor", "2.0") - .put("http.server_name", "server") .put("http.status_code", 200) + .put("http.request_content_length", 100) + .put("http.response_content_length", 200) .build(); SpanContext spanContext1 = @@ -71,7 +79,7 @@ void collectsMetrics() { .hasName("http.server.active_requests") .hasDescription( "The number of concurrent HTTP requests that are currently in-flight") - .hasUnit("requests") + .hasUnit("{requests}") .hasLongSumSatisfying( sum -> sum.hasPointsSatisfying( @@ -79,9 +87,10 @@ void collectsMetrics() { point .hasValue(1) .hasAttributesSatisfying( - equalTo(SemanticAttributes.HTTP_HOST, "host"), equalTo(SemanticAttributes.HTTP_METHOD, "GET"), - equalTo(SemanticAttributes.HTTP_SCHEME, "https")) + equalTo(SemanticAttributes.HTTP_SCHEME, "https"), + equalTo(SemanticAttributes.HTTP_FLAVOR, HTTP_2_0), + equalTo(SemanticAttributes.NET_HOST_NAME, "localhost")) .hasExemplarsSatisfying( exemplar -> exemplar @@ -103,9 +112,10 @@ void collectsMetrics() { point .hasValue(2) .hasAttributesSatisfying( - equalTo(SemanticAttributes.HTTP_HOST, "host"), equalTo(SemanticAttributes.HTTP_METHOD, "GET"), - equalTo(SemanticAttributes.HTTP_SCHEME, "https")) + equalTo(SemanticAttributes.HTTP_SCHEME, "https"), + equalTo(SemanticAttributes.HTTP_FLAVOR, HTTP_2_0), + equalTo(SemanticAttributes.NET_HOST_NAME, "localhost")) .hasExemplarsSatisfying( exemplar -> exemplar @@ -126,9 +136,10 @@ void collectsMetrics() { point .hasValue(1) .hasAttributesSatisfying( - equalTo(SemanticAttributes.HTTP_HOST, "host"), equalTo(SemanticAttributes.HTTP_METHOD, "GET"), - equalTo(SemanticAttributes.HTTP_SCHEME, "https")) + equalTo(SemanticAttributes.HTTP_SCHEME, "https"), + equalTo(SemanticAttributes.HTTP_FLAVOR, HTTP_2_0), + equalTo(SemanticAttributes.NET_HOST_NAME, "localhost")) .hasExemplarsSatisfying( exemplar -> exemplar @@ -145,16 +156,51 @@ void collectsMetrics() { point .hasSum(150 /* millis */) .hasAttributesSatisfying( - equalTo(SemanticAttributes.HTTP_SCHEME, "https"), - equalTo(SemanticAttributes.HTTP_HOST, "host"), equalTo(SemanticAttributes.HTTP_METHOD, "GET"), equalTo(SemanticAttributes.HTTP_STATUS_CODE, 200), - equalTo(SemanticAttributes.HTTP_FLAVOR, "2.0")) + equalTo(SemanticAttributes.HTTP_FLAVOR, "2.0"), + equalTo(SemanticAttributes.HTTP_SCHEME, "https"), + equalTo(SemanticAttributes.NET_HOST_NAME, "localhost"), + equalTo(SemanticAttributes.NET_HOST_PORT, 1234)) .hasExemplarsSatisfying( exemplar -> exemplar .hasTraceId(spanContext1.getTraceId()) - .hasSpanId(spanContext1.getSpanId()))))); + .hasSpanId(spanContext1.getSpanId())))), + metric -> + assertThat(metric) + .hasName("http.server.request.size") + .hasUnit("By") + .hasHistogramSatisfying( + histogram -> + histogram.hasPointsSatisfying( + point -> + point + .hasSum(100 /* bytes */) + .hasAttributesSatisfying( + equalTo(SemanticAttributes.HTTP_METHOD, "GET"), + equalTo(SemanticAttributes.HTTP_STATUS_CODE, 200), + equalTo(SemanticAttributes.HTTP_FLAVOR, "2.0"), + equalTo(SemanticAttributes.HTTP_SCHEME, "https"), + equalTo(SemanticAttributes.NET_HOST_NAME, "localhost"), + equalTo(SemanticAttributes.NET_HOST_PORT, 1234)))), + metric -> + assertThat(metric) + .hasName("http.server.response.size") + .hasUnit("By") + .hasHistogramSatisfying( + histogram -> + histogram.hasPointsSatisfying( + point -> + point + .hasSum(200 /* bytes */) + .hasAttributesSatisfying( + equalTo(SemanticAttributes.HTTP_METHOD, "GET"), + equalTo(SemanticAttributes.HTTP_STATUS_CODE, 200), + equalTo(SemanticAttributes.HTTP_FLAVOR, "2.0"), + equalTo(SemanticAttributes.HTTP_SCHEME, "https"), + equalTo(SemanticAttributes.NET_HOST_NAME, "localhost"), + equalTo(SemanticAttributes.NET_HOST_PORT, 1234))))); listener.onEnd(context2, responseAttributes, nanos(300)); @@ -178,7 +224,19 @@ void collectsMetrics() { exemplar -> exemplar .hasTraceId(spanContext2.getTraceId()) - .hasSpanId(spanContext2.getSpanId()))))); + .hasSpanId(spanContext2.getSpanId())))), + metric -> + assertThat(metric) + .hasName("http.server.request.size") + .hasHistogramSatisfying( + histogram -> + histogram.hasPointsSatisfying(point -> point.hasSum(200 /* bytes */))), + metric -> + assertThat(metric) + .hasName("http.server.response.size") + .hasHistogramSatisfying( + histogram -> + histogram.hasPointsSatisfying(point -> point.hasSum(400 /* bytes */)))); } @Test @@ -191,7 +249,7 @@ void collectsHttpRouteFromEndAttributes() { OperationListener listener = HttpServerMetrics.get().create(meterProvider.get("test")); Attributes requestAttributes = - Attributes.builder().put("http.host", "host").put("http.scheme", "https").build(); + Attributes.builder().put("net.host.name", "host").put("http.scheme", "https").build(); Attributes responseAttributes = Attributes.builder().put("http.route", "/test/{id}").build(); @@ -216,7 +274,7 @@ void collectsHttpRouteFromEndAttributes() { .hasSum(100 /* millis */) .hasAttributesSatisfying( equalTo(SemanticAttributes.HTTP_SCHEME, "https"), - equalTo(SemanticAttributes.HTTP_HOST, "host"), + equalTo(SemanticAttributes.NET_HOST_NAME, "host"), equalTo( SemanticAttributes.HTTP_ROUTE, "/test/{id}"))))); } diff --git a/instrumentation-api-semconv/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/http/HttpServerStatusConverterTest.groovy b/instrumentation-api-semconv/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/http/HttpServerStatusConverterTest.groovy deleted file mode 100644 index 96714ed18e52..000000000000 --- a/instrumentation-api-semconv/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/http/HttpServerStatusConverterTest.groovy +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.instrumentation.api.instrumenter.http - -import io.opentelemetry.api.trace.StatusCode -import spock.lang.Specification - -class HttpServerStatusConverterTest extends Specification { - - def "test HTTP #httpStatus to OTel #expectedStatus"() { - when: - def status = HttpStatusConverter.SERVER.statusFromHttpStatus(httpStatus) - - then: - status == expectedStatus - - // https://en.wikipedia.org/wiki/List_of_HTTP_status_codes - where: - httpStatus | expectedStatus - 100 | StatusCode.UNSET - 101 | StatusCode.UNSET - 102 | StatusCode.UNSET - 103 | StatusCode.UNSET - - 200 | StatusCode.UNSET - 201 | StatusCode.UNSET - 202 | StatusCode.UNSET - 203 | StatusCode.UNSET - 204 | StatusCode.UNSET - 205 | StatusCode.UNSET - 206 | StatusCode.UNSET - 207 | StatusCode.UNSET - 208 | StatusCode.UNSET - 226 | StatusCode.UNSET - - 300 | StatusCode.UNSET - 301 | StatusCode.UNSET - 302 | StatusCode.UNSET - 303 | StatusCode.UNSET - 304 | StatusCode.UNSET - 305 | StatusCode.UNSET - 306 | StatusCode.UNSET - 307 | StatusCode.UNSET - 308 | StatusCode.UNSET - - 400 | StatusCode.UNSET - 401 | StatusCode.UNSET - 403 | StatusCode.UNSET - 404 | StatusCode.UNSET - 405 | StatusCode.UNSET - 406 | StatusCode.UNSET - 407 | StatusCode.UNSET - 408 | StatusCode.UNSET - 409 | StatusCode.UNSET - 410 | StatusCode.UNSET - 411 | StatusCode.UNSET - 412 | StatusCode.UNSET - 413 | StatusCode.UNSET - 414 | StatusCode.UNSET - 415 | StatusCode.UNSET - 416 | StatusCode.UNSET - 417 | StatusCode.UNSET - 418 | StatusCode.UNSET - 421 | StatusCode.UNSET - 422 | StatusCode.UNSET - 423 | StatusCode.UNSET - 424 | StatusCode.UNSET - 425 | StatusCode.UNSET - 426 | StatusCode.UNSET - 428 | StatusCode.UNSET - 429 | StatusCode.UNSET - 431 | StatusCode.UNSET - 451 | StatusCode.UNSET - - 500 | StatusCode.ERROR - 501 | StatusCode.ERROR - 502 | StatusCode.ERROR - 503 | StatusCode.ERROR - 504 | StatusCode.ERROR - 505 | StatusCode.ERROR - 506 | StatusCode.ERROR - 507 | StatusCode.ERROR - 508 | StatusCode.ERROR - 510 | StatusCode.ERROR - 511 | StatusCode.ERROR - - // Don't exist - 99 | StatusCode.ERROR - 600 | StatusCode.ERROR - } -} diff --git a/instrumentation-api-semconv/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/http/HttpServerStatusConverterTest.java b/instrumentation-api-semconv/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/http/HttpServerStatusConverterTest.java new file mode 100644 index 000000000000..532c5338095e --- /dev/null +++ b/instrumentation-api-semconv/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/http/HttpServerStatusConverterTest.java @@ -0,0 +1,94 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.api.instrumenter.http; + +import static io.opentelemetry.instrumentation.api.instrumenter.http.HttpStatusConverter.SERVER; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import io.opentelemetry.api.trace.StatusCode; +import java.util.stream.Stream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +public class HttpServerStatusConverterTest { + + @ParameterizedTest + @MethodSource("generateParams") + void httpStatusCodeToOtelStatus(int numeric, StatusCode code) { + assertEquals(code, SERVER.statusFromHttpStatus(numeric)); + } + + static Stream generateParams() { + return Stream.of( + Arguments.of(100, StatusCode.UNSET), + Arguments.of(101, StatusCode.UNSET), + Arguments.of(102, StatusCode.UNSET), + Arguments.of(103, StatusCode.UNSET), + Arguments.of(200, StatusCode.UNSET), + Arguments.of(201, StatusCode.UNSET), + Arguments.of(202, StatusCode.UNSET), + Arguments.of(203, StatusCode.UNSET), + Arguments.of(204, StatusCode.UNSET), + Arguments.of(205, StatusCode.UNSET), + Arguments.of(206, StatusCode.UNSET), + Arguments.of(207, StatusCode.UNSET), + Arguments.of(208, StatusCode.UNSET), + Arguments.of(226, StatusCode.UNSET), + Arguments.of(300, StatusCode.UNSET), + Arguments.of(301, StatusCode.UNSET), + Arguments.of(302, StatusCode.UNSET), + Arguments.of(303, StatusCode.UNSET), + Arguments.of(304, StatusCode.UNSET), + Arguments.of(305, StatusCode.UNSET), + Arguments.of(306, StatusCode.UNSET), + Arguments.of(307, StatusCode.UNSET), + Arguments.of(308, StatusCode.UNSET), + Arguments.of(400, StatusCode.UNSET), + Arguments.of(401, StatusCode.UNSET), + Arguments.of(403, StatusCode.UNSET), + Arguments.of(404, StatusCode.UNSET), + Arguments.of(405, StatusCode.UNSET), + Arguments.of(406, StatusCode.UNSET), + Arguments.of(407, StatusCode.UNSET), + Arguments.of(408, StatusCode.UNSET), + Arguments.of(409, StatusCode.UNSET), + Arguments.of(410, StatusCode.UNSET), + Arguments.of(411, StatusCode.UNSET), + Arguments.of(412, StatusCode.UNSET), + Arguments.of(413, StatusCode.UNSET), + Arguments.of(414, StatusCode.UNSET), + Arguments.of(415, StatusCode.UNSET), + Arguments.of(416, StatusCode.UNSET), + Arguments.of(417, StatusCode.UNSET), + Arguments.of(418, StatusCode.UNSET), + Arguments.of(421, StatusCode.UNSET), + Arguments.of(422, StatusCode.UNSET), + Arguments.of(423, StatusCode.UNSET), + Arguments.of(424, StatusCode.UNSET), + Arguments.of(425, StatusCode.UNSET), + Arguments.of(426, StatusCode.UNSET), + Arguments.of(428, StatusCode.UNSET), + Arguments.of(429, StatusCode.UNSET), + Arguments.of(431, StatusCode.UNSET), + Arguments.of(451, StatusCode.UNSET), + Arguments.of(500, StatusCode.ERROR), + Arguments.of(501, StatusCode.ERROR), + Arguments.of(502, StatusCode.ERROR), + Arguments.of(503, StatusCode.ERROR), + Arguments.of(504, StatusCode.ERROR), + Arguments.of(505, StatusCode.ERROR), + Arguments.of(506, StatusCode.ERROR), + Arguments.of(507, StatusCode.ERROR), + Arguments.of(508, StatusCode.ERROR), + Arguments.of(510, StatusCode.ERROR), + Arguments.of(511, StatusCode.ERROR), + + // Don't exist + Arguments.of(99, StatusCode.ERROR), + Arguments.of(600, StatusCode.ERROR)); + } +} diff --git a/instrumentation-api-semconv/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/http/HttpSpanStatusExtractorTest.java b/instrumentation-api-semconv/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/http/HttpSpanStatusExtractorTest.java index 484a456eb68a..5ee05e8ab9ea 100644 --- a/instrumentation-api-semconv/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/http/HttpSpanStatusExtractorTest.java +++ b/instrumentation-api-semconv/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/http/HttpSpanStatusExtractorTest.java @@ -6,6 +6,8 @@ package io.opentelemetry.instrumentation.api.instrumenter.http; import static org.mockito.ArgumentMatchers.anyMap; +import static org.mockito.ArgumentMatchers.isNull; +import static org.mockito.ArgumentMatchers.same; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoInteractions; import static org.mockito.Mockito.when; @@ -33,7 +35,7 @@ class HttpSpanStatusExtractorTest { @ValueSource(ints = {1, 100, 101, 200, 201, 300, 301, 500, 501, 600, 601}) void hasServerStatus(int statusCode) { StatusCode expectedStatusCode = HttpStatusConverter.SERVER.statusFromHttpStatus(statusCode); - when(serverGetter.statusCode(anyMap(), anyMap())).thenReturn(statusCode); + when(serverGetter.statusCode(anyMap(), anyMap(), isNull())).thenReturn(statusCode); HttpSpanStatusExtractor.create(serverGetter) .extract(spanStatusBuilder, Collections.emptyMap(), Collections.emptyMap(), null); @@ -49,7 +51,7 @@ void hasServerStatus(int statusCode) { @ValueSource(ints = {1, 100, 101, 200, 201, 300, 301, 400, 401, 500, 501, 600, 601}) void hasClientStatus(int statusCode) { StatusCode expectedStatusCode = HttpStatusConverter.CLIENT.statusFromHttpStatus(statusCode); - when(clientGetter.statusCode(anyMap(), anyMap())).thenReturn(statusCode); + when(clientGetter.statusCode(anyMap(), anyMap(), isNull())).thenReturn(statusCode); HttpSpanStatusExtractor.create(clientGetter) .extract(spanStatusBuilder, Collections.emptyMap(), Collections.emptyMap(), null); @@ -64,48 +66,32 @@ void hasClientStatus(int statusCode) { @ParameterizedTest @ValueSource(ints = {1, 100, 101, 200, 201, 300, 301, 400, 401, 500, 501, 600, 601}) void hasServerStatusAndException(int statusCode) { - StatusCode expectedStatusCode = HttpStatusConverter.SERVER.statusFromHttpStatus(statusCode); - when(serverGetter.statusCode(anyMap(), anyMap())).thenReturn(statusCode); + Throwable error = new IllegalStateException("test"); + when(serverGetter.statusCode(anyMap(), anyMap(), same(error))).thenReturn(statusCode); - // Presence of exception has no effect. + // Presence of exception overshadows the HTTP status HttpSpanStatusExtractor.create(serverGetter) - .extract( - spanStatusBuilder, - Collections.emptyMap(), - Collections.emptyMap(), - new IllegalStateException("test")); + .extract(spanStatusBuilder, Collections.emptyMap(), Collections.emptyMap(), error); - if (expectedStatusCode == StatusCode.ERROR) { - verify(spanStatusBuilder).setStatus(expectedStatusCode); - } else { - verify(spanStatusBuilder).setStatus(StatusCode.ERROR); - } + verify(spanStatusBuilder).setStatus(StatusCode.ERROR); } @ParameterizedTest @ValueSource(ints = {1, 100, 101, 200, 201, 300, 301, 400, 401, 500, 501, 600, 601}) void hasClientStatusAndException(int statusCode) { - StatusCode expectedStatusCode = HttpStatusConverter.CLIENT.statusFromHttpStatus(statusCode); - when(clientGetter.statusCode(anyMap(), anyMap())).thenReturn(statusCode); + Throwable error = new IllegalStateException("test"); + when(clientGetter.statusCode(anyMap(), anyMap(), same(error))).thenReturn(statusCode); - // Presence of exception has no effect. + // Presence of exception overshadows the HTTP status HttpSpanStatusExtractor.create(clientGetter) - .extract( - spanStatusBuilder, - Collections.emptyMap(), - Collections.emptyMap(), - new IllegalStateException("test")); + .extract(spanStatusBuilder, Collections.emptyMap(), Collections.emptyMap(), error); - if (expectedStatusCode == StatusCode.ERROR) { - verify(spanStatusBuilder).setStatus(expectedStatusCode); - } else { - verify(spanStatusBuilder).setStatus(StatusCode.ERROR); - } + verify(spanStatusBuilder).setStatus(StatusCode.ERROR); } @Test void hasNoServerStatus_fallsBackToDefault_unset() { - when(serverGetter.statusCode(anyMap(), anyMap())).thenReturn(null); + when(serverGetter.statusCode(anyMap(), anyMap(), isNull())).thenReturn(null); HttpSpanStatusExtractor.create(serverGetter) .extract(spanStatusBuilder, Collections.emptyMap(), Collections.emptyMap(), null); @@ -115,7 +101,7 @@ void hasNoServerStatus_fallsBackToDefault_unset() { @Test void hasNoClientStatus_fallsBackToDefault_unset() { - when(clientGetter.statusCode(anyMap(), anyMap())).thenReturn(null); + when(clientGetter.statusCode(anyMap(), anyMap(), isNull())).thenReturn(null); HttpSpanStatusExtractor.create(clientGetter) .extract(spanStatusBuilder, Collections.emptyMap(), Collections.emptyMap(), null); @@ -125,27 +111,23 @@ void hasNoClientStatus_fallsBackToDefault_unset() { @Test void hasNoServerStatus_fallsBackToDefault_error() { - when(serverGetter.statusCode(anyMap(), anyMap())).thenReturn(null); + Throwable error = new IllegalStateException("test"); + when(serverGetter.statusCode(anyMap(), anyMap(), same(error))).thenReturn(null); HttpSpanStatusExtractor.create(serverGetter) - .extract( - spanStatusBuilder, - Collections.emptyMap(), - Collections.emptyMap(), - new IllegalStateException("test")); + .extract(spanStatusBuilder, Collections.emptyMap(), Collections.emptyMap(), error); + verify(spanStatusBuilder).setStatus(StatusCode.ERROR); } @Test void hasNoClientStatus_fallsBackToDefault_error() { - when(clientGetter.statusCode(anyMap(), anyMap())).thenReturn(null); + IllegalStateException error = new IllegalStateException("test"); + when(clientGetter.statusCode(anyMap(), anyMap(), same(error))).thenReturn(null); HttpSpanStatusExtractor.create(clientGetter) - .extract( - spanStatusBuilder, - Collections.emptyMap(), - Collections.emptyMap(), - new IllegalStateException("test")); + .extract(spanStatusBuilder, Collections.emptyMap(), Collections.emptyMap(), error); + verify(spanStatusBuilder).setStatus(StatusCode.ERROR); } } diff --git a/instrumentation-api-semconv/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/http/TemporaryMetricsViewTest.java b/instrumentation-api-semconv/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/http/TemporaryMetricsViewTest.java index 708e76ffbd31..aefb28301e8e 100644 --- a/instrumentation-api-semconv/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/http/TemporaryMetricsViewTest.java +++ b/instrumentation-api-semconv/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/http/TemporaryMetricsViewTest.java @@ -5,20 +5,23 @@ package io.opentelemetry.instrumentation.api.instrumenter.http; +import static io.opentelemetry.api.common.AttributeKey.stringKey; import static io.opentelemetry.instrumentation.api.instrumenter.http.TemporaryMetricsView.applyActiveRequestsView; -import static io.opentelemetry.instrumentation.api.instrumenter.http.TemporaryMetricsView.applyClientDurationView; -import static io.opentelemetry.instrumentation.api.instrumenter.http.TemporaryMetricsView.applyServerDurationView; -import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.attributeEntry; +import static io.opentelemetry.instrumentation.api.instrumenter.http.TemporaryMetricsView.applyClientDurationAndSizeView; +import static io.opentelemetry.instrumentation.api.instrumenter.http.TemporaryMetricsView.applyServerDurationAndSizeView; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; +import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.HttpFlavorValues.HTTP_1_1; +import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.NetTransportValues.IP_TCP; +import static org.assertj.core.api.Assertions.entry; import io.opentelemetry.api.common.Attributes; -import io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions; import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; import org.junit.jupiter.api.Test; class TemporaryMetricsViewTest { @Test - void shouldApplyClientDurationView() { + void shouldApplyClientDurationAndSizeView() { Attributes startAttributes = Attributes.builder() .put( @@ -26,60 +29,70 @@ void shouldApplyClientDurationView() { "https://somehost/high/cardinality/12345?jsessionId=121454") .put(SemanticAttributes.HTTP_METHOD, "GET") .put(SemanticAttributes.HTTP_SCHEME, "https") - .put(SemanticAttributes.HTTP_HOST, "somehost") .put(SemanticAttributes.HTTP_TARGET, "/high/cardinality/12345?jsessionId=121454") .build(); Attributes endAttributes = Attributes.builder() .put(SemanticAttributes.HTTP_STATUS_CODE, 500) + .put(SemanticAttributes.HTTP_FLAVOR, HTTP_1_1) + .put(SemanticAttributes.NET_TRANSPORT, IP_TCP) .put(SemanticAttributes.NET_PEER_NAME, "somehost2") - .put(SemanticAttributes.NET_PEER_IP, "127.0.0.1") .put(SemanticAttributes.NET_PEER_PORT, 443) + .put("net.sock.family", "inet") + .put("net.peer.sock.addr", "1.2.3.4") + .put("net.peer.sock.name", "somehost20") + .put("net.peer.sock.port", 8080) .build(); - OpenTelemetryAssertions.assertThat(applyClientDurationView(startAttributes, endAttributes)) + assertThat(applyClientDurationAndSizeView(startAttributes, endAttributes)) .containsOnly( - attributeEntry(SemanticAttributes.NET_PEER_NAME.getKey(), "somehost2"), - attributeEntry(SemanticAttributes.NET_PEER_PORT.getKey(), 443), - attributeEntry(SemanticAttributes.HTTP_METHOD.getKey(), "GET"), - attributeEntry(SemanticAttributes.HTTP_STATUS_CODE.getKey(), 500)); + entry(SemanticAttributes.HTTP_METHOD, "GET"), + entry(SemanticAttributes.HTTP_STATUS_CODE, 500L), + entry(SemanticAttributes.HTTP_FLAVOR, HTTP_1_1), + entry(SemanticAttributes.NET_PEER_NAME, "somehost2"), + entry(SemanticAttributes.NET_PEER_PORT, 443L), + entry(stringKey("net.peer.sock.addr"), "1.2.3.4")); } @Test - void shouldApplyServerDurationView() { + void shouldApplyServerDurationAndSizeView() { Attributes startAttributes = Attributes.builder() .put(SemanticAttributes.HTTP_METHOD, "GET") .put( SemanticAttributes.HTTP_URL, "https://somehost/high/cardinality/12345?jsessionId=121454") + .put(SemanticAttributes.HTTP_FLAVOR, HTTP_1_1) + .put(SemanticAttributes.HTTP_TARGET, "/high/cardinality/12345?jsessionId=121454") .put(SemanticAttributes.HTTP_SCHEME, "https") - .put(SemanticAttributes.HTTP_HOST, "somehost") - .put(SemanticAttributes.HTTP_SERVER_NAME, "somehost") - .put( - SemanticAttributes.HTTP_TARGET, - "/somehost/high/cardinality/12345?jsessionId=121454") - .put(SemanticAttributes.HTTP_ROUTE, "/somehost/high/{name}/{id}") + .put(SemanticAttributes.NET_TRANSPORT, IP_TCP) .put(SemanticAttributes.NET_HOST_NAME, "somehost") .put(SemanticAttributes.NET_HOST_PORT, 443) + .put("net.sock.family", "inet") + .put("net.peer.sock.addr", "1.2.3.4") + .put("net.peer.sock.port", 8080) + .put("net.host.sock.addr", "4.3.2.1") + .put("net.host.sock.port", 9090) .build(); Attributes endAttributes = Attributes.builder() + .put(SemanticAttributes.HTTP_ROUTE, "/somehost/high/{name}/{id}") .put(SemanticAttributes.HTTP_STATUS_CODE, 500) .put(SemanticAttributes.NET_PEER_NAME, "somehost2") - .put(SemanticAttributes.NET_PEER_IP, "127.0.0.1") .put(SemanticAttributes.NET_PEER_PORT, 443) .build(); - OpenTelemetryAssertions.assertThat(applyServerDurationView(startAttributes, endAttributes)) + assertThat(applyServerDurationAndSizeView(startAttributes, endAttributes)) .containsOnly( - attributeEntry(SemanticAttributes.HTTP_SCHEME.getKey(), "https"), - attributeEntry(SemanticAttributes.HTTP_HOST.getKey(), "somehost"), - attributeEntry(SemanticAttributes.HTTP_ROUTE.getKey(), "/somehost/high/{name}/{id}"), - attributeEntry(SemanticAttributes.HTTP_METHOD.getKey(), "GET"), - attributeEntry(SemanticAttributes.HTTP_STATUS_CODE.getKey(), 500)); + entry(SemanticAttributes.HTTP_METHOD, "GET"), + entry(SemanticAttributes.HTTP_STATUS_CODE, 500L), + entry(SemanticAttributes.HTTP_FLAVOR, HTTP_1_1), + entry(SemanticAttributes.HTTP_SCHEME, "https"), + entry(SemanticAttributes.NET_HOST_NAME, "somehost"), + entry(SemanticAttributes.NET_HOST_PORT, 443L), + entry(SemanticAttributes.HTTP_ROUTE, "/somehost/high/{name}/{id}")); } @Test @@ -87,11 +100,27 @@ void shouldApplyActiveRequestsView() { Attributes attributes = Attributes.builder() .put(SemanticAttributes.HTTP_METHOD, "GET") - .put(SemanticAttributes.HTTP_URL, "/high/cardinality/12345") - .put(SemanticAttributes.NET_PEER_NAME, "somehost") + .put( + SemanticAttributes.HTTP_URL, + "https://somehost/high/cardinality/12345?jsessionId=121454") + .put(SemanticAttributes.HTTP_FLAVOR, HTTP_1_1) + .put(SemanticAttributes.HTTP_TARGET, "/high/cardinality/12345?jsessionId=121454") + .put(SemanticAttributes.HTTP_SCHEME, "https") + .put(SemanticAttributes.NET_TRANSPORT, IP_TCP) + .put(SemanticAttributes.NET_HOST_NAME, "somehost") + .put(SemanticAttributes.NET_HOST_PORT, 443) + .put("net.sock.family", "inet") + .put("net.peer.sock.addr", "1.2.3.4") + .put("net.peer.sock.port", 8080) + .put("net.host.sock.addr", "4.3.2.1") + .put("net.host.sock.port", 9090) .build(); - OpenTelemetryAssertions.assertThat(applyActiveRequestsView(attributes)) - .containsOnly(attributeEntry("http.method", "GET")); + assertThat(applyActiveRequestsView(attributes)) + .containsOnly( + entry(SemanticAttributes.HTTP_METHOD, "GET"), + entry(SemanticAttributes.HTTP_SCHEME, "https"), + entry(SemanticAttributes.HTTP_FLAVOR, HTTP_1_1), + entry(SemanticAttributes.NET_HOST_NAME, "somehost")); } } diff --git a/instrumentation-api-semconv/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/messaging/MessagingSpanNameExtractorTest.java b/instrumentation-api-semconv/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/messaging/MessagingSpanNameExtractorTest.java index 80fba00792a0..d1164d011e81 100644 --- a/instrumentation-api-semconv/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/messaging/MessagingSpanNameExtractorTest.java +++ b/instrumentation-api-semconv/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/messaging/MessagingSpanNameExtractorTest.java @@ -6,7 +6,7 @@ package io.opentelemetry.instrumentation.api.instrumenter.messaging; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.when; import io.opentelemetry.instrumentation.api.instrumenter.SpanNameExtractor; import java.util.stream.Stream; @@ -33,9 +33,9 @@ void shouldExtractSpanName( Message message = new Message(); if (isTemporaryQueue) { - given(getter.temporaryDestination(message)).willReturn(true); + when(getter.temporaryDestination(message)).thenReturn(true); } else { - given(getter.destination(message)).willReturn(destinationName); + when(getter.destination(message)).thenReturn(destinationName); } SpanNameExtractor underTest = MessagingSpanNameExtractor.create(getter, operation); diff --git a/instrumentation-api-semconv/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/net/InetSocketAddressNetClientAttributesGetterTest.java b/instrumentation-api-semconv/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/net/InetSocketAddressNetClientAttributesGetterTest.java index ad95b8da761f..bdfcd7ea12ed 100644 --- a/instrumentation-api-semconv/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/net/InetSocketAddressNetClientAttributesGetterTest.java +++ b/instrumentation-api-semconv/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/net/InetSocketAddressNetClientAttributesGetterTest.java @@ -12,6 +12,7 @@ import io.opentelemetry.api.common.AttributesBuilder; import io.opentelemetry.context.Context; import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; +import java.net.Inet4Address; import java.net.InetSocketAddress; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -24,14 +25,26 @@ class InetSocketAddressNetClientAttributesGetterTest { getter = new InetSocketAddressNetClientAttributesGetter() { @Override - public InetSocketAddress getAddress( - InetSocketAddress request, InetSocketAddress response) { - return response; + public String transport(InetSocketAddress request, InetSocketAddress response) { + return SemanticAttributes.NetTransportValues.IP_TCP; } @Override - public String transport(InetSocketAddress request, InetSocketAddress response) { - return SemanticAttributes.NetTransportValues.IP_TCP; + public String peerName(InetSocketAddress request) { + // net.peer.name and net.peer.port are tested in NetClientAttributesExtractorTest + return null; + } + + @Override + public Integer peerPort(InetSocketAddress request) { + // net.peer.name and net.peer.port are tested in NetClientAttributesExtractorTest + return null; + } + + @Override + protected InetSocketAddress getPeerSocketAddress( + InetSocketAddress request, InetSocketAddress response) { + return response; } }; private final NetClientAttributesExtractor extractor = @@ -50,57 +63,55 @@ void noInetSocketAddress() { @Test void fullAddress() { // given - InetSocketAddress request = new InetSocketAddress("github.com", 123); - assertThat(request.getAddress().getHostAddress()).isNotNull(); + InetSocketAddress address = new InetSocketAddress("api.github.com", 456); + assertThat(address.getAddress().getHostAddress()).isNotNull(); - InetSocketAddress response = new InetSocketAddress("api.github.com", 456); - assertThat(request.getAddress().getHostAddress()).isNotNull(); + boolean ipv4 = address.getAddress() instanceof Inet4Address; Context context = Context.root(); // when AttributesBuilder startAttributes = Attributes.builder(); - extractor.onStart(startAttributes, context, request); + extractor.onStart(startAttributes, context, address); AttributesBuilder endAttributes = Attributes.builder(); - extractor.onEnd(endAttributes, context, request, response, null); + extractor.onEnd(endAttributes, context, address, address, null); // then assertThat(startAttributes.build()).isEmpty(); - assertThat(endAttributes.build()) - .containsOnly( - entry(SemanticAttributes.NET_TRANSPORT, SemanticAttributes.NetTransportValues.IP_TCP), - entry(SemanticAttributes.NET_PEER_IP, response.getAddress().getHostAddress()), - entry(SemanticAttributes.NET_PEER_NAME, "api.github.com"), - entry(SemanticAttributes.NET_PEER_PORT, 456L)); + AttributesBuilder builder = Attributes.builder(); + builder.put(SemanticAttributes.NET_TRANSPORT, SemanticAttributes.NetTransportValues.IP_TCP); + builder.put(SemanticAttributes.NET_SOCK_PEER_ADDR, address.getAddress().getHostAddress()); + if (!ipv4) { + builder.put(SemanticAttributes.NET_SOCK_FAMILY, "inet6"); + } + builder.put(SemanticAttributes.NET_SOCK_PEER_NAME, "api.github.com"); + builder.put(SemanticAttributes.NET_SOCK_PEER_PORT, 456L); + + assertThat(endAttributes.build()).isEqualTo(builder.build()); } @Test void unresolved() { // given - InetSocketAddress request = InetSocketAddress.createUnresolved("github.com", 123); - assertThat(request.getAddress()).isNull(); - - InetSocketAddress response = InetSocketAddress.createUnresolved("api.github.com", 456); - assertThat(request.getAddress()).isNull(); + InetSocketAddress address = InetSocketAddress.createUnresolved("api.github.com", 456); + assertThat(address.getAddress()).isNull(); Context context = Context.root(); // when AttributesBuilder startAttributes = Attributes.builder(); - extractor.onStart(startAttributes, context, request); + extractor.onStart(startAttributes, context, address); AttributesBuilder endAttributes = Attributes.builder(); - extractor.onEnd(endAttributes, context, request, response, null); + extractor.onEnd(endAttributes, context, address, address, null); // then assertThat(startAttributes.build()).isEmpty(); assertThat(endAttributes.build()) .containsOnly( - entry(SemanticAttributes.NET_TRANSPORT, SemanticAttributes.NetTransportValues.IP_TCP), - entry(SemanticAttributes.NET_PEER_NAME, "api.github.com"), - entry(SemanticAttributes.NET_PEER_PORT, 456L)); + entry(SemanticAttributes.NET_TRANSPORT, SemanticAttributes.NetTransportValues.IP_TCP)); } } diff --git a/instrumentation-api-semconv/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/net/InetSocketAddressNetServerAttributesGetterTest.java b/instrumentation-api-semconv/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/net/InetSocketAddressNetServerAttributesGetterTest.java index b292012b6b3f..881bca80f664 100644 --- a/instrumentation-api-semconv/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/net/InetSocketAddressNetServerAttributesGetterTest.java +++ b/instrumentation-api-semconv/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/net/InetSocketAddressNetServerAttributesGetterTest.java @@ -12,6 +12,7 @@ import io.opentelemetry.api.common.AttributesBuilder; import io.opentelemetry.context.Context; import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; +import java.net.Inet4Address; import java.net.InetSocketAddress; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -20,24 +21,43 @@ @ExtendWith(MockitoExtension.class) class InetSocketAddressNetServerAttributesGetterTest { - private final NetServerAttributesExtractor extractor = - NetServerAttributesExtractor.create( - new InetSocketAddressNetServerAttributesGetter() { - @Override - public InetSocketAddress getAddress(InetSocketAddress request) { - return request; - } - - @Override - public String transport(InetSocketAddress request) { - return SemanticAttributes.NetTransportValues.IP_TCP; - } - }); + final InetSocketAddressNetServerAttributesGetter getter = + new InetSocketAddressNetServerAttributesGetter() { + + @Override + public String transport(Addresses request) { + return SemanticAttributes.NetTransportValues.IP_TCP; + } + + @Override + public String hostName(Addresses request) { + // net.host.name and net.host.port are tested in NetClientAttributesExtractorTest + return null; + } + + @Override + public Integer hostPort(Addresses request) { + // net.host.name and net.host.port are tested in NetClientAttributesExtractorTest + return null; + } + + @Override + protected InetSocketAddress getPeerSocketAddress(Addresses request) { + return request.peer; + } + + @Override + protected InetSocketAddress getHostSocketAddress(Addresses request) { + return request.host; + } + }; + private final NetServerAttributesExtractor extractor = + NetServerAttributesExtractor.create(getter); @Test void noInetSocketAddress() { AttributesBuilder attributes = Attributes.builder(); - extractor.onStart(attributes, Context.root(), null); + extractor.onStart(attributes, Context.root(), new Addresses(null, null)); assertThat(attributes.build()) .containsOnly( entry(SemanticAttributes.NET_TRANSPORT, SemanticAttributes.NetTransportValues.IP_TCP)); @@ -46,11 +66,11 @@ void noInetSocketAddress() { @Test void fullAddress() { // given - InetSocketAddress request = new InetSocketAddress("github.com", 123); - assertThat(request.getAddress().getHostAddress()).isNotNull(); - - InetSocketAddress response = new InetSocketAddress("api.github.com", 456); - assertThat(request.getAddress().getHostAddress()).isNotNull(); + Addresses request = + new Addresses( + new InetSocketAddress("github.com", 123), new InetSocketAddress("api.github.com", 456)); + assertThat(request.peer.getAddress().getHostAddress()).isNotNull(); + assertThat(request.host.getAddress().getHostAddress()).isNotNull(); Context context = Context.root(); @@ -59,14 +79,20 @@ void fullAddress() { extractor.onStart(startAttributes, context, request); AttributesBuilder endAttributes = Attributes.builder(); - extractor.onEnd(endAttributes, context, request, response, null); + extractor.onEnd(endAttributes, context, request, request, null); // then - assertThat(startAttributes.build()) - .containsOnly( - entry(SemanticAttributes.NET_TRANSPORT, SemanticAttributes.NetTransportValues.IP_TCP), - entry(SemanticAttributes.NET_PEER_IP, request.getAddress().getHostAddress()), - entry(SemanticAttributes.NET_PEER_PORT, 123L)); + AttributesBuilder builder = Attributes.builder(); + builder.put(SemanticAttributes.NET_TRANSPORT, SemanticAttributes.NetTransportValues.IP_TCP); + if (!request.isIpv4()) { + builder.put(SemanticAttributes.NET_SOCK_FAMILY, "inet6"); + } + builder.put(SemanticAttributes.NET_SOCK_PEER_ADDR, request.peer.getAddress().getHostAddress()); + builder.put(SemanticAttributes.NET_SOCK_PEER_PORT, 123L); + builder.put(SemanticAttributes.NET_SOCK_HOST_ADDR, request.host.getAddress().getHostAddress()); + builder.put(SemanticAttributes.NET_SOCK_HOST_PORT, 456L); + + assertThat(startAttributes.build()).isEqualTo(builder.build()); assertThat(endAttributes.build()).isEmpty(); } @@ -74,11 +100,12 @@ void fullAddress() { @Test void unresolved() { // given - InetSocketAddress request = InetSocketAddress.createUnresolved("github.com", 123); - assertThat(request.getAddress()).isNull(); - - InetSocketAddress response = InetSocketAddress.createUnresolved("api.github.com", 456); - assertThat(request.getAddress()).isNull(); + Addresses request = + new Addresses( + InetSocketAddress.createUnresolved("github.com", 123), + InetSocketAddress.createUnresolved("api.github.com", 456)); + assertThat(request.peer.getAddress()).isNull(); + assertThat(request.host.getAddress()).isNull(); Context context = Context.root(); @@ -87,14 +114,28 @@ void unresolved() { extractor.onStart(startAttributes, context, request); AttributesBuilder endAttributes = Attributes.builder(); - extractor.onEnd(endAttributes, context, request, response, null); + extractor.onEnd(endAttributes, context, request, request, null); // then assertThat(startAttributes.build()) .containsOnly( - entry(SemanticAttributes.NET_TRANSPORT, SemanticAttributes.NetTransportValues.IP_TCP), - entry(SemanticAttributes.NET_PEER_PORT, 123L)); + entry(SemanticAttributes.NET_TRANSPORT, SemanticAttributes.NetTransportValues.IP_TCP)); assertThat(endAttributes.build()).isEmpty(); } + + static final class Addresses { + + private final InetSocketAddress peer; + private final InetSocketAddress host; + + Addresses(InetSocketAddress peer, InetSocketAddress host) { + this.peer = peer; + this.host = host; + } + + boolean isIpv4() { + return peer.getAddress() instanceof Inet4Address; + } + } } diff --git a/instrumentation-api-semconv/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/net/NetClientAttributesExtractorTest.java b/instrumentation-api-semconv/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/net/NetClientAttributesExtractorTest.java index ecc25f9d15af..63d7c819f0f1 100644 --- a/instrumentation-api-semconv/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/net/NetClientAttributesExtractorTest.java +++ b/instrumentation-api-semconv/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/net/NetClientAttributesExtractorTest.java @@ -6,14 +6,18 @@ package io.opentelemetry.instrumentation.api.instrumenter.net; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; +import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.NetTransportValues.IP_TCP; +import static java.util.Collections.emptyMap; import static org.assertj.core.api.Assertions.entry; import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.common.AttributesBuilder; import io.opentelemetry.context.Context; +import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor; import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; import java.util.HashMap; import java.util.Map; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; class NetClientAttributesExtractorTest { @@ -27,127 +31,209 @@ public String transport(Map request, Map respons } @Override - public String peerName(Map request, Map response) { - if (response != null) { - return response.get("peerName"); - } - return null; + public String peerName(Map request) { + return request.get("peerName"); } @Override - public Integer peerPort(Map request, Map response) { - if (response != null) { - return Integer.valueOf(response.get("peerPort")); - } - return null; + public Integer peerPort(Map request) { + String peerPort = request.get("peerPort"); + return peerPort == null ? null : Integer.valueOf(peerPort); } @Override - public String peerIp(Map request, Map response) { - if (response != null) { - return response.get("peerIp"); - } - return null; + public String sockFamily(Map request, Map response) { + return response.get("sockFamily"); + } + + @Override + public String sockPeerAddr(Map request, Map response) { + return response.get("sockPeerAddr"); + } + + @Override + public String sockPeerName(Map request, Map response) { + return response.get("sockPeerName"); + } + + @Override + public Integer sockPeerPort(Map request, Map response) { + String sockPeerPort = response.get("sockPeerPort"); + return sockPeerPort == null ? null : Integer.valueOf(sockPeerPort); } } + private final AttributesExtractor, Map> extractor = + NetClientAttributesExtractor.create(new TestNetClientAttributesGetter()); + @Test void normal() { // given - Map request = new HashMap<>(); - request.put("transport", "TCP"); - request.put("peerName", "github.com"); - request.put("peerPort", "123"); - request.put("peerIp", "1.2.3.4"); - - Map response = new HashMap<>(); - response.put("peerName", "opentelemetry.io"); - response.put("peerPort", "42"); - response.put("peerIp", "4.3.2.1"); - - TestNetClientAttributesGetter getter = new TestNetClientAttributesGetter(); - NetClientAttributesExtractor, Map> extractor = - NetClientAttributesExtractor.create(getter); + Map map = new HashMap<>(); + map.put("transport", IP_TCP); + map.put("peerName", "opentelemetry.io"); + map.put("peerPort", "42"); + map.put("sockFamily", "inet6"); + map.put("sockPeerAddr", "1:2:3:4::"); + map.put("sockPeerName", "proxy.opentelemetry.io"); + map.put("sockPeerPort", "123"); Context context = Context.root(); // when AttributesBuilder startAttributes = Attributes.builder(); - extractor.onStart(startAttributes, context, request); + extractor.onStart(startAttributes, context, map); AttributesBuilder endAttributes = Attributes.builder(); - extractor.onEnd(endAttributes, context, request, response, null); + extractor.onEnd(endAttributes, context, map, map, null); // then - assertThat(startAttributes.build()).isEmpty(); + assertThat(startAttributes.build()) + .containsOnly( + entry(SemanticAttributes.NET_PEER_NAME, "opentelemetry.io"), + entry(SemanticAttributes.NET_PEER_PORT, 42L)); assertThat(endAttributes.build()) .containsOnly( - entry(SemanticAttributes.NET_PEER_NAME, "opentelemetry.io"), - entry(SemanticAttributes.NET_PEER_PORT, 42L), - entry(SemanticAttributes.NET_PEER_IP, "4.3.2.1")); + entry(SemanticAttributes.NET_TRANSPORT, IP_TCP), + entry(SemanticAttributes.NET_SOCK_FAMILY, "inet6"), + entry(SemanticAttributes.NET_SOCK_PEER_ADDR, "1:2:3:4::"), + entry(SemanticAttributes.NET_SOCK_PEER_NAME, "proxy.opentelemetry.io"), + entry(SemanticAttributes.NET_SOCK_PEER_PORT, 123L)); } @Test - public void doesNotSetDuplicateAttributes() { + void empty() { // given - Map request = new HashMap<>(); - request.put("transport", "TCP"); - request.put("peerName", "1.2.3.4"); - request.put("peerIp", "1.2.3.4"); - request.put("peerPort", "123"); + Context context = Context.root(); + + // when + AttributesBuilder startAttributes = Attributes.builder(); + extractor.onStart(startAttributes, context, emptyMap()); - Map response = new HashMap<>(); - response.put("peerName", "4.3.2.1"); - response.put("peerPort", "42"); - response.put("peerIp", "4.3.2.1"); + AttributesBuilder endAttributes = Attributes.builder(); + extractor.onEnd(endAttributes, context, emptyMap(), emptyMap(), null); + + // then + assertThat(startAttributes.build()).isEmpty(); + assertThat(endAttributes.build()).isEmpty(); + } - TestNetClientAttributesGetter getter = new TestNetClientAttributesGetter(); - NetClientAttributesExtractor, Map> extractor = - NetClientAttributesExtractor.create(getter); + @Test + @DisplayName("does not set any net.sock.* attributes when net.peer.name = net.sock.peer.addr") + void doesNotSetDuplicates1() { + // given + Map map = new HashMap<>(); + map.put("transport", IP_TCP); + map.put("peerName", "1:2:3:4::"); + map.put("peerPort", "42"); + map.put("sockFamily", "inet6"); + map.put("sockPeerAddr", "1:2:3:4::"); + map.put("sockPeerName", "proxy.opentelemetry.io"); + map.put("sockPeerPort", "123"); Context context = Context.root(); // when AttributesBuilder startAttributes = Attributes.builder(); - extractor.onStart(startAttributes, context, request); + extractor.onStart(startAttributes, context, map); AttributesBuilder endAttributes = Attributes.builder(); - extractor.onEnd(endAttributes, context, request, response, null); + extractor.onEnd(endAttributes, context, map, map, null); // then - assertThat(startAttributes.build()).isEmpty(); + assertThat(startAttributes.build()) + .containsOnly( + entry(SemanticAttributes.NET_PEER_NAME, "1:2:3:4::"), + entry(SemanticAttributes.NET_PEER_PORT, 42L)); + + assertThat(endAttributes.build()).containsOnly(entry(SemanticAttributes.NET_TRANSPORT, IP_TCP)); + } + + @Test + @DisplayName( + "does not set net.sock.* attributes when they duplicate related net.peer.* attributes") + void doesNotSetDuplicates2() { + // given + Map map = new HashMap<>(); + map.put("transport", IP_TCP); + map.put("peerName", "opentelemetry.io"); + map.put("peerPort", "42"); + map.put("sockFamily", "inet6"); + map.put("sockPeerAddr", "1:2:3:4::"); + map.put("sockPeerName", "opentelemetry.io"); + map.put("sockPeerPort", "42"); + + Context context = Context.root(); + + // when + AttributesBuilder startAttributes = Attributes.builder(); + extractor.onStart(startAttributes, context, map); + + AttributesBuilder endAttributes = Attributes.builder(); + extractor.onEnd(endAttributes, context, map, map, null); + + // then + assertThat(startAttributes.build()) + .containsOnly( + entry(SemanticAttributes.NET_PEER_NAME, "opentelemetry.io"), + entry(SemanticAttributes.NET_PEER_PORT, 42L)); assertThat(endAttributes.build()) .containsOnly( - entry(SemanticAttributes.NET_PEER_PORT, 42L), - entry(SemanticAttributes.NET_PEER_IP, "4.3.2.1")); + entry(SemanticAttributes.NET_TRANSPORT, IP_TCP), + entry(SemanticAttributes.NET_SOCK_FAMILY, "inet6"), + entry(SemanticAttributes.NET_SOCK_PEER_ADDR, "1:2:3:4::")); } @Test - public void doesNotSetNegativePort() { + void doesNotSetNegativePortValues() { // given - Map request = new HashMap<>(); - request.put("peerPort", "-42"); + Map map = new HashMap<>(); + map.put("peerName", "opentelemetry.io"); + map.put("peerPort", "-12"); + map.put("sockPeerAddr", "1:2:3:4::"); + map.put("sockPeerPort", "-42"); - Map response = new HashMap<>(); - response.put("peerPort", "-1"); + Context context = Context.root(); - TestNetClientAttributesGetter getter = new TestNetClientAttributesGetter(); - NetClientAttributesExtractor, Map> extractor = - NetClientAttributesExtractor.create(getter); + // when + AttributesBuilder startAttributes = Attributes.builder(); + extractor.onStart(startAttributes, context, map); + + AttributesBuilder endAttributes = Attributes.builder(); + extractor.onEnd(endAttributes, context, map, map, null); + + // then + assertThat(startAttributes.build()) + .containsOnly(entry(SemanticAttributes.NET_PEER_NAME, "opentelemetry.io")); + + assertThat(endAttributes.build()) + .containsOnly(entry(SemanticAttributes.NET_SOCK_PEER_ADDR, "1:2:3:4::")); + } + + @Test + void doesNotSetSockFamilyInet() { + // given + Map map = new HashMap<>(); + map.put("peerName", "opentelemetry.io"); + map.put("sockPeerAddr", "1.2.3.4"); + map.put("sockFamily", SemanticAttributes.NetSockFamilyValues.INET); Context context = Context.root(); // when AttributesBuilder startAttributes = Attributes.builder(); - extractor.onStart(startAttributes, context, request); + extractor.onStart(startAttributes, context, map); AttributesBuilder endAttributes = Attributes.builder(); - extractor.onEnd(endAttributes, context, request, response, null); + extractor.onEnd(endAttributes, context, map, map, null); // then - assertThat(startAttributes.build()).isEmpty(); - assertThat(endAttributes.build()).isEmpty(); + assertThat(startAttributes.build()) + .containsOnly(entry(SemanticAttributes.NET_PEER_NAME, "opentelemetry.io")); + + assertThat(endAttributes.build()) + .containsOnly(entry(SemanticAttributes.NET_SOCK_PEER_ADDR, "1.2.3.4")); } } diff --git a/instrumentation-api-semconv/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/net/NetServerAttributesExtractorTest.java b/instrumentation-api-semconv/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/net/NetServerAttributesExtractorTest.java index 79504e93dac8..48c7acd5e5fb 100644 --- a/instrumentation-api-semconv/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/net/NetServerAttributesExtractorTest.java +++ b/instrumentation-api-semconv/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/net/NetServerAttributesExtractorTest.java @@ -6,6 +6,8 @@ package io.opentelemetry.instrumentation.api.instrumenter.net; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; +import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.NetTransportValues.IP_TCP; +import static java.util.Collections.emptyMap; import static org.assertj.core.api.Assertions.entry; import io.opentelemetry.api.common.Attributes; @@ -14,6 +16,8 @@ import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; import java.util.HashMap; import java.util.Map; +import javax.annotation.Nullable; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; class NetServerAttributesExtractorTest { @@ -26,117 +30,226 @@ public String transport(Map request) { return request.get("transport"); } + @Nullable @Override - public Integer peerPort(Map request) { - return Integer.valueOf(request.get("peerPort")); + public String hostName(Map request) { + return request.get("hostName"); } + @Nullable @Override - public String peerIp(Map request) { - return request.get("peerIp"); + public Integer hostPort(Map request) { + String hostPort = request.get("hostPort"); + return hostPort == null ? null : Integer.valueOf(hostPort); + } + + @Nullable + @Override + public String sockFamily(Map request) { + return request.get("sockFamily"); + } + + @Override + public String sockPeerAddr(Map request) { + return request.get("sockPeerAddr"); + } + + @Override + public Integer sockPeerPort(Map request) { + String sockPeerPort = request.get("sockPeerPort"); + return sockPeerPort == null ? null : Integer.valueOf(sockPeerPort); + } + + @Nullable + @Override + public String sockHostAddr(Map request) { + return request.get("sockHostAddr"); + } + + @Nullable + @Override + public Integer sockHostPort(Map request) { + String sockHostPort = request.get("sockHostPort"); + return sockHostPort == null ? null : Integer.valueOf(sockHostPort); } } + NetServerAttributesExtractor, Map> extractor = + NetServerAttributesExtractor.create(new TestNetServerAttributesGetter()); + @Test void normal() { // given - Map request = new HashMap<>(); - request.put("transport", "TCP"); - request.put("peerName", "github.com"); - request.put("peerPort", "123"); - request.put("peerIp", "1.2.3.4"); - - Map response = new HashMap<>(); - response.put("peerName", "opentelemetry.io"); - response.put("peerPort", "42"); - response.put("peerIp", "4.3.2.1"); - - NetServerAttributesExtractor, Map> extractor = - createTestExtractor(); + Map map = new HashMap<>(); + map.put("transport", IP_TCP); + map.put("hostName", "opentelemetry.io"); + map.put("hostPort", "80"); + map.put("sockFamily", "inet6"); + map.put("sockPeerAddr", "1:2:3:4::"); + map.put("sockPeerPort", "42"); + map.put("sockHostAddr", "4:3:2:1::"); + map.put("sockHostPort", "8080"); Context context = Context.root(); // when AttributesBuilder startAttributes = Attributes.builder(); - extractor.onStart(startAttributes, context, request); + extractor.onStart(startAttributes, context, map); AttributesBuilder endAttributes = Attributes.builder(); - extractor.onEnd(endAttributes, context, request, response, null); + extractor.onEnd(endAttributes, context, map, map, null); // then assertThat(startAttributes.build()) .containsOnly( - entry(SemanticAttributes.NET_TRANSPORT, "TCP"), - entry(SemanticAttributes.NET_PEER_PORT, 123L), - entry(SemanticAttributes.NET_PEER_IP, "1.2.3.4")); + entry(SemanticAttributes.NET_TRANSPORT, IP_TCP), + entry(SemanticAttributes.NET_HOST_NAME, "opentelemetry.io"), + entry(SemanticAttributes.NET_HOST_PORT, 80L), + entry(SemanticAttributes.NET_SOCK_FAMILY, "inet6"), + entry(SemanticAttributes.NET_SOCK_PEER_ADDR, "1:2:3:4::"), + entry(SemanticAttributes.NET_SOCK_PEER_PORT, 42L), + entry(SemanticAttributes.NET_SOCK_HOST_ADDR, "4:3:2:1::"), + entry(SemanticAttributes.NET_SOCK_HOST_PORT, 8080L)); assertThat(endAttributes.build()).isEmpty(); } @Test - public void doesNotSetDuplicateAttributes() { + void empty() { // given - Map request = new HashMap<>(); - request.put("transport", "TCP"); - request.put("peerName", "1.2.3.4"); - request.put("peerIp", "1.2.3.4"); - request.put("peerPort", "123"); + Context context = Context.root(); + + // when + AttributesBuilder startAttributes = Attributes.builder(); + extractor.onStart(startAttributes, context, emptyMap()); + + AttributesBuilder endAttributes = Attributes.builder(); + extractor.onEnd(endAttributes, context, emptyMap(), emptyMap(), null); - Map response = new HashMap<>(); - response.put("peerName", "4.3.2.1"); - response.put("peerPort", "42"); - response.put("peerIp", "4.3.2.1"); + // then + assertThat(startAttributes.build()).isEmpty(); + assertThat(endAttributes.build()).isEmpty(); + } - NetServerAttributesExtractor, Map> extractor = - createTestExtractor(); + @Test + @DisplayName( + "does not set any net.sock.host.* attributes when net.host.name = net.sock.host.addr") + void doesNotSetDuplicates1() { + // given + Map map = new HashMap<>(); + map.put("transport", IP_TCP); + map.put("hostName", "4:3:2:1::"); + map.put("hostPort", "80"); + map.put("sockFamily", "inet6"); + map.put("sockHostAddr", "4:3:2:1::"); + map.put("sockHostPort", "8080"); Context context = Context.root(); // when AttributesBuilder startAttributes = Attributes.builder(); - extractor.onStart(startAttributes, context, request); + extractor.onStart(startAttributes, context, map); AttributesBuilder endAttributes = Attributes.builder(); - extractor.onEnd(endAttributes, context, request, response, null); + extractor.onEnd(endAttributes, context, map, map, null); // then assertThat(startAttributes.build()) .containsOnly( - entry(SemanticAttributes.NET_TRANSPORT, "TCP"), - entry(SemanticAttributes.NET_PEER_PORT, 123L), - entry(SemanticAttributes.NET_PEER_IP, "1.2.3.4")); + entry(SemanticAttributes.NET_TRANSPORT, IP_TCP), + entry(SemanticAttributes.NET_HOST_NAME, "4:3:2:1::"), + entry(SemanticAttributes.NET_HOST_PORT, 80L)); assertThat(endAttributes.build()).isEmpty(); } @Test - public void doesNotSetNegativePort() { + @DisplayName( + "does not set net.sock.host.* attributes when they duplicate related net.host.* attributes") + void doesNotSetDuplicates2() { // given - Map request = new HashMap<>(); - request.put("peerPort", "-42"); + Map map = new HashMap<>(); + map.put("transport", IP_TCP); + map.put("hostName", "opentelemetry.io"); + map.put("hostPort", "80"); + map.put("sockFamily", "inet6"); + map.put("sockHostAddr", "4:3:2:1::"); + map.put("sockHostPort", "80"); + + Context context = Context.root(); - Map response = new HashMap<>(); - response.put("peerPort", "-1"); + // when + AttributesBuilder startAttributes = Attributes.builder(); + extractor.onStart(startAttributes, context, map); + + AttributesBuilder endAttributes = Attributes.builder(); + extractor.onEnd(endAttributes, context, map, map, null); + + // then + assertThat(startAttributes.build()) + .containsOnly( + entry(SemanticAttributes.NET_TRANSPORT, IP_TCP), + entry(SemanticAttributes.NET_HOST_NAME, "opentelemetry.io"), + entry(SemanticAttributes.NET_HOST_PORT, 80L), + entry(SemanticAttributes.NET_SOCK_FAMILY, "inet6"), + entry(SemanticAttributes.NET_SOCK_HOST_ADDR, "4:3:2:1::")); - NetServerAttributesExtractor, Map> extractor = - createTestExtractor(); + assertThat(endAttributes.build()).isEmpty(); + } + + @Test + void doesNotSetNegativePort() { + // given + Map map = new HashMap<>(); + map.put("hostName", "opentelemetry.io"); + map.put("hostPort", "-80"); + map.put("sockPeerAddr", "1:2:3:4::"); + map.put("sockPeerPort", "-42"); + map.put("sockHostAddr", "4:3:2:1::"); + map.put("sockHostPort", "-8080"); Context context = Context.root(); // when AttributesBuilder startAttributes = Attributes.builder(); - extractor.onStart(startAttributes, context, request); + extractor.onStart(startAttributes, context, map); AttributesBuilder endAttributes = Attributes.builder(); - extractor.onEnd(endAttributes, context, request, response, null); + extractor.onEnd(endAttributes, context, map, map, null); // then - assertThat(startAttributes.build()).isEmpty(); + assertThat(startAttributes.build()) + .containsOnly( + entry(SemanticAttributes.NET_HOST_NAME, "opentelemetry.io"), + entry(SemanticAttributes.NET_SOCK_PEER_ADDR, "1:2:3:4::"), + entry(SemanticAttributes.NET_SOCK_HOST_ADDR, "4:3:2:1::")); + assertThat(endAttributes.build()).isEmpty(); } - private static NetServerAttributesExtractor, Map> - createTestExtractor() { - return NetServerAttributesExtractor.create(new TestNetServerAttributesGetter()); + @Test + void doesNotSetSockFamilyInet() { + // given + Map map = new HashMap<>(); + map.put("hostName", "opentelemetry.io"); + map.put("sockPeerAddr", "1.2.3.4"); + map.put("sockFamily", SemanticAttributes.NetSockFamilyValues.INET); + + Context context = Context.root(); + + // when + AttributesBuilder startAttributes = Attributes.builder(); + extractor.onStart(startAttributes, context, map); + + AttributesBuilder endAttributes = Attributes.builder(); + extractor.onEnd(endAttributes, context, map, map, null); + + // then + assertThat(startAttributes.build()) + .containsOnly( + entry(SemanticAttributes.NET_HOST_NAME, "opentelemetry.io"), + entry(SemanticAttributes.NET_SOCK_PEER_ADDR, "1.2.3.4")); + + assertThat(endAttributes.build()).isEmpty(); } } diff --git a/instrumentation-api-semconv/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/PeerServiceAttributesExtractorTest.java b/instrumentation-api-semconv/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/net/PeerServiceAttributesExtractorTest.java similarity index 53% rename from instrumentation-api-semconv/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/PeerServiceAttributesExtractorTest.java rename to instrumentation-api-semconv/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/net/PeerServiceAttributesExtractorTest.java index 2f24e7cbde55..773d1b67ca48 100644 --- a/instrumentation-api-semconv/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/PeerServiceAttributesExtractorTest.java +++ b/instrumentation-api-semconv/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/net/PeerServiceAttributesExtractorTest.java @@ -3,19 +3,18 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.instrumentation.api.instrumenter; +package io.opentelemetry.instrumentation.api.instrumenter.net; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; import static java.util.Collections.singletonMap; import static org.assertj.core.api.Assertions.entry; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.when; import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.common.AttributesBuilder; import io.opentelemetry.context.Context; -import io.opentelemetry.instrumentation.api.instrumenter.net.NetClientAttributesGetter; import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; import java.util.HashMap; import java.util.Map; @@ -34,7 +33,7 @@ void shouldNotSetAnyValueIfNetExtractorReturnsNulls() { Map peerServiceMapping = singletonMap("1.2.3.4", "myService"); PeerServiceAttributesExtractor underTest = - new PeerServiceAttributesExtractor<>(peerServiceMapping, netAttributesExtractor); + new PeerServiceAttributesExtractor<>(netAttributesExtractor, peerServiceMapping); Context context = Context.root(); @@ -53,32 +52,9 @@ void shouldNotSetAnyValueIfPeerNameDoesNotMatch() { Map peerServiceMapping = singletonMap("example.com", "myService"); PeerServiceAttributesExtractor underTest = - new PeerServiceAttributesExtractor<>(peerServiceMapping, netAttributesExtractor); + new PeerServiceAttributesExtractor<>(netAttributesExtractor, peerServiceMapping); - given(netAttributesExtractor.peerName(any(), any())).willReturn("example2.com"); - - Context context = Context.root(); - - // when - AttributesBuilder startAttributes = Attributes.builder(); - underTest.onStart(startAttributes, context, "request"); - AttributesBuilder endAttributes = Attributes.builder(); - underTest.onEnd(endAttributes, context, "request", "response", null); - - // then - assertTrue(startAttributes.build().isEmpty()); - assertTrue(endAttributes.build().isEmpty()); - } - - @Test - void shouldNotSetAnyValueIfPeerIpDoesNotMatch() { - // given - Map peerServiceMapping = singletonMap("1.2.3.4", "myService"); - - PeerServiceAttributesExtractor underTest = - new PeerServiceAttributesExtractor<>(peerServiceMapping, netAttributesExtractor); - - given(netAttributesExtractor.peerIp(any(), any())).willReturn("1.2.3.5"); + when(netAttributesExtractor.peerName(any())).thenReturn("example2.com"); Context context = Context.root(); @@ -101,9 +77,9 @@ void shouldSetPeerNameIfItMatches() { peerServiceMapping.put("1.2.3.4", "someOtherService"); PeerServiceAttributesExtractor underTest = - new PeerServiceAttributesExtractor<>(peerServiceMapping, netAttributesExtractor); + new PeerServiceAttributesExtractor<>(netAttributesExtractor, peerServiceMapping); - given(netAttributesExtractor.peerName(any(), any())).willReturn("example.com"); + when(netAttributesExtractor.peerName(any())).thenReturn("example.com"); Context context = Context.root(); @@ -118,31 +94,4 @@ void shouldSetPeerNameIfItMatches() { assertThat(endAttributes.build()) .containsOnly(entry(SemanticAttributes.PEER_SERVICE, "myService")); } - - @Test - void shouldSetPeerIpIfItMatchesAndNameDoesNot() { - // given - Map peerServiceMapping = new HashMap<>(); - peerServiceMapping.put("example.com", "myService"); - peerServiceMapping.put("1.2.3.4", "someOtherService"); - - PeerServiceAttributesExtractor underTest = - new PeerServiceAttributesExtractor<>(peerServiceMapping, netAttributesExtractor); - - given(netAttributesExtractor.peerName(any(), any())).willReturn("test.com"); - given(netAttributesExtractor.peerIp(any(), any())).willReturn("1.2.3.4"); - - Context context = Context.root(); - - // when - AttributesBuilder startAttributes = Attributes.builder(); - underTest.onStart(startAttributes, context, "request"); - AttributesBuilder endAttributes = Attributes.builder(); - underTest.onEnd(endAttributes, context, "request", "response", null); - - // then - assertThat(startAttributes.build()).isEmpty(); - assertThat(endAttributes.build()) - .containsOnly(entry(SemanticAttributes.PEER_SERVICE, "someOtherService")); - } } diff --git a/instrumentation-api-semconv/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/rpc/RpcClientMetricsTest.java b/instrumentation-api-semconv/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/rpc/RpcClientMetricsTest.java index 2189c051d994..295851545797 100644 --- a/instrumentation-api-semconv/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/rpc/RpcClientMetricsTest.java +++ b/instrumentation-api-semconv/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/rpc/RpcClientMetricsTest.java @@ -41,14 +41,12 @@ void collectsMetrics() { Attributes responseAttributes1 = Attributes.builder() .put(SemanticAttributes.NET_PEER_NAME, "example.com") - .put(SemanticAttributes.NET_PEER_IP, "127.0.0.1") .put(SemanticAttributes.NET_PEER_PORT, 8080) .put(SemanticAttributes.NET_TRANSPORT, "ip_tcp") .build(); Attributes responseAttributes2 = Attributes.builder() - .put(SemanticAttributes.NET_PEER_IP, "127.0.0.1") .put(SemanticAttributes.NET_PEER_PORT, 8080) .put(SemanticAttributes.NET_TRANSPORT, "ip_tcp") .build(); @@ -121,7 +119,6 @@ void collectsMetrics() { SemanticAttributes.RPC_SERVICE, "myservice.EchoService"), equalTo(SemanticAttributes.RPC_METHOD, "exampleMethod"), - equalTo(SemanticAttributes.NET_PEER_IP, "127.0.0.1"), equalTo(SemanticAttributes.NET_PEER_PORT, 8080), equalTo(SemanticAttributes.NET_TRANSPORT, "ip_tcp"))))); } diff --git a/instrumentation-api-semconv/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/rpc/RpcServerMetricsTest.java b/instrumentation-api-semconv/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/rpc/RpcServerMetricsTest.java index ee1c3eeb49db..dae6f1cc2e25 100644 --- a/instrumentation-api-semconv/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/rpc/RpcServerMetricsTest.java +++ b/instrumentation-api-semconv/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/rpc/RpcServerMetricsTest.java @@ -41,14 +41,14 @@ void collectsMetrics() { Attributes responseAttributes1 = Attributes.builder() .put(SemanticAttributes.NET_HOST_NAME, "example.com") - .put(SemanticAttributes.NET_HOST_IP, "127.0.0.1") + .put(SemanticAttributes.NET_SOCK_HOST_ADDR, "127.0.0.1") .put(SemanticAttributes.NET_HOST_PORT, 8080) .put(SemanticAttributes.NET_TRANSPORT, "ip_tcp") .build(); Attributes responseAttributes2 = Attributes.builder() - .put(SemanticAttributes.NET_HOST_IP, "127.0.0.1") + .put(SemanticAttributes.NET_SOCK_HOST_ADDR, "127.0.0.1") .put(SemanticAttributes.NET_HOST_PORT, 8080) .put(SemanticAttributes.NET_TRANSPORT, "ip_tcp") .build(); @@ -120,7 +120,8 @@ void collectsMetrics() { SemanticAttributes.RPC_SERVICE, "myservice.EchoService"), equalTo(SemanticAttributes.RPC_METHOD, "exampleMethod"), - equalTo(SemanticAttributes.NET_HOST_IP, "127.0.0.1"), + equalTo( + SemanticAttributes.NET_SOCK_HOST_ADDR, "127.0.0.1"), equalTo(SemanticAttributes.NET_TRANSPORT, "ip_tcp"))))); } diff --git a/instrumentation-api-semconv/src/test/java/io/opentelemetry/instrumentation/api/internal/ClassNamesTest.java b/instrumentation-api-semconv/src/test/java/io/opentelemetry/instrumentation/api/internal/ClassNamesTest.java new file mode 100644 index 000000000000..20f0ae8df487 --- /dev/null +++ b/instrumentation-api-semconv/src/test/java/io/opentelemetry/instrumentation/api/internal/ClassNamesTest.java @@ -0,0 +1,44 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.api.internal; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.Test; + +class ClassNamesTest { + + @Test + void testNamed() { + assertThat(ClassNames.simpleName(Outer.class)).isEqualTo("Outer"); + assertThat(ClassNames.simpleName(Outer.Inner.class)).isEqualTo("Inner"); + } + + @Test + void testAnonymous() { + Runnable x = + new Runnable() { + @Override + public void run() {} + }; + assertThat(ClassNames.simpleName(x.getClass())).isEqualTo("ClassNamesTest$1"); + } + + @Test + void testLambda() { + Runnable x = () -> {}; + assertThat(ClassNames.simpleName(x.getClass())).startsWith("ClassNamesTest$$Lambda$"); + } + + static class Outer { + + static class Inner { + private Inner() {} + } + + private Outer() {} + } +} diff --git a/instrumentation-api/gradle.properties b/instrumentation-api/gradle.properties new file mode 100644 index 000000000000..45d64bec279d --- /dev/null +++ b/instrumentation-api/gradle.properties @@ -0,0 +1 @@ +otel.stable=true diff --git a/instrumentation-api/src/jmh/java/io/opentelemetry/instrumentation/api/instrumenter/InstrumenterBenchmark.java b/instrumentation-api/src/jmh/java/io/opentelemetry/instrumentation/api/instrumenter/InstrumenterBenchmark.java index 6d06c45b219c..4ea150d2011c 100644 --- a/instrumentation-api/src/jmh/java/io/opentelemetry/instrumentation/api/instrumenter/InstrumenterBenchmark.java +++ b/instrumentation-api/src/jmh/java/io/opentelemetry/instrumentation/api/instrumenter/InstrumenterBenchmark.java @@ -45,7 +45,7 @@ public class InstrumenterBenchmark { HttpClientAttributesExtractor.create(ConstantHttpAttributesGetter.INSTANCE)) .addAttributesExtractor( NetServerAttributesExtractor.create(new ConstantNetAttributesGetter())) - .newInstrumenter(); + .buildInstrumenter(); @Benchmark public Context start() { @@ -80,38 +80,16 @@ public List requestHeader(Void unused, String name) { return Collections.emptyList(); } - @Override - public Long requestContentLength(Void unused, @Nullable Void unused2) { - return 100L; - } - - @Override - @Nullable - public Long requestContentLengthUncompressed(Void unused, @Nullable Void unused2) { - return null; - } - @Override public String flavor(Void unused, @Nullable Void unused2) { return SemanticAttributes.HttpFlavorValues.HTTP_2_0; } @Override - public Integer statusCode(Void unused, Void unused2) { + public Integer statusCode(Void unused, Void unused2, @Nullable Throwable error) { return 200; } - @Override - public Long responseContentLength(Void unused, Void unused2) { - return 100L; - } - - @Override - @Nullable - public Long responseContentLengthUncompressed(Void unused, Void unused2) { - return null; - } - @Override public List responseHeader(Void unused, Void unused2, String name) { return Collections.emptyList(); @@ -121,19 +99,39 @@ public List responseHeader(Void unused, Void unused2, String name) { static class ConstantNetAttributesGetter extends InetSocketAddressNetServerAttributesGetter { - private static final InetSocketAddress ADDRESS = + private static final InetSocketAddress PEER_ADDRESS = InetSocketAddress.createUnresolved("localhost", 8080); + private static final InetSocketAddress HOST_ADDRESS = + InetSocketAddress.createUnresolved("localhost", 80); + + @Override + @Nullable + public String transport(Void unused) { + return SemanticAttributes.NetTransportValues.IP_TCP; + } + @Nullable @Override + public String hostName(Void unused) { + return null; + } + @Nullable - public InetSocketAddress getAddress(Void unused) { - return ADDRESS; + @Override + public Integer hostPort(Void unused) { + return null; } @Override @Nullable - public String transport(Void unused) { - return SemanticAttributes.NetTransportValues.IP_TCP; + protected InetSocketAddress getPeerSocketAddress(Void unused) { + return PEER_ADDRESS; + } + + @Nullable + @Override + protected InetSocketAddress getHostSocketAddress(Void unused) { + return HOST_ADDRESS; } } } diff --git a/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/config/Config.java b/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/config/Config.java deleted file mode 100644 index 3312d3619b96..000000000000 --- a/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/config/Config.java +++ /dev/null @@ -1,205 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.instrumentation.api.config; - -import static java.util.Objects.requireNonNull; -import static java.util.logging.Level.FINE; - -import com.google.auto.value.AutoValue; -import java.time.Duration; -import java.util.List; -import java.util.Map; -import java.util.logging.Logger; -import javax.annotation.Nullable; - -/** - * Represents the global instrumentation configuration consisting of system properties and - * environment variables; and, if using the OpenTelemetry javaagent, contents of the agent - * configuration file and properties defined by the {@code ContextCustomizer} SPI implementations. - * - *

In case any {@code get*()} method variant gets called for the same property more than once - * (e.g. each time an advice class executes) it is suggested to cache the result instead of - * repeatedly calling {@link Config}. The instrumentation configuration does not change during the - * runtime so retrieving the property once and storing its result in a static final field allows JIT - * to do its magic and remove some code branches. - */ -// TODO: deprecate -@AutoValue -public abstract class Config { - private static final Logger logger = Logger.getLogger(Config.class.getName()); - - // lazy initialized, so that javaagent can set it, and library instrumentation can fall back and - // read system properties - @Nullable private static volatile Config instance = null; - - /** Start building a new {@link Config} instance. */ - public static ConfigBuilder builder() { - return new ConfigBuilder(); - } - - static Config create(Map allProperties) { - return new AutoValue_Config(allProperties); - } - - // package protected constructor to make extending this class impossible - Config() {} - - /** - * Sets the instrumentation configuration singleton. This method is only supposed to be called - * once, during the javaagent initialization, just before {@link Config#get()} is used for the - * first time. - * - *

This method is internal and is hence not for public use. Its API is unstable and can change - * at any time. - */ - public static void internalInitializeConfig(Config config) { - if (instance != null) { - logger.warning("Config#INSTANCE was already set earlier"); - return; - } - instance = requireNonNull(config); - } - - /** Returns the global instrumentation configuration. */ - public static Config get() { - if (instance == null) { - // this should only happen in library instrumentation - // - // no need to synchronize because worst case is creating instance more than once - instance = builder().addEnvironmentVariables().addSystemProperties().build(); - } - return instance; - } - - /** - * Returns all properties stored in this {@link Config} instance. The returned map is - * unmodifiable. - */ - public abstract Map getAllProperties(); - - /** - * Returns a string-valued configuration property or {@code null} if a property with name {@code - * name} has not been configured. - */ - @Nullable - public String getString(String name) { - return getRawProperty(name, null); - } - - /** - * Returns a string-valued configuration property or {@code defaultValue} if a property with name - * {@code name} has not been configured. - */ - public String getString(String name, String defaultValue) { - return getRawProperty(name, defaultValue); - } - - /** - * Returns a boolean-valued configuration property or {@code defaultValue} if a property with name - * {@code name} has not been configured. - */ - public boolean getBoolean(String name, boolean defaultValue) { - return safeGetTypedProperty(name, ConfigValueParsers::parseBoolean, defaultValue); - } - - /** - * Returns an integer-valued configuration property or {@code defaultValue} if a property with - * name {@code name} has not been configured or when parsing has failed. - */ - public int getInt(String name, int defaultValue) { - return safeGetTypedProperty(name, ConfigValueParsers::parseInt, defaultValue); - } - - /** - * Returns a long-valued configuration property or {@code defaultValue} if a property with name - * {@code name} has not been configured or when parsing has failed. - */ - public long getLong(String name, long defaultValue) { - return safeGetTypedProperty(name, ConfigValueParsers::parseLong, defaultValue); - } - - /** - * Returns a double-valued configuration property or {@code defaultValue} if a property with name - * {@code name} has not been configured or when parsing has failed. - */ - public double getDouble(String name, double defaultValue) { - return safeGetTypedProperty(name, ConfigValueParsers::parseDouble, defaultValue); - } - - /** - * Returns a duration-valued configuration property or {@code defaultValue} if a property with - * name {@code name} has not been configured or when parsing has failed. - * - *

Durations can be of the form "{number}{unit}", where unit is one of: - * - *

    - *
  • ms - *
  • s - *
  • m - *
  • h - *
  • d - *
- * - *

If no unit is specified, milliseconds is the assumed duration unit. - * - *

Examples: 10s, 20ms, 5000 - */ - public Duration getDuration(String name, Duration defaultValue) { - return safeGetTypedProperty(name, ConfigValueParsers::parseDuration, defaultValue); - } - - /** - * Returns a list-valued configuration property or {@code defaultValue} if a property with name - * {@code name} has not been configured. The format of the original value must be comma-separated, - * e.g. {@code one,two,three}. The returned list is unmodifiable. - */ - public List getList(String name, List defaultValue) { - return safeGetTypedProperty(name, ConfigValueParsers::parseList, defaultValue); - } - - /** - * Returns a map-valued configuration property or {@code defaultValue} if a property with name - * {@code name} has not been configured or when parsing has failed. The format of the original - * value must be comma-separated for each key, with an '=' separating the key and value, e.g. - * {@code key=value,anotherKey=anotherValue}. The returned map is unmodifiable. - */ - public Map getMap(String name, Map defaultValue) { - return safeGetTypedProperty(name, ConfigValueParsers::parseMap, defaultValue); - } - - private T safeGetTypedProperty(String name, ConfigValueParser parser, T defaultValue) { - try { - T value = getTypedProperty(name, parser); - return value == null ? defaultValue : value; - } catch (RuntimeException t) { - if (logger.isLoggable(FINE)) { - logger.log(FINE, "Error occurred during parsing: " + t.getMessage(), t); - } - return defaultValue; - } - } - - @Nullable - private T getTypedProperty(String name, ConfigValueParser parser) { - String value = getRawProperty(name, null); - if (value == null || value.trim().isEmpty()) { - return null; - } - return parser.parse(name, value); - } - - private String getRawProperty(String name, String defaultValue) { - return getAllProperties().getOrDefault(NamingConvention.DOT.normalize(name), defaultValue); - } - - /** - * Returns a new {@link ConfigBuilder} instance populated with the properties of this {@link - * Config}. - */ - public ConfigBuilder toBuilder() { - return new ConfigBuilder(getAllProperties()); - } -} diff --git a/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/config/ConfigBuilder.java b/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/config/ConfigBuilder.java deleted file mode 100644 index d291a9646f9c..000000000000 --- a/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/config/ConfigBuilder.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.instrumentation.api.config; - -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; -import java.util.Properties; -import javax.annotation.Nullable; - -/** A builder of a {@link Config}. */ -public final class ConfigBuilder { - - private final Map allProperties; - - ConfigBuilder() { - allProperties = new HashMap<>(); - } - - ConfigBuilder(Map propertiesToCopy) { - allProperties = new HashMap<>(propertiesToCopy); - } - - /** Adds a single property to the config. */ - public ConfigBuilder addProperty(String name, @Nullable String value) { - if (value != null) { - allProperties.put(NamingConvention.DOT.normalize(name), value); - } - return this; - } - - /** Adds all properties from the passed {@link Properties} to the config. */ - public ConfigBuilder addProperties(Properties properties) { - for (String name : properties.stringPropertyNames()) { - addProperty(name, properties.getProperty(name)); - } - return this; - } - - /** Adds all properties from the passed {@link Map} to the config. */ - public ConfigBuilder addProperties(Map properties) { - return fromConfigMap(properties, NamingConvention.DOT); - } - - /** - * Adds environment variables (converted to the Java property naming convention) to the config. - * - *

Environment variable names are converted to lower case, with all underscores replaced by - * dots. - */ - public ConfigBuilder addEnvironmentVariables() { - return fromConfigMap(System.getenv(), NamingConvention.ENV_VAR); - } - - /** Adds system properties to the config. */ - public ConfigBuilder addSystemProperties() { - return addProperties(System.getProperties()); - } - - private ConfigBuilder fromConfigMap( - Map configMap, NamingConvention namingConvention) { - for (Map.Entry entry : configMap.entrySet()) { - allProperties.put(namingConvention.normalize(entry.getKey()), entry.getValue()); - } - return this; - } - - /** Returns a new {@link Config} with properties from this {@linkplain ConfigBuilder builder}. */ - public Config build() { - return Config.create(Collections.unmodifiableMap(new HashMap<>(allProperties))); - } -} diff --git a/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/config/ConfigParsingException.java b/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/config/ConfigParsingException.java deleted file mode 100644 index 169ec6042606..000000000000 --- a/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/config/ConfigParsingException.java +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.instrumentation.api.config; - -class ConfigParsingException extends RuntimeException { - private static final long serialVersionUID = 1L; - - ConfigParsingException(String message) { - super(message); - } -} diff --git a/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/config/ConfigValueParser.java b/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/config/ConfigValueParser.java deleted file mode 100644 index 78e9cba3d51e..000000000000 --- a/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/config/ConfigValueParser.java +++ /dev/null @@ -1,11 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.instrumentation.api.config; - -@FunctionalInterface -interface ConfigValueParser { - T parse(String propertyName, String rawValue); -} diff --git a/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/config/ConfigValueParsers.java b/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/config/ConfigValueParsers.java deleted file mode 100644 index b646f7b55f1c..000000000000 --- a/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/config/ConfigValueParsers.java +++ /dev/null @@ -1,156 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.instrumentation.api.config; - -import static java.util.Collections.unmodifiableList; -import static java.util.Collections.unmodifiableMap; - -import java.time.Duration; -import java.util.AbstractMap; -import java.util.Arrays; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.TimeUnit; -import java.util.stream.Collectors; - -// most of the parsing code copied from -// https://github.com/open-telemetry/opentelemetry-java/blob/main/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/DefaultConfigProperties.java -@SuppressWarnings("UnusedException") -final class ConfigValueParsers { - - static boolean parseBoolean(@SuppressWarnings("unused") String propertyName, String value) { - return Boolean.parseBoolean(value); - } - - static int parseInt(String propertyName, String value) { - try { - return Integer.parseInt(value); - } catch (NumberFormatException e) { - throw newInvalidPropertyException(propertyName, value, "integer"); - } - } - - static long parseLong(String propertyName, String value) { - try { - return Long.parseLong(value); - } catch (NumberFormatException e) { - throw newInvalidPropertyException(propertyName, value, "long"); - } - } - - static double parseDouble(String propertyName, String value) { - try { - return Double.parseDouble(value); - } catch (NumberFormatException e) { - throw newInvalidPropertyException(propertyName, value, "double"); - } - } - - private static ConfigParsingException newInvalidPropertyException( - String name, String value, String type) { - throw new ConfigParsingException( - "Invalid value for property " + name + "=" + value + ". Must be a " + type + "."); - } - - static List parseList(@SuppressWarnings("unused") String propertyName, String value) { - return unmodifiableList(filterBlanks(value.split(","))); - } - - static Map parseMap(String propertyName, String value) { - return unmodifiableMap( - parseList(propertyName, value).stream() - .map(keyValuePair -> trim(keyValuePair.split("=", 2))) - .map( - splitKeyValuePairs -> { - if (splitKeyValuePairs.size() != 2 || splitKeyValuePairs.get(0).isEmpty()) { - throw new ConfigParsingException( - "Invalid map property: " + propertyName + "=" + value); - } - return new AbstractMap.SimpleImmutableEntry<>( - splitKeyValuePairs.get(0), splitKeyValuePairs.get(1)); - }) - // If duplicate keys, prioritize later ones similar to duplicate system properties on a - // Java command line. - .collect( - Collectors.toMap( - Map.Entry::getKey, - Map.Entry::getValue, - (first, next) -> next, - LinkedHashMap::new))); - } - - private static List filterBlanks(String[] values) { - return Arrays.stream(values) - .map(String::trim) - .filter(s -> !s.isEmpty()) - .collect(Collectors.toList()); - } - - private static List trim(String[] values) { - return Arrays.stream(values).map(String::trim).collect(Collectors.toList()); - } - - static Duration parseDuration(String propertyName, String value) { - String unitString = getUnitString(value); - String numberString = value.substring(0, value.length() - unitString.length()); - try { - long rawNumber = Long.parseLong(numberString.trim()); - TimeUnit unit = getDurationUnit(unitString.trim()); - return Duration.ofMillis(TimeUnit.MILLISECONDS.convert(rawNumber, unit)); - } catch (NumberFormatException e) { - throw new ConfigParsingException( - "Invalid duration property " - + propertyName - + "=" - + value - + ". Expected number, found: " - + numberString); - } catch (ConfigParsingException ex) { - throw new ConfigParsingException( - "Invalid duration property " + propertyName + "=" + value + ". " + ex.getMessage()); - } - } - - /** Returns the TimeUnit associated with a unit string. Defaults to milliseconds. */ - private static TimeUnit getDurationUnit(String unitString) { - switch (unitString) { - case "": // Fallthrough expected - case "ms": - return TimeUnit.MILLISECONDS; - case "s": - return TimeUnit.SECONDS; - case "m": - return TimeUnit.MINUTES; - case "h": - return TimeUnit.HOURS; - case "d": - return TimeUnit.DAYS; - default: - throw new ConfigParsingException("Invalid duration string, found: " + unitString); - } - } - - /** - * Fragments the 'units' portion of a config value from the 'value' portion. - * - *

E.g. "1ms" would return the string "ms". - */ - private static String getUnitString(String rawValue) { - int lastDigitIndex = rawValue.length() - 1; - while (lastDigitIndex >= 0) { - char c = rawValue.charAt(lastDigitIndex); - if (Character.isDigit(c)) { - break; - } - lastDigitIndex -= 1; - } - // Pull everything after the last digit. - return rawValue.substring(lastDigitIndex + 1); - } - - private ConfigValueParsers() {} -} diff --git a/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/config/NamingConvention.java b/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/config/NamingConvention.java deleted file mode 100644 index a962b7148e31..000000000000 --- a/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/config/NamingConvention.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.instrumentation.api.config; - -import java.util.Locale; - -// config property names are normalized to dot separated lowercase words -enum NamingConvention { - DOT { - @Override - public String normalize(String key) { - // many instrumentation names have dashes ('-') - return key.toLowerCase(Locale.ROOT).replace('-', '.'); - } - }, - ENV_VAR { - @Override - public String normalize(String key) { - return key.toLowerCase(Locale.ROOT).replace('_', '.'); - } - }; - - abstract String normalize(String key); -} diff --git a/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/AttributesExtractor.java b/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/AttributesExtractor.java index cd6fb1fbb533..501ae159ffe6 100644 --- a/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/AttributesExtractor.java +++ b/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/AttributesExtractor.java @@ -5,6 +5,8 @@ package io.opentelemetry.instrumentation.api.instrumenter; +import static java.util.Objects.requireNonNull; + import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.common.AttributesBuilder; @@ -44,6 +46,8 @@ void onEnd( */ static AttributesExtractor constant( AttributeKey attributeKey, T attributeValue) { - return new ConstantAttributesExtractor<>(attributeKey, attributeValue); + return new ConstantAttributesExtractor<>( + requireNonNull(attributeKey, "attributeKey"), + requireNonNull(attributeValue, "attributeValue")); } } diff --git a/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/ContextCustomizer.java b/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/ContextCustomizer.java index c84c50f4ef53..23b8dff6a204 100644 --- a/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/ContextCustomizer.java +++ b/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/ContextCustomizer.java @@ -18,5 +18,5 @@ public interface ContextCustomizer { /** Allows to customize the operation {@link Context}. */ - Context onStart(Context context, REQUEST request, Attributes startAttributes); + Context onStart(Context parentContext, REQUEST request, Attributes startAttributes); } diff --git a/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/JdkErrorCauseExtractor.java b/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/DefaultErrorCauseExtractor.java similarity index 87% rename from instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/JdkErrorCauseExtractor.java rename to instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/DefaultErrorCauseExtractor.java index a9474b7ec590..b2dd00ef91be 100644 --- a/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/JdkErrorCauseExtractor.java +++ b/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/DefaultErrorCauseExtractor.java @@ -10,8 +10,8 @@ import java.util.concurrent.ExecutionException; import javax.annotation.Nullable; -final class JdkErrorCauseExtractor implements ErrorCauseExtractor { - static final ErrorCauseExtractor INSTANCE = new JdkErrorCauseExtractor(); +final class DefaultErrorCauseExtractor implements ErrorCauseExtractor { + static final ErrorCauseExtractor INSTANCE = new DefaultErrorCauseExtractor(); @Nullable private static final Class COMPLETION_EXCEPTION_CLASS = getCompletionExceptionClass(); @@ -42,5 +42,5 @@ private static Class getCompletionExceptionClass() { } } - private JdkErrorCauseExtractor() {} + private DefaultErrorCauseExtractor() {} } diff --git a/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/ErrorCauseExtractor.java b/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/ErrorCauseExtractor.java index dfd08415e60d..d744b88e5953 100644 --- a/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/ErrorCauseExtractor.java +++ b/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/ErrorCauseExtractor.java @@ -17,10 +17,10 @@ public interface ErrorCauseExtractor { Throwable extract(Throwable error); /** - * Returns a {@link ErrorCauseExtractor} which unwraps common standard library wrapping + * Returns the default {@link ErrorCauseExtractor}, which unwraps common standard library wrapping * exceptions. */ - static ErrorCauseExtractor jdk() { - return JdkErrorCauseExtractor.INSTANCE; + static ErrorCauseExtractor getDefault() { + return DefaultErrorCauseExtractor.INSTANCE; } } diff --git a/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/Instrumenter.java b/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/Instrumenter.java index 8e45354aa6e3..430a93a67f5f 100644 --- a/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/Instrumenter.java +++ b/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/Instrumenter.java @@ -160,10 +160,7 @@ Context startAndEnd( private Context doStart(Context parentContext, REQUEST request, @Nullable Instant startTime) { SpanKind spanKind = spanKindExtractor.extract(request); SpanBuilder spanBuilder = - tracer - .spanBuilder(spanNameExtractor.extract(request)) - .setSpanKind(spanKind) - .setParent(parentContext); + tracer.spanBuilder(spanNameExtractor.extract(request)).setSpanKind(spanKind); if (startTime != null) { spanBuilder.setStartTimestamp(startTime); @@ -181,22 +178,28 @@ private Context doStart(Context parentContext, REQUEST request, @Nullable Instan Context context = parentContext; - spanBuilder.setAllAttributes(attributes); - Span span = spanBuilder.startSpan(); - context = context.with(span); - + // context customizers run before span start, so that they can have access to the parent span + // context, and so that their additions to the context will be visible to span processors for (ContextCustomizer contextCustomizer : contextCustomizers) { context = contextCustomizer.onStart(context, request, attributes); } + boolean localRoot = LocalRootSpan.isLocalRoot(context); + + spanBuilder.setAllAttributes(attributes); + Span span = spanBuilder.setParent(context).startSpan(); + context = context.with(span); + if (!operationListeners.isEmpty()) { + // operation listeners run after span start, so that they have access to the current span + // for capturing exemplars long startNanos = getNanos(startTime); for (OperationListener operationListener : operationListeners) { context = operationListener.onStart(context, attributes, startNanos); } } - if (LocalRootSpan.isLocalRoot(parentContext)) { + if (localRoot) { context = LocalRootSpan.store(context, span); } diff --git a/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/InstrumenterBuilder.java b/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/InstrumenterBuilder.java index 292e8b937b9a..f63ab431e0e8 100644 --- a/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/InstrumenterBuilder.java +++ b/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/InstrumenterBuilder.java @@ -5,6 +5,9 @@ package io.opentelemetry.instrumentation.api.instrumenter; +import static java.util.Objects.requireNonNull; + +import com.google.errorprone.annotations.CanIgnoreReturnValue; import io.opentelemetry.api.OpenTelemetry; import io.opentelemetry.api.metrics.Meter; import io.opentelemetry.api.metrics.MeterBuilder; @@ -20,7 +23,6 @@ import io.opentelemetry.instrumentation.api.internal.SpanKey; import io.opentelemetry.instrumentation.api.internal.SpanKeyProvider; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; import java.util.Set; import java.util.stream.Collectors; @@ -56,7 +58,7 @@ public final class InstrumenterBuilder { SpanKindExtractor spanKindExtractor = SpanKindExtractor.alwaysInternal(); SpanStatusExtractor spanStatusExtractor = SpanStatusExtractor.getDefault(); - ErrorCauseExtractor errorCauseExtractor = ErrorCauseExtractor.jdk(); + ErrorCauseExtractor errorCauseExtractor = ErrorCauseExtractor.getDefault(); boolean enabled = true; InstrumenterBuilder( @@ -77,9 +79,10 @@ public final class InstrumenterBuilder { * @param instrumentationVersion is the version of the instrumentation library, not the version of * the instrumented library. */ + @CanIgnoreReturnValue public InstrumenterBuilder setInstrumentationVersion( String instrumentationVersion) { - this.instrumentationVersion = instrumentationVersion; + this.instrumentationVersion = requireNonNull(instrumentationVersion, "instrumentationVersion"); return this; } @@ -87,49 +90,46 @@ public InstrumenterBuilder setInstrumentationVersion( * Sets the OpenTelemetry schema URL that will be associated with all telemetry produced by this * {@link Instrumenter}. */ + @CanIgnoreReturnValue public InstrumenterBuilder setSchemaUrl(String schemaUrl) { - this.schemaUrl = schemaUrl; + this.schemaUrl = requireNonNull(schemaUrl, "schemaUrl"); return this; } /** * Sets the {@link SpanStatusExtractor} that will determine the {@link StatusCode} for a response. */ + @CanIgnoreReturnValue public InstrumenterBuilder setSpanStatusExtractor( SpanStatusExtractor spanStatusExtractor) { - this.spanStatusExtractor = spanStatusExtractor; + this.spanStatusExtractor = requireNonNull(spanStatusExtractor, "spanStatusExtractor"); return this; } /** * Adds a {@link AttributesExtractor} that will extract attributes from requests and responses. */ + @CanIgnoreReturnValue public InstrumenterBuilder addAttributesExtractor( AttributesExtractor attributesExtractor) { - this.attributesExtractors.add(attributesExtractor); + this.attributesExtractors.add(requireNonNull(attributesExtractor, "attributesExtractor")); return this; } /** Adds {@link AttributesExtractor}s that will extract attributes from requests and responses. */ + @CanIgnoreReturnValue public InstrumenterBuilder addAttributesExtractors( Iterable> attributesExtractors) { - attributesExtractors.forEach(this.attributesExtractors::add); + attributesExtractors.forEach(this::addAttributesExtractor); return this; } - /** Adds {@link AttributesExtractor}s that will extract attributes from requests and responses. */ - @SafeVarargs - @SuppressWarnings("varargs") - public final InstrumenterBuilder addAttributesExtractors( - AttributesExtractor... attributesExtractors) { - return addAttributesExtractors(Arrays.asList(attributesExtractors)); - } - /** Adds a {@link SpanLinksExtractor} that will extract span links from requests. */ + @CanIgnoreReturnValue public InstrumenterBuilder addSpanLinksExtractor( SpanLinksExtractor spanLinksExtractor) { - spanLinksExtractors.add(spanLinksExtractor); + spanLinksExtractors.add(requireNonNull(spanLinksExtractor, "spanLinksExtractor")); return this; } @@ -137,9 +137,10 @@ public InstrumenterBuilder addSpanLinksExtractor( * Adds a {@link ContextCustomizer} that will customize the context during {@link * Instrumenter#start(Context, Object)}. */ + @CanIgnoreReturnValue public InstrumenterBuilder addContextCustomizer( ContextCustomizer contextCustomizer) { - contextCustomizers.add(contextCustomizer); + contextCustomizers.add(requireNonNull(contextCustomizer, "contextCustomizer")); return this; } @@ -147,8 +148,9 @@ public InstrumenterBuilder addContextCustomizer( * Adds a {@link OperationListener} that will be called when an instrumented operation starts and * ends. */ + @CanIgnoreReturnValue public InstrumenterBuilder addOperationListener(OperationListener listener) { - operationListeners.add(listener); + operationListeners.add(requireNonNull(listener, "operationListener")); return this; } @@ -156,8 +158,9 @@ public InstrumenterBuilder addOperationListener(OperationList * Adds a {@link OperationMetrics} that will produce a {@link OperationListener} capturing the * requests processing metrics. */ + @CanIgnoreReturnValue public InstrumenterBuilder addOperationMetrics(OperationMetrics factory) { - operationMetrics.add(factory); + operationMetrics.add(requireNonNull(factory, "operationMetrics")); return this; } @@ -165,9 +168,10 @@ public InstrumenterBuilder addOperationMetrics(OperationMetri * Sets the {@link ErrorCauseExtractor} that will extract the root cause of an error thrown during * request processing. */ + @CanIgnoreReturnValue public InstrumenterBuilder setErrorCauseExtractor( ErrorCauseExtractor errorCauseExtractor) { - this.errorCauseExtractor = errorCauseExtractor; + this.errorCauseExtractor = requireNonNull(errorCauseExtractor, "errorCauseExtractor"); return this; } @@ -175,6 +179,7 @@ public InstrumenterBuilder setErrorCauseExtractor( * Allows enabling/disabling the {@link Instrumenter} based on the {@code enabled} value passed as * parameter. All instrumenters are enabled by default. */ + @CanIgnoreReturnValue public InstrumenterBuilder setEnabled(boolean enabled) { this.enabled = enabled; return this; @@ -184,27 +189,29 @@ public InstrumenterBuilder setEnabled(boolean enabled) { * Returns a new {@link Instrumenter} which will create {@linkplain SpanKind#CLIENT client} spans * and inject context into requests. */ - public Instrumenter newClientInstrumenter(TextMapSetter setter) { - return newInstrumenter( - InstrumenterConstructor.propagatingToDownstream(setter), SpanKindExtractor.alwaysClient()); + public Instrumenter buildClientInstrumenter(TextMapSetter setter) { + return buildInstrumenter( + InstrumenterConstructor.propagatingToDownstream(requireNonNull(setter, "setter")), + SpanKindExtractor.alwaysClient()); } /** * Returns a new {@link Instrumenter} which will create {@linkplain SpanKind#SERVER server} spans * and extract context from requests. */ - public Instrumenter newServerInstrumenter(TextMapGetter getter) { - return newInstrumenter( - InstrumenterConstructor.propagatingFromUpstream(getter), SpanKindExtractor.alwaysServer()); + public Instrumenter buildServerInstrumenter(TextMapGetter getter) { + return buildInstrumenter( + InstrumenterConstructor.propagatingFromUpstream(requireNonNull(getter, "getter")), + SpanKindExtractor.alwaysServer()); } /** * Returns a new {@link Instrumenter} which will create {@linkplain SpanKind#PRODUCER producer} * spans and inject context into requests. */ - public Instrumenter newProducerInstrumenter(TextMapSetter setter) { - return newInstrumenter( - InstrumenterConstructor.propagatingToDownstream(setter), + public Instrumenter buildProducerInstrumenter(TextMapSetter setter) { + return buildInstrumenter( + InstrumenterConstructor.propagatingToDownstream(requireNonNull(setter, "setter")), SpanKindExtractor.alwaysProducer()); } @@ -212,9 +219,9 @@ public Instrumenter newProducerInstrumenter(TextMapSetter newConsumerInstrumenter(TextMapGetter getter) { - return newInstrumenter( - InstrumenterConstructor.propagatingFromUpstream(getter), + public Instrumenter buildConsumerInstrumenter(TextMapGetter getter) { + return buildInstrumenter( + InstrumenterConstructor.propagatingFromUpstream(requireNonNull(getter, "getter")), SpanKindExtractor.alwaysConsumer()); } @@ -222,20 +229,22 @@ public Instrumenter newConsumerInstrumenter(TextMapGetter newInstrumenter() { - return newInstrumenter(InstrumenterConstructor.internal(), SpanKindExtractor.alwaysInternal()); + public Instrumenter buildInstrumenter() { + return buildInstrumenter( + InstrumenterConstructor.internal(), SpanKindExtractor.alwaysInternal()); } /** * Returns a new {@link Instrumenter} which will create spans with kind determined by the passed * {@link SpanKindExtractor} and do no context propagation. */ - public Instrumenter newInstrumenter( + public Instrumenter buildInstrumenter( SpanKindExtractor spanKindExtractor) { - return newInstrumenter(InstrumenterConstructor.internal(), spanKindExtractor); + return buildInstrumenter( + InstrumenterConstructor.internal(), requireNonNull(spanKindExtractor, "spanKindExtractor")); } - private Instrumenter newInstrumenter( + private Instrumenter buildInstrumenter( InstrumenterConstructor constructor, SpanKindExtractor spanKindExtractor) { this.spanKindExtractor = spanKindExtractor; @@ -304,12 +313,12 @@ static InstrumenterConstructor internal() { static InstrumenterConstructor propagatingToDownstream( TextMapSetter setter) { - return builder -> new ClientInstrumenter<>(builder, setter); + return builder -> new PropagatingToDownstreamInstrumenter<>(builder, setter); } static InstrumenterConstructor propagatingFromUpstream( TextMapGetter getter) { - return builder -> new ServerInstrumenter<>(builder, getter); + return builder -> new PropagatingFromUpstreamInstrumenter<>(builder, getter); } } } diff --git a/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/LocalRootSpan.java b/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/LocalRootSpan.java index 3afafdb35f74..bed97084102d 100644 --- a/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/LocalRootSpan.java +++ b/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/LocalRootSpan.java @@ -54,4 +54,6 @@ static boolean isLocalRoot(Context parentContext) { static Context store(Context context, Span span) { return context.with(KEY, span); } + + private LocalRootSpan() {} } diff --git a/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/ServerInstrumenter.java b/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/PropagatingFromUpstreamInstrumenter.java similarity index 86% rename from instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/ServerInstrumenter.java rename to instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/PropagatingFromUpstreamInstrumenter.java index f26a2e7e66e6..c5c1b184f208 100644 --- a/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/ServerInstrumenter.java +++ b/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/PropagatingFromUpstreamInstrumenter.java @@ -10,12 +10,13 @@ import io.opentelemetry.context.propagation.TextMapGetter; import io.opentelemetry.instrumentation.api.internal.ContextPropagationDebug; -final class ServerInstrumenter extends Instrumenter { +final class PropagatingFromUpstreamInstrumenter + extends Instrumenter { private final ContextPropagators propagators; private final TextMapGetter getter; - ServerInstrumenter( + PropagatingFromUpstreamInstrumenter( InstrumenterBuilder builder, TextMapGetter getter) { super(builder); this.propagators = builder.openTelemetry.getPropagators(); diff --git a/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/ClientInstrumenter.java b/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/PropagatingToDownstreamInstrumenter.java similarity index 84% rename from instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/ClientInstrumenter.java rename to instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/PropagatingToDownstreamInstrumenter.java index c5549ad3f5bd..2671f051f74e 100644 --- a/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/ClientInstrumenter.java +++ b/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/PropagatingToDownstreamInstrumenter.java @@ -9,12 +9,13 @@ import io.opentelemetry.context.propagation.ContextPropagators; import io.opentelemetry.context.propagation.TextMapSetter; -final class ClientInstrumenter extends Instrumenter { +final class PropagatingToDownstreamInstrumenter + extends Instrumenter { private final ContextPropagators propagators; private final TextMapSetter setter; - ClientInstrumenter( + PropagatingToDownstreamInstrumenter( InstrumenterBuilder builder, TextMapSetter setter) { super(builder); this.propagators = builder.openTelemetry.getPropagators(); diff --git a/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/SpanLinksBuilderImpl.java b/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/SpanLinksBuilderImpl.java index 36959a7fed6d..dedb8fb92943 100644 --- a/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/SpanLinksBuilderImpl.java +++ b/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/SpanLinksBuilderImpl.java @@ -5,6 +5,7 @@ package io.opentelemetry.instrumentation.api.instrumenter; +import com.google.errorprone.annotations.CanIgnoreReturnValue; import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.trace.SpanBuilder; import io.opentelemetry.api.trace.SpanContext; @@ -17,12 +18,14 @@ final class SpanLinksBuilderImpl implements SpanLinksBuilder { } @Override + @CanIgnoreReturnValue public SpanLinksBuilder addLink(SpanContext spanContext) { spanBuilder.addLink(spanContext); return this; } @Override + @CanIgnoreReturnValue public SpanLinksBuilder addLink(SpanContext spanContext, Attributes attributes) { spanBuilder.addLink(spanContext, attributes); return this; diff --git a/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/SpanLinksExtractor.java b/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/SpanLinksExtractor.java index 71a7173b2ffd..77ce0e2c0fea 100644 --- a/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/SpanLinksExtractor.java +++ b/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/SpanLinksExtractor.java @@ -7,8 +7,6 @@ import io.opentelemetry.api.trace.SpanContext; import io.opentelemetry.context.Context; -import io.opentelemetry.context.propagation.TextMapGetter; -import io.opentelemetry.context.propagation.TextMapPropagator; /** Extractor of span links for a request. */ @FunctionalInterface @@ -19,13 +17,4 @@ public interface SpanLinksExtractor { * {@code spanLinks}. */ void extract(SpanLinksBuilder spanLinks, Context parentContext, REQUEST request); - - /** - * Returns a new {@link SpanLinksExtractor} that will extract a {@link SpanContext} from the - * request using configured {@link TextMapPropagator}. - */ - static SpanLinksExtractor extractFromRequest( - TextMapPropagator propagator, TextMapGetter getter) { - return new PropagatorBasedSpanLinksExtractor<>(propagator, getter); - } } diff --git a/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/SpanStatusBuilderImpl.java b/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/SpanStatusBuilderImpl.java index 565dd90be35a..a448c902fe50 100644 --- a/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/SpanStatusBuilderImpl.java +++ b/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/SpanStatusBuilderImpl.java @@ -5,6 +5,7 @@ package io.opentelemetry.instrumentation.api.instrumenter; +import com.google.errorprone.annotations.CanIgnoreReturnValue; import io.opentelemetry.api.trace.Span; import io.opentelemetry.api.trace.StatusCode; @@ -16,6 +17,7 @@ final class SpanStatusBuilderImpl implements SpanStatusBuilder { } @Override + @CanIgnoreReturnValue public SpanStatusBuilder setStatus(StatusCode statusCode, String description) { span.setStatus(statusCode, description); return this; diff --git a/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/SpanSuppressionStrategy.java b/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/SpanSuppressionStrategy.java index 5d7f33407241..6210a74c8154 100644 --- a/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/SpanSuppressionStrategy.java +++ b/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/SpanSuppressionStrategy.java @@ -8,9 +8,9 @@ import static java.util.Collections.singleton; import io.opentelemetry.api.trace.SpanKind; -import io.opentelemetry.instrumentation.api.instrumenter.SpanSuppressor.BySpanKey; -import io.opentelemetry.instrumentation.api.instrumenter.SpanSuppressor.DelegateBySpanKind; -import io.opentelemetry.instrumentation.api.instrumenter.SpanSuppressor.Noop; +import io.opentelemetry.instrumentation.api.instrumenter.SpanSuppressors.BySpanKey; +import io.opentelemetry.instrumentation.api.instrumenter.SpanSuppressors.DelegateBySpanKind; +import io.opentelemetry.instrumentation.api.instrumenter.SpanSuppressors.Noop; import io.opentelemetry.instrumentation.api.internal.SpanKey; import io.opentelemetry.instrumentation.api.internal.SpanKeyProvider; import java.util.EnumMap; diff --git a/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/SpanSuppressor.java b/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/SpanSuppressor.java index c81ba58e8669..5249cd16eb8b 100644 --- a/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/SpanSuppressor.java +++ b/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/SpanSuppressor.java @@ -8,81 +8,10 @@ import io.opentelemetry.api.trace.Span; import io.opentelemetry.api.trace.SpanKind; import io.opentelemetry.context.Context; -import io.opentelemetry.instrumentation.api.internal.SpanKey; -import java.util.Map; -import java.util.Set; interface SpanSuppressor { Context storeInContext(Context context, SpanKind spanKind, Span span); boolean shouldSuppress(Context parentContext, SpanKind spanKind); - - enum Noop implements SpanSuppressor { - INSTANCE; - - @Override - public Context storeInContext(Context context, SpanKind spanKind, Span span) { - return context; - } - - @Override - public boolean shouldSuppress(Context parentContext, SpanKind spanKind) { - return false; - } - } - - final class DelegateBySpanKind implements SpanSuppressor { - - private final Map delegates; - - DelegateBySpanKind(Map delegates) { - this.delegates = delegates; - } - - @Override - public Context storeInContext(Context context, SpanKind spanKind, Span span) { - SpanSuppressor delegate = delegates.get(spanKind); - if (delegate == null) { - return context; - } - return delegate.storeInContext(context, spanKind, span); - } - - @Override - public boolean shouldSuppress(Context parentContext, SpanKind spanKind) { - SpanSuppressor delegate = delegates.get(spanKind); - if (delegate == null) { - return false; - } - return delegate.shouldSuppress(parentContext, spanKind); - } - } - - final class BySpanKey implements SpanSuppressor { - - private final Set spanKeys; - - BySpanKey(Set spanKeys) { - this.spanKeys = spanKeys; - } - - @Override - public Context storeInContext(Context context, SpanKind spanKind, Span span) { - for (SpanKey spanKey : spanKeys) { - context = spanKey.storeInContext(context, span); - } - return context; - } - - @Override - public boolean shouldSuppress(Context parentContext, SpanKind spanKind) { - for (SpanKey spanKey : spanKeys) { - if (spanKey.fromContextOrNull(parentContext) == null) { - return false; - } - } - return true; - } - } } diff --git a/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/SpanSuppressors.java b/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/SpanSuppressors.java new file mode 100644 index 000000000000..7587d83ae18e --- /dev/null +++ b/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/SpanSuppressors.java @@ -0,0 +1,86 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.api.instrumenter; + +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.context.Context; +import io.opentelemetry.instrumentation.api.internal.SpanKey; +import java.util.Map; +import java.util.Set; + +final class SpanSuppressors { + + private SpanSuppressors() {} + + enum Noop implements SpanSuppressor { + INSTANCE; + + @Override + public Context storeInContext(Context context, SpanKind spanKind, Span span) { + return context; + } + + @Override + public boolean shouldSuppress(Context parentContext, SpanKind spanKind) { + return false; + } + } + + static final class DelegateBySpanKind implements SpanSuppressor { + + private final Map delegates; + + DelegateBySpanKind(Map delegates) { + this.delegates = delegates; + } + + @Override + public Context storeInContext(Context context, SpanKind spanKind, Span span) { + SpanSuppressor delegate = delegates.get(spanKind); + if (delegate == null) { + return context; + } + return delegate.storeInContext(context, spanKind, span); + } + + @Override + public boolean shouldSuppress(Context parentContext, SpanKind spanKind) { + SpanSuppressor delegate = delegates.get(spanKind); + if (delegate == null) { + return false; + } + return delegate.shouldSuppress(parentContext, spanKind); + } + } + + static final class BySpanKey implements SpanSuppressor { + + private final Set spanKeys; + + BySpanKey(Set spanKeys) { + this.spanKeys = spanKeys; + } + + @Override + public Context storeInContext(Context context, SpanKind spanKind, Span span) { + for (SpanKey spanKey : spanKeys) { + context = spanKey.storeInContext(context, span); + } + return context; + } + + @Override + public boolean shouldSuppress(Context parentContext, SpanKind spanKind) { + for (SpanKey spanKey : spanKeys) { + if (spanKey.fromContextOrNull(parentContext) == null) { + return false; + } + } + return true; + } + } +} diff --git a/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/UnsafeAttributes.java b/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/UnsafeAttributes.java index 5d08e9fb20b3..782592d5f09a 100644 --- a/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/UnsafeAttributes.java +++ b/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/UnsafeAttributes.java @@ -5,6 +5,7 @@ package io.opentelemetry.instrumentation.api.instrumenter; +import com.google.errorprone.annotations.CanIgnoreReturnValue; import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.common.AttributesBuilder; @@ -54,17 +55,20 @@ public Attributes build() { } @Override + @CanIgnoreReturnValue public AttributesBuilder put(AttributeKey key, int value) { return put(key, (long) value); } @Override + @CanIgnoreReturnValue public AttributesBuilder put(AttributeKey key, T value) { super.put(key, value); return this; } @Override + @CanIgnoreReturnValue public AttributesBuilder putAll(Attributes attributes) { attributes.forEach(this::put); return this; diff --git a/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/package-info.java b/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/package-info.java new file mode 100644 index 000000000000..45783eb040f3 --- /dev/null +++ b/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/package-info.java @@ -0,0 +1,4 @@ +@ParametersAreNonnullByDefault +package io.opentelemetry.instrumentation.api.instrumenter; + +import javax.annotation.ParametersAreNonnullByDefault; diff --git a/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/util/ClassNames.java b/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/internal/ClassNames.java similarity index 80% rename from instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/util/ClassNames.java rename to instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/internal/ClassNames.java index cce8ecc1af62..f611707d5fe5 100644 --- a/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/util/ClassNames.java +++ b/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/internal/ClassNames.java @@ -3,11 +3,16 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.instrumentation.api.util; +package io.opentelemetry.instrumentation.api.internal; import io.opentelemetry.instrumentation.api.internal.cache.Cache; -/** A utility class used to compute readable simple class names. */ +/** + * A utility class used to compute readable simple class names. + * + *

This class is internal and is hence not for public use. Its APIs are unstable and can change + * at any time. + */ public final class ClassNames { private static final Cache, String> simpleNames = Cache.weak(); diff --git a/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/internal/ConfigPropertiesUtil.java b/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/internal/ConfigPropertiesUtil.java index e48c9ed0bdfe..69da5e5deac6 100644 --- a/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/internal/ConfigPropertiesUtil.java +++ b/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/internal/ConfigPropertiesUtil.java @@ -19,6 +19,20 @@ public static boolean getBoolean(String propertyName, boolean defaultValue) { return strValue == null ? defaultValue : Boolean.parseBoolean(strValue); } + public static boolean getBoolean( + String propertyName, String additionalPropertyName, boolean defaultValue) { + String strValue = getString(propertyName); + String additionalStrValue = getString(additionalPropertyName); + + if (strValue != null) { + return Boolean.parseBoolean(strValue); + } + if (additionalStrValue != null) { + return Boolean.parseBoolean(additionalStrValue); + } + return defaultValue; + } + @Nullable public static String getString(String propertyName) { String value = System.getProperty(propertyName); diff --git a/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/internal/ContextPropagationDebug.java b/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/internal/ContextPropagationDebug.java index 596d60bb0fcb..252dd7749be0 100644 --- a/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/internal/ContextPropagationDebug.java +++ b/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/internal/ContextPropagationDebug.java @@ -32,7 +32,8 @@ public final class ContextPropagationDebug { private static final boolean FAIL_ON_CONTEXT_LEAK; static { - boolean agentDebugEnabled = ConfigPropertiesUtil.getBoolean("otel.javaagent.debug", false); + boolean agentDebugEnabled = + ConfigPropertiesUtil.getBoolean("hs.debug", "otel.javaagent.debug", false); THREAD_PROPAGATION_DEBUGGER = ConfigPropertiesUtil.getBoolean( diff --git a/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/internal/HttpRouteState.java b/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/internal/HttpRouteState.java similarity index 100% rename from instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/internal/HttpRouteState.java rename to instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/internal/HttpRouteState.java diff --git a/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/PropagatorBasedSpanLinksExtractor.java b/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/internal/PropagatorBasedSpanLinksExtractor.java similarity index 56% rename from instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/PropagatorBasedSpanLinksExtractor.java rename to instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/internal/PropagatorBasedSpanLinksExtractor.java index 529c8c6bc6df..5439ea25995a 100644 --- a/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/PropagatorBasedSpanLinksExtractor.java +++ b/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/internal/PropagatorBasedSpanLinksExtractor.java @@ -3,18 +3,27 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.instrumentation.api.instrumenter; +package io.opentelemetry.instrumentation.api.internal; import io.opentelemetry.api.trace.Span; import io.opentelemetry.context.Context; import io.opentelemetry.context.propagation.TextMapGetter; import io.opentelemetry.context.propagation.TextMapPropagator; +import io.opentelemetry.instrumentation.api.instrumenter.SpanLinksBuilder; +import io.opentelemetry.instrumentation.api.instrumenter.SpanLinksExtractor; + +/** + * This class is internal and is hence not for public use. Its APIs are unstable and can change at + * any time. + */ +public final class PropagatorBasedSpanLinksExtractor + implements SpanLinksExtractor { -final class PropagatorBasedSpanLinksExtractor implements SpanLinksExtractor { private final TextMapPropagator propagator; private final TextMapGetter getter; - PropagatorBasedSpanLinksExtractor(TextMapPropagator propagator, TextMapGetter getter) { + public PropagatorBasedSpanLinksExtractor( + TextMapPropagator propagator, TextMapGetter getter) { this.propagator = propagator; this.getter = getter; } diff --git a/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/internal/SupportabilityMetrics.java b/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/internal/SupportabilityMetrics.java index 14890106c699..9fb0500a92e7 100644 --- a/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/internal/SupportabilityMetrics.java +++ b/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/internal/SupportabilityMetrics.java @@ -28,7 +28,8 @@ public final class SupportabilityMetrics { private static final SupportabilityMetrics INSTANCE = new SupportabilityMetrics( - ConfigPropertiesUtil.getBoolean("otel.javaagent.debug", false), logger::fine) + ConfigPropertiesUtil.getBoolean("hs.debug", "otel.javaagent.debug", false), + logger::fine) .start(); public static SupportabilityMetrics instance() { @@ -80,7 +81,9 @@ void report() { }); } - SupportabilityMetrics start() { + // this private method is designed for assignment of the return value + @SuppressWarnings("CanIgnoreReturnValueSuggester") + private SupportabilityMetrics start() { if (agentDebugEnabled) { Executors.newScheduledThreadPool( 1, diff --git a/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/internal/cache/weaklockfree/WeakConcurrentMap.java b/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/internal/cache/weaklockfree/WeakConcurrentMap.java index 1e0944269a06..637c91d8d102 100644 --- a/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/internal/cache/weaklockfree/WeakConcurrentMap.java +++ b/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/internal/cache/weaklockfree/WeakConcurrentMap.java @@ -174,6 +174,7 @@ static final class LookupKey { private K key; private int hashCode; + @SuppressWarnings("CanIgnoreReturnValueSuggester") LookupKey withValue(K key) { this.key = key; hashCode = System.identityHashCode(key); diff --git a/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/internal/package-info.java b/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/internal/package-info.java new file mode 100644 index 000000000000..82a7738fa02e --- /dev/null +++ b/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/internal/package-info.java @@ -0,0 +1,4 @@ +@ParametersAreNonnullByDefault +package io.opentelemetry.instrumentation.api.internal; + +import javax.annotation.ParametersAreNonnullByDefault; diff --git a/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/util/package-info.java b/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/util/package-info.java new file mode 100644 index 000000000000..cbf14508ad8e --- /dev/null +++ b/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/util/package-info.java @@ -0,0 +1,4 @@ +@ParametersAreNonnullByDefault +package io.opentelemetry.instrumentation.api.util; + +import javax.annotation.ParametersAreNonnullByDefault; diff --git a/instrumentation-api/src/test/java/io/opentelemetry/instrumentation/api/config/ConfigTest.java b/instrumentation-api/src/test/java/io/opentelemetry/instrumentation/api/config/ConfigTest.java deleted file mode 100644 index 8ae1bb197ff4..000000000000 --- a/instrumentation-api/src/test/java/io/opentelemetry/instrumentation/api/config/ConfigTest.java +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.instrumentation.api.config; - -import static java.util.Arrays.asList; -import static java.util.Collections.emptyMap; -import static java.util.Collections.singletonList; -import static java.util.Collections.singletonMap; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.entry; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import java.time.Duration; -import org.junit.jupiter.api.Test; - -// suppress duration unit check, e.g. ofMillis(5000) -> ofSeconds(5) -@SuppressWarnings({"CanonicalDuration"}) -class ConfigTest { - @Test - void shouldGetString() { - Config config = Config.builder().addProperty("prop.string", "some text").build(); - - assertEquals("some text", config.getString("prop.string")); - assertEquals("some text", config.getString("prop.string", "default")); - assertNull(config.getString("prop.missing")); - assertEquals("default", config.getString("prop.missing", "default")); - } - - @Test - void shouldGetBoolean() { - Config config = Config.builder().addProperty("prop.boolean", "true").build(); - - assertTrue(config.getBoolean("prop.boolean", false)); - assertFalse(config.getBoolean("prop.missing", false)); - } - - @Test - void shouldGetInt() { - Config config = - Config.builder().addProperty("prop.int", "12").addProperty("prop.wrong", "twelve").build(); - - assertEquals(12, config.getInt("prop.int", 1000)); - assertEquals(1000, config.getInt("prop.wrong", 1000)); - assertEquals(1000, config.getInt("prop.missing", 1000)); - } - - @Test - void shouldGetLong() { - Config config = - Config.builder().addProperty("prop.long", "12").addProperty("prop.wrong", "twelve").build(); - - assertEquals(12, config.getLong("prop.long", 1000)); - assertEquals(1000, config.getLong("prop.wrong", 1000)); - assertEquals(1000, config.getLong("prop.missing", 1000)); - } - - @Test - void shouldGetDouble() { - Config config = - Config.builder() - .addProperty("prop.double", "12.345") - .addProperty("prop.wrong", "twelve point something") - .build(); - - assertEquals(12.345, config.getDouble("prop.double", 99.99)); - assertEquals(99.99, config.getDouble("prop.wrong", 99.99)); - assertEquals(99.99, config.getDouble("prop.missing", 99.99)); - } - - @Test - void shouldGetDuration_defaultUnit() { - Config config = - Config.builder() - .addProperty("prop.duration", "5000") - .addProperty("prop.wrong", "hundred days") - .build(); - - assertEquals(Duration.ofMillis(5000), config.getDuration("prop.duration", Duration.ZERO)); - assertEquals(Duration.ZERO, config.getDuration("prop.wrong", Duration.ZERO)); - assertEquals(Duration.ZERO, config.getDuration("prop.missing", Duration.ZERO)); - } - - @Test - void shouldGetDuration_variousUnits() { - Config config = Config.builder().addProperty("prop.duration", "100ms").build(); - assertEquals(Duration.ofMillis(100), config.getDuration("prop.duration", Duration.ZERO)); - - config = Config.builder().addProperty("prop.duration", "100s").build(); - assertEquals(Duration.ofSeconds(100), config.getDuration("prop.duration", Duration.ZERO)); - - config = Config.builder().addProperty("prop.duration", "100m").build(); - assertEquals(Duration.ofMinutes(100), config.getDuration("prop.duration", Duration.ZERO)); - - config = Config.builder().addProperty("prop.duration", "100h").build(); - assertEquals(Duration.ofHours(100), config.getDuration("prop.duration", Duration.ZERO)); - - config = Config.builder().addProperty("prop.duration", "100d").build(); - assertEquals(Duration.ofDays(100), config.getDuration("prop.duration", Duration.ZERO)); - } - - @Test - void shouldGetList() { - Config config = Config.builder().addProperty("prop.list", "one, two ,three").build(); - - assertEquals( - asList("one", "two", "three"), config.getList("prop.list", singletonList("default"))); - assertEquals( - singletonList("default"), config.getList("prop.missing", singletonList("default"))); - } - - @Test - void shouldGetMap() { - Config config = - Config.builder() - .addProperty("prop.map", "one=1, two=2") - .addProperty("prop.wrong", "one=1, but not two!") - .addProperty("prop.trailing", "one=1,") - .build(); - - assertThat(config.getMap("prop.map", singletonMap("three", "3"))) - .containsOnly(entry("one", "1"), entry("two", "2")); - assertThat(config.getMap("prop.wrong", singletonMap("three", "3"))) - .containsOnly(entry("three", "3")); - assertThat(config.getMap("prop.missing", singletonMap("three", "3"))) - .containsOnly(entry("three", "3")); - assertThat(config.getMap("prop.trailing", emptyMap())).containsOnly(entry("one", "1")); - } -} diff --git a/instrumentation-api/src/test/java/io/opentelemetry/instrumentation/api/config/NamingConventionTest.java b/instrumentation-api/src/test/java/io/opentelemetry/instrumentation/api/config/NamingConventionTest.java deleted file mode 100644 index 7c13f752f5b1..000000000000 --- a/instrumentation-api/src/test/java/io/opentelemetry/instrumentation/api/config/NamingConventionTest.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.instrumentation.api.config; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -import org.junit.jupiter.api.Test; - -public class NamingConventionTest { - - @Test - void normalizeEnvWithHyphen() { - // Unlikely that the environment can contain a name like this, but let's doc the behavior - String result = NamingConvention.ENV_VAR.normalize("FOO_BAR-BAZ"); - assertEquals("foo.bar-baz", result); - } - - @Test - void systemPropertyWithHyphen() { - // Normalizes to the same thing - String result1 = - NamingConvention.ENV_VAR.normalize( - "OTEL_INSTRUMENTATION_COMMON_DB_STATEMENT_SANITIZER_ENABLED"); - String result2 = - NamingConvention.DOT.normalize( - "otel.instrumentation.common.db-statement-sanitizer.enabled"); - assertEquals("otel.instrumentation.common.db.statement.sanitizer.enabled", result1); - assertEquals("otel.instrumentation.common.db.statement.sanitizer.enabled", result2); - } - - @Test - void hyphensAndUnderscoresDoNotNormalizeTheSame() { - String result1 = NamingConvention.DOT.normalize("otel.something_else_entirely.foobar"); - assertEquals("otel.something_else_entirely.foobar", result1); - - String result2 = NamingConvention.DOT.normalize("otel.something-else-entirely.foobar"); - assertEquals("otel.something.else.entirely.foobar", result2); - } -} diff --git a/instrumentation-api/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/JdkErrorCauseExtractorTest.java b/instrumentation-api/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/DefaultErrorCauseExtractorTest.java similarity index 85% rename from instrumentation-api/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/JdkErrorCauseExtractorTest.java rename to instrumentation-api/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/DefaultErrorCauseExtractorTest.java index 02cbaf161612..cb17e46dc7db 100644 --- a/instrumentation-api/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/JdkErrorCauseExtractorTest.java +++ b/instrumentation-api/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/DefaultErrorCauseExtractorTest.java @@ -15,7 +15,7 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; -class JdkErrorCauseExtractorTest { +class DefaultErrorCauseExtractorTest { @ParameterizedTest @ValueSource( @@ -31,7 +31,7 @@ void unwraps(Class exceptionClass) throws Exception { .getConstructor(Throwable.class) .newInstance(new IllegalArgumentException("test")); - assertThat(ErrorCauseExtractor.jdk().extract(exception)) + assertThat(ErrorCauseExtractor.getDefault().extract(exception)) .isInstanceOf(IllegalArgumentException.class) .hasMessage("test"); } @@ -39,7 +39,7 @@ void unwraps(Class exceptionClass) throws Exception { @Test void multipleUnwraps() { assertThat( - ErrorCauseExtractor.jdk() + ErrorCauseExtractor.getDefault() .extract( new ExecutionException( new UndeclaredThrowableException(new IllegalArgumentException("test"))))) @@ -49,11 +49,11 @@ void multipleUnwraps() { @Test void notWrapped() { - assertThat(ErrorCauseExtractor.jdk().extract(new IllegalArgumentException("test"))) + assertThat(ErrorCauseExtractor.getDefault().extract(new IllegalArgumentException("test"))) .isInstanceOf(IllegalArgumentException.class) .hasMessage("test"); assertThat( - ErrorCauseExtractor.jdk() + ErrorCauseExtractor.getDefault() .extract(new IllegalArgumentException("test", new IllegalStateException("state")))) .isInstanceOf(IllegalArgumentException.class) .hasMessage("test"); diff --git a/instrumentation-api/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/InstrumenterTest.java b/instrumentation-api/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/InstrumenterTest.java index b7620a81816c..f031274c97d6 100644 --- a/instrumentation-api/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/InstrumenterTest.java +++ b/instrumentation-api/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/InstrumenterTest.java @@ -154,9 +154,10 @@ void server() { Instrumenter, Map> instrumenter = Instrumenter., Map>builder( otelTesting.getOpenTelemetry(), "test", unused -> "span") - .addAttributesExtractors(new AttributesExtractor1(), new AttributesExtractor2()) + .addAttributesExtractor(new AttributesExtractor1()) + .addAttributesExtractor(new AttributesExtractor2()) .addSpanLinksExtractor(new LinksExtractor()) - .newServerInstrumenter(new MapGetter()); + .buildServerInstrumenter(new MapGetter()); Context context = instrumenter.start(Context.root(), REQUEST); SpanContext spanContext = Span.fromContext(context).getSpanContext(); @@ -195,8 +196,9 @@ void server_error() { Instrumenter, Map> instrumenter = Instrumenter., Map>builder( otelTesting.getOpenTelemetry(), "test", unused -> "span") - .addAttributesExtractors(new AttributesExtractor1(), new AttributesExtractor2()) - .newServerInstrumenter(new MapGetter()); + .addAttributesExtractor(new AttributesExtractor1()) + .addAttributesExtractor(new AttributesExtractor2()) + .buildServerInstrumenter(new MapGetter()); Context context = instrumenter.start(Context.root(), REQUEST); assertThat(Span.fromContext(context).getSpanContext().isValid()).isTrue(); @@ -216,8 +218,9 @@ void server_parent() { Instrumenter, Map> instrumenter = Instrumenter., Map>builder( otelTesting.getOpenTelemetry(), "test", unused -> "span") - .addAttributesExtractors(new AttributesExtractor1(), new AttributesExtractor2()) - .newServerInstrumenter(new MapGetter()); + .addAttributesExtractor(new AttributesExtractor1()) + .addAttributesExtractor(new AttributesExtractor2()) + .buildServerInstrumenter(new MapGetter()); Map request = new HashMap<>(REQUEST); W3CTraceContextPropagator.getInstance() @@ -256,9 +259,10 @@ void client() { Instrumenter, Map> instrumenter = Instrumenter., Map>builder( otelTesting.getOpenTelemetry(), "test", unused -> "span") - .addAttributesExtractors(new AttributesExtractor1(), new AttributesExtractor2()) + .addAttributesExtractor(new AttributesExtractor1()) + .addAttributesExtractor(new AttributesExtractor2()) .addSpanLinksExtractor(new LinksExtractor()) - .newClientInstrumenter(Map::put); + .buildClientInstrumenter(Map::put); Map request = new HashMap<>(REQUEST); Context context = instrumenter.start(Context.root(), request); @@ -300,8 +304,9 @@ void client_error() { Instrumenter, Map> instrumenter = Instrumenter., Map>builder( otelTesting.getOpenTelemetry(), "test", unused -> "span") - .addAttributesExtractors(new AttributesExtractor1(), new AttributesExtractor2()) - .newClientInstrumenter(Map::put); + .addAttributesExtractor(new AttributesExtractor1()) + .addAttributesExtractor(new AttributesExtractor2()) + .buildClientInstrumenter(Map::put); Map request = new HashMap<>(REQUEST); Context context = instrumenter.start(Context.root(), request); @@ -325,8 +330,9 @@ void client_parent() { Instrumenter, Map> instrumenter = Instrumenter., Map>builder( otelTesting.getOpenTelemetry(), "test", unused -> "span") - .addAttributesExtractors(new AttributesExtractor1(), new AttributesExtractor2()) - .newClientInstrumenter(Map::put); + .addAttributesExtractor(new AttributesExtractor1()) + .addAttributesExtractor(new AttributesExtractor2()) + .buildClientInstrumenter(Map::put); Context parent = Context.root() @@ -383,7 +389,7 @@ public void onEnd(Context context, Attributes endAttributes, long endNanos) { Instrumenter., Map>builder( otelTesting.getOpenTelemetry(), "test", unused -> "span") .addOperationListener(operationListener) - .newServerInstrumenter(new MapGetter()); + .buildServerInstrumenter(new MapGetter()); Context context = instrumenter.start(Context.root(), REQUEST); instrumenter.end(context, REQUEST, RESPONSE, null); @@ -415,7 +421,7 @@ public void onEnd(Context context, Attributes endAttributes, long endNanos) { Instrumenter., Map>builder( otelTesting.getOpenTelemetry(), "test", unused -> "span") .addOperationMetrics(meter -> operationListener) - .newServerInstrumenter(new MapGetter()); + .buildServerInstrumenter(new MapGetter()); Context context = instrumenter.start(Context.root(), REQUEST); instrumenter.end(context, REQUEST, RESPONSE, null); @@ -432,7 +438,7 @@ void shouldNotAddInvalidLink() { otelTesting.getOpenTelemetry(), "test", request -> "test span") .addSpanLinksExtractor( (spanLinks, parentContext, request) -> spanLinks.addLink(SpanContext.getInvalid())) - .newInstrumenter(); + .buildInstrumenter(); // when Context context = instrumenter.start(Context.root(), "request"); @@ -456,7 +462,7 @@ void shouldUseContextCustomizer() { otelTesting.getOpenTelemetry(), "test", request -> "test span") .addContextCustomizer( (context, request, attributes) -> context.with(testKey, "testVal")) - .newInstrumenter(); + .buildInstrumenter(); // when Context context = instrumenter.start(Context.root(), "request"); @@ -471,7 +477,7 @@ void shouldDisableInstrumenter() { Instrumenter.builder( otelTesting.getOpenTelemetry(), "test", request -> "test span") .setEnabled(false) - .newInstrumenter(); + .buildInstrumenter(); assertThat(instrumenter.shouldStart(Context.root(), "request")).isFalse(); } @@ -482,7 +488,8 @@ void instrumentationVersion_default() { Instrumenter.builder( otelTesting.getOpenTelemetry(), "test-instrumentation", name -> "span"); - Instrumenter, Map> instrumenter = builder.newInstrumenter(); + Instrumenter, Map> instrumenter = + builder.buildInstrumenter(); Context context = instrumenter.start(Context.root(), Collections.emptyMap()); assertThat(Span.fromContext(context)).isNotNull(); @@ -491,7 +498,7 @@ void instrumentationVersion_default() { // see the test-instrumentation.properties file InstrumentationScopeInfo expectedLibraryInfo = - InstrumentationScopeInfo.create("test-instrumentation", "1.2.3", /* schemaUrl= */ null); + InstrumentationScopeInfo.builder("test-instrumentation").setVersion("1.2.3").build(); otelTesting .assertTraces() @@ -507,7 +514,7 @@ void instrumentationVersion_custom() { Instrumenter., Map>builder( otelTesting.getOpenTelemetry(), "test", name -> "span") .setInstrumentationVersion("1.0") - .newInstrumenter(); + .buildInstrumenter(); Context context = instrumenter.start(Context.root(), Collections.emptyMap()); assertThat(Span.fromContext(context)).isNotNull(); @@ -522,8 +529,9 @@ void instrumentationVersion_custom() { span -> span.hasName("span") .hasInstrumentationScopeInfo( - InstrumentationScopeInfo.create( - "test", "1.0", /* schemaUrl= */ null)))); + InstrumentationScopeInfo.builder("test") + .setVersion("1.0") + .build()))); } @Test @@ -532,7 +540,7 @@ void schemaUrl() { Instrumenter., Map>builder( otelTesting.getOpenTelemetry(), "test", name -> "span") .setSchemaUrl("https://opentelemetry.io/schemas/1.0.0") - .newInstrumenter(); + .buildInstrumenter(); Context context = instrumenter.start(Context.root(), Collections.emptyMap()); assertThat(Span.fromContext(context)).isNotNull(); @@ -540,7 +548,9 @@ void schemaUrl() { instrumenter.end(context, Collections.emptyMap(), Collections.emptyMap(), null); InstrumentationScopeInfo expectedLibraryInfo = - InstrumentationScopeInfo.create("test", null, "https://opentelemetry.io/schemas/1.0.0"); + InstrumentationScopeInfo.builder("test") + .setSchemaUrl("https://opentelemetry.io/schemas/1.0.0") + .build(); otelTesting .assertTraces() .hasTracesSatisfyingExactly( @@ -559,12 +569,11 @@ void shouldRetrieveSpanKeysFromAttributesExtractors() { Instrumenter, Map> instrumenter = Instrumenter., Map>builder( otelTesting.getOpenTelemetry(), "test", unused -> "span") - .addAttributesExtractors( - new AttributesExtractor2(), - mockHttpClientAttributes, - mockNetClientAttributes, - mockDbClientAttributes) - .newInstrumenter(); + .addAttributesExtractor(new AttributesExtractor2()) + .addAttributesExtractor(mockHttpClientAttributes) + .addAttributesExtractor(mockNetClientAttributes) + .addAttributesExtractor(mockDbClientAttributes) + .buildInstrumenter(); Context context = instrumenter.start(Context.root(), REQUEST); diff --git a/instrumentation-api/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/PropagatorBasedSpanLinksExtractorTest.java b/instrumentation-api/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/PropagatorBasedSpanLinksExtractorTest.java index ac78b15a8adb..aad6c32c6726 100644 --- a/instrumentation-api/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/PropagatorBasedSpanLinksExtractorTest.java +++ b/instrumentation-api/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/PropagatorBasedSpanLinksExtractorTest.java @@ -17,6 +17,7 @@ import io.opentelemetry.context.Context; import io.opentelemetry.context.propagation.TextMapGetter; import io.opentelemetry.context.propagation.TextMapPropagator; +import io.opentelemetry.instrumentation.api.internal.PropagatorBasedSpanLinksExtractor; import java.util.Map; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -36,7 +37,7 @@ void shouldExtractSpanLink() { TextMapPropagator propagator = W3CTraceContextPropagator.getInstance(); SpanLinksExtractor> underTest = - SpanLinksExtractor.extractFromRequest(propagator, new MapGetter()); + new PropagatorBasedSpanLinksExtractor<>(propagator, new MapGetter()); Map request = singletonMap("traceparent", String.format("00-%s-%s-01", TRACE_ID, SPAN_ID)); diff --git a/instrumentation-appender-api-internal/build.gradle.kts b/instrumentation-appender-api-internal/build.gradle.kts deleted file mode 100644 index f682e71c0098..000000000000 --- a/instrumentation-appender-api-internal/build.gradle.kts +++ /dev/null @@ -1,13 +0,0 @@ -plugins { - id("otel.java-conventions") - id("otel.animalsniffer-conventions") - id("otel.jacoco-conventions") - id("otel.japicmp-conventions") - id("otel.publish-conventions") -} - -group = "io.opentelemetry.instrumentation" - -dependencies { - api("io.opentelemetry:opentelemetry-api") -} diff --git a/instrumentation-appender-api-internal/src/main/java/io/opentelemetry/instrumentation/api/appender/internal/LogBuilder.java b/instrumentation-appender-api-internal/src/main/java/io/opentelemetry/instrumentation/api/appender/internal/LogBuilder.java deleted file mode 100644 index 8f4fe4eb7fd0..000000000000 --- a/instrumentation-appender-api-internal/src/main/java/io/opentelemetry/instrumentation/api/appender/internal/LogBuilder.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.instrumentation.api.appender.internal; - -import io.opentelemetry.api.common.Attributes; -import io.opentelemetry.context.Context; -import java.time.Instant; -import java.util.concurrent.TimeUnit; - -/** - * Used to construct and emit logs from a {@link LogEmitter}. - * - *

Obtain a {@link LogBuilder} via {@link LogEmitter#logBuilder()}, add properties using the - * setters, and emit the log by calling {@link #emit()}. - * - *

This class is internal and is hence not for public use. Its APIs are unstable and can change - * at any time. - */ -public interface LogBuilder { - - /** Set the epoch timestamp using the timestamp and unit. */ - LogBuilder setEpoch(long timestamp, TimeUnit unit); - - /** Set the epoch timestamp using the instant. */ - LogBuilder setEpoch(Instant instant); - - /** Set the context. */ - LogBuilder setContext(Context context); - - /** Set the severity. */ - LogBuilder setSeverity(Severity severity); - - /** Set the severity text. */ - LogBuilder setSeverityText(String severityText); - - /** Set the body string. */ - LogBuilder setBody(String body); - - /** Set the attributes. */ - LogBuilder setAttributes(Attributes attributes); - - /** Emit the log. */ - void emit(); -} diff --git a/instrumentation-appender-api-internal/src/main/java/io/opentelemetry/instrumentation/api/appender/internal/LogEmitter.java b/instrumentation-appender-api-internal/src/main/java/io/opentelemetry/instrumentation/api/appender/internal/LogEmitter.java deleted file mode 100644 index 1705f419f8a2..000000000000 --- a/instrumentation-appender-api-internal/src/main/java/io/opentelemetry/instrumentation/api/appender/internal/LogEmitter.java +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.instrumentation.api.appender.internal; - -import javax.annotation.concurrent.ThreadSafe; - -/** - * A {@link LogEmitter} is the entry point into a log pipeline. - * - *

Obtain a log builder via {@link #logBuilder()}, add properties using the setters, and emit it - * via {@link LogBuilder#emit()}. - * - *

This class is internal and is hence not for public use. Its APIs are unstable and can change - * at any time. - */ -@ThreadSafe -public interface LogEmitter { - - /** Return a new {@link LogBuilder} to emit a log. */ - LogBuilder logBuilder(); -} diff --git a/instrumentation-appender-api-internal/src/main/java/io/opentelemetry/instrumentation/api/appender/internal/LogEmitterBuilder.java b/instrumentation-appender-api-internal/src/main/java/io/opentelemetry/instrumentation/api/appender/internal/LogEmitterBuilder.java deleted file mode 100644 index 121a4aba7caa..000000000000 --- a/instrumentation-appender-api-internal/src/main/java/io/opentelemetry/instrumentation/api/appender/internal/LogEmitterBuilder.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.instrumentation.api.appender.internal; - -/** - * Builder class for creating {@link LogEmitter} instances. - * - *

This class is internal and is hence not for public use. Its APIs are unstable and can change - * at any time. - */ -public interface LogEmitterBuilder { - - /** - * Assign an OpenTelemetry schema URL to the resulting {@link LogEmitter}. - * - * @param schemaUrl the URL of the OpenTelemetry schema being used by this instrumentation library - * @return this - */ - LogEmitterBuilder setSchemaUrl(String schemaUrl); - - /** - * Assign a version to the instrumentation library that is using the resulting {@link LogEmitter}. - * - * @param instrumentationVersion the version of the instrumentation library - * @return this - */ - LogEmitterBuilder setInstrumentationVersion(String instrumentationVersion); - - /** - * Gets or creates a {@link LogEmitter} instance. - * - * @return a log emitter instance configured with the provided options - */ - LogEmitter build(); -} diff --git a/instrumentation-appender-api-internal/src/main/java/io/opentelemetry/instrumentation/api/appender/internal/LogEmitterProvider.java b/instrumentation-appender-api-internal/src/main/java/io/opentelemetry/instrumentation/api/appender/internal/LogEmitterProvider.java deleted file mode 100644 index 9b97756857c2..000000000000 --- a/instrumentation-appender-api-internal/src/main/java/io/opentelemetry/instrumentation/api/appender/internal/LogEmitterProvider.java +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.instrumentation.api.appender.internal; - -/** - * This class is internal and is hence not for public use. Its APIs are unstable and can change at - * any time. - */ -public interface LogEmitterProvider { - - /** - * Creates a {@link LogEmitterBuilder} instance. - * - * @param instrumentationName the name of the instrumentation library - * @return a log emitter builder instance - */ - LogEmitterBuilder logEmitterBuilder(String instrumentationName); -} diff --git a/instrumentation-appender-api-internal/src/main/java/io/opentelemetry/instrumentation/api/appender/internal/LogEmitterProviderHolder.java b/instrumentation-appender-api-internal/src/main/java/io/opentelemetry/instrumentation/api/appender/internal/LogEmitterProviderHolder.java deleted file mode 100644 index f5a0a397b797..000000000000 --- a/instrumentation-appender-api-internal/src/main/java/io/opentelemetry/instrumentation/api/appender/internal/LogEmitterProviderHolder.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.instrumentation.api.appender.internal; - -import java.util.concurrent.atomic.AtomicReference; -import javax.annotation.Nullable; - -/** - * This class is internal and is hence not for public use. Its APIs are unstable and can change at - * any time. - */ -public final class LogEmitterProviderHolder { - - private final AtomicReference instance = - new AtomicReference<>(NoopLogEmitterProvider.INSTANCE); - - @Nullable private volatile Throwable setInstanceCaller; - - /** Returns the registered {@link LogEmitterProvider}. */ - public LogEmitterProvider get() { - return instance.get(); - } - - /** - * Sets the {@link LogEmitterProvider} that should be the instance. Future calls to {@link #get()} - * will return the provided {@link LogEmitterProvider} instance. This should be called once as - * early as possible in your application initialization logic, often in a {@code static} block in - * your main class. It should only be called once - an attempt to call it a second time will - * result in an error. If trying to set the instance {@link LogEmitterProvider} multiple times in - * tests, use {@link LogEmitterProviderHolder#resetForTest()} between them. - */ - public void set(LogEmitterProvider logEmitterProvider) { - boolean changed = instance.compareAndSet(NoopLogEmitterProvider.INSTANCE, logEmitterProvider); - if (!changed && (logEmitterProvider != NoopLogEmitterProvider.INSTANCE)) { - throw new IllegalStateException( - "LogEmitterProviderHolder.set has already been called. LogEmitterProviderHolder.set " - + "must be called only once before any calls to LogEmitterProviderHolder.get. " - + "Previous invocation set to cause of this exception.", - setInstanceCaller); - } - setInstanceCaller = new Throwable(); - } - - /** - * Unsets the {@link LogEmitterProvider}. This is only meant to be used from tests which need to - * reconfigure {@link LogEmitterProvider}. - */ - public void resetForTest() { - instance.set(NoopLogEmitterProvider.INSTANCE); - } -} diff --git a/instrumentation-appender-api-internal/src/main/java/io/opentelemetry/instrumentation/api/appender/internal/NoopLogBuilder.java b/instrumentation-appender-api-internal/src/main/java/io/opentelemetry/instrumentation/api/appender/internal/NoopLogBuilder.java deleted file mode 100644 index 6e51ac4496dc..000000000000 --- a/instrumentation-appender-api-internal/src/main/java/io/opentelemetry/instrumentation/api/appender/internal/NoopLogBuilder.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.instrumentation.api.appender.internal; - -import io.opentelemetry.api.common.Attributes; -import io.opentelemetry.context.Context; -import java.time.Instant; -import java.util.concurrent.TimeUnit; - -final class NoopLogBuilder implements LogBuilder { - - static final LogBuilder INSTANCE = new NoopLogBuilder(); - - @Override - public LogBuilder setEpoch(long timestamp, TimeUnit unit) { - return this; - } - - @Override - public LogBuilder setEpoch(Instant instant) { - return this; - } - - @Override - public LogBuilder setContext(Context context) { - return this; - } - - @Override - public LogBuilder setSeverity(Severity severity) { - return this; - } - - @Override - public LogBuilder setSeverityText(String severityText) { - return this; - } - - @Override - public LogBuilder setBody(String body) { - return this; - } - - @Override - public LogBuilder setAttributes(Attributes attributes) { - return this; - } - - @Override - public void emit() {} -} diff --git a/instrumentation-appender-api-internal/src/main/java/io/opentelemetry/instrumentation/api/appender/internal/NoopLogEmitter.java b/instrumentation-appender-api-internal/src/main/java/io/opentelemetry/instrumentation/api/appender/internal/NoopLogEmitter.java deleted file mode 100644 index 64ea4801ebab..000000000000 --- a/instrumentation-appender-api-internal/src/main/java/io/opentelemetry/instrumentation/api/appender/internal/NoopLogEmitter.java +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.instrumentation.api.appender.internal; - -final class NoopLogEmitter implements LogEmitter { - - static final LogEmitter INSTANCE = new NoopLogEmitter(); - - @Override - public LogBuilder logBuilder() { - return NoopLogBuilder.INSTANCE; - } -} diff --git a/instrumentation-appender-api-internal/src/main/java/io/opentelemetry/instrumentation/api/appender/internal/NoopLogEmitterBuilder.java b/instrumentation-appender-api-internal/src/main/java/io/opentelemetry/instrumentation/api/appender/internal/NoopLogEmitterBuilder.java deleted file mode 100644 index 10e1a23759a8..000000000000 --- a/instrumentation-appender-api-internal/src/main/java/io/opentelemetry/instrumentation/api/appender/internal/NoopLogEmitterBuilder.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.instrumentation.api.appender.internal; - -final class NoopLogEmitterBuilder implements LogEmitterBuilder { - - static final LogEmitterBuilder INSTANCE = new NoopLogEmitterBuilder(); - - @Override - public LogEmitterBuilder setSchemaUrl(String schemaUrl) { - return this; - } - - @Override - public LogEmitterBuilder setInstrumentationVersion(String instrumentationVersion) { - return this; - } - - @Override - public LogEmitter build() { - return NoopLogEmitter.INSTANCE; - } -} diff --git a/instrumentation-appender-api-internal/src/main/java/io/opentelemetry/instrumentation/api/appender/internal/NoopLogEmitterProvider.java b/instrumentation-appender-api-internal/src/main/java/io/opentelemetry/instrumentation/api/appender/internal/NoopLogEmitterProvider.java deleted file mode 100644 index e022b753a793..000000000000 --- a/instrumentation-appender-api-internal/src/main/java/io/opentelemetry/instrumentation/api/appender/internal/NoopLogEmitterProvider.java +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.instrumentation.api.appender.internal; - -final class NoopLogEmitterProvider implements LogEmitterProvider { - - static final NoopLogEmitterProvider INSTANCE = new NoopLogEmitterProvider(); - - @Override - public LogEmitterBuilder logEmitterBuilder(String instrumentationName) { - return NoopLogEmitterBuilder.INSTANCE; - } -} diff --git a/instrumentation-appender-api-internal/src/main/java/io/opentelemetry/instrumentation/api/appender/internal/Severity.java b/instrumentation-appender-api-internal/src/main/java/io/opentelemetry/instrumentation/api/appender/internal/Severity.java deleted file mode 100644 index 4c71eafb9e35..000000000000 --- a/instrumentation-appender-api-internal/src/main/java/io/opentelemetry/instrumentation/api/appender/internal/Severity.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.instrumentation.api.appender.internal; - -/** - * This class is internal and is hence not for public use. Its APIs are unstable and can change at - * any time. - */ -public enum Severity { - UNDEFINED_SEVERITY_NUMBER(0), - TRACE(1), - TRACE2(2), - TRACE3(3), - TRACE4(4), - DEBUG(5), - DEBUG2(6), - DEBUG3(7), - DEBUG4(8), - INFO(9), - INFO2(10), - INFO3(11), - INFO4(12), - WARN(13), - WARN2(14), - WARN3(15), - WARN4(16), - ERROR(17), - ERROR2(18), - ERROR3(19), - ERROR4(20), - FATAL(21), - FATAL2(22), - FATAL3(23), - FATAL4(24); - - private final int severityNumber; - - Severity(int severityNumber) { - this.severityNumber = severityNumber; - } - - public int getSeverityNumber() { - return severityNumber; - } -} diff --git a/instrumentation-appender-api-internal/src/test/java/io/opentelemetry/instrumentation/api/appender/internal/LogEmitterProviderHolderTest.java b/instrumentation-appender-api-internal/src/test/java/io/opentelemetry/instrumentation/api/appender/internal/LogEmitterProviderHolderTest.java deleted file mode 100644 index f9e7ed03e137..000000000000 --- a/instrumentation-appender-api-internal/src/test/java/io/opentelemetry/instrumentation/api/appender/internal/LogEmitterProviderHolderTest.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.instrumentation.api.appender.internal; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.junit.jupiter.api.Assertions.assertSame; -import static org.mockito.Mockito.mock; - -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -class LogEmitterProviderHolderTest { - - private static final LogEmitterProviderHolder holder = new LogEmitterProviderHolder(); - - @BeforeAll - static void beforeClass() { - holder.resetForTest(); - } - - @AfterEach - void after() { - holder.resetForTest(); - } - - @Test - void testGlobalBeforeSet() { - assertThat(holder.get()).isSameAs(NoopLogEmitterProvider.INSTANCE); - } - - @Test - void setThenSet() { - setLogEmitterProvider(); - assertThatThrownBy(() -> holder.set(mock(LogEmitterProvider.class))) - .isInstanceOf(IllegalStateException.class) - .hasMessageContaining("LogEmitterProviderHolder.set has already been called") - .hasStackTraceContaining("setLogEmitterProvider"); - } - - @Test - void getThenSet() { - LogEmitterProvider existingProvider = holder.get(); - assertSame(existingProvider, NoopLogEmitterProvider.INSTANCE); - LogEmitterProvider newProvider = mock(LogEmitterProvider.class); - holder.set(newProvider); - assertSame(newProvider, holder.get()); - } - - @Test - void okToSetNoopMultipleTimes() { - holder.set(NoopLogEmitterProvider.INSTANCE); - holder.set(NoopLogEmitterProvider.INSTANCE); - holder.set(NoopLogEmitterProvider.INSTANCE); - holder.set(NoopLogEmitterProvider.INSTANCE); - // pass - } - - private static void setLogEmitterProvider() { - holder.set(mock(LogEmitterProvider.class)); - } -} diff --git a/instrumentation-appender-sdk-internal/build.gradle.kts b/instrumentation-appender-sdk-internal/build.gradle.kts deleted file mode 100644 index fe8c116f7b48..000000000000 --- a/instrumentation-appender-sdk-internal/build.gradle.kts +++ /dev/null @@ -1,15 +0,0 @@ -plugins { - id("otel.java-conventions") - id("otel.animalsniffer-conventions") - id("otel.jacoco-conventions") - id("otel.japicmp-conventions") - id("otel.publish-conventions") -} - -group = "io.opentelemetry.instrumentation" - -dependencies { - api(project(":instrumentation-appender-api-internal")) - - api("io.opentelemetry:opentelemetry-sdk-logs") -} diff --git a/instrumentation-appender-sdk-internal/src/main/java/io/opentelemetry/instrumentation/sdk/appender/internal/DelegatingLogBuilder.java b/instrumentation-appender-sdk-internal/src/main/java/io/opentelemetry/instrumentation/sdk/appender/internal/DelegatingLogBuilder.java deleted file mode 100644 index e94512fa9f4c..000000000000 --- a/instrumentation-appender-sdk-internal/src/main/java/io/opentelemetry/instrumentation/sdk/appender/internal/DelegatingLogBuilder.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.instrumentation.sdk.appender.internal; - -import io.opentelemetry.api.common.Attributes; -import io.opentelemetry.context.Context; -import io.opentelemetry.instrumentation.api.appender.internal.LogBuilder; -import io.opentelemetry.instrumentation.api.appender.internal.Severity; -import java.time.Instant; -import java.util.concurrent.TimeUnit; - -final class DelegatingLogBuilder implements LogBuilder { - - private final io.opentelemetry.sdk.logs.LogBuilder delegate; - - DelegatingLogBuilder(io.opentelemetry.sdk.logs.LogBuilder delegate) { - this.delegate = delegate; - } - - @Override - public LogBuilder setEpoch(long timestamp, TimeUnit unit) { - delegate.setEpoch(timestamp, unit); - return this; - } - - @Override - public LogBuilder setEpoch(Instant instant) { - delegate.setEpoch(instant); - return this; - } - - @Override - public LogBuilder setContext(Context context) { - delegate.setContext(context); - return this; - } - - @Override - public LogBuilder setSeverity(Severity severity) { - delegate.setSeverity(io.opentelemetry.sdk.logs.data.Severity.valueOf(severity.name())); - return this; - } - - @Override - public LogBuilder setSeverityText(String severityText) { - delegate.setSeverityText(severityText); - return this; - } - - @Override - public LogBuilder setBody(String body) { - delegate.setBody(body); - return this; - } - - @Override - public LogBuilder setAttributes(Attributes attributes) { - delegate.setAttributes(attributes); - return this; - } - - @Override - public void emit() { - delegate.emit(); - } -} diff --git a/instrumentation-appender-sdk-internal/src/main/java/io/opentelemetry/instrumentation/sdk/appender/internal/DelegatingLogEmitter.java b/instrumentation-appender-sdk-internal/src/main/java/io/opentelemetry/instrumentation/sdk/appender/internal/DelegatingLogEmitter.java deleted file mode 100644 index 2f1a0adae9f7..000000000000 --- a/instrumentation-appender-sdk-internal/src/main/java/io/opentelemetry/instrumentation/sdk/appender/internal/DelegatingLogEmitter.java +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.instrumentation.sdk.appender.internal; - -import io.opentelemetry.instrumentation.api.appender.internal.LogBuilder; -import io.opentelemetry.instrumentation.api.appender.internal.LogEmitter; - -final class DelegatingLogEmitter implements LogEmitter { - - private final io.opentelemetry.sdk.logs.LogEmitter delegate; - - DelegatingLogEmitter(io.opentelemetry.sdk.logs.LogEmitter delegate) { - this.delegate = delegate; - } - - @Override - public LogBuilder logBuilder() { - return new DelegatingLogBuilder(delegate.logBuilder()); - } -} diff --git a/instrumentation-appender-sdk-internal/src/main/java/io/opentelemetry/instrumentation/sdk/appender/internal/DelegatingLogEmitterBuilder.java b/instrumentation-appender-sdk-internal/src/main/java/io/opentelemetry/instrumentation/sdk/appender/internal/DelegatingLogEmitterBuilder.java deleted file mode 100644 index 7df9c1558141..000000000000 --- a/instrumentation-appender-sdk-internal/src/main/java/io/opentelemetry/instrumentation/sdk/appender/internal/DelegatingLogEmitterBuilder.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.instrumentation.sdk.appender.internal; - -import io.opentelemetry.instrumentation.api.appender.internal.LogEmitter; -import io.opentelemetry.instrumentation.api.appender.internal.LogEmitterBuilder; - -final class DelegatingLogEmitterBuilder implements LogEmitterBuilder { - - private final io.opentelemetry.sdk.logs.LogEmitterBuilder delegate; - - DelegatingLogEmitterBuilder(io.opentelemetry.sdk.logs.LogEmitterBuilder delegate) { - this.delegate = delegate; - } - - @Override - public LogEmitterBuilder setSchemaUrl(String schemaUrl) { - delegate.setSchemaUrl(schemaUrl); - return this; - } - - @Override - public LogEmitterBuilder setInstrumentationVersion(String instrumentationVersion) { - delegate.setInstrumentationVersion(instrumentationVersion); - return this; - } - - @Override - public LogEmitter build() { - return new DelegatingLogEmitter(delegate.build()); - } -} diff --git a/instrumentation-appender-sdk-internal/src/main/java/io/opentelemetry/instrumentation/sdk/appender/internal/DelegatingLogEmitterProvider.java b/instrumentation-appender-sdk-internal/src/main/java/io/opentelemetry/instrumentation/sdk/appender/internal/DelegatingLogEmitterProvider.java deleted file mode 100644 index 88e8a74a7a89..000000000000 --- a/instrumentation-appender-sdk-internal/src/main/java/io/opentelemetry/instrumentation/sdk/appender/internal/DelegatingLogEmitterProvider.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.instrumentation.sdk.appender.internal; - -import io.opentelemetry.instrumentation.api.appender.internal.LogEmitterBuilder; -import io.opentelemetry.instrumentation.api.appender.internal.LogEmitterProvider; -import io.opentelemetry.sdk.logs.SdkLogEmitterProvider; - -/** - * This class is internal and is hence not for public use. Its APIs are unstable and can change at - * any time. - */ -public final class DelegatingLogEmitterProvider implements LogEmitterProvider { - - private final SdkLogEmitterProvider delegate; - - public static DelegatingLogEmitterProvider from(SdkLogEmitterProvider delegate) { - return new DelegatingLogEmitterProvider(delegate); - } - - private DelegatingLogEmitterProvider(SdkLogEmitterProvider delegate) { - this.delegate = delegate; - } - - @Override - public LogEmitterBuilder logEmitterBuilder(String instrumentationName) { - return new DelegatingLogEmitterBuilder(delegate.logEmitterBuilder(instrumentationName)); - } -} diff --git a/instrumentation/akka/akka-actor-2.5/javaagent/build.gradle.kts b/instrumentation/akka/akka-actor-2.3/javaagent/build.gradle.kts similarity index 66% rename from instrumentation/akka/akka-actor-2.5/javaagent/build.gradle.kts rename to instrumentation/akka/akka-actor-2.3/javaagent/build.gradle.kts index 72b6bacd2c14..8c33b181a4be 100644 --- a/instrumentation/akka/akka-actor-2.5/javaagent/build.gradle.kts +++ b/instrumentation/akka/akka-actor-2.3/javaagent/build.gradle.kts @@ -7,25 +7,28 @@ muzzle { pass { group.set("com.typesafe.akka") module.set("akka-actor_2.11") - versions.set("[2.5.0,)") + versions.set("[2.3,)") + assertInverse.set(true) } pass { group.set("com.typesafe.akka") module.set("akka-actor_2.12") - versions.set("[2.5.0,)") + versions.set("[2.3,)") + assertInverse.set(true) } pass { group.set("com.typesafe.akka") module.set("akka-actor_2.13") - versions.set("(,)") + versions.set("[2.3,)") + assertInverse.set(true) } } dependencies { bootstrap(project(":instrumentation:executors:bootstrap")) - compileOnly("com.typesafe.akka:akka-actor_2.11:2.5.0") - testImplementation("com.typesafe.akka:akka-actor_2.11:2.5.0") + compileOnly("com.typesafe.akka:akka-actor_2.11:2.3.2") // first version in maven central + testImplementation("com.typesafe.akka:akka-actor_2.11:2.3.2") // first version in maven central latestDepTestLibrary("com.typesafe.akka:akka-actor_2.13:+") } diff --git a/instrumentation/akka/akka-actor-2.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/akkaactor/AkkaActorCellInstrumentation.java b/instrumentation/akka/akka-actor-2.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/akkaactor/AkkaActorCellInstrumentation.java similarity index 100% rename from instrumentation/akka/akka-actor-2.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/akkaactor/AkkaActorCellInstrumentation.java rename to instrumentation/akka/akka-actor-2.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/akkaactor/AkkaActorCellInstrumentation.java diff --git a/instrumentation/akka/akka-actor-2.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/akkaactor/AkkaActorInstrumentationModule.java b/instrumentation/akka/akka-actor-2.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/akkaactor/AkkaActorInstrumentationModule.java similarity index 95% rename from instrumentation/akka/akka-actor-2.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/akkaactor/AkkaActorInstrumentationModule.java rename to instrumentation/akka/akka-actor-2.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/akkaactor/AkkaActorInstrumentationModule.java index b58a2be9050c..0bae483ce168 100644 --- a/instrumentation/akka/akka-actor-2.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/akkaactor/AkkaActorInstrumentationModule.java +++ b/instrumentation/akka/akka-actor-2.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/akkaactor/AkkaActorInstrumentationModule.java @@ -15,7 +15,7 @@ @AutoService(InstrumentationModule.class) public class AkkaActorInstrumentationModule extends InstrumentationModule { public AkkaActorInstrumentationModule() { - super("akka-actor", "akka-actor-2.5"); + super("akka-actor", "akka-actor-2.3"); } @Override diff --git a/instrumentation/akka/akka-actor-2.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/akkaactor/AkkaDefaultSystemMessageQueueInstrumentation.java b/instrumentation/akka/akka-actor-2.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/akkaactor/AkkaDefaultSystemMessageQueueInstrumentation.java similarity index 100% rename from instrumentation/akka/akka-actor-2.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/akkaactor/AkkaDefaultSystemMessageQueueInstrumentation.java rename to instrumentation/akka/akka-actor-2.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/akkaactor/AkkaDefaultSystemMessageQueueInstrumentation.java diff --git a/instrumentation/akka/akka-actor-2.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/akkaactor/AkkaDispatcherInstrumentation.java b/instrumentation/akka/akka-actor-2.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/akkaactor/AkkaDispatcherInstrumentation.java similarity index 100% rename from instrumentation/akka/akka-actor-2.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/akkaactor/AkkaDispatcherInstrumentation.java rename to instrumentation/akka/akka-actor-2.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/akkaactor/AkkaDispatcherInstrumentation.java diff --git a/instrumentation/akka/akka-actor-2.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/akkaactor/AkkaIgnoredTypesConfigurer.java b/instrumentation/akka/akka-actor-2.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/akkaactor/AkkaIgnoredTypesConfigurer.java similarity index 91% rename from instrumentation/akka/akka-actor-2.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/akkaactor/AkkaIgnoredTypesConfigurer.java rename to instrumentation/akka/akka-actor-2.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/akkaactor/AkkaIgnoredTypesConfigurer.java index b25a5834e3ba..37bace28bb6c 100644 --- a/instrumentation/akka/akka-actor-2.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/akkaactor/AkkaIgnoredTypesConfigurer.java +++ b/instrumentation/akka/akka-actor-2.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/akkaactor/AkkaIgnoredTypesConfigurer.java @@ -14,7 +14,7 @@ public class AkkaIgnoredTypesConfigurer implements IgnoredTypesConfigurer { @Override - public void configure(ConfigProperties config, IgnoredTypesBuilder builder) { + public void configure(IgnoredTypesBuilder builder, ConfigProperties config) { // This is a Mailbox created by akka.dispatch.Dispatcher#createMailbox. We must not add // a context to it as context should only be carried by individual envelopes in the queue // of this mailbox. diff --git a/instrumentation/akka/akka-actor-2.5/javaagent/src/test/scala/io/opentelemetry/instrumentation/akkaactor/AkkaActorTest.scala b/instrumentation/akka/akka-actor-2.3/javaagent/src/test/scala/io/opentelemetry/instrumentation/akkaactor/AkkaActorTest.scala similarity index 97% rename from instrumentation/akka/akka-actor-2.5/javaagent/src/test/scala/io/opentelemetry/instrumentation/akkaactor/AkkaActorTest.scala rename to instrumentation/akka/akka-actor-2.3/javaagent/src/test/scala/io/opentelemetry/instrumentation/akkaactor/AkkaActorTest.scala index 1264367e9d97..50137cc3d9a6 100644 --- a/instrumentation/akka/akka-actor-2.5/javaagent/src/test/scala/io/opentelemetry/instrumentation/akkaactor/AkkaActorTest.scala +++ b/instrumentation/akka/akka-actor-2.3/javaagent/src/test/scala/io/opentelemetry/instrumentation/akkaactor/AkkaActorTest.scala @@ -5,7 +5,6 @@ package io.opentelemetry.instrumentation.akkaactor -import collection.JavaConverters._ import io.opentelemetry.api.common.Attributes import io.opentelemetry.instrumentation.testing.junit.{ AgentInstrumentationExtension, @@ -18,7 +17,7 @@ import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.ValueSource import java.util.function.Consumer -import scala.compat.java8.FunctionConverters._ +import scala.collection.JavaConverters._ @TestInstance(TestInstance.Lifecycle.PER_CLASS) class AkkaActorTest { diff --git a/instrumentation/akka/akka-actor-2.5/javaagent/src/test/scala/io/opentelemetry/instrumentation/akkaactor/AkkaActors.scala b/instrumentation/akka/akka-actor-2.3/javaagent/src/test/scala/io/opentelemetry/instrumentation/akkaactor/AkkaActors.scala similarity index 100% rename from instrumentation/akka/akka-actor-2.5/javaagent/src/test/scala/io/opentelemetry/instrumentation/akkaactor/AkkaActors.scala rename to instrumentation/akka/akka-actor-2.3/javaagent/src/test/scala/io/opentelemetry/instrumentation/akkaactor/AkkaActors.scala diff --git a/instrumentation/akka/akka-actor-fork-join-2.5/javaagent/build.gradle.kts b/instrumentation/akka/akka-actor-fork-join-2.5/javaagent/build.gradle.kts index 61a2060762b5..501e31ce83d5 100644 --- a/instrumentation/akka/akka-actor-fork-join-2.5/javaagent/build.gradle.kts +++ b/instrumentation/akka/akka-actor-fork-join-2.5/javaagent/build.gradle.kts @@ -7,7 +7,8 @@ muzzle { pass { group.set("com.typesafe.akka") module.set("akka-actor_2.11") - versions.set("[2.5.0,)") + versions.set("[2.5,)") + assertInverse.set(true) } } 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/akkaactor/AkkaActorForkJoinInstrumentationModule.java index fa7552ae9cd0..58942ec5f380 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/akkaactor/AkkaActorForkJoinInstrumentationModule.java @@ -15,7 +15,7 @@ @AutoService(InstrumentationModule.class) public class AkkaActorForkJoinInstrumentationModule extends InstrumentationModule { public AkkaActorForkJoinInstrumentationModule() { - super("akka-actor", "akka-actor-fork-join", "akka-actor-2.5", "akka-actor-fork-join-2.5"); + super("akka-actor", "akka-actor-fork-join", "akka-actor-fork-join-2.5"); } @Override diff --git a/instrumentation/akka/akka-http-10.0/javaagent/build.gradle.kts b/instrumentation/akka/akka-http-10.0/javaagent/build.gradle.kts index 2cf50046b495..1d71c79b190e 100644 --- a/instrumentation/akka/akka-http-10.0/javaagent/build.gradle.kts +++ b/instrumentation/akka/akka-http-10.0/javaagent/build.gradle.kts @@ -7,38 +7,26 @@ muzzle { pass { group.set("com.typesafe.akka") module.set("akka-http_2.11") - versions.set("[10.0.0,10.1.0)") + versions.set("[10,)") + assertInverse.set(true) // later versions of akka-http expect streams to be provided - extraDependency("com.typesafe.akka:akka-stream_2.11:2.4.14") + extraDependency("com.typesafe.akka:akka-stream_2.11:2.5.32") } pass { group.set("com.typesafe.akka") module.set("akka-http_2.12") - versions.set("[10.0.0,10.1.0)") + versions.set("[10,)") + assertInverse.set(true) // later versions of akka-http expect streams to be provided - extraDependency("com.typesafe.akka:akka-stream_2.12:2.4.14") + extraDependency("com.typesafe.akka:akka-stream_2.12:2.5.32") } - pass { - group.set("com.typesafe.akka") - module.set("akka-http_2.11") - versions.set("[10.1.0,)") - // later versions of akka-http expect streams to be provided - extraDependency("com.typesafe.akka:akka-stream_2.11:2.5.11") - } - pass { - group.set("com.typesafe.akka") - module.set("akka-http_2.12") - versions.set("[10.1.0,)") - // later versions of akka-http expect streams to be provided - extraDependency("com.typesafe.akka:akka-stream_2.12:2.5.11") - } - // There is no akka-http 10.0.x series for scala 2.13 pass { group.set("com.typesafe.akka") module.set("akka-http_2.13") - versions.set("[10.1.8,)") + versions.set("[10,)") + assertInverse.set(true) // later versions of akka-http expect streams to be provided - extraDependency("com.typesafe.akka:akka-stream_2.13:2.5.23") + extraDependency("com.typesafe.akka:akka-stream_2.13:2.5.32") } } @@ -46,9 +34,11 @@ dependencies { library("com.typesafe.akka:akka-http_2.11:10.0.0") library("com.typesafe.akka:akka-stream_2.11:2.4.14") + implementation("org.json:json:20220320") + // these instrumentations are not needed for the tests to pass // they are here to test for context leaks - testInstrumentation(project(":instrumentation:akka:akka-actor-2.5:javaagent")) + testInstrumentation(project(":instrumentation:akka:akka-actor-2.3:javaagent")) testInstrumentation(project(":instrumentation:akka:akka-actor-fork-join-2.5:javaagent")) latestDepTestLibrary("com.typesafe.akka:akka-http_2.13:+") diff --git a/instrumentation/akka/akka-http-10.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/akkahttp/client/AkkaHttpClientAttributesGetter.java b/instrumentation/akka/akka-http-10.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/akkahttp/client/AkkaHttpClientAttributesGetter.java index a0a981932b2d..0aa413285604 100644 --- a/instrumentation/akka/akka-http-10.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/akkahttp/client/AkkaHttpClientAttributesGetter.java +++ b/instrumentation/akka/akka-http-10.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/akkahttp/client/AkkaHttpClientAttributesGetter.java @@ -36,36 +36,11 @@ public List requestHeader(HttpRequest httpRequest, String name) { } @Override - @Nullable - public Long requestContentLength(HttpRequest httpRequest, @Nullable HttpResponse httpResponse) { - return null; - } - - @Override - @Nullable - public Long requestContentLengthUncompressed( - HttpRequest httpRequest, @Nullable HttpResponse httpResponse) { - return null; - } - - @Override - public Integer statusCode(HttpRequest httpRequest, HttpResponse httpResponse) { + public Integer statusCode( + HttpRequest httpRequest, HttpResponse httpResponse, @Nullable Throwable error) { return httpResponse.status().intValue(); } - @Override - @Nullable - public Long responseContentLength(HttpRequest httpRequest, HttpResponse httpResponse) { - return null; - } - - @Override - @Nullable - public Long responseContentLengthUncompressed( - HttpRequest httpRequest, HttpResponse httpResponse) { - return null; - } - @Override public List responseHeader( HttpRequest httpRequest, HttpResponse httpResponse, String name) { diff --git a/instrumentation/akka/akka-http-10.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/akkahttp/client/AkkaHttpClientSingletons.java b/instrumentation/akka/akka-http-10.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/akkahttp/client/AkkaHttpClientSingletons.java index c4917f362553..b36fb5ec6219 100644 --- a/instrumentation/akka/akka-http-10.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/akkahttp/client/AkkaHttpClientSingletons.java +++ b/instrumentation/akka/akka-http-10.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/akkahttp/client/AkkaHttpClientSingletons.java @@ -9,13 +9,14 @@ import akka.http.scaladsl.model.HttpResponse; import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; -import io.opentelemetry.instrumentation.api.instrumenter.PeerServiceAttributesExtractor; import io.opentelemetry.instrumentation.api.instrumenter.SpanKindExtractor; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpClientAttributesExtractor; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpClientMetrics; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanNameExtractor; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanStatusExtractor; import io.opentelemetry.instrumentation.api.instrumenter.net.NetClientAttributesExtractor; +import io.opentelemetry.instrumentation.api.instrumenter.net.PeerServiceAttributesExtractor; +import io.opentelemetry.javaagent.bootstrap.internal.CommonConfig; import io.opentelemetry.javaagent.instrumentation.akkahttp.AkkaHttpUtil; public class AkkaHttpClientSingletons { @@ -33,11 +34,17 @@ public class AkkaHttpClientSingletons { AkkaHttpUtil.instrumentationName(), HttpSpanNameExtractor.create(httpAttributesGetter)) .setSpanStatusExtractor(HttpSpanStatusExtractor.create(httpAttributesGetter)) - .addAttributesExtractor(HttpClientAttributesExtractor.create(httpAttributesGetter)) + .addAttributesExtractor( + HttpClientAttributesExtractor.builder(httpAttributesGetter) + .setCapturedRequestHeaders(CommonConfig.get().getClientRequestHeaders()) + .setCapturedResponseHeaders(CommonConfig.get().getClientResponseHeaders()) + .build()) .addAttributesExtractor(NetClientAttributesExtractor.create(netAttributesGetter)) - .addAttributesExtractor(PeerServiceAttributesExtractor.create(netAttributesGetter)) + .addAttributesExtractor( + PeerServiceAttributesExtractor.create( + netAttributesGetter, CommonConfig.get().getPeerServiceMapping())) .addOperationMetrics(HttpClientMetrics.get()) - .newInstrumenter(SpanKindExtractor.alwaysClient()); + .buildInstrumenter(SpanKindExtractor.alwaysClient()); } public static Instrumenter instrumenter() { diff --git a/instrumentation/akka/akka-http-10.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/akkahttp/client/AkkaHttpNetAttributesGetter.java b/instrumentation/akka/akka-http-10.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/akkahttp/client/AkkaHttpNetAttributesGetter.java index d3614e0b20a2..25b143a8b725 100644 --- a/instrumentation/akka/akka-http-10.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/akkahttp/client/AkkaHttpNetAttributesGetter.java +++ b/instrumentation/akka/akka-http-10.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/akkahttp/client/AkkaHttpNetAttributesGetter.java @@ -19,18 +19,12 @@ public String transport(HttpRequest httpRequest, @Nullable HttpResponse httpResp } @Override - public String peerName(HttpRequest httpRequest, @Nullable HttpResponse httpResponse) { + public String peerName(HttpRequest httpRequest) { return httpRequest.uri().authority().host().address(); } @Override - public Integer peerPort(HttpRequest httpRequest, @Nullable HttpResponse httpResponse) { + public Integer peerPort(HttpRequest httpRequest) { return httpRequest.uri().authority().port(); } - - @Override - @Nullable - public String peerIp(HttpRequest httpRequest, @Nullable HttpResponse httpResponse) { - return null; - } } diff --git a/instrumentation/akka/akka-http-10.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/akkahttp/client/FutureWrapper.java b/instrumentation/akka/akka-http-10.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/akkahttp/client/FutureWrapper.java index 47e2ba30f273..7dfa341e4bd3 100644 --- a/instrumentation/akka/akka-http-10.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/akkahttp/client/FutureWrapper.java +++ b/instrumentation/akka/akka-http-10.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/akkahttp/client/FutureWrapper.java @@ -13,7 +13,8 @@ import scala.runtime.AbstractFunction1; import scala.util.Try; -public class FutureWrapper { +public final class FutureWrapper { + public static Future wrap( Future future, ExecutionContext executionContext, Context context) { Promise.DefaultPromise promise = new Promise.DefaultPromise<>(); @@ -31,4 +32,6 @@ public Object apply(Try result) { return promise; } + + private FutureWrapper() {} } diff --git a/instrumentation/akka/akka-http-10.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/akkahttp/server/AkkaHttpServerAttributesGetter.java b/instrumentation/akka/akka-http-10.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/akkahttp/server/AkkaHttpServerAttributesGetter.java index 5c24bd6b4f74..6b059e96683d 100644 --- a/instrumentation/akka/akka-http-10.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/akkahttp/server/AkkaHttpServerAttributesGetter.java +++ b/instrumentation/akka/akka-http-10.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/akkahttp/server/AkkaHttpServerAttributesGetter.java @@ -10,7 +10,11 @@ import io.opentelemetry.instrumentation.api.instrumenter.http.HttpServerAttributesGetter; import io.opentelemetry.javaagent.instrumentation.akkahttp.AkkaHttpUtil; import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import java.util.stream.StreamSupport; import javax.annotation.Nullable; +import org.json.JSONObject; import scala.Option; class AkkaHttpServerAttributesGetter @@ -28,37 +32,29 @@ public List requestHeader(HttpRequest request, String name) { @Override @Nullable - public Long requestContentLength(HttpRequest request, @Nullable HttpResponse httpResponse) { - return null; + public String requestHeaders(HttpRequest request, HttpResponse unused) { + return toJsonString( + StreamSupport.stream(request.getHeaders().spliterator(), false) + .collect(Collectors.toMap(akka.http.javadsl.model.HttpHeader::name, akka.http.javadsl.model.HttpHeader::value))); } @Override - @Nullable - public Long requestContentLengthUncompressed( - HttpRequest request, @Nullable HttpResponse httpResponse) { - return null; - } - - @Override - public Integer statusCode(HttpRequest request, HttpResponse httpResponse) { + public Integer statusCode( + HttpRequest request, HttpResponse httpResponse, @Nullable Throwable error) { return httpResponse.status().intValue(); } @Override - @Nullable - public Long responseContentLength(HttpRequest request, HttpResponse httpResponse) { - return null; + public List responseHeader(HttpRequest request, HttpResponse httpResponse, String name) { + return AkkaHttpUtil.responseHeader(httpResponse, name); } @Override @Nullable - public Long responseContentLengthUncompressed(HttpRequest request, HttpResponse httpResponse) { - return null; - } - - @Override - public List responseHeader(HttpRequest request, HttpResponse httpResponse, String name) { - return AkkaHttpUtil.responseHeader(httpResponse, name); + public String responseHeaders(HttpRequest unused, HttpResponse httpResponse) { + return toJsonString( + StreamSupport.stream(httpResponse.getHeaders().spliterator(), false) + .collect(Collectors.toMap(akka.http.javadsl.model.HttpHeader::name, akka.http.javadsl.model.HttpHeader::value))); } @Override @@ -87,9 +83,8 @@ public String scheme(HttpRequest request) { return request.uri().scheme(); } - @Override @Nullable - public String serverName(HttpRequest request) { - return null; + private static String toJsonString(Map m) { + return new JSONObject(m).toString(); } } diff --git a/instrumentation/akka/akka-http-10.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/akkahttp/server/AkkaHttpServerInstrumentationModule.java b/instrumentation/akka/akka-http-10.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/akkahttp/server/AkkaHttpServerInstrumentationModule.java index d489220b65a9..f3bcf6960afd 100644 --- a/instrumentation/akka/akka-http-10.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/akkahttp/server/AkkaHttpServerInstrumentationModule.java +++ b/instrumentation/akka/akka-http-10.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/akkahttp/server/AkkaHttpServerInstrumentationModule.java @@ -29,6 +29,11 @@ public AkkaHttpServerInstrumentationModule() { super("akka-http", "akka-http-server"); } + @Override + public boolean isHelperClass(String className) { + return className.startsWith("org.json"); + } + @Override public List typeInstrumentations() { return singletonList(new HttpExtServerInstrumentation()); diff --git a/instrumentation/akka/akka-http-10.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/akkahttp/server/AkkaHttpServerSingletons.java b/instrumentation/akka/akka-http-10.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/akkahttp/server/AkkaHttpServerSingletons.java index 2e368fa3b2f4..5058d6e1cac7 100644 --- a/instrumentation/akka/akka-http-10.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/akkahttp/server/AkkaHttpServerSingletons.java +++ b/instrumentation/akka/akka-http-10.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/akkahttp/server/AkkaHttpServerSingletons.java @@ -14,9 +14,10 @@ import io.opentelemetry.instrumentation.api.instrumenter.http.HttpServerMetrics; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanNameExtractor; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanStatusExtractor; +import io.opentelemetry.javaagent.bootstrap.internal.CommonConfig; import io.opentelemetry.javaagent.instrumentation.akkahttp.AkkaHttpUtil; -public class AkkaHttpServerSingletons { +public final class AkkaHttpServerSingletons { private static final Instrumenter INSTRUMENTER; @@ -28,10 +29,15 @@ public class AkkaHttpServerSingletons { AkkaHttpUtil.instrumentationName(), HttpSpanNameExtractor.create(httpAttributesGetter)) .setSpanStatusExtractor(HttpSpanStatusExtractor.create(httpAttributesGetter)) - .addAttributesExtractor(HttpServerAttributesExtractor.create(httpAttributesGetter)) + .addAttributesExtractor( + HttpServerAttributesExtractor.builder( + httpAttributesGetter, new AkkaNetServerAttributesGetter()) + .setCapturedRequestHeaders(CommonConfig.get().getServerRequestHeaders()) + .setCapturedResponseHeaders(CommonConfig.get().getServerResponseHeaders()) + .build()) .addOperationMetrics(HttpServerMetrics.get()) .addContextCustomizer(HttpRouteHolder.get()) - .newServerInstrumenter(AkkaHttpServerHeaders.INSTANCE); + .buildServerInstrumenter(AkkaHttpServerHeaders.INSTANCE); } public static Instrumenter instrumenter() { @@ -41,4 +47,6 @@ public static Instrumenter instrumenter() { public static HttpResponse errorResponse() { return (HttpResponse) HttpResponse.create().withStatus(500); } + + private AkkaHttpServerSingletons() {} } diff --git a/instrumentation/akka/akka-http-10.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/akkahttp/server/AkkaNetServerAttributesGetter.java b/instrumentation/akka/akka-http-10.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/akkahttp/server/AkkaNetServerAttributesGetter.java new file mode 100644 index 000000000000..6f26307ca79f --- /dev/null +++ b/instrumentation/akka/akka-http-10.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/akkahttp/server/AkkaNetServerAttributesGetter.java @@ -0,0 +1,32 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.akkahttp.server; + +import akka.http.scaladsl.model.HttpRequest; +import io.opentelemetry.instrumentation.api.instrumenter.net.NetServerAttributesGetter; +import javax.annotation.Nullable; + +// TODO (trask) capture net attributes? +class AkkaNetServerAttributesGetter implements NetServerAttributesGetter { + + @Nullable + @Override + public String transport(HttpRequest httpRequest) { + return null; + } + + @Nullable + @Override + public String hostName(HttpRequest httpRequest) { + return null; + } + + @Nullable + @Override + public Integer hostPort(HttpRequest httpRequest) { + return null; + } +} diff --git a/instrumentation/akka/akka-http-10.0/javaagent/src/test/scala/io/opentelemetry/javaagent/instrumentation/akkahttp/AbstractHttpServerInstrumentationTest.scala b/instrumentation/akka/akka-http-10.0/javaagent/src/test/scala/io/opentelemetry/javaagent/instrumentation/akkahttp/AbstractHttpServerInstrumentationTest.scala index 645e407ada5b..b163ee49ebbc 100644 --- a/instrumentation/akka/akka-http-10.0/javaagent/src/test/scala/io/opentelemetry/javaagent/instrumentation/akkahttp/AbstractHttpServerInstrumentationTest.scala +++ b/instrumentation/akka/akka-http-10.0/javaagent/src/test/scala/io/opentelemetry/javaagent/instrumentation/akkahttp/AbstractHttpServerInstrumentationTest.scala @@ -6,8 +6,6 @@ package io.opentelemetry.javaagent.instrumentation.akkahttp import io.opentelemetry.api.common.AttributeKey - -import collection.JavaConverters._ import io.opentelemetry.instrumentation.testing.junit.http.{ AbstractHttpServerTest, HttpServerTestOptions, @@ -25,6 +23,7 @@ abstract class AbstractHttpServerInstrumentationTest options: HttpServerTestOptions ): Unit = { options.setTestCaptureHttpHeaders(false) + options.setTestCaptureHttpHeadersAsJson(true) options.setHttpAttributes( new Function[ServerEndpoint, util.Set[AttributeKey[_]]] { override def apply(v1: ServerEndpoint): util.Set[AttributeKey[_]] = diff --git a/instrumentation/akka/akka-http-10.0/javaagent/src/test/scala/io/opentelemetry/javaagent/instrumentation/akkahttp/AkkaHttpTestAsyncWebServer.scala b/instrumentation/akka/akka-http-10.0/javaagent/src/test/scala/io/opentelemetry/javaagent/instrumentation/akkahttp/AkkaHttpTestAsyncWebServer.scala index 40eeea70a1cd..2d72696248c1 100644 --- a/instrumentation/akka/akka-http-10.0/javaagent/src/test/scala/io/opentelemetry/javaagent/instrumentation/akkahttp/AkkaHttpTestAsyncWebServer.scala +++ b/instrumentation/akka/akka-http-10.0/javaagent/src/test/scala/io/opentelemetry/javaagent/instrumentation/akkahttp/AkkaHttpTestAsyncWebServer.scala @@ -6,6 +6,7 @@ package io.opentelemetry.javaagent.instrumentation.akkahttp import akka.actor.ActorSystem +import akka.http.javadsl.model.headers.RawHeader import akka.http.scaladsl.Http import akka.http.scaladsl.Http.ServerBinding import akka.http.scaladsl.model.HttpMethods.GET @@ -45,6 +46,10 @@ object AkkaHttpTestAsyncWebServer { }) resp.withEntity("") case QUERY_PARAM => resp.withEntity(uri.queryString().orNull) + case CAPTURE_HEADERS_AS_JSON => + resp + .withHeaders(RawHeader.create("X-Test-Response", "xxx")) + .withEntity(CAPTURE_HEADERS_AS_JSON.getBody) case REDIRECT => resp.withHeaders(headers.Location(endpoint.getBody)) case ERROR => resp.withEntity(endpoint.getBody) diff --git a/instrumentation/akka/akka-http-10.0/javaagent/src/test/scala/io/opentelemetry/javaagent/instrumentation/akkahttp/AkkaHttpTestSyncWebServer.scala b/instrumentation/akka/akka-http-10.0/javaagent/src/test/scala/io/opentelemetry/javaagent/instrumentation/akkahttp/AkkaHttpTestSyncWebServer.scala index 0d8d2856c175..554fec1174e7 100644 --- a/instrumentation/akka/akka-http-10.0/javaagent/src/test/scala/io/opentelemetry/javaagent/instrumentation/akkahttp/AkkaHttpTestSyncWebServer.scala +++ b/instrumentation/akka/akka-http-10.0/javaagent/src/test/scala/io/opentelemetry/javaagent/instrumentation/akkahttp/AkkaHttpTestSyncWebServer.scala @@ -6,6 +6,7 @@ package io.opentelemetry.javaagent.instrumentation.akkahttp import akka.actor.ActorSystem +import akka.http.javadsl.model.headers.RawHeader import akka.http.scaladsl.Http import akka.http.scaladsl.Http.ServerBinding import akka.http.scaladsl.model.HttpMethods.GET @@ -42,6 +43,10 @@ object AkkaHttpTestSyncWebServer { }) resp.withEntity("") case QUERY_PARAM => resp.withEntity(uri.queryString().orNull) + case CAPTURE_HEADERS_AS_JSON => + resp + .withHeaders(RawHeader.create("X-Test-Response", "xxx")) + .withEntity(CAPTURE_HEADERS_AS_JSON.getBody) case REDIRECT => resp.withHeaders(headers.Location(endpoint.getBody)) case ERROR => resp.withEntity(endpoint.getBody) diff --git a/instrumentation/apache-camel-2.20/javaagent-unit-tests/build.gradle.kts b/instrumentation/apache-camel-2.20/javaagent-unit-tests/build.gradle.kts index f6a12eaa7a6f..21bc3e56e153 100644 --- a/instrumentation/apache-camel-2.20/javaagent-unit-tests/build.gradle.kts +++ b/instrumentation/apache-camel-2.20/javaagent-unit-tests/build.gradle.kts @@ -14,3 +14,9 @@ dependencies { testImplementation("io.opentelemetry:opentelemetry-extension-trace-propagators") testImplementation("io.opentelemetry:opentelemetry-extension-aws") } + +tasks { + test { + jvmArgs("-Dotel.instrumentation.common.db-statement-sanitizer.enabled=true") + } +} diff --git a/instrumentation/apache-camel-2.20/javaagent/build.gradle.kts b/instrumentation/apache-camel-2.20/javaagent/build.gradle.kts index 367831ff2ddb..4b0d3b77aebe 100644 --- a/instrumentation/apache-camel-2.20/javaagent/build.gradle.kts +++ b/instrumentation/apache-camel-2.20/javaagent/build.gradle.kts @@ -6,12 +6,12 @@ muzzle { pass { group.set("org.apache.camel") module.set("camel-core") - versions.set("[2.20.1,3)") + versions.set("[2.19,3)") + assertInverse.set(true) } } -val camelversion = "2.20.1" -val versions: Map by project +val camelversion = "2.20.1" // first version that the tests pass on dependencies { library("org.apache.camel:camel-core:$camelversion") @@ -42,7 +42,7 @@ dependencies { testImplementation("org.springframework.boot:spring-boot-starter-test:1.5.17.RELEASE") testImplementation("org.springframework.boot:spring-boot-starter:1.5.17.RELEASE") - testImplementation("org.spockframework:spock-spring:${versions["org.spockframework"]}") + testImplementation("org.spockframework:spock-spring") testImplementation("javax.xml.bind:jaxb-api:2.3.1") testImplementation("org.elasticmq:elasticmq-rest-sqs_2.12:1.0.0") @@ -67,5 +67,17 @@ tasks { // TODO: fix camel instrumentation so that it uses semantic attributes extractors jvmArgs("-Dotel.instrumentation.experimental.span-suppression-strategy=span-kind") + + // required on jdk17 + jvmArgs("--add-opens=java.base/java.lang=ALL-UNNAMED") + jvmArgs("-XX:+IgnoreUnrecognizedVMOptions") + } +} + +configurations.testRuntimeClasspath { + resolutionStrategy { + // requires old logback (and therefore also old slf4j) + force("ch.qos.logback:logback-classic:1.2.11") + force("org.slf4j:slf4j-api:1.7.36") } } diff --git a/instrumentation/apache-camel-2.20/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachecamel/CamelSingletons.java b/instrumentation/apache-camel-2.20/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachecamel/CamelSingletons.java index b0b7b32673b2..048914ce4644 100644 --- a/instrumentation/apache-camel-2.20/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachecamel/CamelSingletons.java +++ b/instrumentation/apache-camel-2.20/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachecamel/CamelSingletons.java @@ -74,7 +74,7 @@ public void onEnd( builder.addAttributesExtractor(attributesExtractor); builder.setSpanStatusExtractor(spanStatusExtractor); - INSTRUMENTER = builder.newInstrumenter(request -> request.getSpanKind()); + INSTRUMENTER = builder.buildInstrumenter(request -> request.getSpanKind()); } public static Instrumenter instrumenter() { diff --git a/instrumentation/apache-camel-2.20/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachecamel/decorators/DbSpanDecorator.java b/instrumentation/apache-camel-2.20/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachecamel/decorators/DbSpanDecorator.java index 65d029ef0bf1..f831cf8b61c3 100644 --- a/instrumentation/apache-camel-2.20/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachecamel/decorators/DbSpanDecorator.java +++ b/instrumentation/apache-camel-2.20/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachecamel/decorators/DbSpanDecorator.java @@ -25,6 +25,7 @@ import io.opentelemetry.api.common.AttributesBuilder; import io.opentelemetry.instrumentation.api.db.SqlStatementSanitizer; +import io.opentelemetry.javaagent.bootstrap.internal.CommonConfig; import io.opentelemetry.javaagent.instrumentation.apachecamel.CamelDirection; import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; import java.net.URI; @@ -34,6 +35,9 @@ class DbSpanDecorator extends BaseSpanDecorator { + private static final SqlStatementSanitizer sanitizer = + SqlStatementSanitizer.create(CommonConfig.get().isStatementSanitizationEnabled()); + private final String component; private final String system; @@ -65,19 +69,19 @@ String getStatement(Exchange exchange, Endpoint endpoint) { case "cql": Object cqlObj = exchange.getIn().getHeader("CamelCqlQuery"); if (cqlObj != null) { - return SqlStatementSanitizer.sanitize(cqlObj.toString()).getFullStatement(); + return sanitizer.sanitize(cqlObj.toString()).getFullStatement(); } return null; case "jdbc": Object body = exchange.getIn().getBody(); if (body instanceof String) { - return SqlStatementSanitizer.sanitize((String) body).getFullStatement(); + return sanitizer.sanitize((String) body).getFullStatement(); } return null; case "sql": Object sqlquery = exchange.getIn().getHeader("CamelSqlQuery"); if (sqlquery instanceof String) { - return SqlStatementSanitizer.sanitize((String) sqlquery).getFullStatement(); + return sanitizer.sanitize((String) sqlquery).getFullStatement(); } return null; default: diff --git a/instrumentation/apache-camel-2.20/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/apachecamel/RestCamelTest.groovy b/instrumentation/apache-camel-2.20/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/apachecamel/RestCamelTest.groovy index 5bd8d77f5ffa..e68ed017126a 100644 --- a/instrumentation/apache-camel-2.20/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/apachecamel/RestCamelTest.groovy +++ b/instrumentation/apache-camel-2.20/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/apachecamel/RestCamelTest.groovy @@ -89,16 +89,18 @@ class RestCamelTest extends AgentInstrumentationSpecification implements RetryOn parentSpanId(span(1).spanId) attributes { "$SemanticAttributes.HTTP_SCHEME" "http" - "$SemanticAttributes.HTTP_HOST" "localhost:$port" "$SemanticAttributes.HTTP_TARGET" "/api/firstModule/unit/unitOne" "$SemanticAttributes.HTTP_STATUS_CODE" 200 "$SemanticAttributes.HTTP_USER_AGENT" String "$SemanticAttributes.HTTP_FLAVOR" "1.1" "$SemanticAttributes.HTTP_METHOD" "GET" - "$SemanticAttributes.NET_PEER_IP" "127.0.0.1" - "$SemanticAttributes.NET_PEER_PORT" Long - "$SemanticAttributes.NET_TRANSPORT" IP_TCP "$SemanticAttributes.HTTP_ROUTE" "/api/{module}/unit/{unitId}" + "$SemanticAttributes.NET_TRANSPORT" IP_TCP + "$SemanticAttributes.NET_HOST_NAME" "localhost" + "$SemanticAttributes.NET_HOST_PORT" port + "$SemanticAttributes.NET_SOCK_PEER_ADDR" "127.0.0.1" + "$SemanticAttributes.NET_SOCK_PEER_PORT" Long + "$SemanticAttributes.NET_SOCK_HOST_ADDR" "127.0.0.1" } } it.span(3) { diff --git a/instrumentation/apache-camel-2.20/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/apachecamel/TwoServicesWithDirectClientCamelTest.groovy b/instrumentation/apache-camel-2.20/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/apachecamel/TwoServicesWithDirectClientCamelTest.groovy index 1d71bb4a1b9d..21142d898d99 100644 --- a/instrumentation/apache-camel-2.20/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/apachecamel/TwoServicesWithDirectClientCamelTest.groovy +++ b/instrumentation/apache-camel-2.20/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/apachecamel/TwoServicesWithDirectClientCamelTest.groovy @@ -126,15 +126,16 @@ class TwoServicesWithDirectClientCamelTest extends AgentInstrumentationSpecifica "$SemanticAttributes.HTTP_METHOD" "POST" "$SemanticAttributes.HTTP_STATUS_CODE" 200 "$SemanticAttributes.HTTP_SCHEME" "http" - "$SemanticAttributes.HTTP_HOST" "127.0.0.1:$portTwo" "$SemanticAttributes.HTTP_TARGET" "/serviceTwo" - "$SemanticAttributes.NET_PEER_PORT" Number - "$SemanticAttributes.NET_PEER_IP" "127.0.0.1" "$SemanticAttributes.HTTP_USER_AGENT" "Jakarta Commons-HttpClient/3.1" "$SemanticAttributes.HTTP_FLAVOR" "1.1" - "$SemanticAttributes.NET_TRANSPORT" IP_TCP "$SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH" Long "$SemanticAttributes.HTTP_ROUTE" "/serviceTwo" + "$SemanticAttributes.NET_TRANSPORT" IP_TCP + "$SemanticAttributes.NET_HOST_NAME" "127.0.0.1" + "$SemanticAttributes.NET_HOST_PORT" portTwo + "$SemanticAttributes.NET_SOCK_PEER_ADDR" "127.0.0.1" + "$SemanticAttributes.NET_SOCK_PEER_PORT" Long } } it.span(5) { diff --git a/instrumentation/apache-camel-2.20/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/apachecamel/aws/AwsSpan.groovy b/instrumentation/apache-camel-2.20/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/apachecamel/aws/AwsSpan.groovy index 5c4ebb3d91b1..765c917fbc84 100644 --- a/instrumentation/apache-camel-2.20/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/apachecamel/aws/AwsSpan.groovy +++ b/instrumentation/apache-camel-2.20/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/apachecamel/aws/AwsSpan.groovy @@ -34,7 +34,7 @@ class AwsSpan { "http.url" String "net.peer.name" String "net.transport" IP_TCP - "net.peer.port" { it == null || Number } + "net.peer.port" { it == null || it instanceof Number } } } } @@ -60,9 +60,11 @@ class AwsSpan { "http.method" "POST" "http.status_code" 200 "http.url" String - "http.user_agent" { it == null || String } + "http.user_agent" { it == null || it instanceof String } + "http.request_content_length" { it == null || it instanceof Long } + "http.response_content_length" { it == null || it instanceof Long } "net.peer.name" String - "net.peer.port" { it == null || Number } + "net.peer.port" { it == null || it instanceof Number } "net.transport" IP_TCP } } @@ -88,7 +90,7 @@ class AwsSpan { "http.status_code" 200 "http.url" String "net.peer.name" String - "net.peer.port" { it == null || Number } + "net.peer.port" { it == null || it instanceof Number } "net.transport" IP_TCP } } diff --git a/instrumentation/apache-dbcp-2.0/javaagent/build.gradle.kts b/instrumentation/apache-dbcp-2.0/javaagent/build.gradle.kts index 7792ee1db966..c0a58d047e8b 100644 --- a/instrumentation/apache-dbcp-2.0/javaagent/build.gradle.kts +++ b/instrumentation/apache-dbcp-2.0/javaagent/build.gradle.kts @@ -6,7 +6,7 @@ muzzle { pass { group.set("org.apache.commons") module.set("commons-dbcp2") - versions.set("[2.0,)") + versions.set("[2,)") assertInverse.set(true) } } diff --git a/instrumentation/apache-dbcp-2.0/library/README.md b/instrumentation/apache-dbcp-2.0/library/README.md index f10b34730214..c7c6d44e4011 100644 --- a/instrumentation/apache-dbcp-2.0/library/README.md +++ b/instrumentation/apache-dbcp-2.0/library/README.md @@ -1,4 +1,4 @@ -# Manual Instrumentation for Apache DBCP +# Library Instrumentation for Apache DBCP version 2.0 and higher Provides OpenTelemetry instrumentation for [Apache DBCP](https://commons.apache.org/proper/commons-dbcp/). @@ -6,13 +6,12 @@ Provides OpenTelemetry instrumentation for [Apache DBCP](https://commons.apache. ### Add these dependencies to your project: -Replace `OPENTELEMETRY_VERSION` with the latest stable -[release](https://mvnrepository.com/artifact/io.opentelemetry). `Minimum version: 1.15.0` +Replace `OPENTELEMETRY_VERSION` with the [latest +release](https://search.maven.org/search?q=g:io.opentelemetry.instrumentation%20AND%20a:opentelemetry-apache-dbcp-2.0). For Maven, add to your `pom.xml` dependencies: ```xml - io.opentelemetry.instrumentation diff --git a/instrumentation/apache-dubbo-2.7/javaagent/build.gradle.kts b/instrumentation/apache-dubbo-2.7/javaagent/build.gradle.kts index d3da1fd76714..6c206efce039 100644 --- a/instrumentation/apache-dubbo-2.7/javaagent/build.gradle.kts +++ b/instrumentation/apache-dubbo-2.7/javaagent/build.gradle.kts @@ -6,7 +6,8 @@ muzzle { pass { group.set("org.apache.dubbo") module.set("dubbo") - versions.set("[2.7.0,)") + versions.set("[2.7,)") + assertInverse.set(true) } } @@ -21,7 +22,9 @@ dependencies { } tasks.withType().configureEach { + jvmArgs("-XX:+IgnoreUnrecognizedVMOptions") // to suppress non-fatal errors on jdk17 jvmArgs("--add-opens=java.base/java.math=ALL-UNNAMED") - jvmArgs("-XX:+IgnoreUnrecognizedVMOptions") + // required on jdk17 + jvmArgs("--add-opens=java.base/java.lang=ALL-UNNAMED") } diff --git a/instrumentation/apache-dubbo-2.7/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachedubbo/v2_7/OpenTelemetryFilter.java b/instrumentation/apache-dubbo-2.7/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachedubbo/v2_7/OpenTelemetryFilter.java index 915289cd8094..a3010a6cfc8d 100644 --- a/instrumentation/apache-dubbo-2.7/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachedubbo/v2_7/OpenTelemetryFilter.java +++ b/instrumentation/apache-dubbo-2.7/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachedubbo/v2_7/OpenTelemetryFilter.java @@ -7,6 +7,9 @@ import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.instrumentation.apachedubbo.v2_7.DubboTelemetry; +import io.opentelemetry.instrumentation.apachedubbo.v2_7.internal.DubboNetClientAttributesGetter; +import io.opentelemetry.instrumentation.api.instrumenter.net.PeerServiceAttributesExtractor; +import io.opentelemetry.javaagent.bootstrap.internal.CommonConfig; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.rpc.Filter; import org.apache.dubbo.rpc.Invocation; @@ -19,7 +22,14 @@ public class OpenTelemetryFilter implements Filter { private final Filter delegate; public OpenTelemetryFilter() { - delegate = DubboTelemetry.create(GlobalOpenTelemetry.get()).newFilter(); + delegate = + DubboTelemetry.builder(GlobalOpenTelemetry.get()) + .addAttributesExtractor( + PeerServiceAttributesExtractor.create( + new DubboNetClientAttributesGetter(), + CommonConfig.get().getPeerServiceMapping())) + .build() + .newFilter(); } @Override diff --git a/instrumentation/apache-dubbo-2.7/javaagent/src/test/groovy/io/opentelemetry/instrumentation/apachedubbo/v2_7/DubboTest.groovy b/instrumentation/apache-dubbo-2.7/javaagent/src/test/groovy/io/opentelemetry/instrumentation/apachedubbo/v2_7/DubboTest.groovy index 6de451fb638b..36f81f58b828 100644 --- a/instrumentation/apache-dubbo-2.7/javaagent/src/test/groovy/io/opentelemetry/instrumentation/apachedubbo/v2_7/DubboTest.groovy +++ b/instrumentation/apache-dubbo-2.7/javaagent/src/test/groovy/io/opentelemetry/instrumentation/apachedubbo/v2_7/DubboTest.groovy @@ -5,7 +5,6 @@ package io.opentelemetry.instrumentation.apachedubbo.v2_7 - import io.opentelemetry.instrumentation.test.AgentTestTrait class DubboTest extends AbstractDubboTest implements AgentTestTrait { diff --git a/instrumentation/apache-dubbo-2.7/javaagent/src/test/groovy/io/opentelemetry/instrumentation/apachedubbo/v2_7/DubboTraceChainTest.groovy b/instrumentation/apache-dubbo-2.7/javaagent/src/test/groovy/io/opentelemetry/instrumentation/apachedubbo/v2_7/DubboTraceChainTest.groovy new file mode 100644 index 000000000000..432be0305633 --- /dev/null +++ b/instrumentation/apache-dubbo-2.7/javaagent/src/test/groovy/io/opentelemetry/instrumentation/apachedubbo/v2_7/DubboTraceChainTest.groovy @@ -0,0 +1,11 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.apachedubbo.v2_7 + +import io.opentelemetry.instrumentation.test.AgentTestTrait + +class DubboTraceChainTest extends AbstractDubboTraceChainTest implements AgentTestTrait { +} diff --git a/instrumentation/apache-dubbo-2.7/library-autoconfigure/build.gradle.kts b/instrumentation/apache-dubbo-2.7/library-autoconfigure/build.gradle.kts index 9c52e74ae123..c37558dd4f6d 100644 --- a/instrumentation/apache-dubbo-2.7/library-autoconfigure/build.gradle.kts +++ b/instrumentation/apache-dubbo-2.7/library-autoconfigure/build.gradle.kts @@ -14,7 +14,9 @@ dependencies { } tasks.withType().configureEach { + jvmArgs("-XX:+IgnoreUnrecognizedVMOptions") // to suppress non-fatal errors on jdk17 jvmArgs("--add-opens=java.base/java.math=ALL-UNNAMED") - jvmArgs("-XX:+IgnoreUnrecognizedVMOptions") + // required on jdk17 + jvmArgs("--add-opens=java.base/java.lang=ALL-UNNAMED") } diff --git a/instrumentation/apache-dubbo-2.7/library-autoconfigure/src/main/java/io/opentelemetry/instrumentation/apachedubbo/v2_7/DubboHeadersSetter.java b/instrumentation/apache-dubbo-2.7/library-autoconfigure/src/main/java/io/opentelemetry/instrumentation/apachedubbo/v2_7/DubboHeadersSetter.java index d220d877c35d..8dc502c051eb 100644 --- a/instrumentation/apache-dubbo-2.7/library-autoconfigure/src/main/java/io/opentelemetry/instrumentation/apachedubbo/v2_7/DubboHeadersSetter.java +++ b/instrumentation/apache-dubbo-2.7/library-autoconfigure/src/main/java/io/opentelemetry/instrumentation/apachedubbo/v2_7/DubboHeadersSetter.java @@ -13,5 +13,6 @@ enum DubboHeadersSetter implements TextMapSetter { @Override public void set(DubboRequest request, String key, String value) { request.context().setAttachment(key, value); + request.invocation().setAttachment(key, value); } } diff --git a/instrumentation/apache-dubbo-2.7/library-autoconfigure/src/main/java/io/opentelemetry/instrumentation/apachedubbo/v2_7/DubboRequest.java b/instrumentation/apache-dubbo-2.7/library-autoconfigure/src/main/java/io/opentelemetry/instrumentation/apachedubbo/v2_7/DubboRequest.java index 8dd0b0dbe72b..0733b744c867 100644 --- a/instrumentation/apache-dubbo-2.7/library-autoconfigure/src/main/java/io/opentelemetry/instrumentation/apachedubbo/v2_7/DubboRequest.java +++ b/instrumentation/apache-dubbo-2.7/library-autoconfigure/src/main/java/io/opentelemetry/instrumentation/apachedubbo/v2_7/DubboRequest.java @@ -19,7 +19,11 @@ static DubboRequest create(RpcInvocation invocation, RpcContext context) { // In dubbo 3 RpcContext delegates to a ThreadLocal context. We copy the url and remote address // here to ensure we can access them from the thread that ends the span. return new AutoValue_DubboRequest( - invocation, context, context.getUrl(), context.getRemoteAddress()); + invocation, + context, + context.getUrl(), + context.getRemoteAddress(), + context.getLocalAddress()); } abstract RpcInvocation invocation(); @@ -30,4 +34,7 @@ static DubboRequest create(RpcInvocation invocation, RpcContext context) { @Nullable public abstract InetSocketAddress remoteAddress(); + + @Nullable + public abstract InetSocketAddress localAddress(); } diff --git a/instrumentation/apache-dubbo-2.7/library-autoconfigure/src/main/java/io/opentelemetry/instrumentation/apachedubbo/v2_7/DubboTelemetryBuilder.java b/instrumentation/apache-dubbo-2.7/library-autoconfigure/src/main/java/io/opentelemetry/instrumentation/apachedubbo/v2_7/DubboTelemetryBuilder.java index bbe1f6e4ab3c..f20838fd74b5 100644 --- a/instrumentation/apache-dubbo-2.7/library-autoconfigure/src/main/java/io/opentelemetry/instrumentation/apachedubbo/v2_7/DubboTelemetryBuilder.java +++ b/instrumentation/apache-dubbo-2.7/library-autoconfigure/src/main/java/io/opentelemetry/instrumentation/apachedubbo/v2_7/DubboTelemetryBuilder.java @@ -5,13 +5,13 @@ package io.opentelemetry.instrumentation.apachedubbo.v2_7; +import com.google.errorprone.annotations.CanIgnoreReturnValue; import io.opentelemetry.api.OpenTelemetry; import io.opentelemetry.instrumentation.apachedubbo.v2_7.internal.DubboNetClientAttributesGetter; import io.opentelemetry.instrumentation.apachedubbo.v2_7.internal.DubboNetServerAttributesGetter; import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; import io.opentelemetry.instrumentation.api.instrumenter.InstrumenterBuilder; -import io.opentelemetry.instrumentation.api.instrumenter.PeerServiceAttributesExtractor; import io.opentelemetry.instrumentation.api.instrumenter.SpanNameExtractor; import io.opentelemetry.instrumentation.api.instrumenter.net.NetClientAttributesExtractor; import io.opentelemetry.instrumentation.api.instrumenter.net.NetServerAttributesExtractor; @@ -47,6 +47,7 @@ public void setPeerService(String peerService) { * Adds an additional {@link AttributesExtractor} to invoke to set attributes to instrumented * items. */ + @CanIgnoreReturnValue public DubboTelemetryBuilder addAttributesExtractor( AttributesExtractor attributesExtractor) { attributesExtractors.add(attributesExtractor); @@ -65,7 +66,7 @@ public DubboTelemetry build() { InstrumenterBuilder serverInstrumenterBuilder = Instrumenter.builder( openTelemetry, INSTRUMENTATION_NAME, spanNameExtractor) - .addAttributesExtractors(RpcServerAttributesExtractor.create(rpcAttributesGetter)) + .addAttributesExtractor(RpcServerAttributesExtractor.create(rpcAttributesGetter)) .addAttributesExtractor( NetServerAttributesExtractor.create(new DubboNetServerAttributesGetter())) .addAttributesExtractors(attributesExtractors); @@ -80,13 +81,10 @@ public DubboTelemetry build() { if (peerService != null) { clientInstrumenterBuilder.addAttributesExtractor( AttributesExtractor.constant(SemanticAttributes.PEER_SERVICE, peerService)); - } else { - clientInstrumenterBuilder.addAttributesExtractor( - PeerServiceAttributesExtractor.create(netClientAttributesGetter)); } return new DubboTelemetry( - serverInstrumenterBuilder.newServerInstrumenter(DubboHeadersGetter.INSTANCE), - clientInstrumenterBuilder.newClientInstrumenter(DubboHeadersSetter.INSTANCE)); + serverInstrumenterBuilder.buildServerInstrumenter(DubboHeadersGetter.INSTANCE), + clientInstrumenterBuilder.buildClientInstrumenter(DubboHeadersSetter.INSTANCE)); } } diff --git a/instrumentation/apache-dubbo-2.7/library-autoconfigure/src/main/java/io/opentelemetry/instrumentation/apachedubbo/v2_7/TracingFilter.java b/instrumentation/apache-dubbo-2.7/library-autoconfigure/src/main/java/io/opentelemetry/instrumentation/apachedubbo/v2_7/TracingFilter.java index af0dffe1c4b5..a0b0b1665c25 100644 --- a/instrumentation/apache-dubbo-2.7/library-autoconfigure/src/main/java/io/opentelemetry/instrumentation/apachedubbo/v2_7/TracingFilter.java +++ b/instrumentation/apache-dubbo-2.7/library-autoconfigure/src/main/java/io/opentelemetry/instrumentation/apachedubbo/v2_7/TracingFilter.java @@ -35,6 +35,10 @@ public Result invoke(Invoker invoker, Invocation invocation) { } RpcContext rpcContext = RpcContext.getContext(); + if (rpcContext.getUrl() == null) { + return invoker.invoke(invocation); + } + boolean isServer = rpcContext.isProviderSide(); Instrumenter instrumenter = isServer ? serverInstrumenter : clientInstrumenter; diff --git a/instrumentation/apache-dubbo-2.7/library-autoconfigure/src/main/java/io/opentelemetry/instrumentation/apachedubbo/v2_7/internal/DubboNetClientAttributesGetter.java b/instrumentation/apache-dubbo-2.7/library-autoconfigure/src/main/java/io/opentelemetry/instrumentation/apachedubbo/v2_7/internal/DubboNetClientAttributesGetter.java index 7af5c97d1ce6..3d0055718bc4 100644 --- a/instrumentation/apache-dubbo-2.7/library-autoconfigure/src/main/java/io/opentelemetry/instrumentation/apachedubbo/v2_7/internal/DubboNetClientAttributesGetter.java +++ b/instrumentation/apache-dubbo-2.7/library-autoconfigure/src/main/java/io/opentelemetry/instrumentation/apachedubbo/v2_7/internal/DubboNetClientAttributesGetter.java @@ -9,7 +9,6 @@ import io.opentelemetry.instrumentation.api.instrumenter.net.InetSocketAddressNetClientAttributesGetter; import java.net.InetSocketAddress; import javax.annotation.Nullable; -import org.apache.dubbo.common.URL; import org.apache.dubbo.rpc.Result; /** @@ -21,19 +20,25 @@ public final class DubboNetClientAttributesGetter @Override @Nullable - public InetSocketAddress getAddress(DubboRequest request, @Nullable Result response) { - InetSocketAddress address = request.remoteAddress(); - // dubbo 3 doesn't set remote address for client calls - if (address == null) { - URL url = request.url(); - address = InetSocketAddress.createUnresolved(url.getHost(), url.getPort()); - } - return address; + public String transport(DubboRequest request, @Nullable Result response) { + return null; + } + + @Nullable + @Override + public String peerName(DubboRequest request) { + return request.url().getHost(); + } + + @Override + public Integer peerPort(DubboRequest request) { + return request.url().getPort(); } @Override @Nullable - public String transport(DubboRequest request, @Nullable Result response) { - return null; + protected InetSocketAddress getPeerSocketAddress( + DubboRequest request, @Nullable Result response) { + return request.remoteAddress(); } } diff --git a/instrumentation/apache-dubbo-2.7/library-autoconfigure/src/main/java/io/opentelemetry/instrumentation/apachedubbo/v2_7/internal/DubboNetServerAttributesGetter.java b/instrumentation/apache-dubbo-2.7/library-autoconfigure/src/main/java/io/opentelemetry/instrumentation/apachedubbo/v2_7/internal/DubboNetServerAttributesGetter.java index 659587637b70..db5f707ab7d7 100644 --- a/instrumentation/apache-dubbo-2.7/library-autoconfigure/src/main/java/io/opentelemetry/instrumentation/apachedubbo/v2_7/internal/DubboNetServerAttributesGetter.java +++ b/instrumentation/apache-dubbo-2.7/library-autoconfigure/src/main/java/io/opentelemetry/instrumentation/apachedubbo/v2_7/internal/DubboNetServerAttributesGetter.java @@ -19,13 +19,31 @@ public final class DubboNetServerAttributesGetter @Override @Nullable - public InetSocketAddress getAddress(DubboRequest request) { - return request.remoteAddress(); + public String transport(DubboRequest request) { + return null; } + @Nullable @Override + public String hostName(DubboRequest request) { + return null; + } + @Nullable - public String transport(DubboRequest request) { + @Override + public Integer hostPort(DubboRequest request) { return null; } + + @Override + @Nullable + protected InetSocketAddress getPeerSocketAddress(DubboRequest request) { + return request.remoteAddress(); + } + + @Nullable + @Override + protected InetSocketAddress getHostSocketAddress(DubboRequest request) { + return request.localAddress(); + } } diff --git a/instrumentation/apache-dubbo-2.7/library-autoconfigure/src/test/groovy/io/opentelemetry/instrumentation/apachedubbo/v2_7/DubboTraceChainTest.groovy b/instrumentation/apache-dubbo-2.7/library-autoconfigure/src/test/groovy/io/opentelemetry/instrumentation/apachedubbo/v2_7/DubboTraceChainTest.groovy new file mode 100644 index 000000000000..ee0b1040dec5 --- /dev/null +++ b/instrumentation/apache-dubbo-2.7/library-autoconfigure/src/test/groovy/io/opentelemetry/instrumentation/apachedubbo/v2_7/DubboTraceChainTest.groovy @@ -0,0 +1,11 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.apachedubbo.v2_7 + +import io.opentelemetry.instrumentation.test.LibraryTestTrait + +class DubboTraceChainTest extends AbstractDubboTraceChainTest implements LibraryTestTrait { +} diff --git a/instrumentation/apache-dubbo-2.7/testing/src/main/groovy/io/opentelemetry/instrumentation/apachedubbo/v2_7/AbstractDubboTest.groovy b/instrumentation/apache-dubbo-2.7/testing/src/main/groovy/io/opentelemetry/instrumentation/apachedubbo/v2_7/AbstractDubboTest.groovy index 8dd5784194bd..7059555f2d3d 100644 --- a/instrumentation/apache-dubbo-2.7/testing/src/main/groovy/io/opentelemetry/instrumentation/apachedubbo/v2_7/AbstractDubboTest.groovy +++ b/instrumentation/apache-dubbo-2.7/testing/src/main/groovy/io/opentelemetry/instrumentation/apachedubbo/v2_7/AbstractDubboTest.groovy @@ -111,9 +111,9 @@ abstract class AbstractDubboTest extends InstrumentationSpecification { "$SemanticAttributes.RPC_SYSTEM" "apache_dubbo" "$SemanticAttributes.RPC_SERVICE" "io.opentelemetry.instrumentation.apachedubbo.v2_7.api.HelloService" "$SemanticAttributes.RPC_METHOD" "hello" - "$SemanticAttributes.NET_PEER_IP" String - "$SemanticAttributes.NET_PEER_NAME" { it == null || it instanceof String } - "$SemanticAttributes.NET_PEER_PORT" Long + "$SemanticAttributes.NET_SOCK_PEER_ADDR" String + "$SemanticAttributes.NET_SOCK_PEER_PORT" Long + "$SemanticAttributes.NET_SOCK_FAMILY" { it == SemanticAttributes.NetSockFamilyValues.INET6 || it == null } } } } @@ -182,9 +182,9 @@ abstract class AbstractDubboTest extends InstrumentationSpecification { "$SemanticAttributes.RPC_SYSTEM" "apache_dubbo" "$SemanticAttributes.RPC_SERVICE" "io.opentelemetry.instrumentation.apachedubbo.v2_7.api.HelloService" "$SemanticAttributes.RPC_METHOD" "hello" - "$SemanticAttributes.NET_PEER_IP" String - "$SemanticAttributes.NET_PEER_NAME" { it == null || it instanceof String } - "$SemanticAttributes.NET_PEER_PORT" Long + "$SemanticAttributes.NET_SOCK_PEER_ADDR" String + "$SemanticAttributes.NET_SOCK_PEER_PORT" Long + "$SemanticAttributes.NET_SOCK_FAMILY" { it == SemanticAttributes.NetSockFamilyValues.INET6 || it == null } } } } diff --git a/instrumentation/apache-dubbo-2.7/testing/src/main/groovy/io/opentelemetry/instrumentation/apachedubbo/v2_7/AbstractDubboTraceChainTest.groovy b/instrumentation/apache-dubbo-2.7/testing/src/main/groovy/io/opentelemetry/instrumentation/apachedubbo/v2_7/AbstractDubboTraceChainTest.groovy new file mode 100644 index 000000000000..6a4da45224d0 --- /dev/null +++ b/instrumentation/apache-dubbo-2.7/testing/src/main/groovy/io/opentelemetry/instrumentation/apachedubbo/v2_7/AbstractDubboTraceChainTest.groovy @@ -0,0 +1,183 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.apachedubbo.v2_7 + +import io.opentelemetry.api.trace.SpanKind +import io.opentelemetry.instrumentation.apachedubbo.v2_7.api.HelloService +import io.opentelemetry.instrumentation.apachedubbo.v2_7.api.MiddleService +import io.opentelemetry.instrumentation.apachedubbo.v2_7.impl.HelloServiceImpl +import io.opentelemetry.instrumentation.apachedubbo.v2_7.impl.MiddleServiceImpl +import io.opentelemetry.instrumentation.test.InstrumentationSpecification +import io.opentelemetry.instrumentation.test.utils.PortUtils +import io.opentelemetry.semconv.trace.attributes.SemanticAttributes +import org.apache.dubbo.common.utils.NetUtils +import org.apache.dubbo.config.ApplicationConfig +import org.apache.dubbo.config.ProtocolConfig +import org.apache.dubbo.config.ReferenceConfig +import org.apache.dubbo.config.RegistryConfig +import org.apache.dubbo.config.ServiceConfig +import org.apache.dubbo.config.bootstrap.DubboBootstrap +import org.apache.dubbo.rpc.service.GenericService +import spock.lang.Unroll + +import static io.opentelemetry.api.trace.SpanKind.CLIENT +import static io.opentelemetry.api.trace.SpanKind.SERVER + +@Unroll +abstract class AbstractDubboTraceChainTest extends InstrumentationSpecification { + + def setupSpec() { + NetUtils.LOCAL_ADDRESS = InetAddress.getLoopbackAddress() + } + + ReferenceConfig configureClient(int port) { + ReferenceConfig reference = new ReferenceConfig<>() + reference.setInterface(HelloService) + reference.setGeneric("true") + reference.setUrl("dubbo://localhost:" + port + "/?timeout=30000") + return reference + } + + ReferenceConfig configureMiddleClient(int port) { + ReferenceConfig reference = new ReferenceConfig<>() + reference.setInterface(MiddleService) + reference.setGeneric("true") + reference.setUrl("dubbo://localhost:" + port + "/?timeout=30000") + return reference + } + + ServiceConfig configureServer() { + def registerConfig = new RegistryConfig() + registerConfig.setAddress("N/A") + ServiceConfig service = new ServiceConfig<>() + service.setInterface(HelloService) + service.setRef(new HelloServiceImpl()) + service.setRegistry(registerConfig) + return service + } + + ServiceConfig configureMiddleServer(ReferenceConfig referenceConfig) { + def registerConfig = new RegistryConfig() + registerConfig.setAddress("N/A") + ServiceConfig service = new ServiceConfig<>() + service.setInterface(MiddleService) + service.setRef(new MiddleServiceImpl(referenceConfig)) + service.setRegistry(registerConfig) + return service + } + + def "test that context is propagated correctly in chained dubbo calls"() { + setup: + def port = PortUtils.findOpenPorts(2) + def middlePort = port + 1 + def protocolConfig = new ProtocolConfig() + protocolConfig.setPort(port) + + DubboBootstrap bootstrap = DubboBootstrap.newInstance() + bootstrap.application(new ApplicationConfig("dubbo-test-provider")) + .service(configureServer()) + .protocol(protocolConfig) + .start() + + def middleProtocolConfig = new ProtocolConfig() + middleProtocolConfig.setPort(middlePort) + + def reference = configureClient(port) + DubboBootstrap middleBootstrap = DubboBootstrap.newInstance() + middleBootstrap.application(new ApplicationConfig("dubbo-demo-middle")) + .reference(reference) + .service(configureMiddleServer(reference)) + .protocol(middleProtocolConfig) + .start() + + + def consumerProtocolConfig = new ProtocolConfig() + consumerProtocolConfig.setRegister(false) + + def middleReference = configureMiddleClient(middlePort) + DubboBootstrap consumerBootstrap = DubboBootstrap.newInstance() + consumerBootstrap.application(new ApplicationConfig("dubbo-demo-api-consumer")) + .reference(middleReference) + .protocol(consumerProtocolConfig) + .start() + + when: + GenericService genericService = middleReference.get() + def response = runWithSpan("parent") { + genericService.$invoke("hello", [String.getName()] as String[], ["hello"] as Object[]) + } + + then: + response == "hello" + assertTraces(1) { + trace(0, 5) { + span(0) { + name "parent" + kind SpanKind.INTERNAL + hasNoParent() + } + span(1) { + name "org.apache.dubbo.rpc.service.GenericService/\$invoke" + kind CLIENT + childOf span(0) + attributes { + "$SemanticAttributes.RPC_SYSTEM" "apache_dubbo" + "$SemanticAttributes.RPC_SERVICE" "org.apache.dubbo.rpc.service.GenericService" + "$SemanticAttributes.RPC_METHOD" "\$invoke" + "$SemanticAttributes.NET_PEER_NAME" "localhost" + "$SemanticAttributes.NET_PEER_PORT" Long + } + } + span(2) { + name "io.opentelemetry.instrumentation.apachedubbo.v2_7.api.MiddleService/hello" + kind SERVER + childOf span(1) + attributes { + "$SemanticAttributes.RPC_SYSTEM" "apache_dubbo" + "$SemanticAttributes.RPC_SERVICE" "io.opentelemetry.instrumentation.apachedubbo.v2_7.api.MiddleService" + "$SemanticAttributes.RPC_METHOD" "hello" + "$SemanticAttributes.NET_SOCK_PEER_ADDR" String + "$SemanticAttributes.NET_SOCK_PEER_PORT" Long + "$SemanticAttributes.NET_SOCK_FAMILY" { it == SemanticAttributes.NetSockFamilyValues.INET6 || it == null } + } + } + span(3) { + name "org.apache.dubbo.rpc.service.GenericService/\$invoke" + kind CLIENT + childOf span(2) + attributes { + "$SemanticAttributes.RPC_SYSTEM" "apache_dubbo" + "$SemanticAttributes.RPC_SERVICE" "org.apache.dubbo.rpc.service.GenericService" + "$SemanticAttributes.RPC_METHOD" "\$invoke" + "$SemanticAttributes.NET_PEER_NAME" "localhost" + "$SemanticAttributes.NET_PEER_PORT" Long + "$SemanticAttributes.NET_SOCK_PEER_ADDR" { it == null || String } + "$SemanticAttributes.NET_SOCK_PEER_PORT" { it == null || Long } + "$SemanticAttributes.NET_SOCK_PEER_NAME" { it == null || String } + } + } + span(4) { + name "io.opentelemetry.instrumentation.apachedubbo.v2_7.api.HelloService/hello" + kind SERVER + childOf span(3) + attributes { + "$SemanticAttributes.RPC_SYSTEM" "apache_dubbo" + "$SemanticAttributes.RPC_SERVICE" "io.opentelemetry.instrumentation.apachedubbo.v2_7.api.HelloService" + "$SemanticAttributes.RPC_METHOD" "hello" + "$SemanticAttributes.NET_SOCK_PEER_ADDR" String + "$SemanticAttributes.NET_SOCK_PEER_PORT" Long + "$SemanticAttributes.NET_SOCK_FAMILY" { it == SemanticAttributes.NetSockFamilyValues.INET6 || it == null } + } + } + } + } + + cleanup: + bootstrap.destroy() + middleBootstrap.destroy() + consumerBootstrap.destroy() + } +} diff --git a/instrumentation/apache-dubbo-2.7/testing/src/main/java/io/opentelemetry/instrumentation/apachedubbo/v2_7/api/MiddleService.java b/instrumentation/apache-dubbo-2.7/testing/src/main/java/io/opentelemetry/instrumentation/apachedubbo/v2_7/api/MiddleService.java new file mode 100644 index 000000000000..e80f1cd00438 --- /dev/null +++ b/instrumentation/apache-dubbo-2.7/testing/src/main/java/io/opentelemetry/instrumentation/apachedubbo/v2_7/api/MiddleService.java @@ -0,0 +1,10 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.apachedubbo.v2_7.api; + +public interface MiddleService { + String hello(String hello); +} diff --git a/instrumentation/apache-dubbo-2.7/testing/src/main/java/io/opentelemetry/instrumentation/apachedubbo/v2_7/impl/MiddleServiceImpl.java b/instrumentation/apache-dubbo-2.7/testing/src/main/java/io/opentelemetry/instrumentation/apachedubbo/v2_7/impl/MiddleServiceImpl.java new file mode 100644 index 000000000000..c461a8c92266 --- /dev/null +++ b/instrumentation/apache-dubbo-2.7/testing/src/main/java/io/opentelemetry/instrumentation/apachedubbo/v2_7/impl/MiddleServiceImpl.java @@ -0,0 +1,27 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.apachedubbo.v2_7.impl; + +import io.opentelemetry.instrumentation.apachedubbo.v2_7.api.MiddleService; +import org.apache.dubbo.config.ReferenceConfig; +import org.apache.dubbo.rpc.service.GenericService; + +public class MiddleServiceImpl implements MiddleService { + + private final ReferenceConfig referenceConfig; + + public MiddleServiceImpl(ReferenceConfig referenceConfig) { + this.referenceConfig = referenceConfig; + } + + @Override + public String hello(String hello) { + GenericService genericService = (GenericService) referenceConfig.get(); + return genericService + .$invoke("hello", new String[] {String.class.getName()}, new Object[] {hello}) + .toString(); + } +} diff --git a/instrumentation/apache-httpasyncclient-4.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpasyncclient/ApacheHttpAsyncClientHttpAttributesGetter.java b/instrumentation/apache-httpasyncclient-4.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpasyncclient/ApacheHttpAsyncClientHttpAttributesGetter.java index 9544a5869899..6ba84b3944e4 100644 --- a/instrumentation/apache-httpasyncclient-4.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpasyncclient/ApacheHttpAsyncClientHttpAttributesGetter.java +++ b/instrumentation/apache-httpasyncclient-4.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpasyncclient/ApacheHttpAsyncClientHttpAttributesGetter.java @@ -33,21 +33,8 @@ public List requestHeader(ApacheHttpClientRequest request, String name) @Override @Nullable - public Long requestContentLength( - ApacheHttpClientRequest request, @Nullable HttpResponse response) { - return null; - } - - @Override - @Nullable - public Long requestContentLengthUncompressed( - ApacheHttpClientRequest request, @Nullable HttpResponse response) { - return null; - } - - @Override - @Nullable - public Integer statusCode(ApacheHttpClientRequest request, HttpResponse response) { + public Integer statusCode( + ApacheHttpClientRequest request, HttpResponse response, @Nullable Throwable error) { StatusLine statusLine = response.getStatusLine(); return statusLine != null ? statusLine.getStatusCode() : null; } @@ -58,19 +45,6 @@ public String flavor(ApacheHttpClientRequest request, @Nullable HttpResponse res return request.getFlavor(); } - @Override - @Nullable - public Long responseContentLength(ApacheHttpClientRequest request, HttpResponse response) { - return null; - } - - @Override - @Nullable - public Long responseContentLengthUncompressed( - ApacheHttpClientRequest request, HttpResponse response) { - return null; - } - @Override public List responseHeader( ApacheHttpClientRequest request, HttpResponse response, String name) { diff --git a/instrumentation/apache-httpasyncclient-4.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpasyncclient/ApacheHttpAsyncClientInstrumentation.java b/instrumentation/apache-httpasyncclient-4.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpasyncclient/ApacheHttpAsyncClientInstrumentation.java index 849d449e1ddf..53b9cffb58a7 100644 --- a/instrumentation/apache-httpasyncclient-4.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpasyncclient/ApacheHttpAsyncClientInstrumentation.java +++ b/instrumentation/apache-httpasyncclient-4.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpasyncclient/ApacheHttpAsyncClientInstrumentation.java @@ -20,6 +20,7 @@ import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; import java.io.IOException; import java.util.logging.Logger; +import javax.annotation.Nullable; import net.bytebuddy.asm.Advice; import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.matcher.ElementMatcher; @@ -148,7 +149,7 @@ public static class WrappedFutureCallback implements FutureCallback { private static final Logger logger = Logger.getLogger(WrappedFutureCallback.class.getName()); private final Context parentContext; - private final HttpContext httpContext; + @Nullable private final HttpContext httpContext; private final FutureCallback delegate; private volatile Context context; @@ -171,7 +172,7 @@ public void completed(T result) { return; } - instrumenter().end(context, otelRequest, getResponse(httpContext), null); + instrumenter().end(context, otelRequest, getResponseFromHttpContext(), null); if (parentContext == null) { completeDelegate(result); @@ -193,7 +194,7 @@ public void failed(Exception ex) { } // end span before calling delegate - instrumenter().end(context, otelRequest, getResponse(httpContext), ex); + instrumenter().end(context, otelRequest, getResponseFromHttpContext(), ex); if (parentContext == null) { failDelegate(ex); @@ -216,7 +217,7 @@ public void cancelled() { // TODO (trask) add "canceled" span attribute // end span before calling delegate - instrumenter().end(context, otelRequest, getResponse(httpContext), null); + instrumenter().end(context, otelRequest, getResponseFromHttpContext(), null); if (parentContext == null) { cancelDelegate(); @@ -246,8 +247,12 @@ private void cancelDelegate() { } } - private static HttpResponse getResponse(HttpContext context) { - return (HttpResponse) context.getAttribute(HttpCoreContext.HTTP_RESPONSE); + @Nullable + private HttpResponse getResponseFromHttpContext() { + if (httpContext == null) { + return null; + } + return (HttpResponse) httpContext.getAttribute(HttpCoreContext.HTTP_RESPONSE); } } } diff --git a/instrumentation/apache-httpasyncclient-4.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpasyncclient/ApacheHttpAsyncClientNetAttributesGetter.java b/instrumentation/apache-httpasyncclient-4.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpasyncclient/ApacheHttpAsyncClientNetAttributesGetter.java index 68c20fe7d854..31cb00e399b7 100644 --- a/instrumentation/apache-httpasyncclient-4.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpasyncclient/ApacheHttpAsyncClientNetAttributesGetter.java +++ b/instrumentation/apache-httpasyncclient-4.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpasyncclient/ApacheHttpAsyncClientNetAttributesGetter.java @@ -5,13 +5,14 @@ package io.opentelemetry.javaagent.instrumentation.apachehttpasyncclient; -import io.opentelemetry.instrumentation.api.instrumenter.net.NetClientAttributesGetter; +import io.opentelemetry.instrumentation.api.instrumenter.net.InetSocketAddressNetClientAttributesGetter; import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; +import java.net.InetSocketAddress; import javax.annotation.Nullable; import org.apache.http.HttpResponse; final class ApacheHttpAsyncClientNetAttributesGetter - implements NetClientAttributesGetter { + extends InetSocketAddressNetClientAttributesGetter { @Override public String transport(ApacheHttpClientRequest request, @Nullable HttpResponse response) { @@ -20,18 +21,19 @@ public String transport(ApacheHttpClientRequest request, @Nullable HttpResponse @Override @Nullable - public String peerName(ApacheHttpClientRequest request, @Nullable HttpResponse response) { + public String peerName(ApacheHttpClientRequest request) { return request.getPeerName(); } @Override - public Integer peerPort(ApacheHttpClientRequest request, @Nullable HttpResponse response) { + public Integer peerPort(ApacheHttpClientRequest request) { return request.getPeerPort(); } - @Override @Nullable - public String peerIp(ApacheHttpClientRequest request, @Nullable HttpResponse response) { - return null; + @Override + protected InetSocketAddress getPeerSocketAddress( + ApacheHttpClientRequest request, @Nullable HttpResponse response) { + return request.peerSocketAddress(); } } diff --git a/instrumentation/apache-httpasyncclient-4.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpasyncclient/ApacheHttpAsyncClientSingletons.java b/instrumentation/apache-httpasyncclient-4.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpasyncclient/ApacheHttpAsyncClientSingletons.java index 9f84e964570b..27ff322420c6 100644 --- a/instrumentation/apache-httpasyncclient-4.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpasyncclient/ApacheHttpAsyncClientSingletons.java +++ b/instrumentation/apache-httpasyncclient-4.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpasyncclient/ApacheHttpAsyncClientSingletons.java @@ -7,12 +7,13 @@ import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; -import io.opentelemetry.instrumentation.api.instrumenter.PeerServiceAttributesExtractor; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpClientAttributesExtractor; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpClientMetrics; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanNameExtractor; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanStatusExtractor; import io.opentelemetry.instrumentation.api.instrumenter.net.NetClientAttributesExtractor; +import io.opentelemetry.instrumentation.api.instrumenter.net.PeerServiceAttributesExtractor; +import io.opentelemetry.javaagent.bootstrap.internal.CommonConfig; import org.apache.http.HttpResponse; public final class ApacheHttpAsyncClientSingletons { @@ -32,11 +33,17 @@ public final class ApacheHttpAsyncClientSingletons { INSTRUMENTATION_NAME, HttpSpanNameExtractor.create(httpAttributesGetter)) .setSpanStatusExtractor(HttpSpanStatusExtractor.create(httpAttributesGetter)) - .addAttributesExtractor(HttpClientAttributesExtractor.create(httpAttributesGetter)) + .addAttributesExtractor( + HttpClientAttributesExtractor.builder(httpAttributesGetter) + .setCapturedRequestHeaders(CommonConfig.get().getClientRequestHeaders()) + .setCapturedResponseHeaders(CommonConfig.get().getClientResponseHeaders()) + .build()) .addAttributesExtractor(NetClientAttributesExtractor.create(netAttributesGetter)) - .addAttributesExtractor(PeerServiceAttributesExtractor.create(netAttributesGetter)) + .addAttributesExtractor( + PeerServiceAttributesExtractor.create( + netAttributesGetter, CommonConfig.get().getPeerServiceMapping())) .addOperationMetrics(HttpClientMetrics.get()) - .newClientInstrumenter(HttpHeaderSetter.INSTANCE); + .buildClientInstrumenter(HttpHeaderSetter.INSTANCE); } public static Instrumenter instrumenter() { diff --git a/instrumentation/apache-httpasyncclient-4.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpasyncclient/ApacheHttpClientRequest.java b/instrumentation/apache-httpasyncclient-4.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpasyncclient/ApacheHttpClientRequest.java index dd92339354dc..e9b1c6f27e0d 100644 --- a/instrumentation/apache-httpasyncclient-4.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpasyncclient/ApacheHttpClientRequest.java +++ b/instrumentation/apache-httpasyncclient-4.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpasyncclient/ApacheHttpClientRequest.java @@ -8,6 +8,8 @@ import static java.util.logging.Level.FINE; import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; +import java.net.InetAddress; +import java.net.InetSocketAddress; import java.net.URI; import java.net.URISyntaxException; import java.util.ArrayList; @@ -27,8 +29,9 @@ public final class ApacheHttpClientRequest { @Nullable private final URI uri; private final HttpRequest delegate; + @Nullable private final HttpHost target; - public ApacheHttpClientRequest(HttpHost httpHost, HttpRequest httpRequest) { + public ApacheHttpClientRequest(@Nullable HttpHost httpHost, HttpRequest httpRequest) { URI calculatedUri = getUri(httpRequest); if (calculatedUri != null && httpHost != null) { uri = getCalculatedUri(httpHost, calculatedUri); @@ -36,6 +39,7 @@ public ApacheHttpClientRequest(HttpHost httpHost, HttpRequest httpRequest) { uri = calculatedUri; } delegate = httpRequest; + target = httpHost; } public List getHeader(String name) { @@ -143,4 +147,13 @@ private static URI getCalculatedUri(HttpHost httpHost, URI uri) { return null; } } + + @Nullable + public InetSocketAddress peerSocketAddress() { + if (target == null) { + return null; + } + InetAddress inetAddress = target.getAddress(); + return inetAddress == null ? null : new InetSocketAddress(inetAddress, target.getPort()); + } } diff --git a/instrumentation/apache-httpclient/apache-httpclient-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v2_0/ApacheHttpClientHttpAttributesGetter.java b/instrumentation/apache-httpclient/apache-httpclient-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v2_0/ApacheHttpClientHttpAttributesGetter.java index 8824fda97c97..34bda395d4bf 100644 --- a/instrumentation/apache-httpclient/apache-httpclient-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v2_0/ApacheHttpClientHttpAttributesGetter.java +++ b/instrumentation/apache-httpclient/apache-httpclient-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v2_0/ApacheHttpClientHttpAttributesGetter.java @@ -39,19 +39,7 @@ public List requestHeader(HttpMethod request, String name) { @Override @Nullable - public Long requestContentLength(HttpMethod request, @Nullable HttpMethod response) { - return null; - } - - @Override - @Nullable - public Long requestContentLengthUncompressed(HttpMethod request, @Nullable HttpMethod response) { - return null; - } - - @Override - @Nullable - public Integer statusCode(HttpMethod request, HttpMethod response) { + public Integer statusCode(HttpMethod request, HttpMethod response, @Nullable Throwable error) { StatusLine statusLine = response.getStatusLine(); return statusLine == null ? null : statusLine.getStatusCode(); } @@ -67,18 +55,6 @@ public String flavor(HttpMethod request, @Nullable HttpMethod response) { return null; } - @Override - @Nullable - public Long responseContentLength(HttpMethod request, HttpMethod response) { - return null; - } - - @Override - @Nullable - public Long responseContentLengthUncompressed(HttpMethod request, HttpMethod response) { - return null; - } - @Override public List responseHeader(HttpMethod request, HttpMethod response, String name) { Header header = response.getResponseHeader(name); diff --git a/instrumentation/apache-httpclient/apache-httpclient-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v2_0/ApacheHttpClientNetAttributesGetter.java b/instrumentation/apache-httpclient/apache-httpclient-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v2_0/ApacheHttpClientNetAttributesGetter.java index ecda03fac812..a6009d8fcc10 100644 --- a/instrumentation/apache-httpclient/apache-httpclient-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v2_0/ApacheHttpClientNetAttributesGetter.java +++ b/instrumentation/apache-httpclient/apache-httpclient-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v2_0/ApacheHttpClientNetAttributesGetter.java @@ -21,21 +21,15 @@ public String transport(HttpMethod request, @Nullable HttpMethod response) { @Override @Nullable - public String peerName(HttpMethod request, @Nullable HttpMethod response) { + public String peerName(HttpMethod request) { HostConfiguration hostConfiguration = request.getHostConfiguration(); return hostConfiguration != null ? hostConfiguration.getHost() : null; } @Override @Nullable - public Integer peerPort(HttpMethod request, @Nullable HttpMethod response) { + public Integer peerPort(HttpMethod request) { HostConfiguration hostConfiguration = request.getHostConfiguration(); return hostConfiguration != null ? hostConfiguration.getPort() : null; } - - @Override - @Nullable - public String peerIp(HttpMethod request, @Nullable HttpMethod response) { - return null; - } } diff --git a/instrumentation/apache-httpclient/apache-httpclient-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v2_0/ApacheHttpClientSingletons.java b/instrumentation/apache-httpclient/apache-httpclient-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v2_0/ApacheHttpClientSingletons.java index 150ed7b9d0c8..13b95a3a9da1 100644 --- a/instrumentation/apache-httpclient/apache-httpclient-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v2_0/ApacheHttpClientSingletons.java +++ b/instrumentation/apache-httpclient/apache-httpclient-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v2_0/ApacheHttpClientSingletons.java @@ -7,12 +7,13 @@ import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; -import io.opentelemetry.instrumentation.api.instrumenter.PeerServiceAttributesExtractor; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpClientAttributesExtractor; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpClientMetrics; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanNameExtractor; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanStatusExtractor; import io.opentelemetry.instrumentation.api.instrumenter.net.NetClientAttributesExtractor; +import io.opentelemetry.instrumentation.api.instrumenter.net.PeerServiceAttributesExtractor; +import io.opentelemetry.javaagent.bootstrap.internal.CommonConfig; import org.apache.commons.httpclient.HttpMethod; public final class ApacheHttpClientSingletons { @@ -32,11 +33,17 @@ public final class ApacheHttpClientSingletons { INSTRUMENTATION_NAME, HttpSpanNameExtractor.create(httpAttributesGetter)) .setSpanStatusExtractor(HttpSpanStatusExtractor.create(httpAttributesGetter)) - .addAttributesExtractor(HttpClientAttributesExtractor.create(httpAttributesGetter)) + .addAttributesExtractor( + HttpClientAttributesExtractor.builder(httpAttributesGetter) + .setCapturedRequestHeaders(CommonConfig.get().getClientRequestHeaders()) + .setCapturedResponseHeaders(CommonConfig.get().getClientResponseHeaders()) + .build()) .addAttributesExtractor(NetClientAttributesExtractor.create(netAttributesGetter)) - .addAttributesExtractor(PeerServiceAttributesExtractor.create(netAttributesGetter)) + .addAttributesExtractor( + PeerServiceAttributesExtractor.create( + netAttributesGetter, CommonConfig.get().getPeerServiceMapping())) .addOperationMetrics(HttpClientMetrics.get()) - .newClientInstrumenter(HttpHeaderSetter.INSTANCE); + .buildClientInstrumenter(HttpHeaderSetter.INSTANCE); } public static Instrumenter instrumenter() { diff --git a/instrumentation/apache-httpclient/apache-httpclient-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v4_0/ApacheHttpClientHelper.java b/instrumentation/apache-httpclient/apache-httpclient-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v4_0/ApacheHttpClientHelper.java index 1c63f3df168a..bdfc241a81fd 100644 --- a/instrumentation/apache-httpclient/apache-httpclient-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v4_0/ApacheHttpClientHelper.java +++ b/instrumentation/apache-httpclient/apache-httpclient-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v4_0/ApacheHttpClientHelper.java @@ -22,4 +22,6 @@ public static void doMethodExit( // ended in WrappingStatusSettingResponseHandler } } + + private ApacheHttpClientHelper() {} } diff --git a/instrumentation/apache-httpclient/apache-httpclient-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v4_0/ApacheHttpClientHttpAttributesGetter.java b/instrumentation/apache-httpclient/apache-httpclient-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v4_0/ApacheHttpClientHttpAttributesGetter.java index 94f9534ba5d1..affdb348391c 100644 --- a/instrumentation/apache-httpclient/apache-httpclient-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v4_0/ApacheHttpClientHttpAttributesGetter.java +++ b/instrumentation/apache-httpclient/apache-httpclient-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v4_0/ApacheHttpClientHttpAttributesGetter.java @@ -31,21 +31,8 @@ public List requestHeader(ApacheHttpClientRequest request, String name) } @Override - @Nullable - public Long requestContentLength( - ApacheHttpClientRequest request, @Nullable HttpResponse response) { - return null; - } - - @Override - @Nullable - public Long requestContentLengthUncompressed( - ApacheHttpClientRequest request, @Nullable HttpResponse response) { - return null; - } - - @Override - public Integer statusCode(ApacheHttpClientRequest request, HttpResponse response) { + public Integer statusCode( + ApacheHttpClientRequest request, HttpResponse response, @Nullable Throwable error) { return response.getStatusLine().getStatusCode(); } @@ -55,19 +42,6 @@ public String flavor(ApacheHttpClientRequest request, @Nullable HttpResponse res return request.getFlavor(); } - @Override - @Nullable - public Long responseContentLength(ApacheHttpClientRequest request, HttpResponse response) { - return null; - } - - @Override - @Nullable - public Long responseContentLengthUncompressed( - ApacheHttpClientRequest request, HttpResponse response) { - return null; - } - @Override public List responseHeader( ApacheHttpClientRequest request, HttpResponse response, String name) { diff --git a/instrumentation/apache-httpclient/apache-httpclient-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v4_0/ApacheHttpClientNetAttributesGetter.java b/instrumentation/apache-httpclient/apache-httpclient-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v4_0/ApacheHttpClientNetAttributesGetter.java index dfe289d7eafa..9cca3ac8b398 100644 --- a/instrumentation/apache-httpclient/apache-httpclient-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v4_0/ApacheHttpClientNetAttributesGetter.java +++ b/instrumentation/apache-httpclient/apache-httpclient-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v4_0/ApacheHttpClientNetAttributesGetter.java @@ -20,18 +20,12 @@ public String transport(ApacheHttpClientRequest request, @Nullable HttpResponse @Override @Nullable - public String peerName(ApacheHttpClientRequest request, @Nullable HttpResponse response) { + public String peerName(ApacheHttpClientRequest request) { return request.getPeerName(); } @Override - public Integer peerPort(ApacheHttpClientRequest request, @Nullable HttpResponse response) { + public Integer peerPort(ApacheHttpClientRequest request) { return request.getPeerPort(); } - - @Override - @Nullable - public String peerIp(ApacheHttpClientRequest request, @Nullable HttpResponse response) { - return null; - } } diff --git a/instrumentation/apache-httpclient/apache-httpclient-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v4_0/ApacheHttpClientSingletons.java b/instrumentation/apache-httpclient/apache-httpclient-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v4_0/ApacheHttpClientSingletons.java index caa52b621dc1..28f56538e628 100644 --- a/instrumentation/apache-httpclient/apache-httpclient-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v4_0/ApacheHttpClientSingletons.java +++ b/instrumentation/apache-httpclient/apache-httpclient-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v4_0/ApacheHttpClientSingletons.java @@ -7,12 +7,13 @@ import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; -import io.opentelemetry.instrumentation.api.instrumenter.PeerServiceAttributesExtractor; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpClientAttributesExtractor; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpClientMetrics; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanNameExtractor; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanStatusExtractor; import io.opentelemetry.instrumentation.api.instrumenter.net.NetClientAttributesExtractor; +import io.opentelemetry.instrumentation.api.instrumenter.net.PeerServiceAttributesExtractor; +import io.opentelemetry.javaagent.bootstrap.internal.CommonConfig; import org.apache.http.HttpResponse; public final class ApacheHttpClientSingletons { @@ -32,11 +33,17 @@ public final class ApacheHttpClientSingletons { INSTRUMENTATION_NAME, HttpSpanNameExtractor.create(httpAttributesGetter)) .setSpanStatusExtractor(HttpSpanStatusExtractor.create(httpAttributesGetter)) - .addAttributesExtractor(HttpClientAttributesExtractor.create(httpAttributesGetter)) + .addAttributesExtractor( + HttpClientAttributesExtractor.builder(httpAttributesGetter) + .setCapturedRequestHeaders(CommonConfig.get().getClientRequestHeaders()) + .setCapturedResponseHeaders(CommonConfig.get().getClientResponseHeaders()) + .build()) .addAttributesExtractor(NetClientAttributesExtractor.create(netAttributesGetter)) - .addAttributesExtractor(PeerServiceAttributesExtractor.create(netAttributesGetter)) + .addAttributesExtractor( + PeerServiceAttributesExtractor.create( + netAttributesGetter, CommonConfig.get().getPeerServiceMapping())) .addOperationMetrics(HttpClientMetrics.get()) - .newClientInstrumenter(HttpHeaderSetter.INSTANCE); + .buildClientInstrumenter(HttpHeaderSetter.INSTANCE); } public static Instrumenter instrumenter() { diff --git a/instrumentation/apache-httpclient/apache-httpclient-4.3/library/src/main/java/io/opentelemetry/instrumentation/apachehttpclient/v4_3/ApacheHttpClientHttpAttributesGetter.java b/instrumentation/apache-httpclient/apache-httpclient-4.3/library/src/main/java/io/opentelemetry/instrumentation/apachehttpclient/v4_3/ApacheHttpClientHttpAttributesGetter.java index 677739cd8fa9..c8717f310395 100644 --- a/instrumentation/apache-httpclient/apache-httpclient-4.3/library/src/main/java/io/opentelemetry/instrumentation/apachehttpclient/v4_3/ApacheHttpClientHttpAttributesGetter.java +++ b/instrumentation/apache-httpclient/apache-httpclient-4.3/library/src/main/java/io/opentelemetry/instrumentation/apachehttpclient/v4_3/ApacheHttpClientHttpAttributesGetter.java @@ -33,21 +33,8 @@ public List requestHeader(ApacheHttpClientRequest request, String name) } @Override - @Nullable - public Long requestContentLength( - ApacheHttpClientRequest request, @Nullable HttpResponse response) { - return null; - } - - @Override - @Nullable - public Long requestContentLengthUncompressed( - ApacheHttpClientRequest request, @Nullable HttpResponse response) { - return null; - } - - @Override - public Integer statusCode(ApacheHttpClientRequest request, HttpResponse response) { + public Integer statusCode( + ApacheHttpClientRequest request, HttpResponse response, @Nullable Throwable error) { return response.getStatusLine().getStatusCode(); } @@ -57,19 +44,6 @@ public String flavor(ApacheHttpClientRequest request, @Nullable HttpResponse res return request.getFlavor(); } - @Override - @Nullable - public Long responseContentLength(ApacheHttpClientRequest request, HttpResponse response) { - return null; - } - - @Override - @Nullable - public Long responseContentLengthUncompressed( - ApacheHttpClientRequest request, HttpResponse response) { - return null; - } - @Override public List responseHeader( ApacheHttpClientRequest request, HttpResponse response, String name) { diff --git a/instrumentation/apache-httpclient/apache-httpclient-4.3/library/src/main/java/io/opentelemetry/instrumentation/apachehttpclient/v4_3/ApacheHttpClientNetAttributesGetter.java b/instrumentation/apache-httpclient/apache-httpclient-4.3/library/src/main/java/io/opentelemetry/instrumentation/apachehttpclient/v4_3/ApacheHttpClientNetAttributesGetter.java index 2e1b1b8df869..b2b3510319ec 100644 --- a/instrumentation/apache-httpclient/apache-httpclient-4.3/library/src/main/java/io/opentelemetry/instrumentation/apachehttpclient/v4_3/ApacheHttpClientNetAttributesGetter.java +++ b/instrumentation/apache-httpclient/apache-httpclient-4.3/library/src/main/java/io/opentelemetry/instrumentation/apachehttpclient/v4_3/ApacheHttpClientNetAttributesGetter.java @@ -5,13 +5,14 @@ package io.opentelemetry.instrumentation.apachehttpclient.v4_3; -import io.opentelemetry.instrumentation.api.instrumenter.net.NetClientAttributesGetter; +import io.opentelemetry.instrumentation.api.instrumenter.net.InetSocketAddressNetClientAttributesGetter; import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; +import java.net.InetSocketAddress; import javax.annotation.Nullable; import org.apache.http.HttpResponse; final class ApacheHttpClientNetAttributesGetter - implements NetClientAttributesGetter { + extends InetSocketAddressNetClientAttributesGetter { @Override public String transport(ApacheHttpClientRequest request, @Nullable HttpResponse response) { @@ -20,19 +21,20 @@ public String transport(ApacheHttpClientRequest request, @Nullable HttpResponse @Override @Nullable - public String peerName(ApacheHttpClientRequest request, @Nullable HttpResponse response) { + public String peerName(ApacheHttpClientRequest request) { return request.getPeerName(); } @Override @Nullable - public Integer peerPort(ApacheHttpClientRequest request, @Nullable HttpResponse response) { + public Integer peerPort(ApacheHttpClientRequest request) { return request.getPeerPort(); } - @Override @Nullable - public String peerIp(ApacheHttpClientRequest request, @Nullable HttpResponse response) { - return null; + @Override + protected InetSocketAddress getPeerSocketAddress( + ApacheHttpClientRequest request, @Nullable HttpResponse response) { + return request.peerSocketAddress(); } } diff --git a/instrumentation/apache-httpclient/apache-httpclient-4.3/library/src/main/java/io/opentelemetry/instrumentation/apachehttpclient/v4_3/ApacheHttpClientRequest.java b/instrumentation/apache-httpclient/apache-httpclient-4.3/library/src/main/java/io/opentelemetry/instrumentation/apachehttpclient/v4_3/ApacheHttpClientRequest.java index 4264c9035329..6ea15860e673 100644 --- a/instrumentation/apache-httpclient/apache-httpclient-4.3/library/src/main/java/io/opentelemetry/instrumentation/apachehttpclient/v4_3/ApacheHttpClientRequest.java +++ b/instrumentation/apache-httpclient/apache-httpclient-4.3/library/src/main/java/io/opentelemetry/instrumentation/apachehttpclient/v4_3/ApacheHttpClientRequest.java @@ -8,6 +8,8 @@ import static java.util.logging.Level.FINE; import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; +import java.net.InetAddress; +import java.net.InetSocketAddress; import java.net.URI; import java.net.URISyntaxException; import java.util.ArrayList; @@ -27,6 +29,7 @@ public final class ApacheHttpClientRequest { @Nullable private final URI uri; private final HttpRequest delegate; + @Nullable private final HttpHost target; ApacheHttpClientRequest(@Nullable HttpHost httpHost, HttpRequest httpRequest) { URI calculatedUri = getUri(httpRequest); @@ -36,6 +39,7 @@ public final class ApacheHttpClientRequest { uri = calculatedUri; } delegate = httpRequest; + target = httpHost; } /** Returns the actual {@link HttpRequest} being executed by the client. */ @@ -146,4 +150,13 @@ private static URI getCalculatedUri(HttpHost httpHost, URI uri) { return null; } } + + @Nullable + public InetSocketAddress peerSocketAddress() { + if (target == null) { + return null; + } + InetAddress inetAddress = target.getAddress(); + return inetAddress == null ? null : new InetSocketAddress(inetAddress, target.getPort()); + } } diff --git a/instrumentation/apache-httpclient/apache-httpclient-4.3/library/src/main/java/io/opentelemetry/instrumentation/apachehttpclient/v4_3/ApacheHttpClientTelemetryBuilder.java b/instrumentation/apache-httpclient/apache-httpclient-4.3/library/src/main/java/io/opentelemetry/instrumentation/apachehttpclient/v4_3/ApacheHttpClientTelemetryBuilder.java index 88425f9e26c5..9352006994e2 100644 --- a/instrumentation/apache-httpclient/apache-httpclient-4.3/library/src/main/java/io/opentelemetry/instrumentation/apachehttpclient/v4_3/ApacheHttpClientTelemetryBuilder.java +++ b/instrumentation/apache-httpclient/apache-httpclient-4.3/library/src/main/java/io/opentelemetry/instrumentation/apachehttpclient/v4_3/ApacheHttpClientTelemetryBuilder.java @@ -5,6 +5,7 @@ package io.opentelemetry.instrumentation.apachehttpclient.v4_3; +import com.google.errorprone.annotations.CanIgnoreReturnValue; import io.opentelemetry.api.OpenTelemetry; import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; @@ -39,6 +40,7 @@ public final class ApacheHttpClientTelemetryBuilder { * Adds an additional {@link AttributesExtractor} to invoke to set attributes to instrumented * items. The {@link AttributesExtractor} will be executed after all default extractors. */ + @CanIgnoreReturnValue public ApacheHttpClientTelemetryBuilder addAttributeExtractor( AttributesExtractor attributesExtractor) { @@ -51,6 +53,7 @@ public ApacheHttpClientTelemetryBuilder addAttributeExtractor( * * @param requestHeaders A list of HTTP header names. */ + @CanIgnoreReturnValue public ApacheHttpClientTelemetryBuilder setCapturedRequestHeaders(List requestHeaders) { httpAttributesExtractorBuilder.setCapturedRequestHeaders(requestHeaders); return this; @@ -61,6 +64,7 @@ public ApacheHttpClientTelemetryBuilder setCapturedRequestHeaders(List r * * @param responseHeaders A list of HTTP header names. */ + @CanIgnoreReturnValue public ApacheHttpClientTelemetryBuilder setCapturedResponseHeaders(List responseHeaders) { httpAttributesExtractorBuilder.setCapturedResponseHeaders(responseHeaders); return this; @@ -86,7 +90,7 @@ public ApacheHttpClientTelemetry build() { .addAttributesExtractor(NetClientAttributesExtractor.create(netAttributesGetter)) .addAttributesExtractors(additionalExtractors) // We manually inject because we need to inject internal requests for redirects. - .newInstrumenter(SpanKindExtractor.alwaysClient()); + .buildInstrumenter(SpanKindExtractor.alwaysClient()); return new ApacheHttpClientTelemetry(instrumenter, openTelemetry.getPropagators()); } diff --git a/instrumentation/apache-httpclient/apache-httpclient-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v5_0/ApacheHttpAsyncClientInstrumentation.java b/instrumentation/apache-httpclient/apache-httpclient-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v5_0/ApacheHttpAsyncClientInstrumentation.java index 85f8ae28c9bf..5a6885710e0e 100644 --- a/instrumentation/apache-httpclient/apache-httpclient-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v5_0/ApacheHttpAsyncClientInstrumentation.java +++ b/instrumentation/apache-httpclient/apache-httpclient-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v5_0/ApacheHttpAsyncClientInstrumentation.java @@ -21,6 +21,7 @@ import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; import java.io.IOException; import java.util.logging.Logger; +import javax.annotation.Nullable; import net.bytebuddy.asm.Advice; import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.matcher.ElementMatcher; @@ -32,6 +33,7 @@ import org.apache.hc.core5.http.nio.AsyncRequestProducer; import org.apache.hc.core5.http.nio.DataStreamChannel; import org.apache.hc.core5.http.nio.RequestChannel; +import org.apache.hc.core5.http.protocol.BasicHttpContext; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.http.protocol.HttpCoreContext; @@ -67,10 +69,13 @@ public static class ClientAdvice { @Advice.OnMethodEnter(suppress = Throwable.class) public static void methodEnter( @Advice.Argument(value = 0, readOnly = false) AsyncRequestProducer requestProducer, - @Advice.Argument(3) HttpContext httpContext, + @Advice.Argument(value = 3, readOnly = false) HttpContext httpContext, @Advice.Argument(value = 4, readOnly = false) FutureCallback futureCallback) { Context parentContext = currentContext(); + if (httpContext == null) { + httpContext = new BasicHttpContext(); + } WrappedFutureCallback wrappedFutureCallback = new WrappedFutureCallback<>(parentContext, httpContext, futureCallback); @@ -182,7 +187,7 @@ public void completed(T result) { return; } - instrumenter().end(context, httpRequest, getResponse(httpContext), null); + instrumenter().end(context, httpRequest, getResponseFromHttpContext(), null); if (parentContext == null) { completeDelegate(result); @@ -204,7 +209,7 @@ public void failed(Exception ex) { } // end span before calling delegate - instrumenter().end(context, httpRequest, getResponse(httpContext), ex); + instrumenter().end(context, httpRequest, getResponseFromHttpContext(), ex); if (parentContext == null) { failDelegate(ex); @@ -227,7 +232,7 @@ public void cancelled() { // TODO (trask) add "canceled" span attribute // end span before calling delegate - instrumenter().end(context, httpRequest, getResponse(httpContext), null); + instrumenter().end(context, httpRequest, getResponseFromHttpContext(), null); if (parentContext == null) { cancelDelegate(); @@ -257,8 +262,9 @@ private void cancelDelegate() { } } - private static HttpResponse getResponse(HttpContext context) { - return (HttpResponse) context.getAttribute(HttpCoreContext.HTTP_RESPONSE); + @Nullable + private HttpResponse getResponseFromHttpContext() { + return (HttpResponse) httpContext.getAttribute(HttpCoreContext.HTTP_RESPONSE); } } } diff --git a/instrumentation/apache-httpclient/apache-httpclient-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v5_0/ApacheHttpClientHelper.java b/instrumentation/apache-httpclient/apache-httpclient-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v5_0/ApacheHttpClientHelper.java index f8adce35b103..0a75742dd513 100644 --- a/instrumentation/apache-httpclient/apache-httpclient-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v5_0/ApacheHttpClientHelper.java +++ b/instrumentation/apache-httpclient/apache-httpclient-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v5_0/ApacheHttpClientHelper.java @@ -23,4 +23,6 @@ public static void doMethodExit( // ended in WrappingStatusSettingResponseHandler } } + + private ApacheHttpClientHelper() {} } diff --git a/instrumentation/apache-httpclient/apache-httpclient-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v5_0/ApacheHttpClientHttpAttributesGetter.java b/instrumentation/apache-httpclient/apache-httpclient-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v5_0/ApacheHttpClientHttpAttributesGetter.java index d85b396ff5a5..a1f229763493 100644 --- a/instrumentation/apache-httpclient/apache-httpclient-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v5_0/ApacheHttpClientHttpAttributesGetter.java +++ b/instrumentation/apache-httpclient/apache-httpclient-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v5_0/ApacheHttpClientHttpAttributesGetter.java @@ -69,20 +69,7 @@ public List requestHeader(HttpRequest request, String name) { } @Override - @Nullable - public Long requestContentLength(HttpRequest request, @Nullable HttpResponse response) { - return null; - } - - @Override - @Nullable - public Long requestContentLengthUncompressed( - HttpRequest request, @Nullable HttpResponse response) { - return null; - } - - @Override - public Integer statusCode(HttpRequest request, HttpResponse response) { + public Integer statusCode(HttpRequest request, HttpResponse response, @Nullable Throwable error) { return response.getCode(); } @@ -112,18 +99,6 @@ public String flavor(HttpRequest request, @Nullable HttpResponse response) { return null; } - @Override - @Nullable - public Long responseContentLength(HttpRequest request, HttpResponse response) { - return null; - } - - @Override - @Nullable - public Long responseContentLengthUncompressed(HttpRequest request, HttpResponse response) { - return null; - } - @Override public List responseHeader(HttpRequest request, HttpResponse response, String name) { return getHeader(response, name); diff --git a/instrumentation/apache-httpclient/apache-httpclient-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v5_0/ApacheHttpClientNetAttributesGetter.java b/instrumentation/apache-httpclient/apache-httpclient-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v5_0/ApacheHttpClientNetAttributesGetter.java index e0eaa8c01aa0..d2829dc68097 100644 --- a/instrumentation/apache-httpclient/apache-httpclient-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v5_0/ApacheHttpClientNetAttributesGetter.java +++ b/instrumentation/apache-httpclient/apache-httpclient-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v5_0/ApacheHttpClientNetAttributesGetter.java @@ -26,12 +26,12 @@ public String transport(HttpRequest request, @Nullable HttpResponse response) { @Override @Nullable - public String peerName(HttpRequest request, @Nullable HttpResponse response) { + public String peerName(HttpRequest request) { return request.getAuthority().getHostName(); } @Override - public Integer peerPort(HttpRequest request, @Nullable HttpResponse response) { + public Integer peerPort(HttpRequest request) { int port = request.getAuthority().getPort(); if (port != -1) { return port; @@ -50,10 +50,4 @@ public Integer peerPort(HttpRequest request, @Nullable HttpResponse response) { return null; } } - - @Override - @Nullable - public String peerIp(HttpRequest request, @Nullable HttpResponse response) { - return null; - } } diff --git a/instrumentation/apache-httpclient/apache-httpclient-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v5_0/ApacheHttpClientSingletons.java b/instrumentation/apache-httpclient/apache-httpclient-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v5_0/ApacheHttpClientSingletons.java index 267c40a5a8ee..f06a4b0f5b62 100644 --- a/instrumentation/apache-httpclient/apache-httpclient-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v5_0/ApacheHttpClientSingletons.java +++ b/instrumentation/apache-httpclient/apache-httpclient-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v5_0/ApacheHttpClientSingletons.java @@ -7,12 +7,13 @@ import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; -import io.opentelemetry.instrumentation.api.instrumenter.PeerServiceAttributesExtractor; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpClientAttributesExtractor; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpClientMetrics; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanNameExtractor; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanStatusExtractor; import io.opentelemetry.instrumentation.api.instrumenter.net.NetClientAttributesExtractor; +import io.opentelemetry.instrumentation.api.instrumenter.net.PeerServiceAttributesExtractor; +import io.opentelemetry.javaagent.bootstrap.internal.CommonConfig; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpResponse; @@ -33,11 +34,17 @@ public final class ApacheHttpClientSingletons { INSTRUMENTATION_NAME, HttpSpanNameExtractor.create(httpAttributesGetter)) .setSpanStatusExtractor(HttpSpanStatusExtractor.create(httpAttributesGetter)) - .addAttributesExtractor(HttpClientAttributesExtractor.create(httpAttributesGetter)) + .addAttributesExtractor( + HttpClientAttributesExtractor.builder(httpAttributesGetter) + .setCapturedRequestHeaders(CommonConfig.get().getClientRequestHeaders()) + .setCapturedResponseHeaders(CommonConfig.get().getClientResponseHeaders()) + .build()) .addAttributesExtractor(NetClientAttributesExtractor.create(netAttributesGetter)) - .addAttributesExtractor(PeerServiceAttributesExtractor.create(netAttributesGetter)) + .addAttributesExtractor( + PeerServiceAttributesExtractor.create( + netAttributesGetter, CommonConfig.get().getPeerServiceMapping())) .addOperationMetrics(HttpClientMetrics.get()) - .newClientInstrumenter(HttpHeaderSetter.INSTANCE); + .buildClientInstrumenter(HttpHeaderSetter.INSTANCE); } public static Instrumenter instrumenter() { diff --git a/instrumentation/apache-httpclient/apache-httpclient-5.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v5_0/ApacheHttpAsyncClientTest.java b/instrumentation/apache-httpclient/apache-httpclient-5.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v5_0/ApacheHttpAsyncClientTest.java index f3e1d067a07f..588ebc659fb7 100644 --- a/instrumentation/apache-httpclient/apache-httpclient-5.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v5_0/ApacheHttpAsyncClientTest.java +++ b/instrumentation/apache-httpclient/apache-httpclient-5.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v5_0/ApacheHttpAsyncClientTest.java @@ -19,6 +19,7 @@ import org.apache.hc.client5.http.impl.async.HttpAsyncClients; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.http.HttpResponse; +import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.http2.HttpVersionPolicy; import org.apache.hc.core5.io.CloseMode; import org.junit.jupiter.api.AfterAll; @@ -72,6 +73,19 @@ SimpleHttpRequest createRequest(String method, URI uri) { } } + @Nested + class ApacheClientNullContextTest extends AbstractTest { + @Override + SimpleHttpRequest createRequest(String method, URI uri) { + return new SimpleHttpRequest(method, uri); + } + + @Override + protected HttpContext getContext() { + return null; + } + } + abstract class AbstractTest extends AbstractApacheHttpClientTest { @Override protected SimpleHttpRequest buildRequest(String method, URI uri, Map headers) { @@ -88,12 +102,12 @@ protected SimpleHttpRequest buildRequest(String method, URI uri, Map typeMatcher() { - return named("com.linecorp.armeria.common.stream.AbstractStreamMessage$SubscriptionImpl"); + return namedOneOf( + "com.linecorp.armeria.common.stream.AbstractStreamMessage$SubscriptionImpl", + // renamed in 1.19.0 + "com.linecorp.armeria.common.stream.CancellableStreamMessage$SubscriptionImpl"); } @Override @@ -28,10 +32,14 @@ public void transform(TypeTransformer transformer) { transformer.applyAdviceToMethod( isConstructor() .and( - takesArgument(0, named("com.linecorp.armeria.common.stream.AbstractStreamMessage"))) + takesArgument( + 0, + namedOneOf( + "com.linecorp.armeria.common.stream.AbstractStreamMessage", + "com.linecorp.armeria.common.stream.CancellableStreamMessage"))) .and(takesArgument(1, named("org.reactivestreams.Subscriber"))), AbstractStreamMessageSubscriptionInstrumentation.class.getName() + "$WrapSubscriberAdvice"); - // since 1.9.0 + // from 1.9.0 to 1.9.2 transformer.applyAdviceToMethod( isConstructor() .and( @@ -51,6 +59,7 @@ public static void wrapSubscriber( } } + @SuppressWarnings("unused") public static class WrapCompletableFutureAdvice { @Advice.OnMethodEnter(suppress = Throwable.class) diff --git a/instrumentation/armeria-1.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/armeria/v1_3/ArmeriaSingletons.java b/instrumentation/armeria-1.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/armeria/v1_3/ArmeriaSingletons.java index 659ddbf61d1c..5ce1a32a79f2 100644 --- a/instrumentation/armeria-1.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/armeria/v1_3/ArmeriaSingletons.java +++ b/instrumentation/armeria-1.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/armeria/v1_3/ArmeriaSingletons.java @@ -8,7 +8,10 @@ import com.linecorp.armeria.client.HttpClient; import com.linecorp.armeria.server.HttpService; import io.opentelemetry.api.GlobalOpenTelemetry; +import io.opentelemetry.instrumentation.api.instrumenter.net.PeerServiceAttributesExtractor; import io.opentelemetry.instrumentation.armeria.v1_3.ArmeriaTelemetry; +import io.opentelemetry.instrumentation.armeria.v1_3.internal.ArmeriaNetClientAttributesGetter; +import io.opentelemetry.javaagent.bootstrap.internal.CommonConfig; import java.util.function.Function; // Holds singleton references to decorators to match against during suppression. @@ -19,7 +22,17 @@ public final class ArmeriaSingletons { public static final Function SERVER_DECORATOR; static { - ArmeriaTelemetry telemetry = ArmeriaTelemetry.create(GlobalOpenTelemetry.get()); + ArmeriaTelemetry telemetry = + ArmeriaTelemetry.builder(GlobalOpenTelemetry.get()) + .setCapturedClientRequestHeaders(CommonConfig.get().getClientRequestHeaders()) + .setCapturedClientResponseHeaders(CommonConfig.get().getClientResponseHeaders()) + .setCapturedServerRequestHeaders(CommonConfig.get().getServerRequestHeaders()) + .setCapturedServerResponseHeaders(CommonConfig.get().getServerResponseHeaders()) + .addClientAttributeExtractor( + PeerServiceAttributesExtractor.create( + new ArmeriaNetClientAttributesGetter(), + CommonConfig.get().getPeerServiceMapping())) + .build(); CLIENT_DECORATOR = telemetry.newClientDecorator(); SERVER_DECORATOR = telemetry.newServiceDecorator(); diff --git a/instrumentation/armeria-1.3/library/build.gradle.kts b/instrumentation/armeria-1.3/library/build.gradle.kts index fc599f337d78..2d38bf072d15 100644 --- a/instrumentation/armeria-1.3/library/build.gradle.kts +++ b/instrumentation/armeria-1.3/library/build.gradle.kts @@ -8,3 +8,9 @@ dependencies { testImplementation(project(":instrumentation:armeria-1.3:testing")) } + +tasks { + test { + systemProperty("testLatestDeps", findProperty("testLatestDeps") as Boolean) + } +} diff --git a/instrumentation/armeria-1.3/library/src/main/java/io/opentelemetry/instrumentation/armeria/v1_3/ArmeriaHttpClientAttributesGetter.java b/instrumentation/armeria-1.3/library/src/main/java/io/opentelemetry/instrumentation/armeria/v1_3/ArmeriaHttpClientAttributesGetter.java index dc93929faa7d..fe91f25b3a9f 100644 --- a/instrumentation/armeria-1.3/library/src/main/java/io/opentelemetry/instrumentation/armeria/v1_3/ArmeriaHttpClientAttributesGetter.java +++ b/instrumentation/armeria-1.3/library/src/main/java/io/opentelemetry/instrumentation/armeria/v1_3/ArmeriaHttpClientAttributesGetter.java @@ -36,23 +36,7 @@ public List requestHeader(RequestContext ctx, String name) { @Override @Nullable - public Long requestContentLength(RequestContext ctx, @Nullable RequestLog requestLog) { - if (requestLog == null) { - return null; - } - return requestLog.requestLength(); - } - - @Override - @Nullable - public Long requestContentLengthUncompressed( - RequestContext ctx, @Nullable RequestLog requestLog) { - return null; - } - - @Override - @Nullable - public Integer statusCode(RequestContext ctx, RequestLog requestLog) { + public Integer statusCode(RequestContext ctx, RequestLog requestLog, @Nullable Throwable error) { HttpStatus status = requestLog.responseHeaders().status(); if (!status.equals(HttpStatus.UNKNOWN)) { return status.code(); @@ -70,17 +54,6 @@ public String flavor(RequestContext ctx, @Nullable RequestLog requestLog) { } } - @Override - public Long responseContentLength(RequestContext ctx, RequestLog requestLog) { - return requestLog.responseLength(); - } - - @Override - @Nullable - public Long responseContentLengthUncompressed(RequestContext ctx, RequestLog requestLog) { - return null; - } - @Override public List responseHeader(RequestContext ctx, RequestLog requestLog, String name) { return requestLog.responseHeaders().getAll(name); diff --git a/instrumentation/armeria-1.3/library/src/main/java/io/opentelemetry/instrumentation/armeria/v1_3/ArmeriaHttpServerAttributesGetter.java b/instrumentation/armeria-1.3/library/src/main/java/io/opentelemetry/instrumentation/armeria/v1_3/ArmeriaHttpServerAttributesGetter.java index cfae5c0b0a64..5a56fc4c8bc9 100644 --- a/instrumentation/armeria-1.3/library/src/main/java/io/opentelemetry/instrumentation/armeria/v1_3/ArmeriaHttpServerAttributesGetter.java +++ b/instrumentation/armeria-1.3/library/src/main/java/io/opentelemetry/instrumentation/armeria/v1_3/ArmeriaHttpServerAttributesGetter.java @@ -43,23 +43,7 @@ public List requestHeader(RequestContext ctx, String name) { @Override @Nullable - public Long requestContentLength(RequestContext ctx, @Nullable RequestLog requestLog) { - if (requestLog == null) { - return null; - } - return requestLog.requestLength(); - } - - @Override - @Nullable - public Long requestContentLengthUncompressed( - RequestContext ctx, @Nullable RequestLog requestLog) { - return null; - } - - @Override - @Nullable - public Integer statusCode(RequestContext ctx, RequestLog requestLog) { + public Integer statusCode(RequestContext ctx, RequestLog requestLog, @Nullable Throwable error) { HttpStatus status = requestLog.responseHeaders().status(); if (!status.equals(HttpStatus.UNKNOWN)) { return status.code(); @@ -77,31 +61,11 @@ public String flavor(RequestContext ctx) { } } - @Override - public Long responseContentLength(RequestContext ctx, RequestLog requestLog) { - return requestLog.responseLength(); - } - - @Override - @Nullable - public Long responseContentLengthUncompressed(RequestContext ctx, RequestLog requestLog) { - return null; - } - @Override public List responseHeader(RequestContext ctx, RequestLog requestLog, String name) { return requestLog.responseHeaders().getAll(name); } - @Override - @Nullable - public String serverName(RequestContext ctx) { - if (ctx instanceof ServiceRequestContext) { - return ((ServiceRequestContext) ctx).config().virtualHost().defaultHostname(); - } - return null; - } - @Override @Nullable public String route(RequestContext ctx) { diff --git a/instrumentation/armeria-1.3/library/src/main/java/io/opentelemetry/instrumentation/armeria/v1_3/ArmeriaNetClientAttributesGetter.java b/instrumentation/armeria-1.3/library/src/main/java/io/opentelemetry/instrumentation/armeria/v1_3/ArmeriaNetClientAttributesGetter.java deleted file mode 100644 index b5cd4d735d0f..000000000000 --- a/instrumentation/armeria-1.3/library/src/main/java/io/opentelemetry/instrumentation/armeria/v1_3/ArmeriaNetClientAttributesGetter.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.instrumentation.armeria.v1_3; - -import com.linecorp.armeria.common.RequestContext; -import com.linecorp.armeria.common.logging.RequestLog; -import io.opentelemetry.instrumentation.api.instrumenter.net.InetSocketAddressNetClientAttributesGetter; -import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; -import java.net.InetSocketAddress; -import java.net.SocketAddress; -import javax.annotation.Nullable; - -final class ArmeriaNetClientAttributesGetter - extends InetSocketAddressNetClientAttributesGetter { - - @Override - public String transport(RequestContext ctx, @Nullable RequestLog requestLog) { - return SemanticAttributes.NetTransportValues.IP_TCP; - } - - @Override - @Nullable - public InetSocketAddress getAddress(RequestContext ctx, @Nullable RequestLog requestLog) { - SocketAddress address = ctx.remoteAddress(); - if (address instanceof InetSocketAddress) { - return (InetSocketAddress) address; - } - return null; - } -} diff --git a/instrumentation/armeria-1.3/library/src/main/java/io/opentelemetry/instrumentation/armeria/v1_3/ArmeriaNetServerAttributesGetter.java b/instrumentation/armeria-1.3/library/src/main/java/io/opentelemetry/instrumentation/armeria/v1_3/ArmeriaNetServerAttributesGetter.java index 6b3983a2b869..def4eeb71829 100644 --- a/instrumentation/armeria-1.3/library/src/main/java/io/opentelemetry/instrumentation/armeria/v1_3/ArmeriaNetServerAttributesGetter.java +++ b/instrumentation/armeria-1.3/library/src/main/java/io/opentelemetry/instrumentation/armeria/v1_3/ArmeriaNetServerAttributesGetter.java @@ -20,13 +20,35 @@ public String transport(RequestContext ctx) { return SemanticAttributes.NetTransportValues.IP_TCP; } + @Nullable + @Override + public String hostName(RequestContext ctx) { + return null; + } + + @Nullable + @Override + public Integer hostPort(RequestContext ctx) { + return null; + } + @Override @Nullable - public InetSocketAddress getAddress(RequestContext ctx) { + protected InetSocketAddress getPeerSocketAddress(RequestContext ctx) { SocketAddress address = ctx.remoteAddress(); if (address instanceof InetSocketAddress) { return (InetSocketAddress) address; } return null; } + + @Nullable + @Override + protected InetSocketAddress getHostSocketAddress(RequestContext ctx) { + SocketAddress address = ctx.localAddress(); + if (address instanceof InetSocketAddress) { + return (InetSocketAddress) address; + } + return null; + } } diff --git a/instrumentation/armeria-1.3/library/src/main/java/io/opentelemetry/instrumentation/armeria/v1_3/ArmeriaTelemetryBuilder.java b/instrumentation/armeria-1.3/library/src/main/java/io/opentelemetry/instrumentation/armeria/v1_3/ArmeriaTelemetryBuilder.java index e55869c56da4..b1fbcbcb9a54 100644 --- a/instrumentation/armeria-1.3/library/src/main/java/io/opentelemetry/instrumentation/armeria/v1_3/ArmeriaTelemetryBuilder.java +++ b/instrumentation/armeria-1.3/library/src/main/java/io/opentelemetry/instrumentation/armeria/v1_3/ArmeriaTelemetryBuilder.java @@ -5,6 +5,7 @@ package io.opentelemetry.instrumentation.armeria.v1_3; +import com.google.errorprone.annotations.CanIgnoreReturnValue; import com.linecorp.armeria.client.ClientRequestContext; import com.linecorp.armeria.common.RequestContext; import com.linecorp.armeria.common.logging.RequestLog; @@ -13,7 +14,6 @@ import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; import io.opentelemetry.instrumentation.api.instrumenter.InstrumenterBuilder; -import io.opentelemetry.instrumentation.api.instrumenter.PeerServiceAttributesExtractor; import io.opentelemetry.instrumentation.api.instrumenter.SpanStatusExtractor; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpClientAttributesExtractor; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpClientAttributesExtractorBuilder; @@ -25,7 +25,7 @@ import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanNameExtractor; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanStatusExtractor; import io.opentelemetry.instrumentation.api.instrumenter.net.NetClientAttributesExtractor; -import io.opentelemetry.instrumentation.api.instrumenter.net.NetServerAttributesExtractor; +import io.opentelemetry.instrumentation.armeria.v1_3.internal.ArmeriaNetClientAttributesGetter; import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; import java.util.ArrayList; import java.util.List; @@ -42,13 +42,16 @@ public final class ArmeriaTelemetryBuilder { private final List> additionalExtractors = new ArrayList<>(); + private final List> + additionalClientExtractors = new ArrayList<>(); private final HttpClientAttributesExtractorBuilder httpClientAttributesExtractorBuilder = HttpClientAttributesExtractor.builder(ArmeriaHttpClientAttributesGetter.INSTANCE); private final HttpServerAttributesExtractorBuilder httpServerAttributesExtractorBuilder = - HttpServerAttributesExtractor.builder(ArmeriaHttpServerAttributesGetter.INSTANCE); + HttpServerAttributesExtractor.builder( + ArmeriaHttpServerAttributesGetter.INSTANCE, new ArmeriaNetServerAttributesGetter()); private Function< SpanStatusExtractor, @@ -59,6 +62,7 @@ public final class ArmeriaTelemetryBuilder { this.openTelemetry = openTelemetry; } + @CanIgnoreReturnValue public ArmeriaTelemetryBuilder setStatusExtractor( Function< SpanStatusExtractor, @@ -72,13 +76,27 @@ public ArmeriaTelemetryBuilder setStatusExtractor( * Adds an additional {@link AttributesExtractor} to invoke to set attributes to instrumented * items. The {@link AttributesExtractor} will be executed after all default extractors. */ + @CanIgnoreReturnValue public ArmeriaTelemetryBuilder addAttributeExtractor( AttributesExtractor attributesExtractor) { additionalExtractors.add(attributesExtractor); return this; } + /** + * Adds an extra client-only {@link AttributesExtractor} to invoke to set attributes to + * instrumented items. The {@link AttributesExtractor} will be executed after all default + * extractors. + */ + @CanIgnoreReturnValue + public ArmeriaTelemetryBuilder addClientAttributeExtractor( + AttributesExtractor attributesExtractor) { + additionalClientExtractors.add(attributesExtractor); + return this; + } + /** Sets the {@code peer.service} attribute for http client spans. */ + @CanIgnoreReturnValue public ArmeriaTelemetryBuilder setPeerService(String peerService) { this.peerService = peerService; return this; @@ -89,6 +107,7 @@ public ArmeriaTelemetryBuilder setPeerService(String peerService) { * * @param requestHeaders A list of HTTP header names. */ + @CanIgnoreReturnValue public ArmeriaTelemetryBuilder setCapturedClientRequestHeaders(List requestHeaders) { httpClientAttributesExtractorBuilder.setCapturedRequestHeaders(requestHeaders); return this; @@ -99,6 +118,7 @@ public ArmeriaTelemetryBuilder setCapturedClientRequestHeaders(List requ * * @param responseHeaders A list of HTTP header names. */ + @CanIgnoreReturnValue public ArmeriaTelemetryBuilder setCapturedClientResponseHeaders(List responseHeaders) { httpClientAttributesExtractorBuilder.setCapturedResponseHeaders(responseHeaders); return this; @@ -109,6 +129,7 @@ public ArmeriaTelemetryBuilder setCapturedClientResponseHeaders(List res * * @param requestHeaders A list of HTTP header names. */ + @CanIgnoreReturnValue public ArmeriaTelemetryBuilder setCapturedServerRequestHeaders(List requestHeaders) { httpServerAttributesExtractorBuilder.setCapturedRequestHeaders(requestHeaders); return this; @@ -119,6 +140,7 @@ public ArmeriaTelemetryBuilder setCapturedServerRequestHeaders(List requ * * @param responseHeaders A list of HTTP header names. */ + @CanIgnoreReturnValue public ArmeriaTelemetryBuilder setCapturedServerResponseHeaders(List responseHeaders) { httpServerAttributesExtractorBuilder.setCapturedResponseHeaders(responseHeaders); return this; @@ -155,13 +177,12 @@ public ArmeriaTelemetry build() { HttpSpanStatusExtractor.create(clientAttributesGetter))) .addAttributesExtractor(netClientAttributesExtractor) .addAttributesExtractor(httpClientAttributesExtractorBuilder.build()) + .addAttributesExtractors(additionalClientExtractors) .addOperationMetrics(HttpClientMetrics.get()); serverInstrumenterBuilder .setSpanStatusExtractor( statusExtractorTransformer.apply( HttpSpanStatusExtractor.create(serverAttributesGetter))) - .addAttributesExtractor( - NetServerAttributesExtractor.create(new ArmeriaNetServerAttributesGetter())) .addAttributesExtractor(httpServerAttributesExtractorBuilder.build()) .addOperationMetrics(HttpServerMetrics.get()) .addContextCustomizer(HttpRouteHolder.get()); @@ -169,13 +190,10 @@ public ArmeriaTelemetry build() { if (peerService != null) { clientInstrumenterBuilder.addAttributesExtractor( AttributesExtractor.constant(SemanticAttributes.PEER_SERVICE, peerService)); - } else { - clientInstrumenterBuilder.addAttributesExtractor( - PeerServiceAttributesExtractor.create(netClientAttributesGetter)); } return new ArmeriaTelemetry( - clientInstrumenterBuilder.newClientInstrumenter(ClientRequestContextSetter.INSTANCE), - serverInstrumenterBuilder.newServerInstrumenter(RequestContextGetter.INSTANCE)); + clientInstrumenterBuilder.buildClientInstrumenter(ClientRequestContextSetter.INSTANCE), + serverInstrumenterBuilder.buildServerInstrumenter(RequestContextGetter.INSTANCE)); } } diff --git a/instrumentation/armeria-1.3/library/src/main/java/io/opentelemetry/instrumentation/armeria/v1_3/internal/ArmeriaNetClientAttributesGetter.java b/instrumentation/armeria-1.3/library/src/main/java/io/opentelemetry/instrumentation/armeria/v1_3/internal/ArmeriaNetClientAttributesGetter.java new file mode 100644 index 000000000000..be39b313e902 --- /dev/null +++ b/instrumentation/armeria-1.3/library/src/main/java/io/opentelemetry/instrumentation/armeria/v1_3/internal/ArmeriaNetClientAttributesGetter.java @@ -0,0 +1,59 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.armeria.v1_3.internal; + +import com.linecorp.armeria.common.HttpRequest; +import com.linecorp.armeria.common.RequestContext; +import com.linecorp.armeria.common.logging.RequestLog; +import io.opentelemetry.instrumentation.api.instrumenter.net.InetSocketAddressNetClientAttributesGetter; +import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import javax.annotation.Nullable; + +/** + * This class is internal and is hence not for public use. Its APIs are unstable and can change at + * any time. + */ +public final class ArmeriaNetClientAttributesGetter + extends InetSocketAddressNetClientAttributesGetter { + + @Override + public String transport(RequestContext ctx, @Nullable RequestLog requestLog) { + return SemanticAttributes.NetTransportValues.IP_TCP; + } + + @Nullable + @Override + public String peerName(RequestContext ctx) { + return request(ctx).uri().getHost(); + } + + @Override + public Integer peerPort(RequestContext ctx) { + return request(ctx).uri().getPort(); + } + + @Override + @Nullable + protected InetSocketAddress getPeerSocketAddress( + RequestContext ctx, @Nullable RequestLog requestLog) { + SocketAddress address = ctx.remoteAddress(); + if (address instanceof InetSocketAddress) { + return (InetSocketAddress) address; + } + return null; + } + + private static HttpRequest request(RequestContext ctx) { + HttpRequest request = ctx.request(); + if (request == null) { + throw new IllegalStateException( + "Context always has a request in decorators, this exception indicates a programming bug."); + } + return request; + } +} diff --git a/instrumentation/armeria-1.3/testing/src/main/java/io/opentelemetry/instrumentation/armeria/v1_3/AbstractArmeriaHttpClientTest.java b/instrumentation/armeria-1.3/testing/src/main/java/io/opentelemetry/instrumentation/armeria/v1_3/AbstractArmeriaHttpClientTest.java index 5e28a687c8ab..9ce8cfd6c71e 100644 --- a/instrumentation/armeria-1.3/testing/src/main/java/io/opentelemetry/instrumentation/armeria/v1_3/AbstractArmeriaHttpClientTest.java +++ b/instrumentation/armeria-1.3/testing/src/main/java/io/opentelemetry/instrumentation/armeria/v1_3/AbstractArmeriaHttpClientTest.java @@ -14,14 +14,10 @@ import com.linecorp.armeria.common.HttpRequest; import com.linecorp.armeria.common.RequestHeaders; import com.linecorp.armeria.common.util.Exceptions; -import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.instrumentation.testing.junit.http.AbstractHttpClientTest; import io.opentelemetry.instrumentation.testing.junit.http.HttpClientTestOptions; -import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; import java.net.URI; -import java.util.HashSet; import java.util.Map; -import java.util.Set; import java.util.concurrent.CompletionException; import java.util.concurrent.atomic.AtomicBoolean; import org.junit.jupiter.api.BeforeEach; @@ -104,12 +100,6 @@ protected void configure(HttpClientTestOptions options) { // armeria requests can't be reused options.disableTestReusedRequest(); options.enableTestReadTimeout(); - - Set> extra = new HashSet<>(); - extra.add(SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH); - extra.add(SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH); - extra.addAll(HttpClientTestOptions.DEFAULT_HTTP_ATTRIBUTES); - options.setHttpAttributes(unused -> extra); } @Test diff --git a/instrumentation/armeria-1.3/testing/src/main/java/io/opentelemetry/instrumentation/armeria/v1_3/AbstractArmeriaHttpServerTest.java b/instrumentation/armeria-1.3/testing/src/main/java/io/opentelemetry/instrumentation/armeria/v1_3/AbstractArmeriaHttpServerTest.java index b31a1f57885b..a8f0f3a67b32 100644 --- a/instrumentation/armeria-1.3/testing/src/main/java/io/opentelemetry/instrumentation/armeria/v1_3/AbstractArmeriaHttpServerTest.java +++ b/instrumentation/armeria-1.3/testing/src/main/java/io/opentelemetry/instrumentation/armeria/v1_3/AbstractArmeriaHttpServerTest.java @@ -6,6 +6,7 @@ package io.opentelemetry.instrumentation.armeria.v1_3; import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.CAPTURE_HEADERS; +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.CAPTURE_HEADERS_AS_JSON; import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.ERROR; import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.EXCEPTION; import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.INDEXED_CHILD; @@ -23,14 +24,10 @@ import com.linecorp.armeria.common.ResponseHeaders; import com.linecorp.armeria.server.Server; import com.linecorp.armeria.server.ServerBuilder; -import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.api.trace.Span; import io.opentelemetry.instrumentation.testing.junit.http.AbstractHttpServerTest; import io.opentelemetry.instrumentation.testing.junit.http.HttpServerTestOptions; import io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint; -import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; -import java.util.HashSet; -import java.util.Set; public abstract class AbstractArmeriaHttpServerTest extends AbstractHttpServerTest { @@ -144,6 +141,22 @@ protected Server setupServer() { MediaType.PLAIN_TEXT_UTF_8), HttpData.ofUtf8(CAPTURE_HEADERS.getBody())))); + sb.service( + CAPTURE_HEADERS_AS_JSON.getPath(), + (ctx, req) -> + testing() + .runWithSpan( + "controller", + () -> + HttpResponse.of( + ResponseHeaders.of( + HttpStatus.valueOf(CAPTURE_HEADERS_AS_JSON.getStatus()), + "X-Test-Response", + req.headers().get("X-Test-Request"), + HttpHeaderNames.CONTENT_TYPE, + MediaType.PLAIN_TEXT_UTF_8), + HttpData.ofUtf8(CAPTURE_HEADERS_AS_JSON.getBody())))); + // Make sure user decorators see spans. sb.decorator( (delegate, ctx, req) -> { @@ -191,15 +204,6 @@ protected final void configure(HttpServerTestOptions options) { return expectedHttpRoute(endpoint); }); - options.setHttpAttributes( - endpoint -> { - Set> keys = new HashSet<>(HttpServerTestOptions.DEFAULT_HTTP_ATTRIBUTES); - keys.add(SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH); - keys.add(SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH); - keys.add(SemanticAttributes.HTTP_SERVER_NAME); - return keys; - }); - options.setTestPathParam(true); } } diff --git a/instrumentation/async-http-client/async-http-client-1.9/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/asynchttpclient/v1_9/AsyncHttpClientHttpAttributesGetter.java b/instrumentation/async-http-client/async-http-client-1.9/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/asynchttpclient/v1_9/AsyncHttpClientHttpAttributesGetter.java index 376b0e0fe621..d6e912a6e3e3 100644 --- a/instrumentation/async-http-client/async-http-client-1.9/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/asynchttpclient/v1_9/AsyncHttpClientHttpAttributesGetter.java +++ b/instrumentation/async-http-client/async-http-client-1.9/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/asynchttpclient/v1_9/AsyncHttpClientHttpAttributesGetter.java @@ -32,19 +32,7 @@ public List requestHeader(Request request, String name) { } @Override - @Nullable - public Long requestContentLength(Request request, @Nullable Response response) { - return null; - } - - @Override - @Nullable - public Long requestContentLengthUncompressed(Request request, @Nullable Response response) { - return null; - } - - @Override - public Integer statusCode(Request request, Response response) { + public Integer statusCode(Request request, Response response, @Nullable Throwable error) { return response.getStatusCode(); } @@ -53,18 +41,6 @@ public String flavor(Request request, @Nullable Response response) { return SemanticAttributes.HttpFlavorValues.HTTP_1_1; } - @Override - @Nullable - public Long responseContentLength(Request request, Response response) { - return null; - } - - @Override - @Nullable - public Long responseContentLengthUncompressed(Request request, Response response) { - return null; - } - @Override public List responseHeader(Request request, Response response, String name) { return response.getHeaders().getOrDefault(name, Collections.emptyList()); diff --git a/instrumentation/async-http-client/async-http-client-1.9/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/asynchttpclient/v1_9/AsyncHttpClientNetAttributesGetter.java b/instrumentation/async-http-client/async-http-client-1.9/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/asynchttpclient/v1_9/AsyncHttpClientNetAttributesGetter.java index 294f6c6e7d33..128f01184a50 100644 --- a/instrumentation/async-http-client/async-http-client-1.9/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/asynchttpclient/v1_9/AsyncHttpClientNetAttributesGetter.java +++ b/instrumentation/async-http-client/async-http-client-1.9/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/asynchttpclient/v1_9/AsyncHttpClientNetAttributesGetter.java @@ -20,18 +20,12 @@ public String transport(Request request, @Nullable Response response) { } @Override - public String peerName(Request request, @Nullable Response response) { + public String peerName(Request request) { return request.getUri().getHost(); } @Override - public Integer peerPort(Request request, @Nullable Response response) { + public Integer peerPort(Request request) { return request.getUri().getPort(); } - - @Override - @Nullable - public String peerIp(Request request, @Nullable Response response) { - return null; - } } diff --git a/instrumentation/async-http-client/async-http-client-1.9/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/asynchttpclient/v1_9/AsyncHttpClientSingletons.java b/instrumentation/async-http-client/async-http-client-1.9/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/asynchttpclient/v1_9/AsyncHttpClientSingletons.java index 149ed060a05a..cf211cb24f43 100644 --- a/instrumentation/async-http-client/async-http-client-1.9/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/asynchttpclient/v1_9/AsyncHttpClientSingletons.java +++ b/instrumentation/async-http-client/async-http-client-1.9/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/asynchttpclient/v1_9/AsyncHttpClientSingletons.java @@ -9,12 +9,13 @@ import com.ning.http.client.Response; import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; -import io.opentelemetry.instrumentation.api.instrumenter.PeerServiceAttributesExtractor; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpClientAttributesExtractor; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpClientMetrics; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanNameExtractor; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanStatusExtractor; import io.opentelemetry.instrumentation.api.instrumenter.net.NetClientAttributesExtractor; +import io.opentelemetry.instrumentation.api.instrumenter.net.PeerServiceAttributesExtractor; +import io.opentelemetry.javaagent.bootstrap.internal.CommonConfig; public final class AsyncHttpClientSingletons { private static final String INSTRUMENTATION_NAME = "io.opentelemetry.async-http-client-1.9"; @@ -33,11 +34,17 @@ public final class AsyncHttpClientSingletons { INSTRUMENTATION_NAME, HttpSpanNameExtractor.create(httpAttributesGetter)) .setSpanStatusExtractor(HttpSpanStatusExtractor.create(httpAttributesGetter)) - .addAttributesExtractor(HttpClientAttributesExtractor.create(httpAttributesGetter)) + .addAttributesExtractor( + HttpClientAttributesExtractor.builder(httpAttributesGetter) + .setCapturedRequestHeaders(CommonConfig.get().getClientRequestHeaders()) + .setCapturedResponseHeaders(CommonConfig.get().getClientResponseHeaders()) + .build()) .addAttributesExtractor(NetClientAttributesExtractor.create(netAttributesGetter)) - .addAttributesExtractor(PeerServiceAttributesExtractor.create(netAttributesGetter)) + .addAttributesExtractor( + PeerServiceAttributesExtractor.create( + netAttributesGetter, CommonConfig.get().getPeerServiceMapping())) .addOperationMetrics(HttpClientMetrics.get()) - .newClientInstrumenter(HttpHeaderSetter.INSTANCE); + .buildClientInstrumenter(HttpHeaderSetter.INSTANCE); } public static Instrumenter instrumenter() { diff --git a/instrumentation/async-http-client/async-http-client-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/asynchttpclient/v2_0/AsyncHttpClientHttpAttributesGetter.java b/instrumentation/async-http-client/async-http-client-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/asynchttpclient/v2_0/AsyncHttpClientHttpAttributesGetter.java index 4212387286e1..0605e1ac4806 100644 --- a/instrumentation/async-http-client/async-http-client-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/asynchttpclient/v2_0/AsyncHttpClientHttpAttributesGetter.java +++ b/instrumentation/async-http-client/async-http-client-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/asynchttpclient/v2_0/AsyncHttpClientHttpAttributesGetter.java @@ -10,7 +10,6 @@ import java.util.List; import javax.annotation.Nullable; import org.asynchttpclient.Response; -import org.asynchttpclient.netty.request.NettyRequest; final class AsyncHttpClientHttpAttributesGetter implements HttpClientAttributesGetter { @@ -31,31 +30,8 @@ public List requestHeader(RequestContext requestContext, String name) { } @Override - @Nullable - public Long requestContentLength(RequestContext requestContext, @Nullable Response response) { - NettyRequest nettyRequest = requestContext.getNettyRequest(); - if (nettyRequest != null) { - String contentLength = nettyRequest.getHttpRequest().headers().get("Content-Length"); - if (contentLength != null) { - try { - return Long.valueOf(contentLength); - } catch (NumberFormatException ignored) { - // ignore - } - } - } - return null; - } - - @Override - @Nullable - public Long requestContentLengthUncompressed( - RequestContext requestContext, @Nullable Response response) { - return null; - } - - @Override - public Integer statusCode(RequestContext requestContext, Response response) { + public Integer statusCode( + RequestContext requestContext, Response response, @Nullable Throwable error) { return response.getStatusCode(); } @@ -64,26 +40,6 @@ public String flavor(RequestContext requestContext, @Nullable Response response) return SemanticAttributes.HttpFlavorValues.HTTP_1_1; } - @Override - @Nullable - public Long responseContentLength(RequestContext requestContext, Response response) { - String contentLength = response.getHeaders().get("Content-Length"); - if (contentLength != null) { - try { - return Long.valueOf(contentLength); - } catch (NumberFormatException ignored) { - // ignore - } - } - return null; - } - - @Override - @Nullable - public Long responseContentLengthUncompressed(RequestContext requestContext, Response response) { - return null; - } - @Override public List responseHeader( RequestContext requestContext, Response response, String name) { diff --git a/instrumentation/async-http-client/async-http-client-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/asynchttpclient/v2_0/AsyncHttpClientNetAttributesGetter.java b/instrumentation/async-http-client/async-http-client-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/asynchttpclient/v2_0/AsyncHttpClientNetAttributesGetter.java index 3f720975e7bb..b9e4c8cc4f1a 100644 --- a/instrumentation/async-http-client/async-http-client-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/asynchttpclient/v2_0/AsyncHttpClientNetAttributesGetter.java +++ b/instrumentation/async-http-client/async-http-client-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/asynchttpclient/v2_0/AsyncHttpClientNetAttributesGetter.java @@ -19,9 +19,21 @@ public String transport(RequestContext request, @Nullable Response response) { return SemanticAttributes.NetTransportValues.IP_TCP; } + @Nullable + @Override + public String peerName(RequestContext requestContext) { + return requestContext.getRequest().getUri().getHost(); + } + + @Override + public Integer peerPort(RequestContext requestContext) { + return requestContext.getRequest().getUri().getPort(); + } + @Override @Nullable - public InetSocketAddress getAddress(RequestContext request, @Nullable Response response) { + protected InetSocketAddress getPeerSocketAddress( + RequestContext request, @Nullable Response response) { if (response != null && response.getRemoteAddress() instanceof InetSocketAddress) { return (InetSocketAddress) response.getRemoteAddress(); } diff --git a/instrumentation/async-http-client/async-http-client-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/asynchttpclient/v2_0/AsyncHttpClientSingletons.java b/instrumentation/async-http-client/async-http-client-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/asynchttpclient/v2_0/AsyncHttpClientSingletons.java index 32e6a1abaf03..3ca1f037b307 100644 --- a/instrumentation/async-http-client/async-http-client-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/asynchttpclient/v2_0/AsyncHttpClientSingletons.java +++ b/instrumentation/async-http-client/async-http-client-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/asynchttpclient/v2_0/AsyncHttpClientSingletons.java @@ -7,12 +7,13 @@ import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; -import io.opentelemetry.instrumentation.api.instrumenter.PeerServiceAttributesExtractor; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpClientAttributesExtractor; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpClientMetrics; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanNameExtractor; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanStatusExtractor; import io.opentelemetry.instrumentation.api.instrumenter.net.NetClientAttributesExtractor; +import io.opentelemetry.instrumentation.api.instrumenter.net.PeerServiceAttributesExtractor; +import io.opentelemetry.javaagent.bootstrap.internal.CommonConfig; import org.asynchttpclient.Response; public final class AsyncHttpClientSingletons { @@ -32,12 +33,18 @@ public final class AsyncHttpClientSingletons { INSTRUMENTATION_NAME, HttpSpanNameExtractor.create(httpAttributesGetter)) .setSpanStatusExtractor(HttpSpanStatusExtractor.create(httpAttributesGetter)) - .addAttributesExtractor(HttpClientAttributesExtractor.create(httpAttributesGetter)) + .addAttributesExtractor( + HttpClientAttributesExtractor.builder(httpAttributesGetter) + .setCapturedRequestHeaders(CommonConfig.get().getClientRequestHeaders()) + .setCapturedResponseHeaders(CommonConfig.get().getClientResponseHeaders()) + .build()) .addAttributesExtractor(NetClientAttributesExtractor.create(netAttributeGetter)) - .addAttributesExtractor(PeerServiceAttributesExtractor.create(netAttributeGetter)) + .addAttributesExtractor( + PeerServiceAttributesExtractor.create( + netAttributeGetter, CommonConfig.get().getPeerServiceMapping())) .addAttributesExtractor(new AsyncHttpClientAdditionalAttributesExtractor()) .addOperationMetrics(HttpClientMetrics.get()) - .newClientInstrumenter(HttpHeaderSetter.INSTANCE); + .buildClientInstrumenter(HttpHeaderSetter.INSTANCE); } public static Instrumenter instrumenter() { diff --git a/instrumentation/async-http-client/async-http-client-2.0/javaagent/src/test/groovy/AsyncHttpClientTest.groovy b/instrumentation/async-http-client/async-http-client-2.0/javaagent/src/test/groovy/AsyncHttpClientTest.groovy index 0d8808b620fd..cef53131ac02 100644 --- a/instrumentation/async-http-client/async-http-client-2.0/javaagent/src/test/groovy/AsyncHttpClientTest.groovy +++ b/instrumentation/async-http-client/async-http-client-2.0/javaagent/src/test/groovy/AsyncHttpClientTest.groovy @@ -72,14 +72,6 @@ class AsyncHttpClientTest extends HttpClientTest implements AgentTestTr SemanticAttributes.HTTP_SCHEME, SemanticAttributes.HTTP_TARGET ] - switch (uri.toString()) { - case "http://localhost:61/": // unopened port - case "https://192.0.2.1/": // non routable address - break - default: - extra.add(SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH) - } - super.httpAttributes(uri) + extra } } diff --git a/instrumentation/aws-lambda/aws-lambda-core-1.0/javaagent/build.gradle.kts b/instrumentation/aws-lambda/aws-lambda-core-1.0/javaagent/build.gradle.kts index ac1f53dc9a73..68cfa9a0b6e8 100644 --- a/instrumentation/aws-lambda/aws-lambda-core-1.0/javaagent/build.gradle.kts +++ b/instrumentation/aws-lambda/aws-lambda-core-1.0/javaagent/build.gradle.kts @@ -21,3 +21,10 @@ dependencies { testImplementation(project(":instrumentation:aws-lambda:aws-lambda-core-1.0:testing")) testInstrumentation(project(":instrumentation:aws-lambda:aws-lambda-events-2.2:javaagent")) } + +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-core-1.0/library/README.md b/instrumentation/aws-lambda/aws-lambda-core-1.0/library/README.md index 464fd291fd2c..5752554a0dca 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 @@ -48,6 +48,7 @@ link to tracing information provided by Lambda itself. To do so, add a dependenc you use. Gradle: + ```kotlin dependencies { implementation("io.opentelemetry:opentelemetry-extension-trace-propagators:0.8.0") @@ -55,6 +56,7 @@ dependencies { ``` Maven: + ```xml diff --git a/instrumentation/aws-lambda/aws-lambda-core-1.0/library/build.gradle.kts b/instrumentation/aws-lambda/aws-lambda-core-1.0/library/build.gradle.kts index a2669a01a9fa..ab7d240b8b60 100644 --- a/instrumentation/aws-lambda/aws-lambda-core-1.0/library/build.gradle.kts +++ b/instrumentation/aws-lambda/aws-lambda-core-1.0/library/build.gradle.kts @@ -29,3 +29,9 @@ dependencies { testImplementation(project(":instrumentation:aws-lambda:aws-lambda-core-1.0:testing")) testImplementation("uk.org.webcompere:system-stubs-jupiter") } + +tasks.withType().configureEach { + // required on jdk17 + jvmArgs("--add-opens=java.base/java.util=ALL-UNNAMED") + jvmArgs("-XX:+IgnoreUnrecognizedVMOptions") +} diff --git a/instrumentation/aws-lambda/aws-lambda-core-1.0/library/src/main/java/io/opentelemetry/instrumentation/awslambdacore/v1_0/internal/AwsLambdaFunctionInstrumenterFactory.java b/instrumentation/aws-lambda/aws-lambda-core-1.0/library/src/main/java/io/opentelemetry/instrumentation/awslambdacore/v1_0/internal/AwsLambdaFunctionInstrumenterFactory.java index 3a81435de3d5..277c358ca8e2 100644 --- a/instrumentation/aws-lambda/aws-lambda-core-1.0/library/src/main/java/io/opentelemetry/instrumentation/awslambdacore/v1_0/internal/AwsLambdaFunctionInstrumenterFactory.java +++ b/instrumentation/aws-lambda/aws-lambda-core-1.0/library/src/main/java/io/opentelemetry/instrumentation/awslambdacore/v1_0/internal/AwsLambdaFunctionInstrumenterFactory.java @@ -14,7 +14,7 @@ * This class is internal and is hence not for public use. Its APIs are unstable and can change at * any time. */ -public class AwsLambdaFunctionInstrumenterFactory { +public final class AwsLambdaFunctionInstrumenterFactory { public static AwsLambdaFunctionInstrumenter createInstrumenter(OpenTelemetry openTelemetry) { return new AwsLambdaFunctionInstrumenter( @@ -23,11 +23,13 @@ public static AwsLambdaFunctionInstrumenter createInstrumenter(OpenTelemetry ope openTelemetry, "io.opentelemetry.aws-lambda-core-1.0", AwsLambdaFunctionInstrumenterFactory::spanName) - .addAttributesExtractors(new AwsLambdaFunctionAttributesExtractor()) - .newInstrumenter(SpanKindExtractor.alwaysServer())); + .addAttributesExtractor(new AwsLambdaFunctionAttributesExtractor()) + .buildInstrumenter(SpanKindExtractor.alwaysServer())); } private static String spanName(AwsLambdaRequest input) { return input.getAwsContext().getFunctionName(); } + + private AwsLambdaFunctionInstrumenterFactory() {} } diff --git a/instrumentation/aws-lambda/aws-lambda-core-1.0/library/src/test/java/io/opentelemetry/instrumentation/awslambdacore/v1_0/internal/ApiGatewayProxyRequestTest.java b/instrumentation/aws-lambda/aws-lambda-core-1.0/library/src/test/java/io/opentelemetry/instrumentation/awslambdacore/v1_0/internal/ApiGatewayProxyRequestTest.java index a59b08d54a2a..b1a7ca2e98ba 100644 --- a/instrumentation/aws-lambda/aws-lambda-core-1.0/library/src/test/java/io/opentelemetry/instrumentation/awslambdacore/v1_0/internal/ApiGatewayProxyRequestTest.java +++ b/instrumentation/aws-lambda/aws-lambda-core-1.0/library/src/test/java/io/opentelemetry/instrumentation/awslambdacore/v1_0/internal/ApiGatewayProxyRequestTest.java @@ -7,10 +7,10 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.BDDMockito.given; -import static org.mockito.BDDMockito.then; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.api.OpenTelemetry; @@ -58,23 +58,23 @@ public void shouldCreateNoopRequestIfXrayPropagatorsSet() throws IOException { public void shouldUseStreamMarkingIfHttpPropagatorsSet() throws IOException { // given InputStream mock = mock(InputStream.class); - given(mock.markSupported()).willReturn(true); + when(mock.markSupported()).thenReturn(true); GlobalOpenTelemetry.set( OpenTelemetry.propagating(ContextPropagators.create(B3Propagator.injectingSingleHeader()))); // when ApiGatewayProxyRequest created = ApiGatewayProxyRequest.forStream(mock); // then assertThat(created.freshStream()).isEqualTo(mock); - then(mock).should(atLeastOnce()).mark(Integer.MAX_VALUE); - then(mock).should().reset(); + verify(mock, atLeastOnce()).mark(Integer.MAX_VALUE); + verify(mock).reset(); } @Test public void shouldUseNoopIfMarkingNotAvailableAndHttpPropagatorsSet() throws IOException { // given InputStream mock = mock(InputStream.class); - given(mock.markSupported()).willReturn(false); - given(mock.read(any(byte[].class))).willReturn(-1); + when(mock.markSupported()).thenReturn(false); + when(mock.read(any(byte[].class))).thenReturn(-1); GlobalOpenTelemetry.set( OpenTelemetry.propagating(ContextPropagators.create(B3Propagator.injectingSingleHeader()))); // when diff --git a/instrumentation/aws-lambda/aws-lambda-events-2.2/library/README.md b/instrumentation/aws-lambda/aws-lambda-events-2.2/library/README.md index 94e851f2905b..c812b01a50d9 100644 --- a/instrumentation/aws-lambda/aws-lambda-events-2.2/library/README.md +++ b/instrumentation/aws-lambda/aws-lambda-events-2.2/library/README.md @@ -58,6 +58,7 @@ dependencies { ``` Maven: + ```xml 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 01bbd85dc307..43c251f71f87 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 @@ -37,3 +37,9 @@ dependencies { 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.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/AwsLambdaEventsInstrumenterFactory.java b/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/internal/AwsLambdaEventsInstrumenterFactory.java index 062095fbceac..99fa10c5c348 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-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/internal/AwsLambdaEventsInstrumenterFactory.java @@ -17,7 +17,7 @@ * This class is internal and is hence not for public use. Its APIs are unstable and can change at * any time. */ -public class AwsLambdaEventsInstrumenterFactory { +public final class AwsLambdaEventsInstrumenterFactory { public static AwsLambdaFunctionInstrumenter createInstrumenter(OpenTelemetry openTelemetry) { return new AwsLambdaFunctionInstrumenter( @@ -26,10 +26,9 @@ public static AwsLambdaFunctionInstrumenter createInstrumenter(OpenTelemetry ope openTelemetry, "io.opentelemetry.aws-lambda-events-2.2", AwsLambdaEventsInstrumenterFactory::spanName) - .addAttributesExtractors( - new AwsLambdaFunctionAttributesExtractor(), - new ApiGatewayProxyAttributesExtractor()) - .newInstrumenter(SpanKindExtractor.alwaysServer())); + .addAttributesExtractor(new AwsLambdaFunctionAttributesExtractor()) + .addAttributesExtractor(new ApiGatewayProxyAttributesExtractor()) + .buildInstrumenter(SpanKindExtractor.alwaysServer())); } private static String spanName(AwsLambdaRequest input) { @@ -39,4 +38,6 @@ private static String spanName(AwsLambdaRequest input) { } return name == null ? input.getAwsContext().getFunctionName() : name; } + + private AwsLambdaEventsInstrumenterFactory() {} } 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-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/internal/AwsLambdaSqsInstrumenterFactory.java index 70d7ece91e6c..3433b667e0f1 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-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/internal/AwsLambdaSqsInstrumenterFactory.java @@ -15,16 +15,16 @@ * This class is internal and is hence not for public use. Its APIs are unstable and can change at * any time. */ -public class AwsLambdaSqsInstrumenterFactory { +public final class AwsLambdaSqsInstrumenterFactory { public static Instrumenter forEvent(OpenTelemetry openTelemetry) { return Instrumenter.builder( openTelemetry, "io.opentelemetry.aws-lambda-events-2.2", AwsLambdaSqsInstrumenterFactory::spanName) - .addAttributesExtractors(new SqsEventAttributesExtractor()) + .addAttributesExtractor(new SqsEventAttributesExtractor()) .addSpanLinksExtractor(new SqsEventSpanLinksExtractor()) - .newInstrumenter(SpanKindExtractor.alwaysConsumer()); + .buildInstrumenter(SpanKindExtractor.alwaysConsumer()); } public static Instrumenter forMessage(OpenTelemetry openTelemetry) { @@ -32,9 +32,9 @@ public static Instrumenter forMessage(OpenTelemetry openTeleme openTelemetry, "io.opentelemetry.aws-lambda-events-2.2", message -> message.getEventSource() + " process") - .addAttributesExtractors(new SqsMessageAttributesExtractor()) + .addAttributesExtractor(new SqsMessageAttributesExtractor()) .addSpanLinksExtractor(new SqsMessageSpanLinksExtractor()) - .newInstrumenter(SpanKindExtractor.alwaysConsumer()); + .buildInstrumenter(SpanKindExtractor.alwaysConsumer()); } private static String spanName(SQSEvent event) { @@ -55,4 +55,6 @@ private static String spanName(SQSEvent event) { return source + " process"; } + + private AwsLambdaSqsInstrumenterFactory() {} } diff --git a/instrumentation/aws-sdk/aws-sdk-1.11/javaagent/src/test/groovy/S3TracingTest.groovy b/instrumentation/aws-sdk/aws-sdk-1.11/javaagent/src/test/groovy/S3TracingTest.groovy index aa4d5daf99ef..1101c8539371 100644 --- a/instrumentation/aws-sdk/aws-sdk-1.11/javaagent/src/test/groovy/S3TracingTest.groovy +++ b/instrumentation/aws-sdk/aws-sdk-1.11/javaagent/src/test/groovy/S3TracingTest.groovy @@ -4,6 +4,7 @@ */ import io.opentelemetry.instrumentation.test.AgentInstrumentationSpecification +import io.opentelemetry.semconv.trace.attributes.SemanticAttributes import spock.lang.Shared import static io.opentelemetry.api.trace.SpanKind.CLIENT @@ -63,6 +64,8 @@ class S3TracingTest extends AgentInstrumentationSpecification { "net.peer.name" String "net.transport" IP_TCP "net.peer.port" { it == null || Number } + "$SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH" { it == null || it instanceof Long } + "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" { it == null || it instanceof Long } } } } @@ -86,6 +89,8 @@ class S3TracingTest extends AgentInstrumentationSpecification { "net.peer.name" String "net.transport" IP_TCP "net.peer.port" { it == null || Number } + "$SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH" { it == null || it instanceof Long } + "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" { it == null || it instanceof Long } } } } @@ -109,6 +114,8 @@ class S3TracingTest extends AgentInstrumentationSpecification { "net.peer.name" String "net.transport" IP_TCP "net.peer.port" { it == null || Number } + "$SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH" { it == null || it instanceof Long } + "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" { it == null || it instanceof Long } } } } @@ -132,6 +139,8 @@ class S3TracingTest extends AgentInstrumentationSpecification { "net.peer.name" String "net.transport" IP_TCP "net.peer.port" { it == null || Number } + "$SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH" { it == null || it instanceof Long } + "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" { it == null || it instanceof Long } } } } @@ -155,6 +164,8 @@ class S3TracingTest extends AgentInstrumentationSpecification { "net.peer.name" String "net.transport" IP_TCP "net.peer.port" { it == null || Number } + "$SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH" { it == null || it instanceof Long } + "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" { it == null || it instanceof Long } } } } @@ -177,6 +188,8 @@ class S3TracingTest extends AgentInstrumentationSpecification { "net.peer.name" String "net.transport" IP_TCP "net.peer.port" { it == null || Number } + "$SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH" { it == null || it instanceof Long } + "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" { it == null || it instanceof Long } } } } @@ -199,6 +212,8 @@ class S3TracingTest extends AgentInstrumentationSpecification { "net.peer.name" String "net.transport" IP_TCP "net.peer.port" { it == null || Number } + "$SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH" { it == null || it instanceof Long } + "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" { it == null || it instanceof Long } } } span(1) { @@ -220,6 +235,8 @@ class S3TracingTest extends AgentInstrumentationSpecification { "net.peer.name" String "net.transport" IP_TCP "net.peer.port" { it == null || Number } + "$SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH" { it == null || it instanceof Long } + "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" { it == null || it instanceof Long } } } } @@ -247,6 +264,8 @@ class S3TracingTest extends AgentInstrumentationSpecification { "net.peer.name" String "net.transport" IP_TCP "net.peer.port" { it == null || Number } + "$SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH" { it == null || it instanceof Long } + "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" { it == null || it instanceof Long } } } } @@ -269,6 +288,8 @@ class S3TracingTest extends AgentInstrumentationSpecification { "net.peer.name" String "net.transport" IP_TCP "net.peer.port" { it == null || Number } + "$SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH" { it == null || it instanceof Long } + "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" { it == null || it instanceof Long } } } } @@ -291,6 +312,8 @@ class S3TracingTest extends AgentInstrumentationSpecification { "net.peer.name" String "net.transport" IP_TCP "net.peer.port" { it == null || Number } + "$SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH" { it == null || it instanceof Long } + "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" { it == null || it instanceof Long } } } } @@ -313,6 +336,8 @@ class S3TracingTest extends AgentInstrumentationSpecification { "net.peer.name" String "net.transport" IP_TCP "net.peer.port" { it == null || Number } + "$SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH" { it == null || it instanceof Long } + "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" { it == null || it instanceof Long } } } } @@ -335,6 +360,8 @@ class S3TracingTest extends AgentInstrumentationSpecification { "net.peer.name" String "net.transport" IP_TCP "net.peer.port" { it == null || Number } + "$SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH" { it == null || it instanceof Long } + "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" { it == null || it instanceof Long } } } } @@ -387,6 +414,8 @@ class S3TracingTest extends AgentInstrumentationSpecification { "net.peer.name" String "net.transport" IP_TCP "net.peer.port" { it == null || Number } + "$SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH" { it == null || it instanceof Long } + "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" { it == null || it instanceof Long } } } } @@ -409,6 +438,8 @@ class S3TracingTest extends AgentInstrumentationSpecification { "net.peer.name" String "net.transport" IP_TCP "net.peer.port" { it == null || Number } + "$SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH" { it == null || it instanceof Long } + "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" { it == null || it instanceof Long } } } } @@ -431,6 +462,8 @@ class S3TracingTest extends AgentInstrumentationSpecification { "net.peer.name" String "net.transport" IP_TCP "net.peer.port" { it == null || Number } + "$SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH" { it == null || it instanceof Long } + "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" { it == null || it instanceof Long } } } } @@ -452,6 +485,8 @@ class S3TracingTest extends AgentInstrumentationSpecification { "net.peer.name" String "net.transport" IP_TCP "net.peer.port" { it == null || Number } + "$SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH" { it == null || it instanceof Long } + "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" { it == null || it instanceof Long } } } } @@ -473,6 +508,8 @@ class S3TracingTest extends AgentInstrumentationSpecification { "net.peer.name" String "net.transport" IP_TCP "net.peer.port" { it == null || Number } + "$SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH" { it == null || it instanceof Long } + "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" { it == null || it instanceof Long } } } } @@ -495,6 +532,8 @@ class S3TracingTest extends AgentInstrumentationSpecification { "net.peer.name" String "net.transport" IP_TCP "net.peer.port" { it == null || Number } + "$SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH" { it == null || it instanceof Long } + "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" { it == null || it instanceof Long } } } } @@ -516,6 +555,8 @@ class S3TracingTest extends AgentInstrumentationSpecification { "net.peer.name" String "net.transport" IP_TCP "net.peer.port" { it == null || Number } + "$SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH" { it == null || it instanceof Long } + "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" { it == null || it instanceof Long } } } } @@ -538,6 +579,8 @@ class S3TracingTest extends AgentInstrumentationSpecification { "net.peer.name" String "net.transport" IP_TCP "net.peer.port" { it == null || Number } + "$SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH" { it == null || it instanceof Long } + "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" { it == null || it instanceof Long } } } } @@ -561,6 +604,8 @@ class S3TracingTest extends AgentInstrumentationSpecification { "net.peer.name" String "net.transport" IP_TCP "net.peer.port" { it == null || Number } + "$SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH" { it == null || it instanceof Long } + "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" { it == null || it instanceof Long } } } } @@ -583,6 +628,8 @@ class S3TracingTest extends AgentInstrumentationSpecification { "net.peer.name" String "net.transport" IP_TCP "net.peer.port" { it == null || Number } + "$SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH" { it == null || it instanceof Long } + "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" { it == null || it instanceof Long } } } } @@ -609,6 +656,8 @@ class S3TracingTest extends AgentInstrumentationSpecification { "net.peer.name" String "net.transport" IP_TCP "net.peer.port" { it == null || Number } + "$SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH" { it == null || it instanceof Long } + "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" { it == null || it instanceof Long } } } } @@ -632,6 +681,8 @@ class S3TracingTest extends AgentInstrumentationSpecification { "net.peer.name" String "net.transport" IP_TCP "net.peer.port" { it == null || Number } + "$SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH" { it == null || it instanceof Long } + "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" { it == null || it instanceof Long } } } } @@ -654,6 +705,8 @@ class S3TracingTest extends AgentInstrumentationSpecification { "net.peer.name" String "net.transport" IP_TCP "net.peer.port" { it == null || Number } + "$SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH" { it == null || it instanceof Long } + "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" { it == null || it instanceof Long } } } } @@ -676,6 +729,8 @@ class S3TracingTest extends AgentInstrumentationSpecification { "net.peer.name" String "net.transport" IP_TCP "net.peer.port" { it == null || Number } + "$SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH" { it == null || it instanceof Long } + "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" { it == null || it instanceof Long } } } } @@ -698,6 +753,8 @@ class S3TracingTest extends AgentInstrumentationSpecification { "net.peer.name" String "net.transport" IP_TCP "net.peer.port" { it == null || Number } + "$SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH" { it == null || it instanceof Long } + "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" { it == null || it instanceof Long } } } } @@ -720,6 +777,8 @@ class S3TracingTest extends AgentInstrumentationSpecification { "net.peer.name" String "net.transport" IP_TCP "net.peer.port" { it == null || Number } + "$SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH" { it == null || it instanceof Long } + "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" { it == null || it instanceof Long } } } } diff --git a/instrumentation/aws-sdk/aws-sdk-1.11/javaagent/src/test/groovy/SnsTracingTest.groovy b/instrumentation/aws-sdk/aws-sdk-1.11/javaagent/src/test/groovy/SnsTracingTest.groovy index 98e9bfc96a0a..cbe9e5c59267 100644 --- a/instrumentation/aws-sdk/aws-sdk-1.11/javaagent/src/test/groovy/SnsTracingTest.groovy +++ b/instrumentation/aws-sdk/aws-sdk-1.11/javaagent/src/test/groovy/SnsTracingTest.groovy @@ -4,6 +4,7 @@ */ import io.opentelemetry.instrumentation.test.AgentInstrumentationSpecification +import io.opentelemetry.semconv.trace.attributes.SemanticAttributes import spock.lang.Shared import static io.opentelemetry.api.trace.SpanKind.CLIENT @@ -56,6 +57,7 @@ class SnsTracingTest extends AgentInstrumentationSpecification { "net.peer.name" String "net.transport" IP_TCP "net.peer.port" { it == null || Number } + "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" Long } } } @@ -79,6 +81,7 @@ class SnsTracingTest extends AgentInstrumentationSpecification { "net.peer.name" String "net.transport" IP_TCP "net.peer.port" { it == null || Number } + "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" Long } } } @@ -102,6 +105,7 @@ class SnsTracingTest extends AgentInstrumentationSpecification { "net.peer.name" String "net.transport" IP_TCP "net.peer.port" { it == null || Number } + "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" Long } } } @@ -124,6 +128,7 @@ class SnsTracingTest extends AgentInstrumentationSpecification { "net.peer.name" String "net.transport" IP_TCP "net.peer.port" { it == null || Number } + "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" Long } } } @@ -146,6 +151,7 @@ class SnsTracingTest extends AgentInstrumentationSpecification { "net.peer.name" String "net.transport" IP_TCP "net.peer.port" { it == null || Number } + "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" Long } } } @@ -167,6 +173,7 @@ class SnsTracingTest extends AgentInstrumentationSpecification { "net.peer.name" String "net.transport" IP_TCP "net.peer.port" { it == null || Number } + "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" Long } } span(1) { @@ -188,6 +195,7 @@ class SnsTracingTest extends AgentInstrumentationSpecification { "net.peer.name" String "net.transport" IP_TCP "net.peer.port" { it == null || Number } + "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" Long } } } @@ -214,6 +222,7 @@ class SnsTracingTest extends AgentInstrumentationSpecification { "net.peer.name" String "net.transport" IP_TCP "net.peer.port" { it == null || Number } + "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" Long } } } diff --git a/instrumentation/aws-sdk/aws-sdk-1.11/javaagent/src/test_before_1_11_106/groovy/Aws0ClientTest.groovy b/instrumentation/aws-sdk/aws-sdk-1.11/javaagent/src/test_before_1_11_106/groovy/Aws0ClientTest.groovy index ee706e35aea9..089e7afbabbb 100644 --- a/instrumentation/aws-sdk/aws-sdk-1.11/javaagent/src/test_before_1_11_106/groovy/Aws0ClientTest.groovy +++ b/instrumentation/aws-sdk/aws-sdk-1.11/javaagent/src/test_before_1_11_106/groovy/Aws0ClientTest.groovy @@ -110,6 +110,7 @@ class Aws0ClientTest extends AgentInstrumentationSpecification { "$SemanticAttributes.HTTP_METHOD" "$method" "$SemanticAttributes.HTTP_STATUS_CODE" 200 "$SemanticAttributes.HTTP_FLAVOR" "1.1" + "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" Long "$SemanticAttributes.NET_PEER_PORT" server.httpPort() "$SemanticAttributes.NET_PEER_NAME" "127.0.0.1" "$SemanticAttributes.RPC_SYSTEM" "aws-api" diff --git a/instrumentation/aws-sdk/aws-sdk-1.11/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v1_11/AwsSdkHttpAttributesGetter.java b/instrumentation/aws-sdk/aws-sdk-1.11/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v1_11/AwsSdkHttpAttributesGetter.java index e628f4391fb0..80d02c9d91d9 100644 --- a/instrumentation/aws-sdk/aws-sdk-1.11/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v1_11/AwsSdkHttpAttributesGetter.java +++ b/instrumentation/aws-sdk/aws-sdk-1.11/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v1_11/AwsSdkHttpAttributesGetter.java @@ -40,34 +40,10 @@ public List requestHeader(Request request, String name) { } @Override - @Nullable - public Long requestContentLength(Request request, @Nullable Response response) { - return null; - } - - @Override - @Nullable - public Long requestContentLengthUncompressed(Request request, @Nullable Response response) { - return null; - } - - @Override - public Integer statusCode(Request request, Response response) { + public Integer statusCode(Request request, Response response, @Nullable Throwable error) { return response.getHttpResponse().getStatusCode(); } - @Override - @Nullable - public Long responseContentLength(Request request, Response response) { - return null; - } - - @Override - @Nullable - public Long responseContentLengthUncompressed(Request request, Response response) { - return null; - } - @Override public List responseHeader(Request request, Response response, String name) { String value = response.getHttpResponse().getHeaders().get(name); diff --git a/instrumentation/aws-sdk/aws-sdk-1.11/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v1_11/AwsSdkInstrumenterFactory.java b/instrumentation/aws-sdk/aws-sdk-1.11/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v1_11/AwsSdkInstrumenterFactory.java index d6a3ac6b6d39..d2fba68e6ef2 100644 --- a/instrumentation/aws-sdk/aws-sdk-1.11/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v1_11/AwsSdkInstrumenterFactory.java +++ b/instrumentation/aws-sdk/aws-sdk-1.11/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v1_11/AwsSdkInstrumenterFactory.java @@ -68,7 +68,7 @@ private static Instrumenter, Response> createInstrumenter( captureExperimentalSpanAttributes ? extendedAttributesExtractors : defaultAttributesExtractors) - .newInstrumenter(kindExtractor); + .buildInstrumenter(kindExtractor); } private AwsSdkInstrumenterFactory() {} diff --git a/instrumentation/aws-sdk/aws-sdk-1.11/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v1_11/AwsSdkNetAttributesGetter.java b/instrumentation/aws-sdk/aws-sdk-1.11/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v1_11/AwsSdkNetAttributesGetter.java index ee9ec7584106..8389ce598f94 100644 --- a/instrumentation/aws-sdk/aws-sdk-1.11/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v1_11/AwsSdkNetAttributesGetter.java +++ b/instrumentation/aws-sdk/aws-sdk-1.11/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v1_11/AwsSdkNetAttributesGetter.java @@ -20,18 +20,12 @@ public String transport(Request request, @Nullable Response response) { @Override @Nullable - public String peerName(Request request, @Nullable Response response) { + public String peerName(Request request) { return request.getEndpoint().getHost(); } @Override - public Integer peerPort(Request request, @Nullable Response response) { + public Integer peerPort(Request request) { return request.getEndpoint().getPort(); } - - @Override - @Nullable - public String peerIp(Request request, @Nullable Response response) { - return null; - } } diff --git a/instrumentation/aws-sdk/aws-sdk-1.11/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v1_11/AwsSdkTelemetryBuilder.java b/instrumentation/aws-sdk/aws-sdk-1.11/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v1_11/AwsSdkTelemetryBuilder.java index c0e270dea2c8..7ecb5341c8d2 100644 --- a/instrumentation/aws-sdk/aws-sdk-1.11/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v1_11/AwsSdkTelemetryBuilder.java +++ b/instrumentation/aws-sdk/aws-sdk-1.11/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v1_11/AwsSdkTelemetryBuilder.java @@ -5,6 +5,7 @@ package io.opentelemetry.instrumentation.awssdk.v1_11; +import com.google.errorprone.annotations.CanIgnoreReturnValue; import io.opentelemetry.api.OpenTelemetry; /** A builder of {@link AwsSdkTelemetry}. */ @@ -23,6 +24,7 @@ public class AwsSdkTelemetryBuilder { * removed in the future, so only enable this if you know you do not require attributes filled by * this instrumentation to be stable across versions */ + @CanIgnoreReturnValue public AwsSdkTelemetryBuilder setCaptureExperimentalSpanAttributes( boolean captureExperimentalSpanAttributes) { this.captureExperimentalSpanAttributes = captureExperimentalSpanAttributes; diff --git a/instrumentation/aws-sdk/aws-sdk-1.11/testing/src/main/groovy/io/opentelemetry/instrumentation/awssdk/v1_11/AbstractAws1ClientTest.groovy b/instrumentation/aws-sdk/aws-sdk-1.11/testing/src/main/groovy/io/opentelemetry/instrumentation/awssdk/v1_11/AbstractAws1ClientTest.groovy index 93a3e070e9f7..0d12342c8be5 100644 --- a/instrumentation/aws-sdk/aws-sdk-1.11/testing/src/main/groovy/io/opentelemetry/instrumentation/awssdk/v1_11/AbstractAws1ClientTest.groovy +++ b/instrumentation/aws-sdk/aws-sdk-1.11/testing/src/main/groovy/io/opentelemetry/instrumentation/awssdk/v1_11/AbstractAws1ClientTest.groovy @@ -108,6 +108,8 @@ abstract class AbstractAws1ClientTest extends InstrumentationSpecification { "$SemanticAttributes.HTTP_METHOD" "$method" "$SemanticAttributes.HTTP_STATUS_CODE" 200 "$SemanticAttributes.HTTP_FLAVOR" "1.1" + "$SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH" { it == null || it instanceof Long } + "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" Long "$SemanticAttributes.NET_PEER_PORT" server.httpPort() "$SemanticAttributes.NET_PEER_NAME" "127.0.0.1" "$SemanticAttributes.RPC_SYSTEM" "aws-api" diff --git a/instrumentation/aws-sdk/aws-sdk-1.11/testing/src/main/groovy/io/opentelemetry/instrumentation/awssdk/v1_11/AbstractSqsTracingTest.groovy b/instrumentation/aws-sdk/aws-sdk-1.11/testing/src/main/groovy/io/opentelemetry/instrumentation/awssdk/v1_11/AbstractSqsTracingTest.groovy index 41b6149796ac..731a16ad36a0 100644 --- a/instrumentation/aws-sdk/aws-sdk-1.11/testing/src/main/groovy/io/opentelemetry/instrumentation/awssdk/v1_11/AbstractSqsTracingTest.groovy +++ b/instrumentation/aws-sdk/aws-sdk-1.11/testing/src/main/groovy/io/opentelemetry/instrumentation/awssdk/v1_11/AbstractSqsTracingTest.groovy @@ -14,6 +14,7 @@ import com.amazonaws.services.sqs.model.ReceiveMessageRequest import com.amazonaws.services.sqs.model.SendMessageRequest import io.opentelemetry.instrumentation.test.InstrumentationSpecification import io.opentelemetry.instrumentation.test.utils.PortUtils +import io.opentelemetry.semconv.trace.attributes.SemanticAttributes import org.elasticmq.rest.sqs.SQSRestServerBuilder import spock.lang.Shared @@ -81,6 +82,7 @@ abstract class AbstractSqsTracingTest extends InstrumentationSpecification { "net.peer.name" "localhost" "net.peer.port" sqsPort "net.transport" IP_TCP + "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" Long } } } @@ -103,6 +105,7 @@ abstract class AbstractSqsTracingTest extends InstrumentationSpecification { "net.peer.name" "localhost" "net.peer.port" sqsPort "net.transport" IP_TCP + "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" Long } } span(1) { @@ -124,6 +127,7 @@ abstract class AbstractSqsTracingTest extends InstrumentationSpecification { "net.peer.name" "localhost" "net.peer.port" sqsPort "net.transport" IP_TCP + "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" Long } } } @@ -150,6 +154,7 @@ abstract class AbstractSqsTracingTest extends InstrumentationSpecification { "net.peer.name" "localhost" "net.peer.port" sqsPort "net.transport" IP_TCP + "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" Long } } } 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 fa013305f4fb..a17e8378186a 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 @@ -10,6 +10,8 @@ muzzle { // Used by all SDK services, the only case it isn't is an SDK extension such as a custom HTTP // client, which is not target of instrumentation anyways. extraDependency("software.amazon.awssdk:protocol-core") + // several software.amazon.awssdk artifacts are missing for this version + skip("2.17.200") } } @@ -23,7 +25,7 @@ dependencies { testImplementation(project(":instrumentation:apache-httpclient:apache-httpclient-4.0:javaagent")) testImplementation(project(":instrumentation:netty:netty-4.1:javaagent")) - latestDepTestLibrary("software.amazon.awssdk:aws-json-protocol:+") + latestDepTestLibrary("software.amazon.awssdk:aws-json-protocol:2.17.+") latestDepTestLibrary("software.amazon.awssdk:aws-core:+") latestDepTestLibrary("software.amazon.awssdk:dynamodb:+") latestDepTestLibrary("software.amazon.awssdk:ec2:+") @@ -34,6 +36,7 @@ dependencies { } tasks.withType().configureEach { + systemProperty("testLatestDeps", findProperty("testLatestDeps") as Boolean) // TODO run tests both with and without experimental span attributes jvmArgs("-Dotel.instrumentation.aws-sdk.experimental-span-attributes=true") } 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 58373615d77e..fc1764c887fb 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 @@ -24,6 +24,7 @@ dependencies { tasks { test { + systemProperty("testLatestDeps", findProperty("testLatestDeps") as Boolean) systemProperty("otel.instrumentation.aws-sdk.experimental-span-attributes", true) } } 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 385ab827afe5..681a92a8396e 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 @@ -22,6 +22,7 @@ dependencies { tasks { test { + systemProperty("testLatestDeps", findProperty("testLatestDeps") as Boolean) systemProperty("otel.instrumentation.aws-sdk.experimental-span-attributes", true) } } diff --git a/instrumentation/aws-sdk/aws-sdk-2.2/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AwsSdkHttpAttributesGetter.java b/instrumentation/aws-sdk/aws-sdk-2.2/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AwsSdkHttpAttributesGetter.java index acb7e95d2434..dd9b4362f360 100644 --- a/instrumentation/aws-sdk/aws-sdk-2.2/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AwsSdkHttpAttributesGetter.java +++ b/instrumentation/aws-sdk/aws-sdk-2.2/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AwsSdkHttpAttributesGetter.java @@ -47,38 +47,11 @@ public List requestHeader(ExecutionAttributes request, String name) { } @Override - @Nullable - public Long requestContentLength( - ExecutionAttributes request, @Nullable SdkHttpResponse response) { - return null; - } - - @Override - @Nullable - public Long requestContentLengthUncompressed( - ExecutionAttributes request, @Nullable SdkHttpResponse response) { - return null; - } - - @Override - public Integer statusCode(ExecutionAttributes request, SdkHttpResponse response) { + public Integer statusCode( + ExecutionAttributes request, SdkHttpResponse response, @Nullable Throwable error) { return response.statusCode(); } - @Override - @Nullable - public Long responseContentLength( - ExecutionAttributes request, @Nullable SdkHttpResponse response) { - return null; - } - - @Override - @Nullable - public Long responseContentLengthUncompressed( - ExecutionAttributes request, @Nullable SdkHttpResponse response) { - return null; - } - @Override public List responseHeader( ExecutionAttributes request, SdkHttpResponse response, String name) { diff --git a/instrumentation/aws-sdk/aws-sdk-2.2/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AwsSdkInstrumenterFactory.java b/instrumentation/aws-sdk/aws-sdk-2.2/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AwsSdkInstrumenterFactory.java index 8b06455c3b08..1498a692a717 100644 --- a/instrumentation/aws-sdk/aws-sdk-2.2/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AwsSdkInstrumenterFactory.java +++ b/instrumentation/aws-sdk/aws-sdk-2.2/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AwsSdkInstrumenterFactory.java @@ -73,7 +73,7 @@ private static Instrumenter createInstrume captureExperimentalSpanAttributes ? extendedAttributesExtractors : defaultAttributesExtractors) - .newInstrumenter(spanKindExtractor); + .buildInstrumenter(spanKindExtractor); } private static String spanName(ExecutionAttributes attributes) { diff --git a/instrumentation/aws-sdk/aws-sdk-2.2/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AwsSdkNetAttributesGetter.java b/instrumentation/aws-sdk/aws-sdk-2.2/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AwsSdkNetAttributesGetter.java index 32e89a3f791b..b48e19d40f7b 100644 --- a/instrumentation/aws-sdk/aws-sdk-2.2/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AwsSdkNetAttributesGetter.java +++ b/instrumentation/aws-sdk/aws-sdk-2.2/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AwsSdkNetAttributesGetter.java @@ -22,22 +22,16 @@ public String transport(ExecutionAttributes request, @Nullable SdkHttpResponse r @Override @Nullable - public String peerName(ExecutionAttributes request, @Nullable SdkHttpResponse response) { + public String peerName(ExecutionAttributes request) { SdkHttpRequest httpRequest = request.getAttribute(TracingExecutionInterceptor.SDK_HTTP_REQUEST_ATTRIBUTE); return httpRequest.host(); } @Override - public Integer peerPort(ExecutionAttributes request, @Nullable SdkHttpResponse response) { + public Integer peerPort(ExecutionAttributes request) { SdkHttpRequest httpRequest = request.getAttribute(TracingExecutionInterceptor.SDK_HTTP_REQUEST_ATTRIBUTE); return httpRequest.port(); } - - @Override - @Nullable - public String peerIp(ExecutionAttributes request, @Nullable SdkHttpResponse response) { - return null; - } } diff --git a/instrumentation/aws-sdk/aws-sdk-2.2/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AwsSdkTelemetryBuilder.java b/instrumentation/aws-sdk/aws-sdk-2.2/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AwsSdkTelemetryBuilder.java index 05b46f06a8f7..56dfb39b4e9c 100644 --- a/instrumentation/aws-sdk/aws-sdk-2.2/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AwsSdkTelemetryBuilder.java +++ b/instrumentation/aws-sdk/aws-sdk-2.2/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AwsSdkTelemetryBuilder.java @@ -5,6 +5,7 @@ package io.opentelemetry.instrumentation.awssdk.v2_2; +import com.google.errorprone.annotations.CanIgnoreReturnValue; import io.opentelemetry.api.OpenTelemetry; /** A builder of {@link AwsSdkTelemetry}. */ @@ -23,6 +24,7 @@ public final class AwsSdkTelemetryBuilder { * removed in the future, so only enable this if you know you do not require attributes filled by * this instrumentation to be stable across versions */ + @CanIgnoreReturnValue public AwsSdkTelemetryBuilder setCaptureExperimentalSpanAttributes( boolean captureExperimentalSpanAttributes) { this.captureExperimentalSpanAttributes = captureExperimentalSpanAttributes; diff --git a/instrumentation/aws-sdk/aws-sdk-2.2/library/src/test/java/io/opentelemetry/instrumentation/awssdk/v2_2/FieldMapperTest.java b/instrumentation/aws-sdk/aws-sdk-2.2/library/src/test/java/io/opentelemetry/instrumentation/awssdk/v2_2/FieldMapperTest.java index 983c1edfdef6..756d09ef61d6 100644 --- a/instrumentation/aws-sdk/aws-sdk-2.2/library/src/test/java/io/opentelemetry/instrumentation/awssdk/v2_2/FieldMapperTest.java +++ b/instrumentation/aws-sdk/aws-sdk-2.2/library/src/test/java/io/opentelemetry/instrumentation/awssdk/v2_2/FieldMapperTest.java @@ -7,10 +7,10 @@ import static io.opentelemetry.instrumentation.awssdk.v2_2.AwsSdkRequest.BatchWriteItem; import static io.opentelemetry.instrumentation.awssdk.v2_2.AwsSdkRequest.UpdateTable; -import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; import io.opentelemetry.api.trace.Span; import java.util.Collection; @@ -43,8 +43,8 @@ public void shouldMapNestedField() { .writeCapacityUnits(77L) .build()) .build(); - given(serializer.serialize(55L)).willReturn("55"); - given(serializer.serialize(77L)).willReturn("77"); + when(serializer.serialize(55L)).thenReturn("55"); + when(serializer.serialize(77L)).thenReturn("77"); Span span = mock(Span.class); // when @@ -65,7 +65,7 @@ public void shouldMapRequestFieldsOnly() { FieldMapper underTest = new FieldMapper(serializer, methodHandleFactory); Map> items = new HashMap<>(); BatchWriteItemRequest sdkRequest = BatchWriteItemRequest.builder().requestItems(items).build(); - given(serializer.serialize(items)).willReturn("firstTable,secondTable"); + when(serializer.serialize(items)).thenReturn("firstTable,secondTable"); Span span = mock(Span.class); // when @@ -89,8 +89,8 @@ public void shouldMapResponseFieldsOnly() { .consumedCapacity(ConsumedCapacity.builder().build()) .itemCollectionMetrics(items) .build(); - given(serializer.serialize(sdkResponse.consumedCapacity())).willReturn("consumedCapacity"); - given(serializer.serialize(items)).willReturn("itemCollectionMetrics"); + when(serializer.serialize(sdkResponse.consumedCapacity())).thenReturn("consumedCapacity"); + when(serializer.serialize(items)).thenReturn("itemCollectionMetrics"); Span span = mock(Span.class); // when diff --git a/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/groovy/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2ClientTest.groovy b/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/groovy/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2ClientTest.groovy index 2ebcdf28023f..8504091db141 100644 --- a/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/groovy/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2ClientTest.groovy +++ b/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/groovy/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2ClientTest.groovy @@ -169,6 +169,8 @@ abstract class AbstractAws2ClientTest extends InstrumentationSpecification { "$SemanticAttributes.HTTP_STATUS_CODE" 200 "$SemanticAttributes.HTTP_USER_AGENT" { it.startsWith("aws-sdk-java/") } "$SemanticAttributes.HTTP_FLAVOR" "1.1" + "$SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH" { it == null || it instanceof Long } + "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" { it == null || it instanceof Long } "$SemanticAttributes.RPC_SYSTEM" "aws-api" "$SemanticAttributes.RPC_SERVICE" "DynamoDb" "$SemanticAttributes.RPC_METHOD" "CreateTable" @@ -204,6 +206,8 @@ abstract class AbstractAws2ClientTest extends InstrumentationSpecification { "$SemanticAttributes.HTTP_METHOD" "$method" "$SemanticAttributes.HTTP_STATUS_CODE" 200 "$SemanticAttributes.HTTP_USER_AGENT" { it.startsWith("aws-sdk-java/") } + "$SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH" { it == null || it instanceof Long } + "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" { it == null || it instanceof Long } "$SemanticAttributes.HTTP_FLAVOR" "1.1" "$SemanticAttributes.RPC_SYSTEM" "aws-api" "$SemanticAttributes.RPC_SERVICE" "DynamoDb" @@ -240,6 +244,8 @@ abstract class AbstractAws2ClientTest extends InstrumentationSpecification { "$SemanticAttributes.HTTP_STATUS_CODE" 200 "$SemanticAttributes.HTTP_USER_AGENT" { it.startsWith("aws-sdk-java/") } "$SemanticAttributes.HTTP_FLAVOR" "1.1" + "$SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH" { it == null || it instanceof Long } + "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" { it == null || it instanceof Long } "$SemanticAttributes.RPC_SYSTEM" "aws-api" "$SemanticAttributes.RPC_SERVICE" "$service" "$SemanticAttributes.RPC_METHOD" "${operation}" @@ -353,6 +359,8 @@ abstract class AbstractAws2ClientTest extends InstrumentationSpecification { "$SemanticAttributes.HTTP_FLAVOR" "1.1" "$SemanticAttributes.HTTP_STATUS_CODE" 200 "$SemanticAttributes.HTTP_USER_AGENT" { it.startsWith("aws-sdk-java/") } + "$SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH" { it == null || it instanceof Long } + "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" { it == null || it instanceof Long } "$SemanticAttributes.RPC_SYSTEM" "aws-api" "$SemanticAttributes.RPC_SERVICE" "$service" "$SemanticAttributes.RPC_METHOD" "${operation}" @@ -376,11 +384,11 @@ abstract class AbstractAws2ClientTest extends InstrumentationSpecification { request.request().headers().get("traceparent") == null where: - service | operation | method | path | requestId | builder | call | body - "S3" | "CreateBucket" | "PUT" | "/somebucket" | "UNKNOWN" | S3Client.builder() | { c -> c.createBucket(CreateBucketRequest.builder().bucket("somebucket").build()) } | "" - "S3" | "GetObject" | "GET" | "/somebucket/somekey" | "UNKNOWN" | S3Client.builder() | { c -> c.getObject(GetObjectRequest.builder().bucket("somebucket").key("somekey").build()) } | "" - "Kinesis" | "DeleteStream" | "POST" | "" | "UNKNOWN" | KinesisClient.builder() | { c -> c.deleteStream(DeleteStreamRequest.builder().streamName("somestream").build()) } | "" - "Sqs" | "CreateQueue" | "POST" | "" | "7a62c49f-347e-4fc4-9331-6e8e7a96aa73" | SqsClient.builder() | { c -> c.createQueue(CreateQueueRequest.builder().queueName("somequeue").build()) } | """ + service | operation | method | path | requestId | builder | call | body + "S3" | "CreateBucket" | "PUT" | path("somebucket") | "UNKNOWN" | S3Client.builder() | { c -> c.createBucket(CreateBucketRequest.builder().bucket("somebucket").build()) } | "" + "S3" | "GetObject" | "GET" | path("somebucket", "somekey") | "UNKNOWN" | S3Client.builder() | { c -> c.getObject(GetObjectRequest.builder().bucket("somebucket").key("somekey").build()) } | "" + "Kinesis" | "DeleteStream" | "POST" | "" | "UNKNOWN" | KinesisClient.builder() | { c -> c.deleteStream(DeleteStreamRequest.builder().streamName("somestream").build()) } | "" + "Sqs" | "CreateQueue" | "POST" | "" | "7a62c49f-347e-4fc4-9331-6e8e7a96aa73" | SqsClient.builder() | { c -> c.createQueue(CreateQueueRequest.builder().queueName("somequeue").build()) } | """ https://queue.amazonaws.com/123456789012/MyQueue 7a62c49f-347e-4fc4-9331-6e8e7a96aa73 @@ -443,6 +451,8 @@ abstract class AbstractAws2ClientTest extends InstrumentationSpecification { "$SemanticAttributes.HTTP_FLAVOR" "1.1" "$SemanticAttributes.HTTP_STATUS_CODE" 200 "$SemanticAttributes.HTTP_USER_AGENT" { it.startsWith("aws-sdk-java/") } + "$SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH" { it == null || it instanceof Long } + "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" { it == null || it instanceof Long } "$SemanticAttributes.RPC_SYSTEM" "aws-api" "$SemanticAttributes.RPC_SERVICE" "$service" "$SemanticAttributes.RPC_METHOD" "${operation}" @@ -466,18 +476,18 @@ abstract class AbstractAws2ClientTest extends InstrumentationSpecification { request.request().headers().get("traceparent") == null where: - service | operation | method | path | requestId | builder | call | body - "S3" | "CreateBucket" | "PUT" | "/somebucket" | "UNKNOWN" | S3AsyncClient.builder() | { c -> c.createBucket(CreateBucketRequest.builder().bucket("somebucket").build()) } | "" - "S3" | "GetObject" | "GET" | "/somebucket/somekey" | "UNKNOWN" | S3AsyncClient.builder() | { c -> c.getObject(GetObjectRequest.builder().bucket("somebucket").key("somekey").build(), AsyncResponseTransformer.toBytes()) } | "1234567890" + service | operation | method | path | requestId | builder | call | body + "S3" | "CreateBucket" | "PUT" | path("somebucket") | "UNKNOWN" | S3AsyncClient.builder() | { c -> c.createBucket(CreateBucketRequest.builder().bucket("somebucket").build()) } | "" + "S3" | "GetObject" | "GET" | path("somebucket", "somekey") | "UNKNOWN" | S3AsyncClient.builder() | { c -> c.getObject(GetObjectRequest.builder().bucket("somebucket").key("somekey").build(), AsyncResponseTransformer.toBytes()) } | "1234567890" // Kinesis seems to expect an http2 response which is incompatible with our test server. // "Kinesis" | "DeleteStream" | "POST" | "/" | "UNKNOWN" | KinesisAsyncClient.builder() | { c -> c.deleteStream(DeleteStreamRequest.builder().streamName("somestream").build()) } | "" - "Sqs" | "CreateQueue" | "POST" | "" | "7a62c49f-347e-4fc4-9331-6e8e7a96aa73" | SqsAsyncClient.builder() | { c -> c.createQueue(CreateQueueRequest.builder().queueName("somequeue").build()) } | """ + "Sqs" | "CreateQueue" | "POST" | "" | "7a62c49f-347e-4fc4-9331-6e8e7a96aa73" | SqsAsyncClient.builder() | { c -> c.createQueue(CreateQueueRequest.builder().queueName("somequeue").build()) } | """ https://queue.amazonaws.com/123456789012/MyQueue 7a62c49f-347e-4fc4-9331-6e8e7a96aa73 """ - "Sqs" | "SendMessage" | "POST" | "" | "27daac76-34dd-47df-bd01-1f6e873584a0" | SqsAsyncClient.builder() | { c -> c.sendMessage(SendMessageRequest.builder().queueUrl("someurl").messageBody("").build()) } | """ + "Sqs" | "SendMessage" | "POST" | "" | "27daac76-34dd-47df-bd01-1f6e873584a0" | SqsAsyncClient.builder() | { c -> c.sendMessage(SendMessageRequest.builder().queueUrl("someurl").messageBody("").build()) } | """ d41d8cd98f00b204e9800998ecf8427e @@ -487,14 +497,14 @@ abstract class AbstractAws2ClientTest extends InstrumentationSpecification { 27daac76-34dd-47df-bd01-1f6e873584a0 """ - "Ec2" | "AllocateAddress" | "POST" | "" | "59dbff89-35bd-4eac-99ed-be587EXAMPLE" | Ec2AsyncClient.builder() | { c -> c.allocateAddress() } | """ + "Ec2" | "AllocateAddress" | "POST" | "" | "59dbff89-35bd-4eac-99ed-be587EXAMPLE" | Ec2AsyncClient.builder() | { c -> c.allocateAddress() } | """ 59dbff89-35bd-4eac-99ed-be587EXAMPLE 192.0.2.1 standard """ - "Rds" | "DeleteOptionGroup" | "POST" | "" | "0ac9cda2-bbf4-11d3-f92b-31fa5e8dbc99" | RdsAsyncClient.builder() | { c -> c.deleteOptionGroup(DeleteOptionGroupRequest.builder().build()) } | """ + "Rds" | "DeleteOptionGroup" | "POST" | "" | "0ac9cda2-bbf4-11d3-f92b-31fa5e8dbc99" | RdsAsyncClient.builder() | { c -> c.deleteOptionGroup(DeleteOptionGroupRequest.builder().build()) } | """ 0ac9cda2-bbf4-11d3-f92b-31fa5e8dbc99 @@ -525,6 +535,7 @@ abstract class AbstractAws2ClientTest extends InstrumentationSpecification { then: thrown SdkClientException + def path = path("somebucket", "somekey") assertTraces(1) { trace(0, 1) { span(0) { @@ -537,7 +548,7 @@ abstract class AbstractAws2ClientTest extends InstrumentationSpecification { "$SemanticAttributes.NET_TRANSPORT" IP_TCP "$SemanticAttributes.NET_PEER_NAME" "127.0.0.1" "$SemanticAttributes.NET_PEER_PORT" server.httpPort() - "$SemanticAttributes.HTTP_URL" "${server.httpUri()}/somebucket/somekey" + "$SemanticAttributes.HTTP_URL" "${server.httpUri()}${path}" "$SemanticAttributes.HTTP_METHOD" "GET" "$SemanticAttributes.HTTP_FLAVOR" "1.1" "$SemanticAttributes.RPC_SYSTEM" "aws-api" @@ -550,4 +561,16 @@ abstract class AbstractAws2ClientTest extends InstrumentationSpecification { } } } + + static String path(String bucket, String path = null) { + def result = "" + // since 2.18.0 bucket name is not present in request path + if (!Boolean.getBoolean("testLatestDeps") && !bucket.isEmpty()) { + result = "/" + bucket + } + if (path != null && !path.isEmpty()) { + result += "/" + path + } + return result + } } diff --git a/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/groovy/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2SqsTracingTest.groovy b/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/groovy/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2SqsTracingTest.groovy index 97225b26d018..d5ce940dfe63 100644 --- a/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/groovy/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2SqsTracingTest.groovy +++ b/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/groovy/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2SqsTracingTest.groovy @@ -7,6 +7,7 @@ package io.opentelemetry.instrumentation.awssdk.v2_2 import io.opentelemetry.instrumentation.test.InstrumentationSpecification import io.opentelemetry.instrumentation.test.utils.PortUtils +import io.opentelemetry.semconv.trace.attributes.SemanticAttributes import org.elasticmq.rest.sqs.SQSRestServerBuilder import software.amazon.awssdk.auth.credentials.AwsBasicCredentials import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider @@ -104,6 +105,8 @@ abstract class AbstractAws2SqsTracingTest extends InstrumentationSpecification { "net.peer.name" "localhost" "net.peer.port" sqsPort "net.transport" IP_TCP + "$SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH" { it == null || it instanceof Long } + "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" { it == null || it instanceof Long } } } } @@ -127,6 +130,8 @@ abstract class AbstractAws2SqsTracingTest extends InstrumentationSpecification { "net.peer.name" "localhost" "net.peer.port" sqsPort "net.transport" IP_TCP + "$SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH" { it == null || it instanceof Long } + "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" { it == null || it instanceof Long } } } span(1) { @@ -145,6 +150,8 @@ abstract class AbstractAws2SqsTracingTest extends InstrumentationSpecification { "net.peer.name" "localhost" "net.peer.port" sqsPort "net.transport" IP_TCP + "$SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH" { it == null || it instanceof Long } + "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" { it == null || it instanceof Long } } } } @@ -172,6 +179,8 @@ abstract class AbstractAws2SqsTracingTest extends InstrumentationSpecification { "net.peer.name" "localhost" "net.peer.port" sqsPort "net.transport" IP_TCP + "$SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH" { it == null || it instanceof Long } + "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" { it == null || it instanceof Long } } } } diff --git a/instrumentation/azure-core/azure-core-1.14/javaagent/build.gradle.kts b/instrumentation/azure-core/azure-core-1.14/javaagent/build.gradle.kts index e79af1e34200..ceb066de1a62 100644 --- a/instrumentation/azure-core/azure-core-1.14/javaagent/build.gradle.kts +++ b/instrumentation/azure-core/azure-core-1.14/javaagent/build.gradle.kts @@ -26,5 +26,5 @@ dependencies { // Ensure no cross interference testInstrumentation(project(":instrumentation:azure-core:azure-core-1.19:javaagent")) - latestDepTestLibrary("com.azure:azure-core:1.18.+") // see azure-core-1.19 + latestDepTestLibrary("com.azure:azure-core:1.18.+") // see azure-core-1.19 module } diff --git a/instrumentation/azure-core/azure-core-1.14/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/azurecore/v1_14/AzureHttpClientInstrumentation.java b/instrumentation/azure-core/azure-core-1.14/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/azurecore/v1_14/AzureHttpClientInstrumentation.java index 87dfc502372e..b9e31440d93d 100644 --- a/instrumentation/azure-core/azure-core-1.14/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/azurecore/v1_14/AzureHttpClientInstrumentation.java +++ b/instrumentation/azure-core/azure-core-1.14/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/azurecore/v1_14/AzureHttpClientInstrumentation.java @@ -36,6 +36,7 @@ public void transform(TypeTransformer transformer) { this.getClass().getName() + "$SuppressNestedClientAdvice"); } + @SuppressWarnings("unused") public static class SuppressNestedClientAdvice { @Advice.OnMethodExit(suppress = Throwable.class) diff --git a/instrumentation/azure-core/azure-core-1.19/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/azurecore/v1_19/AzureHttpClientInstrumentation.java b/instrumentation/azure-core/azure-core-1.19/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/azurecore/v1_19/AzureHttpClientInstrumentation.java index 849fbe283f58..198c7588d815 100644 --- a/instrumentation/azure-core/azure-core-1.19/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/azurecore/v1_19/AzureHttpClientInstrumentation.java +++ b/instrumentation/azure-core/azure-core-1.19/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/azurecore/v1_19/AzureHttpClientInstrumentation.java @@ -36,6 +36,7 @@ public void transform(TypeTransformer transformer) { this.getClass().getName() + "$SuppressNestedClientAdvice"); } + @SuppressWarnings("unused") public static class SuppressNestedClientAdvice { @Advice.OnMethodExit(suppress = Throwable.class) diff --git a/instrumentation/c3p0-0.9/library/README.md b/instrumentation/c3p0-0.9/library/README.md index 5fcf8bfe91d5..ebfb4960a5e1 100644 --- a/instrumentation/c3p0-0.9/library/README.md +++ b/instrumentation/c3p0-0.9/library/README.md @@ -1,4 +1,4 @@ -# Manual Instrumentation for C3P0 +# Library Instrumentation for C3P0 version 0.9 and higher Provides OpenTelemetry instrumentation for [C3P0](https://www.mchange.com/projects/c3p0/). @@ -6,13 +6,12 @@ Provides OpenTelemetry instrumentation for [C3P0](https://www.mchange.com/projec ### Add these dependencies to your project: -Replace `OPENTELEMETRY_VERSION` with the latest stable -[release](https://mvnrepository.com/artifact/io.opentelemetry). `Minimum version: 1.15.0` +Replace `OPENTELEMETRY_VERSION` with the [latest +release](https://search.maven.org/search?q=g:io.opentelemetry.instrumentation%20AND%20a:opentelemetry-c3p0-0.9). For Maven, add to your `pom.xml` dependencies: ```xml - io.opentelemetry.instrumentation diff --git a/instrumentation/cassandra/cassandra-3.0/javaagent/build.gradle.kts b/instrumentation/cassandra/cassandra-3.0/javaagent/build.gradle.kts index bbb99dc2547c..6c3eb7cf5f83 100644 --- a/instrumentation/cassandra/cassandra-3.0/javaagent/build.gradle.kts +++ b/instrumentation/cassandra/cassandra-3.0/javaagent/build.gradle.kts @@ -43,5 +43,6 @@ configurations.testRuntimeClasspath.resolutionStrategy.force("com.google.guava:g tasks { test { usesService(gradle.sharedServices.registrations["testcontainersBuildService"].getService()) + jvmArgs("-Dotel.instrumentation.common.db-statement-sanitizer.enabled=true") } } diff --git a/instrumentation/cassandra/cassandra-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/cassandra/v3_0/CassandraNetAttributesGetter.java b/instrumentation/cassandra/cassandra-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/cassandra/v3_0/CassandraNetAttributesGetter.java index c8b625e588c9..dfc357bd8181 100644 --- a/instrumentation/cassandra/cassandra-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/cassandra/v3_0/CassandraNetAttributesGetter.java +++ b/instrumentation/cassandra/cassandra-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/cassandra/v3_0/CassandraNetAttributesGetter.java @@ -19,9 +19,21 @@ public String transport(CassandraRequest request, @Nullable ExecutionInfo execut return null; } + @Nullable + @Override + public String peerName(CassandraRequest request) { + return null; + } + + @Nullable + @Override + public Integer peerPort(CassandraRequest request) { + return null; + } + @Override @Nullable - public InetSocketAddress getAddress( + protected InetSocketAddress getPeerSocketAddress( CassandraRequest request, @Nullable ExecutionInfo executionInfo) { return executionInfo == null ? null : executionInfo.getQueriedHost().getSocketAddress(); } diff --git a/instrumentation/cassandra/cassandra-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/cassandra/v3_0/CassandraSingletons.java b/instrumentation/cassandra/cassandra-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/cassandra/v3_0/CassandraSingletons.java index 01f4599bb149..9b61b45e186a 100644 --- a/instrumentation/cassandra/cassandra-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/cassandra/v3_0/CassandraSingletons.java +++ b/instrumentation/cassandra/cassandra-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/cassandra/v3_0/CassandraSingletons.java @@ -12,6 +12,7 @@ import io.opentelemetry.instrumentation.api.instrumenter.db.DbClientSpanNameExtractor; import io.opentelemetry.instrumentation.api.instrumenter.db.SqlClientAttributesExtractor; import io.opentelemetry.instrumentation.api.instrumenter.net.NetClientAttributesExtractor; +import io.opentelemetry.javaagent.bootstrap.internal.CommonConfig; import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; public final class CassandraSingletons { @@ -32,10 +33,12 @@ public final class CassandraSingletons { .addAttributesExtractor( SqlClientAttributesExtractor.builder(attributesGetter) .setTableAttribute(SemanticAttributes.DB_CASSANDRA_TABLE) + .setStatementSanitizationEnabled( + CommonConfig.get().isStatementSanitizationEnabled()) .build()) .addAttributesExtractor( NetClientAttributesExtractor.create(new CassandraNetAttributesGetter())) - .newInstrumenter(SpanKindExtractor.alwaysClient()); + .buildInstrumenter(SpanKindExtractor.alwaysClient()); } public static Instrumenter instrumenter() { diff --git a/instrumentation/cassandra/cassandra-3.0/javaagent/src/test/groovy/CassandraClientTest.groovy b/instrumentation/cassandra/cassandra-3.0/javaagent/src/test/groovy/CassandraClientTest.groovy index 2d7111cd2a2a..cfa6ee5e07f4 100644 --- a/instrumentation/cassandra/cassandra-3.0/javaagent/src/test/groovy/CassandraClientTest.groovy +++ b/instrumentation/cassandra/cassandra-3.0/javaagent/src/test/groovy/CassandraClientTest.groovy @@ -37,7 +37,7 @@ class CassandraClientTest extends AgentInstrumentationSpecification { def setupSpec() { cassandra = new GenericContainer("cassandra:3") - // limit memory usage + // limit memory usage .withEnv("JVM_OPTS", "-Xmx128m -Xms128m") .withExposedPorts(9042) .withLogConsumer(new Slf4jLogConsumer(logger)) @@ -147,9 +147,9 @@ class CassandraClientTest extends AgentInstrumentationSpecification { childOf((SpanData) parentSpan) } attributes { - "$SemanticAttributes.NET_PEER_NAME" "localhost" - "$SemanticAttributes.NET_PEER_IP" "127.0.0.1" - "$SemanticAttributes.NET_PEER_PORT" cassandraPort + "$SemanticAttributes.NET_SOCK_PEER_ADDR" "127.0.0.1" + "$SemanticAttributes.NET_SOCK_PEER_NAME" "localhost" + "$SemanticAttributes.NET_SOCK_PEER_PORT" cassandraPort "$SemanticAttributes.DB_SYSTEM" "cassandra" "$SemanticAttributes.DB_NAME" keyspace "$SemanticAttributes.DB_STATEMENT" statement diff --git a/instrumentation/cassandra/cassandra-4.0/javaagent/build.gradle.kts b/instrumentation/cassandra/cassandra-4.0/javaagent/build.gradle.kts index aad53b864a45..329695c848f1 100644 --- a/instrumentation/cassandra/cassandra-4.0/javaagent/build.gradle.kts +++ b/instrumentation/cassandra/cassandra-4.0/javaagent/build.gradle.kts @@ -21,5 +21,6 @@ dependencies { tasks { test { usesService(gradle.sharedServices.registrations["testcontainersBuildService"].getService()) + jvmArgs("-Dotel.instrumentation.common.db-statement-sanitizer.enabled=true") } } diff --git a/instrumentation/cassandra/cassandra-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/cassandra/v4_0/CassandraNetAttributesGetter.java b/instrumentation/cassandra/cassandra-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/cassandra/v4_0/CassandraNetAttributesGetter.java index cb58a2a6d434..373c2da7389e 100644 --- a/instrumentation/cassandra/cassandra-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/cassandra/v4_0/CassandraNetAttributesGetter.java +++ b/instrumentation/cassandra/cassandra-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/cassandra/v4_0/CassandraNetAttributesGetter.java @@ -21,9 +21,21 @@ public String transport(CassandraRequest request, @Nullable ExecutionInfo execut return null; } + @Nullable + @Override + public String peerName(CassandraRequest request) { + return null; + } + + @Nullable + @Override + public Integer peerPort(CassandraRequest request) { + return null; + } + @Override @Nullable - public InetSocketAddress getAddress( + protected InetSocketAddress getPeerSocketAddress( CassandraRequest request, @Nullable ExecutionInfo executionInfo) { if (executionInfo == null) { return null; diff --git a/instrumentation/cassandra/cassandra-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/cassandra/v4_0/CassandraSingletons.java b/instrumentation/cassandra/cassandra-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/cassandra/v4_0/CassandraSingletons.java index eaa03855ea4d..b84df6935485 100644 --- a/instrumentation/cassandra/cassandra-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/cassandra/v4_0/CassandraSingletons.java +++ b/instrumentation/cassandra/cassandra-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/cassandra/v4_0/CassandraSingletons.java @@ -12,6 +12,7 @@ import io.opentelemetry.instrumentation.api.instrumenter.db.DbClientSpanNameExtractor; import io.opentelemetry.instrumentation.api.instrumenter.db.SqlClientAttributesExtractor; import io.opentelemetry.instrumentation.api.instrumenter.net.NetClientAttributesExtractor; +import io.opentelemetry.javaagent.bootstrap.internal.CommonConfig; import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; public final class CassandraSingletons { @@ -31,11 +32,13 @@ public final class CassandraSingletons { .addAttributesExtractor( SqlClientAttributesExtractor.builder(attributesGetter) .setTableAttribute(SemanticAttributes.DB_CASSANDRA_TABLE) + .setStatementSanitizationEnabled( + CommonConfig.get().isStatementSanitizationEnabled()) .build()) .addAttributesExtractor( NetClientAttributesExtractor.create(new CassandraNetAttributesGetter())) .addAttributesExtractor(new CassandraAttributesExtractor()) - .newInstrumenter(SpanKindExtractor.alwaysClient()); + .buildInstrumenter(SpanKindExtractor.alwaysClient()); } public static Instrumenter instrumenter() { diff --git a/instrumentation/cassandra/cassandra-4.0/javaagent/src/test/groovy/CassandraClientTest.groovy b/instrumentation/cassandra/cassandra-4.0/javaagent/src/test/groovy/CassandraClientTest.groovy index 3711bfd09149..2f81f3943a0f 100644 --- a/instrumentation/cassandra/cassandra-4.0/javaagent/src/test/groovy/CassandraClientTest.groovy +++ b/instrumentation/cassandra/cassandra-4.0/javaagent/src/test/groovy/CassandraClientTest.groovy @@ -32,7 +32,7 @@ class CassandraClientTest extends AgentInstrumentationSpecification { def setupSpec() { cassandra = new GenericContainer("cassandra:4.0") - // limit memory usage + // limit memory usage .withEnv("JVM_OPTS", "-Xmx128m -Xms128m") .withExposedPorts(9042) .withLogConsumer(new Slf4jLogConsumer(logger)) @@ -120,9 +120,9 @@ class CassandraClientTest extends AgentInstrumentationSpecification { childOf((SpanData) parentSpan) } attributes { - "$SemanticAttributes.NET_PEER_NAME" "localhost" - "$SemanticAttributes.NET_PEER_IP" "127.0.0.1" - "$SemanticAttributes.NET_PEER_PORT" cassandraPort + "$SemanticAttributes.NET_SOCK_PEER_ADDR" "127.0.0.1" + "$SemanticAttributes.NET_SOCK_PEER_NAME" "localhost" + "$SemanticAttributes.NET_SOCK_PEER_PORT" cassandraPort "$SemanticAttributes.DB_SYSTEM" "cassandra" "$SemanticAttributes.DB_NAME" keyspace "$SemanticAttributes.DB_STATEMENT" statement diff --git a/instrumentation/cdi-testing/build.gradle.kts b/instrumentation/cdi-testing/build.gradle.kts index 25d02214738a..8ec3f9bd82b3 100644 --- a/instrumentation/cdi-testing/build.gradle.kts +++ b/instrumentation/cdi-testing/build.gradle.kts @@ -6,8 +6,10 @@ dependencies { testLibrary("org.jboss.weld:weld-core:2.3.0.Final") testLibrary("org.jboss.weld.se:weld-se:2.3.0.Final") testLibrary("org.jboss.weld.se:weld-se-core:2.3.0.Final") +} - latestDepTestLibrary("org.jboss.weld:weld-core:2.+") - latestDepTestLibrary("org.jboss.weld.se:weld-se:2.+") - latestDepTestLibrary("org.jboss.weld.se:weld-se-core:2.+") +tasks.withType().configureEach { + // required on jdk17 + jvmArgs("--add-opens=java.base/java.lang=ALL-UNNAMED") + jvmArgs("-XX:+IgnoreUnrecognizedVMOptions") } diff --git a/instrumentation/couchbase/couchbase-2-common/javaagent-unit-tests/build.gradle.kts b/instrumentation/couchbase/couchbase-2-common/javaagent-unit-tests/build.gradle.kts index d91fd2027c67..104008fbc70b 100644 --- a/instrumentation/couchbase/couchbase-2-common/javaagent-unit-tests/build.gradle.kts +++ b/instrumentation/couchbase/couchbase-2-common/javaagent-unit-tests/build.gradle.kts @@ -7,6 +7,13 @@ dependencies { testImplementation("org.spockframework:spock-core") testImplementation(project(":instrumentation-api-semconv")) + testImplementation(project(":javaagent-extension-api")) testImplementation(project(":instrumentation:couchbase:couchbase-2-common:javaagent")) testImplementation("com.couchbase.client:java-client:2.5.0") } + +tasks { + test { + jvmArgs("-Dotel.instrumentation.common.db-statement-sanitizer.enabled=true") + } +} diff --git a/instrumentation/couchbase/couchbase-2-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/couchbase/v2_0/CouchbaseQuerySanitizer.java b/instrumentation/couchbase/couchbase-2-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/couchbase/v2_0/CouchbaseQuerySanitizer.java index 10c6a533d1a3..222d1b2e2f19 100644 --- a/instrumentation/couchbase/couchbase-2-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/couchbase/v2_0/CouchbaseQuerySanitizer.java +++ b/instrumentation/couchbase/couchbase-2-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/couchbase/v2_0/CouchbaseQuerySanitizer.java @@ -8,12 +8,17 @@ import io.opentelemetry.instrumentation.api.db.SqlDialect; import io.opentelemetry.instrumentation.api.db.SqlStatementInfo; import io.opentelemetry.instrumentation.api.db.SqlStatementSanitizer; +import io.opentelemetry.javaagent.bootstrap.internal.CommonConfig; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; import javax.annotation.Nullable; public final class CouchbaseQuerySanitizer { + + private static final SqlStatementSanitizer sanitizer = + SqlStatementSanitizer.create(CommonConfig.get().isStatementSanitizationEnabled()); + @Nullable private static final Class QUERY_CLASS; @Nullable private static final Class STATEMENT_CLASS; @Nullable private static final Class N1QL_QUERY_CLASS; @@ -116,7 +121,7 @@ private static String getStatementString(MethodHandle handle, Object query) { } private static SqlStatementInfo sanitizeString(String query) { - return SqlStatementSanitizer.sanitize(query, SqlDialect.COUCHBASE); + return sanitizer.sanitize(query, SqlDialect.COUCHBASE); } private CouchbaseQuerySanitizer() {} diff --git a/instrumentation/couchbase/couchbase-2-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/couchbase/v2_0/CouchbaseRequestInfo.java b/instrumentation/couchbase/couchbase-2-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/couchbase/v2_0/CouchbaseRequestInfo.java index f7e01a33ff5c..992a8512f855 100644 --- a/instrumentation/couchbase/couchbase-2-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/couchbase/v2_0/CouchbaseRequestInfo.java +++ b/instrumentation/couchbase/couchbase-2-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/couchbase/v2_0/CouchbaseRequestInfo.java @@ -11,6 +11,7 @@ import io.opentelemetry.context.Context; import io.opentelemetry.context.ContextKey; import io.opentelemetry.instrumentation.api.db.SqlStatementInfo; +import java.net.SocketAddress; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import javax.annotation.Nullable; @@ -29,10 +30,9 @@ protected Map computeValue(Class type) { } }; - private String peerName; - private Integer peerPort; private String localAddress; private String operationId; + private SocketAddress peerAddress; public static CouchbaseRequestInfo create( @Nullable String bucket, Class declaringClass, String methodName) { @@ -76,24 +76,6 @@ public static CouchbaseRequestInfo get(Context context) { public abstract boolean isMethodCall(); - @Nullable - public String getPeerName() { - return peerName; - } - - public void setPeerName(String peerName) { - this.peerName = peerName; - } - - @Nullable - public Integer getPeerPort() { - return peerPort; - } - - public void setPeerPort(Integer peerPort) { - this.peerPort = peerPort; - } - @Nullable public String getLocalAddress() { return localAddress; @@ -111,4 +93,13 @@ public String getOperationId() { public void setOperationId(String operationId) { this.operationId = operationId; } + + @Nullable + public SocketAddress getPeerAddress() { + return peerAddress; + } + + public void setPeerAddress(SocketAddress peerAddress) { + this.peerAddress = peerAddress; + } } diff --git a/instrumentation/couchbase/couchbase-2.0/javaagent/build.gradle.kts b/instrumentation/couchbase/couchbase-2.0/javaagent/build.gradle.kts index 57a09cef9787..fab2b7b83eb5 100644 --- a/instrumentation/couchbase/couchbase-2.0/javaagent/build.gradle.kts +++ b/instrumentation/couchbase/couchbase-2.0/javaagent/build.gradle.kts @@ -3,22 +3,13 @@ plugins { } muzzle { - // Version 2.7.5 and 2.7.8 were not released properly and muzzle cannot test against it causing failure. - // So we have to skip them resulting in this verbose setup. pass { group.set("com.couchbase.client") module.set("java-client") - versions.set("[2.0.0,2.7.5)") - } - pass { - group.set("com.couchbase.client") - module.set("java-client") - versions.set("[2.7.6,2.7.8)") - } - pass { - group.set("com.couchbase.client") - module.set("java-client") - versions.set("[2.7.9,3.0.0)") + versions.set("[2,3)") + // these versions were released as ".bundle" instead of ".jar" + skip("2.7.5", "2.7.8") + assertInverse.set(true) } fail { group.set("com.couchbase.client") @@ -41,6 +32,8 @@ dependencies { tasks.withType().configureEach { // required on jdk17 + jvmArgs("--add-opens=java.base/java.lang=ALL-UNNAMED") jvmArgs("--add-opens=java.base/java.lang.invoke=ALL-UNNAMED") jvmArgs("-XX:+IgnoreUnrecognizedVMOptions") + jvmArgs("-Dotel.instrumentation.common.db-statement-sanitizer.enabled=true") } diff --git a/instrumentation/couchbase/couchbase-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/couchbase/v2_0/CouchbaseNetAttributesGetter.java b/instrumentation/couchbase/couchbase-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/couchbase/v2_0/CouchbaseNetAttributesGetter.java index 2987673e1dfb..f50729e5932a 100644 --- a/instrumentation/couchbase/couchbase-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/couchbase/v2_0/CouchbaseNetAttributesGetter.java +++ b/instrumentation/couchbase/couchbase-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/couchbase/v2_0/CouchbaseNetAttributesGetter.java @@ -5,35 +5,42 @@ package io.opentelemetry.javaagent.instrumentation.couchbase.v2_0; -import io.opentelemetry.instrumentation.api.instrumenter.net.NetClientAttributesGetter; +import io.opentelemetry.instrumentation.api.instrumenter.net.InetSocketAddressNetClientAttributesGetter; import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; +import java.net.InetSocketAddress; +import java.net.SocketAddress; import javax.annotation.Nullable; public class CouchbaseNetAttributesGetter - implements NetClientAttributesGetter { + extends InetSocketAddressNetClientAttributesGetter { @Nullable @Override public String transport(CouchbaseRequestInfo couchbaseRequest, @Nullable Void unused) { - return couchbaseRequest.getPeerName() != null + return couchbaseRequest.getPeerAddress() != null ? SemanticAttributes.NetTransportValues.IP_TCP : null; } @Nullable @Override - public String peerName(CouchbaseRequestInfo couchbaseRequest, @Nullable Void unused) { - return couchbaseRequest.getPeerName(); + public String peerName(CouchbaseRequestInfo couchbaseRequest) { + return null; } @Nullable @Override - public Integer peerPort(CouchbaseRequestInfo couchbaseRequest, @Nullable Void unused) { - return couchbaseRequest.getPeerPort(); + public Integer peerPort(CouchbaseRequestInfo couchbaseRequest) { + return null; } @Nullable @Override - public String peerIp(CouchbaseRequestInfo couchbaseRequest, @Nullable Void unused) { + protected InetSocketAddress getPeerSocketAddress( + CouchbaseRequestInfo couchbaseRequest, @Nullable Void unused) { + SocketAddress peerAddress = couchbaseRequest.getPeerAddress(); + if (peerAddress instanceof InetSocketAddress) { + return (InetSocketAddress) peerAddress; + } return null; } } diff --git a/instrumentation/couchbase/couchbase-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/couchbase/v2_0/CouchbaseSingletons.java b/instrumentation/couchbase/couchbase-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/couchbase/v2_0/CouchbaseSingletons.java index d8b14a5d3e88..15b9ebddf3ad 100644 --- a/instrumentation/couchbase/couchbase-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/couchbase/v2_0/CouchbaseSingletons.java +++ b/instrumentation/couchbase/couchbase-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/couchbase/v2_0/CouchbaseSingletons.java @@ -8,12 +8,13 @@ import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; import io.opentelemetry.instrumentation.api.instrumenter.InstrumenterBuilder; -import io.opentelemetry.instrumentation.api.instrumenter.PeerServiceAttributesExtractor; import io.opentelemetry.instrumentation.api.instrumenter.SpanKindExtractor; import io.opentelemetry.instrumentation.api.instrumenter.SpanNameExtractor; import io.opentelemetry.instrumentation.api.instrumenter.db.DbClientAttributesExtractor; import io.opentelemetry.instrumentation.api.instrumenter.db.DbClientSpanNameExtractor; import io.opentelemetry.instrumentation.api.instrumenter.net.NetClientAttributesExtractor; +import io.opentelemetry.instrumentation.api.instrumenter.net.PeerServiceAttributesExtractor; +import io.opentelemetry.javaagent.bootstrap.internal.CommonConfig; import io.opentelemetry.javaagent.bootstrap.internal.InstrumentationConfig; public final class CouchbaseSingletons { @@ -33,7 +34,9 @@ public final class CouchbaseSingletons { GlobalOpenTelemetry.get(), INSTRUMENTATION_NAME, spanNameExtractor) .addAttributesExtractor(DbClientAttributesExtractor.create(couchbaseAttributesGetter)) .addAttributesExtractor(NetClientAttributesExtractor.create(netAttributesGetter)) - .addAttributesExtractor(PeerServiceAttributesExtractor.create(netAttributesGetter)) + .addAttributesExtractor( + PeerServiceAttributesExtractor.create( + netAttributesGetter, CommonConfig.get().getPeerServiceMapping())) .addContextCustomizer( (context, couchbaseRequest, startAttributes) -> CouchbaseRequestInfo.init(context, couchbaseRequest)); @@ -43,7 +46,7 @@ public final class CouchbaseSingletons { builder.addAttributesExtractor(new ExperimentalAttributesExtractor()); } - INSTRUMENTER = builder.newInstrumenter(SpanKindExtractor.alwaysClient()); + INSTRUMENTER = builder.buildInstrumenter(SpanKindExtractor.alwaysClient()); } public static Instrumenter instrumenter() { diff --git a/instrumentation/couchbase/couchbase-2.6/javaagent/build.gradle.kts b/instrumentation/couchbase/couchbase-2.6/javaagent/build.gradle.kts index 8b9f96ec6f58..19428dc07b26 100644 --- a/instrumentation/couchbase/couchbase-2.6/javaagent/build.gradle.kts +++ b/instrumentation/couchbase/couchbase-2.6/javaagent/build.gradle.kts @@ -37,4 +37,8 @@ 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") } diff --git a/instrumentation/couchbase/couchbase-2.6/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/couchbase/v2_6/CouchbaseNetworkInstrumentation.java b/instrumentation/couchbase/couchbase-2.6/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/couchbase/v2_6/CouchbaseNetworkInstrumentation.java index 8199fee48790..4794b9fcaf2a 100644 --- a/instrumentation/couchbase/couchbase-2.6/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/couchbase/v2_6/CouchbaseNetworkInstrumentation.java +++ b/instrumentation/couchbase/couchbase-2.6/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/couchbase/v2_6/CouchbaseNetworkInstrumentation.java @@ -5,15 +5,13 @@ package io.opentelemetry.javaagent.instrumentation.couchbase.v2_6; -import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.extendsClass; -import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed; import static net.bytebuddy.matcher.ElementMatchers.isMethod; -import static net.bytebuddy.matcher.ElementMatchers.nameStartsWith; import static net.bytebuddy.matcher.ElementMatchers.named; import static net.bytebuddy.matcher.ElementMatchers.takesArgument; import static net.bytebuddy.matcher.ElementMatchers.takesArguments; import com.couchbase.client.core.message.CouchbaseRequest; +import com.couchbase.client.deps.io.netty.channel.ChannelHandlerContext; import io.opentelemetry.instrumentation.api.util.VirtualField; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; @@ -25,16 +23,9 @@ public class CouchbaseNetworkInstrumentation implements TypeInstrumentation { - @Override - public ElementMatcher classLoaderOptimization() { - return hasClassesNamed("com.couchbase.client.core.endpoint.AbstractGenericHandler"); - } - @Override public ElementMatcher typeMatcher() { - // Exact class because private fields are used - return nameStartsWith("com.couchbase.client.") - .and(extendsClass(named("com.couchbase.client.core.endpoint.AbstractGenericHandler"))); + return named("com.couchbase.client.core.endpoint.AbstractGenericHandler"); } @Override @@ -56,26 +47,16 @@ public static class CouchbaseNetworkAdvice { @Advice.OnMethodEnter(suppress = Throwable.class) public static void addNetworkTagsToSpan( - @Advice.FieldValue("remoteHostname") String remoteHostname, - @Advice.FieldValue("remoteSocket") String remoteSocket, @Advice.FieldValue("localSocket") String localSocket, + @Advice.Argument(0) ChannelHandlerContext channelHandlerContext, @Advice.Argument(1) CouchbaseRequest request) { + VirtualField virtualField = VirtualField.find(CouchbaseRequest.class, CouchbaseRequestInfo.class); CouchbaseRequestInfo requestInfo = virtualField.get(request); if (requestInfo != null) { - if (remoteHostname != null) { - requestInfo.setPeerName(remoteHostname); - } - - if (remoteSocket != null) { - int splitIndex = remoteSocket.lastIndexOf(":"); - if (splitIndex != -1) { - requestInfo.setPeerPort(Integer.parseInt(remoteSocket.substring(splitIndex + 1))); - } - } - + requestInfo.setPeerAddress(channelHandlerContext.channel().remoteAddress()); requestInfo.setLocalAddress(localSocket); } } diff --git a/instrumentation/couchbase/couchbase-2.6/javaagent/src/test/groovy/CouchbaseSpanUtil.groovy b/instrumentation/couchbase/couchbase-2.6/javaagent/src/test/groovy/CouchbaseSpanUtil.groovy index 6cc47e47bc76..cf9b1a76e160 100644 --- a/instrumentation/couchbase/couchbase-2.6/javaagent/src/test/groovy/CouchbaseSpanUtil.groovy +++ b/instrumentation/couchbase/couchbase-2.6/javaagent/src/test/groovy/CouchbaseSpanUtil.groovy @@ -36,16 +36,17 @@ class CouchbaseSpanUtil { "$SemanticAttributes.NET_TRANSPORT" { it == null || it == IP_TCP } // Because of caching, not all requests hit the server so these attributes may be absent - "$SemanticAttributes.NET_PEER_NAME" { it == "localhost" || it == "127.0.0.1" || it == null } - "$SemanticAttributes.NET_PEER_PORT" { it == null || Number } + "$SemanticAttributes.NET_SOCK_PEER_ADDR" { it == "127.0.0.1" || it == null } + "$SemanticAttributes.NET_SOCK_PEER_NAME" { it == "localhost" || it == "127.0.0.1" || it == null } + "$SemanticAttributes.NET_SOCK_PEER_PORT" { it == null || it instanceof Number } // Because of caching, not all requests hit the server so this tag may be absent - "couchbase.local.address" { it == null || String } + "couchbase.local.address" { it == null || it instanceof String } // Not all couchbase operations have operation id. Notably, 'ViewQuery's do not // We assign a spanName of 'Bucket.query' and this is shared with n1ql queries // that do have operation ids - "couchbase.operation_id" { it == null || String } + "couchbase.operation_id" { it == null || it instanceof String } } } } 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 8f88bb6a496d..3e1f204514ef 100644 --- a/instrumentation/couchbase/couchbase-3.1.6/javaagent/build.gradle.kts +++ b/instrumentation/couchbase/couchbase-3.1.6/javaagent/build.gradle.kts @@ -35,11 +35,11 @@ dependencies { testImplementation("org.testcontainers:couchbase") - latestDepTestLibrary("com.couchbase.client:java-client:3.1.+") + latestDepTestLibrary("com.couchbase.client:java-client:3.1.+") // see couchbase-3.2 module } tasks { test { - usesService(gradle.sharedServices.registrations["testcontainersBuildService"].getService()) + usesService(gradle.sharedServices.registrations["testcontainersBuildService"].service) } } diff --git a/instrumentation/couchbase/couchbase-3.1/javaagent/build.gradle.kts b/instrumentation/couchbase/couchbase-3.1/javaagent/build.gradle.kts index cb322a9fd16b..feab46c94526 100644 --- a/instrumentation/couchbase/couchbase-3.1/javaagent/build.gradle.kts +++ b/instrumentation/couchbase/couchbase-3.1/javaagent/build.gradle.kts @@ -35,11 +35,11 @@ dependencies { testImplementation("org.testcontainers:couchbase") - latestDepTestLibrary("com.couchbase.client:java-client:3.1.5") + latestDepTestLibrary("com.couchbase.client:java-client:3.1.5") // see couchbase-3.1.6 module } tasks { test { - usesService(gradle.sharedServices.registrations["testcontainersBuildService"].getService()) + usesService(gradle.sharedServices.registrations["testcontainersBuildService"].service) } } diff --git a/instrumentation/couchbase/couchbase-3.2/javaagent/build.gradle.kts b/instrumentation/couchbase/couchbase-3.2/javaagent/build.gradle.kts index 2ae51cc0f28b..e1a598c0cb3e 100644 --- a/instrumentation/couchbase/couchbase-3.2/javaagent/build.gradle.kts +++ b/instrumentation/couchbase/couchbase-3.2/javaagent/build.gradle.kts @@ -38,6 +38,7 @@ dependencies { tasks { test { + systemProperty("testLatestDeps", findProperty("testLatestDeps") as Boolean) usesService(gradle.sharedServices.registrations["testcontainersBuildService"].getService()) } } diff --git a/instrumentation/couchbase/couchbase-3.2/javaagent/src/test/groovy/CouchbaseClient32Test.groovy b/instrumentation/couchbase/couchbase-3.2/javaagent/src/test/groovy/CouchbaseClient32Test.groovy index 4f28650d9f53..9b8a4fba60a2 100644 --- a/instrumentation/couchbase/couchbase-3.2/javaagent/src/test/groovy/CouchbaseClient32Test.groovy +++ b/instrumentation/couchbase/couchbase-3.2/javaagent/src/test/groovy/CouchbaseClient32Test.groovy @@ -17,6 +17,8 @@ import spock.lang.Shared import java.time.Duration +import static io.opentelemetry.api.trace.StatusCode.ERROR + // Couchbase instrumentation is owned upstream so we don't assert on the contents of the spans, only // that the instrumentation is properly registered by the agent, meaning some spans were generated. class CouchbaseClient32Test extends AgentInstrumentationSpecification { @@ -61,6 +63,10 @@ class CouchbaseClient32Test extends AgentInstrumentationSpecification { trace(0, 2) { span(0) { name(~/.*get/) + if (Boolean.getBoolean("testLatestDeps")) { + // this is the correct behavior + status ERROR + } } span(1) { name(~/.*dispatch_to_server/) diff --git a/instrumentation/dropwizard/dropwizard-metrics-4.0/javaagent/build.gradle.kts b/instrumentation/dropwizard/dropwizard-metrics-4.0/javaagent/build.gradle.kts new file mode 100644 index 000000000000..261d7550154c --- /dev/null +++ b/instrumentation/dropwizard/dropwizard-metrics-4.0/javaagent/build.gradle.kts @@ -0,0 +1,20 @@ +plugins { + id("otel.javaagent-instrumentation") +} + +muzzle { + pass { + group.set("io.dropwizard.metrics") + module.set("metrics-core") + versions.set("[4.0.0,)") + assertInverse.set(true) + } +} + +dependencies { + library("io.dropwizard.metrics:metrics-core:4.0.0") +} + +tasks.withType().configureEach { + jvmArgs("-Dotel.instrumentation.dropwizard-metrics.enabled=true") +} diff --git a/instrumentation/dropwizard/dropwizard-metrics-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/dropwizardmetrics/CounterInstrumentation.java b/instrumentation/dropwizard/dropwizard-metrics-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/dropwizardmetrics/CounterInstrumentation.java new file mode 100644 index 000000000000..3db26d07e420 --- /dev/null +++ b/instrumentation/dropwizard/dropwizard-metrics-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/dropwizardmetrics/CounterInstrumentation.java @@ -0,0 +1,51 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.dropwizardmetrics; + +import static io.opentelemetry.javaagent.instrumentation.dropwizardmetrics.DropwizardSingletons.metrics; +import static net.bytebuddy.matcher.ElementMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.takesArguments; + +import com.codahale.metrics.Counter; +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; + +public class CounterInstrumentation implements TypeInstrumentation { + + @Override + public ElementMatcher typeMatcher() { + return named("com.codahale.metrics.Counter"); + } + + @Override + public void transform(TypeTransformer transformer) { + transformer.applyAdviceToMethod( + named("inc").and(takesArguments(long.class)), this.getClass().getName() + "$IncAdvice"); + transformer.applyAdviceToMethod( + named("dec").and(takesArguments(long.class)), this.getClass().getName() + "$DecAdvice"); + } + + @SuppressWarnings("unused") + public static class IncAdvice { + + @Advice.OnMethodEnter(suppress = Throwable.class) + public static void onEnter(@Advice.This Counter counter, @Advice.Argument(0) long increment) { + metrics().counterAdd(counter, increment); + } + } + + @SuppressWarnings("unused") + public static class DecAdvice { + + @Advice.OnMethodEnter(suppress = Throwable.class) + public static void onEnter(@Advice.This Counter counter, @Advice.Argument(0) long decrement) { + metrics().counterAdd(counter, -decrement); + } + } +} diff --git a/instrumentation/dropwizard/dropwizard-metrics-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/dropwizardmetrics/DropwizardMetricsAdapter.java b/instrumentation/dropwizard/dropwizard-metrics-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/dropwizardmetrics/DropwizardMetricsAdapter.java new file mode 100644 index 000000000000..61c01ef1c38f --- /dev/null +++ b/instrumentation/dropwizard/dropwizard-metrics-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/dropwizardmetrics/DropwizardMetricsAdapter.java @@ -0,0 +1,174 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.dropwizardmetrics; + +import com.codahale.metrics.Counter; +import com.codahale.metrics.Gauge; +import com.codahale.metrics.Histogram; +import com.codahale.metrics.Meter; +import com.codahale.metrics.MetricRegistryListener; +import com.codahale.metrics.Timer; +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.api.metrics.DoubleHistogram; +import io.opentelemetry.api.metrics.LongCounter; +import io.opentelemetry.api.metrics.LongHistogram; +import io.opentelemetry.api.metrics.LongUpDownCounter; +import io.opentelemetry.api.metrics.ObservableDoubleGauge; +import io.opentelemetry.instrumentation.api.util.VirtualField; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; + +public final class DropwizardMetricsAdapter implements MetricRegistryListener { + + private static final double NANOS_PER_MS = TimeUnit.MILLISECONDS.toNanos(1); + + private static final VirtualField otelUpDownCounterField = + VirtualField.find(Counter.class, LongUpDownCounter.class); + private static final VirtualField otelHistogramField = + VirtualField.find(Histogram.class, LongHistogram.class); + private static final VirtualField otelCounterField = + VirtualField.find(Meter.class, LongCounter.class); + private static final VirtualField otelDoubleHistogramField = + VirtualField.find(Timer.class, DoubleHistogram.class); + + private final io.opentelemetry.api.metrics.Meter otelMeter; + + private final Map otelDoubleHistograms = new ConcurrentHashMap<>(); + private final Map otelCounters = new ConcurrentHashMap<>(); + private final Map otelHistograms = new ConcurrentHashMap<>(); + private final Map otelUpDownCounters = new ConcurrentHashMap<>(); + private final Map otelGauges = new ConcurrentHashMap<>(); + + private final Map dropwizardCounters = new ConcurrentHashMap<>(); + private final Map dropwizardHistograms = new ConcurrentHashMap<>(); + private final Map dropwizardMeters = new ConcurrentHashMap<>(); + private final Map dropwizardTimers = new ConcurrentHashMap<>(); + + public DropwizardMetricsAdapter(OpenTelemetry openTelemetry) { + this.otelMeter = openTelemetry.getMeter("io.opentelemetry.dropwizard-metrics-4.0"); + } + + @Override + public void onGaugeAdded(String name, Gauge gauge) { + ObservableDoubleGauge otelGauge = + otelMeter + .gaugeBuilder(name) + .buildWithCallback( + measurement -> { + Object val = gauge.getValue(); + if (val instanceof Number) { + measurement.record(((Number) val).doubleValue()); + } + }); + otelGauges.put(name, otelGauge); + } + + @Override + public void onGaugeRemoved(String name) { + ObservableDoubleGauge otelGauge = otelGauges.remove(name); + if (otelGauge != null) { + otelGauge.close(); + } + } + + @Override + public void onCounterAdded(String name, Counter dropwizardCounter) { + dropwizardCounters.put(name, dropwizardCounter); + LongUpDownCounter otelCounter = + otelUpDownCounters.computeIfAbsent(name, n -> otelMeter.upDownCounterBuilder(n).build()); + otelUpDownCounterField.set(dropwizardCounter, otelCounter); + } + + @Override + public void onCounterRemoved(String name) { + Counter dropwizardCounter = dropwizardCounters.remove(name); + otelUpDownCounters.remove(name); + if (dropwizardCounter != null) { + otelUpDownCounterField.set(dropwizardCounter, null); + } + } + + public void counterAdd(Counter dropwizardCounter, long increment) { + LongUpDownCounter otelCounter = otelUpDownCounterField.get(dropwizardCounter); + if (otelCounter != null) { + otelCounter.add(increment); + } + } + + @Override + public void onHistogramAdded(String name, Histogram dropwizardHistogram) { + dropwizardHistograms.put(name, dropwizardHistogram); + LongHistogram otelHistogram = + otelHistograms.computeIfAbsent(name, n -> otelMeter.histogramBuilder(n).ofLongs().build()); + otelHistogramField.set(dropwizardHistogram, otelHistogram); + } + + @Override + public void onHistogramRemoved(String name) { + Histogram dropwizardHistogram = dropwizardHistograms.remove(name); + otelHistograms.remove(name); + if (dropwizardHistogram != null) { + otelHistogramField.set(dropwizardHistogram, null); + } + } + + public void histogramUpdate(Histogram dropwizardHistogram, long value) { + LongHistogram otelHistogram = otelHistogramField.get(dropwizardHistogram); + if (otelHistogram != null) { + otelHistogram.record(value); + } + } + + @Override + public void onMeterAdded(String name, Meter dropwizardMeter) { + dropwizardMeters.put(name, dropwizardMeter); + LongCounter otelCounter = + otelCounters.computeIfAbsent(name, n -> otelMeter.counterBuilder(n).build()); + otelCounterField.set(dropwizardMeter, otelCounter); + } + + @Override + public void onMeterRemoved(String name) { + Meter dropwizardMeter = dropwizardMeters.remove(name); + otelCounters.remove(name); + if (dropwizardMeter != null) { + otelCounterField.set(dropwizardMeter, null); + } + } + + public void meterMark(Meter dropwizardMeter, long increment) { + LongCounter otelCounter = otelCounterField.get(dropwizardMeter); + if (otelCounter != null) { + otelCounter.add(increment); + } + } + + @Override + public void onTimerAdded(String name, Timer dropwizardTimer) { + dropwizardTimers.put(name, dropwizardTimer); + DoubleHistogram otelHistogram = + otelDoubleHistograms.computeIfAbsent( + name, n -> otelMeter.histogramBuilder(n).setUnit("ms").build()); + otelDoubleHistogramField.set(dropwizardTimer, otelHistogram); + } + + @Override + public void onTimerRemoved(String name) { + Timer dropwizardTimer = dropwizardTimers.remove(name); + otelDoubleHistograms.remove(name); + if (dropwizardTimer != null) { + otelDoubleHistogramField.set(dropwizardTimer, null); + } + } + + public void timerUpdate(Timer dropwizardTimer, long nanos) { + DoubleHistogram otelHistogram = otelDoubleHistogramField.get(dropwizardTimer); + if (otelHistogram != null) { + otelHistogram.record(nanos / NANOS_PER_MS); + } + } +} diff --git a/instrumentation/dropwizard/dropwizard-metrics-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/dropwizardmetrics/DropwizardMetricsInstrumentationModule.java b/instrumentation/dropwizard/dropwizard-metrics-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/dropwizardmetrics/DropwizardMetricsInstrumentationModule.java new file mode 100644 index 000000000000..4ab8d92e57c9 --- /dev/null +++ b/instrumentation/dropwizard/dropwizard-metrics-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/dropwizardmetrics/DropwizardMetricsInstrumentationModule.java @@ -0,0 +1,49 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.dropwizardmetrics; + +import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed; +import static java.util.Arrays.asList; +import static net.bytebuddy.matcher.ElementMatchers.not; + +import com.google.auto.service.AutoService; +import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule; +import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; +import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; +import java.util.List; +import net.bytebuddy.matcher.ElementMatcher; + +@AutoService(InstrumentationModule.class) +public class DropwizardMetricsInstrumentationModule extends InstrumentationModule { + + public DropwizardMetricsInstrumentationModule() { + super("dropwizard-metrics", "dropwizard-metrics-4.0"); + } + + @Override + public ElementMatcher.Junction classLoaderMatcher() { + // removed in 4.0 + return not(hasClassesNamed("com.codahale.metrics.LongAdder")); + } + + @Override + public boolean defaultEnabled(ConfigProperties config) { + // 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 + return false; + } + + @Override + public List typeInstrumentations() { + return asList( + new MetricRegistryInstrumentation(), + new CounterInstrumentation(), + new HistogramInstrumentation(), + new MeterInstrumentation(), + new TimerInstrumentation()); + } +} diff --git a/instrumentation/dropwizard/dropwizard-metrics-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/dropwizardmetrics/DropwizardSingletons.java b/instrumentation/dropwizard/dropwizard-metrics-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/dropwizardmetrics/DropwizardSingletons.java new file mode 100644 index 000000000000..e8ea2ce5dd32 --- /dev/null +++ b/instrumentation/dropwizard/dropwizard-metrics-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/dropwizardmetrics/DropwizardSingletons.java @@ -0,0 +1,20 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.dropwizardmetrics; + +import io.opentelemetry.api.GlobalOpenTelemetry; + +public final class DropwizardSingletons { + + private static final DropwizardMetricsAdapter METRICS = + new DropwizardMetricsAdapter(GlobalOpenTelemetry.get()); + + public static DropwizardMetricsAdapter metrics() { + return METRICS; + } + + private DropwizardSingletons() {} +} diff --git a/instrumentation/dropwizard/dropwizard-metrics-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/dropwizardmetrics/HistogramInstrumentation.java b/instrumentation/dropwizard/dropwizard-metrics-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/dropwizardmetrics/HistogramInstrumentation.java new file mode 100644 index 000000000000..6786a5cf41bd --- /dev/null +++ b/instrumentation/dropwizard/dropwizard-metrics-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/dropwizardmetrics/HistogramInstrumentation.java @@ -0,0 +1,41 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.dropwizardmetrics; + +import static io.opentelemetry.javaagent.instrumentation.dropwizardmetrics.DropwizardSingletons.metrics; +import static net.bytebuddy.matcher.ElementMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.takesArguments; + +import com.codahale.metrics.Histogram; +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; + +public class HistogramInstrumentation implements TypeInstrumentation { + + @Override + public ElementMatcher typeMatcher() { + return named("com.codahale.metrics.Histogram"); + } + + @Override + public void transform(TypeTransformer transformer) { + transformer.applyAdviceToMethod( + named("update").and(takesArguments(long.class)), + this.getClass().getName() + "$UpdateAdvice"); + } + + @SuppressWarnings("unused") + public static class UpdateAdvice { + + @Advice.OnMethodEnter(suppress = Throwable.class) + public static void onEnter(@Advice.This Histogram histogram, @Advice.Argument(0) long value) { + metrics().histogramUpdate(histogram, value); + } + } +} diff --git a/instrumentation/dropwizard/dropwizard-metrics-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/dropwizardmetrics/MeterInstrumentation.java b/instrumentation/dropwizard/dropwizard-metrics-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/dropwizardmetrics/MeterInstrumentation.java new file mode 100644 index 000000000000..2fbd48948644 --- /dev/null +++ b/instrumentation/dropwizard/dropwizard-metrics-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/dropwizardmetrics/MeterInstrumentation.java @@ -0,0 +1,40 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.dropwizardmetrics; + +import static io.opentelemetry.javaagent.instrumentation.dropwizardmetrics.DropwizardSingletons.metrics; +import static net.bytebuddy.matcher.ElementMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.takesArguments; + +import com.codahale.metrics.Meter; +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; + +public class MeterInstrumentation implements TypeInstrumentation { + + @Override + public ElementMatcher typeMatcher() { + return named("com.codahale.metrics.Meter"); + } + + @Override + public void transform(TypeTransformer transformer) { + transformer.applyAdviceToMethod( + named("mark").and(takesArguments(long.class)), this.getClass().getName() + "$MarkAdvice"); + } + + @SuppressWarnings("unused") + public static class MarkAdvice { + + @Advice.OnMethodEnter(suppress = Throwable.class) + public static void onEnter(@Advice.This Meter meter, @Advice.Argument(0) long increment) { + metrics().meterMark(meter, increment); + } + } +} diff --git a/instrumentation/dropwizard/dropwizard-metrics-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/dropwizardmetrics/MetricRegistryInstrumentation.java b/instrumentation/dropwizard/dropwizard-metrics-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/dropwizardmetrics/MetricRegistryInstrumentation.java new file mode 100644 index 000000000000..40decdfa1cb9 --- /dev/null +++ b/instrumentation/dropwizard/dropwizard-metrics-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/dropwizardmetrics/MetricRegistryInstrumentation.java @@ -0,0 +1,40 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.dropwizardmetrics; + +import static io.opentelemetry.javaagent.instrumentation.dropwizardmetrics.DropwizardSingletons.metrics; +import static net.bytebuddy.matcher.ElementMatchers.isDefaultConstructor; +import static net.bytebuddy.matcher.ElementMatchers.named; + +import com.codahale.metrics.MetricRegistry; +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; + +public class MetricRegistryInstrumentation implements TypeInstrumentation { + + @Override + public ElementMatcher typeMatcher() { + return named("com.codahale.metrics.MetricRegistry"); + } + + @Override + public void transform(TypeTransformer transformer) { + transformer.applyAdviceToMethod( + isDefaultConstructor(), this.getClass().getName() + "$ConstructorAdvice"); + } + + @SuppressWarnings("unused") + public static class ConstructorAdvice { + + @Advice.OnMethodExit(suppress = Throwable.class) + public static void onExit(@Advice.This MetricRegistry metricRegistry) { + metricRegistry.addListener(metrics()); + } + } +} diff --git a/instrumentation/dropwizard/dropwizard-metrics-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/dropwizardmetrics/TimerInstrumentation.java b/instrumentation/dropwizard/dropwizard-metrics-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/dropwizardmetrics/TimerInstrumentation.java new file mode 100644 index 000000000000..e7762069874c --- /dev/null +++ b/instrumentation/dropwizard/dropwizard-metrics-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/dropwizardmetrics/TimerInstrumentation.java @@ -0,0 +1,41 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.dropwizardmetrics; + +import static io.opentelemetry.javaagent.instrumentation.dropwizardmetrics.DropwizardSingletons.metrics; +import static net.bytebuddy.matcher.ElementMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.takesArguments; + +import com.codahale.metrics.Timer; +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; + +public class TimerInstrumentation implements TypeInstrumentation { + + @Override + public ElementMatcher typeMatcher() { + return named("com.codahale.metrics.Timer"); + } + + @Override + public void transform(TypeTransformer transformer) { + transformer.applyAdviceToMethod( + named("update").and(takesArguments(long.class)), + this.getClass().getName() + "$UpdateAdvice"); + } + + @SuppressWarnings("unused") + public static class UpdateAdvice { + + @Advice.OnMethodEnter(suppress = Throwable.class) + public static void onEnter(@Advice.This Timer timer, @Advice.Argument(0) long nanos) { + metrics().timerUpdate(timer, nanos); + } + } +} diff --git a/instrumentation/dropwizard/dropwizard-metrics-4.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/dropwizardmetrics/DropwizardMetricsTest.java b/instrumentation/dropwizard/dropwizard-metrics-4.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/dropwizardmetrics/DropwizardMetricsTest.java new file mode 100644 index 000000000000..0f7392dd4469 --- /dev/null +++ b/instrumentation/dropwizard/dropwizard-metrics-4.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/dropwizardmetrics/DropwizardMetricsTest.java @@ -0,0 +1,200 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.dropwizardmetrics; + +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; + +import com.codahale.metrics.Counter; +import com.codahale.metrics.Histogram; +import com.codahale.metrics.Meter; +import com.codahale.metrics.MetricRegistry; +import com.codahale.metrics.Timer; +import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicLong; +import org.assertj.core.api.AbstractIterableAssert; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +public class DropwizardMetricsTest { + + static final String INSTRUMENTATION_NAME = "io.opentelemetry.dropwizard-metrics-4.0"; + + @RegisterExtension + static final InstrumentationExtension testing = AgentInstrumentationExtension.create(); + + @Test + void gauge() throws InterruptedException { + // given + MetricRegistry metricRegistry = new MetricRegistry(); + + AtomicLong value = new AtomicLong(42); + + // when + metricRegistry.gauge("test.gauge", () -> value::get); + + // then + testing.waitAndAssertMetrics( + INSTRUMENTATION_NAME, + "test.gauge", + metrics -> + metrics.anySatisfy( + metric -> + assertThat(metric) + .hasDoubleGaugeSatisfying( + g -> g.hasPointsSatisfying(point -> point.hasValue(42))))); + + // when + metricRegistry.remove("test.gauge"); + Thread.sleep(100); // give time for any inflight metric export to be received + testing.clearData(); + + // then + Thread.sleep(100); // interval of the test metrics exporter + testing.waitAndAssertMetrics( + INSTRUMENTATION_NAME, "test.gauge", AbstractIterableAssert::isEmpty); + } + + @Test + void counter() throws InterruptedException { + // given + MetricRegistry metricRegistry = new MetricRegistry(); + + // when + Counter counter = metricRegistry.counter("test.counter"); + counter.inc(); + counter.inc(11); + counter.dec(5); + + // then + testing.waitAndAssertMetrics( + INSTRUMENTATION_NAME, + "test.counter", + metrics -> + metrics.anySatisfy( + metric -> + assertThat(metric) + .hasLongSumSatisfying( + sum -> + sum.isNotMonotonic() + .hasPointsSatisfying(point -> point.hasValue(7))))); + testing.clearData(); + + // when + metricRegistry.remove("test.counter"); + counter.inc(123); + + // then + Thread.sleep(100); // interval of the test metrics exporter + testing.waitAndAssertMetrics( + INSTRUMENTATION_NAME, "test.counter", AbstractIterableAssert::isEmpty); + } + + @Test + void histogram() throws InterruptedException { + // given + MetricRegistry metricRegistry = new MetricRegistry(); + + // when + Histogram histogram = metricRegistry.histogram("test.histogram"); + histogram.update(12); + histogram.update(30); + + // then + testing.waitAndAssertMetrics( + INSTRUMENTATION_NAME, + "test.histogram", + metrics -> + metrics.anySatisfy( + metric -> + assertThat(metric) + .hasHistogramSatisfying( + histogramMetric -> + histogramMetric.hasPointsSatisfying( + point -> point.hasSum(42).hasCount(2))))); + testing.clearData(); + + // when + metricRegistry.remove("test.histogram"); + histogram.update(100); + + // then + Thread.sleep(100); // interval of the test metrics exporter + testing.waitAndAssertMetrics( + INSTRUMENTATION_NAME, "test.histogram", AbstractIterableAssert::isEmpty); + } + + @Test + void meter() throws InterruptedException { + // given + MetricRegistry metricRegistry = new MetricRegistry(); + + // when + Meter meter = metricRegistry.meter("test.meter"); + meter.mark(); + meter.mark(11); + + // then + testing.waitAndAssertMetrics( + INSTRUMENTATION_NAME, + "test.meter", + metrics -> + metrics.anySatisfy( + metric -> + assertThat(metric) + .hasLongSumSatisfying( + sum -> + sum.isMonotonic() + .hasPointsSatisfying(point -> point.hasValue(12))))); + testing.clearData(); + + // when + metricRegistry.remove("test.meter"); + meter.mark(); + + // then + Thread.sleep(100); // interval of the test metrics exporter + testing.waitAndAssertMetrics( + INSTRUMENTATION_NAME, "test.meter", AbstractIterableAssert::isEmpty); + } + + @Test + @SuppressWarnings("PreferJavaTimeOverload") + void timer() throws InterruptedException { + // given + MetricRegistry metricRegistry = new MetricRegistry(); + + // when + Timer timer = metricRegistry.timer("test.timer"); + timer.update(1, TimeUnit.MILLISECONDS); + timer.update(234_000, TimeUnit.NANOSECONDS); + + // then + testing.waitAndAssertMetrics( + INSTRUMENTATION_NAME, + "test.timer", + metrics -> + metrics.anySatisfy( + metric -> + assertThat(metric) + .hasUnit("ms") + .hasHistogramSatisfying( + histogram -> + histogram.hasPointsSatisfying( + point -> point.hasSum(1.234).hasCount(2))))); + testing.clearData(); + + // when + metricRegistry.remove("test.timer"); + timer.update(12, TimeUnit.SECONDS); + + // then + Thread.sleep(100); // interval of the test metrics exporter + testing.waitAndAssertMetrics( + INSTRUMENTATION_NAME, "test.timer", AbstractIterableAssert::isEmpty); + } +} diff --git a/instrumentation/dropwizard/dropwizard-testing/build.gradle.kts b/instrumentation/dropwizard/dropwizard-testing/build.gradle.kts index 787961ad4096..aef551a103c2 100644 --- a/instrumentation/dropwizard/dropwizard-testing/build.gradle.kts +++ b/instrumentation/dropwizard/dropwizard-testing/build.gradle.kts @@ -13,5 +13,13 @@ dependencies { testImplementation("com.fasterxml.jackson.module:jackson-module-afterburner") } -// Requires old Guava. Can't use enforcedPlatform since predates BOM -configurations.testRuntimeClasspath.resolutionStrategy.force("com.google.guava:guava:19.0") +configurations.testRuntimeClasspath { + resolutionStrategy { + // Requires old Guava. Can't use enforcedPlatform since predates BOM + force("com.google.guava:guava:19.0") + + // requires old logback (and therefore also old slf4j) + force("ch.qos.logback:logback-classic:1.2.11") + force("org.slf4j:slf4j-api:1.7.36") + } +} diff --git a/instrumentation/dropwizard/dropwizard-testing/src/test/groovy/DropwizardTest.groovy b/instrumentation/dropwizard/dropwizard-testing/src/test/groovy/DropwizardTest.groovy index b2d7e924ae70..a121252656d3 100644 --- a/instrumentation/dropwizard/dropwizard-testing/src/test/groovy/DropwizardTest.groovy +++ b/instrumentation/dropwizard/dropwizard-testing/src/test/groovy/DropwizardTest.groovy @@ -63,7 +63,7 @@ class DropwizardTest extends HttpServerTest implements Ag // this override is needed because dropwizard reports peer ip as the client ip @Override - String peerIp(ServerEndpoint endpoint) { + String sockPeerAddr(ServerEndpoint endpoint) { TEST_CLIENT_IP } 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 c6c4cb954fa3..f9c799c12e65 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 @@ -18,7 +18,7 @@ public final class DropwizardSingletons { Instrumenter.builder( GlobalOpenTelemetry.get(), INSTRUMENTATION_NAME, DropwizardSingletons::spanName) .setEnabled(ExperimentalConfig.get().controllerTelemetryEnabled()) - .newInstrumenter(); + .buildInstrumenter(); private static String spanName(View view) { return "Render " + view.getTemplateName(); diff --git a/instrumentation/elasticsearch/elasticsearch-rest-5.0/javaagent/src/test/groovy/ElasticsearchRest5Test.groovy b/instrumentation/elasticsearch/elasticsearch-rest-5.0/javaagent/src/test/groovy/ElasticsearchRest5Test.groovy index 75c6554c5d46..b9830005044e 100644 --- a/instrumentation/elasticsearch/elasticsearch-rest-5.0/javaagent/src/test/groovy/ElasticsearchRest5Test.groovy +++ b/instrumentation/elasticsearch/elasticsearch-rest-5.0/javaagent/src/test/groovy/ElasticsearchRest5Test.groovy @@ -80,8 +80,6 @@ class ElasticsearchRest5Test extends AgentInstrumentationSpecification { "$SemanticAttributes.DB_OPERATION" "GET" "$SemanticAttributes.DB_STATEMENT" "GET _cluster/health" "$SemanticAttributes.NET_TRANSPORT" SemanticAttributes.NetTransportValues.IP_TCP - "$SemanticAttributes.NET_PEER_NAME" httpHost.hostName - "$SemanticAttributes.NET_PEER_PORT" httpHost.port } } span(1) { @@ -96,6 +94,7 @@ class ElasticsearchRest5Test extends AgentInstrumentationSpecification { "$SemanticAttributes.HTTP_FLAVOR" SemanticAttributes.HttpFlavorValues.HTTP_1_1 "$SemanticAttributes.HTTP_URL" "${httpHost.toURI()}/_cluster/health" "$SemanticAttributes.HTTP_STATUS_CODE" 200 + "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" Long } } } @@ -154,8 +153,6 @@ class ElasticsearchRest5Test extends AgentInstrumentationSpecification { "$SemanticAttributes.DB_OPERATION" "GET" "$SemanticAttributes.DB_STATEMENT" "GET _cluster/health" "$SemanticAttributes.NET_TRANSPORT" SemanticAttributes.NetTransportValues.IP_TCP - "$SemanticAttributes.NET_PEER_NAME" httpHost.hostName - "$SemanticAttributes.NET_PEER_PORT" httpHost.port } } span(2) { @@ -170,6 +167,7 @@ class ElasticsearchRest5Test extends AgentInstrumentationSpecification { "$SemanticAttributes.HTTP_FLAVOR" SemanticAttributes.HttpFlavorValues.HTTP_1_1 "$SemanticAttributes.HTTP_URL" "${httpHost.toURI()}/_cluster/health" "$SemanticAttributes.HTTP_STATUS_CODE" 200 + "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" Long } } span(3) { diff --git a/instrumentation/elasticsearch/elasticsearch-rest-6.4/javaagent/src/test/groovy/ElasticsearchRest6Test.groovy b/instrumentation/elasticsearch/elasticsearch-rest-6.4/javaagent/src/test/groovy/ElasticsearchRest6Test.groovy index 1e94e0b3778d..522d8ba4218e 100644 --- a/instrumentation/elasticsearch/elasticsearch-rest-6.4/javaagent/src/test/groovy/ElasticsearchRest6Test.groovy +++ b/instrumentation/elasticsearch/elasticsearch-rest-6.4/javaagent/src/test/groovy/ElasticsearchRest6Test.groovy @@ -74,8 +74,6 @@ class ElasticsearchRest6Test extends AgentInstrumentationSpecification { "$SemanticAttributes.DB_OPERATION" "GET" "$SemanticAttributes.DB_STATEMENT" "GET _cluster/health" "$SemanticAttributes.NET_TRANSPORT" SemanticAttributes.NetTransportValues.IP_TCP - "$SemanticAttributes.NET_PEER_NAME" httpHost.hostName - "$SemanticAttributes.NET_PEER_PORT" httpHost.port } } span(1) { @@ -90,6 +88,7 @@ class ElasticsearchRest6Test extends AgentInstrumentationSpecification { "$SemanticAttributes.HTTP_FLAVOR" SemanticAttributes.HttpFlavorValues.HTTP_1_1 "$SemanticAttributes.HTTP_URL" "${httpHost.toURI()}/_cluster/health" "$SemanticAttributes.HTTP_STATUS_CODE" 200 + "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" Long } } } @@ -147,8 +146,6 @@ class ElasticsearchRest6Test extends AgentInstrumentationSpecification { "$SemanticAttributes.DB_OPERATION" "GET" "$SemanticAttributes.DB_STATEMENT" "GET _cluster/health" "$SemanticAttributes.NET_TRANSPORT" SemanticAttributes.NetTransportValues.IP_TCP - "$SemanticAttributes.NET_PEER_NAME" httpHost.hostName - "$SemanticAttributes.NET_PEER_PORT" httpHost.port } } span(2) { @@ -163,6 +160,7 @@ class ElasticsearchRest6Test extends AgentInstrumentationSpecification { "$SemanticAttributes.HTTP_FLAVOR" SemanticAttributes.HttpFlavorValues.HTTP_1_1 "$SemanticAttributes.HTTP_URL" "${httpHost.toURI()}/_cluster/health" "$SemanticAttributes.HTTP_STATUS_CODE" 200 + "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" Long } } span(3) { diff --git a/instrumentation/elasticsearch/elasticsearch-rest-7.0/javaagent/src/test/groovy/ElasticsearchRest7Test.groovy b/instrumentation/elasticsearch/elasticsearch-rest-7.0/javaagent/src/test/groovy/ElasticsearchRest7Test.groovy index c4db7098c936..ac32b39275bc 100644 --- a/instrumentation/elasticsearch/elasticsearch-rest-7.0/javaagent/src/test/groovy/ElasticsearchRest7Test.groovy +++ b/instrumentation/elasticsearch/elasticsearch-rest-7.0/javaagent/src/test/groovy/ElasticsearchRest7Test.groovy @@ -73,8 +73,6 @@ class ElasticsearchRest7Test extends AgentInstrumentationSpecification { "$SemanticAttributes.DB_OPERATION" "GET" "$SemanticAttributes.DB_STATEMENT" "GET _cluster/health" "$SemanticAttributes.NET_TRANSPORT" SemanticAttributes.NetTransportValues.IP_TCP - "$SemanticAttributes.NET_PEER_NAME" httpHost.hostName - "$SemanticAttributes.NET_PEER_PORT" httpHost.port } } span(1) { @@ -89,6 +87,7 @@ class ElasticsearchRest7Test extends AgentInstrumentationSpecification { "$SemanticAttributes.HTTP_FLAVOR" SemanticAttributes.HttpFlavorValues.HTTP_1_1 "$SemanticAttributes.HTTP_URL" "${httpHost.toURI()}/_cluster/health" "$SemanticAttributes.HTTP_STATUS_CODE" 200 + "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" Long } } } @@ -146,8 +145,6 @@ class ElasticsearchRest7Test extends AgentInstrumentationSpecification { "$SemanticAttributes.DB_OPERATION" "GET" "$SemanticAttributes.DB_STATEMENT" "GET _cluster/health" "$SemanticAttributes.NET_TRANSPORT" SemanticAttributes.NetTransportValues.IP_TCP - "$SemanticAttributes.NET_PEER_NAME" httpHost.hostName - "$SemanticAttributes.NET_PEER_PORT" httpHost.port } } span(2) { @@ -162,6 +159,7 @@ class ElasticsearchRest7Test extends AgentInstrumentationSpecification { "$SemanticAttributes.HTTP_FLAVOR" SemanticAttributes.HttpFlavorValues.HTTP_1_1 "$SemanticAttributes.HTTP_URL" "${httpHost.toURI()}/_cluster/health" "$SemanticAttributes.HTTP_STATUS_CODE" 200 + "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" Long } } span(3) { diff --git a/instrumentation/elasticsearch/elasticsearch-rest-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/elasticsearch/rest/ElasticsearchRestInstrumenterFactory.java b/instrumentation/elasticsearch/elasticsearch-rest-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/elasticsearch/rest/ElasticsearchRestInstrumenterFactory.java index b42cd510876f..e624a3a2d388 100644 --- a/instrumentation/elasticsearch/elasticsearch-rest-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/elasticsearch/rest/ElasticsearchRestInstrumenterFactory.java +++ b/instrumentation/elasticsearch/elasticsearch-rest-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/elasticsearch/rest/ElasticsearchRestInstrumenterFactory.java @@ -7,11 +7,12 @@ import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; -import io.opentelemetry.instrumentation.api.instrumenter.PeerServiceAttributesExtractor; import io.opentelemetry.instrumentation.api.instrumenter.SpanKindExtractor; import io.opentelemetry.instrumentation.api.instrumenter.db.DbClientAttributesExtractor; import io.opentelemetry.instrumentation.api.instrumenter.db.DbClientSpanNameExtractor; import io.opentelemetry.instrumentation.api.instrumenter.net.NetClientAttributesExtractor; +import io.opentelemetry.instrumentation.api.instrumenter.net.PeerServiceAttributesExtractor; +import io.opentelemetry.javaagent.bootstrap.internal.CommonConfig; import org.elasticsearch.client.Response; public final class ElasticsearchRestInstrumenterFactory { @@ -29,8 +30,10 @@ public static Instrumenter create( DbClientSpanNameExtractor.create(dbClientAttributesGetter)) .addAttributesExtractor(DbClientAttributesExtractor.create(dbClientAttributesGetter)) .addAttributesExtractor(NetClientAttributesExtractor.create(netAttributesGetter)) - .addAttributesExtractor(PeerServiceAttributesExtractor.create(netAttributesGetter)) - .newInstrumenter(SpanKindExtractor.alwaysClient()); + .addAttributesExtractor( + PeerServiceAttributesExtractor.create( + netAttributesGetter, CommonConfig.get().getPeerServiceMapping())) + .buildInstrumenter(SpanKindExtractor.alwaysClient()); } private ElasticsearchRestInstrumenterFactory() {} diff --git a/instrumentation/elasticsearch/elasticsearch-rest-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/elasticsearch/rest/ElasticsearchRestNetResponseAttributesGetter.java b/instrumentation/elasticsearch/elasticsearch-rest-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/elasticsearch/rest/ElasticsearchRestNetResponseAttributesGetter.java index 0b93f3f5ed56..675e8b201ec0 100644 --- a/instrumentation/elasticsearch/elasticsearch-rest-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/elasticsearch/rest/ElasticsearchRestNetResponseAttributesGetter.java +++ b/instrumentation/elasticsearch/elasticsearch-rest-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/elasticsearch/rest/ElasticsearchRestNetResponseAttributesGetter.java @@ -7,6 +7,7 @@ import io.opentelemetry.instrumentation.api.instrumenter.net.NetClientAttributesGetter; import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; +import java.net.Inet6Address; import javax.annotation.Nullable; import org.elasticsearch.client.Response; @@ -20,25 +21,29 @@ public String transport(ElasticsearchRestRequest request, Response response) { @Override @Nullable - public String peerName(ElasticsearchRestRequest request, @Nullable Response response) { - if (response != null) { - return response.getHost().getHostName(); - } + public String peerName(ElasticsearchRestRequest request) { return null; } @Override @Nullable - public Integer peerPort(ElasticsearchRestRequest request, @Nullable Response response) { - if (response != null) { - return response.getHost().getPort(); + public Integer peerPort(ElasticsearchRestRequest request) { + return null; + } + + @Nullable + @Override + public String sockFamily( + ElasticsearchRestRequest elasticsearchRestRequest, @Nullable Response response) { + if (response != null && response.getHost().getAddress() instanceof Inet6Address) { + return "inet6"; } return null; } @Override @Nullable - public String peerIp(ElasticsearchRestRequest request, @Nullable Response response) { + public String sockPeerAddr(ElasticsearchRestRequest request, @Nullable Response response) { if (response != null && response.getHost().getAddress() != null) { return response.getHost().getAddress().getHostAddress(); } diff --git a/instrumentation/elasticsearch/elasticsearch-transport-5.0/javaagent/src/test/groovy/Elasticsearch5TransportClientTest.groovy b/instrumentation/elasticsearch/elasticsearch-transport-5.0/javaagent/src/test/groovy/Elasticsearch5TransportClientTest.groovy index 817f0ae13ddb..2ed7137beb75 100644 --- a/instrumentation/elasticsearch/elasticsearch-transport-5.0/javaagent/src/test/groovy/Elasticsearch5TransportClientTest.groovy +++ b/instrumentation/elasticsearch/elasticsearch-transport-5.0/javaagent/src/test/groovy/Elasticsearch5TransportClientTest.groovy @@ -125,9 +125,10 @@ class Elasticsearch5TransportClientTest extends AbstractElasticsearchTransportCl name "ClusterHealthAction" kind CLIENT attributes { - "$SemanticAttributes.NET_PEER_NAME" tcpPublishAddress.host == tcpPublishAddress.address ? null : tcpPublishAddress.host - "$SemanticAttributes.NET_PEER_IP" tcpPublishAddress.address - "$SemanticAttributes.NET_PEER_PORT" tcpPublishAddress.port + "$SemanticAttributes.NET_SOCK_FAMILY" { it == SemanticAttributes.NetSockFamilyValues.INET6 || it == null } + "$SemanticAttributes.NET_SOCK_PEER_ADDR" tcpPublishAddress.address + "$SemanticAttributes.NET_SOCK_PEER_NAME" tcpPublishAddress.host + "$SemanticAttributes.NET_SOCK_PEER_PORT" tcpPublishAddress.port "$SemanticAttributes.DB_SYSTEM" "elasticsearch" "$SemanticAttributes.DB_OPERATION" "ClusterHealthAction" "elasticsearch.action" "ClusterHealthAction" @@ -242,9 +243,10 @@ class Elasticsearch5TransportClientTest extends AbstractElasticsearchTransportCl name "CreateIndexAction" kind CLIENT attributes { - "$SemanticAttributes.NET_PEER_NAME" tcpPublishAddress.host == tcpPublishAddress.address ? null : tcpPublishAddress.host - "$SemanticAttributes.NET_PEER_IP" tcpPublishAddress.address - "$SemanticAttributes.NET_PEER_PORT" tcpPublishAddress.port + "$SemanticAttributes.NET_SOCK_FAMILY" { it == SemanticAttributes.NetSockFamilyValues.INET6 || it == null } + "$SemanticAttributes.NET_SOCK_PEER_ADDR" tcpPublishAddress.address + "$SemanticAttributes.NET_SOCK_PEER_NAME" tcpPublishAddress.host + "$SemanticAttributes.NET_SOCK_PEER_PORT" tcpPublishAddress.port "$SemanticAttributes.DB_SYSTEM" "elasticsearch" "$SemanticAttributes.DB_OPERATION" "CreateIndexAction" "elasticsearch.action" "CreateIndexAction" @@ -258,9 +260,10 @@ class Elasticsearch5TransportClientTest extends AbstractElasticsearchTransportCl name "GetAction" kind CLIENT attributes { - "$SemanticAttributes.NET_PEER_NAME" tcpPublishAddress.host == tcpPublishAddress.address ? null : tcpPublishAddress.host - "$SemanticAttributes.NET_PEER_IP" tcpPublishAddress.address - "$SemanticAttributes.NET_PEER_PORT" tcpPublishAddress.port + "$SemanticAttributes.NET_SOCK_FAMILY" { it == SemanticAttributes.NetSockFamilyValues.INET6 || it == null } + "$SemanticAttributes.NET_SOCK_PEER_ADDR" tcpPublishAddress.address + "$SemanticAttributes.NET_SOCK_PEER_NAME" tcpPublishAddress.host + "$SemanticAttributes.NET_SOCK_PEER_PORT" tcpPublishAddress.port "$SemanticAttributes.DB_SYSTEM" "elasticsearch" "$SemanticAttributes.DB_OPERATION" "GetAction" "elasticsearch.action" "GetAction" @@ -289,9 +292,10 @@ class Elasticsearch5TransportClientTest extends AbstractElasticsearchTransportCl name "IndexAction" kind CLIENT attributes { - "$SemanticAttributes.NET_PEER_NAME" tcpPublishAddress.host == tcpPublishAddress.address ? null : tcpPublishAddress.host - "$SemanticAttributes.NET_PEER_IP" tcpPublishAddress.address - "$SemanticAttributes.NET_PEER_PORT" tcpPublishAddress.port + "$SemanticAttributes.NET_SOCK_FAMILY" { it == SemanticAttributes.NetSockFamilyValues.INET6 || it == null } + "$SemanticAttributes.NET_SOCK_PEER_ADDR" tcpPublishAddress.address + "$SemanticAttributes.NET_SOCK_PEER_NAME" tcpPublishAddress.host + "$SemanticAttributes.NET_SOCK_PEER_PORT" tcpPublishAddress.port "$SemanticAttributes.DB_SYSTEM" "elasticsearch" "$SemanticAttributes.DB_OPERATION" "IndexAction" "elasticsearch.action" "IndexAction" @@ -310,9 +314,10 @@ class Elasticsearch5TransportClientTest extends AbstractElasticsearchTransportCl name "GetAction" kind CLIENT attributes { - "$SemanticAttributes.NET_PEER_NAME" tcpPublishAddress.host == tcpPublishAddress.address ? null : tcpPublishAddress.address - "$SemanticAttributes.NET_PEER_IP" tcpPublishAddress.address - "$SemanticAttributes.NET_PEER_PORT" tcpPublishAddress.port + "$SemanticAttributes.NET_SOCK_FAMILY" { it == SemanticAttributes.NetSockFamilyValues.INET6 || it == null } + "$SemanticAttributes.NET_SOCK_PEER_ADDR" tcpPublishAddress.address + "$SemanticAttributes.NET_SOCK_PEER_NAME" tcpPublishAddress.host + "$SemanticAttributes.NET_SOCK_PEER_PORT" tcpPublishAddress.port "$SemanticAttributes.DB_SYSTEM" "elasticsearch" "$SemanticAttributes.DB_OPERATION" "GetAction" "elasticsearch.action" "GetAction" 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 f65c76796804..c240cd4b9e0a 100644 --- a/instrumentation/elasticsearch/elasticsearch-transport-5.3/javaagent/build.gradle.kts +++ b/instrumentation/elasticsearch/elasticsearch-transport-5.3/javaagent/build.gradle.kts @@ -56,4 +56,8 @@ dependencies { tasks.withType().configureEach { // TODO run tests both with and without experimental span attributes jvmArgs("-Dotel.instrumentation.elasticsearch.experimental-span-attributes=true") + + // required on jdk17 + jvmArgs("--add-opens=java.base/java.lang=ALL-UNNAMED") + jvmArgs("-XX:+IgnoreUnrecognizedVMOptions") } diff --git a/instrumentation/elasticsearch/elasticsearch-transport-5.3/javaagent/src/test/groovy/Elasticsearch53TransportClientTest.groovy b/instrumentation/elasticsearch/elasticsearch-transport-5.3/javaagent/src/test/groovy/Elasticsearch53TransportClientTest.groovy index 6c5d9390e199..e876438e3f24 100644 --- a/instrumentation/elasticsearch/elasticsearch-transport-5.3/javaagent/src/test/groovy/Elasticsearch53TransportClientTest.groovy +++ b/instrumentation/elasticsearch/elasticsearch-transport-5.3/javaagent/src/test/groovy/Elasticsearch53TransportClientTest.groovy @@ -131,9 +131,10 @@ class Elasticsearch53TransportClientTest extends AbstractElasticsearchTransportC kind CLIENT childOf(span(0)) attributes { - "$SemanticAttributes.NET_PEER_NAME" tcpPublishAddress.host == tcpPublishAddress.address ? null : tcpPublishAddress.address - "$SemanticAttributes.NET_PEER_IP" tcpPublishAddress.address - "$SemanticAttributes.NET_PEER_PORT" tcpPublishAddress.port + "$SemanticAttributes.NET_SOCK_FAMILY" { it == SemanticAttributes.NetSockFamilyValues.INET6 || it == null } + "$SemanticAttributes.NET_SOCK_PEER_ADDR" tcpPublishAddress.address + "$SemanticAttributes.NET_SOCK_PEER_NAME" tcpPublishAddress.host + "$SemanticAttributes.NET_SOCK_PEER_PORT" tcpPublishAddress.port "$SemanticAttributes.DB_SYSTEM" "elasticsearch" "$SemanticAttributes.DB_OPERATION" "ClusterHealthAction" "elasticsearch.action" "ClusterHealthAction" @@ -247,9 +248,10 @@ class Elasticsearch53TransportClientTest extends AbstractElasticsearchTransportC name "CreateIndexAction" kind CLIENT attributes { - "$SemanticAttributes.NET_PEER_NAME" tcpPublishAddress.host == tcpPublishAddress.address ? null : tcpPublishAddress.address - "$SemanticAttributes.NET_PEER_IP" tcpPublishAddress.address - "$SemanticAttributes.NET_PEER_PORT" tcpPublishAddress.port + "$SemanticAttributes.NET_SOCK_FAMILY" { it == SemanticAttributes.NetSockFamilyValues.INET6 || it == null } + "$SemanticAttributes.NET_SOCK_PEER_ADDR" tcpPublishAddress.address + "$SemanticAttributes.NET_SOCK_PEER_NAME" tcpPublishAddress.host + "$SemanticAttributes.NET_SOCK_PEER_PORT" tcpPublishAddress.port "$SemanticAttributes.DB_SYSTEM" "elasticsearch" "$SemanticAttributes.DB_OPERATION" "CreateIndexAction" "elasticsearch.action" "CreateIndexAction" @@ -263,9 +265,10 @@ class Elasticsearch53TransportClientTest extends AbstractElasticsearchTransportC name "GetAction" kind CLIENT attributes { - "$SemanticAttributes.NET_PEER_NAME" tcpPublishAddress.host == tcpPublishAddress.address ? null : tcpPublishAddress.address - "$SemanticAttributes.NET_PEER_IP" tcpPublishAddress.address - "$SemanticAttributes.NET_PEER_PORT" tcpPublishAddress.port + "$SemanticAttributes.NET_SOCK_FAMILY" { it == SemanticAttributes.NetSockFamilyValues.INET6 || it == null } + "$SemanticAttributes.NET_SOCK_PEER_ADDR" tcpPublishAddress.address + "$SemanticAttributes.NET_SOCK_PEER_NAME" tcpPublishAddress.host + "$SemanticAttributes.NET_SOCK_PEER_PORT" tcpPublishAddress.port "$SemanticAttributes.DB_SYSTEM" "elasticsearch" "$SemanticAttributes.DB_OPERATION" "GetAction" "elasticsearch.action" "GetAction" @@ -294,9 +297,10 @@ class Elasticsearch53TransportClientTest extends AbstractElasticsearchTransportC name "IndexAction" kind CLIENT attributes { - "$SemanticAttributes.NET_PEER_NAME" tcpPublishAddress.host == tcpPublishAddress.address ? null : tcpPublishAddress.address - "$SemanticAttributes.NET_PEER_IP" tcpPublishAddress.address - "$SemanticAttributes.NET_PEER_PORT" tcpPublishAddress.port + "$SemanticAttributes.NET_SOCK_FAMILY" { it == SemanticAttributes.NetSockFamilyValues.INET6 || it == null } + "$SemanticAttributes.NET_SOCK_PEER_ADDR" tcpPublishAddress.address + "$SemanticAttributes.NET_SOCK_PEER_NAME" tcpPublishAddress.host + "$SemanticAttributes.NET_SOCK_PEER_PORT" tcpPublishAddress.port "$SemanticAttributes.DB_SYSTEM" "elasticsearch" "$SemanticAttributes.DB_OPERATION" "IndexAction" "elasticsearch.action" "IndexAction" @@ -316,9 +320,10 @@ class Elasticsearch53TransportClientTest extends AbstractElasticsearchTransportC name "GetAction" kind CLIENT attributes { - "$SemanticAttributes.NET_PEER_NAME" tcpPublishAddress.host == tcpPublishAddress.address ? null : tcpPublishAddress.address - "$SemanticAttributes.NET_PEER_IP" tcpPublishAddress.address - "$SemanticAttributes.NET_PEER_PORT" tcpPublishAddress.port + "$SemanticAttributes.NET_SOCK_FAMILY" { it == SemanticAttributes.NetSockFamilyValues.INET6 || it == null } + "$SemanticAttributes.NET_SOCK_PEER_ADDR" tcpPublishAddress.address + "$SemanticAttributes.NET_SOCK_PEER_NAME" tcpPublishAddress.host + "$SemanticAttributes.NET_SOCK_PEER_PORT" tcpPublishAddress.port "$SemanticAttributes.DB_SYSTEM" "elasticsearch" "$SemanticAttributes.DB_OPERATION" "GetAction" "elasticsearch.action" "GetAction" diff --git a/instrumentation/elasticsearch/elasticsearch-transport-5.3/javaagent/src/test/groovy/springdata/Elasticsearch53SpringRepositoryTest.groovy b/instrumentation/elasticsearch/elasticsearch-transport-5.3/javaagent/src/test/groovy/springdata/Elasticsearch53SpringRepositoryTest.groovy index daffbae3037e..5fef17105943 100644 --- a/instrumentation/elasticsearch/elasticsearch-transport-5.3/javaagent/src/test/groovy/springdata/Elasticsearch53SpringRepositoryTest.groovy +++ b/instrumentation/elasticsearch/elasticsearch-transport-5.3/javaagent/src/test/groovy/springdata/Elasticsearch53SpringRepositoryTest.groovy @@ -84,6 +84,8 @@ class Elasticsearch53SpringRepositoryTest extends AgentInstrumentationSpecificat name "DocRepository.findAll" kind INTERNAL attributes { + "$SemanticAttributes.CODE_NAMESPACE" DocRepository.name + "$SemanticAttributes.CODE_FUNCTION" "findAll" } } span(1) { @@ -120,6 +122,8 @@ class Elasticsearch53SpringRepositoryTest extends AgentInstrumentationSpecificat name "DocRepository.index" kind INTERNAL attributes { + "$SemanticAttributes.CODE_NAMESPACE" DocRepository.name + "$SemanticAttributes.CODE_FUNCTION" "index" } } span(1) { @@ -169,6 +173,8 @@ class Elasticsearch53SpringRepositoryTest extends AgentInstrumentationSpecificat name "DocRepository.findById" kind INTERNAL attributes { + "$SemanticAttributes.CODE_NAMESPACE" DocRepository.name + "$SemanticAttributes.CODE_FUNCTION" "findById" } } span(1) { @@ -204,6 +210,8 @@ class Elasticsearch53SpringRepositoryTest extends AgentInstrumentationSpecificat name "DocRepository.index" kind INTERNAL attributes { + "$SemanticAttributes.CODE_NAMESPACE" DocRepository.name + "$SemanticAttributes.CODE_FUNCTION" "index" } } span(1) { @@ -245,6 +253,8 @@ class Elasticsearch53SpringRepositoryTest extends AgentInstrumentationSpecificat name "DocRepository.findById" kind INTERNAL attributes { + "$SemanticAttributes.CODE_NAMESPACE" DocRepository.name + "$SemanticAttributes.CODE_FUNCTION" "findById" } } span(1) { @@ -279,6 +289,8 @@ class Elasticsearch53SpringRepositoryTest extends AgentInstrumentationSpecificat name "DocRepository.deleteById" kind INTERNAL attributes { + "$SemanticAttributes.CODE_NAMESPACE" DocRepository.name + "$SemanticAttributes.CODE_FUNCTION" "deleteById" } } span(1) { @@ -320,6 +332,8 @@ class Elasticsearch53SpringRepositoryTest extends AgentInstrumentationSpecificat name "DocRepository.findAll" kind INTERNAL attributes { + "$SemanticAttributes.CODE_NAMESPACE" DocRepository.name + "$SemanticAttributes.CODE_FUNCTION" "findAll" } } span(1) { diff --git a/instrumentation/elasticsearch/elasticsearch-transport-6.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/elasticsearch/transport/v6_0/Elasticsearch6TransportNetAttributesGetter.java b/instrumentation/elasticsearch/elasticsearch-transport-6.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/elasticsearch/transport/v6_0/Elasticsearch6TransportNetAttributesGetter.java index 5374fe27fbab..3cdac789a3f0 100644 --- a/instrumentation/elasticsearch/elasticsearch-transport-6.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/elasticsearch/transport/v6_0/Elasticsearch6TransportNetAttributesGetter.java +++ b/instrumentation/elasticsearch/elasticsearch-transport-6.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/elasticsearch/transport/v6_0/Elasticsearch6TransportNetAttributesGetter.java @@ -19,9 +19,21 @@ public String transport(ElasticTransportRequest request, @Nullable ActionRespons return null; } + @Nullable + @Override + public String peerName(ElasticTransportRequest request) { + return null; + } + + @Nullable + @Override + public Integer peerPort(ElasticTransportRequest request) { + return null; + } + @Override @Nullable - public InetSocketAddress getAddress( + protected InetSocketAddress getPeerSocketAddress( ElasticTransportRequest request, @Nullable ActionResponse response) { if (response != null && response.remoteAddress() != null) { return response.remoteAddress().address(); diff --git a/instrumentation/elasticsearch/elasticsearch-transport-6.0/javaagent/src/test/groovy/Elasticsearch6TransportClientTest.groovy b/instrumentation/elasticsearch/elasticsearch-transport-6.0/javaagent/src/test/groovy/Elasticsearch6TransportClientTest.groovy index 26b182e3b3b1..55b46edd969c 100644 --- a/instrumentation/elasticsearch/elasticsearch-transport-6.0/javaagent/src/test/groovy/Elasticsearch6TransportClientTest.groovy +++ b/instrumentation/elasticsearch/elasticsearch-transport-6.0/javaagent/src/test/groovy/Elasticsearch6TransportClientTest.groovy @@ -105,8 +105,10 @@ class Elasticsearch6TransportClientTest extends AbstractElasticsearchTransportCl kind CLIENT childOf(span(0)) attributes { - "$SemanticAttributes.NET_PEER_IP" tcpPublishAddress.address - "$SemanticAttributes.NET_PEER_PORT" tcpPublishAddress.port + "$SemanticAttributes.NET_SOCK_FAMILY" { it == SemanticAttributes.NetSockFamilyValues.INET6 || it == null } + "$SemanticAttributes.NET_SOCK_PEER_ADDR" tcpPublishAddress.address + "$SemanticAttributes.NET_SOCK_PEER_NAME" tcpPublishAddress.address().hostString + "$SemanticAttributes.NET_SOCK_PEER_PORT" tcpPublishAddress.port "$SemanticAttributes.DB_SYSTEM" "elasticsearch" "$SemanticAttributes.DB_OPERATION" "ClusterHealthAction" "elasticsearch.action" "ClusterHealthAction" @@ -223,8 +225,10 @@ class Elasticsearch6TransportClientTest extends AbstractElasticsearchTransportCl name "CreateIndexAction" kind CLIENT attributes { - "$SemanticAttributes.NET_PEER_IP" tcpPublishAddress.address - "$SemanticAttributes.NET_PEER_PORT" tcpPublishAddress.port + "$SemanticAttributes.NET_SOCK_FAMILY" { it == SemanticAttributes.NetSockFamilyValues.INET6 || it == null } + "$SemanticAttributes.NET_SOCK_PEER_ADDR" tcpPublishAddress.address + "$SemanticAttributes.NET_SOCK_PEER_NAME" tcpPublishAddress.address().hostString + "$SemanticAttributes.NET_SOCK_PEER_PORT" tcpPublishAddress.port "$SemanticAttributes.DB_SYSTEM" "elasticsearch" "$SemanticAttributes.DB_OPERATION" "CreateIndexAction" "elasticsearch.action" "CreateIndexAction" @@ -238,8 +242,10 @@ class Elasticsearch6TransportClientTest extends AbstractElasticsearchTransportCl name "GetAction" kind CLIENT attributes { - "$SemanticAttributes.NET_PEER_IP" tcpPublishAddress.address - "$SemanticAttributes.NET_PEER_PORT" tcpPublishAddress.port + "$SemanticAttributes.NET_SOCK_FAMILY" { it == SemanticAttributes.NetSockFamilyValues.INET6 || it == null } + "$SemanticAttributes.NET_SOCK_PEER_ADDR" tcpPublishAddress.address + "$SemanticAttributes.NET_SOCK_PEER_NAME" tcpPublishAddress.address().hostString + "$SemanticAttributes.NET_SOCK_PEER_PORT" tcpPublishAddress.port "$SemanticAttributes.DB_SYSTEM" "elasticsearch" "$SemanticAttributes.DB_OPERATION" "GetAction" "elasticsearch.action" "GetAction" @@ -268,8 +274,10 @@ class Elasticsearch6TransportClientTest extends AbstractElasticsearchTransportCl name "IndexAction" kind CLIENT attributes { - "$SemanticAttributes.NET_PEER_IP" tcpPublishAddress.address - "$SemanticAttributes.NET_PEER_PORT" tcpPublishAddress.port + "$SemanticAttributes.NET_SOCK_FAMILY" { it == SemanticAttributes.NetSockFamilyValues.INET6 || it == null } + "$SemanticAttributes.NET_SOCK_PEER_ADDR" tcpPublishAddress.address + "$SemanticAttributes.NET_SOCK_PEER_NAME" tcpPublishAddress.address().hostString + "$SemanticAttributes.NET_SOCK_PEER_PORT" tcpPublishAddress.port "$SemanticAttributes.DB_SYSTEM" "elasticsearch" "$SemanticAttributes.DB_OPERATION" "IndexAction" "elasticsearch.action" "IndexAction" @@ -289,8 +297,10 @@ class Elasticsearch6TransportClientTest extends AbstractElasticsearchTransportCl name "GetAction" kind CLIENT attributes { - "$SemanticAttributes.NET_PEER_IP" tcpPublishAddress.address - "$SemanticAttributes.NET_PEER_PORT" tcpPublishAddress.port + "$SemanticAttributes.NET_SOCK_FAMILY" { it == SemanticAttributes.NetSockFamilyValues.INET6 || it == null } + "$SemanticAttributes.NET_SOCK_PEER_ADDR" tcpPublishAddress.address + "$SemanticAttributes.NET_SOCK_PEER_NAME" tcpPublishAddress.address().hostString + "$SemanticAttributes.NET_SOCK_PEER_PORT" tcpPublishAddress.port "$SemanticAttributes.DB_SYSTEM" "elasticsearch" "$SemanticAttributes.DB_OPERATION" "GetAction" "elasticsearch.action" "GetAction" diff --git a/instrumentation/elasticsearch/elasticsearch-transport-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/elasticsearch/transport/ElasticTransportNetResponseAttributesGetter.java b/instrumentation/elasticsearch/elasticsearch-transport-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/elasticsearch/transport/ElasticTransportNetResponseAttributesGetter.java index 4af4dd83c4a4..0cdbeb22d0e2 100644 --- a/instrumentation/elasticsearch/elasticsearch-transport-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/elasticsearch/transport/ElasticTransportNetResponseAttributesGetter.java +++ b/instrumentation/elasticsearch/elasticsearch-transport-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/elasticsearch/transport/ElasticTransportNetResponseAttributesGetter.java @@ -20,27 +20,39 @@ public String transport(ElasticTransportRequest request, @Nullable ActionRespons @Override @Nullable - public String peerName(ElasticTransportRequest request, @Nullable ActionResponse response) { - if (response != null && response.remoteAddress() != null) { - return response.remoteAddress().getHost(); - } + public String peerName(ElasticTransportRequest request) { + return null; + } + + @Override + @Nullable + public Integer peerPort(ElasticTransportRequest request) { return null; } @Override @Nullable - public Integer peerPort(ElasticTransportRequest request, @Nullable ActionResponse response) { + public String sockPeerAddr(ElasticTransportRequest request, @Nullable ActionResponse response) { if (response != null && response.remoteAddress() != null) { - return response.remoteAddress().getPort(); + return response.remoteAddress().getAddress(); } return null; } + @Nullable @Override + public String sockPeerName(ElasticTransportRequest request, @Nullable ActionResponse response) { + if (response != null && response.remoteAddress() != null) { + return response.remoteAddress().getHost(); + } + return null; + } + @Nullable - public String peerIp(ElasticTransportRequest request, @Nullable ActionResponse response) { + @Override + public Integer sockPeerPort(ElasticTransportRequest request, @Nullable ActionResponse response) { if (response != null && response.remoteAddress() != null) { - return response.remoteAddress().getAddress(); + return response.remoteAddress().getPort(); } return null; } diff --git a/instrumentation/elasticsearch/elasticsearch-transport-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/elasticsearch/transport/ElasticsearchTransportInstrumenterFactory.java b/instrumentation/elasticsearch/elasticsearch-transport-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/elasticsearch/transport/ElasticsearchTransportInstrumenterFactory.java index 22cfb0e3205f..c552e4d4b35a 100644 --- a/instrumentation/elasticsearch/elasticsearch-transport-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/elasticsearch/transport/ElasticsearchTransportInstrumenterFactory.java +++ b/instrumentation/elasticsearch/elasticsearch-transport-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/elasticsearch/transport/ElasticsearchTransportInstrumenterFactory.java @@ -40,7 +40,7 @@ public static Instrumenter create( instrumenterBuilder.addAttributesExtractor(experimentalAttributesExtractor); } - return instrumenterBuilder.newInstrumenter(SpanKindExtractor.alwaysClient()); + return instrumenterBuilder.buildInstrumenter(SpanKindExtractor.alwaysClient()); } private ElasticsearchTransportInstrumenterFactory() {} diff --git a/instrumentation/executors/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/executors/ExecutorInstrumentationTest.java b/instrumentation/executors/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/executors/ExecutorInstrumentationTest.java index b3147c798f90..aa05c1b6af0f 100644 --- a/instrumentation/executors/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/executors/ExecutorInstrumentationTest.java +++ b/instrumentation/executors/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/executors/ExecutorInstrumentationTest.java @@ -15,6 +15,7 @@ import java.util.concurrent.BlockingQueue; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; import java.util.concurrent.ForkJoinPool; import java.util.concurrent.ForkJoinTask; import java.util.concurrent.Future; @@ -46,6 +47,12 @@ static class ThreadPoolExecutorTest extends ExecutorInstrumentationTest { + public WorkStealingPoolTest() { + super(Executors.newWorkStealingPool(2)); + } + } + static class ScheduledThreadPoolExecutorTest extends ExecutorInstrumentationTest { ScheduledThreadPoolExecutorTest() { diff --git a/instrumentation/external-annotations/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/extannotations/ExternalAnnotationInstrumentation.java b/instrumentation/external-annotations/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/extannotations/ExternalAnnotationInstrumentation.java index ac16f0686e12..781b4e01d82a 100644 --- a/instrumentation/external-annotations/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/extannotations/ExternalAnnotationInstrumentation.java +++ b/instrumentation/external-annotations/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/extannotations/ExternalAnnotationInstrumentation.java @@ -17,7 +17,7 @@ import io.opentelemetry.context.Context; import io.opentelemetry.context.Scope; -import io.opentelemetry.instrumentation.api.util.ClassAndMethod; +import io.opentelemetry.instrumentation.api.instrumenter.util.ClassAndMethod; import io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge; import io.opentelemetry.javaagent.bootstrap.internal.InstrumentationConfig; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; diff --git a/instrumentation/external-annotations/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/extannotations/ExternalAnnotationSingletons.java b/instrumentation/external-annotations/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/extannotations/ExternalAnnotationSingletons.java index 8f7d1fd74403..eefa5d3f6dbf 100644 --- a/instrumentation/external-annotations/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/extannotations/ExternalAnnotationSingletons.java +++ b/instrumentation/external-annotations/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/extannotations/ExternalAnnotationSingletons.java @@ -10,7 +10,7 @@ import io.opentelemetry.instrumentation.api.instrumenter.code.CodeAttributesExtractor; import io.opentelemetry.instrumentation.api.instrumenter.code.CodeAttributesGetter; import io.opentelemetry.instrumentation.api.instrumenter.code.CodeSpanNameExtractor; -import io.opentelemetry.instrumentation.api.util.ClassAndMethod; +import io.opentelemetry.instrumentation.api.instrumenter.util.ClassAndMethod; public final class ExternalAnnotationSingletons { @@ -26,7 +26,7 @@ public final class ExternalAnnotationSingletons { "io.opentelemetry.external-annotations", CodeSpanNameExtractor.create(codeAttributesGetter)) .addAttributesExtractor(CodeAttributesExtractor.create(codeAttributesGetter)) - .newInstrumenter(); + .buildInstrumenter(); } public static Instrumenter instrumenter() { diff --git a/instrumentation/external-annotations/javaagent/src/test/java/OuterClass.java b/instrumentation/external-annotations/javaagent/src/test/java/OuterClass.java index 5fb43c2330f9..951d8d95c242 100644 --- a/instrumentation/external-annotations/javaagent/src/test/java/OuterClass.java +++ b/instrumentation/external-annotations/javaagent/src/test/java/OuterClass.java @@ -14,4 +14,6 @@ public class OuterClass { @Retention(RUNTIME) @Target(METHOD) public @interface InterestingMethod {} + + private OuterClass() {} } diff --git a/instrumentation/finatra-2.9/javaagent/build.gradle.kts b/instrumentation/finatra-2.9/javaagent/build.gradle.kts index 6776a05d3e21..b862bb849412 100644 --- a/instrumentation/finatra-2.9/javaagent/build.gradle.kts +++ b/instrumentation/finatra-2.9/javaagent/build.gradle.kts @@ -64,6 +64,12 @@ tasks { } } +tasks.withType().configureEach { + // required on jdk17 + jvmArgs("--add-opens=java.base/java.lang=ALL-UNNAMED") + jvmArgs("-XX:+IgnoreUnrecognizedVMOptions") +} + if (findProperty("testLatestDeps") as Boolean) { configurations { // finatra artifact name is different for regular and latest tests diff --git a/instrumentation/finatra-2.9/javaagent/src/latestDepTest/scala/io/opentelemetry/javaagent/instrumentation/finatra/FinatraServerLatestTest.scala b/instrumentation/finatra-2.9/javaagent/src/latestDepTest/scala/io/opentelemetry/javaagent/instrumentation/finatra/FinatraServerLatestTest.scala index 1d1ca0d86acf..dcc771532373 100644 --- a/instrumentation/finatra-2.9/javaagent/src/latestDepTest/scala/io/opentelemetry/javaagent/instrumentation/finatra/FinatraServerLatestTest.scala +++ b/instrumentation/finatra-2.9/javaagent/src/latestDepTest/scala/io/opentelemetry/javaagent/instrumentation/finatra/FinatraServerLatestTest.scala @@ -6,7 +6,6 @@ package io.opentelemetry.javaagent.instrumentation.finatra import com.twitter.finatra.http.HttpServer -import io.opentelemetry.api.common.Attributes import io.opentelemetry.api.trace.SpanKind import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension import io.opentelemetry.instrumentation.testing.junit.http.{ @@ -15,8 +14,10 @@ import io.opentelemetry.instrumentation.testing.junit.http.{ HttpServerTestOptions, ServerEndpoint } +import io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo import io.opentelemetry.sdk.testing.assertj.SpanDataAssert import io.opentelemetry.sdk.trace.data.StatusData +import io.opentelemetry.semconv.trace.attributes.SemanticAttributes import org.junit.jupiter.api.extension.RegisterExtension import java.util.concurrent.Executors @@ -66,7 +67,12 @@ class FinatraServerLatestTest extends AbstractHttpServerTest[HttpServer] { "FinatraController" ) .hasKind(SpanKind.INTERNAL) - .hasAttributes(Attributes.empty()) + .hasAttributesSatisfyingExactly( + equalTo( + SemanticAttributes.CODE_NAMESPACE, + "io.opentelemetry.javaagent.instrumentation.finatra.FinatraController" + ) + ) if (endpoint == ServerEndpoint.EXCEPTION) { span diff --git a/instrumentation/finatra-2.9/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/finatra/FinatraCodeAttributesGetter.java b/instrumentation/finatra-2.9/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/finatra/FinatraCodeAttributesGetter.java new file mode 100644 index 000000000000..c3367cc7e73e --- /dev/null +++ b/instrumentation/finatra-2.9/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/finatra/FinatraCodeAttributesGetter.java @@ -0,0 +1,23 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.finatra; + +import io.opentelemetry.instrumentation.api.instrumenter.code.CodeAttributesGetter; +import javax.annotation.Nullable; + +public class FinatraCodeAttributesGetter implements CodeAttributesGetter> { + @Nullable + @Override + public Class codeClass(Class request) { + return request; + } + + @Nullable + @Override + public String methodName(Class request) { + return null; + } +} diff --git a/instrumentation/finatra-2.9/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/finatra/FinatraRouteInstrumentation.java b/instrumentation/finatra-2.9/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/finatra/FinatraRouteInstrumentation.java index 6027af4cde48..f538e5772d94 100644 --- a/instrumentation/finatra-2.9/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/finatra/FinatraRouteInstrumentation.java +++ b/instrumentation/finatra-2.9/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/finatra/FinatraRouteInstrumentation.java @@ -5,12 +5,9 @@ package io.opentelemetry.javaagent.instrumentation.finatra; -import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.extendsClass; -import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed; import static io.opentelemetry.javaagent.instrumentation.finatra.FinatraSingletons.instrumenter; import static io.opentelemetry.javaagent.instrumentation.finatra.FinatraSingletons.updateServerSpanName; import static net.bytebuddy.matcher.ElementMatchers.isMethod; -import static net.bytebuddy.matcher.ElementMatchers.nameStartsWith; import static net.bytebuddy.matcher.ElementMatchers.named; import static net.bytebuddy.matcher.ElementMatchers.takesArgument; import static net.bytebuddy.matcher.ElementMatchers.takesArguments; @@ -29,15 +26,10 @@ import scala.Some; public class FinatraRouteInstrumentation implements TypeInstrumentation { - @Override - public ElementMatcher classLoaderOptimization() { - return hasClassesNamed("com.twitter.finatra.http.internal.routing.Route"); - } @Override public ElementMatcher typeMatcher() { - return nameStartsWith("com.twitter.finatra.") - .and(extendsClass(named("com.twitter.finatra.http.internal.routing.Route"))); + return named("com.twitter.finatra.http.internal.routing.Route"); } @Override diff --git a/instrumentation/finatra-2.9/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/finatra/FinatraSingletons.java b/instrumentation/finatra-2.9/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/finatra/FinatraSingletons.java index cc06f65fa9fa..b9215f21db4a 100644 --- a/instrumentation/finatra-2.9/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/finatra/FinatraSingletons.java +++ b/instrumentation/finatra-2.9/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/finatra/FinatraSingletons.java @@ -9,16 +9,25 @@ import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.context.Context; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; +import io.opentelemetry.instrumentation.api.instrumenter.code.CodeAttributesExtractor; +import io.opentelemetry.instrumentation.api.instrumenter.code.CodeSpanNameExtractor; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpRouteHolder; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpRouteSource; -import io.opentelemetry.instrumentation.api.util.ClassNames; public final class FinatraSingletons { - private static final Instrumenter, Void> INSTRUMENTER = - Instrumenter., Void>builder( - GlobalOpenTelemetry.get(), "io.opentelemetry.finatra-2.9", ClassNames::simpleName) - .newInstrumenter(); + private static final Instrumenter, Void> INSTRUMENTER; + + static { + FinatraCodeAttributesGetter codeAttributesGetter = new FinatraCodeAttributesGetter(); + INSTRUMENTER = + Instrumenter., Void>builder( + GlobalOpenTelemetry.get(), + "io.opentelemetry.finatra-2.9", + CodeSpanNameExtractor.create(codeAttributesGetter)) + .addAttributesExtractor(CodeAttributesExtractor.create(codeAttributesGetter)) + .buildInstrumenter(); + } public static Instrumenter, Void> instrumenter() { return INSTRUMENTER; diff --git a/instrumentation/finatra-2.9/javaagent/src/test/scala/io/opentelemetry/javaagent/instrumentation/finatra/FinatraServerTest.scala b/instrumentation/finatra-2.9/javaagent/src/test/scala/io/opentelemetry/javaagent/instrumentation/finatra/FinatraServerTest.scala index d81653303388..2679d67b0829 100644 --- a/instrumentation/finatra-2.9/javaagent/src/test/scala/io/opentelemetry/javaagent/instrumentation/finatra/FinatraServerTest.scala +++ b/instrumentation/finatra-2.9/javaagent/src/test/scala/io/opentelemetry/javaagent/instrumentation/finatra/FinatraServerTest.scala @@ -6,7 +6,6 @@ package io.opentelemetry.javaagent.instrumentation.finatra import com.twitter.finatra.http.HttpServer -import io.opentelemetry.api.common.Attributes import io.opentelemetry.api.trace.SpanKind import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension import io.opentelemetry.instrumentation.testing.junit.http.{ @@ -15,12 +14,13 @@ import io.opentelemetry.instrumentation.testing.junit.http.{ HttpServerTestOptions, ServerEndpoint } +import io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo import io.opentelemetry.sdk.testing.assertj.SpanDataAssert import io.opentelemetry.sdk.trace.data.StatusData - -import java.util.concurrent.Executors +import io.opentelemetry.semconv.trace.attributes.SemanticAttributes import org.junit.jupiter.api.extension.RegisterExtension +import java.util.concurrent.Executors import java.util.function.Predicate import scala.concurrent.{ExecutionContext, Future} import scala.language.postfixOps @@ -68,7 +68,12 @@ class FinatraServerTest extends AbstractHttpServerTest[HttpServer] { "FinatraController" ) .hasKind(SpanKind.INTERNAL) - .hasAttributes(Attributes.empty()) + .hasAttributesSatisfyingExactly( + equalTo( + SemanticAttributes.CODE_NAMESPACE, + "io.opentelemetry.javaagent.instrumentation.finatra.FinatraController" + ) + ) if (endpoint == ServerEndpoint.EXCEPTION) { span diff --git a/instrumentation/geode-1.4/javaagent/build.gradle.kts b/instrumentation/geode-1.4/javaagent/build.gradle.kts index 6382b0aedd6d..01b5db187e58 100644 --- a/instrumentation/geode-1.4/javaagent/build.gradle.kts +++ b/instrumentation/geode-1.4/javaagent/build.gradle.kts @@ -16,3 +16,9 @@ dependencies { compileOnly("com.google.auto.value:auto-value-annotations") annotationProcessor("com.google.auto.value:auto-value") } + +tasks { + test { + jvmArgs("-Dotel.instrumentation.common.db-statement-sanitizer.enabled=true") + } +} diff --git a/instrumentation/geode-1.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/geode/GeodeDbAttributesGetter.java b/instrumentation/geode-1.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/geode/GeodeDbAttributesGetter.java index f93ce7030305..cc0304c5d5ed 100644 --- a/instrumentation/geode-1.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/geode/GeodeDbAttributesGetter.java +++ b/instrumentation/geode-1.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/geode/GeodeDbAttributesGetter.java @@ -7,11 +7,15 @@ import io.opentelemetry.instrumentation.api.db.SqlStatementSanitizer; import io.opentelemetry.instrumentation.api.instrumenter.db.DbClientAttributesGetter; +import io.opentelemetry.javaagent.bootstrap.internal.CommonConfig; import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; import javax.annotation.Nullable; final class GeodeDbAttributesGetter implements DbClientAttributesGetter { + private static final SqlStatementSanitizer sanitizer = + SqlStatementSanitizer.create(CommonConfig.get().isStatementSanitizationEnabled()); + @Override public String system(GeodeRequest request) { return SemanticAttributes.DbSystemValues.GEODE; @@ -38,7 +42,7 @@ public String connectionString(GeodeRequest request) { @Nullable public String statement(GeodeRequest request) { // sanitized statement is cached - return SqlStatementSanitizer.sanitize(request.getQuery()).getFullStatement(); + return sanitizer.sanitize(request.getQuery()).getFullStatement(); } @Override diff --git a/instrumentation/geode-1.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/geode/GeodeSingletons.java b/instrumentation/geode-1.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/geode/GeodeSingletons.java index 40d45fa4b048..15d67bcb9740 100644 --- a/instrumentation/geode-1.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/geode/GeodeSingletons.java +++ b/instrumentation/geode-1.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/geode/GeodeSingletons.java @@ -25,7 +25,7 @@ public final class GeodeSingletons { INSTRUMENTATION_NAME, DbClientSpanNameExtractor.create(dbClientAttributesGetter)) .addAttributesExtractor(DbClientAttributesExtractor.create(dbClientAttributesGetter)) - .newInstrumenter(SpanKindExtractor.alwaysClient()); + .buildInstrumenter(SpanKindExtractor.alwaysClient()); } public static Instrumenter instrumenter() { diff --git a/instrumentation/google-http-client-1.19/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/googlehttpclient/GoogleHttpClientHttpAttributesGetter.java b/instrumentation/google-http-client-1.19/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/googlehttpclient/GoogleHttpClientHttpAttributesGetter.java index aa71bd68f16a..ac5abe259849 100644 --- a/instrumentation/google-http-client-1.19/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/googlehttpclient/GoogleHttpClientHttpAttributesGetter.java +++ b/instrumentation/google-http-client-1.19/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/googlehttpclient/GoogleHttpClientHttpAttributesGetter.java @@ -31,42 +31,17 @@ public List requestHeader(HttpRequest httpRequest, String name) { return httpRequest.getHeaders().getHeaderStringValues(name); } - @Override - @Nullable - public Long requestContentLength(HttpRequest httpRequest, @Nullable HttpResponse httpResponse) { - return null; - } - - @Override - @Nullable - public Long requestContentLengthUncompressed( - HttpRequest httpRequest, @Nullable HttpResponse httpResponse) { - return null; - } - @Override public String flavor(HttpRequest httpRequest, @Nullable HttpResponse httpResponse) { return SemanticAttributes.HttpFlavorValues.HTTP_1_1; } @Override - public Integer statusCode(HttpRequest httpRequest, HttpResponse httpResponse) { + public Integer statusCode( + HttpRequest httpRequest, HttpResponse httpResponse, @Nullable Throwable error) { return httpResponse.getStatusCode(); } - @Override - @Nullable - public Long responseContentLength(HttpRequest httpRequest, HttpResponse httpResponse) { - return null; - } - - @Override - @Nullable - public Long responseContentLengthUncompressed( - HttpRequest httpRequest, HttpResponse httpResponse) { - return null; - } - @Override public List responseHeader( HttpRequest httpRequest, HttpResponse httpResponse, String name) { diff --git a/instrumentation/google-http-client-1.19/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/googlehttpclient/GoogleHttpClientNetAttributesGetter.java b/instrumentation/google-http-client-1.19/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/googlehttpclient/GoogleHttpClientNetAttributesGetter.java index 36ab5e41b707..113685ef5f06 100644 --- a/instrumentation/google-http-client-1.19/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/googlehttpclient/GoogleHttpClientNetAttributesGetter.java +++ b/instrumentation/google-http-client-1.19/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/googlehttpclient/GoogleHttpClientNetAttributesGetter.java @@ -21,22 +21,12 @@ public String transport(HttpRequest request, @Nullable HttpResponse response) { @Override @Nullable - public String peerName(HttpRequest request, @Nullable HttpResponse response) { + public String peerName(HttpRequest request) { return request.getUrl().getHost(); } @Override - public Integer peerPort(HttpRequest request, @Nullable HttpResponse response) { - int port = request.getUrl().getPort(); - if (port != -1) { - return port; - } - return null; - } - - @Override - @Nullable - public String peerIp(HttpRequest request, @Nullable HttpResponse response) { - return null; + public Integer peerPort(HttpRequest request) { + return request.getUrl().getPort(); } } diff --git a/instrumentation/google-http-client-1.19/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/googlehttpclient/GoogleHttpClientSingletons.java b/instrumentation/google-http-client-1.19/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/googlehttpclient/GoogleHttpClientSingletons.java index 6f7dacc0ed5e..ecb50d515fd1 100644 --- a/instrumentation/google-http-client-1.19/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/googlehttpclient/GoogleHttpClientSingletons.java +++ b/instrumentation/google-http-client-1.19/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/googlehttpclient/GoogleHttpClientSingletons.java @@ -9,12 +9,13 @@ import com.google.api.client.http.HttpResponse; import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; -import io.opentelemetry.instrumentation.api.instrumenter.PeerServiceAttributesExtractor; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpClientAttributesExtractor; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpClientMetrics; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanNameExtractor; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanStatusExtractor; import io.opentelemetry.instrumentation.api.instrumenter.net.NetClientAttributesExtractor; +import io.opentelemetry.instrumentation.api.instrumenter.net.PeerServiceAttributesExtractor; +import io.opentelemetry.javaagent.bootstrap.internal.CommonConfig; public class GoogleHttpClientSingletons { private static final String INSTRUMENTATION_NAME = "io.opentelemetry.google-http-client-1.19"; @@ -33,11 +34,17 @@ public class GoogleHttpClientSingletons { INSTRUMENTATION_NAME, HttpSpanNameExtractor.create(httpAttributesGetter)) .setSpanStatusExtractor(HttpSpanStatusExtractor.create(httpAttributesGetter)) - .addAttributesExtractor(HttpClientAttributesExtractor.create(httpAttributesGetter)) + .addAttributesExtractor( + HttpClientAttributesExtractor.builder(httpAttributesGetter) + .setCapturedRequestHeaders(CommonConfig.get().getClientRequestHeaders()) + .setCapturedResponseHeaders(CommonConfig.get().getClientResponseHeaders()) + .build()) .addAttributesExtractor(NetClientAttributesExtractor.create(netAttributesGetter)) - .addAttributesExtractor(PeerServiceAttributesExtractor.create(netAttributesGetter)) + .addAttributesExtractor( + PeerServiceAttributesExtractor.create( + netAttributesGetter, CommonConfig.get().getPeerServiceMapping())) .addOperationMetrics(HttpClientMetrics.get()) - .newClientInstrumenter(HttpHeaderSetter.INSTANCE); + .buildClientInstrumenter(HttpHeaderSetter.INSTANCE); } public static Instrumenter instrumenter() { diff --git a/instrumentation/google-http-client-1.19/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/googlehttpclient/AbstractGoogleHttpClientTest.java b/instrumentation/google-http-client-1.19/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/googlehttpclient/AbstractGoogleHttpClientTest.java index db279d3de3ca..df685703a8c5 100644 --- a/instrumentation/google-http-client-1.19/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/googlehttpclient/AbstractGoogleHttpClientTest.java +++ b/instrumentation/google-http-client-1.19/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/googlehttpclient/AbstractGoogleHttpClientTest.java @@ -20,8 +20,6 @@ import io.opentelemetry.instrumentation.testing.junit.http.HttpClientTestOptions; import io.opentelemetry.sdk.trace.data.StatusData; import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; -import java.io.IOException; -import java.io.UncheckedIOException; import java.net.URI; import java.util.Collections; import java.util.Locale; @@ -43,15 +41,11 @@ void setUp() { } @Override - protected final HttpRequest buildRequest(String method, URI uri, Map headers) { + protected final HttpRequest buildRequest(String method, URI uri, Map headers) + throws Exception { GenericUrl genericUrl = new GenericUrl(uri); - HttpRequest request; - try { - request = requestFactory.buildRequest(method, genericUrl, null); - } catch (IOException e) { - throw new UncheckedIOException(e); - } + HttpRequest request = requestFactory.buildRequest(method, genericUrl, null); request.setConnectTimeout((int) connectTimeout().toMillis()); if (uri.toString().contains("/read-timeout")) { request.setReadTimeout((int) readTimeout().toMillis()); @@ -98,7 +92,7 @@ void errorTracesWhenExceptionIsNotThrown() throws Exception { .hasAttributesSatisfying( attrs -> assertThat(attrs) - .hasSize(7) + .hasSize(8) .containsEntry( SemanticAttributes.NET_TRANSPORT, SemanticAttributes.NetTransportValues.IP_TCP) @@ -109,6 +103,9 @@ void errorTracesWhenExceptionIsNotThrown() throws Exception { .containsEntry(SemanticAttributes.HTTP_URL, uri.toString()) .containsEntry(SemanticAttributes.HTTP_METHOD, "GET") .containsEntry(SemanticAttributes.HTTP_STATUS_CODE, 500) + .hasEntrySatisfying( + SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH, + length -> assertThat(length).isPositive()) .containsEntry( SemanticAttributes.HTTP_FLAVOR, SemanticAttributes.HttpFlavorValues.HTTP_1_1)), diff --git a/instrumentation/grails-3.0/javaagent/build.gradle.kts b/instrumentation/grails-3.0/javaagent/build.gradle.kts index d3a2281c8a28..a8bb9c690d9d 100644 --- a/instrumentation/grails-3.0/javaagent/build.gradle.kts +++ b/instrumentation/grails-3.0/javaagent/build.gradle.kts @@ -12,23 +12,13 @@ muzzle { // version 3.3.6 depends on org.grails:grails-datastore-core:6.1.10.BUILD-SNAPSHOT // which (also obviously) does not exist skip("3.1.15", "3.3.6") + // these versions pass if you add the grails maven repository (https://repo.grails.org/artifactory/core) + skip("3.2.0", "3.3.0", "3.3.1", "3.3.2", "3.3.3", "3.3.10", "3.3.13", "3.3.14", "3.3.15", "3.3.16", "4.0.0", "4.0.1", "4.0.5", "4.0.6", "4.0.7", "4.0.8", "4.0.9", "4.0.10", "4.0.11", "4.0.12", "4.0.13") assertInverse.set(true) } } -repositories { - mavenCentral() - maven { - setUrl("https://repo.grails.org/artifactory/core") - mavenContent { - releasesOnly() - } - } - mavenLocal() -} - -// first version where our tests work -val grailsVersion = "3.0.6" +val grailsVersion = "3.0.6" // first version that the tests pass on val springBootVersion = "1.2.5.RELEASE" dependencies { @@ -63,3 +53,17 @@ configurations.configureEach { } } } + +configurations.testRuntimeClasspath { + resolutionStrategy { + // requires old logback (and therefore also old slf4j) + force("ch.qos.logback:logback-classic:1.2.11") + force("org.slf4j:slf4j-api:1.7.36") + } +} + +tasks.withType().configureEach { + // required on jdk17 + jvmArgs("--add-opens=java.base/java.lang=ALL-UNNAMED") + jvmArgs("-XX:+IgnoreUnrecognizedVMOptions") +} diff --git a/instrumentation/grails-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/grails/GrailsSingletons.java b/instrumentation/grails-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/grails/GrailsSingletons.java index 984f4482079e..240e8d0eb223 100644 --- a/instrumentation/grails-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/grails/GrailsSingletons.java +++ b/instrumentation/grails-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/grails/GrailsSingletons.java @@ -19,7 +19,7 @@ public final class GrailsSingletons { Instrumenter.builder( GlobalOpenTelemetry.get(), INSTRUMENTATION_NAME, HandlerData::spanName) .setEnabled(ExperimentalConfig.get().controllerTelemetryEnabled()) - .newInstrumenter(); + .buildInstrumenter(); } public static Instrumenter instrumenter() { diff --git a/instrumentation/grails-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/grails/HandlerData.java b/instrumentation/grails-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/grails/HandlerData.java index 2703f5fa1ce9..ac03f8b097ab 100644 --- a/instrumentation/grails-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/grails/HandlerData.java +++ b/instrumentation/grails-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/grails/HandlerData.java @@ -5,7 +5,7 @@ package io.opentelemetry.javaagent.instrumentation.grails; -import io.opentelemetry.instrumentation.api.util.SpanNames; +import io.opentelemetry.instrumentation.api.instrumenter.util.SpanNames; public class HandlerData { diff --git a/instrumentation/graphql-java-12.0/library/README.md b/instrumentation/graphql-java-12.0/library/README.md index efe6ea404d84..9aec4e022977 100644 --- a/instrumentation/graphql-java-12.0/library/README.md +++ b/instrumentation/graphql-java-12.0/library/README.md @@ -1,4 +1,4 @@ -# Manual Instrumentation for GraphQL Java +# Library Instrumentation for GraphQL Java version 12.0 and higher Provides OpenTelemetry instrumentation for [GraphQL Java](https://www.graphql-java.com/). @@ -6,13 +6,12 @@ Provides OpenTelemetry instrumentation for [GraphQL Java](https://www.graphql-ja ### Add these dependencies to your project: -Replace `OPENTELEMETRY_VERSION` with the latest stable -[release](https://mvnrepository.com/artifact/io.opentelemetry). `Minimum version: 1.13.0` +Replace `OPENTELEMETRY_VERSION` with the [latest +release](https://search.maven.org/search?q=g:io.opentelemetry.instrumentation%20AND%20a:opentelemetry-graphql-java-12.0). For Maven, add to your `pom.xml` dependencies: ```xml - io.opentelemetry.instrumentation diff --git a/instrumentation/graphql-java-12.0/library/src/main/java/io/opentelemetry/instrumentation/graphql/GraphQLTelemetry.java b/instrumentation/graphql-java-12.0/library/src/main/java/io/opentelemetry/instrumentation/graphql/GraphQLTelemetry.java index b51a07940c76..f16f7ddd10b1 100644 --- a/instrumentation/graphql-java-12.0/library/src/main/java/io/opentelemetry/instrumentation/graphql/GraphQLTelemetry.java +++ b/instrumentation/graphql-java-12.0/library/src/main/java/io/opentelemetry/instrumentation/graphql/GraphQLTelemetry.java @@ -52,7 +52,7 @@ public static GraphQLTelemetryBuilder builder(OpenTelemetry openTelemetry) { }); builder.addAttributesExtractor(new GraphqlAttributesExtractor()); - this.instrumenter = builder.newInstrumenter(); + this.instrumenter = builder.buildInstrumenter(); this.sanitizeQuery = sanitizeQuery; } diff --git a/instrumentation/graphql-java-12.0/library/src/main/java/io/opentelemetry/instrumentation/graphql/GraphQLTelemetryBuilder.java b/instrumentation/graphql-java-12.0/library/src/main/java/io/opentelemetry/instrumentation/graphql/GraphQLTelemetryBuilder.java index f0ba043045c8..8ff98655a1ae 100644 --- a/instrumentation/graphql-java-12.0/library/src/main/java/io/opentelemetry/instrumentation/graphql/GraphQLTelemetryBuilder.java +++ b/instrumentation/graphql-java-12.0/library/src/main/java/io/opentelemetry/instrumentation/graphql/GraphQLTelemetryBuilder.java @@ -5,6 +5,7 @@ package io.opentelemetry.instrumentation.graphql; +import com.google.errorprone.annotations.CanIgnoreReturnValue; import io.opentelemetry.api.OpenTelemetry; /** A builder of {@link GraphQLTelemetry}. */ @@ -19,18 +20,8 @@ public final class GraphQLTelemetryBuilder { this.openTelemetry = openTelemetry; } - /** - * Sets whether experimental attributes should be set to spans. These attributes may be changed or - * removed in the future, so only enable this if you know you do not require attributes filled by - * this instrumentation to be stable across versions. - */ - @Deprecated - public GraphQLTelemetryBuilder setCaptureExperimentalSpanAttributes( - boolean captureExperimentalSpanAttributes) { - return this; - } - /** Sets whether sensitive information should be removed from queries. Default is {@code true}. */ + @CanIgnoreReturnValue public GraphQLTelemetryBuilder setSanitizeQuery(boolean sanitizeQuery) { this.sanitizeQuery = sanitizeQuery; return this; diff --git a/instrumentation/graphql-java-12.0/testing/src/main/java/io/opentelemetry/instrumentation/graphql/AbstractGraphqlTest.java b/instrumentation/graphql-java-12.0/testing/src/main/java/io/opentelemetry/instrumentation/graphql/AbstractGraphqlTest.java index 4ec8189b35d5..b6fd174aaa56 100644 --- a/instrumentation/graphql-java-12.0/testing/src/main/java/io/opentelemetry/instrumentation/graphql/AbstractGraphqlTest.java +++ b/instrumentation/graphql-java-12.0/testing/src/main/java/io/opentelemetry/instrumentation/graphql/AbstractGraphqlTest.java @@ -209,12 +209,8 @@ void parseError() { assertThat(attrs) .containsEntry("exception.type", "InvalidSyntax"); String message = - String.valueOf( - attrs - .asMap() - .get( - AttributeKey.stringKey( - "exception.message"))); + attrs.get( + AttributeKey.stringKey("exception.message")); assertThat(message).startsWith("Invalid Syntax"); })))); } @@ -256,12 +252,8 @@ void validationError() { .containsEntry( "exception.type", "ValidationError"); String message = - String.valueOf( - attrs - .asMap() - .get( - AttributeKey.stringKey( - "exception.message"))); + attrs.get( + AttributeKey.stringKey("exception.message")); assertThat(message) .startsWith( "Validation error of type FieldUndefined"); diff --git a/instrumentation/grizzly-2.0/javaagent/build.gradle.kts b/instrumentation/grizzly-2.0/javaagent/build.gradle.kts index 90c528bf4208..a3e12f6ad880 100644 --- a/instrumentation/grizzly-2.0/javaagent/build.gradle.kts +++ b/instrumentation/grizzly-2.0/javaagent/build.gradle.kts @@ -26,6 +26,10 @@ dependencies { tasks.withType().configureEach { jvmArgs("-Dotel.instrumentation.grizzly.enabled=true") + + // required on jdk17 + jvmArgs("--add-opens=java.base/java.lang=ALL-UNNAMED") + jvmArgs("-XX:+IgnoreUnrecognizedVMOptions") } // Requires old Guava. Can't use enforcedPlatform since predates BOM diff --git a/instrumentation/grizzly-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/grizzly/GrizzlyHttpAttributesGetter.java b/instrumentation/grizzly-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/grizzly/GrizzlyHttpAttributesGetter.java index c23d7a82bab9..2571aadc4086 100644 --- a/instrumentation/grizzly-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/grizzly/GrizzlyHttpAttributesGetter.java +++ b/instrumentation/grizzly-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/grizzly/GrizzlyHttpAttributesGetter.java @@ -6,9 +6,9 @@ package io.opentelemetry.javaagent.instrumentation.grizzly; import static java.util.Collections.emptyList; -import static java.util.Collections.singletonList; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpServerAttributesGetter; +import java.util.ArrayList; import java.util.List; import javax.annotation.Nullable; import org.glassfish.grizzly.http.HttpRequestPacket; @@ -24,47 +24,28 @@ public String method(HttpRequestPacket request) { @Override public List requestHeader(HttpRequestPacket request, String name) { - String value = request.getHeader(name); - return value == null ? emptyList() : singletonList(value); + return toHeaderList(request.getHeaders().values(name)); } - @Nullable - @Override - public Long requestContentLength( - HttpRequestPacket request, @Nullable HttpResponsePacket response) { - return null; - } - - @Nullable - @Override - public Long requestContentLengthUncompressed( - HttpRequestPacket request, @Nullable HttpResponsePacket response) { - return null; + private static List toHeaderList(Iterable values) { + if (values.iterator().hasNext()) { + List result = new ArrayList<>(); + values.forEach(result::add); + return result; + } + return emptyList(); } @Override - public Integer statusCode(HttpRequestPacket request, HttpResponsePacket response) { + public Integer statusCode( + HttpRequestPacket request, HttpResponsePacket response, @Nullable Throwable error) { return response.getStatus(); } - @Nullable - @Override - public Long responseContentLength(HttpRequestPacket request, HttpResponsePacket response) { - return null; - } - - @Nullable - @Override - public Long responseContentLengthUncompressed( - HttpRequestPacket request, HttpResponsePacket response) { - return null; - } - @Override public List responseHeader( HttpRequestPacket request, HttpResponsePacket response, String name) { - String value = response.getHeader(name); - return value == null ? emptyList() : singletonList(value); + return toHeaderList(response.getHeaders().values(name)); } @Override @@ -97,10 +78,4 @@ public String route(HttpRequestPacket request) { public String scheme(HttpRequestPacket request) { return request.isSecure() ? "https" : "http"; } - - @Nullable - @Override - public String serverName(HttpRequestPacket request) { - return null; - } } diff --git a/instrumentation/grizzly-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/grizzly/GrizzlyNetAttributesGetter.java b/instrumentation/grizzly-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/grizzly/GrizzlyNetAttributesGetter.java index 12c5014e93a8..586775ff664a 100644 --- a/instrumentation/grizzly-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/grizzly/GrizzlyNetAttributesGetter.java +++ b/instrumentation/grizzly-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/grizzly/GrizzlyNetAttributesGetter.java @@ -5,26 +5,51 @@ package io.opentelemetry.javaagent.instrumentation.grizzly; +import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.NetTransportValues.IP_TCP; +import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.NetTransportValues.IP_UDP; + import io.opentelemetry.instrumentation.api.instrumenter.net.NetServerAttributesGetter; import javax.annotation.Nullable; import org.glassfish.grizzly.http.HttpRequestPacket; +import org.glassfish.grizzly.nio.transport.TCPNIOTransport; final class GrizzlyNetAttributesGetter implements NetServerAttributesGetter { - @Nullable @Override public String transport(HttpRequestPacket request) { - return null; + return request.getConnection().getTransport() instanceof TCPNIOTransport ? IP_TCP : IP_UDP; } + @Nullable @Override - public Integer peerPort(HttpRequestPacket request) { - return request.getRemotePort(); + public String hostName(HttpRequestPacket request) { + return request.getLocalHost(); + } + + @Override + public Integer hostPort(HttpRequestPacket request) { + return request.getServerPort(); } @Nullable @Override - public String peerIp(HttpRequestPacket request) { + public String sockPeerAddr(HttpRequestPacket request) { return request.getRemoteAddress(); } + + @Override + public Integer sockPeerPort(HttpRequestPacket request) { + return request.getRemotePort(); + } + + @Nullable + @Override + public String sockHostAddr(HttpRequestPacket request) { + return request.getLocalAddress(); + } + + @Override + public Integer sockHostPort(HttpRequestPacket request) { + return request.getLocalPort(); + } } diff --git a/instrumentation/grizzly-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/grizzly/GrizzlySingletons.java b/instrumentation/grizzly-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/grizzly/GrizzlySingletons.java index bf5d6df734cd..839272f8e4db 100644 --- a/instrumentation/grizzly-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/grizzly/GrizzlySingletons.java +++ b/instrumentation/grizzly-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/grizzly/GrizzlySingletons.java @@ -12,7 +12,7 @@ import io.opentelemetry.instrumentation.api.instrumenter.http.HttpServerMetrics; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanNameExtractor; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanStatusExtractor; -import io.opentelemetry.instrumentation.api.instrumenter.net.NetServerAttributesExtractor; +import io.opentelemetry.javaagent.bootstrap.internal.CommonConfig; import io.opentelemetry.javaagent.bootstrap.servlet.AppServerBridge; import org.glassfish.grizzly.http.HttpRequestPacket; import org.glassfish.grizzly.http.HttpResponsePacket; @@ -31,8 +31,11 @@ public final class GrizzlySingletons { "io.opentelemetry.grizzly-2.0", HttpSpanNameExtractor.create(httpAttributesGetter)) .setSpanStatusExtractor(HttpSpanStatusExtractor.create(httpAttributesGetter)) - .addAttributesExtractor(HttpServerAttributesExtractor.create(httpAttributesGetter)) - .addAttributesExtractor(NetServerAttributesExtractor.create(netAttributesGetter)) + .addAttributesExtractor( + HttpServerAttributesExtractor.builder(httpAttributesGetter, netAttributesGetter) + .setCapturedRequestHeaders(CommonConfig.get().getServerRequestHeaders()) + .setCapturedResponseHeaders(CommonConfig.get().getServerResponseHeaders()) + .build()) .addOperationMetrics(HttpServerMetrics.get()) .addContextCustomizer( (context, request, attributes) -> @@ -43,7 +46,7 @@ public final class GrizzlySingletons { .addContextCustomizer( (context, httpRequestPacket, startAttributes) -> GrizzlyErrorHolder.init(context)) .addContextCustomizer(HttpRouteHolder.get()) - .newServerInstrumenter(HttpRequestHeadersGetter.INSTANCE); + .buildServerInstrumenter(HttpRequestHeadersGetter.INSTANCE); } public static Instrumenter instrumenter() { diff --git a/instrumentation/grpc-1.6/library/README.md b/instrumentation/grpc-1.6/library/README.md new file mode 100644 index 000000000000..0f4cdef3bde9 --- /dev/null +++ b/instrumentation/grpc-1.6/library/README.md @@ -0,0 +1,45 @@ +# Library Instrumentation for gRPC 1.6.0+ + +Provides OpenTelemetry instrumentation for [gRPC](https://grpc.io/). + +## Quickstart + +### 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). + +For Maven, add the following to your `pom.xml` dependencies: + +```xml + + + io.opentelemetry.instrumentation + opentelemetry-grpc-1.6 + OPENTELEMETRY_VERSION + + +``` + +For Gradle, add the following to your dependencies: + +```groovy +implementation("io.opentelemetry.instrumentation:opentelemetry-grpc-1.6:OPENTELEMETRY_VERSION") +``` + +### Usage + +The instrumentation library provides the implementation of `ClientInterceptor` and `ServerInterceptor` to provide OpenTelemetry-based spans and context propagation. + +```java +// For client-side, attach the interceptor to your channel builder. +void configureClientInterceptor(Opentelemetry opentelemetry, NettyChannelBuilder nettyChannelBuilder) { + GrpcTelemetry grpcTelemetry = GrpcTelemetry.create(opentelemetry); + nettyChannelBuilder.intercept(grpcTelemetry.newClientInterceptor()); +} + +// For server-side, attatch the interceptor to your service. +ServerServiceDefinition configureServerInterceptor(Opentelemetry opentelemetry, ServerServiceDefinition serviceDefinition) { + GrpcTelemetry grpcTelemetry = GrpcTelemetry.create(opentelemetry); + return ServiceInterceptors.intercept(serviceDefinition, grpcTelemetry.newServerInterceptor()); +} +``` diff --git a/instrumentation/grpc-1.6/library/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/GrpcRequest.java b/instrumentation/grpc-1.6/library/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/GrpcRequest.java index dd1a49936a73..cd429d22e850 100644 --- a/instrumentation/grpc-1.6/library/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/GrpcRequest.java +++ b/instrumentation/grpc-1.6/library/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/GrpcRequest.java @@ -8,6 +8,7 @@ import io.grpc.Metadata; import io.grpc.MethodDescriptor; import java.net.SocketAddress; +import java.net.URI; import javax.annotation.Nullable; public final class GrpcRequest { @@ -16,15 +17,32 @@ public final class GrpcRequest { @Nullable private volatile Metadata metadata; - @Nullable private volatile SocketAddress remoteAddress; + @Nullable private volatile String logicalHost; + private volatile int logicalPort = -1; + @Nullable private volatile SocketAddress peerSocketAddress; GrpcRequest( MethodDescriptor method, @Nullable Metadata metadata, - @Nullable SocketAddress remoteAddress) { + @Nullable SocketAddress peerSocketAddress, + @Nullable String authority) { this.method = method; this.metadata = metadata; - this.remoteAddress = remoteAddress; + this.peerSocketAddress = peerSocketAddress; + setLogicalAddress(authority); + } + + private void setLogicalAddress(@Nullable String authority) { + if (authority == null) { + return; + } + try { + URI uri = new URI(null, authority, null, null, null); + logicalHost = uri.getHost(); + logicalPort = uri.getPort(); + } catch (Throwable e) { + // do nothing + } } public MethodDescriptor getMethod() { @@ -41,11 +59,20 @@ void setMetadata(Metadata metadata) { } @Nullable - public SocketAddress getRemoteAddress() { - return remoteAddress; + public String getLogicalHost() { + return logicalHost; + } + + public int getLogicalPort() { + return logicalPort; + } + + @Nullable + public SocketAddress getPeerSocketAddress() { + return peerSocketAddress; } - void setRemoteAddress(SocketAddress remoteAddress) { - this.remoteAddress = remoteAddress; + void setPeerSocketAddress(SocketAddress peerSocketAddress) { + this.peerSocketAddress = peerSocketAddress; } } diff --git a/instrumentation/grpc-1.6/library/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/GrpcTelemetryBuilder.java b/instrumentation/grpc-1.6/library/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/GrpcTelemetryBuilder.java index 8cb0552ae40d..1ce44d1dbc44 100644 --- a/instrumentation/grpc-1.6/library/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/GrpcTelemetryBuilder.java +++ b/instrumentation/grpc-1.6/library/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/GrpcTelemetryBuilder.java @@ -5,12 +5,12 @@ package io.opentelemetry.instrumentation.grpc.v1_6; +import com.google.errorprone.annotations.CanIgnoreReturnValue; import io.grpc.Status; import io.opentelemetry.api.OpenTelemetry; import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; import io.opentelemetry.instrumentation.api.instrumenter.InstrumenterBuilder; -import io.opentelemetry.instrumentation.api.instrumenter.PeerServiceAttributesExtractor; import io.opentelemetry.instrumentation.api.instrumenter.SpanKindExtractor; import io.opentelemetry.instrumentation.api.instrumenter.SpanNameExtractor; import io.opentelemetry.instrumentation.api.instrumenter.net.NetClientAttributesExtractor; @@ -46,6 +46,8 @@ public final class GrpcTelemetryBuilder { private final List> additionalExtractors = new ArrayList<>(); + private final List> + additionalClientExtractors = new ArrayList<>(); private boolean captureExperimentalSpanAttributes; @@ -57,13 +59,27 @@ public final class GrpcTelemetryBuilder { * Adds an additional {@link AttributesExtractor} to invoke to set attributes to instrumented * items. The {@link AttributesExtractor} will be executed after all default extractors. */ + @CanIgnoreReturnValue public GrpcTelemetryBuilder addAttributeExtractor( AttributesExtractor attributesExtractor) { additionalExtractors.add(attributesExtractor); return this; } + /** + * Adds an extra client-only {@link AttributesExtractor} to invoke to set attributes to + * instrumented items. The {@link AttributesExtractor} will be executed after all default + * extractors. + */ + @CanIgnoreReturnValue + public GrpcTelemetryBuilder addClientAttributeExtractor( + AttributesExtractor attributesExtractor) { + additionalClientExtractors.add(attributesExtractor); + return this; + } + /** Sets custom client {@link SpanNameExtractor} via transform function. */ + @CanIgnoreReturnValue public GrpcTelemetryBuilder setClientSpanNameExtractor( Function, ? extends SpanNameExtractor> clientSpanNameExtractor) { @@ -72,6 +88,7 @@ public GrpcTelemetryBuilder setClientSpanNameExtractor( } /** Sets custom server {@link SpanNameExtractor} via transform function. */ + @CanIgnoreReturnValue public GrpcTelemetryBuilder setServerSpanNameExtractor( Function, ? extends SpanNameExtractor> serverSpanNameExtractor) { @@ -80,6 +97,7 @@ public GrpcTelemetryBuilder setServerSpanNameExtractor( } /** Sets the {@code peer.service} attribute for http client spans. */ + @CanIgnoreReturnValue public GrpcTelemetryBuilder setPeerService(String peerService) { this.peerService = peerService; return this; @@ -90,6 +108,7 @@ public GrpcTelemetryBuilder setPeerService(String peerService) { * removed in the future, so only enable this if you know you do not require attributes filled by * this instrumentation to be stable across versions */ + @CanIgnoreReturnValue public GrpcTelemetryBuilder setCaptureExperimentalSpanAttributes( boolean captureExperimentalSpanAttributes) { this.captureExperimentalSpanAttributes = captureExperimentalSpanAttributes; @@ -129,6 +148,7 @@ public GrpcTelemetry build() { clientInstrumenterBuilder .addAttributesExtractor(RpcClientAttributesExtractor.create(rpcAttributesGetter)) .addAttributesExtractor(NetClientAttributesExtractor.create(netClientAttributesGetter)) + .addAttributesExtractors(additionalClientExtractors) .addOperationMetrics(RpcClientMetrics.get()); serverInstrumenterBuilder .addAttributesExtractor(RpcServerAttributesExtractor.create(rpcAttributesGetter)) @@ -139,16 +159,13 @@ public GrpcTelemetry build() { if (peerService != null) { clientInstrumenterBuilder.addAttributesExtractor( AttributesExtractor.constant(SemanticAttributes.PEER_SERVICE, peerService)); - } else { - clientInstrumenterBuilder.addAttributesExtractor( - PeerServiceAttributesExtractor.create(netClientAttributesGetter)); } return new GrpcTelemetry( - serverInstrumenterBuilder.newServerInstrumenter(GrpcRequestGetter.INSTANCE), + serverInstrumenterBuilder.buildServerInstrumenter(GrpcRequestGetter.INSTANCE), // gRPC client interceptors require two phases, one to set up request and one to execute. // So we go ahead and inject manually in this instrumentation. - clientInstrumenterBuilder.newInstrumenter(SpanKindExtractor.alwaysClient()), + clientInstrumenterBuilder.buildInstrumenter(SpanKindExtractor.alwaysClient()), openTelemetry.getPropagators(), captureExperimentalSpanAttributes); } diff --git a/instrumentation/grpc-1.6/library/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/TracingClientInterceptor.java b/instrumentation/grpc-1.6/library/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/TracingClientInterceptor.java index 9244eb879822..e78b6896b3f3 100644 --- a/instrumentation/grpc-1.6/library/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/TracingClientInterceptor.java +++ b/instrumentation/grpc-1.6/library/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/TracingClientInterceptor.java @@ -11,6 +11,7 @@ import io.grpc.ClientInterceptor; import io.grpc.ForwardingClientCall; import io.grpc.ForwardingClientCallListener; +import io.grpc.Grpc; import io.grpc.Metadata; import io.grpc.MethodDescriptor; import io.grpc.Status; @@ -20,9 +21,6 @@ import io.opentelemetry.context.Scope; import io.opentelemetry.context.propagation.ContextPropagators; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; -import java.net.InetSocketAddress; -import java.net.SocketAddress; -import java.net.URI; import java.util.concurrent.atomic.AtomicLongFieldUpdater; final class TracingClientInterceptor implements ClientInterceptor { @@ -43,8 +41,12 @@ final class TracingClientInterceptor implements ClientInterceptor { @Override public ClientCall interceptCall( MethodDescriptor method, CallOptions callOptions, Channel next) { - GrpcRequest request = new GrpcRequest(method, null, null); + GrpcRequest request = new GrpcRequest(method, null, null, next.authority()); Context parentContext = Context.current(); + if (!instrumenter.shouldStart(parentContext, request)) { + return next.newCall(method, callOptions); + } + Context context = instrumenter.start(parentContext, request); ClientCall result; try (Scope ignored = context.makeCurrent()) { @@ -56,14 +58,8 @@ public ClientCall interceptCall( throw e; } } - SocketAddress address = null; - try { - URI uri = new URI(null, next.authority(), null, null, null); - address = InetSocketAddress.createUnresolved(uri.getHost(), uri.getPort()); - } catch (Throwable e) { - // do nothing - } - request.setRemoteAddress(address); + + request.setPeerSocketAddress(result.getAttributes().get(Grpc.TRANSPORT_ATTR_REMOTE_ADDR)); return new TracingClientCall<>(result, parentContext, context, request); } diff --git a/instrumentation/grpc-1.6/library/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/TracingServerInterceptor.java b/instrumentation/grpc-1.6/library/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/TracingServerInterceptor.java index eb9801dca7e3..bd4175d1f62c 100644 --- a/instrumentation/grpc-1.6/library/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/TracingServerInterceptor.java +++ b/instrumentation/grpc-1.6/library/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/TracingServerInterceptor.java @@ -45,8 +45,14 @@ public ServerCall.Listener interceptCall( new GrpcRequest( call.getMethodDescriptor(), headers, - call.getAttributes().get(Grpc.TRANSPORT_ATTR_REMOTE_ADDR)); - Context context = instrumenter.start(Context.current(), request); + call.getAttributes().get(Grpc.TRANSPORT_ATTR_REMOTE_ADDR), + call.getAuthority()); + Context parentContext = Context.current(); + if (!instrumenter.shouldStart(parentContext, request)) { + return next.startCall(call, headers); + } + + Context context = instrumenter.start(parentContext, request); try (Scope ignored = context.makeCurrent()) { return new TracingServerCall<>(call, context, request).start(headers, next); diff --git a/instrumentation/grpc-1.6/library/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/internal/GrpcNetClientAttributesGetter.java b/instrumentation/grpc-1.6/library/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/internal/GrpcNetClientAttributesGetter.java index 37b2bb6589b1..48a00c59b755 100644 --- a/instrumentation/grpc-1.6/library/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/internal/GrpcNetClientAttributesGetter.java +++ b/instrumentation/grpc-1.6/library/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/internal/GrpcNetClientAttributesGetter.java @@ -19,18 +19,30 @@ */ public final class GrpcNetClientAttributesGetter extends InetSocketAddressNetClientAttributesGetter { + + @Override + public String transport(GrpcRequest request, @Nullable Status response) { + return SemanticAttributes.NetTransportValues.IP_TCP; + } + + @Nullable + @Override + public String peerName(GrpcRequest grpcRequest) { + return grpcRequest.getLogicalHost(); + } + + @Override + public Integer peerPort(GrpcRequest grpcRequest) { + return grpcRequest.getLogicalPort(); + } + @Override @Nullable - public InetSocketAddress getAddress(GrpcRequest request, @Nullable Status response) { - SocketAddress address = request.getRemoteAddress(); + protected InetSocketAddress getPeerSocketAddress(GrpcRequest request, @Nullable Status response) { + SocketAddress address = request.getPeerSocketAddress(); if (address instanceof InetSocketAddress) { return (InetSocketAddress) address; } return null; } - - @Override - public String transport(GrpcRequest request, @Nullable Status response) { - return SemanticAttributes.NetTransportValues.IP_TCP; - } } diff --git a/instrumentation/grpc-1.6/library/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/internal/GrpcNetServerAttributesGetter.java b/instrumentation/grpc-1.6/library/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/internal/GrpcNetServerAttributesGetter.java index f234e221484a..07c5d965a48a 100644 --- a/instrumentation/grpc-1.6/library/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/internal/GrpcNetServerAttributesGetter.java +++ b/instrumentation/grpc-1.6/library/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/internal/GrpcNetServerAttributesGetter.java @@ -18,18 +18,37 @@ */ public final class GrpcNetServerAttributesGetter extends InetSocketAddressNetServerAttributesGetter { + @Override + public String transport(GrpcRequest request) { + return SemanticAttributes.NetTransportValues.IP_TCP; + } + @Nullable - public InetSocketAddress getAddress(GrpcRequest request) { - SocketAddress address = request.getRemoteAddress(); + @Override + public String hostName(GrpcRequest grpcRequest) { + return grpcRequest.getLogicalHost(); + } + + @Override + public Integer hostPort(GrpcRequest grpcRequest) { + return grpcRequest.getLogicalPort(); + } + + @Override + @Nullable + protected InetSocketAddress getPeerSocketAddress(GrpcRequest request) { + SocketAddress address = request.getPeerSocketAddress(); if (address instanceof InetSocketAddress) { return (InetSocketAddress) address; } return null; } + @Nullable @Override - public String transport(GrpcRequest request) { - return SemanticAttributes.NetTransportValues.IP_TCP; + protected InetSocketAddress getHostSocketAddress(GrpcRequest grpcRequest) { + // TODO: later version introduces TRANSPORT_ATTR_LOCAL_ADDR, might be a good idea to use it + return null; } } 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 f8a2a7e4dfc7..9061407592ac 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 @@ -195,24 +195,18 @@ public void onCompleted() { equalTo(SemanticAttributes.RPC_SYSTEM, "grpc"), equalTo(SemanticAttributes.RPC_SERVICE, "example.Greeter"), equalTo(SemanticAttributes.RPC_METHOD, "Conversation"), - equalTo(SemanticAttributes.NET_PEER_IP, "127.0.0.1"), - // net.peer.name resolves to "127.0.0.1" on windows which is same as - // net.peer.ip so then not captured - satisfies( - SemanticAttributes.NET_PEER_NAME, - val -> - val.satisfiesAnyOf( - v -> assertThat(v).isNull(), - v -> assertThat(v).isEqualTo("localhost"))), - satisfies( - SemanticAttributes.NET_PEER_PORT, - val -> assertThat(val).isNotNull()), + equalTo( + SemanticAttributes.RPC_GRPC_STATUS_CODE, + (long) Status.Code.OK.value()), equalTo( SemanticAttributes.NET_TRANSPORT, SemanticAttributes.NetTransportValues.IP_TCP), - equalTo( - SemanticAttributes.RPC_GRPC_STATUS_CODE, - (long) Status.Code.OK.value())) + equalTo(SemanticAttributes.NET_HOST_NAME, "localhost"), + equalTo(SemanticAttributes.NET_HOST_PORT, server.getPort()), + equalTo(SemanticAttributes.NET_SOCK_PEER_ADDR, "127.0.0.1"), + satisfies( + SemanticAttributes.NET_SOCK_PEER_PORT, + val -> assertThat(val).isNotNull())) .hasEventsSatisfyingExactly(events.toArray(new Consumer[0])))); testing() .waitAndAssertMetrics( @@ -229,12 +223,17 @@ public void onCompleted() { point -> point.hasAttributesSatisfying( equalTo(SemanticAttributes.NET_TRANSPORT, "ip_tcp"), + equalTo( + SemanticAttributes.NET_HOST_NAME, "localhost"), equalTo( SemanticAttributes.RPC_METHOD, "Conversation"), equalTo( SemanticAttributes.RPC_SERVICE, "example.Greeter"), - equalTo(SemanticAttributes.RPC_SYSTEM, "grpc")))))); + equalTo(SemanticAttributes.RPC_SYSTEM, "grpc"), + equalTo( + SemanticAttributes.RPC_GRPC_STATUS_CODE, + (long) Status.Code.OK.value())))))); testing() .waitAndAssertMetrics( "io.opentelemetry.grpc-1.6", @@ -260,7 +259,10 @@ public void onCompleted() { equalTo( SemanticAttributes.RPC_SERVICE, "example.Greeter"), - equalTo(SemanticAttributes.RPC_SYSTEM, "grpc")))))); + equalTo(SemanticAttributes.RPC_SYSTEM, "grpc"), + equalTo( + SemanticAttributes.RPC_GRPC_STATUS_CODE, + (long) Status.Code.OK.value())))))); } private ManagedChannel createChannel(Server server) throws Exception { 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 55c9938c4ebf..e06d70667466 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 @@ -163,24 +163,18 @@ public void sayHello( equalTo(SemanticAttributes.RPC_SYSTEM, "grpc"), equalTo(SemanticAttributes.RPC_SERVICE, "example.Greeter"), equalTo(SemanticAttributes.RPC_METHOD, "SayHello"), - equalTo(SemanticAttributes.NET_PEER_IP, "127.0.0.1"), - // net.peer.name resolves to "127.0.0.1" on windows which is same as - // net.peer.ip so then not captured - satisfies( - SemanticAttributes.NET_PEER_NAME, - val -> - val.satisfiesAnyOf( - v -> assertThat(v).isNull(), - v -> assertThat(v).isEqualTo("localhost"))), - satisfies( - SemanticAttributes.NET_PEER_PORT, - val -> assertThat(val).isNotNull()), + equalTo( + SemanticAttributes.RPC_GRPC_STATUS_CODE, + (long) Status.Code.OK.value()), equalTo( SemanticAttributes.NET_TRANSPORT, SemanticAttributes.NetTransportValues.IP_TCP), - equalTo( - SemanticAttributes.RPC_GRPC_STATUS_CODE, - (long) Status.Code.OK.value())) + equalTo(SemanticAttributes.NET_HOST_NAME, "localhost"), + equalTo(SemanticAttributes.NET_HOST_PORT, server.getPort()), + equalTo(SemanticAttributes.NET_SOCK_PEER_ADDR, "127.0.0.1"), + satisfies( + SemanticAttributes.NET_SOCK_PEER_PORT, + val -> assertThat(val).isNotNull())) .hasEventsSatisfyingExactly( event -> event @@ -220,11 +214,16 @@ public void sayHello( point -> point.hasAttributesSatisfying( equalTo(SemanticAttributes.NET_TRANSPORT, "ip_tcp"), + equalTo( + SemanticAttributes.NET_HOST_NAME, "localhost"), equalTo(SemanticAttributes.RPC_METHOD, "SayHello"), equalTo( SemanticAttributes.RPC_SERVICE, "example.Greeter"), - equalTo(SemanticAttributes.RPC_SYSTEM, "grpc")))))); + equalTo(SemanticAttributes.RPC_SYSTEM, "grpc"), + equalTo( + SemanticAttributes.RPC_GRPC_STATUS_CODE, + (long) Status.Code.OK.value())))))); testing() .waitAndAssertMetrics( "io.opentelemetry.grpc-1.6", @@ -249,7 +248,10 @@ public void sayHello( equalTo( SemanticAttributes.RPC_SERVICE, "example.Greeter"), - equalTo(SemanticAttributes.RPC_SYSTEM, "grpc")))))); + equalTo(SemanticAttributes.RPC_SYSTEM, "grpc"), + equalTo( + SemanticAttributes.RPC_GRPC_STATUS_CODE, + (long) Status.Code.OK.value())))))); } @Test @@ -349,24 +351,18 @@ public void sayHello( equalTo(SemanticAttributes.RPC_SYSTEM, "grpc"), equalTo(SemanticAttributes.RPC_SERVICE, "example.Greeter"), equalTo(SemanticAttributes.RPC_METHOD, "SayHello"), - equalTo(SemanticAttributes.NET_PEER_IP, "127.0.0.1"), - // net.peer.name resolves to "127.0.0.1" on windows which is same as - // net.peer.ip so then not captured - satisfies( - SemanticAttributes.NET_PEER_NAME, - val -> - val.satisfiesAnyOf( - v -> assertThat(v).isNull(), - v -> assertThat(v).isEqualTo("localhost"))), - satisfies( - SemanticAttributes.NET_PEER_PORT, - val -> assertThat(val).isNotNull()), + equalTo( + SemanticAttributes.RPC_GRPC_STATUS_CODE, + (long) Status.Code.OK.value()), equalTo( SemanticAttributes.NET_TRANSPORT, SemanticAttributes.NetTransportValues.IP_TCP), - equalTo( - SemanticAttributes.RPC_GRPC_STATUS_CODE, - (long) Status.Code.OK.value())) + equalTo(SemanticAttributes.NET_HOST_NAME, "localhost"), + equalTo(SemanticAttributes.NET_HOST_PORT, server.getPort()), + equalTo(SemanticAttributes.NET_SOCK_PEER_ADDR, "127.0.0.1"), + satisfies( + SemanticAttributes.NET_SOCK_PEER_PORT, + val -> assertThat(val).isNotNull())) .hasEventsSatisfyingExactly( event -> event @@ -409,11 +405,16 @@ public void sayHello( point -> point.hasAttributesSatisfying( equalTo(SemanticAttributes.NET_TRANSPORT, "ip_tcp"), + equalTo( + SemanticAttributes.NET_HOST_NAME, "localhost"), equalTo(SemanticAttributes.RPC_METHOD, "SayHello"), equalTo( SemanticAttributes.RPC_SERVICE, "example.Greeter"), - equalTo(SemanticAttributes.RPC_SYSTEM, "grpc")))))); + equalTo(SemanticAttributes.RPC_SYSTEM, "grpc"), + equalTo( + SemanticAttributes.RPC_GRPC_STATUS_CODE, + (long) Status.Code.OK.value())))))); testing() .waitAndAssertMetrics( "io.opentelemetry.grpc-1.6", @@ -438,7 +439,10 @@ public void sayHello( equalTo( SemanticAttributes.RPC_SERVICE, "example.Greeter"), - equalTo(SemanticAttributes.RPC_SYSTEM, "grpc")))))); + equalTo(SemanticAttributes.RPC_SYSTEM, "grpc"), + equalTo( + SemanticAttributes.RPC_GRPC_STATUS_CODE, + (long) Status.Code.OK.value())))))); } @Test @@ -546,24 +550,18 @@ public void onCompleted() { equalTo(SemanticAttributes.RPC_SYSTEM, "grpc"), equalTo(SemanticAttributes.RPC_SERVICE, "example.Greeter"), equalTo(SemanticAttributes.RPC_METHOD, "SayHello"), - equalTo(SemanticAttributes.NET_PEER_IP, "127.0.0.1"), - // net.peer.name resolves to "127.0.0.1" on windows which is same as - // net.peer.ip so then not captured - satisfies( - SemanticAttributes.NET_PEER_NAME, - val -> - val.satisfiesAnyOf( - v -> assertThat(v).isNull(), - v -> assertThat(v).isEqualTo("localhost"))), - satisfies( - SemanticAttributes.NET_PEER_PORT, - val -> assertThat(val).isNotNull()), + equalTo( + SemanticAttributes.RPC_GRPC_STATUS_CODE, + (long) Status.Code.OK.value()), equalTo( SemanticAttributes.NET_TRANSPORT, SemanticAttributes.NetTransportValues.IP_TCP), - equalTo( - SemanticAttributes.RPC_GRPC_STATUS_CODE, - (long) Status.Code.OK.value())) + equalTo(SemanticAttributes.NET_HOST_NAME, "localhost"), + equalTo(SemanticAttributes.NET_HOST_PORT, server.getPort()), + equalTo(SemanticAttributes.NET_SOCK_PEER_ADDR, "127.0.0.1"), + satisfies( + SemanticAttributes.NET_SOCK_PEER_PORT, + val -> assertThat(val).isNotNull())) .hasEventsSatisfyingExactly( event -> event @@ -606,11 +604,16 @@ public void onCompleted() { point -> point.hasAttributesSatisfying( equalTo(SemanticAttributes.NET_TRANSPORT, "ip_tcp"), + equalTo( + SemanticAttributes.NET_HOST_NAME, "localhost"), equalTo(SemanticAttributes.RPC_METHOD, "SayHello"), equalTo( SemanticAttributes.RPC_SERVICE, "example.Greeter"), - equalTo(SemanticAttributes.RPC_SYSTEM, "grpc")))))); + equalTo(SemanticAttributes.RPC_SYSTEM, "grpc"), + equalTo( + SemanticAttributes.RPC_GRPC_STATUS_CODE, + (long) Status.Code.OK.value())))))); testing() .waitAndAssertMetrics( "io.opentelemetry.grpc-1.6", @@ -635,7 +638,10 @@ public void onCompleted() { equalTo( SemanticAttributes.RPC_SERVICE, "example.Greeter"), - equalTo(SemanticAttributes.RPC_SYSTEM, "grpc")))))); + equalTo(SemanticAttributes.RPC_SYSTEM, "grpc"), + equalTo( + SemanticAttributes.RPC_GRPC_STATUS_CODE, + (long) Status.Code.OK.value())))))); } @ParameterizedTest @@ -707,24 +713,18 @@ public void sayHello( equalTo(SemanticAttributes.RPC_SYSTEM, "grpc"), equalTo(SemanticAttributes.RPC_SERVICE, "example.Greeter"), equalTo(SemanticAttributes.RPC_METHOD, "SayHello"), - equalTo(SemanticAttributes.NET_PEER_IP, "127.0.0.1"), - // net.peer.name resolves to "127.0.0.1" on windows which is same as - // net.peer.ip so then not captured - satisfies( - SemanticAttributes.NET_PEER_NAME, - val -> - val.satisfiesAnyOf( - v -> assertThat(v).isNull(), - v -> assertThat(v).isEqualTo("localhost"))), - satisfies( - SemanticAttributes.NET_PEER_PORT, - val -> assertThat(val).isNotNull()), + equalTo( + SemanticAttributes.RPC_GRPC_STATUS_CODE, + (long) status.getCode().value()), equalTo( SemanticAttributes.NET_TRANSPORT, SemanticAttributes.NetTransportValues.IP_TCP), - equalTo( - SemanticAttributes.RPC_GRPC_STATUS_CODE, - (long) status.getCode().value())) + equalTo(SemanticAttributes.NET_HOST_NAME, "localhost"), + equalTo(SemanticAttributes.NET_HOST_PORT, server.getPort()), + equalTo(SemanticAttributes.NET_SOCK_PEER_ADDR, "127.0.0.1"), + satisfies( + SemanticAttributes.NET_SOCK_PEER_PORT, + val -> assertThat(val).isNotNull())) .hasEventsSatisfying( events -> { assertThat(events).isNotEmpty(); @@ -760,11 +760,16 @@ public void sayHello( point -> point.hasAttributesSatisfying( equalTo(SemanticAttributes.NET_TRANSPORT, "ip_tcp"), + equalTo( + SemanticAttributes.NET_HOST_NAME, "localhost"), equalTo(SemanticAttributes.RPC_METHOD, "SayHello"), equalTo( SemanticAttributes.RPC_SERVICE, "example.Greeter"), - equalTo(SemanticAttributes.RPC_SYSTEM, "grpc")))))); + equalTo(SemanticAttributes.RPC_SYSTEM, "grpc"), + equalTo( + SemanticAttributes.RPC_GRPC_STATUS_CODE, + (long) status.getCode().value())))))); testing() .waitAndAssertMetrics( "io.opentelemetry.grpc-1.6", @@ -789,7 +794,10 @@ public void sayHello( equalTo( SemanticAttributes.RPC_SERVICE, "example.Greeter"), - equalTo(SemanticAttributes.RPC_SYSTEM, "grpc")))))); + equalTo(SemanticAttributes.RPC_SYSTEM, "grpc"), + equalTo( + SemanticAttributes.RPC_GRPC_STATUS_CODE, + (long) status.getCode().value())))))); } @ParameterizedTest @@ -867,24 +875,18 @@ public void sayHello( equalTo(SemanticAttributes.RPC_SYSTEM, "grpc"), equalTo(SemanticAttributes.RPC_SERVICE, "example.Greeter"), equalTo(SemanticAttributes.RPC_METHOD, "SayHello"), - equalTo(SemanticAttributes.NET_PEER_IP, "127.0.0.1"), - // net.peer.name resolves to "127.0.0.1" on windows which is same as - // net.peer.ip so then not captured - satisfies( - SemanticAttributes.NET_PEER_NAME, - val -> - val.satisfiesAnyOf( - v -> assertThat(v).isNull(), - v -> assertThat(v).isEqualTo("localhost"))), - satisfies( - SemanticAttributes.NET_PEER_PORT, - val -> assertThat(val).isNotNull()), + equalTo( + SemanticAttributes.RPC_GRPC_STATUS_CODE, + (long) Status.Code.UNKNOWN.value()), equalTo( SemanticAttributes.NET_TRANSPORT, SemanticAttributes.NetTransportValues.IP_TCP), - equalTo( - SemanticAttributes.RPC_GRPC_STATUS_CODE, - (long) Status.Code.UNKNOWN.value())) + equalTo(SemanticAttributes.NET_HOST_NAME, "localhost"), + equalTo(SemanticAttributes.NET_HOST_PORT, server.getPort()), + equalTo(SemanticAttributes.NET_SOCK_PEER_ADDR, "127.0.0.1"), + satisfies( + SemanticAttributes.NET_SOCK_PEER_PORT, + val -> assertThat(val).isNotNull())) .hasEventsSatisfying( events -> { assertThat(events).hasSize(2); @@ -915,11 +917,16 @@ public void sayHello( point -> point.hasAttributesSatisfying( equalTo(SemanticAttributes.NET_TRANSPORT, "ip_tcp"), + equalTo( + SemanticAttributes.NET_HOST_NAME, "localhost"), equalTo(SemanticAttributes.RPC_METHOD, "SayHello"), equalTo( SemanticAttributes.RPC_SERVICE, "example.Greeter"), - equalTo(SemanticAttributes.RPC_SYSTEM, "grpc")))))); + equalTo(SemanticAttributes.RPC_SYSTEM, "grpc"), + equalTo( + SemanticAttributes.RPC_GRPC_STATUS_CODE, + (long) Status.Code.UNKNOWN.value())))))); testing() .waitAndAssertMetrics( "io.opentelemetry.grpc-1.6", @@ -944,7 +951,10 @@ public void sayHello( equalTo( SemanticAttributes.RPC_SERVICE, "example.Greeter"), - equalTo(SemanticAttributes.RPC_SYSTEM, "grpc")))))); + equalTo(SemanticAttributes.RPC_SYSTEM, "grpc"), + equalTo( + SemanticAttributes.RPC_GRPC_STATUS_CODE, + (long) Status.Code.UNKNOWN.value())))))); } static class ErrorProvider implements ArgumentsProvider { @@ -1128,24 +1138,18 @@ public void onCompleted() { equalTo(SemanticAttributes.RPC_SYSTEM, "grpc"), equalTo(SemanticAttributes.RPC_SERVICE, "example.Greeter"), equalTo(SemanticAttributes.RPC_METHOD, "SayHello"), - equalTo(SemanticAttributes.NET_PEER_IP, "127.0.0.1"), - // net.peer.name resolves to "127.0.0.1" on windows which is same as - // net.peer.ip so then not captured - satisfies( - SemanticAttributes.NET_PEER_NAME, - val -> - val.satisfiesAnyOf( - v -> assertThat(v).isNull(), - v -> assertThat(v).isEqualTo("localhost"))), - satisfies( - SemanticAttributes.NET_PEER_PORT, - val -> assertThat(val).isNotNull()), + equalTo( + SemanticAttributes.RPC_GRPC_STATUS_CODE, + (long) Status.Code.OK.value()), equalTo( SemanticAttributes.NET_TRANSPORT, SemanticAttributes.NetTransportValues.IP_TCP), - equalTo( - SemanticAttributes.RPC_GRPC_STATUS_CODE, - (long) Status.Code.OK.value())) + equalTo(SemanticAttributes.NET_HOST_NAME, "localhost"), + equalTo(SemanticAttributes.NET_HOST_PORT, server.getPort()), + equalTo(SemanticAttributes.NET_SOCK_PEER_ADDR, "127.0.0.1"), + satisfies( + SemanticAttributes.NET_SOCK_PEER_PORT, + val -> assertThat(val).isNotNull())) .hasEventsSatisfyingExactly( event -> event @@ -1177,8 +1181,8 @@ void clientErrorThrown() throws Exception { BindableService greeter = new GreeterGrpc.GreeterImplBase() { @Override - public void sayHello( - Helloworld.Request req, StreamObserver responseObserver) { + public void sayMultipleHello( + Helloworld.Request request, StreamObserver responseObserver) { // Send a response but don't complete so client can fail itself responseObserver.onNext(Helloworld.Response.getDefaultInstance()); } @@ -1198,7 +1202,7 @@ public void sayHello( .runWithSpan( "parent", () -> - client.sayHello( + client.sayMultipleHello( Helloworld.Request.newBuilder().setName("test").build(), new StreamObserver() { @Override @@ -1229,14 +1233,14 @@ public void onCompleted() { trace.hasSpansSatisfyingExactly( span -> span.hasName("parent").hasKind(SpanKind.INTERNAL).hasNoParent(), span -> - span.hasName("example.Greeter/SayHello") + span.hasName("example.Greeter/SayMultipleHello") .hasKind(SpanKind.CLIENT) .hasParent(trace.getSpan(0)) .hasStatus(StatusData.error()) .hasAttributesSatisfyingExactly( equalTo(SemanticAttributes.RPC_SYSTEM, "grpc"), equalTo(SemanticAttributes.RPC_SERVICE, "example.Greeter"), - equalTo(SemanticAttributes.RPC_METHOD, "SayHello"), + equalTo(SemanticAttributes.RPC_METHOD, "SayMultipleHello"), equalTo( SemanticAttributes.NET_TRANSPORT, SemanticAttributes.NetTransportValues.IP_TCP), @@ -1270,31 +1274,25 @@ public void onCompleted() { span.hasException(thrown); }), span -> - span.hasName("example.Greeter/SayHello") + span.hasName("example.Greeter/SayMultipleHello") .hasKind(SpanKind.SERVER) .hasParent(trace.getSpan(1)) .hasAttributesSatisfyingExactly( equalTo(SemanticAttributes.RPC_SYSTEM, "grpc"), equalTo(SemanticAttributes.RPC_SERVICE, "example.Greeter"), - equalTo(SemanticAttributes.RPC_METHOD, "SayHello"), - equalTo(SemanticAttributes.NET_PEER_IP, "127.0.0.1"), - // net.peer.name resolves to "127.0.0.1" on windows which is same as - // net.peer.ip so then not captured - satisfies( - SemanticAttributes.NET_PEER_NAME, - val -> - val.satisfiesAnyOf( - v -> assertThat(v).isNull(), - v -> assertThat(v).isEqualTo("localhost"))), - satisfies( - SemanticAttributes.NET_PEER_PORT, - val -> assertThat(val).isNotNull()), + equalTo(SemanticAttributes.RPC_METHOD, "SayMultipleHello"), + equalTo( + SemanticAttributes.RPC_GRPC_STATUS_CODE, + (long) Status.Code.CANCELLED.value()), equalTo( SemanticAttributes.NET_TRANSPORT, SemanticAttributes.NetTransportValues.IP_TCP), - equalTo( - SemanticAttributes.RPC_GRPC_STATUS_CODE, - (long) Status.Code.CANCELLED.value())) + equalTo(SemanticAttributes.NET_HOST_NAME, "localhost"), + equalTo(SemanticAttributes.NET_HOST_PORT, server.getPort()), + equalTo(SemanticAttributes.NET_SOCK_PEER_ADDR, "127.0.0.1"), + satisfies( + SemanticAttributes.NET_SOCK_PEER_PORT, + val -> assertThat(val).isNotNull())) .hasEventsSatisfyingExactly( event -> event @@ -1426,24 +1424,18 @@ public void onCompleted() { SemanticAttributes.RPC_SERVICE, "grpc.reflection.v1alpha.ServerReflection"), equalTo(SemanticAttributes.RPC_METHOD, "ServerReflectionInfo"), - equalTo(SemanticAttributes.NET_PEER_IP, "127.0.0.1"), - // net.peer.name resolves to "127.0.0.1" on windows which is same as - // net.peer.ip so then not captured - satisfies( - SemanticAttributes.NET_PEER_NAME, - val -> - val.satisfiesAnyOf( - v -> assertThat(v).isNull(), - v -> assertThat(v).isEqualTo("localhost"))), - satisfies( - SemanticAttributes.NET_PEER_PORT, - val -> assertThat(val).isNotNull()), + equalTo( + SemanticAttributes.RPC_GRPC_STATUS_CODE, + (long) Status.Code.OK.value()), equalTo( SemanticAttributes.NET_TRANSPORT, SemanticAttributes.NetTransportValues.IP_TCP), - equalTo( - SemanticAttributes.RPC_GRPC_STATUS_CODE, - (long) Status.Code.OK.value())) + equalTo(SemanticAttributes.NET_HOST_NAME, "localhost"), + equalTo(SemanticAttributes.NET_HOST_PORT, server.getPort()), + equalTo(SemanticAttributes.NET_SOCK_PEER_ADDR, "127.0.0.1"), + satisfies( + SemanticAttributes.NET_SOCK_PEER_PORT, + val -> assertThat(val).isNotNull())) .hasEventsSatisfyingExactly( event -> event @@ -1558,24 +1550,18 @@ public void sayHello( equalTo(SemanticAttributes.RPC_SYSTEM, "grpc"), equalTo(SemanticAttributes.RPC_SERVICE, "example.Greeter"), equalTo(SemanticAttributes.RPC_METHOD, "SayHello"), - equalTo(SemanticAttributes.NET_PEER_IP, "127.0.0.1"), - // net.peer.name resolves to "127.0.0.1" on windows which is same as - // net.peer.ip so then not captured - satisfies( - SemanticAttributes.NET_PEER_NAME, - val -> - val.satisfiesAnyOf( - v -> assertThat(v).isNull(), - v -> assertThat(v).isEqualTo("localhost"))), - satisfies( - SemanticAttributes.NET_PEER_PORT, - val -> assertThat(val).isNotNull()), + equalTo( + SemanticAttributes.RPC_GRPC_STATUS_CODE, + (long) Status.Code.OK.value()), equalTo( SemanticAttributes.NET_TRANSPORT, SemanticAttributes.NetTransportValues.IP_TCP), - equalTo( - SemanticAttributes.RPC_GRPC_STATUS_CODE, - (long) Status.Code.OK.value())) + equalTo(SemanticAttributes.NET_HOST_NAME, "localhost"), + equalTo(SemanticAttributes.NET_HOST_PORT, server.getPort()), + equalTo(SemanticAttributes.NET_SOCK_PEER_ADDR, "127.0.0.1"), + satisfies( + SemanticAttributes.NET_SOCK_PEER_PORT, + val -> assertThat(val).isNotNull())) .hasEventsSatisfyingExactly( event -> event diff --git a/instrumentation/grpc-1.6/testing/src/main/proto/helloworld.proto b/instrumentation/grpc-1.6/testing/src/main/proto/helloworld.proto index 412aec01113f..e5a12aadd367 100644 --- a/instrumentation/grpc-1.6/testing/src/main/proto/helloworld.proto +++ b/instrumentation/grpc-1.6/testing/src/main/proto/helloworld.proto @@ -6,6 +6,9 @@ service Greeter { rpc SayHello (Request) returns (Response) { } + rpc SayMultipleHello (Request) returns (stream Response) { + } + rpc Conversation (stream Response) returns (stream Response) { } } diff --git a/instrumentation/guava-10.0/javaagent/build.gradle.kts b/instrumentation/guava-10.0/javaagent/build.gradle.kts index 73c5a399d1d1..e4957cd26c75 100644 --- a/instrumentation/guava-10.0/javaagent/build.gradle.kts +++ b/instrumentation/guava-10.0/javaagent/build.gradle.kts @@ -24,6 +24,9 @@ dependencies { implementation(project(":instrumentation:guava-10.0:library")) + testInstrumentation(project(":instrumentation:opentelemetry-extension-annotations-1.0:javaagent")) + testImplementation(project(":instrumentation-annotations-support-testing")) + testImplementation(project(":instrumentation-annotations")) testImplementation("io.opentelemetry:opentelemetry-extension-annotations") } diff --git a/instrumentation/guava-10.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/guava/ExtensionAnnotationsGuavaWithSpanTest.java b/instrumentation/guava-10.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/guava/ExtensionAnnotationsGuavaWithSpanTest.java index 0b6a96f680bc..447032289f78 100644 --- a/instrumentation/guava-10.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/guava/ExtensionAnnotationsGuavaWithSpanTest.java +++ b/instrumentation/guava-10.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/guava/ExtensionAnnotationsGuavaWithSpanTest.java @@ -8,9 +8,9 @@ import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.SettableFuture; -import io.opentelemetry.extension.annotations.WithSpan; import io.opentelemetry.javaagent.instrumentation.otelannotations.AbstractTraced; +@SuppressWarnings("deprecation") // testing instrumentation of deprecated class class ExtensionAnnotationsGuavaWithSpanTest extends BaseGuavaWithSpanTest { @Override @@ -22,19 +22,19 @@ static final class Traced extends AbstractTraced, ListenableFuture> { @Override - @WithSpan + @io.opentelemetry.extension.annotations.WithSpan protected SettableFuture completable() { return SettableFuture.create(); } @Override - @WithSpan + @io.opentelemetry.extension.annotations.WithSpan protected ListenableFuture alreadySucceeded() { return Futures.immediateFuture("Value"); } @Override - @WithSpan + @io.opentelemetry.extension.annotations.WithSpan protected ListenableFuture alreadyFailed() { return Futures.immediateFailedFuture(FAILURE); } diff --git a/instrumentation/guava-10.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/guava/InstrumentationAnnotationsGuavaWithSpanTest.java b/instrumentation/guava-10.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/guava/InstrumentationAnnotationsGuavaWithSpanTest.java new file mode 100644 index 000000000000..1a2a094a4ccf --- /dev/null +++ b/instrumentation/guava-10.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/guava/InstrumentationAnnotationsGuavaWithSpanTest.java @@ -0,0 +1,42 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.guava; + +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.SettableFuture; +import io.opentelemetry.instrumentation.annotations.WithSpan; +import io.opentelemetry.javaagent.instrumentation.otelannotations.AbstractTraced; + +class InstrumentationAnnotationsGuavaWithSpanTest extends BaseGuavaWithSpanTest { + + @Override + protected AbstractTraced, ListenableFuture> newTraced() { + return new Traced(); + } + + static final class Traced + extends AbstractTraced, ListenableFuture> { + + @Override + @WithSpan + protected SettableFuture completable() { + return SettableFuture.create(); + } + + @Override + @WithSpan + protected ListenableFuture alreadySucceeded() { + return Futures.immediateFuture("Value"); + } + + @Override + @WithSpan + protected ListenableFuture alreadyFailed() { + return Futures.immediateFailedFuture(FAILURE); + } + } +} diff --git a/instrumentation/guava-10.0/library/src/main/java/io/opentelemetry/instrumentation/guava/GuavaAsyncOperationEndStrategyBuilder.java b/instrumentation/guava-10.0/library/src/main/java/io/opentelemetry/instrumentation/guava/GuavaAsyncOperationEndStrategyBuilder.java index 4d76ba496418..0e26d3a8522a 100644 --- a/instrumentation/guava-10.0/library/src/main/java/io/opentelemetry/instrumentation/guava/GuavaAsyncOperationEndStrategyBuilder.java +++ b/instrumentation/guava-10.0/library/src/main/java/io/opentelemetry/instrumentation/guava/GuavaAsyncOperationEndStrategyBuilder.java @@ -5,11 +5,14 @@ package io.opentelemetry.instrumentation.guava; +import com.google.errorprone.annotations.CanIgnoreReturnValue; + public final class GuavaAsyncOperationEndStrategyBuilder { private boolean captureExperimentalSpanAttributes = false; GuavaAsyncOperationEndStrategyBuilder() {} + @CanIgnoreReturnValue public GuavaAsyncOperationEndStrategyBuilder setCaptureExperimentalSpanAttributes( boolean captureExperimentalSpanAttributes) { this.captureExperimentalSpanAttributes = captureExperimentalSpanAttributes; diff --git a/instrumentation/gwt-2.0/javaagent/build.gradle.kts b/instrumentation/gwt-2.0/javaagent/build.gradle.kts index 1b9a2dbfec4d..98c3edff1a06 100644 --- a/instrumentation/gwt-2.0/javaagent/build.gradle.kts +++ b/instrumentation/gwt-2.0/javaagent/build.gradle.kts @@ -107,3 +107,9 @@ tasks { usesService(gradle.sharedServices.registrations["testcontainersBuildService"].getService()) } } + +tasks.withType().configureEach { + // required on jdk17 + jvmArgs("--add-opens=java.base/java.lang=ALL-UNNAMED") + jvmArgs("-XX:+IgnoreUnrecognizedVMOptions") +} diff --git a/instrumentation/gwt-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/gwt/GwtSingletons.java b/instrumentation/gwt-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/gwt/GwtSingletons.java index 9cd134b226bf..5bfeb789917d 100644 --- a/instrumentation/gwt-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/gwt/GwtSingletons.java +++ b/instrumentation/gwt-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/gwt/GwtSingletons.java @@ -31,7 +31,7 @@ public final class GwtSingletons { .addAttributesExtractor(RpcServerAttributesExtractor.create(rpcAttributesGetter)) // TODO(anuraaga): This should be a server span, but we currently have no way to merge // with the HTTP instrumentation's server span. - .newInstrumenter(); + .buildInstrumenter(); } public static Instrumenter instrumenter() { diff --git a/instrumentation/hibernate/hibernate-3.3/javaagent/build.gradle.kts b/instrumentation/hibernate/hibernate-3.3/javaagent/build.gradle.kts index 6e0049a5fda1..e39a67ccebc2 100644 --- a/instrumentation/hibernate/hibernate-3.3/javaagent/build.gradle.kts +++ b/instrumentation/hibernate/hibernate-3.3/javaagent/build.gradle.kts @@ -52,4 +52,7 @@ 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") } diff --git a/instrumentation/hibernate/hibernate-4.0/javaagent/build.gradle.kts b/instrumentation/hibernate/hibernate-4.0/javaagent/build.gradle.kts index b1460249056c..bf0f66b5b4d6 100644 --- a/instrumentation/hibernate/hibernate-4.0/javaagent/build.gradle.kts +++ b/instrumentation/hibernate/hibernate-4.0/javaagent/build.gradle.kts @@ -63,9 +63,11 @@ dependencies { tasks.withType().configureEach { // required on jdk17 + jvmArgs("--add-opens=java.base/java.lang=ALL-UNNAMED") 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.db-statement-sanitizer.enabled=true") } diff --git a/instrumentation/hibernate/hibernate-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/HibernateInstrumenterFactory.java b/instrumentation/hibernate/hibernate-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/HibernateInstrumenterFactory.java index cf20a799f4a3..b9aa1c4c4d2d 100644 --- a/instrumentation/hibernate/hibernate-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/HibernateInstrumenterFactory.java +++ b/instrumentation/hibernate/hibernate-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/HibernateInstrumenterFactory.java @@ -25,7 +25,7 @@ public static Instrumenter createInstrumenter( instrumenterBuilder.addAttributesExtractor(new HibernateExperimentalAttributesExtractor()); } - return instrumenterBuilder.newInstrumenter(); + return instrumenterBuilder.buildInstrumenter(); } private HibernateInstrumenterFactory() {} diff --git a/instrumentation/hibernate/hibernate-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/OperationNameUtil.java b/instrumentation/hibernate/hibernate-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/OperationNameUtil.java index 199eeeb4aa97..480e2ce6f797 100644 --- a/instrumentation/hibernate/hibernate-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/OperationNameUtil.java +++ b/instrumentation/hibernate/hibernate-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/OperationNameUtil.java @@ -7,15 +7,19 @@ import io.opentelemetry.instrumentation.api.db.SqlStatementInfo; import io.opentelemetry.instrumentation.api.db.SqlStatementSanitizer; +import io.opentelemetry.javaagent.bootstrap.internal.CommonConfig; import java.util.function.Function; public final class OperationNameUtil { + private static final SqlStatementSanitizer sanitizer = + SqlStatementSanitizer.create(CommonConfig.get().isStatementSanitizationEnabled()); + public static String getOperationNameForQuery(String query) { // set operation to default value that is used when sql sanitizer fails to extract // operation name String operation = "Hibernate Query"; - SqlStatementInfo info = SqlStatementSanitizer.sanitize(query); + SqlStatementInfo info = sanitizer.sanitize(query); if (info.getOperation() != null) { operation = info.getOperation(); if (info.getTable() != null) { 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 e25697693a40..b59a4d1b2174 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 @@ -27,11 +27,12 @@ dependencies { testImplementation("javax.xml.bind:jaxb-api:2.3.1") testImplementation("org.glassfish.jaxb:jaxb-runtime:2.3.3") - latestDepTestLibrary("org.hibernate:hibernate-core:5.+") - latestDepTestLibrary("org.hibernate:hibernate-entitymanager:5.+") + latestDepTestLibrary("org.hibernate:hibernate-core:5.+") // documented limitation + latestDepTestLibrary("org.hibernate:hibernate-entitymanager:5.+") // documented limitation } tasks.withType().configureEach { // TODO run tests both with and without experimental span attributes jvmArgs("-Dotel.instrumentation.hibernate.experimental-span-attributes=true") + jvmArgs("-Dotel.instrumentation.common.db-statement-sanitizer.enabled=true") } diff --git a/instrumentation/hikaricp-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hikaricp/HikariPoolInstrumentation.java b/instrumentation/hikaricp-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hikaricp/HikariPoolInstrumentation.java index 703c7d0c52f8..d93e1bb59491 100644 --- a/instrumentation/hikaricp-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hikaricp/HikariPoolInstrumentation.java +++ b/instrumentation/hikaricp-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hikaricp/HikariPoolInstrumentation.java @@ -39,8 +39,16 @@ public static class SetMetricsTrackerFactoryAdvice { @Advice.OnMethodEnter(suppress = Throwable.class) public static void onEnter( - @Advice.Argument(value = 0, readOnly = false) MetricsTrackerFactory userMetricsTracker) { - + @Advice.Argument(value = 0, readOnly = false) MetricsTrackerFactory userMetricsTracker, + @Advice.FieldValue(value = "metricsTracker") AutoCloseable existingMetricsTracker) + throws Exception { + + if (existingMetricsTracker != null) { + // we call close on the existing metrics tracker in case it's our wrapper, so that our + // wrapper will unregister itself and won't keep recording metrics which leads to warnings + // about duplicate metrics + existingMetricsTracker.close(); + } userMetricsTracker = HikariSingletons.createMetricsTrackerFactory(userMetricsTracker); } } diff --git a/instrumentation/hikaricp-3.0/library/README.md b/instrumentation/hikaricp-3.0/library/README.md index 0f0fab427dde..a403308f9929 100644 --- a/instrumentation/hikaricp-3.0/library/README.md +++ b/instrumentation/hikaricp-3.0/library/README.md @@ -1,4 +1,4 @@ -# Manual Instrumentation for HikariCP +# Library Instrumentation for HikariCP version 3.0 and higher Provides OpenTelemetry instrumentation for [HikariCP](https://github.com/brettwooldridge/HikariCP). @@ -6,13 +6,12 @@ Provides OpenTelemetry instrumentation for [HikariCP](https://github.com/brettwo ### Add these dependencies to your project: -Replace `OPENTELEMETRY_VERSION` with the latest stable -[release](https://mvnrepository.com/artifact/io.opentelemetry). `Minimum version: 1.14.0` +Replace `OPENTELEMETRY_VERSION` with the [latest +release](https://search.maven.org/search?q=g:io.opentelemetry.instrumentation%20AND%20a:opentelemetry-hikaricp-3.0). For Maven, add to your `pom.xml` dependencies: ```xml - io.opentelemetry.instrumentation diff --git a/instrumentation/http-url-connection/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/httpurlconnection/HttpUrlConnectionSingletons.java b/instrumentation/http-url-connection/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/httpurlconnection/HttpUrlConnectionSingletons.java index bc22ef4f1095..0017198e1dfa 100644 --- a/instrumentation/http-url-connection/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/httpurlconnection/HttpUrlConnectionSingletons.java +++ b/instrumentation/http-url-connection/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/httpurlconnection/HttpUrlConnectionSingletons.java @@ -7,12 +7,13 @@ import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; -import io.opentelemetry.instrumentation.api.instrumenter.PeerServiceAttributesExtractor; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpClientAttributesExtractor; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpClientMetrics; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanNameExtractor; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanStatusExtractor; import io.opentelemetry.instrumentation.api.instrumenter.net.NetClientAttributesExtractor; +import io.opentelemetry.instrumentation.api.instrumenter.net.PeerServiceAttributesExtractor; +import io.opentelemetry.javaagent.bootstrap.internal.CommonConfig; import java.net.HttpURLConnection; public final class HttpUrlConnectionSingletons { @@ -29,15 +30,21 @@ public final class HttpUrlConnectionSingletons { "io.opentelemetry.http-url-connection", HttpSpanNameExtractor.create(httpAttributesGetter)) .setSpanStatusExtractor(HttpSpanStatusExtractor.create(httpAttributesGetter)) - .addAttributesExtractor(HttpClientAttributesExtractor.create(httpAttributesGetter)) + .addAttributesExtractor( + HttpClientAttributesExtractor.builder(httpAttributesGetter) + .setCapturedRequestHeaders(CommonConfig.get().getClientRequestHeaders()) + .setCapturedResponseHeaders(CommonConfig.get().getClientResponseHeaders()) + .build()) .addAttributesExtractor(NetClientAttributesExtractor.create(netAttributesGetter)) - .addAttributesExtractor(PeerServiceAttributesExtractor.create(netAttributesGetter)) + .addAttributesExtractor( + PeerServiceAttributesExtractor.create( + netAttributesGetter, CommonConfig.get().getPeerServiceMapping())) .addAttributesExtractor(HttpMethodAttributeExtractor.create()) .addContextCustomizer( (context, httpRequestPacket, startAttributes) -> GetOutputStreamContext.init(context)) .addOperationMetrics(HttpClientMetrics.get()) - .newClientInstrumenter(RequestPropertySetter.INSTANCE); + .buildClientInstrumenter(RequestPropertySetter.INSTANCE); } public static Instrumenter instrumenter() { diff --git a/instrumentation/http-url-connection/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/httpurlconnection/HttpUrlHttpAttributesGetter.java b/instrumentation/http-url-connection/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/httpurlconnection/HttpUrlHttpAttributesGetter.java index c7a43899e470..56499b72f292 100644 --- a/instrumentation/http-url-connection/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/httpurlconnection/HttpUrlHttpAttributesGetter.java +++ b/instrumentation/http-url-connection/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/httpurlconnection/HttpUrlHttpAttributesGetter.java @@ -33,41 +33,17 @@ public List requestHeader(HttpURLConnection connection, String name) { return value == null ? emptyList() : singletonList(value); } - @Override - @Nullable - public Long requestContentLength(HttpURLConnection connection, @Nullable Integer statusCode) { - return null; - } - - @Override - @Nullable - public Long requestContentLengthUncompressed( - HttpURLConnection connection, @Nullable Integer response) { - return null; - } - @Override public String flavor(HttpURLConnection connection, @Nullable Integer statusCode) { return SemanticAttributes.HttpFlavorValues.HTTP_1_1; } @Override - public Integer statusCode(HttpURLConnection connection, Integer statusCode) { + public Integer statusCode( + HttpURLConnection connection, Integer statusCode, @Nullable Throwable error) { return statusCode; } - @Override - @Nullable - public Long responseContentLength(HttpURLConnection connection, Integer statusCode) { - return null; - } - - @Override - @Nullable - public Long responseContentLengthUncompressed(HttpURLConnection connection, Integer statusCode) { - return null; - } - @Override public List responseHeader( HttpURLConnection connection, Integer statusCode, String name) { diff --git a/instrumentation/http-url-connection/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/httpurlconnection/HttpUrlNetAttributesGetter.java b/instrumentation/http-url-connection/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/httpurlconnection/HttpUrlNetAttributesGetter.java index 1c288e627df8..aec4624a8f5f 100644 --- a/instrumentation/http-url-connection/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/httpurlconnection/HttpUrlNetAttributesGetter.java +++ b/instrumentation/http-url-connection/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/httpurlconnection/HttpUrlNetAttributesGetter.java @@ -19,18 +19,12 @@ public String transport(HttpURLConnection connection, @Nullable Integer status) } @Override - public String peerName(HttpURLConnection connection, @Nullable Integer status) { + public String peerName(HttpURLConnection connection) { return connection.getURL().getHost(); } @Override - public Integer peerPort(HttpURLConnection connection, @Nullable Integer status) { + public Integer peerPort(HttpURLConnection connection) { return connection.getURL().getPort(); } - - @Override - @Nullable - public String peerIp(HttpURLConnection connection, @Nullable Integer status) { - return null; - } } diff --git a/instrumentation/http-url-connection/javaagent/src/test/groovy/HttpUrlConnectionTest.groovy b/instrumentation/http-url-connection/javaagent/src/test/groovy/HttpUrlConnectionTest.groovy index 9662a09ee475..0b518a08d61c 100644 --- a/instrumentation/http-url-connection/javaagent/src/test/groovy/HttpUrlConnectionTest.groovy +++ b/instrumentation/http-url-connection/javaagent/src/test/groovy/HttpUrlConnectionTest.groovy @@ -119,6 +119,7 @@ class HttpUrlConnectionTest extends HttpClientTest implements "$SemanticAttributes.HTTP_METHOD" "GET" "$SemanticAttributes.HTTP_STATUS_CODE" STATUS "$SemanticAttributes.HTTP_FLAVOR" "1.1" + "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" Long } } span(2) { @@ -140,6 +141,7 @@ class HttpUrlConnectionTest extends HttpClientTest implements "$SemanticAttributes.HTTP_METHOD" "GET" "$SemanticAttributes.HTTP_STATUS_CODE" STATUS "$SemanticAttributes.HTTP_FLAVOR" "1.1" + "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" Long } } span(4) { @@ -156,7 +158,7 @@ class HttpUrlConnectionTest extends HttpClientTest implements useCaches << [false, true] } - def "test broken API usage"() { + def "test broken API usage (#iteration)"() { setup: def url = resolveAddress("/success").toURL() HttpURLConnection connection = runWithSpan("someTrace") { @@ -188,6 +190,7 @@ class HttpUrlConnectionTest extends HttpClientTest implements "$SemanticAttributes.HTTP_METHOD" "GET" "$SemanticAttributes.HTTP_STATUS_CODE" STATUS "$SemanticAttributes.HTTP_FLAVOR" "1.1" + "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" Long } } serverSpan(it, 2, span(1)) @@ -246,6 +249,8 @@ class HttpUrlConnectionTest extends HttpClientTest implements "$SemanticAttributes.HTTP_METHOD" "POST" "$SemanticAttributes.HTTP_STATUS_CODE" STATUS "$SemanticAttributes.HTTP_FLAVOR" "1.1" + "$SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH" Long + "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" Long } } span(2) { @@ -310,6 +315,8 @@ class HttpUrlConnectionTest extends HttpClientTest implements "$SemanticAttributes.HTTP_METHOD" "POST" "$SemanticAttributes.HTTP_STATUS_CODE" STATUS "$SemanticAttributes.HTTP_FLAVOR" "1.1" + "$SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH" Long + "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" Long } } span(2) { diff --git a/instrumentation/hystrix-1.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hystrix/HystrixSingletons.java b/instrumentation/hystrix-1.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hystrix/HystrixSingletons.java index 90346799185f..19848e54d7cc 100644 --- a/instrumentation/hystrix-1.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hystrix/HystrixSingletons.java +++ b/instrumentation/hystrix-1.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hystrix/HystrixSingletons.java @@ -26,7 +26,7 @@ public final class HystrixSingletons { builder.addAttributesExtractor(new ExperimentalAttributesExtractor()); } - INSTRUMENTER = builder.newInstrumenter(); + INSTRUMENTER = builder.buildInstrumenter(); } public static Instrumenter instrumenter() { diff --git a/instrumentation/internal/internal-class-loader/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/internal/classloader/BootDelegationInstrumentation.java b/instrumentation/internal/internal-class-loader/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/internal/classloader/BootDelegationInstrumentation.java index b6e473b7c3ee..74ef07ba249c 100644 --- a/instrumentation/internal/internal-class-loader/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/internal/classloader/BootDelegationInstrumentation.java +++ b/instrumentation/internal/internal-class-loader/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/internal/classloader/BootDelegationInstrumentation.java @@ -72,6 +72,7 @@ public void transform(TypeTransformer transformer) { } public static class Holder { + public static final List bootstrapPackagesPrefixes = findBootstrapPackagePrefixes(); /** @@ -95,6 +96,8 @@ private static List findBootstrapPackagePrefixes() { return Constants.BOOTSTRAP_PACKAGE_PREFIXES; } } + + private Holder() {} } @SuppressWarnings("unused") diff --git a/instrumentation/internal/internal-class-loader/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/internal/classloader/ClassLoaderIgnoredTypesConfigurer.java b/instrumentation/internal/internal-class-loader/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/internal/classloader/ClassLoaderIgnoredTypesConfigurer.java index 6e045aa90c33..727423673347 100644 --- a/instrumentation/internal/internal-class-loader/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/internal/classloader/ClassLoaderIgnoredTypesConfigurer.java +++ b/instrumentation/internal/internal-class-loader/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/internal/classloader/ClassLoaderIgnoredTypesConfigurer.java @@ -14,7 +14,7 @@ public class ClassLoaderIgnoredTypesConfigurer implements IgnoredTypesConfigurer { @Override - public void configure(ConfigProperties config, IgnoredTypesBuilder builder) { + public void configure(IgnoredTypesBuilder builder, ConfigProperties config) { builder.allowClass("jdk.internal.loader.BuiltinClassLoader"); builder.allowClass("sun.misc.Launcher$AppClassLoader"); } diff --git a/instrumentation/internal/internal-class-loader/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/internal/classloader/ClassLoaderInstrumentationModule.java b/instrumentation/internal/internal-class-loader/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/internal/classloader/ClassLoaderInstrumentationModule.java index 014511692931..9fff9300895e 100644 --- a/instrumentation/internal/internal-class-loader/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/internal/classloader/ClassLoaderInstrumentationModule.java +++ b/instrumentation/internal/internal-class-loader/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/internal/classloader/ClassLoaderInstrumentationModule.java @@ -10,6 +10,7 @@ import com.google.auto.service.AutoService; import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; +import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import java.util.List; @AutoService(InstrumentationModule.class) @@ -19,7 +20,7 @@ public ClassLoaderInstrumentationModule() { } @Override - public boolean defaultEnabled() { + public boolean defaultEnabled(ConfigProperties config) { // internal instrumentations are always enabled by default return true; } diff --git a/instrumentation/internal/internal-class-loader/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/internal/classloader/DefineClassInstrumentation.java b/instrumentation/internal/internal-class-loader/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/internal/classloader/DefineClassInstrumentation.java index ff38e05a417f..1e3b63acf1a8 100644 --- a/instrumentation/internal/internal-class-loader/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/internal/classloader/DefineClassInstrumentation.java +++ b/instrumentation/internal/internal-class-loader/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/internal/classloader/DefineClassInstrumentation.java @@ -36,7 +36,7 @@ public void transform(TypeTransformer transformer) { transformer.applyAdviceToMethod( named("defineClass") .and(takesArguments(String.class, ByteBuffer.class, ProtectionDomain.class)), - DefineClassInstrumentation.class.getName() + "$DefineClassAdvice2"); + DefineClassInstrumentation.class.getName() + "$DefineClassWithThreeArgsAdvice"); } @SuppressWarnings("unused") @@ -59,7 +59,7 @@ public static void onExit(@Advice.Enter DefineClassContext context) { } @SuppressWarnings("unused") - public static class DefineClassAdvice2 { + public static class DefineClassWithThreeArgsAdvice { @Advice.OnMethodEnter(suppress = Throwable.class) public static DefineClassContext onEnter( @Advice.This ClassLoader classLoader, diff --git a/instrumentation/internal/internal-eclipse-osgi-3.6/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/internal/osgi/EclipseOsgiInstrumentationModule.java b/instrumentation/internal/internal-eclipse-osgi-3.6/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/internal/osgi/EclipseOsgiInstrumentationModule.java index f5d7e8cd2291..e82b7bcade9e 100644 --- a/instrumentation/internal/internal-eclipse-osgi-3.6/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/internal/osgi/EclipseOsgiInstrumentationModule.java +++ b/instrumentation/internal/internal-eclipse-osgi-3.6/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/internal/osgi/EclipseOsgiInstrumentationModule.java @@ -10,6 +10,7 @@ import com.google.auto.service.AutoService; import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; +import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import java.util.List; @AutoService(InstrumentationModule.class) @@ -19,7 +20,7 @@ public EclipseOsgiInstrumentationModule() { } @Override - public boolean defaultEnabled() { + public boolean defaultEnabled(ConfigProperties config) { // internal instrumentations are always enabled by default return true; } diff --git a/instrumentation/internal/internal-lambda/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/internal/lambda/InnerClassLambdaMetafactoryInstrumentation.java b/instrumentation/internal/internal-lambda/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/internal/lambda/InnerClassLambdaMetafactoryInstrumentation.java index f864f68f21a8..145d7b8b9fa8 100644 --- a/instrumentation/internal/internal-lambda/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/internal/lambda/InnerClassLambdaMetafactoryInstrumentation.java +++ b/instrumentation/internal/internal-lambda/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/internal/lambda/InnerClassLambdaMetafactoryInstrumentation.java @@ -33,7 +33,7 @@ public ElementMatcher typeMatcher() { @Override public void transform(TypeTransformer transformer) { transformer.applyTransformer( - (builder, typeDescription, classLoader, module) -> + (builder, typeDescription, classLoader, javaModule, protectionDomain) -> builder.visit( new AsmVisitorWrapper() { @Override diff --git a/instrumentation/internal/internal-lambda/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/internal/lambda/LambdaInstrumentationModule.java b/instrumentation/internal/internal-lambda/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/internal/lambda/LambdaInstrumentationModule.java index ce11b5bf7940..edb209473b4d 100644 --- a/instrumentation/internal/internal-lambda/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/internal/lambda/LambdaInstrumentationModule.java +++ b/instrumentation/internal/internal-lambda/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/internal/lambda/LambdaInstrumentationModule.java @@ -10,6 +10,7 @@ import com.google.auto.service.AutoService; import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; +import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import java.util.ArrayList; import java.util.List; import net.bytebuddy.utility.JavaModule; @@ -21,7 +22,7 @@ public LambdaInstrumentationModule() { } @Override - public boolean defaultEnabled() { + public boolean defaultEnabled(ConfigProperties config) { // internal instrumentations are always enabled by default return true; } diff --git a/instrumentation/internal/internal-lambda/javaagent/src/test/java/TestLambda.java b/instrumentation/internal/internal-lambda/javaagent/src/test/java/TestLambda.java index 1039e12a1e83..8ea194b6a12f 100644 --- a/instrumentation/internal/internal-lambda/javaagent/src/test/java/TestLambda.java +++ b/instrumentation/internal/internal-lambda/javaagent/src/test/java/TestLambda.java @@ -4,7 +4,10 @@ */ public class TestLambda { + static Runnable makeRunnable() { return () -> {}; } + + private TestLambda() {} } diff --git a/instrumentation/internal/internal-reflection/javaagent-integration-tests/src/main/java/instrumentation/TestTypeInstrumentation.java b/instrumentation/internal/internal-reflection/javaagent-integration-tests/src/main/java/instrumentation/TestTypeInstrumentation.java index 8549d87e92b4..e8c05c8c1b1d 100644 --- a/instrumentation/internal/internal-reflection/javaagent-integration-tests/src/main/java/instrumentation/TestTypeInstrumentation.java +++ b/instrumentation/internal/internal-reflection/javaagent-integration-tests/src/main/java/instrumentation/TestTypeInstrumentation.java @@ -26,7 +26,7 @@ public void transform(TypeTransformer transformer) { transformer.applyAdviceToMethod( named("testMethod"), TestTypeInstrumentation.class.getName() + "$TestAdvice"); transformer.applyAdviceToMethod( - named("testMethod2"), TestTypeInstrumentation.class.getName() + "$TestAdvice2"); + named("testMethod2"), TestTypeInstrumentation.class.getName() + "$Test2Advice"); } @SuppressWarnings("unused") @@ -41,7 +41,7 @@ public static void methodExit( } @SuppressWarnings("unused") - public static class TestAdvice2 { + public static class Test2Advice { @Advice.OnMethodExit public static void methodExit( diff --git a/instrumentation/internal/internal-reflection/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/internal/reflection/ClassInstrumentation.java b/instrumentation/internal/internal-reflection/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/internal/reflection/ClassInstrumentation.java index 60870f214e02..68908317ccf7 100644 --- a/instrumentation/internal/internal-reflection/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/internal/reflection/ClassInstrumentation.java +++ b/instrumentation/internal/internal-reflection/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/internal/reflection/ClassInstrumentation.java @@ -32,7 +32,7 @@ public ElementMatcher typeMatcher() { @Override public void transform(TypeTransformer transformer) { transformer.applyTransformer( - (builder, typeDescription, classLoader, module) -> + (builder, typeDescription, classLoader, javaModule, protectionDomain) -> builder.visit( new AsmVisitorWrapper() { @Override diff --git a/instrumentation/internal/internal-reflection/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/internal/reflection/ReflectionIgnoredTypesConfigurer.java b/instrumentation/internal/internal-reflection/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/internal/reflection/ReflectionIgnoredTypesConfigurer.java index b7668d270277..426d0a618ed1 100644 --- a/instrumentation/internal/internal-reflection/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/internal/reflection/ReflectionIgnoredTypesConfigurer.java +++ b/instrumentation/internal/internal-reflection/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/internal/reflection/ReflectionIgnoredTypesConfigurer.java @@ -14,7 +14,7 @@ public class ReflectionIgnoredTypesConfigurer implements IgnoredTypesConfigurer { @Override - public void configure(ConfigProperties config, IgnoredTypesBuilder builder) { + public void configure(IgnoredTypesBuilder builder, ConfigProperties config) { builder.allowClass("jdk.internal.reflect.Reflection"); builder.allowClass("sun.reflect.Reflection"); builder.allowClass("java.lang.Class"); diff --git a/instrumentation/internal/internal-reflection/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/internal/reflection/ReflectionInstrumentationModule.java b/instrumentation/internal/internal-reflection/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/internal/reflection/ReflectionInstrumentationModule.java index 9b5690181d8e..9f6fcd4dec9e 100644 --- a/instrumentation/internal/internal-reflection/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/internal/reflection/ReflectionInstrumentationModule.java +++ b/instrumentation/internal/internal-reflection/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/internal/reflection/ReflectionInstrumentationModule.java @@ -10,6 +10,7 @@ import com.google.auto.service.AutoService; import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; +import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import java.util.List; @AutoService(InstrumentationModule.class) @@ -19,7 +20,7 @@ public ReflectionInstrumentationModule() { } @Override - public boolean defaultEnabled() { + public boolean defaultEnabled(ConfigProperties config) { // internal instrumentations are always enabled by default return true; } diff --git a/instrumentation/internal/internal-url-class-loader/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/internal/urlclassloader/UrlClassLoaderInstrumentation.java b/instrumentation/internal/internal-url-class-loader/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/internal/urlclassloader/UrlClassLoaderInstrumentation.java index 4f5f84e7c45f..40d59d4ae778 100644 --- a/instrumentation/internal/internal-url-class-loader/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/internal/urlclassloader/UrlClassLoaderInstrumentation.java +++ b/instrumentation/internal/internal-url-class-loader/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/internal/urlclassloader/UrlClassLoaderInstrumentation.java @@ -38,11 +38,11 @@ public void transform(TypeTransformer transformer) { .and(takesArgument(0, URL.class)) .and(isProtected()) .and(not(isStatic())), - UrlClassLoaderInstrumentation.class.getName() + "$InvalidateClassLoaderMatcher"); + UrlClassLoaderInstrumentation.class.getName() + "$AddUrlAdvice"); } @SuppressWarnings("unused") - public static class InvalidateClassLoaderMatcher { + public static class AddUrlAdvice { @Advice.OnMethodExit(suppress = Throwable.class) public static void onExit(@Advice.This URLClassLoader loader) { ClassLoaderMatcherCacheHolder.invalidateAllCachesForClassLoader(loader); diff --git a/instrumentation/internal/internal-url-class-loader/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/internal/urlclassloader/UrlClassLoaderInstrumentationModule.java b/instrumentation/internal/internal-url-class-loader/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/internal/urlclassloader/UrlClassLoaderInstrumentationModule.java index 03f92be72deb..a4cd602e602b 100644 --- a/instrumentation/internal/internal-url-class-loader/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/internal/urlclassloader/UrlClassLoaderInstrumentationModule.java +++ b/instrumentation/internal/internal-url-class-loader/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/internal/urlclassloader/UrlClassLoaderInstrumentationModule.java @@ -10,6 +10,7 @@ import com.google.auto.service.AutoService; import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; +import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import java.util.List; @AutoService(InstrumentationModule.class) @@ -19,7 +20,7 @@ public UrlClassLoaderInstrumentationModule() { } @Override - public boolean defaultEnabled() { + public boolean defaultEnabled(ConfigProperties config) { // internal instrumentations are always enabled by default return true; } diff --git a/instrumentation/java-http-client/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/httpclient/JdkHttpAttributesGetter.java b/instrumentation/java-http-client/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/httpclient/JdkHttpAttributesGetter.java index fbc3b44ce5e7..86c0cc3db3e5 100644 --- a/instrumentation/java-http-client/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/httpclient/JdkHttpAttributesGetter.java +++ b/instrumentation/java-http-client/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/httpclient/JdkHttpAttributesGetter.java @@ -31,21 +31,8 @@ public List requestHeader(HttpRequest httpRequest, String name) { } @Override - @Nullable - public Long requestContentLength( - HttpRequest httpRequest, @Nullable HttpResponse httpResponse) { - return null; - } - - @Override - @Nullable - public Long requestContentLengthUncompressed( - HttpRequest httpRequest, @Nullable HttpResponse httpResponse) { - return null; - } - - @Override - public Integer statusCode(HttpRequest httpRequest, HttpResponse httpResponse) { + public Integer statusCode( + HttpRequest httpRequest, HttpResponse httpResponse, @Nullable Throwable error) { return httpResponse.statusCode(); } @@ -57,19 +44,6 @@ public String flavor(HttpRequest httpRequest, @Nullable HttpResponse httpResp return SemanticAttributes.HttpFlavorValues.HTTP_1_1; } - @Override - @Nullable - public Long responseContentLength(HttpRequest httpRequest, HttpResponse httpResponse) { - return null; - } - - @Override - @Nullable - public Long responseContentLengthUncompressed( - HttpRequest httpRequest, HttpResponse httpResponse) { - return null; - } - @Override public List responseHeader( HttpRequest httpRequest, HttpResponse httpResponse, String name) { diff --git a/instrumentation/java-http-client/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/httpclient/JdkHttpClientSingletons.java b/instrumentation/java-http-client/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/httpclient/JdkHttpClientSingletons.java index 0713f38b352e..86596d213a39 100644 --- a/instrumentation/java-http-client/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/httpclient/JdkHttpClientSingletons.java +++ b/instrumentation/java-http-client/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/httpclient/JdkHttpClientSingletons.java @@ -7,12 +7,13 @@ import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; -import io.opentelemetry.instrumentation.api.instrumenter.PeerServiceAttributesExtractor; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpClientAttributesExtractor; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpClientMetrics; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanNameExtractor; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanStatusExtractor; import io.opentelemetry.instrumentation.api.instrumenter.net.NetClientAttributesExtractor; +import io.opentelemetry.instrumentation.api.instrumenter.net.PeerServiceAttributesExtractor; +import io.opentelemetry.javaagent.bootstrap.internal.CommonConfig; import java.net.http.HttpRequest; import java.net.http.HttpResponse; @@ -33,11 +34,17 @@ public class JdkHttpClientSingletons { "io.opentelemetry.java-http-client", HttpSpanNameExtractor.create(httpAttributesGetter)) .setSpanStatusExtractor(HttpSpanStatusExtractor.create(httpAttributesGetter)) - .addAttributesExtractor(HttpClientAttributesExtractor.create(httpAttributesGetter)) + .addAttributesExtractor( + HttpClientAttributesExtractor.builder(httpAttributesGetter) + .setCapturedRequestHeaders(CommonConfig.get().getClientRequestHeaders()) + .setCapturedResponseHeaders(CommonConfig.get().getClientResponseHeaders()) + .build()) .addAttributesExtractor(NetClientAttributesExtractor.create(netAttributesGetter)) - .addAttributesExtractor(PeerServiceAttributesExtractor.create(netAttributesGetter)) + .addAttributesExtractor( + PeerServiceAttributesExtractor.create( + netAttributesGetter, CommonConfig.get().getPeerServiceMapping())) .addOperationMetrics(HttpClientMetrics.get()) - .newClientInstrumenter(SETTER); + .buildClientInstrumenter(SETTER); } public static Instrumenter> instrumenter() { diff --git a/instrumentation/java-http-client/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/httpclient/JdkHttpNetAttributesGetter.java b/instrumentation/java-http-client/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/httpclient/JdkHttpNetAttributesGetter.java index 854fff392200..5bfa0715b236 100644 --- a/instrumentation/java-http-client/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/httpclient/JdkHttpNetAttributesGetter.java +++ b/instrumentation/java-http-client/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/httpclient/JdkHttpNetAttributesGetter.java @@ -26,13 +26,13 @@ public String transport(HttpRequest httpRequest, @Nullable HttpResponse respo @Override @Nullable - public String peerName(HttpRequest httpRequest, @Nullable HttpResponse response) { + public String peerName(HttpRequest httpRequest) { return httpRequest.uri().getHost(); } @Override @Nullable - public Integer peerPort(HttpRequest httpRequest, @Nullable HttpResponse response) { + public Integer peerPort(HttpRequest httpRequest) { int port = httpRequest.uri().getPort(); if (port != -1) { return port; @@ -51,10 +51,4 @@ public Integer peerPort(HttpRequest httpRequest, @Nullable HttpResponse respo return null; } } - - @Override - @Nullable - public String peerIp(HttpRequest httpRequest, @Nullable HttpResponse response) { - return null; - } } diff --git a/instrumentation/java-http-client/javaagent/src/test/groovy/JdkHttpClientTest.groovy b/instrumentation/java-http-client/javaagent/src/test/groovy/JdkHttpClientTest.groovy deleted file mode 100644 index 287ce8ec7505..000000000000 --- a/instrumentation/java-http-client/javaagent/src/test/groovy/JdkHttpClientTest.groovy +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -import io.opentelemetry.instrumentation.test.AgentTestTrait -import io.opentelemetry.instrumentation.test.base.HttpClientTest -import io.opentelemetry.instrumentation.testing.junit.http.AbstractHttpClientTest -import spock.lang.Shared - -import java.net.http.HttpClient -import java.net.http.HttpRequest -import java.net.http.HttpResponse -import java.time.Duration -import java.time.temporal.ChronoUnit - -class JdkHttpClientTest extends HttpClientTest implements AgentTestTrait { - - @Shared - def client = HttpClient.newBuilder() - .version(HttpClient.Version.HTTP_1_1) - .connectTimeout(Duration.of(CONNECT_TIMEOUT_MS, ChronoUnit.MILLIS)) - .followRedirects(HttpClient.Redirect.NORMAL) - .build() - - @Override - HttpRequest buildRequest(String method, URI uri, Map headers) { - def requestBuilder = HttpRequest.newBuilder() - .uri(uri) - .method(method, HttpRequest.BodyPublishers.noBody()) - headers.entrySet().each { - requestBuilder.header(it.key, it.value) - } - if (uri.toString().contains("/read-timeout")) { - requestBuilder.timeout(Duration.of(READ_TIMEOUT_MS, ChronoUnit.MILLIS)) - } - return requestBuilder.build() - } - - @Override - int sendRequest(HttpRequest request, String method, URI uri, Map headers) { - return client.send(request, HttpResponse.BodyHandlers.ofString()).statusCode() - } - - @Override - void sendRequestWithCallback(HttpRequest request, String method, URI uri, Map headers, AbstractHttpClientTest.RequestResult requestResult) { - client.sendAsync(request, HttpResponse.BodyHandlers.ofString()) - .whenComplete { response, throwable -> - requestResult.complete({ response.statusCode() }, throwable?.getCause()) - } - } - - @Override - boolean testCircularRedirects() { - return false - } - - // TODO nested client span is not created, but context is still injected - // which is not what the test expects - @Override - boolean testWithClientParent() { - false - } - - @Override - boolean testReadTimeout() { - true - } -} diff --git a/instrumentation/java-http-client/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/httpclient/JdkHttpClientTest.java b/instrumentation/java-http-client/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/httpclient/JdkHttpClientTest.java new file mode 100644 index 000000000000..f8e7a3f3ee1e --- /dev/null +++ b/instrumentation/java-http-client/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/httpclient/JdkHttpClientTest.java @@ -0,0 +1,75 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.httpclient; + +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.http.AbstractHttpClientTest; +import io.opentelemetry.instrumentation.testing.junit.http.HttpClientInstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.http.HttpClientTestOptions; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.util.Map; +import org.junit.jupiter.api.extension.RegisterExtension; + +public class JdkHttpClientTest extends AbstractHttpClientTest { + + @RegisterExtension + static final InstrumentationExtension testing = HttpClientInstrumentationExtension.forAgent(); + + private static final HttpClient client = + HttpClient.newBuilder() + .version(HttpClient.Version.HTTP_1_1) + .connectTimeout(CONNECTION_TIMEOUT) + .followRedirects(HttpClient.Redirect.NORMAL) + .build(); + + @Override + public HttpRequest buildRequest(String method, URI uri, Map headers) { + HttpRequest.Builder requestBuilder = + HttpRequest.newBuilder().uri(uri).method(method, HttpRequest.BodyPublishers.noBody()); + headers.forEach(requestBuilder::header); + if (uri.toString().contains("/read-timeout")) { + requestBuilder.timeout(READ_TIMEOUT); + } + return requestBuilder.build(); + } + + @Override + public int sendRequest(HttpRequest request, String method, URI uri, Map headers) + throws Exception { + return client.send(request, HttpResponse.BodyHandlers.ofString()).statusCode(); + } + + @Override + public void sendRequestWithCallback( + HttpRequest request, + String method, + URI uri, + Map headers, + AbstractHttpClientTest.RequestResult requestResult) { + client + .sendAsync(request, HttpResponse.BodyHandlers.ofString()) + .whenComplete( + (response, throwable) -> { + if (throwable == null) { + requestResult.complete(response.statusCode()); + } else { + requestResult.complete(throwable.getCause()); + } + }); + } + + @Override + protected void configure(HttpClientTestOptions options) { + options.disableTestCircularRedirects(); + options.enableTestReadTimeout(); + // TODO nested client span is not created, but context is still injected + // which is not what the test expects + options.disableTestWithClientParent(); + } +} diff --git a/instrumentation/java-util-logging/javaagent/build.gradle.kts b/instrumentation/java-util-logging/javaagent/build.gradle.kts index f5f3f63c9d94..fd5f1a23bc5e 100644 --- a/instrumentation/java-util-logging/javaagent/build.gradle.kts +++ b/instrumentation/java-util-logging/javaagent/build.gradle.kts @@ -5,7 +5,7 @@ plugins { dependencies { compileOnly(project(":instrumentation:java-util-logging:shaded-stub-for-instrumenting")) - compileOnly(project(":instrumentation-appender-api-internal")) + compileOnly("io.opentelemetry:opentelemetry-api-logs") compileOnly(project(":javaagent-bootstrap")) // ensure no cross interference 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 0860fb93e60a..cccd93eaa502 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 @@ -8,10 +8,10 @@ import application.java.util.logging.Logger; import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.common.AttributesBuilder; +import io.opentelemetry.api.logs.GlobalLoggerProvider; +import io.opentelemetry.api.logs.LogRecordBuilder; +import io.opentelemetry.api.logs.Severity; import io.opentelemetry.context.Context; -import io.opentelemetry.instrumentation.api.appender.internal.LogBuilder; -import io.opentelemetry.instrumentation.api.appender.internal.Severity; -import io.opentelemetry.javaagent.bootstrap.AgentLogEmitterProvider; import io.opentelemetry.javaagent.bootstrap.internal.InstrumentationConfig; import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; import java.io.PrintWriter; @@ -40,14 +40,15 @@ public static void capture(Logger logger, LogRecord logRecord) { if (instrumentationName == null || instrumentationName.isEmpty()) { instrumentationName = "ROOT"; } - LogBuilder builder = - AgentLogEmitterProvider.get().logEmitterBuilder(instrumentationName).build().logBuilder(); + LogRecordBuilder builder = + GlobalLoggerProvider.get().loggerBuilder(instrumentationName).build().logRecordBuilder(); mapLogRecord(builder, logRecord); builder.emit(); } /** - * Map the {@link LogRecord} data model onto the {@link LogBuilder}. Unmapped fields include: + * Map the {@link LogRecord} data model onto the {@link LogRecordBuilder}. Unmapped fields + * include: * *

    *
  • Fully qualified class name - {@link LogRecord#getSourceClassName()} @@ -55,7 +56,7 @@ public static void capture(Logger logger, LogRecord logRecord) { *
  • Thread id - {@link LogRecord#getThreadID()} *
*/ - private static void mapLogRecord(LogBuilder builder, LogRecord logRecord) { + private static void mapLogRecord(LogRecordBuilder builder, LogRecord logRecord) { // message String message = FORMATTER.formatMessage(logRecord); if (message != null) { @@ -80,7 +81,7 @@ private static void mapLogRecord(LogBuilder builder, LogRecord logRecord) { Throwable throwable = logRecord.getThrown(); if (throwable != null) { // TODO (trask) extract method for recording exception into - // instrumentation-appender-api-internal + // io.opentelemetry:opentelemetry-api-logs attributes.put(SemanticAttributes.EXCEPTION_TYPE, throwable.getClass().getName()); attributes.put(SemanticAttributes.EXCEPTION_MESSAGE, throwable.getMessage()); StringWriter writer = new StringWriter(); @@ -94,7 +95,7 @@ private static void mapLogRecord(LogBuilder builder, LogRecord logRecord) { attributes.put(SemanticAttributes.THREAD_ID, currentThread.getId()); } - builder.setAttributes(attributes.build()); + builder.setAllAttributes(attributes.build()); // span context builder.setContext(Context.current()); diff --git a/instrumentation/java-util-logging/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jul/JavaUtilLoggingInstrumentation.java b/instrumentation/java-util-logging/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jul/JavaUtilLoggingInstrumentation.java index 019016809cd5..205b7419f18c 100644 --- a/instrumentation/java-util-logging/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jul/JavaUtilLoggingInstrumentation.java +++ b/instrumentation/java-util-logging/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jul/JavaUtilLoggingInstrumentation.java @@ -13,7 +13,7 @@ import static net.bytebuddy.matcher.ElementMatchers.takesArguments; import application.java.util.logging.Logger; -import io.opentelemetry.instrumentation.api.appender.internal.LogEmitterProvider; +import io.opentelemetry.api.logs.LoggerProvider; import io.opentelemetry.javaagent.bootstrap.CallDepth; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; @@ -50,7 +50,7 @@ public static void methodEnter( @Advice.Local("otelCallDepth") CallDepth callDepth) { // need to track call depth across all loggers in order to avoid double capture when one // logging framework delegates to another - callDepth = CallDepth.forClass(LogEmitterProvider.class); + callDepth = CallDepth.forClass(LoggerProvider.class); if (callDepth.getAndIncrement() == 0) { JavaUtilLoggingHelper.capture(logger, logRecord); } diff --git a/instrumentation/java-util-logging/javaagent/src/test/groovy/JavaUtilLoggingTest.groovy b/instrumentation/java-util-logging/javaagent/src/test/groovy/JavaUtilLoggingTest.groovy index 8914dd96a40d..6918b53feb78 100644 --- a/instrumentation/java-util-logging/javaagent/src/test/groovy/JavaUtilLoggingTest.groovy +++ b/instrumentation/java-util-logging/javaagent/src/test/groovy/JavaUtilLoggingTest.groovy @@ -4,7 +4,7 @@ */ import io.opentelemetry.instrumentation.test.AgentInstrumentationSpecification -import io.opentelemetry.sdk.logs.data.Severity +import io.opentelemetry.api.logs.Severity import io.opentelemetry.semconv.trace.attributes.SemanticAttributes import spock.lang.Unroll @@ -50,9 +50,9 @@ class JavaUtilLoggingTest extends AgentInstrumentationSpecification { await() .untilAsserted( () -> { - assertThat(logs).hasSize(1) + assertThat(logRecords).hasSize(1) }) - def log = logs.get(0) + def log = logRecords.get(0) if (testArgs == "params") { assertThat(log.getBody().asString()).isEqualTo("xyz: 123") } else { @@ -81,7 +81,7 @@ class JavaUtilLoggingTest extends AgentInstrumentationSpecification { } } else { Thread.sleep(500) // sleep a bit just to make sure no log is captured - logs.size() == 0 + logRecords.size() == 0 } where: diff --git a/instrumentation/jaxrs-client/jaxrs-client-1.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jaxrsclient/v1_1/ClientHandlerInstrumentation.java b/instrumentation/jaxrs-client/jaxrs-client-1.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jaxrsclient/v1_1/ClientHandlerInstrumentation.java index ec94aa234d8a..85f0293f0db1 100644 --- a/instrumentation/jaxrs-client/jaxrs-client-1.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jaxrsclient/v1_1/ClientHandlerInstrumentation.java +++ b/instrumentation/jaxrs-client/jaxrs-client-1.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jaxrsclient/v1_1/ClientHandlerInstrumentation.java @@ -6,7 +6,6 @@ package io.opentelemetry.javaagent.instrumentation.jaxrsclient.v1_1; import static io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge.currentContext; -import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.extendsClass; import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed; import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.implementsInterface; import static io.opentelemetry.javaagent.instrumentation.jaxrsclient.v1_1.JaxRsClientSingletons.instrumenter; @@ -39,8 +38,8 @@ public ElementMatcher typeMatcher() { public void transform(TypeTransformer transformer) { transformer.applyAdviceToMethod( named("handle") - .and(takesArgument(0, extendsClass(named("com.sun.jersey.api.client.ClientRequest")))) - .and(returns(extendsClass(named("com.sun.jersey.api.client.ClientResponse")))), + .and(takesArgument(0, named("com.sun.jersey.api.client.ClientRequest"))) + .and(returns(named("com.sun.jersey.api.client.ClientResponse"))), this.getClass().getName() + "$HandleAdvice"); } diff --git a/instrumentation/jaxrs-client/jaxrs-client-1.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jaxrsclient/v1_1/JaxRsClientHttpAttributesGetter.java b/instrumentation/jaxrs-client/jaxrs-client-1.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jaxrsclient/v1_1/JaxRsClientHttpAttributesGetter.java index 4777432c92ac..b9a8cee2e897 100644 --- a/instrumentation/jaxrs-client/jaxrs-client-1.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jaxrsclient/v1_1/JaxRsClientHttpAttributesGetter.java +++ b/instrumentation/jaxrs-client/jaxrs-client-1.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jaxrsclient/v1_1/JaxRsClientHttpAttributesGetter.java @@ -42,44 +42,17 @@ public List requestHeader(ClientRequest httpRequest, String name) { return stringHeaders; } - @Override - @Nullable - public Long requestContentLength( - ClientRequest httpRequest, @Nullable ClientResponse httpResponse) { - return null; - } - - @Override - @Nullable - public Long requestContentLengthUncompressed( - ClientRequest httpRequest, @Nullable ClientResponse httpResponse) { - return null; - } - @Override public String flavor(ClientRequest httpRequest, @Nullable ClientResponse httpResponse) { return SemanticAttributes.HttpFlavorValues.HTTP_1_1; } @Override - public Integer statusCode(ClientRequest httpRequest, ClientResponse httpResponse) { + public Integer statusCode( + ClientRequest httpRequest, ClientResponse httpResponse, @Nullable Throwable error) { return httpResponse.getStatus(); } - @Override - @Nullable - public Long responseContentLength(ClientRequest httpRequest, ClientResponse httpResponse) { - int length = httpResponse.getLength(); - return length != -1 ? (long) length : null; - } - - @Override - @Nullable - public Long responseContentLengthUncompressed( - ClientRequest httpRequest, ClientResponse httpResponse) { - return null; - } - @Override public List responseHeader( ClientRequest httpRequest, ClientResponse httpResponse, String name) { diff --git a/instrumentation/jaxrs-client/jaxrs-client-1.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jaxrsclient/v1_1/JaxRsClientNetAttributesGetter.java b/instrumentation/jaxrs-client/jaxrs-client-1.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jaxrsclient/v1_1/JaxRsClientNetAttributesGetter.java index 63f35227ed60..513260cad108 100644 --- a/instrumentation/jaxrs-client/jaxrs-client-1.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jaxrsclient/v1_1/JaxRsClientNetAttributesGetter.java +++ b/instrumentation/jaxrs-client/jaxrs-client-1.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jaxrsclient/v1_1/JaxRsClientNetAttributesGetter.java @@ -21,22 +21,12 @@ public String transport(ClientRequest request, @Nullable ClientResponse response @Override @Nullable - public String peerName(ClientRequest request, @Nullable ClientResponse response) { + public String peerName(ClientRequest request) { return request.getURI().getHost(); } @Override - public Integer peerPort(ClientRequest request, @Nullable ClientResponse response) { - int port = request.getURI().getPort(); - if (port != -1) { - return port; - } - return null; - } - - @Override - @Nullable - public String peerIp(ClientRequest request, @Nullable ClientResponse response) { - return null; + public Integer peerPort(ClientRequest request) { + return request.getURI().getPort(); } } diff --git a/instrumentation/jaxrs-client/jaxrs-client-1.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jaxrsclient/v1_1/JaxRsClientSingletons.java b/instrumentation/jaxrs-client/jaxrs-client-1.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jaxrsclient/v1_1/JaxRsClientSingletons.java index 53dcafa2cadc..b617dce1c2df 100644 --- a/instrumentation/jaxrs-client/jaxrs-client-1.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jaxrsclient/v1_1/JaxRsClientSingletons.java +++ b/instrumentation/jaxrs-client/jaxrs-client-1.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jaxrsclient/v1_1/JaxRsClientSingletons.java @@ -9,12 +9,13 @@ import com.sun.jersey.api.client.ClientResponse; import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; -import io.opentelemetry.instrumentation.api.instrumenter.PeerServiceAttributesExtractor; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpClientAttributesExtractor; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpClientMetrics; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanNameExtractor; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanStatusExtractor; import io.opentelemetry.instrumentation.api.instrumenter.net.NetClientAttributesExtractor; +import io.opentelemetry.instrumentation.api.instrumenter.net.PeerServiceAttributesExtractor; +import io.opentelemetry.javaagent.bootstrap.internal.CommonConfig; public class JaxRsClientSingletons { private static final String INSTRUMENTATION_NAME = "io.opentelemetry.jaxrs-client-1.1"; @@ -31,11 +32,17 @@ public class JaxRsClientSingletons { INSTRUMENTATION_NAME, HttpSpanNameExtractor.create(httpAttributesGetter)) .setSpanStatusExtractor(HttpSpanStatusExtractor.create(httpAttributesGetter)) - .addAttributesExtractor(HttpClientAttributesExtractor.create(httpAttributesGetter)) + .addAttributesExtractor( + HttpClientAttributesExtractor.builder(httpAttributesGetter) + .setCapturedRequestHeaders(CommonConfig.get().getClientRequestHeaders()) + .setCapturedResponseHeaders(CommonConfig.get().getClientResponseHeaders()) + .build()) .addAttributesExtractor(NetClientAttributesExtractor.create(netAttributesGetter)) - .addAttributesExtractor(PeerServiceAttributesExtractor.create(netAttributesGetter)) + .addAttributesExtractor( + PeerServiceAttributesExtractor.create( + netAttributesGetter, CommonConfig.get().getPeerServiceMapping())) .addOperationMetrics(HttpClientMetrics.get()) - .newClientInstrumenter(ClientRequestHeaderSetter.INSTANCE); + .buildClientInstrumenter(ClientRequestHeaderSetter.INSTANCE); } public static Instrumenter instrumenter() { diff --git a/instrumentation/jaxrs-client/jaxrs-client-2.0-testing/src/test/groovy/JaxRsClientTest.groovy b/instrumentation/jaxrs-client/jaxrs-client-2.0-testing/src/test/groovy/JaxRsClientTest.groovy index b221a27a6b42..5752e17075d1 100644 --- a/instrumentation/jaxrs-client/jaxrs-client-2.0-testing/src/test/groovy/JaxRsClientTest.groovy +++ b/instrumentation/jaxrs-client/jaxrs-client-2.0-testing/src/test/groovy/JaxRsClientTest.groovy @@ -110,13 +110,14 @@ abstract class JaxRsClientTest extends HttpClientTest implem attributes { "$SemanticAttributes.NET_TRANSPORT" IP_TCP "$SemanticAttributes.NET_PEER_NAME" uri.host - "$SemanticAttributes.NET_PEER_IP" { it == null || it == "127.0.0.1" } "$SemanticAttributes.NET_PEER_PORT" uri.port > 0 ? uri.port : { it == null || it == 443 } + "$SemanticAttributes.NET_SOCK_PEER_ADDR" { it == "127.0.0.1" || it == null } "$SemanticAttributes.HTTP_URL" "${uri}" "$SemanticAttributes.HTTP_METHOD" method "$SemanticAttributes.HTTP_STATUS_CODE" statusCode "$SemanticAttributes.HTTP_FLAVOR" "1.1" - "$SemanticAttributes.HTTP_USER_AGENT" { it == null || String } + "$SemanticAttributes.HTTP_USER_AGENT" { it == null || it instanceof String } + "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" { it == null || it instanceof Long } } } serverSpan(it, 1, span(0)) diff --git a/instrumentation/jaxrs/jaxrs-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jaxrs/v1_0/JaxrsSingletons.java b/instrumentation/jaxrs/jaxrs-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jaxrs/v1_0/JaxrsSingletons.java index 51295ec06426..47461937e44d 100644 --- a/instrumentation/jaxrs/jaxrs-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jaxrs/v1_0/JaxrsSingletons.java +++ b/instrumentation/jaxrs/jaxrs-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jaxrs/v1_0/JaxrsSingletons.java @@ -32,7 +32,7 @@ public final class JaxrsSingletons { CodeSpanNameExtractor.create(codeAttributesGetter)) .addAttributesExtractor(CodeAttributesExtractor.create(codeAttributesGetter)) .setEnabled(ExperimentalConfig.get().controllerTelemetryEnabled()) - .newInstrumenter(); + .buildInstrumenter(); } public static Instrumenter instrumenter() { diff --git a/instrumentation/jaxrs/jaxrs-1.0/javaagent/src/test/java/JavaInterfaces.java b/instrumentation/jaxrs/jaxrs-1.0/javaagent/src/test/java/JavaInterfaces.java index ed0e594b9942..bcd5e76cc11d 100644 --- a/instrumentation/jaxrs/jaxrs-1.0/javaagent/src/test/java/JavaInterfaces.java +++ b/instrumentation/jaxrs/jaxrs-1.0/javaagent/src/test/java/JavaInterfaces.java @@ -66,4 +66,6 @@ public void actual() { // do nothing } } + + private JavaInterfaces() {} } diff --git a/instrumentation/jaxrs/jaxrs-2.0/jaxrs-2.0-annotations/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jaxrs/v2_0/JaxrsAnnotationsInstrumentation.java b/instrumentation/jaxrs/jaxrs-2.0/jaxrs-2.0-annotations/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jaxrs/v2_0/JaxrsAnnotationsInstrumentation.java index 5f0a43ed53b5..9caa2817c560 100644 --- a/instrumentation/jaxrs/jaxrs-2.0/jaxrs-2.0-annotations/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jaxrs/v2_0/JaxrsAnnotationsInstrumentation.java +++ b/instrumentation/jaxrs/jaxrs-2.0/jaxrs-2.0-annotations/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jaxrs/v2_0/JaxrsAnnotationsInstrumentation.java @@ -151,18 +151,13 @@ public static void stopSpan( CompletionStage asyncReturnValue = returnValue instanceof CompletionStage ? (CompletionStage) returnValue : null; - - if (asyncResponse != null && !asyncResponse.isSuspended()) { - // Clear span from the asyncResponse. Logically this should never happen. Added to be safe. - VirtualField.find(AsyncResponse.class, AsyncResponseData.class).set(asyncResponse, null); - } if (asyncReturnValue != null) { // span finished by CompletionStageFinishCallback asyncReturnValue = asyncReturnValue.handle( new CompletionStageFinishCallback<>(instrumenter(), context, handlerData)); } - if ((asyncResponse == null || !asyncResponse.isSuspended()) && asyncReturnValue == null) { + if (asyncResponse == null && asyncReturnValue == null) { instrumenter().end(context, handlerData, null, null); } // else span finished by AsyncResponse*Advice diff --git a/instrumentation/jaxrs/jaxrs-2.0/jaxrs-2.0-annotations/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jaxrs/v2_0/JaxrsAnnotationsInstrumentationModule.java b/instrumentation/jaxrs/jaxrs-2.0/jaxrs-2.0-annotations/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jaxrs/v2_0/JaxrsAnnotationsInstrumentationModule.java index f2686bb390fb..6f1501c14969 100644 --- a/instrumentation/jaxrs/jaxrs-2.0/jaxrs-2.0-annotations/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jaxrs/v2_0/JaxrsAnnotationsInstrumentationModule.java +++ b/instrumentation/jaxrs/jaxrs-2.0/jaxrs-2.0-annotations/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jaxrs/v2_0/JaxrsAnnotationsInstrumentationModule.java @@ -17,7 +17,7 @@ @AutoService(InstrumentationModule.class) public class JaxrsAnnotationsInstrumentationModule extends InstrumentationModule { public JaxrsAnnotationsInstrumentationModule() { - super("jaxrs-annotations", "jaxrs-2.0-annotations"); + super("jaxrs", "jaxrs-2.0", "jaxrs-annotations", "jaxrs-2.0-annotations"); } // require jax-rs 2 diff --git a/instrumentation/jaxrs/jaxrs-2.0/jaxrs-2.0-annotations/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jaxrs/v2_0/JaxrsAnnotationsSingletons.java b/instrumentation/jaxrs/jaxrs-2.0/jaxrs-2.0-annotations/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jaxrs/v2_0/JaxrsAnnotationsSingletons.java index c5ad08c2c746..2d76a0ba33a3 100644 --- a/instrumentation/jaxrs/jaxrs-2.0/jaxrs-2.0-annotations/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jaxrs/v2_0/JaxrsAnnotationsSingletons.java +++ b/instrumentation/jaxrs/jaxrs-2.0/jaxrs-2.0-annotations/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jaxrs/v2_0/JaxrsAnnotationsSingletons.java @@ -12,7 +12,7 @@ public final class JaxrsAnnotationsSingletons { private static final Instrumenter INSTANCE = - JaxrsInstrumenterFactory.createInstrumenter("io.opentelemetry.jaxrs-annotations-2.0"); + JaxrsInstrumenterFactory.createInstrumenter("io.opentelemetry.jaxrs-2.0-annotations"); public static Instrumenter instrumenter() { return INSTANCE; diff --git a/instrumentation/jaxrs/jaxrs-2.0/jaxrs-2.0-annotations/javaagent/src/test/java/JavaInterfaces.java b/instrumentation/jaxrs/jaxrs-2.0/jaxrs-2.0-annotations/javaagent/src/test/java/JavaInterfaces.java index ed0e594b9942..bcd5e76cc11d 100644 --- a/instrumentation/jaxrs/jaxrs-2.0/jaxrs-2.0-annotations/javaagent/src/test/java/JavaInterfaces.java +++ b/instrumentation/jaxrs/jaxrs-2.0/jaxrs-2.0-annotations/javaagent/src/test/java/JavaInterfaces.java @@ -66,4 +66,6 @@ public void actual() { // do nothing } } + + private JavaInterfaces() {} } diff --git a/instrumentation/jaxrs/jaxrs-2.0/jaxrs-2.0-jersey-2.0/javaagent/build.gradle.kts b/instrumentation/jaxrs/jaxrs-2.0/jaxrs-2.0-jersey-2.0/javaagent/build.gradle.kts index 93866cff601f..53ab6c169dff 100644 --- a/instrumentation/jaxrs/jaxrs-2.0/jaxrs-2.0-jersey-2.0/javaagent/build.gradle.kts +++ b/instrumentation/jaxrs/jaxrs-2.0/jaxrs-2.0-jersey-2.0/javaagent/build.gradle.kts @@ -37,10 +37,10 @@ dependencies { testImplementation("javax.xml.bind:jaxb-api:2.2.3") testImplementation("org.eclipse.jetty:jetty-webapp:9.4.6.v20170531") - latestDepTestLibrary("org.glassfish.jersey.core:jersey-server:2.+") - latestDepTestLibrary("org.glassfish.jersey.containers:jersey-container-servlet:2.+") - latestDepTestLibrary("org.glassfish.jersey.containers:jersey-container-servlet:2.+") - latestDepTestLibrary("org.glassfish.jersey.inject:jersey-hk2:2.+") + latestDepTestLibrary("org.glassfish.jersey.core:jersey-server:2.+") // see jaxrs-3.0-jersey-3.0 module + latestDepTestLibrary("org.glassfish.jersey.containers:jersey-container-servlet:2.+") // see jaxrs-3.0-jersey-3.0 module + latestDepTestLibrary("org.glassfish.jersey.containers:jersey-container-servlet:2.+") // see jaxrs-3.0-jersey-3.0 module + latestDepTestLibrary("org.glassfish.jersey.inject:jersey-hk2:2.+") // see jaxrs-3.0-jersey-3.0 module } if (!(findProperty("testLatestDeps") as Boolean)) { @@ -64,5 +64,8 @@ tasks { withType().configureEach { // TODO run tests both with and without experimental span attributes jvmArgs("-Dotel.instrumentation.jaxrs.experimental-span-attributes=true") + // required on jdk17 + jvmArgs("--add-opens=java.base/java.lang=ALL-UNNAMED") + jvmArgs("-XX:+IgnoreUnrecognizedVMOptions") } } diff --git a/instrumentation/jaxrs/jaxrs-2.0/jaxrs-2.0-resteasy-3.0/javaagent/build.gradle.kts b/instrumentation/jaxrs/jaxrs-2.0/jaxrs-2.0-resteasy-3.0/javaagent/build.gradle.kts index 0b947828f56c..8ba506625557 100644 --- a/instrumentation/jaxrs/jaxrs-2.0/jaxrs-2.0-resteasy-3.0/javaagent/build.gradle.kts +++ b/instrumentation/jaxrs/jaxrs-2.0/jaxrs-2.0-resteasy-3.0/javaagent/build.gradle.kts @@ -56,11 +56,12 @@ dependencies { testLibrary("io.undertow:undertow-servlet:1.4.28.Final") testLibrary("org.jboss.resteasy:resteasy-servlet-initializer:3.0.4.Final") - latestDepTestLibrary("org.jboss.resteasy:resteasy-servlet-initializer:3.0.+") - latestDepTestLibrary("org.jboss.resteasy:resteasy-jaxrs:3.0.+") - latestDepTestLibrary("org.jboss.resteasy:resteasy-undertow:3.0.+") { + latestDepTestLibrary("org.jboss.resteasy:resteasy-servlet-initializer:3.0.+") // see jaxrs-3.0-resteasy-3.1 module + latestDepTestLibrary("org.jboss.resteasy:resteasy-jaxrs:3.0.+") // see jaxrs-3.0-resteasy-3.1 module + latestDepTestLibrary("org.jboss.resteasy:resteasy-undertow:3.0.+") { // see jaxrs-3.0-resteasy-3.1 module exclude("org.jboss.resteasy", "resteasy-client") } + latestDepTestLibrary("io.undertow:undertow-servlet:2.2.+") // 2.3 switches to Servlet 5 / Jakarta namespace } tasks { diff --git a/instrumentation/jaxrs/jaxrs-2.0/jaxrs-2.0-tomee-testing/build.gradle.kts b/instrumentation/jaxrs/jaxrs-2.0/jaxrs-2.0-tomee-testing/build.gradle.kts index 9337800053fa..317ee786df37 100644 --- a/instrumentation/jaxrs/jaxrs-2.0/jaxrs-2.0-tomee-testing/build.gradle.kts +++ b/instrumentation/jaxrs/jaxrs-2.0/jaxrs-2.0-tomee-testing/build.gradle.kts @@ -13,3 +13,10 @@ dependencies { testInstrumentation(project(":instrumentation:jaxrs:jaxrs-2.0:jaxrs-2.0-annotations:javaagent")) testInstrumentation(project(":instrumentation:jaxrs:jaxrs-2.0:jaxrs-2.0-cxf-3.2:javaagent")) } + +tasks.withType().configureEach { + // required on jdk17 + jvmArgs("--add-opens=java.base/java.lang=ALL-UNNAMED") + jvmArgs("--add-exports=java.base/sun.misc=ALL-UNNAMED") + jvmArgs("-XX:+IgnoreUnrecognizedVMOptions") +} diff --git a/instrumentation/jaxrs/jaxrs-2.0/jaxrs-2.0-wildfly-testing/build.gradle.kts b/instrumentation/jaxrs/jaxrs-2.0/jaxrs-2.0-wildfly-testing/build.gradle.kts index 99e3f6d732b8..f84a8863a672 100644 --- a/instrumentation/jaxrs/jaxrs-2.0/jaxrs-2.0-wildfly-testing/build.gradle.kts +++ b/instrumentation/jaxrs/jaxrs-2.0/jaxrs-2.0-wildfly-testing/build.gradle.kts @@ -66,3 +66,9 @@ tasks { } } } + +tasks.withType().configureEach { + // required on jdk17 + jvmArgs("--add-opens=java.base/java.lang=ALL-UNNAMED") + jvmArgs("-XX:+IgnoreUnrecognizedVMOptions") +} diff --git a/instrumentation/jaxrs/jaxrs-3.0/jaxrs-3.0-annotations/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jaxrs/v3_0/JaxrsAnnotationsInstrumentation.java b/instrumentation/jaxrs/jaxrs-3.0/jaxrs-3.0-annotations/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jaxrs/v3_0/JaxrsAnnotationsInstrumentation.java index 61f6055424ff..7ade02505b6b 100644 --- a/instrumentation/jaxrs/jaxrs-3.0/jaxrs-3.0-annotations/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jaxrs/v3_0/JaxrsAnnotationsInstrumentation.java +++ b/instrumentation/jaxrs/jaxrs-3.0/jaxrs-3.0-annotations/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jaxrs/v3_0/JaxrsAnnotationsInstrumentation.java @@ -151,18 +151,13 @@ public static void stopSpan( CompletionStage asyncReturnValue = returnValue instanceof CompletionStage ? (CompletionStage) returnValue : null; - - if (asyncResponse != null && !asyncResponse.isSuspended()) { - // Clear span from the asyncResponse. Logically this should never happen. Added to be safe. - VirtualField.find(AsyncResponse.class, AsyncResponseData.class).set(asyncResponse, null); - } if (asyncReturnValue != null) { // span finished by CompletionStageFinishCallback asyncReturnValue = asyncReturnValue.handle( new CompletionStageFinishCallback<>(instrumenter(), context, handlerData)); } - if ((asyncResponse == null || !asyncResponse.isSuspended()) && asyncReturnValue == null) { + if (asyncResponse == null && asyncReturnValue == null) { instrumenter().end(context, handlerData, null, null); } // else span finished by AsyncResponse*Advice diff --git a/instrumentation/jaxrs/jaxrs-3.0/jaxrs-3.0-annotations/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jaxrs/v3_0/JaxrsAnnotationsInstrumentationModule.java b/instrumentation/jaxrs/jaxrs-3.0/jaxrs-3.0-annotations/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jaxrs/v3_0/JaxrsAnnotationsInstrumentationModule.java index a0ac9744aace..df8d2e4173d4 100644 --- a/instrumentation/jaxrs/jaxrs-3.0/jaxrs-3.0-annotations/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jaxrs/v3_0/JaxrsAnnotationsInstrumentationModule.java +++ b/instrumentation/jaxrs/jaxrs-3.0/jaxrs-3.0-annotations/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jaxrs/v3_0/JaxrsAnnotationsInstrumentationModule.java @@ -17,7 +17,7 @@ @AutoService(InstrumentationModule.class) public class JaxrsAnnotationsInstrumentationModule extends InstrumentationModule { public JaxrsAnnotationsInstrumentationModule() { - super("jaxrs-annotations", "jaxrs-3.0-annotations"); + super("jaxrs", "jaxrs-3.0", "jaxrs-annotations", "jaxrs-3.0-annotations"); } // require jax-rs 3 diff --git a/instrumentation/jaxrs/jaxrs-3.0/jaxrs-3.0-annotations/javaagent/src/test/java/JavaInterfaces.java b/instrumentation/jaxrs/jaxrs-3.0/jaxrs-3.0-annotations/javaagent/src/test/java/JavaInterfaces.java index 0e747d3d3d72..868a64488f44 100644 --- a/instrumentation/jaxrs/jaxrs-3.0/jaxrs-3.0-annotations/javaagent/src/test/java/JavaInterfaces.java +++ b/instrumentation/jaxrs/jaxrs-3.0/jaxrs-3.0-annotations/javaagent/src/test/java/JavaInterfaces.java @@ -66,4 +66,6 @@ public void actual() { // do nothing } } + + private JavaInterfaces() {} } diff --git a/instrumentation/jaxrs/jaxrs-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jaxrs/JaxrsInstrumenterFactory.java b/instrumentation/jaxrs/jaxrs-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jaxrs/JaxrsInstrumenterFactory.java index d9a243a16e24..9e184160891c 100644 --- a/instrumentation/jaxrs/jaxrs-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jaxrs/JaxrsInstrumenterFactory.java +++ b/instrumentation/jaxrs/jaxrs-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jaxrs/JaxrsInstrumenterFactory.java @@ -22,7 +22,7 @@ public static Instrumenter createInstrumenter(String instrume CodeSpanNameExtractor.create(codeAttributesGetter)) .addAttributesExtractor(CodeAttributesExtractor.create(codeAttributesGetter)) .setEnabled(ExperimentalConfig.get().controllerTelemetryEnabled()) - .newInstrumenter(); + .buildInstrumenter(); } private JaxrsInstrumenterFactory() {} diff --git a/instrumentation/jaxrs/jaxrs-common/testing/src/main/groovy/AbstractJaxRsHttpServerTest.groovy b/instrumentation/jaxrs/jaxrs-common/testing/src/main/groovy/AbstractJaxRsHttpServerTest.groovy index b4b00a04490b..6629d28bf834 100644 --- a/instrumentation/jaxrs/jaxrs-common/testing/src/main/groovy/AbstractJaxRsHttpServerTest.groovy +++ b/instrumentation/jaxrs/jaxrs-common/testing/src/main/groovy/AbstractJaxRsHttpServerTest.groovy @@ -9,9 +9,10 @@ import io.opentelemetry.instrumentation.test.base.HttpServerTest import io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint import io.opentelemetry.sdk.trace.data.SpanData import io.opentelemetry.semconv.trace.attributes.SemanticAttributes -import java.util.concurrent.TimeUnit import spock.lang.Unroll +import java.util.concurrent.TimeUnit + import static io.opentelemetry.api.trace.SpanKind.INTERNAL import static io.opentelemetry.api.trace.SpanKind.SERVER import static io.opentelemetry.api.trace.StatusCode.ERROR @@ -267,10 +268,12 @@ abstract class AbstractJaxRsHttpServerTest extends HttpServerTest implemen hasNoParent() } attributes { - "$SemanticAttributes.NET_PEER_IP" { it == null || it == "127.0.0.1" } // Optional - "$SemanticAttributes.NET_PEER_PORT" Long + "$SemanticAttributes.NET_HOST_NAME" fullUrl.host + "$SemanticAttributes.NET_HOST_PORT" fullUrl.port + "$SemanticAttributes.NET_SOCK_PEER_ADDR" "127.0.0.1" + "$SemanticAttributes.NET_SOCK_PEER_PORT" Long + "$SemanticAttributes.NET_SOCK_HOST_ADDR" "127.0.0.1" "$SemanticAttributes.HTTP_SCHEME" fullUrl.getScheme() - "$SemanticAttributes.HTTP_HOST" fullUrl.getHost() + ":" + fullUrl.getPort() "$SemanticAttributes.HTTP_TARGET" fullUrl.getPath() + (fullUrl.getQuery() != null ? "?" + fullUrl.getQuery() : "") "$SemanticAttributes.HTTP_METHOD" method "$SemanticAttributes.HTTP_STATUS_CODE" statusCode diff --git a/instrumentation/jaxws/jaxws-2.0-axis2-1.6/javaagent/build.gradle.kts b/instrumentation/jaxws/jaxws-2.0-axis2-1.6/javaagent/build.gradle.kts index f3c15a989d2e..78860612d087 100644 --- a/instrumentation/jaxws/jaxws-2.0-axis2-1.6/javaagent/build.gradle.kts +++ b/instrumentation/jaxws/jaxws-2.0-axis2-1.6/javaagent/build.gradle.kts @@ -47,3 +47,9 @@ dependencies { testImplementation("com.sun.xml.ws:jaxws-rt:2.2.8") testImplementation("com.sun.xml.ws:jaxws-tools:2.2.8") } + +tasks.withType().configureEach { + // required on jdk17 + jvmArgs("--add-opens=java.base/java.lang=ALL-UNNAMED") + jvmArgs("-XX:+IgnoreUnrecognizedVMOptions") +} diff --git a/instrumentation/jaxws/jaxws-2.0-axis2-1.6/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/axis2/Axis2Singletons.java b/instrumentation/jaxws/jaxws-2.0-axis2-1.6/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/axis2/Axis2Singletons.java index 2d592512fe77..1ffb4e74b6e1 100644 --- a/instrumentation/jaxws/jaxws-2.0-axis2-1.6/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/axis2/Axis2Singletons.java +++ b/instrumentation/jaxws/jaxws-2.0-axis2-1.6/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/axis2/Axis2Singletons.java @@ -19,7 +19,7 @@ public class Axis2Singletons { Instrumenter.builder( GlobalOpenTelemetry.get(), INSTRUMENTATION_NAME, Axis2Request::spanName) .setEnabled(ExperimentalConfig.get().controllerTelemetryEnabled()) - .newInstrumenter(); + .buildInstrumenter(); } public static Instrumenter instrumenter() { diff --git a/instrumentation/jaxws/jaxws-2.0-cxf-3.0/javaagent/build.gradle.kts b/instrumentation/jaxws/jaxws-2.0-cxf-3.0/javaagent/build.gradle.kts index a024bb686630..c5047f054344 100644 --- a/instrumentation/jaxws/jaxws-2.0-cxf-3.0/javaagent/build.gradle.kts +++ b/instrumentation/jaxws/jaxws-2.0-cxf-3.0/javaagent/build.gradle.kts @@ -37,3 +37,9 @@ dependencies { testImplementation("javax.annotation:javax.annotation-api:1.2") testImplementation("com.sun.xml.messaging.saaj:saaj-impl:1.5.2") } + +tasks.withType().configureEach { + // required on jdk17 + jvmArgs("--add-opens=java.base/java.lang=ALL-UNNAMED") + jvmArgs("-XX:+IgnoreUnrecognizedVMOptions") +} diff --git a/instrumentation/jaxws/jaxws-2.0-cxf-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/cxf/CxfSingletons.java b/instrumentation/jaxws/jaxws-2.0-cxf-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/cxf/CxfSingletons.java index ae2ce18139d8..9da76ff3b41c 100644 --- a/instrumentation/jaxws/jaxws-2.0-cxf-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/cxf/CxfSingletons.java +++ b/instrumentation/jaxws/jaxws-2.0-cxf-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/cxf/CxfSingletons.java @@ -19,7 +19,7 @@ public class CxfSingletons { Instrumenter.builder( GlobalOpenTelemetry.get(), INSTRUMENTATION_NAME, CxfRequest::spanName) .setEnabled(ExperimentalConfig.get().controllerTelemetryEnabled()) - .newInstrumenter(); + .buildInstrumenter(); } public static Instrumenter instrumenter() { diff --git a/instrumentation/jaxws/jaxws-2.0-metro-2.2/javaagent/build.gradle.kts b/instrumentation/jaxws/jaxws-2.0-metro-2.2/javaagent/build.gradle.kts index 3a4c7dd74824..59be6661ab3e 100644 --- a/instrumentation/jaxws/jaxws-2.0-metro-2.2/javaagent/build.gradle.kts +++ b/instrumentation/jaxws/jaxws-2.0-metro-2.2/javaagent/build.gradle.kts @@ -40,5 +40,6 @@ tasks.withType().configureEach { // required on jdk17 jvmArgs("--add-exports=java.xml/com.sun.org.apache.xerces.internal.dom=ALL-UNNAMED") jvmArgs("--add-exports=java.xml/com.sun.org.apache.xerces.internal.jaxp=ALL-UNNAMED") + jvmArgs("--add-opens=java.base/java.lang=ALL-UNNAMED") jvmArgs("-XX:+IgnoreUnrecognizedVMOptions") } diff --git a/instrumentation/jaxws/jaxws-2.0-metro-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/metro/MetroSingletons.java b/instrumentation/jaxws/jaxws-2.0-metro-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/metro/MetroSingletons.java index b95e2ece6701..470bccf76cc9 100644 --- a/instrumentation/jaxws/jaxws-2.0-metro-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/metro/MetroSingletons.java +++ b/instrumentation/jaxws/jaxws-2.0-metro-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/metro/MetroSingletons.java @@ -19,7 +19,7 @@ public class MetroSingletons { Instrumenter.builder( GlobalOpenTelemetry.get(), INSTRUMENTATION_NAME, MetroRequest::spanName) .setEnabled(ExperimentalConfig.get().controllerTelemetryEnabled()) - .newInstrumenter(); + .buildInstrumenter(); } public static Instrumenter instrumenter() { diff --git a/instrumentation/jaxws/jaxws-2.0-tomee-testing/build.gradle.kts b/instrumentation/jaxws/jaxws-2.0-tomee-testing/build.gradle.kts index 06e3bdcce53d..c24dea2e87e7 100644 --- a/instrumentation/jaxws/jaxws-2.0-tomee-testing/build.gradle.kts +++ b/instrumentation/jaxws/jaxws-2.0-tomee-testing/build.gradle.kts @@ -14,3 +14,10 @@ dependencies { testInstrumentation(project(":instrumentation:jaxws:jaxws-2.0-cxf-3.0:javaagent")) testInstrumentation(project(":instrumentation:jaxws:jaxws-jws-api-1.1:javaagent")) } + +tasks.withType().configureEach { + // required on jdk17 + jvmArgs("--add-opens=java.base/java.lang=ALL-UNNAMED") + jvmArgs("--add-exports=java.base/sun.misc=ALL-UNNAMED") + jvmArgs("-XX:+IgnoreUnrecognizedVMOptions") +} diff --git a/instrumentation/jaxws/jaxws-2.0-wildfly-testing/build.gradle.kts b/instrumentation/jaxws/jaxws-2.0-wildfly-testing/build.gradle.kts index 34a5ffa25de1..fd00f7a70f9a 100644 --- a/instrumentation/jaxws/jaxws-2.0-wildfly-testing/build.gradle.kts +++ b/instrumentation/jaxws/jaxws-2.0-wildfly-testing/build.gradle.kts @@ -67,3 +67,9 @@ tasks { } } } + +tasks.withType().configureEach { + // required on jdk17 + jvmArgs("--add-opens=java.base/java.lang=ALL-UNNAMED") + jvmArgs("-XX:+IgnoreUnrecognizedVMOptions") +} diff --git a/instrumentation/jaxws/jaxws-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jaxws/common/JaxWsInstrumenterFactory.java b/instrumentation/jaxws/jaxws-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jaxws/common/JaxWsInstrumenterFactory.java index 2d446b263e6a..b67f6d32d619 100644 --- a/instrumentation/jaxws/jaxws-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jaxws/common/JaxWsInstrumenterFactory.java +++ b/instrumentation/jaxws/jaxws-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jaxws/common/JaxWsInstrumenterFactory.java @@ -22,7 +22,7 @@ public static Instrumenter createInstrumenter(String instrum CodeSpanNameExtractor.create(codeAttributesGetter)) .addAttributesExtractor(CodeAttributesExtractor.create(codeAttributesGetter)) .setEnabled(ExperimentalConfig.get().controllerTelemetryEnabled()) - .newInstrumenter(); + .buildInstrumenter(); } private JaxWsInstrumenterFactory() {} diff --git a/instrumentation/jboss-logmanager/jboss-logmanager-appender-1.1/javaagent/build.gradle.kts b/instrumentation/jboss-logmanager/jboss-logmanager-appender-1.1/javaagent/build.gradle.kts index e8b23ad9381a..eda659dd07ce 100644 --- a/instrumentation/jboss-logmanager/jboss-logmanager-appender-1.1/javaagent/build.gradle.kts +++ b/instrumentation/jboss-logmanager/jboss-logmanager-appender-1.1/javaagent/build.gradle.kts @@ -14,7 +14,7 @@ muzzle { dependencies { library("org.jboss.logmanager:jboss-logmanager:1.1.0.GA") - compileOnly(project(":instrumentation-appender-api-internal")) + compileOnly("io.opentelemetry:opentelemetry-api-logs") compileOnly(project(":javaagent-bootstrap")) // ensure no cross interference diff --git a/instrumentation/jboss-logmanager/jboss-logmanager-appender-1.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jbosslogmanager/appender/v1_1/JbossLogmanagerInstrumentation.java b/instrumentation/jboss-logmanager/jboss-logmanager-appender-1.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jbosslogmanager/appender/v1_1/JbossLogmanagerInstrumentation.java index 2315e840e87e..9035042eabbf 100644 --- a/instrumentation/jboss-logmanager/jboss-logmanager-appender-1.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jbosslogmanager/appender/v1_1/JbossLogmanagerInstrumentation.java +++ b/instrumentation/jboss-logmanager/jboss-logmanager-appender-1.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jbosslogmanager/appender/v1_1/JbossLogmanagerInstrumentation.java @@ -11,7 +11,7 @@ import static net.bytebuddy.matcher.ElementMatchers.takesArgument; import static net.bytebuddy.matcher.ElementMatchers.takesArguments; -import io.opentelemetry.instrumentation.api.appender.internal.LogEmitterProvider; +import io.opentelemetry.api.logs.LoggerProvider; import io.opentelemetry.javaagent.bootstrap.CallDepth; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; @@ -47,7 +47,7 @@ public static void methodEnter( @Advice.Local("otelCallDepth") CallDepth callDepth) { // need to track call depth across all loggers in order to avoid double capture when one // logging framework delegates to another - callDepth = CallDepth.forClass(LogEmitterProvider.class); + callDepth = CallDepth.forClass(LoggerProvider.class); if (callDepth.getAndIncrement() == 0) { LoggingEventMapper.INSTANCE.capture(logger, record); } 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 74aa38376d71..dc962bfc7797 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 @@ -10,11 +10,11 @@ import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.common.AttributesBuilder; +import io.opentelemetry.api.logs.GlobalLoggerProvider; +import io.opentelemetry.api.logs.LogRecordBuilder; +import io.opentelemetry.api.logs.Severity; import io.opentelemetry.context.Context; -import io.opentelemetry.instrumentation.api.appender.internal.LogBuilder; -import io.opentelemetry.instrumentation.api.appender.internal.Severity; import io.opentelemetry.instrumentation.api.internal.cache.Cache; -import io.opentelemetry.javaagent.bootstrap.AgentLogEmitterProvider; import io.opentelemetry.javaagent.bootstrap.internal.InstrumentationConfig; import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; import java.io.PrintWriter; @@ -57,8 +57,8 @@ public void capture(Logger logger, ExtLogRecord record) { instrumentationName = "ROOT"; } - LogBuilder builder = - AgentLogEmitterProvider.get().logEmitterBuilder(instrumentationName).build().logBuilder(); + LogRecordBuilder builder = + GlobalLoggerProvider.get().loggerBuilder(instrumentationName).build().logRecordBuilder(); String message = record.getFormattedMessage(); if (message != null) { @@ -76,7 +76,7 @@ public void capture(Logger logger, ExtLogRecord record) { Throwable throwable = record.getThrown(); if (throwable != null) { // TODO (trask) extract method for recording exception into - // instrumentation-appender-api-internal + // io.opentelemetry:opentelemetry-api-logs attributes.put(SemanticAttributes.EXCEPTION_TYPE, throwable.getClass().getName()); attributes.put(SemanticAttributes.EXCEPTION_MESSAGE, throwable.getMessage()); StringWriter writer = new StringWriter(); @@ -91,7 +91,7 @@ public void capture(Logger logger, ExtLogRecord record) { attributes.put(SemanticAttributes.THREAD_ID, currentThread.getId()); } - builder.setAttributes(attributes.build()); + builder.setAllAttributes(attributes.build()); builder.setContext(Context.current()); diff --git a/instrumentation/jboss-logmanager/jboss-logmanager-appender-1.1/javaagent/src/test/groovy/JbossLogmanagerTest.groovy b/instrumentation/jboss-logmanager/jboss-logmanager-appender-1.1/javaagent/src/test/groovy/JbossLogmanagerTest.groovy index 0902b49c1a53..fd5daefbdfb9 100644 --- a/instrumentation/jboss-logmanager/jboss-logmanager-appender-1.1/javaagent/src/test/groovy/JbossLogmanagerTest.groovy +++ b/instrumentation/jboss-logmanager/jboss-logmanager-appender-1.1/javaagent/src/test/groovy/JbossLogmanagerTest.groovy @@ -5,7 +5,7 @@ import io.opentelemetry.api.common.AttributeKey import io.opentelemetry.instrumentation.test.AgentInstrumentationSpecification -import io.opentelemetry.sdk.logs.data.Severity +import io.opentelemetry.api.logs.Severity import io.opentelemetry.semconv.trace.attributes.SemanticAttributes import org.jboss.logmanager.MDC import org.jboss.logmanager.Level @@ -50,9 +50,9 @@ class JbossLogmanagerTest extends AgentInstrumentationSpecification { await() .untilAsserted( () -> { - assertThat(logs).hasSize(1) + assertThat(logRecords).hasSize(1) }) - def log = logs.get(0) + def log = logRecords.get(0) assertThat(log.getBody().asString()).isEqualTo("xyz") assertThat(log.getInstrumentationScopeInfo().getName()).isEqualTo("abc") assertThat(log.getSeverity()).isEqualTo(severity) @@ -77,7 +77,7 @@ class JbossLogmanagerTest extends AgentInstrumentationSpecification { } } else { Thread.sleep(500) // sleep a bit just to make sure no log is captured - assertThat(logs.size() == 0).isTrue() + assertThat(logRecords.size() == 0).isTrue() } where: @@ -113,9 +113,9 @@ class JbossLogmanagerTest extends AgentInstrumentationSpecification { await() .untilAsserted( () -> { - assertThat(logs).hasSize(1) + assertThat(logRecords).hasSize(1) }) - def log = logs.get(0) + def log = logRecords.get(0) assertThat(log.getBody().asString()).isEqualTo("xyz") assertThat(log.getInstrumentationScopeInfo().getName()).isEqualTo("abc") assertThat(log.getSeverity()).isEqualTo(Severity.INFO) diff --git a/instrumentation/jboss-logmanager/jboss-logmanager-mdc-1.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jbosslogmanager/mdc/v1_1/JbossExtLogRecordInstrumentation.java b/instrumentation/jboss-logmanager/jboss-logmanager-mdc-1.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jbosslogmanager/mdc/v1_1/JbossExtLogRecordInstrumentation.java index 10868cd7f8b6..61a9709ab9df 100644 --- a/instrumentation/jboss-logmanager/jboss-logmanager-mdc-1.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jbosslogmanager/mdc/v1_1/JbossExtLogRecordInstrumentation.java +++ b/instrumentation/jboss-logmanager/jboss-logmanager-mdc-1.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jbosslogmanager/mdc/v1_1/JbossExtLogRecordInstrumentation.java @@ -89,6 +89,7 @@ public static void onExit( } } + @SuppressWarnings("unused") public static class GetMdcCopyAdvice { @Advice.OnMethodExit(suppress = Throwable.class) diff --git a/instrumentation/jdbc/javaagent/build.gradle.kts b/instrumentation/jdbc/javaagent/build.gradle.kts index 0f92effb53fa..051243d8a340 100644 --- a/instrumentation/jdbc/javaagent/build.gradle.kts +++ b/instrumentation/jdbc/javaagent/build.gradle.kts @@ -48,4 +48,5 @@ sourceSets { tasks.withType().configureEach { jvmArgs("-Dotel.instrumentation.jdbc-datasource.enabled=true") + jvmArgs("-Dotel.instrumentation.common.db-statement-sanitizer.enabled=true") } diff --git a/instrumentation/jdbc/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jdbc/JdbcIgnoredTypesConfigurer.java b/instrumentation/jdbc/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jdbc/JdbcIgnoredTypesConfigurer.java index 0da80f44f8e4..380cb22f85d6 100644 --- a/instrumentation/jdbc/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jdbc/JdbcIgnoredTypesConfigurer.java +++ b/instrumentation/jdbc/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jdbc/JdbcIgnoredTypesConfigurer.java @@ -14,7 +14,7 @@ public class JdbcIgnoredTypesConfigurer implements IgnoredTypesConfigurer { @Override - public void configure(ConfigProperties config, IgnoredTypesBuilder builder) { + public void configure(IgnoredTypesBuilder builder, ConfigProperties config) { // see https://github.com/open-telemetry/opentelemetry-java-instrumentation/issues/5946 builder.ignoreClass("org.jboss.jca.adapters.jdbc."); } diff --git a/instrumentation/jdbc/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jdbc/JdbcSingletons.java b/instrumentation/jdbc/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jdbc/JdbcSingletons.java index 5ca6692a0c10..7da1e4f50cbc 100644 --- a/instrumentation/jdbc/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jdbc/JdbcSingletons.java +++ b/instrumentation/jdbc/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jdbc/JdbcSingletons.java @@ -7,14 +7,16 @@ import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; -import io.opentelemetry.instrumentation.api.instrumenter.PeerServiceAttributesExtractor; import io.opentelemetry.instrumentation.api.instrumenter.SpanKindExtractor; import io.opentelemetry.instrumentation.api.instrumenter.db.DbClientSpanNameExtractor; import io.opentelemetry.instrumentation.api.instrumenter.db.SqlClientAttributesExtractor; import io.opentelemetry.instrumentation.api.instrumenter.net.NetClientAttributesExtractor; +import io.opentelemetry.instrumentation.api.instrumenter.net.PeerServiceAttributesExtractor; import io.opentelemetry.instrumentation.jdbc.internal.DbRequest; import io.opentelemetry.instrumentation.jdbc.internal.JdbcAttributesGetter; import io.opentelemetry.instrumentation.jdbc.internal.JdbcNetAttributesGetter; +import io.opentelemetry.javaagent.bootstrap.internal.CommonConfig; +import io.opentelemetry.javaagent.bootstrap.internal.InstrumentationConfig; public final class JdbcSingletons { private static final String INSTRUMENTATION_NAME = "io.opentelemetry.jdbc"; @@ -30,10 +32,19 @@ public final class JdbcSingletons { GlobalOpenTelemetry.get(), INSTRUMENTATION_NAME, DbClientSpanNameExtractor.create(dbAttributesGetter)) - .addAttributesExtractor(SqlClientAttributesExtractor.create(dbAttributesGetter)) + .addAttributesExtractor( + SqlClientAttributesExtractor.builder(dbAttributesGetter) + .setStatementSanitizationEnabled( + InstrumentationConfig.get() + .getBoolean( + "otel.instrumentation.jdbc.statement-sanitizer.enabled", + CommonConfig.get().isStatementSanitizationEnabled())) + .build()) .addAttributesExtractor(NetClientAttributesExtractor.create(netAttributesGetter)) - .addAttributesExtractor(PeerServiceAttributesExtractor.create(netAttributesGetter)) - .newInstrumenter(SpanKindExtractor.alwaysClient()); + .addAttributesExtractor( + PeerServiceAttributesExtractor.create( + netAttributesGetter, CommonConfig.get().getPeerServiceMapping())) + .buildInstrumenter(SpanKindExtractor.alwaysClient()); } public static Instrumenter instrumenter() { diff --git a/instrumentation/jdbc/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jdbc/datasource/DataSourceInstrumentationModule.java b/instrumentation/jdbc/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jdbc/datasource/DataSourceInstrumentationModule.java index ba99c87f5734..bb276561fd92 100644 --- a/instrumentation/jdbc/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jdbc/datasource/DataSourceInstrumentationModule.java +++ b/instrumentation/jdbc/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jdbc/datasource/DataSourceInstrumentationModule.java @@ -10,6 +10,7 @@ import com.google.auto.service.AutoService; import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; +import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import java.util.List; @AutoService(InstrumentationModule.class) @@ -24,7 +25,7 @@ public List typeInstrumentations() { } @Override - public boolean defaultEnabled() { + public boolean defaultEnabled(ConfigProperties config) { return false; } } diff --git a/instrumentation/jdbc/javaagent/src/test/groovy/JdbcInstrumentationTest.groovy b/instrumentation/jdbc/javaagent/src/test/groovy/JdbcInstrumentationTest.groovy index ec10458c7df1..73ba8cffab6e 100644 --- a/instrumentation/jdbc/javaagent/src/test/groovy/JdbcInstrumentationTest.groovy +++ b/instrumentation/jdbc/javaagent/src/test/groovy/JdbcInstrumentationTest.groovy @@ -636,7 +636,7 @@ class JdbcInstrumentationTest extends AgentInstrumentationSpecification { kind CLIENT childOf span(0) attributes { - "$SemanticAttributes.DB_SYSTEM" "testdb" + "$SemanticAttributes.DB_SYSTEM" "other_sql" "$SemanticAttributes.DB_STATEMENT" "testing ?" "$SemanticAttributes.DB_CONNECTION_STRING" "testdb://localhost" "$SemanticAttributes.NET_PEER_NAME" "localhost" @@ -677,7 +677,7 @@ class JdbcInstrumentationTest extends AgentInstrumentationSpecification { kind CLIENT childOf span(0) attributes { - "$SemanticAttributes.DB_SYSTEM" "testdb" + "$SemanticAttributes.DB_SYSTEM" "other_sql" "$SemanticAttributes.DB_NAME" databaseName "$SemanticAttributes.DB_CONNECTION_STRING" "testdb://localhost" "$SemanticAttributes.DB_STATEMENT" sanitizedQuery @@ -783,7 +783,7 @@ class JdbcInstrumentationTest extends AgentInstrumentationSpecification { kind CLIENT childOf span(0) attributes { - "$SemanticAttributes.DB_SYSTEM" "testdb" + "$SemanticAttributes.DB_SYSTEM" "other_sql" "$SemanticAttributes.DB_CONNECTION_STRING" "testdb://localhost" "$SemanticAttributes.DB_STATEMENT" "SELECT * FROM table" "$SemanticAttributes.DB_OPERATION" "SELECT" diff --git a/instrumentation/jdbc/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/jdbc/test/ProxyStatementFactory.java b/instrumentation/jdbc/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/jdbc/test/ProxyStatementFactory.java index 88ef3cb93a8f..95f139ddd52a 100644 --- a/instrumentation/jdbc/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/jdbc/test/ProxyStatementFactory.java +++ b/instrumentation/jdbc/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/jdbc/test/ProxyStatementFactory.java @@ -9,7 +9,7 @@ import java.lang.reflect.Proxy; import java.sql.Statement; -public class ProxyStatementFactory { +public final class ProxyStatementFactory { public static Statement proxyStatement(Statement statement) throws Exception { TestClassLoader classLoader = new TestClassLoader(ProxyStatementFactory.class.getClassLoader()); @@ -33,4 +33,6 @@ public static Statement proxyStatement(Statement statement) throws Exception { return proxyStatement; } + + private ProxyStatementFactory() {} } diff --git a/instrumentation/jdbc/library/README.md b/instrumentation/jdbc/library/README.md index e4f5a56a06af..383074109019 100644 --- a/instrumentation/jdbc/library/README.md +++ b/instrumentation/jdbc/library/README.md @@ -1,16 +1,16 @@ -# Manual Instrumentation for JDBC +# Library Instrumentation for JDBC Provides OpenTelemetry instrumentation for [Java JDBC API](https://docs.oracle.com/javase/8/docs/technotes/guides/jdbc/). ## Quickstart -### Add these dependencies to your project. +### Add these dependencies to your project: -Replace `OPENTELEMETRY_VERSION` with the latest stable -[release](https://mvnrepository.com/artifact/io.opentelemetry). `Minimum version: 1.4.0` +Replace `OPENTELEMETRY_VERSION` with the [latest +release](https://search.maven.org/search?q=g:io.opentelemetry.instrumentation%20AND%20a:opentelemetry-jdbc). -For Maven add to your `pom.xml`: +For Maven, add to your `pom.xml` dependencies: ```xml @@ -22,19 +22,19 @@ For Maven add to your `pom.xml`: ``` -For Gradle add to your dependencies: +For Gradle, add to your dependencies: ```groovy implementation("io.opentelemetry.instrumentation:opentelemetry-jdbc:OPENTELEMETRY_VERSION") ``` -##### Usage +### Usage -There are three possible ways to activate the OpenTelemetry JDBC instrumentation. The first way is more preferable for +There are two possible ways to activate the OpenTelemetry JDBC instrumentation. The first way is more preferable for DI frameworks which uses connection pools, as it wraps a `DataSource` with a special OpenTelemetry wrapper. The second one requires to change the connection URL and switch to use a special OpenTelemetry driver. -### Datasource way +#### Datasource way If your application uses a DataSource, simply wrap your current DataSource object with `OpenTelemetryDataSource`. `OpenTelemetryDataSource` has a constructor method that accepts the `DataSource` to wrap. This is by far the simplest @@ -63,7 +63,7 @@ public class DataSourceConfig { } ``` -### Driver way +#### Driver way 1. Activate tracing for JDBC connections by setting `jdbc:otel:` prefix to the JDBC URL: diff --git a/instrumentation/jdbc/library/build.gradle.kts b/instrumentation/jdbc/library/build.gradle.kts index e5c6649cff54..102d585f26d9 100644 --- a/instrumentation/jdbc/library/build.gradle.kts +++ b/instrumentation/jdbc/library/build.gradle.kts @@ -41,4 +41,8 @@ tasks { into("build/extracted/shadow-bootstrap") include("io/opentelemetry/javaagent/bootstrap/**") } + + test { + jvmArgs("-Dotel.instrumentation.common.db-statement-sanitizer.enabled=true") + } } diff --git a/instrumentation/jdbc/library/src/main/java/io/opentelemetry/instrumentation/jdbc/internal/DataSourceSingletons.java b/instrumentation/jdbc/library/src/main/java/io/opentelemetry/instrumentation/jdbc/internal/DataSourceSingletons.java index 0b48082f84f9..b28987af038e 100644 --- a/instrumentation/jdbc/library/src/main/java/io/opentelemetry/instrumentation/jdbc/internal/DataSourceSingletons.java +++ b/instrumentation/jdbc/library/src/main/java/io/opentelemetry/instrumentation/jdbc/internal/DataSourceSingletons.java @@ -29,10 +29,12 @@ public final class DataSourceSingletons { INSTRUMENTATION_NAME, CodeSpanNameExtractor.create(codeAttributesGetter)) .addAttributesExtractor(CodeAttributesExtractor.create(codeAttributesGetter)) - .newInstrumenter(); + .buildInstrumenter(); } public static Instrumenter instrumenter() { return INSTRUMENTER; } + + private DataSourceSingletons() {} } diff --git a/instrumentation/jdbc/library/src/main/java/io/opentelemetry/instrumentation/jdbc/internal/JdbcConnectionUrlParser.java b/instrumentation/jdbc/library/src/main/java/io/opentelemetry/instrumentation/jdbc/internal/JdbcConnectionUrlParser.java index 5d917a8f47bb..30cec9522e87 100644 --- a/instrumentation/jdbc/library/src/main/java/io/opentelemetry/instrumentation/jdbc/internal/JdbcConnectionUrlParser.java +++ b/instrumentation/jdbc/library/src/main/java/io/opentelemetry/instrumentation/jdbc/internal/JdbcConnectionUrlParser.java @@ -55,19 +55,16 @@ DbInfo.Builder doParse(String jdbcUrl, DbInfo.Builder builder) { if (!path.isEmpty()) { builder.db(path); } - if (uri.getHost() != null) { builder.host(uri.getHost()); } - if (uri.getPort() > 0) { builder.port(uri.getPort()); } - - return builder.system(uri.getScheme()); } catch (Exception e) { - return builder; + logger.log(FINE, e.getMessage(), e); } + return builder; } }, @@ -629,13 +626,13 @@ DbInfo.Builder doParse(String jdbcUrl, DbInfo.Builder builder) { if (dbInfo.getPort() == null) { builder.port(DEFAULT_PORT); } - return MODIFIED_URL_LIKE.doParse(jdbcUrl, builder).system(DbSystemValues.H2).subtype("tcp"); + return MODIFIED_URL_LIKE.doParse(jdbcUrl, builder).subtype("tcp"); } else if (h2Url.startsWith("ssl:")) { DbInfo dbInfo = builder.build(); if (dbInfo.getPort() == null) { builder.port(DEFAULT_PORT); } - return MODIFIED_URL_LIKE.doParse(jdbcUrl, builder).system(DbSystemValues.H2).subtype("ssl"); + return MODIFIED_URL_LIKE.doParse(jdbcUrl, builder).subtype("ssl"); } else { builder.subtype("file").host(null).port(null); int propLoc = h2Url.indexOf(";"); @@ -688,22 +685,22 @@ DbInfo.Builder doParse(String jdbcUrl, DbInfo.Builder builder) { if (dbInfo.getPort() == null) { builder.port(DEFAULT_PORT); } - return MODIFIED_URL_LIKE.doParse(jdbcUrl, builder).system("hsqldb").subtype("hsql"); + return MODIFIED_URL_LIKE.doParse(jdbcUrl, builder).subtype("hsql"); } else if (hsqlUrl.startsWith("hsqls:")) { if (dbInfo.getPort() == null) { builder.port(DEFAULT_PORT); } - return MODIFIED_URL_LIKE.doParse(jdbcUrl, builder).system("hsqldb").subtype("hsqls"); + return MODIFIED_URL_LIKE.doParse(jdbcUrl, builder).subtype("hsqls"); } else if (hsqlUrl.startsWith("http:")) { if (dbInfo.getPort() == null) { builder.port(80); } - return MODIFIED_URL_LIKE.doParse(jdbcUrl, builder).system("hsqldb").subtype("http"); + return MODIFIED_URL_LIKE.doParse(jdbcUrl, builder).subtype("http"); } else if (hsqlUrl.startsWith("https:")) { if (dbInfo.getPort() == null) { builder.port(443); } - return MODIFIED_URL_LIKE.doParse(jdbcUrl, builder).system("hsqldb").subtype("https"); + return MODIFIED_URL_LIKE.doParse(jdbcUrl, builder).subtype("https"); } else { builder.subtype("mem").host(null).port(null); instance = hsqlUrl; @@ -792,6 +789,35 @@ DbInfo.Builder doParse(String jdbcUrl, DbInfo.Builder builder) { } return builder.name(instance); } + }, + + DATADIRECT("datadirect", "tibcosoftware") { + @Override + DbInfo.Builder doParse(String jdbcUrl, DbInfo.Builder builder) { + int typeEndIndex = jdbcUrl.indexOf(':'); + int subtypeEndIndex = jdbcUrl.indexOf(':', typeEndIndex + 1); + + if (subtypeEndIndex == -1) { + return builder; + } + + String subtype = jdbcUrl.substring(typeEndIndex + 1, subtypeEndIndex); + builder.subtype(subtype); + + if (subtype.equals("sqlserver")) { + builder.system(DbSystemValues.MSSQL); + } else if (subtype.equals("oracle")) { + builder.system(DbSystemValues.ORACLE); + } else if (subtype.equals("mysql")) { + builder.system(DbSystemValues.MYSQL); + } else if (subtype.equals("postgresql")) { + builder.system(DbSystemValues.POSTGRESQL); + } else if (subtype.equals("db2")) { + builder.system(DbSystemValues.DB2); + } + + return MODIFIED_URL_LIKE.doParse(jdbcUrl, builder); + } }; private static final Logger logger = Logger.getLogger(JdbcConnectionUrlParser.class.getName()); @@ -969,6 +995,8 @@ private static String toDbSystem(String type) { case "microsoft": case "sqlserver": // Microsoft SQL Server return DbSystemValues.MSSQL; + case "sap": // SAP Hana + return DbSystemValues.HANADB; default: return DbSystemValues.OTHER_SQL; // Unknown DBMS } diff --git a/instrumentation/jdbc/library/src/main/java/io/opentelemetry/instrumentation/jdbc/internal/JdbcNetAttributesGetter.java b/instrumentation/jdbc/library/src/main/java/io/opentelemetry/instrumentation/jdbc/internal/JdbcNetAttributesGetter.java index 67d45f7bc7d1..f7057ad5dd31 100644 --- a/instrumentation/jdbc/library/src/main/java/io/opentelemetry/instrumentation/jdbc/internal/JdbcNetAttributesGetter.java +++ b/instrumentation/jdbc/library/src/main/java/io/opentelemetry/instrumentation/jdbc/internal/JdbcNetAttributesGetter.java @@ -22,19 +22,13 @@ public String transport(DbRequest request, @Nullable Void unused) { @Nullable @Override - public String peerName(DbRequest request, @Nullable Void unused) { + public String peerName(DbRequest request) { return request.getDbInfo().getHost(); } @Nullable @Override - public Integer peerPort(DbRequest request, @Nullable Void unused) { + public Integer peerPort(DbRequest request) { return request.getDbInfo().getPort(); } - - @Nullable - @Override - public String peerIp(DbRequest request, @Nullable Void unused) { - return null; - } } diff --git a/instrumentation/jdbc/library/src/main/java/io/opentelemetry/instrumentation/jdbc/internal/JdbcSingletons.java b/instrumentation/jdbc/library/src/main/java/io/opentelemetry/instrumentation/jdbc/internal/JdbcSingletons.java index 6d919947e4e9..a14214ed35d7 100644 --- a/instrumentation/jdbc/library/src/main/java/io/opentelemetry/instrumentation/jdbc/internal/JdbcSingletons.java +++ b/instrumentation/jdbc/library/src/main/java/io/opentelemetry/instrumentation/jdbc/internal/JdbcSingletons.java @@ -11,6 +11,7 @@ import io.opentelemetry.instrumentation.api.instrumenter.db.DbClientSpanNameExtractor; import io.opentelemetry.instrumentation.api.instrumenter.db.SqlClientAttributesExtractor; import io.opentelemetry.instrumentation.api.instrumenter.net.NetClientAttributesExtractor; +import io.opentelemetry.instrumentation.api.internal.ConfigPropertiesUtil; /** * This class is internal and is hence not for public use. Its APIs are unstable and can change at @@ -30,9 +31,14 @@ public final class JdbcSingletons { GlobalOpenTelemetry.get(), INSTRUMENTATION_NAME, DbClientSpanNameExtractor.create(dbAttributesGetter)) - .addAttributesExtractor(SqlClientAttributesExtractor.create(dbAttributesGetter)) + .addAttributesExtractor( + SqlClientAttributesExtractor.builder(dbAttributesGetter) + .setStatementSanitizationEnabled( + ConfigPropertiesUtil.getBoolean( + "otel.instrumentation.common.db-statement-sanitizer.enabled", true)) + .build()) .addAttributesExtractor(NetClientAttributesExtractor.create(netAttributesGetter)) - .newInstrumenter(SpanKindExtractor.alwaysClient()); + .buildInstrumenter(SpanKindExtractor.alwaysClient()); } public static Instrumenter instrumenter() { diff --git a/instrumentation/jdbc/library/src/test/groovy/io/opentelemetry/instrumentation/jdbc/internal/JdbcConnectionUrlParserTest.groovy b/instrumentation/jdbc/library/src/test/groovy/io/opentelemetry/instrumentation/jdbc/internal/JdbcConnectionUrlParserTest.groovy index b49b4ac74363..0e475c9603f2 100644 --- a/instrumentation/jdbc/library/src/test/groovy/io/opentelemetry/instrumentation/jdbc/internal/JdbcConnectionUrlParserTest.groovy +++ b/instrumentation/jdbc/library/src/test/groovy/io/opentelemetry/instrumentation/jdbc/internal/JdbcConnectionUrlParserTest.groovy @@ -147,9 +147,9 @@ class JdbcConnectionUrlParserTest extends Specification { "jdbc:as400://ashost:66/asdb:user=asuser;password=PW;" | null | "as400://ashost:66" | "db2" | null | "asuser" | "ashost" | 66 | "asdb" | null // https://help.sap.com/viewer/0eec0d68141541d1b07893a39944924e/2.0.03/en-US/ff15928cf5594d78b841fbbe649f04b4.html - "jdbc:sap://sap.host" | null | "sap://sap.host" | "sap" | null | null | "sap.host" | null | null | null - "jdbc:sap://sap.host" | stdProps | "sap://sap.host:9999" | "sap" | null | "stdUserName" | "sap.host" | 9999 | null | "stdDatabaseName" - "jdbc:sap://sap.host:88/?databaseName=sapdb&user=sapuser&password=PW" | null | "sap://sap.host:88" | "sap" | null | "sapuser" | "sap.host" | 88 | null | "sapdb" + "jdbc:sap://sap.host" | null | "sap://sap.host" | "hanadb" | null | null | "sap.host" | null | null | null + "jdbc:sap://sap.host" | stdProps | "sap://sap.host:9999" | "hanadb" | null | "stdUserName" | "sap.host" | 9999 | null | "stdDatabaseName" + "jdbc:sap://sap.host:88/?databaseName=sapdb&user=sapuser&password=PW" | null | "sap://sap.host:88" | "hanadb" | null | "sapuser" | "sap.host" | 88 | null | "sapdb" // TODO: // "jdbc:informix-sqli://infxhost:99/infxdb:INFORMIXSERVER=infxsn;user=infxuser;password=PW" | null | "informix-sqli" | null | "infxuser" | "infxhost" | 99 | "infxdb"| null @@ -203,6 +203,21 @@ class JdbcConnectionUrlParserTest extends Specification { "jdbc:derby:jar:/derbydb;user=derbyuser;password=pw" | null | "derby:jar:" | "derby" | "jar" | "derbyuser" | null | null | "/derbydb" | null "jdbc:derby:jar:(~/path/to/db.jar)/other/derbydb;user=derbyuser;password=pw" | null | "derby:jar:" | "derby" | "jar" | "derbyuser" | null | null | "(~/path/to/db.jar)/other/derbydb" | null + // https://docs.progress.com/bundle/datadirect-connect-jdbc-51/page/URL-Formats-DataDirect-Connect-for-JDBC-Drivers.html + "jdbc:datadirect:sqlserver://server_name:1433;DatabaseName=dbname" | null | "datadirect:sqlserver://server_name:1433" | "mssql" | "sqlserver" | null | "server_name" | 1433 | null | "dbname" + "jdbc:datadirect:oracle://server_name:1521;ServiceName=your_servicename" | null | "datadirect:oracle://server_name:1521" | "oracle" | "oracle" | null | "server_name" | 1521 | null | null + "jdbc:datadirect:mysql://server_name:3306" | null | "datadirect:mysql://server_name:3306" | "mysql" | "mysql" | null | "server_name" | 3306 | null | null + "jdbc:datadirect:postgresql://server_name:5432;DatabaseName=dbname" | null | "datadirect:postgresql://server_name:5432" | "postgresql" | "postgresql" | null | "server_name" | 5432 | null | "dbname" + "jdbc:datadirect:db2://server_name:50000;DatabaseName=dbname" | null | "datadirect:db2://server_name:50000" | "db2" | "db2" | null | "server_name" | 50000 | null | "dbname" + + // "the TIBCO JDBC drivers are based on the Progress DataDirect Connect drivers" + // https://community.jaspersoft.com/documentation/tibco-jasperreports-server-administrator-guide/v601/working-data-sources + "jdbc:tibcosoftware:sqlserver://server_name:1433;DatabaseName=dbname" | null | "tibcosoftware:sqlserver://server_name:1433" | "mssql" | "sqlserver" | null | "server_name" | 1433 | null | "dbname" + "jdbc:tibcosoftware:oracle://server_name:1521;ServiceName=your_servicename" | null | "tibcosoftware:oracle://server_name:1521" | "oracle" | "oracle" | null | "server_name" | 1521 | null | null + "jdbc:tibcosoftware:mysql://server_name:3306" | null | "tibcosoftware:mysql://server_name:3306" | "mysql" | "mysql" | null | "server_name" | 3306 | null | null + "jdbc:tibcosoftware:postgresql://server_name:5432;DatabaseName=dbname" | null | "tibcosoftware:postgresql://server_name:5432" | "postgresql" | "postgresql" | null | "server_name" | 5432 | null | "dbname" + "jdbc:tibcosoftware:db2://server_name:50000;DatabaseName=dbname" | null | "tibcosoftware:db2://server_name:50000" | "db2" | "db2" | null | "server_name" | 50000 | null | "dbname" + expected = DbInfo.builder().system(system).subtype(subtype).user(user).name(name).db(db).host(host).port(port).shortUrl(shortUrl).build() } } diff --git a/instrumentation/jedis/jedis-1.4/javaagent/build.gradle.kts b/instrumentation/jedis/jedis-1.4/javaagent/build.gradle.kts index 2e9c14a883ce..ec0d0c2027bd 100644 --- a/instrumentation/jedis/jedis-1.4/javaagent/build.gradle.kts +++ b/instrumentation/jedis/jedis-1.4/javaagent/build.gradle.kts @@ -28,5 +28,6 @@ dependencies { tasks { test { usesService(gradle.sharedServices.registrations["testcontainersBuildService"].getService()) + jvmArgs("-Dotel.instrumentation.common.db-statement-sanitizer.enabled=true") } } diff --git a/instrumentation/jedis/jedis-1.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jedis/v1_4/JedisDbAttributesGetter.java b/instrumentation/jedis/jedis-1.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jedis/v1_4/JedisDbAttributesGetter.java index 36d39562778c..a9f4874af18b 100644 --- a/instrumentation/jedis/jedis-1.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jedis/v1_4/JedisDbAttributesGetter.java +++ b/instrumentation/jedis/jedis-1.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jedis/v1_4/JedisDbAttributesGetter.java @@ -7,11 +7,15 @@ import io.opentelemetry.instrumentation.api.db.RedisCommandSanitizer; import io.opentelemetry.instrumentation.api.instrumenter.db.DbClientAttributesGetter; +import io.opentelemetry.javaagent.bootstrap.internal.CommonConfig; import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; import javax.annotation.Nullable; final class JedisDbAttributesGetter implements DbClientAttributesGetter { + private static final RedisCommandSanitizer sanitizer = + RedisCommandSanitizer.create(CommonConfig.get().isStatementSanitizationEnabled()); + @Override public String system(JedisRequest request) { return SemanticAttributes.DbSystemValues.REDIS; @@ -35,7 +39,7 @@ public String connectionString(JedisRequest request) { @Override public String statement(JedisRequest request) { - return RedisCommandSanitizer.sanitize(request.getCommand().name(), request.getArgs()); + return sanitizer.sanitize(request.getCommand().name(), request.getArgs()); } @Override diff --git a/instrumentation/jedis/jedis-1.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jedis/v1_4/JedisNetAttributesGetter.java b/instrumentation/jedis/jedis-1.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jedis/v1_4/JedisNetAttributesGetter.java index 0e0338016f7e..6d0481a4a060 100644 --- a/instrumentation/jedis/jedis-1.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jedis/v1_4/JedisNetAttributesGetter.java +++ b/instrumentation/jedis/jedis-1.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jedis/v1_4/JedisNetAttributesGetter.java @@ -17,18 +17,12 @@ public String transport(JedisRequest request, @Nullable Void unused) { } @Override - public String peerName(JedisRequest request, @Nullable Void unused) { + public String peerName(JedisRequest request) { return request.getConnection().getHost(); } @Override - public Integer peerPort(JedisRequest request, @Nullable Void unused) { + public Integer peerPort(JedisRequest request) { return request.getConnection().getPort(); } - - @Override - @Nullable - public String peerIp(JedisRequest request, @Nullable Void unused) { - return null; - } } diff --git a/instrumentation/jedis/jedis-1.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jedis/v1_4/JedisSingletons.java b/instrumentation/jedis/jedis-1.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jedis/v1_4/JedisSingletons.java index 88093618e948..4ed9529b7b3a 100644 --- a/instrumentation/jedis/jedis-1.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jedis/v1_4/JedisSingletons.java +++ b/instrumentation/jedis/jedis-1.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jedis/v1_4/JedisSingletons.java @@ -7,11 +7,12 @@ import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; -import io.opentelemetry.instrumentation.api.instrumenter.PeerServiceAttributesExtractor; import io.opentelemetry.instrumentation.api.instrumenter.SpanKindExtractor; import io.opentelemetry.instrumentation.api.instrumenter.db.DbClientAttributesExtractor; import io.opentelemetry.instrumentation.api.instrumenter.db.DbClientSpanNameExtractor; import io.opentelemetry.instrumentation.api.instrumenter.net.NetClientAttributesExtractor; +import io.opentelemetry.instrumentation.api.instrumenter.net.PeerServiceAttributesExtractor; +import io.opentelemetry.javaagent.bootstrap.internal.CommonConfig; public final class JedisSingletons { private static final String INSTRUMENTATION_NAME = "io.opentelemetry.jedis-1.4"; @@ -29,8 +30,10 @@ public final class JedisSingletons { DbClientSpanNameExtractor.create(dbAttributesGetter)) .addAttributesExtractor(DbClientAttributesExtractor.create(dbAttributesGetter)) .addAttributesExtractor(NetClientAttributesExtractor.create(netAttributesGetter)) - .addAttributesExtractor(PeerServiceAttributesExtractor.create(netAttributesGetter)) - .newInstrumenter(SpanKindExtractor.alwaysClient()); + .addAttributesExtractor( + PeerServiceAttributesExtractor.create( + netAttributesGetter, CommonConfig.get().getPeerServiceMapping())) + .buildInstrumenter(SpanKindExtractor.alwaysClient()); } public static Instrumenter instrumenter() { diff --git a/instrumentation/jedis/jedis-3.0/javaagent/build.gradle.kts b/instrumentation/jedis/jedis-3.0/javaagent/build.gradle.kts index 3a137acf13fd..e433c579877c 100644 --- a/instrumentation/jedis/jedis-3.0/javaagent/build.gradle.kts +++ b/instrumentation/jedis/jedis-3.0/javaagent/build.gradle.kts @@ -30,6 +30,7 @@ dependencies { tasks { test { + jvmArgs("-Dotel.instrumentation.common.db-statement-sanitizer.enabled=true") usesService(gradle.sharedServices.registrations["testcontainersBuildService"].getService()) } } diff --git a/instrumentation/jedis/jedis-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jedis/v3_0/JedisNetAttributesGetter.java b/instrumentation/jedis/jedis-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jedis/v3_0/JedisNetAttributesGetter.java index 395626a1d6ba..d60f986bb4e7 100644 --- a/instrumentation/jedis/jedis-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jedis/v3_0/JedisNetAttributesGetter.java +++ b/instrumentation/jedis/jedis-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jedis/v3_0/JedisNetAttributesGetter.java @@ -15,17 +15,29 @@ final class JedisNetAttributesGetter extends InetSocketAddressNetClientAttributesGetter { @Override + public String transport(JedisRequest jedisRequest, @Nullable Void unused) { + return SemanticAttributes.NetTransportValues.IP_TCP; + } + @Nullable - public InetSocketAddress getAddress(JedisRequest jedisRequest, @Nullable Void unused) { + @Override + public String peerName(JedisRequest jedisRequest) { + return jedisRequest.getConnection().getHost(); + } + + @Override + public Integer peerPort(JedisRequest jedisRequest) { + return jedisRequest.getConnection().getPort(); + } + + @Override + @Nullable + protected InetSocketAddress getPeerSocketAddress( + JedisRequest jedisRequest, @Nullable Void unused) { Socket socket = jedisRequest.getConnection().getSocket(); if (socket != null && socket.getRemoteSocketAddress() instanceof InetSocketAddress) { return (InetSocketAddress) socket.getRemoteSocketAddress(); } return null; } - - @Override - public String transport(JedisRequest jedisRequest, @Nullable Void unused) { - return SemanticAttributes.NetTransportValues.IP_TCP; - } } diff --git a/instrumentation/jedis/jedis-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jedis/v3_0/JedisRequest.java b/instrumentation/jedis/jedis-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jedis/v3_0/JedisRequest.java index 48bae7c1b70e..dd69f5c9be0e 100644 --- a/instrumentation/jedis/jedis-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jedis/v3_0/JedisRequest.java +++ b/instrumentation/jedis/jedis-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jedis/v3_0/JedisRequest.java @@ -7,6 +7,7 @@ import com.google.auto.value.AutoValue; import io.opentelemetry.instrumentation.api.db.RedisCommandSanitizer; +import io.opentelemetry.javaagent.bootstrap.internal.CommonConfig; import java.nio.charset.StandardCharsets; import java.util.List; import redis.clients.jedis.Connection; @@ -16,6 +17,9 @@ @AutoValue public abstract class JedisRequest { + private static final RedisCommandSanitizer sanitizer = + RedisCommandSanitizer.create(CommonConfig.get().isStatementSanitizationEnabled()); + public static JedisRequest create( Connection connection, ProtocolCommand command, List args) { return new AutoValue_JedisRequest(connection, command, args); @@ -39,6 +43,6 @@ public String getOperation() { } public String getStatement() { - return RedisCommandSanitizer.sanitize(getOperation(), getArgs()); + return sanitizer.sanitize(getOperation(), getArgs()); } } diff --git a/instrumentation/jedis/jedis-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jedis/v3_0/JedisSingletons.java b/instrumentation/jedis/jedis-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jedis/v3_0/JedisSingletons.java index 3c47da67bb0b..7e4d8483b000 100644 --- a/instrumentation/jedis/jedis-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jedis/v3_0/JedisSingletons.java +++ b/instrumentation/jedis/jedis-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jedis/v3_0/JedisSingletons.java @@ -7,11 +7,12 @@ import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; -import io.opentelemetry.instrumentation.api.instrumenter.PeerServiceAttributesExtractor; import io.opentelemetry.instrumentation.api.instrumenter.SpanKindExtractor; import io.opentelemetry.instrumentation.api.instrumenter.db.DbClientAttributesExtractor; import io.opentelemetry.instrumentation.api.instrumenter.db.DbClientSpanNameExtractor; import io.opentelemetry.instrumentation.api.instrumenter.net.NetClientAttributesExtractor; +import io.opentelemetry.instrumentation.api.instrumenter.net.PeerServiceAttributesExtractor; +import io.opentelemetry.javaagent.bootstrap.internal.CommonConfig; public final class JedisSingletons { private static final String INSTRUMENTATION_NAME = "io.opentelemetry.jedis-3.0"; @@ -29,8 +30,10 @@ public final class JedisSingletons { DbClientSpanNameExtractor.create(dbAttributesGetter)) .addAttributesExtractor(DbClientAttributesExtractor.create(dbAttributesGetter)) .addAttributesExtractor(NetClientAttributesExtractor.create(netAttributesGetter)) - .addAttributesExtractor(PeerServiceAttributesExtractor.create(netAttributesGetter)) - .newInstrumenter(SpanKindExtractor.alwaysClient()); + .addAttributesExtractor( + PeerServiceAttributesExtractor.create( + netAttributesGetter, CommonConfig.get().getPeerServiceMapping())) + .buildInstrumenter(SpanKindExtractor.alwaysClient()); } public static Instrumenter instrumenter() { diff --git a/instrumentation/jedis/jedis-3.0/javaagent/src/test/groovy/Jedis30ClientTest.groovy b/instrumentation/jedis/jedis-3.0/javaagent/src/test/groovy/Jedis30ClientTest.groovy index 4cdbde99b96e..4bf9bf074340 100644 --- a/instrumentation/jedis/jedis-3.0/javaagent/src/test/groovy/Jedis30ClientTest.groovy +++ b/instrumentation/jedis/jedis-3.0/javaagent/src/test/groovy/Jedis30ClientTest.groovy @@ -53,7 +53,7 @@ class Jedis30ClientTest extends AgentInstrumentationSpecification { "$SemanticAttributes.DB_OPERATION" "SET" "$SemanticAttributes.NET_PEER_NAME" "localhost" "$SemanticAttributes.NET_PEER_PORT" port - "$SemanticAttributes.NET_PEER_IP" "127.0.0.1" + "$SemanticAttributes.NET_SOCK_PEER_ADDR" "127.0.0.1" "$SemanticAttributes.NET_TRANSPORT" SemanticAttributes.NetTransportValues.IP_TCP } } @@ -80,7 +80,7 @@ class Jedis30ClientTest extends AgentInstrumentationSpecification { "$SemanticAttributes.DB_OPERATION" "SET" "$SemanticAttributes.NET_PEER_NAME" "localhost" "$SemanticAttributes.NET_PEER_PORT" port - "$SemanticAttributes.NET_PEER_IP" "127.0.0.1" + "$SemanticAttributes.NET_SOCK_PEER_ADDR" "127.0.0.1" "$SemanticAttributes.NET_TRANSPORT" SemanticAttributes.NetTransportValues.IP_TCP } } @@ -95,7 +95,7 @@ class Jedis30ClientTest extends AgentInstrumentationSpecification { "$SemanticAttributes.DB_OPERATION" "GET" "$SemanticAttributes.NET_PEER_NAME" "localhost" "$SemanticAttributes.NET_PEER_PORT" port - "$SemanticAttributes.NET_PEER_IP" "127.0.0.1" + "$SemanticAttributes.NET_SOCK_PEER_ADDR" "127.0.0.1" "$SemanticAttributes.NET_TRANSPORT" SemanticAttributes.NetTransportValues.IP_TCP } } @@ -122,7 +122,7 @@ class Jedis30ClientTest extends AgentInstrumentationSpecification { "$SemanticAttributes.DB_OPERATION" "SET" "$SemanticAttributes.NET_PEER_NAME" "localhost" "$SemanticAttributes.NET_PEER_PORT" port - "$SemanticAttributes.NET_PEER_IP" "127.0.0.1" + "$SemanticAttributes.NET_SOCK_PEER_ADDR" "127.0.0.1" "$SemanticAttributes.NET_TRANSPORT" SemanticAttributes.NetTransportValues.IP_TCP } } @@ -137,7 +137,7 @@ class Jedis30ClientTest extends AgentInstrumentationSpecification { "$SemanticAttributes.DB_OPERATION" "RANDOMKEY" "$SemanticAttributes.NET_PEER_NAME" "localhost" "$SemanticAttributes.NET_PEER_PORT" port - "$SemanticAttributes.NET_PEER_IP" "127.0.0.1" + "$SemanticAttributes.NET_SOCK_PEER_ADDR" "127.0.0.1" "$SemanticAttributes.NET_TRANSPORT" SemanticAttributes.NetTransportValues.IP_TCP } } diff --git a/instrumentation/jedis/jedis-4.0/javaagent/build.gradle.kts b/instrumentation/jedis/jedis-4.0/javaagent/build.gradle.kts index a70f9ed20404..9c06a0939605 100644 --- a/instrumentation/jedis/jedis-4.0/javaagent/build.gradle.kts +++ b/instrumentation/jedis/jedis-4.0/javaagent/build.gradle.kts @@ -28,6 +28,7 @@ tasks { test { // latest dep test fails because peer ip is 0:0:0:0:0:0:0:1 instead of 127.0.0.1 jvmArgs("-Djava.net.preferIPv4Stack=true") + jvmArgs("-Dotel.instrumentation.common.db-statement-sanitizer.enabled=true") usesService(gradle.sharedServices.registrations["testcontainersBuildService"].getService()) } } diff --git a/instrumentation/jedis/jedis-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jedis/v4_0/JedisNetAttributesGetter.java b/instrumentation/jedis/jedis-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jedis/v4_0/JedisNetAttributesGetter.java index 873daf066ec0..e5e877aff650 100644 --- a/instrumentation/jedis/jedis-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jedis/v4_0/JedisNetAttributesGetter.java +++ b/instrumentation/jedis/jedis-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jedis/v4_0/JedisNetAttributesGetter.java @@ -15,17 +15,30 @@ final class JedisNetAttributesGetter extends InetSocketAddressNetClientAttributesGetter { @Override + public String transport(JedisRequest jedisRequest, @Nullable Void unused) { + return SemanticAttributes.NetTransportValues.IP_TCP; + } + @Nullable - public InetSocketAddress getAddress(JedisRequest jedisRequest, @Nullable Void unused) { - SocketAddress socketAddress = jedisRequest.getRemoteSocketAddress(); - if (socketAddress != null && socketAddress instanceof InetSocketAddress) { - return (InetSocketAddress) socketAddress; - } + @Override + public String peerName(JedisRequest jedisRequest) { return null; } + @Nullable @Override - public String transport(JedisRequest jedisRequest, @Nullable Void unused) { - return SemanticAttributes.NetTransportValues.IP_TCP; + public Integer peerPort(JedisRequest jedisRequest) { + return null; + } + + @Override + @Nullable + protected InetSocketAddress getPeerSocketAddress( + JedisRequest jedisRequest, @Nullable Void unused) { + SocketAddress socketAddress = jedisRequest.getRemoteSocketAddress(); + if (socketAddress instanceof InetSocketAddress) { + return (InetSocketAddress) socketAddress; + } + return null; } } diff --git a/instrumentation/jedis/jedis-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jedis/v4_0/JedisRequest.java b/instrumentation/jedis/jedis-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jedis/v4_0/JedisRequest.java index 098094259249..6de09a4ea1cb 100644 --- a/instrumentation/jedis/jedis-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jedis/v4_0/JedisRequest.java +++ b/instrumentation/jedis/jedis-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jedis/v4_0/JedisRequest.java @@ -7,6 +7,7 @@ import com.google.auto.value.AutoValue; import io.opentelemetry.instrumentation.api.db.RedisCommandSanitizer; +import io.opentelemetry.javaagent.bootstrap.internal.CommonConfig; import java.net.Socket; import java.net.SocketAddress; import java.nio.charset.StandardCharsets; @@ -20,6 +21,9 @@ @AutoValue public abstract class JedisRequest { + private static final RedisCommandSanitizer sanitizer = + RedisCommandSanitizer.create(CommonConfig.get().isStatementSanitizationEnabled()); + public static JedisRequest create(ProtocolCommand command, List args) { return new AutoValue_JedisRequest(command, args); } @@ -54,7 +58,7 @@ public String getOperation() { } public String getStatement() { - return RedisCommandSanitizer.sanitize(getOperation(), getArgs()); + return sanitizer.sanitize(getOperation(), getArgs()); } private SocketAddress remoteSocketAddress; diff --git a/instrumentation/jedis/jedis-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jedis/v4_0/JedisSingletons.java b/instrumentation/jedis/jedis-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jedis/v4_0/JedisSingletons.java index 58fb8391d005..0e9c43c5f1ea 100644 --- a/instrumentation/jedis/jedis-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jedis/v4_0/JedisSingletons.java +++ b/instrumentation/jedis/jedis-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jedis/v4_0/JedisSingletons.java @@ -7,11 +7,12 @@ import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; -import io.opentelemetry.instrumentation.api.instrumenter.PeerServiceAttributesExtractor; import io.opentelemetry.instrumentation.api.instrumenter.SpanKindExtractor; import io.opentelemetry.instrumentation.api.instrumenter.db.DbClientAttributesExtractor; import io.opentelemetry.instrumentation.api.instrumenter.db.DbClientSpanNameExtractor; import io.opentelemetry.instrumentation.api.instrumenter.net.NetClientAttributesExtractor; +import io.opentelemetry.instrumentation.api.instrumenter.net.PeerServiceAttributesExtractor; +import io.opentelemetry.javaagent.bootstrap.internal.CommonConfig; public final class JedisSingletons { private static final String INSTRUMENTATION_NAME = "io.opentelemetry.jedis-4.0"; @@ -29,8 +30,10 @@ public final class JedisSingletons { DbClientSpanNameExtractor.create(dbAttributesGetter)) .addAttributesExtractor(DbClientAttributesExtractor.create(dbAttributesGetter)) .addAttributesExtractor(NetClientAttributesExtractor.create(netAttributesGetter)) - .addAttributesExtractor(PeerServiceAttributesExtractor.create(netAttributesGetter)) - .newInstrumenter(SpanKindExtractor.alwaysClient()); + .addAttributesExtractor( + PeerServiceAttributesExtractor.create( + netAttributesGetter, CommonConfig.get().getPeerServiceMapping())) + .buildInstrumenter(SpanKindExtractor.alwaysClient()); } public static Instrumenter instrumenter() { diff --git a/instrumentation/jedis/jedis-4.0/javaagent/src/test/groovy/Jedis40ClientTest.groovy b/instrumentation/jedis/jedis-4.0/javaagent/src/test/groovy/Jedis40ClientTest.groovy index 585f548b7d71..7d2fef8195cb 100644 --- a/instrumentation/jedis/jedis-4.0/javaagent/src/test/groovy/Jedis40ClientTest.groovy +++ b/instrumentation/jedis/jedis-4.0/javaagent/src/test/groovy/Jedis40ClientTest.groovy @@ -24,7 +24,7 @@ class Jedis40ClientTest extends AgentInstrumentationSpecification { def setupSpec() { redisServer.start() port = redisServer.getMappedPort(6379) - jedis = new Jedis("127.0.0.1", port) + jedis = new Jedis("localhost", port) } def cleanupSpec() { @@ -51,9 +51,10 @@ class Jedis40ClientTest extends AgentInstrumentationSpecification { "$SemanticAttributes.DB_SYSTEM" "redis" "$SemanticAttributes.DB_STATEMENT" "SET foo ?" "$SemanticAttributes.DB_OPERATION" "SET" - "$SemanticAttributes.NET_PEER_PORT" port - "$SemanticAttributes.NET_PEER_IP" "127.0.0.1" "$SemanticAttributes.NET_TRANSPORT" SemanticAttributes.NetTransportValues.IP_TCP + "$SemanticAttributes.NET_SOCK_PEER_ADDR" "127.0.0.1" + "$SemanticAttributes.NET_SOCK_PEER_NAME" { it == "localhost" || it == "127.0.0.1" } + "$SemanticAttributes.NET_SOCK_PEER_PORT" port } } } @@ -77,9 +78,10 @@ class Jedis40ClientTest extends AgentInstrumentationSpecification { "$SemanticAttributes.DB_SYSTEM" "redis" "$SemanticAttributes.DB_STATEMENT" "SET foo ?" "$SemanticAttributes.DB_OPERATION" "SET" - "$SemanticAttributes.NET_PEER_PORT" port - "$SemanticAttributes.NET_PEER_IP" "127.0.0.1" "$SemanticAttributes.NET_TRANSPORT" SemanticAttributes.NetTransportValues.IP_TCP + "$SemanticAttributes.NET_SOCK_PEER_ADDR" "127.0.0.1" + "$SemanticAttributes.NET_SOCK_PEER_NAME" { it == "localhost" || it == "127.0.0.1" } + "$SemanticAttributes.NET_SOCK_PEER_PORT" port } } } @@ -91,9 +93,10 @@ class Jedis40ClientTest extends AgentInstrumentationSpecification { "$SemanticAttributes.DB_SYSTEM" "redis" "$SemanticAttributes.DB_STATEMENT" "GET foo" "$SemanticAttributes.DB_OPERATION" "GET" - "$SemanticAttributes.NET_PEER_PORT" port - "$SemanticAttributes.NET_PEER_IP" "127.0.0.1" "$SemanticAttributes.NET_TRANSPORT" SemanticAttributes.NetTransportValues.IP_TCP + "$SemanticAttributes.NET_SOCK_PEER_ADDR" "127.0.0.1" + "$SemanticAttributes.NET_SOCK_PEER_NAME" { it == "localhost" || it == "127.0.0.1" } + "$SemanticAttributes.NET_SOCK_PEER_PORT" port } } } @@ -117,9 +120,10 @@ class Jedis40ClientTest extends AgentInstrumentationSpecification { "$SemanticAttributes.DB_SYSTEM" "redis" "$SemanticAttributes.DB_STATEMENT" "SET foo ?" "$SemanticAttributes.DB_OPERATION" "SET" - "$SemanticAttributes.NET_PEER_PORT" port - "$SemanticAttributes.NET_PEER_IP" "127.0.0.1" "$SemanticAttributes.NET_TRANSPORT" SemanticAttributes.NetTransportValues.IP_TCP + "$SemanticAttributes.NET_SOCK_PEER_ADDR" "127.0.0.1" + "$SemanticAttributes.NET_SOCK_PEER_NAME" { it == "localhost" || it == "127.0.0.1" } + "$SemanticAttributes.NET_SOCK_PEER_PORT" port } } } @@ -131,9 +135,10 @@ class Jedis40ClientTest extends AgentInstrumentationSpecification { "$SemanticAttributes.DB_SYSTEM" "redis" "$SemanticAttributes.DB_STATEMENT" "RANDOMKEY" "$SemanticAttributes.DB_OPERATION" "RANDOMKEY" - "$SemanticAttributes.NET_PEER_PORT" port - "$SemanticAttributes.NET_PEER_IP" "127.0.0.1" "$SemanticAttributes.NET_TRANSPORT" SemanticAttributes.NetTransportValues.IP_TCP + "$SemanticAttributes.NET_SOCK_PEER_ADDR" "127.0.0.1" + "$SemanticAttributes.NET_SOCK_PEER_NAME" { it == "localhost" || it == "127.0.0.1" } + "$SemanticAttributes.NET_SOCK_PEER_PORT" port } } } diff --git a/instrumentation/jetty-httpclient/jetty-httpclient-9.2/javaagent/build.gradle.kts b/instrumentation/jetty-httpclient/jetty-httpclient-9.2/javaagent/build.gradle.kts index e5acf4edd795..00e213117361 100644 --- a/instrumentation/jetty-httpclient/jetty-httpclient-9.2/javaagent/build.gradle.kts +++ b/instrumentation/jetty-httpclient/jetty-httpclient-9.2/javaagent/build.gradle.kts @@ -17,7 +17,8 @@ dependencies { implementation(project(":instrumentation:jetty-httpclient:jetty-httpclient-9.2:library")) library("org.eclipse.jetty:jetty-client:$jettyVers_base9") - latestDepTestLibrary("org.eclipse.jetty:jetty-client:9.+") testImplementation(project(":instrumentation:jetty-httpclient:jetty-httpclient-9.2:testing")) + + latestDepTestLibrary("org.eclipse.jetty:jetty-client:9.+") // documented limitation } diff --git a/instrumentation/jetty-httpclient/jetty-httpclient-9.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jetty/httpclient/v9_2/JettyHttpClientSingletons.java b/instrumentation/jetty-httpclient/jetty-httpclient-9.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jetty/httpclient/v9_2/JettyHttpClientSingletons.java index 7c3db813cef6..8107767b319c 100644 --- a/instrumentation/jetty-httpclient/jetty-httpclient-9.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jetty/httpclient/v9_2/JettyHttpClientSingletons.java +++ b/instrumentation/jetty-httpclient/jetty-httpclient-9.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jetty/httpclient/v9_2/JettyHttpClientSingletons.java @@ -7,28 +7,28 @@ import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; -import io.opentelemetry.instrumentation.api.instrumenter.PeerServiceAttributesExtractor; +import io.opentelemetry.instrumentation.api.instrumenter.net.PeerServiceAttributesExtractor; import io.opentelemetry.instrumentation.jetty.httpclient.v9_2.internal.JettyClientInstrumenterBuilder; import io.opentelemetry.instrumentation.jetty.httpclient.v9_2.internal.JettyHttpClientNetAttributesGetter; +import io.opentelemetry.javaagent.bootstrap.internal.CommonConfig; import org.eclipse.jetty.client.api.Request; import org.eclipse.jetty.client.api.Response; public class JettyHttpClientSingletons { - private static final Instrumenter INSTRUMENTER; - - private JettyHttpClientSingletons() {} - - static { - JettyClientInstrumenterBuilder builder = - new JettyClientInstrumenterBuilder(GlobalOpenTelemetry.get()); - - PeerServiceAttributesExtractor peerServiceAttributesExtractor = - PeerServiceAttributesExtractor.create(new JettyHttpClientNetAttributesGetter()); - INSTRUMENTER = builder.addAttributeExtractor(peerServiceAttributesExtractor).build(); - } + private static final Instrumenter INSTRUMENTER = + new JettyClientInstrumenterBuilder(GlobalOpenTelemetry.get()) + .addAttributeExtractor( + PeerServiceAttributesExtractor.create( + new JettyHttpClientNetAttributesGetter(), + CommonConfig.get().getPeerServiceMapping())) + .setCapturedRequestHeaders(CommonConfig.get().getClientRequestHeaders()) + .setCapturedResponseHeaders(CommonConfig.get().getClientResponseHeaders()) + .build(); public static Instrumenter instrumenter() { return INSTRUMENTER; } + + private JettyHttpClientSingletons() {} } diff --git a/instrumentation/jetty-httpclient/jetty-httpclient-9.2/library/src/main/java/io/opentelemetry/instrumentation/jetty/httpclient/v9_2/JettyClientTelemetryBuilder.java b/instrumentation/jetty-httpclient/jetty-httpclient-9.2/library/src/main/java/io/opentelemetry/instrumentation/jetty/httpclient/v9_2/JettyClientTelemetryBuilder.java index e67c9495d940..ada867f212a3 100644 --- a/instrumentation/jetty-httpclient/jetty-httpclient-9.2/library/src/main/java/io/opentelemetry/instrumentation/jetty/httpclient/v9_2/JettyClientTelemetryBuilder.java +++ b/instrumentation/jetty-httpclient/jetty-httpclient-9.2/library/src/main/java/io/opentelemetry/instrumentation/jetty/httpclient/v9_2/JettyClientTelemetryBuilder.java @@ -5,6 +5,7 @@ package io.opentelemetry.instrumentation.jetty.httpclient.v9_2; +import com.google.errorprone.annotations.CanIgnoreReturnValue; import io.opentelemetry.api.OpenTelemetry; import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor; import io.opentelemetry.instrumentation.jetty.httpclient.v9_2.internal.JettyClientInstrumenterBuilder; @@ -25,12 +26,14 @@ public final class JettyClientTelemetryBuilder { instrumenterBuilder = new JettyClientInstrumenterBuilder(openTelemetry); } + @CanIgnoreReturnValue public JettyClientTelemetryBuilder setHttpClientTransport( HttpClientTransport httpClientTransport) { this.httpClientTransport = httpClientTransport; return this; } + @CanIgnoreReturnValue public JettyClientTelemetryBuilder setSslContextFactory(SslContextFactory sslContextFactory) { this.sslContextFactory = sslContextFactory; return this; @@ -40,6 +43,7 @@ public JettyClientTelemetryBuilder setSslContextFactory(SslContextFactory sslCon * Adds an additional {@link AttributesExtractor} to invoke to set attributes to instrumented * items. */ + @CanIgnoreReturnValue public JettyClientTelemetryBuilder addAttributeExtractor( AttributesExtractor attributesExtractor) { instrumenterBuilder.addAttributeExtractor(attributesExtractor); @@ -51,6 +55,7 @@ public JettyClientTelemetryBuilder addAttributeExtractor( * * @param requestHeaders A list of HTTP header names. */ + @CanIgnoreReturnValue public JettyClientTelemetryBuilder setCapturedRequestHeaders(List requestHeaders) { instrumenterBuilder.setCapturedRequestHeaders(requestHeaders); return this; @@ -61,6 +66,7 @@ public JettyClientTelemetryBuilder setCapturedRequestHeaders(List reques * * @param responseHeaders A list of HTTP header names. */ + @CanIgnoreReturnValue public JettyClientTelemetryBuilder setCapturedResponseHeaders(List responseHeaders) { instrumenterBuilder.setCapturedResponseHeaders(responseHeaders); return this; diff --git a/instrumentation/jetty-httpclient/jetty-httpclient-9.2/library/src/main/java/io/opentelemetry/instrumentation/jetty/httpclient/v9_2/internal/JettyClientHttpAttributesGetter.java b/instrumentation/jetty-httpclient/jetty-httpclient-9.2/library/src/main/java/io/opentelemetry/instrumentation/jetty/httpclient/v9_2/internal/JettyClientHttpAttributesGetter.java index fc76c58376bf..cbef35f70605 100644 --- a/instrumentation/jetty-httpclient/jetty-httpclient-9.2/library/src/main/java/io/opentelemetry/instrumentation/jetty/httpclient/v9_2/internal/JettyClientHttpAttributesGetter.java +++ b/instrumentation/jetty-httpclient/jetty-httpclient-9.2/library/src/main/java/io/opentelemetry/instrumentation/jetty/httpclient/v9_2/internal/JettyClientHttpAttributesGetter.java @@ -8,24 +8,17 @@ import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.HttpFlavorValues.HTTP_1_0; import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.HttpFlavorValues.HTTP_1_1; import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.HttpFlavorValues.HTTP_2_0; -import static java.util.logging.Level.FINE; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpClientAttributesGetter; import java.util.List; -import java.util.logging.Logger; import javax.annotation.Nullable; import org.eclipse.jetty.client.api.Request; import org.eclipse.jetty.client.api.Response; -import org.eclipse.jetty.http.HttpField; -import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.HttpVersion; enum JettyClientHttpAttributesGetter implements HttpClientAttributesGetter { INSTANCE; - private static final Logger logger = - Logger.getLogger(JettyClientHttpAttributesGetter.class.getName()); - @Override @Nullable public String method(Request request) { @@ -43,19 +36,6 @@ public List requestHeader(Request request, String name) { return request.getHeaders().getValuesList(name); } - @Override - @Nullable - public Long requestContentLength(Request request, @Nullable Response response) { - HttpField requestContentLengthField = request.getHeaders().getField(HttpHeader.CONTENT_LENGTH); - return getLongFromJettyHttpField(requestContentLengthField); - } - - @Override - @Nullable - public Long requestContentLengthUncompressed(Request request, @Nullable Response response) { - return null; - } - @Override public String flavor(Request request, @Nullable Response response) { @@ -81,45 +61,12 @@ public String flavor(Request request, @Nullable Response response) { } @Override - public Integer statusCode(Request request, Response response) { + public Integer statusCode(Request request, Response response, @Nullable Throwable error) { return response.getStatus(); } - @Override - @Nullable - public Long responseContentLength(Request request, Response response) { - Long respContentLength = null; - if (response != null) { - HttpField requestContentLengthField = - response.getHeaders().getField(HttpHeader.CONTENT_LENGTH); - respContentLength = getLongFromJettyHttpField(requestContentLengthField); - } - return respContentLength; - } - - @Override - @Nullable - public Long responseContentLengthUncompressed(Request request, Response response) { - return null; - } - @Override public List responseHeader(Request request, Response response, String name) { return response.getHeaders().getValuesList(name); } - - private static Long getLongFromJettyHttpField(HttpField httpField) { - Long longFromField = null; - try { - longFromField = httpField != null ? Long.getLong(httpField.getValue()) : null; - } catch (NumberFormatException t) { - if (logger.isLoggable(FINE)) { - logger.log( - FINE, - "Value {0} is not valid number format for header field: {1}", - new Object[] {httpField.getValue(), httpField.getName()}); - } - } - return longFromField; - } } diff --git a/instrumentation/jetty-httpclient/jetty-httpclient-9.2/library/src/main/java/io/opentelemetry/instrumentation/jetty/httpclient/v9_2/internal/JettyClientInstrumenterBuilder.java b/instrumentation/jetty-httpclient/jetty-httpclient-9.2/library/src/main/java/io/opentelemetry/instrumentation/jetty/httpclient/v9_2/internal/JettyClientInstrumenterBuilder.java index 0ab53152ec8e..0f8cff69cfd8 100644 --- a/instrumentation/jetty-httpclient/jetty-httpclient-9.2/library/src/main/java/io/opentelemetry/instrumentation/jetty/httpclient/v9_2/internal/JettyClientInstrumenterBuilder.java +++ b/instrumentation/jetty-httpclient/jetty-httpclient-9.2/library/src/main/java/io/opentelemetry/instrumentation/jetty/httpclient/v9_2/internal/JettyClientInstrumenterBuilder.java @@ -5,6 +5,7 @@ package io.opentelemetry.instrumentation.jetty.httpclient.v9_2.internal; +import com.google.errorprone.annotations.CanIgnoreReturnValue; import io.opentelemetry.api.OpenTelemetry; import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; @@ -39,17 +40,20 @@ public JettyClientInstrumenterBuilder(OpenTelemetry openTelemetry) { this.openTelemetry = openTelemetry; } + @CanIgnoreReturnValue public JettyClientInstrumenterBuilder addAttributeExtractor( AttributesExtractor attributesExtractor) { additionalExtractors.add(attributesExtractor); return this; } + @CanIgnoreReturnValue public JettyClientInstrumenterBuilder setCapturedRequestHeaders(List requestHeaders) { httpAttributesExtractorBuilder.setCapturedRequestHeaders(requestHeaders); return this; } + @CanIgnoreReturnValue public JettyClientInstrumenterBuilder setCapturedResponseHeaders(List responseHeaders) { httpAttributesExtractorBuilder.setCapturedResponseHeaders(responseHeaders); return this; @@ -69,6 +73,6 @@ public Instrumenter build() { .addAttributesExtractor(NetClientAttributesExtractor.create(netAttributesGetter)) .addAttributesExtractors(additionalExtractors) .addOperationMetrics(HttpClientMetrics.get()) - .newClientInstrumenter(HttpHeaderSetter.INSTANCE); + .buildClientInstrumenter(HttpHeaderSetter.INSTANCE); } } diff --git a/instrumentation/jetty-httpclient/jetty-httpclient-9.2/library/src/main/java/io/opentelemetry/instrumentation/jetty/httpclient/v9_2/internal/JettyHttpClient9TracingInterceptor.java b/instrumentation/jetty-httpclient/jetty-httpclient-9.2/library/src/main/java/io/opentelemetry/instrumentation/jetty/httpclient/v9_2/internal/JettyHttpClient9TracingInterceptor.java index 2ecae543e444..4749877afda6 100644 --- a/instrumentation/jetty-httpclient/jetty-httpclient-9.2/library/src/main/java/io/opentelemetry/instrumentation/jetty/httpclient/v9_2/internal/JettyHttpClient9TracingInterceptor.java +++ b/instrumentation/jetty-httpclient/jetty-httpclient-9.2/library/src/main/java/io/opentelemetry/instrumentation/jetty/httpclient/v9_2/internal/JettyHttpClient9TracingInterceptor.java @@ -110,7 +110,7 @@ private void wrapRequestListeners(List requestListeners continue; } - Request.RequestListener proxiedListner = + Request.RequestListener proxiedListener = (Request.RequestListener) Proxy.newProxyInstance( listenerClass.getClassLoader(), @@ -121,7 +121,7 @@ private void wrapRequestListeners(List requestListeners } }); - iterator.set(proxiedListner); + iterator.set(proxiedListener); } } diff --git a/instrumentation/jetty-httpclient/jetty-httpclient-9.2/library/src/main/java/io/opentelemetry/instrumentation/jetty/httpclient/v9_2/internal/JettyHttpClientNetAttributesGetter.java b/instrumentation/jetty-httpclient/jetty-httpclient-9.2/library/src/main/java/io/opentelemetry/instrumentation/jetty/httpclient/v9_2/internal/JettyHttpClientNetAttributesGetter.java index 7b52e61c9011..e0469fc65765 100644 --- a/instrumentation/jetty-httpclient/jetty-httpclient-9.2/library/src/main/java/io/opentelemetry/instrumentation/jetty/httpclient/v9_2/internal/JettyHttpClientNetAttributesGetter.java +++ b/instrumentation/jetty-httpclient/jetty-httpclient-9.2/library/src/main/java/io/opentelemetry/instrumentation/jetty/httpclient/v9_2/internal/JettyHttpClientNetAttributesGetter.java @@ -25,21 +25,13 @@ public String transport(Request request, @Nullable Response response) { @Override @Nullable - public String peerName(Request request, @Nullable Response response) { + public String peerName(Request request) { return request.getHost(); } @Override @Nullable - public Integer peerPort(Request request, @Nullable Response response) { + public Integer peerPort(Request request) { return request.getPort(); } - - @Override - @Nullable - public String peerIp(Request request, @Nullable Response response) { - // Return null unless the library supports resolution to something similar to SocketAddress - // https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/3012/files#r633188645 - return null; - } } diff --git a/instrumentation/jetty-httpclient/jetty-httpclient-9.2/testing/src/main/groovy/io/opentelemetry/instrumentation/jetty/httpclient/v9_2/AbstractJettyClient9Test.groovy b/instrumentation/jetty-httpclient/jetty-httpclient-9.2/testing/src/main/groovy/io/opentelemetry/instrumentation/jetty/httpclient/v9_2/AbstractJettyClient9Test.groovy index 320b42feb50b..f464a90b2c05 100644 --- a/instrumentation/jetty-httpclient/jetty-httpclient-9.2/testing/src/main/groovy/io/opentelemetry/instrumentation/jetty/httpclient/v9_2/AbstractJettyClient9Test.groovy +++ b/instrumentation/jetty-httpclient/jetty-httpclient-9.2/testing/src/main/groovy/io/opentelemetry/instrumentation/jetty/httpclient/v9_2/AbstractJettyClient9Test.groovy @@ -124,7 +124,6 @@ abstract class AbstractJettyClient9Test extends HttpClientTest { Set> extra = [ SemanticAttributes.HTTP_SCHEME, SemanticAttributes.HTTP_TARGET, - SemanticAttributes.HTTP_HOST ] super.httpAttributes(uri) + extra } diff --git a/instrumentation/jetty/jetty-8.0/javaagent/src/test/java/JavaLambdaMaker.java b/instrumentation/jetty/jetty-8.0/javaagent/src/test/java/JavaLambdaMaker.java index b7c4840d2e67..c620351ebce7 100644 --- a/instrumentation/jetty/jetty-8.0/javaagent/src/test/java/JavaLambdaMaker.java +++ b/instrumentation/jetty/jetty-8.0/javaagent/src/test/java/JavaLambdaMaker.java @@ -9,4 +9,6 @@ public class JavaLambdaMaker { public static Runnable lambda(Runnable runnable) { return runnable::run; } + + private JavaLambdaMaker() {} } diff --git a/instrumentation/jetty/jetty-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jetty/common/JettyHelper.java b/instrumentation/jetty/jetty-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jetty/common/JettyHelper.java index 865ede7b7509..aef567e3f9a6 100644 --- a/instrumentation/jetty/jetty-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jetty/common/JettyHelper.java +++ b/instrumentation/jetty/jetty-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jetty/common/JettyHelper.java @@ -40,8 +40,7 @@ public void end( throwable = errorException(request); } - ServletResponseContext responseContext = - new ServletResponseContext<>(response, throwable); + ServletResponseContext responseContext = new ServletResponseContext<>(response); if (throwable != null || mustEndOnHandlerMethodExit(request)) { instrumenter.end(context, requestContext, responseContext, throwable); } diff --git a/instrumentation/jms-1.1/javaagent-unit-tests/src/test/java/io/opentelemetry/javaagent/instrumentation/jms/MessageWithDestinationTest.java b/instrumentation/jms-1.1/javaagent-unit-tests/src/test/java/io/opentelemetry/javaagent/instrumentation/jms/MessageWithDestinationTest.java index bd8f7333fc6b..f8ef42544c7e 100644 --- a/instrumentation/jms-1.1/javaagent-unit-tests/src/test/java/io/opentelemetry/javaagent/instrumentation/jms/MessageWithDestinationTest.java +++ b/instrumentation/jms-1.1/javaagent-unit-tests/src/test/java/io/opentelemetry/javaagent/instrumentation/jms/MessageWithDestinationTest.java @@ -8,7 +8,7 @@ import static io.opentelemetry.javaagent.instrumentation.jms.MessageWithDestination.TIBCO_TMP_PREFIX; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertSame; -import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.when; import java.util.stream.Stream; import javax.jms.Destination; @@ -39,7 +39,7 @@ class MessageWithDestinationTest { @Test void shouldCreateMessageWithUnknownDestination() throws JMSException { // given - given(message.getJMSDestination()).willReturn(destination); + when(message.getJMSDestination()).thenReturn(destination); // when MessageWithDestination result = MessageWithDestination.create(message, null); @@ -51,7 +51,7 @@ void shouldCreateMessageWithUnknownDestination() throws JMSException { @Test void shouldUseFallbackDestinationToCreateMessage() throws JMSException { // given - given(message.getJMSDestination()).willThrow(JMSException.class); + when(message.getJMSDestination()).thenThrow(JMSException.class); // when MessageWithDestination result = MessageWithDestination.create(message, destination); @@ -71,11 +71,11 @@ void shouldCreateMessageWithQueue( // given Queue queue = useTemporaryDestination ? this.temporaryQueue : this.queue; - given(message.getJMSDestination()).willReturn(queue); + when(message.getJMSDestination()).thenReturn(queue); if (queueName == null) { - given(queue.getQueueName()).willThrow(JMSException.class); + when(queue.getQueueName()).thenThrow(JMSException.class); } else { - given(queue.getQueueName()).willReturn(queueName); + when(queue.getQueueName()).thenReturn(queueName); } // when @@ -96,11 +96,11 @@ void shouldCreateMessageWithTopic( // given Topic topic = useTemporaryDestination ? this.temporaryTopic : this.topic; - given(message.getJMSDestination()).willReturn(topic); + when(message.getJMSDestination()).thenReturn(topic); if (topicName == null) { - given(topic.getTopicName()).willThrow(JMSException.class); + when(topic.getTopicName()).thenThrow(JMSException.class); } else { - given(topic.getTopicName()).willReturn(topicName); + when(topic.getTopicName()).thenReturn(topicName); } // when diff --git a/instrumentation/jms-1.1/javaagent/build.gradle.kts b/instrumentation/jms-1.1/javaagent/build.gradle.kts index 01c32df6c8b9..e88c2b36abfd 100644 --- a/instrumentation/jms-1.1/javaagent/build.gradle.kts +++ b/instrumentation/jms-1.1/javaagent/build.gradle.kts @@ -18,60 +18,34 @@ muzzle { testSets { create("jms2Test") - create("jms2TestReceiveSpansDisabled") { - extendsFrom("jms2Test") - } } tasks { - val testReceiveSpansDisabled by registering(Test::class) { - filter { - includeTestsMatching("SpringListenerJms1SuppressReceiveSpansTest") - } - include("**/SpringListenerJms1SuppressReceiveSpansTest.*") - } - val jms2Test by existing(Test::class) { jvmArgs("-Dotel.instrumentation.messaging.experimental.receive-telemetry.enabled=true") } - val jms2TestReceiveSpansDisabled by existing - test { - usesService(gradle.sharedServices.registrations["testcontainersBuildService"].getService()) - filter { - excludeTestsMatching("SpringListenerJms1SuppressReceiveSpansTest") - } + usesService(gradle.sharedServices.registrations["testcontainersBuildService"].service) jvmArgs("-Dotel.instrumentation.messaging.experimental.receive-telemetry.enabled=true") } check { - dependsOn(testReceiveSpansDisabled) dependsOn(jms2Test) - dependsOn(jms2TestReceiveSpansDisabled) } } -val versions: Map by project - dependencies { compileOnly("com.google.auto.value:auto-value-annotations") annotationProcessor("com.google.auto.value:auto-value") compileOnly("javax.jms:jms-api:1.1-rev-1") - testImplementation("javax.annotation:javax.annotation-api:1.3.2") - testImplementation("org.springframework.boot:spring-boot-starter-activemq:${versions["org.springframework.boot"]}") - testImplementation("org.springframework.boot:spring-boot-starter-test:${versions["org.springframework.boot"]}") { - exclude("org.junit.vintage", "junit-vintage-engine") - } + testImplementation("org.apache.activemq:activemq-client:5.16.5") add("jms2TestImplementation", "org.hornetq:hornetq-jms-client:2.4.7.Final") add("jms2TestImplementation", "org.hornetq:hornetq-jms-server:2.4.7.Final") { // this doesn't exist in maven central, and doesn't seem to be needed anyways exclude("org.jboss.naming", "jnpserver") } - - // this is just to avoid a bit more copy-pasting - add("jms2TestReceiveSpansDisabledImplementation", sourceSets["jms2Test"].output) } diff --git a/instrumentation/jms-1.1/javaagent/src/jms2Test/groovy/Jms2Test.groovy b/instrumentation/jms-1.1/javaagent/src/jms2Test/groovy/Jms2Test.groovy index c252901af95c..c2850647fe7f 100644 --- a/instrumentation/jms-1.1/javaagent/src/jms2Test/groovy/Jms2Test.groovy +++ b/instrumentation/jms-1.1/javaagent/src/jms2Test/groovy/Jms2Test.groovy @@ -3,7 +3,6 @@ * SPDX-License-Identifier: Apache-2.0 */ -import com.google.common.io.Files import io.opentelemetry.instrumentation.test.AgentInstrumentationSpecification import io.opentelemetry.instrumentation.test.asserts.TraceAssert import io.opentelemetry.sdk.trace.data.SpanData @@ -26,6 +25,7 @@ import javax.jms.Message import javax.jms.MessageListener import javax.jms.Session import javax.jms.TextMessage +import java.nio.file.Files import java.util.concurrent.CountDownLatch import java.util.concurrent.atomic.AtomicReference @@ -43,7 +43,7 @@ class Jms2Test extends AgentInstrumentationSpecification { HornetQTextMessage message = session.createTextMessage(messageText) def setupSpec() { - def tempDir = Files.createTempDir() + def tempDir = Files.createTempDirectory("jmsTempDir").toFile() tempDir.deleteOnExit() Configuration config = new ConfigurationImpl() @@ -86,19 +86,34 @@ class Jms2Test extends AgentInstrumentationSpecification { def producer = session.createProducer(destination) def consumer = session.createConsumer(destination) - producer.send(message) + runWithSpan("producer parent") { + producer.send(message) + } - TextMessage receivedMessage = consumer.receive() + TextMessage receivedMessage = runWithSpan("consumer parent") { + return consumer.receive() as TextMessage + } String messageId = receivedMessage.getJMSMessageID() expect: receivedMessage.text == messageText assertTraces(2) { - trace(0, 1) { - producerSpan(it, 0, destinationType, destinationName) + SpanData producerSpanData + trace(0, 2) { + span(0) { + name "producer parent" + hasNoParent() + } + producerSpan(it, 1, destinationType, destinationName, span(0)) + + producerSpanData = span(1) } - trace(1, 1) { - consumerSpan(it, 0, destinationType, destinationName, messageId, null, "receive") + trace(1, 2) { + span(0) { + name "consumer parent" + hasNoParent() + } + consumerSpan(it, 1, destinationType, destinationName, messageId, "receive", span(0), producerSpanData) } } @@ -124,18 +139,24 @@ class Jms2Test extends AgentInstrumentationSpecification { @Override void onMessage(Message message) { lock.await() // ensure the producer trace is reported first. - messageRef.set(message) + messageRef.set(message as TextMessage) } } - producer.send(message) + runWithSpan("parent") { + producer.send(message) + } lock.countDown() expect: assertTraces(1) { - trace(0, 2) { - producerSpan(it, 0, destinationType, destinationName) - consumerSpan(it, 1, destinationType, destinationName, messageRef.get().getJMSMessageID(), span(0), "process") + trace(0, 3) { + span(0) { + name "parent" + hasNoParent() + } + producerSpan(it, 1, destinationType, destinationName, span(0)) + consumerSpan(it, 2, destinationType, destinationName, messageRef.get().getJMSMessageID(), "process", span(1)) } } // This check needs to go after all traces have been accounted for @@ -158,7 +179,7 @@ class Jms2Test extends AgentInstrumentationSpecification { def consumer = session.createConsumer(destination) // Receive with timeout - TextMessage receivedMessage = consumer.receiveNoWait() + Message receivedMessage = consumer.receiveNoWait() expect: receivedMessage == null @@ -179,7 +200,7 @@ class Jms2Test extends AgentInstrumentationSpecification { def consumer = session.createConsumer(destination) // Receive with timeout - TextMessage receivedMessage = consumer.receive(100) + Message receivedMessage = consumer.receive(100) expect: receivedMessage == null @@ -206,19 +227,25 @@ class Jms2Test extends AgentInstrumentationSpecification { @Override void onMessage(Message message) { lock.await() // ensure the producer trace is reported first. - messageRef.set(message) + messageRef.set(message as TextMessage) } } when: - producer.send(destination, message) + runWithSpan("parent") { + producer.send(destination, message) + } lock.countDown() then: assertTraces(1) { - trace(0, 2) { - producerSpan(it, 0, destinationType, destinationName) - consumerSpan(it, 1, destinationType, destinationName, messageRef.get().getJMSMessageID(), span(0), "process") + trace(0, 3) { + span(0) { + name "parent" + hasNoParent() + } + producerSpan(it, 1, destinationType, destinationName, span(0)) + consumerSpan(it, 2, destinationType, destinationName, messageRef.get().getJMSMessageID(), "process", span(1)) } } // This check needs to go after all traces have been accounted for @@ -236,11 +263,15 @@ class Jms2Test extends AgentInstrumentationSpecification { session.createTemporaryTopic() | "topic" | "(temporary)" } - static producerSpan(TraceAssert trace, int index, String destinationType, String destinationName) { + static producerSpan(TraceAssert trace, int index, String destinationType, String destinationName, SpanData parentSpan = null) { trace.span(index) { name destinationName + " send" kind PRODUCER - hasNoParent() + if (parentSpan == null) { + hasNoParent() + } else { + childOf(parentSpan) + } attributes { "$SemanticAttributes.MESSAGING_SYSTEM" "jms" "$SemanticAttributes.MESSAGING_DESTINATION" destinationName @@ -256,14 +287,19 @@ class Jms2Test extends AgentInstrumentationSpecification { // passing messageId = null will verify message.id is not captured, // passing messageId = "" will verify message.id is captured (but won't verify anything about the value), // any other value for messageId will verify that message.id is captured and has that same value - static consumerSpan(TraceAssert trace, int index, String destinationType, String destinationName, String messageId, Object parentOrLinkedSpan, String operation) { + static consumerSpan(TraceAssert trace, int index, String destinationType, String destinationName, String messageId, String operation, SpanData parentSpan, SpanData linkedSpan = null) { trace.span(index) { name destinationName + " " + operation kind CONSUMER - if (parentOrLinkedSpan != null) { - childOf((SpanData) parentOrLinkedSpan) - } else { + if (parentSpan == null) { hasNoParent() + } else { + childOf(parentSpan) + } + if (linkedSpan == null) { + hasNoLinks() + } else { + hasLink(linkedSpan) } attributes { "$SemanticAttributes.MESSAGING_SYSTEM" "jms" diff --git a/instrumentation/jms-1.1/javaagent/src/jms2Test/groovy/SpringListenerJms2Test.groovy b/instrumentation/jms-1.1/javaagent/src/jms2Test/groovy/SpringListenerJms2Test.groovy deleted file mode 100644 index b4d2ec746ee4..000000000000 --- a/instrumentation/jms-1.1/javaagent/src/jms2Test/groovy/SpringListenerJms2Test.groovy +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -import io.opentelemetry.instrumentation.test.AgentInstrumentationSpecification -import listener.Config -import org.springframework.context.annotation.AnnotationConfigApplicationContext -import org.springframework.jms.core.JmsTemplate - -import javax.jms.ConnectionFactory - -import static Jms2Test.consumerSpan -import static Jms2Test.producerSpan -import static io.opentelemetry.api.trace.SpanKind.CONSUMER -import static io.opentelemetry.api.trace.SpanKind.PRODUCER - -class SpringListenerJms2Test extends AgentInstrumentationSpecification { - def "receiving message in spring listener generates spans"() { - setup: - def context = new AnnotationConfigApplicationContext(Config) - def factory = context.getBean(ConnectionFactory) - def template = new JmsTemplate(factory) - - template.convertAndSend("SpringListenerJms2", "a message") - - expect: - assertTraces(2) { - traces.sort(orderByRootSpanKind(CONSUMER, PRODUCER)) - - trace(0, 1) { - consumerSpan(it, 0, "queue", "SpringListenerJms2", "", null, "receive") - } - trace(1, 2) { - producerSpan(it, 0, "queue", "SpringListenerJms2") - consumerSpan(it, 1, "queue", "SpringListenerJms2", "", span(0), "process") - } - } - - cleanup: - context.close() - } -} diff --git a/instrumentation/jms-1.1/javaagent/src/jms2Test/groovy/listener/TestListener.groovy b/instrumentation/jms-1.1/javaagent/src/jms2Test/groovy/listener/TestListener.groovy deleted file mode 100644 index 229dc0b907a4..000000000000 --- a/instrumentation/jms-1.1/javaagent/src/jms2Test/groovy/listener/TestListener.groovy +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package listener - -import org.springframework.jms.annotation.JmsListener -import org.springframework.stereotype.Component - -@Component -class TestListener { - - @JmsListener(destination = "SpringListenerJms2", containerFactory = "containerFactory") - void receiveMessage(String message) { - println "received: " + message - } -} diff --git a/instrumentation/jms-1.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jms/JmsIgnoredTypesConfigurer.java b/instrumentation/jms-1.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jms/JmsIgnoredTypesConfigurer.java index d8aeb398354d..2ec0d282b7ea 100644 --- a/instrumentation/jms-1.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jms/JmsIgnoredTypesConfigurer.java +++ b/instrumentation/jms-1.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jms/JmsIgnoredTypesConfigurer.java @@ -14,7 +14,7 @@ public class JmsIgnoredTypesConfigurer implements IgnoredTypesConfigurer { @Override - public void configure(ConfigProperties config, IgnoredTypesBuilder builder) { + public void configure(IgnoredTypesBuilder builder, ConfigProperties config) { // Avoid instrumenting internal OrderedExecutor worker class builder.ignoreTaskClass("org.hornetq.utils.OrderedExecutorFactory$OrderedExecutor$"); } diff --git a/instrumentation/jms-1.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jms/JmsMessageAttributesGetter.java b/instrumentation/jms-1.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jms/JmsMessageAttributesGetter.java index e19d40c854d0..92268352e7cf 100644 --- a/instrumentation/jms-1.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jms/JmsMessageAttributesGetter.java +++ b/instrumentation/jms-1.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jms/JmsMessageAttributesGetter.java @@ -8,11 +8,14 @@ import static java.util.logging.Level.FINE; import io.opentelemetry.instrumentation.api.instrumenter.messaging.MessagingAttributesGetter; +import java.util.Collections; +import java.util.List; import java.util.logging.Logger; import javax.annotation.Nullable; import javax.jms.JMSException; -enum JmsMessageAttributesGetter implements MessagingAttributesGetter { +public enum JmsMessageAttributesGetter + implements MessagingAttributesGetter { INSTANCE; private static final Logger logger = Logger.getLogger(JmsMessageAttributesGetter.class.getName()); @@ -85,9 +88,28 @@ public Long messagePayloadCompressedSize(MessageWithDestination messageWithDesti public String messageId(MessageWithDestination messageWithDestination, Void unused) { try { return messageWithDestination.message().getJMSMessageID(); - } catch (JMSException e) { - logger.log(FINE, "Failure getting JMS message id", e); + } catch (JMSException exception) { + logger.log(FINE, "Failure getting JMS message id", exception); return null; } } + + @Override + public List header(MessageWithDestination messageWithDestination, String name) { + try { + String value = messageWithDestination.message().getStringProperty(name); + if (value != null) { + return Collections.singletonList(value); + } + } catch (JMSException exception) { + logger.log(FINE, "Failure getting JMS message header", exception); + } + return Collections.emptyList(); + } + + @Nullable + @Override + public String messagePayload(MessageWithDestination messageWithDestination) { + return null; + } } diff --git a/instrumentation/jms-1.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jms/JmsMessageConsumerInstrumentation.java b/instrumentation/jms-1.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jms/JmsMessageConsumerInstrumentation.java index c204ee6be7e5..337b872d9b8e 100644 --- a/instrumentation/jms-1.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jms/JmsMessageConsumerInstrumentation.java +++ b/instrumentation/jms-1.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jms/JmsMessageConsumerInstrumentation.java @@ -10,6 +10,7 @@ import static io.opentelemetry.javaagent.instrumentation.jms.JmsSingletons.consumerInstrumenter; import static net.bytebuddy.matcher.ElementMatchers.isPublic; import static net.bytebuddy.matcher.ElementMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.returns; import static net.bytebuddy.matcher.ElementMatchers.takesArguments; import io.opentelemetry.context.Context; @@ -37,10 +38,16 @@ public ElementMatcher typeMatcher() { @Override public void transform(TypeTransformer transformer) { transformer.applyAdviceToMethod( - named("receive").and(takesArguments(0).or(takesArguments(1))).and(isPublic()), + named("receive") + .and(takesArguments(0).or(takesArguments(1))) + .and(returns(named("javax.jms.Message"))) + .and(isPublic()), JmsMessageConsumerInstrumentation.class.getName() + "$ConsumerAdvice"); transformer.applyAdviceToMethod( - named("receiveNoWait").and(takesArguments(0)).and(isPublic()), + named("receiveNoWait") + .and(takesArguments(0)) + .and(returns(named("javax.jms.Message"))) + .and(isPublic()), JmsMessageConsumerInstrumentation.class.getName() + "$ConsumerAdvice"); } diff --git a/instrumentation/jms-1.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jms/JmsSingletons.java b/instrumentation/jms-1.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jms/JmsSingletons.java index 859a4d87bd74..6cd1c0803e22 100644 --- a/instrumentation/jms-1.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jms/JmsSingletons.java +++ b/instrumentation/jms-1.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jms/JmsSingletons.java @@ -10,7 +10,9 @@ import io.opentelemetry.instrumentation.api.instrumenter.SpanKindExtractor; import io.opentelemetry.instrumentation.api.instrumenter.messaging.MessageOperation; import io.opentelemetry.instrumentation.api.instrumenter.messaging.MessagingAttributesExtractor; +import io.opentelemetry.instrumentation.api.instrumenter.messaging.MessagingAttributesGetter; import io.opentelemetry.instrumentation.api.instrumenter.messaging.MessagingSpanNameExtractor; +import io.opentelemetry.instrumentation.api.internal.PropagatorBasedSpanLinksExtractor; import io.opentelemetry.javaagent.bootstrap.internal.ExperimentalConfig; public final class JmsSingletons { @@ -31,8 +33,8 @@ private static Instrumenter buildProducerInstrumen GlobalOpenTelemetry.get(), INSTRUMENTATION_NAME, MessagingSpanNameExtractor.create(getter, operation)) - .addAttributesExtractor(MessagingAttributesExtractor.create(getter, operation)) - .newProducerInstrumenter(MessagePropertySetter.INSTANCE); + .addAttributesExtractor(buildMessagingAttributesExtractor(getter, operation)) + .buildProducerInstrumenter(MessagePropertySetter.INSTANCE); } private static Instrumenter buildConsumerInstrumenter() { @@ -44,9 +46,13 @@ private static Instrumenter buildConsumerInstrumen GlobalOpenTelemetry.get(), INSTRUMENTATION_NAME, MessagingSpanNameExtractor.create(getter, operation)) - .addAttributesExtractor(MessagingAttributesExtractor.create(getter, operation)) + .addAttributesExtractor(buildMessagingAttributesExtractor(getter, operation)) .setEnabled(ExperimentalConfig.get().messagingReceiveInstrumentationEnabled()) - .newInstrumenter(SpanKindExtractor.alwaysConsumer()); + .addSpanLinksExtractor( + new PropagatorBasedSpanLinksExtractor<>( + GlobalOpenTelemetry.getPropagators().getTextMapPropagator(), + MessagePropertyGetter.INSTANCE)) + .buildInstrumenter(SpanKindExtractor.alwaysConsumer()); } private static Instrumenter buildListenerInstrumenter() { @@ -57,8 +63,17 @@ private static Instrumenter buildListenerInstrumen GlobalOpenTelemetry.get(), INSTRUMENTATION_NAME, MessagingSpanNameExtractor.create(getter, operation)) - .addAttributesExtractor(MessagingAttributesExtractor.create(getter, operation)) - .newConsumerInstrumenter(MessagePropertyGetter.INSTANCE); + .addAttributesExtractor(buildMessagingAttributesExtractor(getter, operation)) + .buildConsumerInstrumenter(MessagePropertyGetter.INSTANCE); + } + + private static MessagingAttributesExtractor + buildMessagingAttributesExtractor( + MessagingAttributesGetter getter, + MessageOperation operation) { + return MessagingAttributesExtractor.builder(getter, operation) + .setCapturedHeaders(ExperimentalConfig.get().getMessagingHeaders()) + .build(); } public static Instrumenter producerInstrumenter() { diff --git a/instrumentation/jms-1.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jms/MessagePropertyGetter.java b/instrumentation/jms-1.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jms/MessagePropertyGetter.java index b997e7f64819..f56c869ad66f 100644 --- a/instrumentation/jms-1.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jms/MessagePropertyGetter.java +++ b/instrumentation/jms-1.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jms/MessagePropertyGetter.java @@ -9,7 +9,7 @@ import java.util.Collections; import javax.jms.JMSException; -enum MessagePropertyGetter implements TextMapGetter { +public enum MessagePropertyGetter implements TextMapGetter { INSTANCE; @Override diff --git a/instrumentation/jms-1.1/javaagent/src/test/groovy/Jms1Test.groovy b/instrumentation/jms-1.1/javaagent/src/test/groovy/Jms1Test.groovy index cb55a4407e33..d181f815626e 100644 --- a/instrumentation/jms-1.1/javaagent/src/test/groovy/Jms1Test.groovy +++ b/instrumentation/jms-1.1/javaagent/src/test/groovy/Jms1Test.groovy @@ -61,19 +61,34 @@ class Jms1Test extends AgentInstrumentationSpecification { def producer = session.createProducer(destination) def consumer = session.createConsumer(destination) - producer.send(message) + runWithSpan("producer parent") { + producer.send(message) + } - TextMessage receivedMessage = consumer.receive() + TextMessage receivedMessage = runWithSpan("consumer parent") { + return consumer.receive() as TextMessage + } String messageId = receivedMessage.getJMSMessageID() expect: receivedMessage.text == messageText assertTraces(2) { - trace(0, 1) { - producerSpan(it, 0, destinationType, destinationName) + SpanData producerSpanData + trace(0, 2) { + span(0) { + name "producer parent" + hasNoParent() + } + producerSpan(it, 1, destinationType, destinationName, span(0)) + + producerSpanData = span(1) } - trace(1, 1) { - consumerSpan(it, 0, destinationType, destinationName, messageId, null, "receive") + trace(1, 2) { + span(0) { + name "consumer parent" + hasNoParent() + } + consumerSpan(it, 1, destinationType, destinationName, messageId, "receive", span(0), producerSpanData) } } @@ -99,7 +114,7 @@ class Jms1Test extends AgentInstrumentationSpecification { @Override void onMessage(Message message) { lock.await() // ensure the producer trace is reported first. - messageRef.set(message) + messageRef.set(message as TextMessage) } } @@ -110,7 +125,7 @@ class Jms1Test extends AgentInstrumentationSpecification { assertTraces(1) { trace(0, 2) { producerSpan(it, 0, destinationType, destinationName) - consumerSpan(it, 1, destinationType, destinationName, messageRef.get().getJMSMessageID(), span(0), "process") + consumerSpan(it, 1, destinationType, destinationName, messageRef.get().getJMSMessageID(), "process", span(0)) } } // This check needs to go after all traces have been accounted for @@ -133,7 +148,7 @@ class Jms1Test extends AgentInstrumentationSpecification { def consumer = session.createConsumer(destination) // Receive with timeout - TextMessage receivedMessage = consumer.receiveNoWait() + Message receivedMessage = consumer.receiveNoWait() expect: receivedMessage == null @@ -154,7 +169,7 @@ class Jms1Test extends AgentInstrumentationSpecification { def consumer = session.createConsumer(destination) // Receive with timeout - TextMessage receivedMessage = consumer.receive(100) + Message receivedMessage = consumer.receive(100) expect: receivedMessage == null @@ -183,7 +198,7 @@ class Jms1Test extends AgentInstrumentationSpecification { and: producer.send(message) - TextMessage receivedMessage = consumer.receive() + TextMessage receivedMessage = consumer.receive() as TextMessage then: receivedMessage.text == messageText @@ -196,21 +211,7 @@ class Jms1Test extends AgentInstrumentationSpecification { producerSpan(it, 0, destinationType, destinationName) } trace(1, 1) { - span(0) { - hasNoParent() - name destinationName + " receive" - kind CONSUMER - attributes { - "$SemanticAttributes.MESSAGING_SYSTEM" "jms" - "$SemanticAttributes.MESSAGING_DESTINATION" destinationName - "$SemanticAttributes.MESSAGING_DESTINATION_KIND" destinationType - "$SemanticAttributes.MESSAGING_MESSAGE_ID" receivedMessage.getJMSMessageID() - "$SemanticAttributes.MESSAGING_OPERATION" "receive" - if (destinationName == "(temporary)") { - "$SemanticAttributes.MESSAGING_TEMP_DESTINATION" true - } - } - } + consumerSpan(it, 0, destinationType, destinationName, "", "receive", null) } } @@ -237,19 +238,25 @@ class Jms1Test extends AgentInstrumentationSpecification { @Override void onMessage(Message message) { lock.await() // ensure the producer trace is reported first. - messageRef.set(message) + messageRef.set(message as TextMessage) } } when: - producer.send(destination, message) + runWithSpan("parent") { + producer.send(destination, message) + } lock.countDown() then: assertTraces(1) { - trace(0, 2) { - producerSpan(it, 0, destinationType, destinationName) - consumerSpan(it, 1, destinationType, destinationName, messageRef.get().getJMSMessageID(), span(0), "process") + trace(0, 3) { + span(0) { + name "parent" + hasNoParent() + } + producerSpan(it, 1, destinationType, destinationName, span(0)) + consumerSpan(it, 2, destinationType, destinationName, messageRef.get().getJMSMessageID(), "process", span(1)) } } // This check needs to go after all traces have been accounted for @@ -267,11 +274,62 @@ class Jms1Test extends AgentInstrumentationSpecification { session.createTemporaryTopic() | "topic" | "(temporary)" } - static producerSpan(TraceAssert trace, int index, String destinationType, String destinationName) { + def "capture message header as span attribute"() { + setup: + def destinationName = "someQueue" + def destinationType = "queue" + def destination = session.createQueue(destinationName) + def producer = session.createProducer(destination) + def consumer = session.createConsumer(destination) + + def message = session.createTextMessage(messageText) + message.setStringProperty("test-message-header", "test") + message.setIntProperty("test-message-int-header", 1234) + runWithSpan("producer parent") { + producer.send(message) + } + + TextMessage receivedMessage = runWithSpan("consumer parent") { + return consumer.receive() as TextMessage + } + String messageId = receivedMessage.getJMSMessageID() + + expect: + receivedMessage.text == messageText + assertTraces(2) { + SpanData producerSpanData + trace(0, 2) { + span(0) { + name "producer parent" + hasNoParent() + } + producerSpan(it, 1, destinationType, destinationName, span(0), true) + + producerSpanData = span(1) + } + trace(1, 2) { + span(0) { + name "consumer parent" + hasNoParent() + } + consumerSpan(it, 1, destinationType, destinationName, messageId, "receive", span(0), producerSpanData, true) + } + } + + cleanup: + producer.close() + consumer.close() + } + + static producerSpan(TraceAssert trace, int index, String destinationType, String destinationName, SpanData parentSpan = null, boolean testHeaders = false) { trace.span(index) { name destinationName + " send" kind PRODUCER - hasNoParent() + if (parentSpan == null) { + hasNoParent() + } else { + childOf(parentSpan) + } attributes { "$SemanticAttributes.MESSAGING_SYSTEM" "jms" "$SemanticAttributes.MESSAGING_DESTINATION" destinationName @@ -280,6 +338,10 @@ class Jms1Test extends AgentInstrumentationSpecification { "$SemanticAttributes.MESSAGING_TEMP_DESTINATION" true } "$SemanticAttributes.MESSAGING_MESSAGE_ID" String + if (testHeaders) { + "messaging.header.test_message_header" { it == ["test"] } + "messaging.header.test_message_int_header" { it == ["1234"] } + } } } } @@ -287,14 +349,19 @@ class Jms1Test extends AgentInstrumentationSpecification { // passing messageId = null will verify message.id is not captured, // passing messageId = "" will verify message.id is captured (but won't verify anything about the value), // any other value for messageId will verify that message.id is captured and has that same value - static consumerSpan(TraceAssert trace, int index, String destinationType, String destinationName, String messageId, Object parentOrLinkedSpan, String operation) { + static consumerSpan(TraceAssert trace, int index, String destinationType, String destinationName, String messageId, String operation, SpanData parentSpan, SpanData linkedSpan = null, boolean testHeaders = false) { trace.span(index) { name destinationName + " " + operation kind CONSUMER - if (parentOrLinkedSpan != null) { - childOf((SpanData) parentOrLinkedSpan) - } else { + if (parentSpan == null) { hasNoParent() + } else { + childOf(parentSpan) + } + if (linkedSpan == null) { + hasNoLinks() + } else { + hasLink(linkedSpan) } attributes { "$SemanticAttributes.MESSAGING_SYSTEM" "jms" @@ -308,6 +375,10 @@ class Jms1Test extends AgentInstrumentationSpecification { if (destinationName == "(temporary)") { "$SemanticAttributes.MESSAGING_TEMP_DESTINATION" true } + if (testHeaders) { + "messaging.header.test_message_header" { it == ["test"] } + "messaging.header.test_message_int_header" { it == ["1234"] } + } } } } diff --git a/instrumentation/jms-1.1/javaagent/src/test/groovy/SpringListenerJms1SuppressReceiveSpansTest.groovy b/instrumentation/jms-1.1/javaagent/src/test/groovy/SpringListenerJms1SuppressReceiveSpansTest.groovy deleted file mode 100644 index 7597a34738d3..000000000000 --- a/instrumentation/jms-1.1/javaagent/src/test/groovy/SpringListenerJms1SuppressReceiveSpansTest.groovy +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -import io.opentelemetry.instrumentation.test.AgentInstrumentationSpecification -import listener.Config -import org.springframework.context.annotation.AnnotationConfigApplicationContext -import org.springframework.jms.core.JmsTemplate - -import javax.jms.ConnectionFactory - -import static Jms1Test.consumerSpan -import static Jms1Test.producerSpan - -class SpringListenerJms1SuppressReceiveSpansTest extends AgentInstrumentationSpecification { - - def "receiving message in spring listener generates spans"() { - setup: - def context = new AnnotationConfigApplicationContext(Config) - def factory = context.getBean(ConnectionFactory) - def template = new JmsTemplate(factory) - - template.convertAndSend("SpringListenerJms1", "a message") - - expect: - assertTraces(1) { - trace(0, 2) { - producerSpan(it, 0, "queue", "SpringListenerJms1") - consumerSpan(it, 1, "queue", "SpringListenerJms1", "", span(0), "process") - } - } - - cleanup: - context.stop() - } -} diff --git a/instrumentation/jms-1.1/javaagent/src/test/groovy/SpringListenerJms1Test.groovy b/instrumentation/jms-1.1/javaagent/src/test/groovy/SpringListenerJms1Test.groovy deleted file mode 100644 index 013a34dba506..000000000000 --- a/instrumentation/jms-1.1/javaagent/src/test/groovy/SpringListenerJms1Test.groovy +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -import io.opentelemetry.instrumentation.test.AgentInstrumentationSpecification -import listener.Config -import org.springframework.context.annotation.AnnotationConfigApplicationContext -import org.springframework.jms.core.JmsTemplate - -import javax.jms.ConnectionFactory - -import static Jms1Test.consumerSpan -import static Jms1Test.producerSpan -import static io.opentelemetry.api.trace.SpanKind.CONSUMER -import static io.opentelemetry.api.trace.SpanKind.PRODUCER - -class SpringListenerJms1Test extends AgentInstrumentationSpecification { - - def "receiving message in spring listener generates spans"() { - setup: - def context = new AnnotationConfigApplicationContext(Config) - def factory = context.getBean(ConnectionFactory) - def template = new JmsTemplate(factory) - - template.convertAndSend("SpringListenerJms1", "a message") - - expect: - assertTraces(2) { - traces.sort(orderByRootSpanKind(CONSUMER, PRODUCER)) - - trace(0, 1) { - consumerSpan(it, 0, "queue", "SpringListenerJms1", "", null, "receive") - } - trace(1, 2) { - producerSpan(it, 0, "queue", "SpringListenerJms1") - consumerSpan(it, 1, "queue", "SpringListenerJms1", "", span(0), "process") - } - } - - cleanup: - context.stop() - } -} diff --git a/instrumentation/jms-1.1/javaagent/src/test/groovy/SpringTemplateJms1Test.groovy b/instrumentation/jms-1.1/javaagent/src/test/groovy/SpringTemplateJms1Test.groovy deleted file mode 100644 index 05d8ec949632..000000000000 --- a/instrumentation/jms-1.1/javaagent/src/test/groovy/SpringTemplateJms1Test.groovy +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -import io.opentelemetry.instrumentation.test.AgentInstrumentationSpecification -import org.apache.activemq.ActiveMQConnectionFactory -import org.slf4j.Logger -import org.slf4j.LoggerFactory -import org.springframework.jms.core.JmsTemplate -import org.testcontainers.containers.GenericContainer -import org.testcontainers.containers.output.Slf4jLogConsumer -import spock.lang.Shared - -import javax.jms.Connection -import javax.jms.Session -import javax.jms.TextMessage -import java.util.concurrent.TimeUnit -import java.util.concurrent.atomic.AtomicReference - -import static Jms1Test.consumerSpan -import static Jms1Test.producerSpan - -class SpringTemplateJms1Test extends AgentInstrumentationSpecification { - private static final Logger logger = LoggerFactory.getLogger("io.opentelemetry.SpringTemplateJms1Test") - - private static final GenericContainer broker = new GenericContainer("rmohr/activemq:latest") - .withExposedPorts(61616, 8161) - .withLogConsumer(new Slf4jLogConsumer(logger)) - - @Shared - String messageText = "a message" - @Shared - JmsTemplate template - @Shared - Session session - - def setupSpec() { - broker.start() - ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory("tcp://localhost:" + broker.getMappedPort(61616)) - // to avoid InvalidDestinationException in "send and receive message generates spans" - // see https://issues.apache.org/jira/browse/AMQ-6155 - connectionFactory.setWatchTopicAdvisories(false) - Connection connection = connectionFactory.createConnection() - connection.start() - session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE) - - template = new JmsTemplate(connectionFactory) - // Make this longer than timeout on testWriter.waitForTraces - // Otherwise caller might give up waiting before callee has a chance to respond. - template.receiveTimeout = TimeUnit.SECONDS.toMillis(21) - } - - def cleanupSpec() { - broker.stop() - } - - def "sending a message to #destinationName generates spans"() { - setup: - template.convertAndSend(destination, messageText) - TextMessage receivedMessage = template.receive(destination) - - expect: - receivedMessage.text == messageText - assertTraces(2) { - trace(0, 1) { - producerSpan(it, 0, destinationType, destinationName) - } - trace(1, 1) { - consumerSpan(it, 0, destinationType, destinationName, receivedMessage.getJMSMessageID(), null, "receive") - } - } - - where: - destination | destinationType | destinationName - session.createQueue("SpringTemplateJms1") | "queue" | "SpringTemplateJms1" - } - - def "send and receive message generates spans"() { - setup: - AtomicReference msgId = new AtomicReference<>() - Thread.start { - logger.info("calling receive") - TextMessage msg = template.receive(destination) - assert msg.text == messageText - msgId.set(msg.getJMSMessageID()) - - logger.info("calling send") - template.send(msg.getJMSReplyTo()) { - session -> template.getMessageConverter().toMessage("responded!", session) - } - } - logger.info("calling sendAndReceive") - def receivedMessage = template.sendAndReceive(destination) { - session -> template.getMessageConverter().toMessage(messageText, session) - } - logger.info("received message " + receivedMessage) - - expect: - receivedMessage != null - receivedMessage.text == "responded!" - assertTraces(4) { - traces.sort(orderByRootSpanName( - "$destinationName receive", - "$destinationName send", - "(temporary) receive", - "(temporary) send")) - - trace(0, 1) { - consumerSpan(it, 0, destinationType, destinationName, msgId.get(), null, "receive") - } - trace(1, 1) { - producerSpan(it, 0, destinationType, destinationName) - } - trace(2, 1) { - consumerSpan(it, 0, "queue", "(temporary)", receivedMessage.getJMSMessageID(), null, "receive") - } - trace(3, 1) { - // receive doesn't propagate the trace, so this is a root - producerSpan(it, 0, "queue", "(temporary)") - } - } - - where: - destination | destinationType | destinationName - session.createQueue("SpringTemplateJms1") | "queue" | "SpringTemplateJms1" - } -} diff --git a/instrumentation/jms-1.1/javaagent/src/test/groovy/listener/Config.groovy b/instrumentation/jms-1.1/javaagent/src/test/groovy/listener/Config.groovy deleted file mode 100644 index bbc888c682e2..000000000000 --- a/instrumentation/jms-1.1/javaagent/src/test/groovy/listener/Config.groovy +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package listener - -import org.apache.activemq.ActiveMQConnectionFactory -import org.springframework.context.annotation.Bean -import org.springframework.context.annotation.ComponentScan -import org.springframework.context.annotation.Configuration -import org.springframework.jms.annotation.EnableJms -import org.springframework.jms.config.DefaultJmsListenerContainerFactory -import org.springframework.jms.config.JmsListenerContainerFactory -import org.testcontainers.containers.GenericContainer -import org.testcontainers.containers.wait.strategy.Wait - -import javax.annotation.PreDestroy -import javax.jms.ConnectionFactory -import java.time.Duration - -@Configuration -@ComponentScan -@EnableJms -class Config { - - private static GenericContainer broker = new GenericContainer("rmohr/activemq:latest") - .withExposedPorts(61616, 8161) - .waitingFor(Wait.forLogMessage(".*Apache ActiveMQ .* started.*", 1)) - .withStartupTimeout(Duration.ofMinutes(2)) - - static { - broker.start() - } - - @Bean - ConnectionFactory connectionFactory() { - return new ActiveMQConnectionFactory("tcp://localhost:" + broker.getMappedPort(61616)) - } - - @Bean - JmsListenerContainerFactory containerFactory(ConnectionFactory connectionFactory) { - DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory() - factory.setConnectionFactory(connectionFactory) - return factory - } - - @PreDestroy - void destroy() { - broker.stop() - } -} diff --git a/instrumentation/jsf/jsf-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jsf/JsfErrorCauseExtractor.java b/instrumentation/jsf/jsf-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jsf/JsfErrorCauseExtractor.java index c6f9d1280a73..598c49f7d4d8 100644 --- a/instrumentation/jsf/jsf-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jsf/JsfErrorCauseExtractor.java +++ b/instrumentation/jsf/jsf-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jsf/JsfErrorCauseExtractor.java @@ -15,6 +15,6 @@ public Throwable extract(Throwable error) { while (error.getCause() != null && error instanceof FacesException) { error = error.getCause(); } - return ErrorCauseExtractor.jdk().extract(error); + return ErrorCauseExtractor.getDefault().extract(error); } } diff --git a/instrumentation/jsf/jsf-common/testing/src/main/groovy/BaseJsfTest.groovy b/instrumentation/jsf/jsf-common/testing/src/main/groovy/BaseJsfTest.groovy index 836785c27d10..0960b1c2d3be 100644 --- a/instrumentation/jsf/jsf-common/testing/src/main/groovy/BaseJsfTest.groovy +++ b/instrumentation/jsf/jsf-common/testing/src/main/groovy/BaseJsfTest.groovy @@ -98,11 +98,13 @@ abstract class BaseJsfTest extends AgentInstrumentationSpecification implements hasNoParent() attributes { "$SemanticAttributes.NET_TRANSPORT" SemanticAttributes.NetTransportValues.IP_TCP - "$SemanticAttributes.NET_PEER_PORT" Long - "$SemanticAttributes.NET_PEER_IP" "127.0.0.1" + "$SemanticAttributes.NET_HOST_NAME" "localhost" + "$SemanticAttributes.NET_HOST_PORT" port + "$SemanticAttributes.NET_SOCK_PEER_ADDR" "127.0.0.1" + "$SemanticAttributes.NET_SOCK_PEER_PORT" Long + "$SemanticAttributes.NET_SOCK_HOST_ADDR" "127.0.0.1" "$SemanticAttributes.HTTP_METHOD" "GET" "$SemanticAttributes.HTTP_SCHEME" "http" - "$SemanticAttributes.HTTP_HOST" { it == "localhost" || it == "localhost:$port" } "$SemanticAttributes.HTTP_TARGET" "/jetty-context/" + path "$SemanticAttributes.HTTP_USER_AGENT" TEST_USER_AGENT "$SemanticAttributes.HTTP_FLAVOR" SemanticAttributes.HttpFlavorValues.HTTP_1_1 diff --git a/instrumentation/jsf/jsf-mojarra-1.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/mojarra/MojarraSingletons.java b/instrumentation/jsf/jsf-mojarra-1.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/mojarra/MojarraSingletons.java index 5287962391b3..395e8654cc5c 100644 --- a/instrumentation/jsf/jsf-mojarra-1.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/mojarra/MojarraSingletons.java +++ b/instrumentation/jsf/jsf-mojarra-1.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/mojarra/MojarraSingletons.java @@ -22,7 +22,7 @@ public class MojarraSingletons { GlobalOpenTelemetry.get(), INSTRUMENTATION_NAME, JsfRequest::spanName) .setErrorCauseExtractor(new JsfErrorCauseExtractor()) .setEnabled(ExperimentalConfig.get().controllerTelemetryEnabled()) - .newInstrumenter(); + .buildInstrumenter(); } public static Instrumenter instrumenter() { diff --git a/instrumentation/jsf/jsf-myfaces-1.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/myfaces/MyFacesSingletons.java b/instrumentation/jsf/jsf-myfaces-1.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/myfaces/MyFacesSingletons.java index 36e1b6072029..8da9a51d3880 100644 --- a/instrumentation/jsf/jsf-myfaces-1.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/myfaces/MyFacesSingletons.java +++ b/instrumentation/jsf/jsf-myfaces-1.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/myfaces/MyFacesSingletons.java @@ -21,7 +21,7 @@ public class MyFacesSingletons { GlobalOpenTelemetry.get(), INSTRUMENTATION_NAME, JsfRequest::spanName) .setErrorCauseExtractor(new MyFacesErrorCauseExtractor()) .setEnabled(ExperimentalConfig.get().controllerTelemetryEnabled()) - .newInstrumenter(); + .buildInstrumenter(); } public static Instrumenter instrumenter() { diff --git a/instrumentation/jsp-2.3/javaagent/build.gradle.kts b/instrumentation/jsp-2.3/javaagent/build.gradle.kts index 50ece458df48..915e510e50a3 100644 --- a/instrumentation/jsp-2.3/javaagent/build.gradle.kts +++ b/instrumentation/jsp-2.3/javaagent/build.gradle.kts @@ -46,6 +46,7 @@ tasks.withType().configureEach { // required on jdk17 jvmArgs("--add-opens=java.rmi/sun.rmi.transport=ALL-UNNAMED") + jvmArgs("--add-opens=java.base/java.util=ALL-UNNAMED") jvmArgs("-XX:+IgnoreUnrecognizedVMOptions") // TODO run tests both with and without experimental span attributes diff --git a/instrumentation/jsp-2.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jsp/HttpJspPageInstrumentationSingletons.java b/instrumentation/jsp-2.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jsp/HttpJspPageInstrumentationSingletons.java index e3af13282014..301657c1d69a 100644 --- a/instrumentation/jsp-2.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jsp/HttpJspPageInstrumentationSingletons.java +++ b/instrumentation/jsp-2.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jsp/HttpJspPageInstrumentationSingletons.java @@ -36,7 +36,7 @@ public class HttpJspPageInstrumentationSingletons { "io.opentelemetry.jsp-2.3", HttpJspPageInstrumentationSingletons::spanNameOnRender) .addAttributesExtractor(new RenderAttributesExtractor()) - .newInstrumenter(SpanKindExtractor.alwaysInternal()); + .buildInstrumenter(SpanKindExtractor.alwaysInternal()); } private static String spanNameOnRender(HttpServletRequest req) { diff --git a/instrumentation/jsp-2.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jsp/JspCompilationContextInstrumentation.java b/instrumentation/jsp-2.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jsp/JspCompilationContextInstrumentation.java index 77b5fd23e7dd..250e61731a73 100644 --- a/instrumentation/jsp-2.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jsp/JspCompilationContextInstrumentation.java +++ b/instrumentation/jsp-2.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jsp/JspCompilationContextInstrumentation.java @@ -31,11 +31,11 @@ public ElementMatcher typeMatcher() { public void transform(TypeTransformer transformer) { transformer.applyAdviceToMethod( named("compile").and(takesArguments(0)).and(isPublic()), - JspCompilationContextInstrumentation.class.getName() + "$JasperJspCompilationContext"); + JspCompilationContextInstrumentation.class.getName() + "$CompileAdvice"); } @SuppressWarnings("unused") - public static class JasperJspCompilationContext { + public static class CompileAdvice { @Advice.OnMethodEnter(suppress = Throwable.class) public static void onEnter( diff --git a/instrumentation/jsp-2.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jsp/JspCompilationContextInstrumentationSingletons.java b/instrumentation/jsp-2.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jsp/JspCompilationContextInstrumentationSingletons.java index 7e3e79b507db..c4c474c725b7 100644 --- a/instrumentation/jsp-2.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jsp/JspCompilationContextInstrumentationSingletons.java +++ b/instrumentation/jsp-2.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jsp/JspCompilationContextInstrumentationSingletons.java @@ -30,7 +30,7 @@ public class JspCompilationContextInstrumentationSingletons { "io.opentelemetry.jsp-2.3", JspCompilationContextInstrumentationSingletons::spanNameOnCompile) .addAttributesExtractor(new CompilationAttributesExtractor()) - .newInstrumenter(SpanKindExtractor.alwaysInternal()); + .buildInstrumenter(SpanKindExtractor.alwaysInternal()); } public static String spanNameOnCompile(JspCompilationContext jspCompilationContext) { diff --git a/instrumentation/jsp-2.3/javaagent/src/test/groovy/JspInstrumentationBasicTests.groovy b/instrumentation/jsp-2.3/javaagent/src/test/groovy/JspInstrumentationBasicTests.groovy index 68a2a2fcc5d1..a09acff320d1 100644 --- a/instrumentation/jsp-2.3/javaagent/src/test/groovy/JspInstrumentationBasicTests.groovy +++ b/instrumentation/jsp-2.3/javaagent/src/test/groovy/JspInstrumentationBasicTests.groovy @@ -90,19 +90,20 @@ class JspInstrumentationBasicTests extends AgentInstrumentationSpecification { name route kind SERVER attributes { - "$SemanticAttributes.NET_PEER_IP" "127.0.0.1" - "$SemanticAttributes.NET_PEER_PORT" Long "$SemanticAttributes.HTTP_SCHEME" "http" - "$SemanticAttributes.HTTP_HOST" "localhost:$port" "$SemanticAttributes.HTTP_TARGET" route "$SemanticAttributes.HTTP_METHOD" "GET" "$SemanticAttributes.HTTP_STATUS_CODE" 200 "$SemanticAttributes.HTTP_FLAVOR" "1.1" "$SemanticAttributes.HTTP_USER_AGENT" String - "$SemanticAttributes.NET_TRANSPORT" IP_TCP - // Optional "$SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH" { it == null || it instanceof Long } "$SemanticAttributes.HTTP_ROUTE" route + "$SemanticAttributes.NET_TRANSPORT" IP_TCP + "$SemanticAttributes.NET_HOST_NAME" "localhost" + "$SemanticAttributes.NET_HOST_PORT" port + "$SemanticAttributes.NET_SOCK_PEER_ADDR" "127.0.0.1" + "$SemanticAttributes.NET_SOCK_PEER_PORT" Long + "$SemanticAttributes.NET_SOCK_HOST_ADDR" "127.0.0.1" } } span(1) { @@ -148,17 +149,19 @@ class JspInstrumentationBasicTests extends AgentInstrumentationSpecification { name route kind SERVER attributes { - "$SemanticAttributes.NET_PEER_IP" "127.0.0.1" - "$SemanticAttributes.NET_PEER_PORT" Long "$SemanticAttributes.HTTP_SCHEME" "http" - "$SemanticAttributes.HTTP_HOST" "localhost:$port" "$SemanticAttributes.HTTP_TARGET" "$route?$queryString" "$SemanticAttributes.HTTP_METHOD" "GET" "$SemanticAttributes.HTTP_STATUS_CODE" 200 "$SemanticAttributes.HTTP_FLAVOR" "1.1" "$SemanticAttributes.HTTP_USER_AGENT" String - "$SemanticAttributes.NET_TRANSPORT" IP_TCP "$SemanticAttributes.HTTP_ROUTE" route + "$SemanticAttributes.NET_TRANSPORT" IP_TCP + "$SemanticAttributes.NET_HOST_NAME" "localhost" + "$SemanticAttributes.NET_HOST_PORT" port + "$SemanticAttributes.NET_SOCK_PEER_ADDR" "127.0.0.1" + "$SemanticAttributes.NET_SOCK_PEER_PORT" Long + "$SemanticAttributes.NET_SOCK_HOST_ADDR" "127.0.0.1" } } span(1) { @@ -200,18 +203,20 @@ class JspInstrumentationBasicTests extends AgentInstrumentationSpecification { name route kind SERVER attributes { - "$SemanticAttributes.NET_PEER_IP" "127.0.0.1" - "$SemanticAttributes.NET_PEER_PORT" Long "$SemanticAttributes.HTTP_SCHEME" "http" - "$SemanticAttributes.HTTP_HOST" "localhost:$port" "$SemanticAttributes.HTTP_TARGET" route "$SemanticAttributes.HTTP_METHOD" "POST" "$SemanticAttributes.HTTP_STATUS_CODE" 200 "$SemanticAttributes.HTTP_FLAVOR" "1.1" "$SemanticAttributes.HTTP_USER_AGENT" String - "$SemanticAttributes.NET_TRANSPORT" IP_TCP "$SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH" Long "$SemanticAttributes.HTTP_ROUTE" route + "$SemanticAttributes.NET_TRANSPORT" IP_TCP + "$SemanticAttributes.NET_HOST_NAME" "localhost" + "$SemanticAttributes.NET_HOST_PORT" port + "$SemanticAttributes.NET_SOCK_PEER_ADDR" "127.0.0.1" + "$SemanticAttributes.NET_SOCK_PEER_PORT" Long + "$SemanticAttributes.NET_SOCK_HOST_ADDR" "127.0.0.1" } } span(1) { @@ -262,17 +267,19 @@ class JspInstrumentationBasicTests extends AgentInstrumentationSpecification { } } attributes { - "$SemanticAttributes.NET_PEER_IP" "127.0.0.1" - "$SemanticAttributes.NET_PEER_PORT" Long "$SemanticAttributes.HTTP_SCHEME" "http" - "$SemanticAttributes.HTTP_HOST" "localhost:$port" "$SemanticAttributes.HTTP_TARGET" route "$SemanticAttributes.HTTP_METHOD" "GET" "$SemanticAttributes.HTTP_STATUS_CODE" 500 "$SemanticAttributes.HTTP_FLAVOR" "1.1" "$SemanticAttributes.HTTP_USER_AGENT" String - "$SemanticAttributes.NET_TRANSPORT" IP_TCP "$SemanticAttributes.HTTP_ROUTE" route + "$SemanticAttributes.NET_TRANSPORT" IP_TCP + "$SemanticAttributes.NET_HOST_NAME" "localhost" + "$SemanticAttributes.NET_HOST_PORT" port + "$SemanticAttributes.NET_SOCK_PEER_ADDR" "127.0.0.1" + "$SemanticAttributes.NET_SOCK_PEER_PORT" Long + "$SemanticAttributes.NET_SOCK_HOST_ADDR" "127.0.0.1" } } span(1) { @@ -328,17 +335,19 @@ class JspInstrumentationBasicTests extends AgentInstrumentationSpecification { name route kind SERVER attributes { - "$SemanticAttributes.NET_PEER_IP" "127.0.0.1" - "$SemanticAttributes.NET_PEER_PORT" Long "$SemanticAttributes.HTTP_SCHEME" "http" - "$SemanticAttributes.HTTP_HOST" "localhost:$port" "$SemanticAttributes.HTTP_TARGET" route "$SemanticAttributes.HTTP_METHOD" "GET" "$SemanticAttributes.HTTP_STATUS_CODE" 200 "$SemanticAttributes.HTTP_FLAVOR" "1.1" "$SemanticAttributes.HTTP_USER_AGENT" String - "$SemanticAttributes.NET_TRANSPORT" IP_TCP "$SemanticAttributes.HTTP_ROUTE" route + "$SemanticAttributes.NET_TRANSPORT" IP_TCP + "$SemanticAttributes.NET_HOST_NAME" "localhost" + "$SemanticAttributes.NET_HOST_PORT" port + "$SemanticAttributes.NET_SOCK_PEER_ADDR" "127.0.0.1" + "$SemanticAttributes.NET_SOCK_PEER_PORT" Long + "$SemanticAttributes.NET_SOCK_HOST_ADDR" "127.0.0.1" } } span(1) { @@ -375,17 +384,19 @@ class JspInstrumentationBasicTests extends AgentInstrumentationSpecification { name route kind SERVER attributes { - "$SemanticAttributes.NET_PEER_IP" "127.0.0.1" - "$SemanticAttributes.NET_PEER_PORT" Long "$SemanticAttributes.HTTP_SCHEME" "http" - "$SemanticAttributes.HTTP_HOST" "localhost:$port" "$SemanticAttributes.HTTP_TARGET" route "$SemanticAttributes.HTTP_METHOD" "GET" "$SemanticAttributes.HTTP_STATUS_CODE" 200 "$SemanticAttributes.HTTP_FLAVOR" "1.1" "$SemanticAttributes.HTTP_USER_AGENT" String - "$SemanticAttributes.NET_TRANSPORT" IP_TCP "$SemanticAttributes.HTTP_ROUTE" route + "$SemanticAttributes.NET_TRANSPORT" IP_TCP + "$SemanticAttributes.NET_HOST_NAME" "localhost" + "$SemanticAttributes.NET_HOST_PORT" port + "$SemanticAttributes.NET_SOCK_PEER_ADDR" "127.0.0.1" + "$SemanticAttributes.NET_SOCK_PEER_PORT" Long + "$SemanticAttributes.NET_SOCK_HOST_ADDR" "127.0.0.1" } } span(1) { @@ -454,17 +465,19 @@ class JspInstrumentationBasicTests extends AgentInstrumentationSpecification { status ERROR errorEvent(JasperException, String) attributes { - "$SemanticAttributes.NET_PEER_IP" "127.0.0.1" - "$SemanticAttributes.NET_PEER_PORT" Long "$SemanticAttributes.HTTP_SCHEME" "http" - "$SemanticAttributes.HTTP_HOST" "localhost:$port" "$SemanticAttributes.HTTP_TARGET" route "$SemanticAttributes.HTTP_METHOD" "GET" "$SemanticAttributes.HTTP_STATUS_CODE" 500 "$SemanticAttributes.HTTP_FLAVOR" "1.1" "$SemanticAttributes.HTTP_USER_AGENT" String - "$SemanticAttributes.NET_TRANSPORT" IP_TCP "$SemanticAttributes.HTTP_ROUTE" route + "$SemanticAttributes.NET_TRANSPORT" IP_TCP + "$SemanticAttributes.NET_HOST_NAME" "localhost" + "$SemanticAttributes.NET_HOST_PORT" port + "$SemanticAttributes.NET_SOCK_PEER_ADDR" "127.0.0.1" + "$SemanticAttributes.NET_SOCK_PEER_PORT" Long + "$SemanticAttributes.NET_SOCK_HOST_ADDR" "127.0.0.1" } } span(1) { @@ -502,17 +515,19 @@ class JspInstrumentationBasicTests extends AgentInstrumentationSpecification { name route kind SERVER attributes { - "$SemanticAttributes.NET_PEER_IP" "127.0.0.1" - "$SemanticAttributes.NET_PEER_PORT" Long "$SemanticAttributes.HTTP_SCHEME" "http" - "$SemanticAttributes.HTTP_HOST" "localhost:$port" "$SemanticAttributes.HTTP_TARGET" "/$jspWebappContext/$staticFile" "$SemanticAttributes.HTTP_METHOD" "GET" "$SemanticAttributes.HTTP_STATUS_CODE" 200 "$SemanticAttributes.HTTP_FLAVOR" "1.1" "$SemanticAttributes.HTTP_USER_AGENT" String - "$SemanticAttributes.NET_TRANSPORT" IP_TCP "$SemanticAttributes.HTTP_ROUTE" route + "$SemanticAttributes.NET_TRANSPORT" IP_TCP + "$SemanticAttributes.NET_HOST_NAME" "localhost" + "$SemanticAttributes.NET_HOST_PORT" port + "$SemanticAttributes.NET_SOCK_PEER_ADDR" "127.0.0.1" + "$SemanticAttributes.NET_SOCK_PEER_PORT" Long + "$SemanticAttributes.NET_SOCK_HOST_ADDR" "127.0.0.1" } } } diff --git a/instrumentation/jsp-2.3/javaagent/src/test/groovy/JspInstrumentationForwardTests.groovy b/instrumentation/jsp-2.3/javaagent/src/test/groovy/JspInstrumentationForwardTests.groovy index b3a97dcc42e8..2249b44bf3dc 100644 --- a/instrumentation/jsp-2.3/javaagent/src/test/groovy/JspInstrumentationForwardTests.groovy +++ b/instrumentation/jsp-2.3/javaagent/src/test/groovy/JspInstrumentationForwardTests.groovy @@ -88,17 +88,19 @@ class JspInstrumentationForwardTests extends AgentInstrumentationSpecification { name route kind SERVER attributes { - "$SemanticAttributes.NET_PEER_IP" "127.0.0.1" - "$SemanticAttributes.NET_PEER_PORT" Long "$SemanticAttributes.HTTP_SCHEME" "http" - "$SemanticAttributes.HTTP_HOST" "localhost:$port" "$SemanticAttributes.HTTP_TARGET" route "$SemanticAttributes.HTTP_METHOD" "GET" "$SemanticAttributes.HTTP_STATUS_CODE" 200 "$SemanticAttributes.HTTP_FLAVOR" "1.1" "$SemanticAttributes.HTTP_USER_AGENT" String - "$SemanticAttributes.NET_TRANSPORT" IP_TCP "$SemanticAttributes.HTTP_ROUTE" route + "$SemanticAttributes.NET_TRANSPORT" IP_TCP + "$SemanticAttributes.NET_HOST_NAME" "localhost" + "$SemanticAttributes.NET_HOST_PORT" port + "$SemanticAttributes.NET_SOCK_PEER_ADDR" "127.0.0.1" + "$SemanticAttributes.NET_SOCK_PEER_PORT" Long + "$SemanticAttributes.NET_SOCK_HOST_ADDR" "127.0.0.1" } } span(1) { @@ -156,17 +158,19 @@ class JspInstrumentationForwardTests extends AgentInstrumentationSpecification { name route kind SERVER attributes { - "$SemanticAttributes.NET_PEER_IP" "127.0.0.1" - "$SemanticAttributes.NET_PEER_PORT" Long "$SemanticAttributes.HTTP_SCHEME" "http" - "$SemanticAttributes.HTTP_HOST" "localhost:$port" "$SemanticAttributes.HTTP_TARGET" route "$SemanticAttributes.HTTP_METHOD" "GET" "$SemanticAttributes.HTTP_STATUS_CODE" 200 "$SemanticAttributes.HTTP_FLAVOR" "1.1" "$SemanticAttributes.HTTP_USER_AGENT" String - "$SemanticAttributes.NET_TRANSPORT" IP_TCP "$SemanticAttributes.HTTP_ROUTE" route + "$SemanticAttributes.NET_TRANSPORT" IP_TCP + "$SemanticAttributes.NET_HOST_NAME" "localhost" + "$SemanticAttributes.NET_HOST_PORT" port + "$SemanticAttributes.NET_SOCK_PEER_ADDR" "127.0.0.1" + "$SemanticAttributes.NET_SOCK_PEER_PORT" Long + "$SemanticAttributes.NET_SOCK_HOST_ADDR" "127.0.0.1" } } span(1) { @@ -203,17 +207,19 @@ class JspInstrumentationForwardTests extends AgentInstrumentationSpecification { name route kind SERVER attributes { - "$SemanticAttributes.NET_PEER_IP" "127.0.0.1" - "$SemanticAttributes.NET_PEER_PORT" Long "$SemanticAttributes.HTTP_SCHEME" "http" - "$SemanticAttributes.HTTP_HOST" "localhost:$port" "$SemanticAttributes.HTTP_TARGET" route "$SemanticAttributes.HTTP_METHOD" "GET" "$SemanticAttributes.HTTP_STATUS_CODE" 200 "$SemanticAttributes.HTTP_FLAVOR" "1.1" "$SemanticAttributes.HTTP_USER_AGENT" String - "$SemanticAttributes.NET_TRANSPORT" IP_TCP "$SemanticAttributes.HTTP_ROUTE" route + "$SemanticAttributes.NET_TRANSPORT" IP_TCP + "$SemanticAttributes.NET_HOST_NAME" "localhost" + "$SemanticAttributes.NET_HOST_PORT" port + "$SemanticAttributes.NET_SOCK_PEER_ADDR" "127.0.0.1" + "$SemanticAttributes.NET_SOCK_PEER_PORT" Long + "$SemanticAttributes.NET_SOCK_HOST_ADDR" "127.0.0.1" } } span(1) { @@ -298,17 +304,19 @@ class JspInstrumentationForwardTests extends AgentInstrumentationSpecification { name route kind SERVER attributes { - "$SemanticAttributes.NET_PEER_IP" "127.0.0.1" - "$SemanticAttributes.NET_PEER_PORT" Long "$SemanticAttributes.HTTP_SCHEME" "http" - "$SemanticAttributes.HTTP_HOST" "localhost:$port" "$SemanticAttributes.HTTP_TARGET" route "$SemanticAttributes.HTTP_METHOD" "GET" "$SemanticAttributes.HTTP_STATUS_CODE" 200 "$SemanticAttributes.HTTP_FLAVOR" "1.1" "$SemanticAttributes.HTTP_USER_AGENT" String - "$SemanticAttributes.NET_TRANSPORT" IP_TCP "$SemanticAttributes.HTTP_ROUTE" route + "$SemanticAttributes.NET_TRANSPORT" IP_TCP + "$SemanticAttributes.NET_HOST_NAME" "localhost" + "$SemanticAttributes.NET_HOST_PORT" port + "$SemanticAttributes.NET_SOCK_PEER_ADDR" "127.0.0.1" + "$SemanticAttributes.NET_SOCK_PEER_PORT" Long + "$SemanticAttributes.NET_SOCK_HOST_ADDR" "127.0.0.1" } } span(1) { @@ -379,17 +387,19 @@ class JspInstrumentationForwardTests extends AgentInstrumentationSpecification { status ERROR errorEvent(JasperException, String) attributes { - "$SemanticAttributes.NET_PEER_IP" "127.0.0.1" - "$SemanticAttributes.NET_PEER_PORT" Long "$SemanticAttributes.HTTP_SCHEME" "http" - "$SemanticAttributes.HTTP_HOST" "localhost:$port" "$SemanticAttributes.HTTP_TARGET" route "$SemanticAttributes.HTTP_METHOD" "GET" "$SemanticAttributes.HTTP_STATUS_CODE" 500 "$SemanticAttributes.HTTP_FLAVOR" "1.1" "$SemanticAttributes.HTTP_USER_AGENT" String - "$SemanticAttributes.NET_TRANSPORT" IP_TCP "$SemanticAttributes.HTTP_ROUTE" route + "$SemanticAttributes.NET_TRANSPORT" IP_TCP + "$SemanticAttributes.NET_HOST_NAME" "localhost" + "$SemanticAttributes.NET_HOST_PORT" port + "$SemanticAttributes.NET_SOCK_PEER_ADDR" "127.0.0.1" + "$SemanticAttributes.NET_SOCK_PEER_PORT" Long + "$SemanticAttributes.NET_SOCK_HOST_ADDR" "127.0.0.1" } } span(1) { @@ -439,17 +449,19 @@ class JspInstrumentationForwardTests extends AgentInstrumentationSpecification { kind SERVER status UNSET attributes { - "$SemanticAttributes.NET_PEER_IP" "127.0.0.1" - "$SemanticAttributes.NET_PEER_PORT" Long "$SemanticAttributes.HTTP_SCHEME" "http" - "$SemanticAttributes.HTTP_HOST" "localhost:$port" "$SemanticAttributes.HTTP_TARGET" route "$SemanticAttributes.HTTP_METHOD" "GET" "$SemanticAttributes.HTTP_STATUS_CODE" 404 "$SemanticAttributes.HTTP_FLAVOR" "1.1" "$SemanticAttributes.HTTP_USER_AGENT" String - "$SemanticAttributes.NET_TRANSPORT" IP_TCP "$SemanticAttributes.HTTP_ROUTE" route + "$SemanticAttributes.NET_TRANSPORT" IP_TCP + "$SemanticAttributes.NET_HOST_NAME" "localhost" + "$SemanticAttributes.NET_HOST_PORT" port + "$SemanticAttributes.NET_SOCK_PEER_ADDR" "127.0.0.1" + "$SemanticAttributes.NET_SOCK_PEER_PORT" Long + "$SemanticAttributes.NET_SOCK_HOST_ADDR" "127.0.0.1" } } span(1) { diff --git a/instrumentation/kafka/kafka-clients/README.md b/instrumentation/kafka/kafka-clients/README.md deleted file mode 100644 index 170efde43cf8..000000000000 --- a/instrumentation/kafka/kafka-clients/README.md +++ /dev/null @@ -1,6 +0,0 @@ -# Settings for the Kafka client instrumentation - -| System property | Type | Default | Description | -|---|---|---|---| -| `otel.instrumentation.kafka.experimental-span-attributes` | Boolean | `false` | Enable the capture of experimental span attributes. | -| `otel.instrumentation.kafka.client-propagation.enabled` | Boolean | `true` | Enables remote context propagation via Kafka message headers. | \ No newline at end of file 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 5ca649875dc7..7cee5bd89a96 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 @@ -28,6 +28,8 @@ tasks { withType().configureEach { usesService(gradle.sharedServices.registrations["testcontainersBuildService"].service) + systemProperty("testLatestDeps", findProperty("testLatestDeps") as Boolean) + // TODO run tests both with and without experimental span attributes jvmArgs("-Dotel.instrumentation.kafka.experimental-span-attributes=true") } @@ -37,7 +39,7 @@ tasks { includeTestsMatching("KafkaClientPropagationDisabledTest") } include("**/KafkaClientPropagationDisabledTest.*") - jvmArgs("-Dotel.instrumentation.kafka.client-propagation.enabled=false") + jvmArgs("-Dotel.instrumentation.kafka.producer-propagation.enabled=false") } val testReceiveSpansDisabled by registering(Test::class) { diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-0.11/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/kafkaclients/KafkaConsumerInstrumentation.java b/instrumentation/kafka/kafka-clients/kafka-clients-0.11/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/kafkaclients/KafkaConsumerInstrumentation.java index c2820c4190f1..d886066353e2 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-0.11/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/kafkaclients/KafkaConsumerInstrumentation.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-0.11/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/kafkaclients/KafkaConsumerInstrumentation.java @@ -7,6 +7,8 @@ import static io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge.currentContext; import static io.opentelemetry.javaagent.instrumentation.kafkaclients.KafkaSingletons.consumerReceiveInstrumenter; +import static io.opentelemetry.javaagent.instrumentation.kafkaclients.KafkaSingletons.enhanceConfig; +import static net.bytebuddy.matcher.ElementMatchers.isConstructor; import static net.bytebuddy.matcher.ElementMatchers.isPublic; import static net.bytebuddy.matcher.ElementMatchers.named; import static net.bytebuddy.matcher.ElementMatchers.returns; @@ -21,6 +23,8 @@ import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; import java.time.Duration; +import java.util.Map; +import java.util.Properties; import net.bytebuddy.asm.Advice; import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.matcher.ElementMatcher; @@ -36,6 +40,12 @@ public ElementMatcher typeMatcher() { @Override public void transform(TypeTransformer transformer) { + transformer.applyAdviceToMethod( + isConstructor().and(takesArgument(0, Map.class)), + this.getClass().getName() + "$ConstructorMapAdvice"); + transformer.applyAdviceToMethod( + isConstructor().and(takesArgument(0, Properties.class)), + this.getClass().getName() + "$ConstructorPropertiesAdvice"); transformer.applyAdviceToMethod( named("poll") .and(isPublic()) @@ -45,6 +55,24 @@ public void transform(TypeTransformer transformer) { this.getClass().getName() + "$PollAdvice"); } + @SuppressWarnings("unused") + public static class ConstructorMapAdvice { + + @Advice.OnMethodEnter(suppress = Throwable.class) + public static void onEnter(@Advice.Argument(0) Map config) { + enhanceConfig(config); + } + } + + @SuppressWarnings("unused") + public static class ConstructorPropertiesAdvice { + + @Advice.OnMethodEnter(suppress = Throwable.class) + public static void onEnter(@Advice.Argument(0) Properties config) { + enhanceConfig(config); + } + } + @SuppressWarnings("unused") public static class PollAdvice { @Advice.OnMethodEnter(suppress = Throwable.class) @@ -65,27 +93,28 @@ public static void onExit( Context parentContext = currentContext(); if (consumerReceiveInstrumenter().shouldStart(parentContext, records)) { - Context context = - InstrumenterUtil.startAndEnd( - consumerReceiveInstrumenter(), - parentContext, - records, - null, - error, - timer.startTime(), - timer.now()); - - // we're storing the context of the receive span so that process spans can use it as parent - // context even though the span has ended - // this is the suggested behavior according to the spec batch receive scenario: - // https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/messaging.md#batch-receiving - VirtualField, Context> consumerRecordsContext = - VirtualField.find(ConsumerRecords.class, Context.class); - consumerRecordsContext.set(records, context); - // disable process tracing and store the receive span for each individual record too boolean previousValue = KafkaClientsConsumerProcessTracing.setEnabled(false); try { + Context context = + InstrumenterUtil.startAndEnd( + consumerReceiveInstrumenter(), + parentContext, + records, + null, + error, + timer.startTime(), + timer.now()); + + // we're storing the context of the receive span so that process spans can use it as + // parent + // context even though the span has ended + // this is the suggested behavior according to the spec batch receive scenario: + // https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/messaging.md#batch-receiving + VirtualField, Context> consumerRecordsContext = + VirtualField.find(ConsumerRecords.class, Context.class); + consumerRecordsContext.set(records, context); + VirtualField, Context> consumerRecordContext = VirtualField.find(ConsumerRecord.class, Context.class); for (ConsumerRecord record : records) { diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-0.11/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/kafkaclients/KafkaProducerInstrumentation.java b/instrumentation/kafka/kafka-clients/kafka-clients-0.11/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/kafkaclients/KafkaProducerInstrumentation.java index 695496d6b5ad..ab636a055efd 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-0.11/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/kafkaclients/KafkaProducerInstrumentation.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-0.11/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/kafkaclients/KafkaProducerInstrumentation.java @@ -5,7 +5,9 @@ package io.opentelemetry.javaagent.instrumentation.kafkaclients; +import static io.opentelemetry.javaagent.instrumentation.kafkaclients.KafkaSingletons.enhanceConfig; import static io.opentelemetry.javaagent.instrumentation.kafkaclients.KafkaSingletons.producerInstrumenter; +import static net.bytebuddy.matcher.ElementMatchers.isConstructor; import static net.bytebuddy.matcher.ElementMatchers.isMethod; import static net.bytebuddy.matcher.ElementMatchers.isPublic; import static net.bytebuddy.matcher.ElementMatchers.named; @@ -17,6 +19,8 @@ import io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; +import java.util.Map; +import java.util.Properties; import net.bytebuddy.asm.Advice; import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.matcher.ElementMatcher; @@ -33,17 +37,41 @@ public ElementMatcher typeMatcher() { @Override public void transform(TypeTransformer transformer) { + transformer.applyAdviceToMethod( + isConstructor().and(takesArgument(0, Map.class)), + this.getClass().getName() + "$ConstructorMapAdvice"); + transformer.applyAdviceToMethod( + isConstructor().and(takesArgument(0, Properties.class)), + this.getClass().getName() + "$ConstructorPropertiesAdvice"); transformer.applyAdviceToMethod( isMethod() .and(isPublic()) .and(named("send")) .and(takesArgument(0, named("org.apache.kafka.clients.producer.ProducerRecord"))) .and(takesArgument(1, named("org.apache.kafka.clients.producer.Callback"))), - KafkaProducerInstrumentation.class.getName() + "$ProducerAdvice"); + KafkaProducerInstrumentation.class.getName() + "$SendAdvice"); + } + + @SuppressWarnings("unused") + public static class ConstructorMapAdvice { + + @Advice.OnMethodEnter(suppress = Throwable.class) + public static void onEnter(@Advice.Argument(0) Map config) { + enhanceConfig(config); + } + } + + @SuppressWarnings("unused") + public static class ConstructorPropertiesAdvice { + + @Advice.OnMethodEnter(suppress = Throwable.class) + public static void onEnter(@Advice.Argument(0) Properties config) { + enhanceConfig(config); + } } @SuppressWarnings("unused") - public static class ProducerAdvice { + public static class SendAdvice { @Advice.OnMethodEnter(suppress = Throwable.class) public static void onEnter( @@ -61,7 +89,8 @@ public static void onEnter( context = producerInstrumenter().start(parentContext, record); scope = context.makeCurrent(); - if (KafkaSingletons.isPropagationEnabled() && KafkaPropagation.shouldPropagate(apiVersions)) { + if (KafkaSingletons.isProducerPropagationEnabled() + && KafkaPropagation.shouldPropagate(apiVersions)) { record = KafkaPropagation.propagateContext(context, record); } diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-0.11/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/kafkaclients/KafkaSingletons.java b/instrumentation/kafka/kafka-clients/kafka-clients-0.11/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/kafkaclients/KafkaSingletons.java index 6db495be87fa..5c9572118876 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-0.11/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/kafkaclients/KafkaSingletons.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-0.11/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/kafkaclients/KafkaSingletons.java @@ -8,8 +8,12 @@ import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; import io.opentelemetry.instrumentation.kafka.internal.KafkaInstrumenterFactory; +import io.opentelemetry.instrumentation.kafka.internal.OpenTelemetryMetricsReporter; +import io.opentelemetry.javaagent.bootstrap.internal.DeprecatedConfigProperties; import io.opentelemetry.javaagent.bootstrap.internal.ExperimentalConfig; import io.opentelemetry.javaagent.bootstrap.internal.InstrumentationConfig; +import java.util.Map; +import org.apache.kafka.clients.CommonClientConfigs; import org.apache.kafka.clients.consumer.ConsumerRecord; import org.apache.kafka.clients.consumer.ConsumerRecords; import org.apache.kafka.clients.producer.ProducerRecord; @@ -17,9 +21,15 @@ public final class KafkaSingletons { private static final String INSTRUMENTATION_NAME = "io.opentelemetry.kafka-clients-0.11"; - private static final boolean PROPAGATION_ENABLED = + private static final boolean PRODUCER_PROPAGATION_ENABLED = + DeprecatedConfigProperties.getBoolean( + InstrumentationConfig.get(), + "otel.instrumentation.kafka.client-propagation.enabled", + "otel.instrumentation.kafka.producer-propagation.enabled", + true); + private static final boolean METRICS_ENABLED = InstrumentationConfig.get() - .getBoolean("otel.instrumentation.kafka.client-propagation.enabled", true); + .getBoolean("otel.instrumentation.kafka.metric-reporter.enabled", true); private static final Instrumenter, Void> PRODUCER_INSTRUMENTER; private static final Instrumenter, Void> CONSUMER_RECEIVE_INSTRUMENTER; @@ -28,10 +38,10 @@ public final class KafkaSingletons { static { KafkaInstrumenterFactory instrumenterFactory = new KafkaInstrumenterFactory(GlobalOpenTelemetry.get(), INSTRUMENTATION_NAME) + .setCapturedHeaders(ExperimentalConfig.get().getMessagingHeaders()) .setCaptureExperimentalSpanAttributes( InstrumentationConfig.get() .getBoolean("otel.instrumentation.kafka.experimental-span-attributes", false)) - .setPropagationEnabled(PROPAGATION_ENABLED) .setMessagingReceiveInstrumentationEnabled( ExperimentalConfig.get().messagingReceiveInstrumentationEnabled()); PRODUCER_INSTRUMENTER = instrumenterFactory.createProducerInstrumenter(); @@ -39,8 +49,8 @@ public final class KafkaSingletons { CONSUMER_PROCESS_INSTRUMENTER = instrumenterFactory.createConsumerProcessInstrumenter(); } - public static boolean isPropagationEnabled() { - return PROPAGATION_ENABLED; + public static boolean isProducerPropagationEnabled() { + return PRODUCER_PROPAGATION_ENABLED; } public static Instrumenter, Void> producerInstrumenter() { @@ -55,5 +65,20 @@ public static boolean isPropagationEnabled() { return CONSUMER_PROCESS_INSTRUMENTER; } + public static void enhanceConfig(Map config) { + if (!METRICS_ENABLED) { + return; + } + config.merge( + CommonClientConfigs.METRIC_REPORTER_CLASSES_CONFIG, + OpenTelemetryMetricsReporter.class.getName(), + (class1, class2) -> class1 + "," + class2); + config.put( + OpenTelemetryMetricsReporter.CONFIG_KEY_OPENTELEMETRY_INSTANCE, GlobalOpenTelemetry.get()); + config.put( + OpenTelemetryMetricsReporter.CONFIG_KEY_OPENTELEMETRY_INSTRUMENTATION_NAME, + INSTRUMENTATION_NAME); + } + private KafkaSingletons() {} } diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-0.11/javaagent/src/test/groovy/io/opentelemetry/instrumentation/kafkaclients/KafkaClientDefaultTest.groovy b/instrumentation/kafka/kafka-clients/kafka-clients-0.11/javaagent/src/test/groovy/io/opentelemetry/instrumentation/kafkaclients/KafkaClientDefaultTest.groovy index 949bb594d7b1..6ee69de0dae7 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-0.11/javaagent/src/test/groovy/io/opentelemetry/instrumentation/kafkaclients/KafkaClientDefaultTest.groovy +++ b/instrumentation/kafka/kafka-clients/kafka-clients-0.11/javaagent/src/test/groovy/io/opentelemetry/instrumentation/kafkaclients/KafkaClientDefaultTest.groovy @@ -7,6 +7,7 @@ package io.opentelemetry.instrumentation.kafkaclients import io.opentelemetry.sdk.trace.data.SpanData import io.opentelemetry.semconv.trace.attributes.SemanticAttributes +import java.nio.charset.StandardCharsets import org.apache.kafka.clients.producer.ProducerRecord import java.time.Duration @@ -18,11 +19,15 @@ import static io.opentelemetry.api.trace.SpanKind.PRODUCER class KafkaClientDefaultTest extends KafkaClientPropagationBaseTest { - def "test kafka produce and consume"() { + def "test kafka produce and consume, test headers: #testHeaders"() { when: String greeting = "Hello Kafka!" runWithSpan("parent") { - producer.send(new ProducerRecord(SHARED_TOPIC, greeting)) { meta, ex -> + def producerRecord = new ProducerRecord(SHARED_TOPIC, greeting) + if (testHeaders) { + producerRecord.headers().add("test-message-header", "test".getBytes(StandardCharsets.UTF_8)) + } + producer.send(producerRecord) { meta, ex -> if (ex == null) { runWithSpan("producer callback") {} } else { @@ -63,6 +68,10 @@ class KafkaClientDefaultTest extends KafkaClientPropagationBaseTest { "$SemanticAttributes.MESSAGING_SYSTEM" "kafka" "$SemanticAttributes.MESSAGING_DESTINATION" SHARED_TOPIC "$SemanticAttributes.MESSAGING_DESTINATION_KIND" "topic" + if (testHeaders) { + "messaging.header.test_message_header" { it == ["test"] } + } + "messaging.payload" greeting } } span(2) { @@ -83,6 +92,9 @@ class KafkaClientDefaultTest extends KafkaClientPropagationBaseTest { "$SemanticAttributes.MESSAGING_DESTINATION" SHARED_TOPIC "$SemanticAttributes.MESSAGING_DESTINATION_KIND" "topic" "$SemanticAttributes.MESSAGING_OPERATION" "receive" + if (testHeaders) { + "messaging.header.test_message_header" { it == ["test"] } + } } } span(1) { @@ -99,6 +111,10 @@ class KafkaClientDefaultTest extends KafkaClientPropagationBaseTest { "$SemanticAttributes.MESSAGING_KAFKA_PARTITION" { it >= 0 } "kafka.offset" Long "kafka.record.queue_time_ms" { it >= 0 } + if (testHeaders) { + "messaging.header.test_message_header" { it == ["test"] } + } + "messaging.payload" greeting } } span(2) { @@ -107,6 +123,9 @@ class KafkaClientDefaultTest extends KafkaClientPropagationBaseTest { } } } + + where: + testHeaders << [false, true] } def "test pass through tombstone"() { @@ -139,6 +158,7 @@ class KafkaClientDefaultTest extends KafkaClientPropagationBaseTest { "$SemanticAttributes.MESSAGING_DESTINATION" SHARED_TOPIC "$SemanticAttributes.MESSAGING_DESTINATION_KIND" "topic" "$SemanticAttributes.MESSAGING_KAFKA_TOMBSTONE" true + "messaging.payload" String } } @@ -171,6 +191,7 @@ class KafkaClientDefaultTest extends KafkaClientPropagationBaseTest { "$SemanticAttributes.MESSAGING_KAFKA_TOMBSTONE" true "kafka.offset" Long "kafka.record.queue_time_ms" { it >= 0 } + "messaging.payload" String } } } @@ -216,6 +237,7 @@ class KafkaClientDefaultTest extends KafkaClientPropagationBaseTest { "$SemanticAttributes.MESSAGING_DESTINATION" SHARED_TOPIC "$SemanticAttributes.MESSAGING_DESTINATION_KIND" "topic" "$SemanticAttributes.MESSAGING_KAFKA_PARTITION" { it >= 0 } + "messaging.payload" greeting } } @@ -247,6 +269,7 @@ class KafkaClientDefaultTest extends KafkaClientPropagationBaseTest { "$SemanticAttributes.MESSAGING_KAFKA_PARTITION" { it >= 0 } "kafka.offset" Long "kafka.record.queue_time_ms" { it >= 0 } + "messaging.payload" greeting } } } diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-0.11/javaagent/src/test/groovy/io/opentelemetry/instrumentation/kafkaclients/KafkaClientPropagationDisabledTest.groovy b/instrumentation/kafka/kafka-clients/kafka-clients-0.11/javaagent/src/test/groovy/io/opentelemetry/instrumentation/kafkaclients/KafkaClientPropagationDisabledTest.groovy index 9d13ab21bf7b..2b35aac76156 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-0.11/javaagent/src/test/groovy/io/opentelemetry/instrumentation/kafkaclients/KafkaClientPropagationDisabledTest.groovy +++ b/instrumentation/kafka/kafka-clients/kafka-clients-0.11/javaagent/src/test/groovy/io/opentelemetry/instrumentation/kafkaclients/KafkaClientPropagationDisabledTest.groovy @@ -31,6 +31,7 @@ class KafkaClientPropagationDisabledTest extends KafkaClientPropagationBaseTest "$SemanticAttributes.MESSAGING_SYSTEM" "kafka" "$SemanticAttributes.MESSAGING_DESTINATION" SHARED_TOPIC "$SemanticAttributes.MESSAGING_DESTINATION_KIND" "topic" + "messaging.payload" message } } } @@ -57,6 +58,7 @@ class KafkaClientPropagationDisabledTest extends KafkaClientPropagationBaseTest "$SemanticAttributes.MESSAGING_SYSTEM" "kafka" "$SemanticAttributes.MESSAGING_DESTINATION" SHARED_TOPIC "$SemanticAttributes.MESSAGING_DESTINATION_KIND" "topic" + "messaging.payload" message } } } @@ -74,6 +76,7 @@ class KafkaClientPropagationDisabledTest extends KafkaClientPropagationBaseTest "$SemanticAttributes.MESSAGING_KAFKA_PARTITION" { it >= 0 } "kafka.offset" Long "kafka.record.queue_time_ms" { it >= 0 } + "messaging.payload" message } span(1) { name "processing" diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-0.11/javaagent/src/test/groovy/io/opentelemetry/instrumentation/kafkaclients/KafkaClientSuppressReceiveSpansTest.groovy b/instrumentation/kafka/kafka-clients/kafka-clients-0.11/javaagent/src/test/groovy/io/opentelemetry/instrumentation/kafkaclients/KafkaClientSuppressReceiveSpansTest.groovy index df3a5293a3d5..cd5840714f86 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-0.11/javaagent/src/test/groovy/io/opentelemetry/instrumentation/kafkaclients/KafkaClientSuppressReceiveSpansTest.groovy +++ b/instrumentation/kafka/kafka-clients/kafka-clients-0.11/javaagent/src/test/groovy/io/opentelemetry/instrumentation/kafkaclients/KafkaClientSuppressReceiveSpansTest.groovy @@ -56,6 +56,7 @@ class KafkaClientSuppressReceiveSpansTest extends KafkaClientPropagationBaseTest "$SemanticAttributes.MESSAGING_SYSTEM" "kafka" "$SemanticAttributes.MESSAGING_DESTINATION" SHARED_TOPIC "$SemanticAttributes.MESSAGING_DESTINATION_KIND" "topic" + "messaging.payload" greeting } } span(2) { @@ -71,6 +72,7 @@ class KafkaClientSuppressReceiveSpansTest extends KafkaClientPropagationBaseTest "$SemanticAttributes.MESSAGING_KAFKA_PARTITION" { it >= 0 } "kafka.offset" Long "kafka.record.queue_time_ms" { it >= 0 } + "messaging.payload" greeting } } span(3) { @@ -110,6 +112,7 @@ class KafkaClientSuppressReceiveSpansTest extends KafkaClientPropagationBaseTest "$SemanticAttributes.MESSAGING_DESTINATION" SHARED_TOPIC "$SemanticAttributes.MESSAGING_DESTINATION_KIND" "topic" "$SemanticAttributes.MESSAGING_KAFKA_TOMBSTONE" true + "messaging.payload" String } } span(1) { @@ -126,6 +129,7 @@ class KafkaClientSuppressReceiveSpansTest extends KafkaClientPropagationBaseTest "$SemanticAttributes.MESSAGING_KAFKA_TOMBSTONE" true "kafka.offset" Long "kafka.record.queue_time_ms" { it >= 0 } + "messaging.payload" String } } } @@ -164,6 +168,7 @@ class KafkaClientSuppressReceiveSpansTest extends KafkaClientPropagationBaseTest "$SemanticAttributes.MESSAGING_DESTINATION" SHARED_TOPIC "$SemanticAttributes.MESSAGING_DESTINATION_KIND" "topic" "$SemanticAttributes.MESSAGING_KAFKA_PARTITION" { it >= 0 } + "messaging.payload" greeting } } span(1) { @@ -179,6 +184,7 @@ class KafkaClientSuppressReceiveSpansTest extends KafkaClientPropagationBaseTest "$SemanticAttributes.MESSAGING_KAFKA_PARTITION" { it >= 0 } "kafka.offset" Long "kafka.record.queue_time_ms" { it >= 0 } + "messaging.payload" greeting } } } diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-0.11/javaagent/src/test/java/io/opentelemetry/instrumentation/kafkaclients/OpenTelemetryMetricsReporterTest.java b/instrumentation/kafka/kafka-clients/kafka-clients-0.11/javaagent/src/test/java/io/opentelemetry/instrumentation/kafkaclients/OpenTelemetryMetricsReporterTest.java new file mode 100644 index 000000000000..28cf833f1889 --- /dev/null +++ b/instrumentation/kafka/kafka-clients/kafka-clients-0.11/javaagent/src/test/java/io/opentelemetry/instrumentation/kafkaclients/OpenTelemetryMetricsReporterTest.java @@ -0,0 +1,36 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.kafkaclients; + +import static java.util.Collections.emptyMap; + +import io.opentelemetry.instrumentation.kafka.internal.AbstractOpenTelemetryMetricsReporterTest; +import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import java.util.Map; +import org.junit.jupiter.api.condition.EnabledIfSystemProperty; +import org.junit.jupiter.api.extension.RegisterExtension; + +@EnabledIfSystemProperty( + named = "testLatestDeps", + matches = "true", + disabledReason = + "kafka-clients 0.11 emits a significantly different set of metrics; it's probably fine to just test the latest version") +class OpenTelemetryMetricsReporterTest extends AbstractOpenTelemetryMetricsReporterTest { + + @RegisterExtension + static final InstrumentationExtension testing = AgentInstrumentationExtension.create(); + + @Override + protected InstrumentationExtension testing() { + return testing; + } + + @Override + protected Map additionalConfig() { + return emptyMap(); + } +} diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-0.11/testing/build.gradle.kts b/instrumentation/kafka/kafka-clients/kafka-clients-0.11/testing/build.gradle.kts index b8dc4adc702b..3058fd208b2f 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-0.11/testing/build.gradle.kts +++ b/instrumentation/kafka/kafka-clients/kafka-clients-0.11/testing/build.gradle.kts @@ -10,4 +10,8 @@ dependencies { implementation(project(":instrumentation:kafka:kafka-clients:kafka-clients-common:library")) implementation("org.testcontainers:kafka") + implementation("org.testcontainers:junit-jupiter") + + compileOnly("com.google.auto.value:auto-value-annotations") + annotationProcessor("com.google.auto.value:auto-value") } diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-0.11/testing/src/main/groovy/io/opentelemetry/instrumentation/kafkaclients/KafkaClientPropagationBaseTest.groovy b/instrumentation/kafka/kafka-clients/kafka-clients-0.11/testing/src/main/groovy/io/opentelemetry/instrumentation/kafkaclients/KafkaClientPropagationBaseTest.groovy index b927f375affd..fe4e12789619 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-0.11/testing/src/main/groovy/io/opentelemetry/instrumentation/kafkaclients/KafkaClientPropagationBaseTest.groovy +++ b/instrumentation/kafka/kafka-clients/kafka-clients-0.11/testing/src/main/groovy/io/opentelemetry/instrumentation/kafkaclients/KafkaClientPropagationBaseTest.groovy @@ -13,8 +13,8 @@ import java.time.Duration abstract class KafkaClientPropagationBaseTest extends KafkaClientBaseTest implements AgentTestTrait { - private static final boolean propagationEnabled = Boolean.parseBoolean( - System.getProperty("otel.instrumentation.kafka.client-propagation.enabled", "true")) + private static final boolean producerPropagationEnabled = Boolean.parseBoolean( + System.getProperty("otel.instrumentation.kafka.producer-propagation.enabled", "true")) @Unroll def "test kafka client header propagation manual config"() { @@ -28,7 +28,7 @@ abstract class KafkaClientPropagationBaseTest extends KafkaClientBaseTest implem def records = consumer.poll(Duration.ofSeconds(5).toMillis()) records.count() == 1 for (record in records) { - assert record.headers().iterator().hasNext() == propagationEnabled + assert record.headers().iterator().hasNext() == producerPropagationEnabled } } } \ No newline at end of file diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-0.11/testing/src/main/java/io/opentelemetry/instrumentation/kafka/internal/AbstractOpenTelemetryMetricsReporterTest.java b/instrumentation/kafka/kafka-clients/kafka-clients-0.11/testing/src/main/java/io/opentelemetry/instrumentation/kafka/internal/AbstractOpenTelemetryMetricsReporterTest.java new file mode 100644 index 000000000000..e38f56ad86c3 --- /dev/null +++ b/instrumentation/kafka/kafka-clients/kafka-clients-0.11/testing/src/main/java/io/opentelemetry/instrumentation/kafka/internal/AbstractOpenTelemetryMetricsReporterTest.java @@ -0,0 +1,449 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.kafka.internal; + +import static java.lang.System.lineSeparator; +import static java.util.Comparator.comparing; +import static java.util.stream.Collectors.groupingBy; +import static java.util.stream.Collectors.joining; +import static java.util.stream.Collectors.toList; +import static java.util.stream.Collectors.toSet; +import static org.assertj.core.api.Assertions.assertThat; + +import com.google.auto.value.AutoValue; +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.sdk.metrics.data.MetricData; +import io.opentelemetry.sdk.metrics.data.PointData; +import java.nio.charset.StandardCharsets; +import java.time.Duration; +import java.time.Instant; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Random; +import java.util.Set; +import org.apache.kafka.clients.CommonClientConfigs; +import org.apache.kafka.clients.consumer.ConsumerConfig; +import org.apache.kafka.clients.consumer.KafkaConsumer; +import org.apache.kafka.clients.producer.KafkaProducer; +import org.apache.kafka.clients.producer.ProducerConfig; +import org.apache.kafka.clients.producer.ProducerRecord; +import org.apache.kafka.common.MetricName; +import org.apache.kafka.common.metrics.KafkaMetric; +import org.apache.kafka.common.metrics.MetricsReporter; +import org.apache.kafka.common.serialization.ByteArrayDeserializer; +import org.apache.kafka.common.serialization.ByteArraySerializer; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testcontainers.containers.KafkaContainer; +import org.testcontainers.containers.output.Slf4jLogConsumer; +import org.testcontainers.containers.wait.strategy.Wait; +import org.testcontainers.junit.jupiter.Testcontainers; +import org.testcontainers.utility.DockerImageName; + +@SuppressWarnings("OtelInternalJavadoc") +@Testcontainers +public abstract class AbstractOpenTelemetryMetricsReporterTest { + + private static final Logger logger = + LoggerFactory.getLogger(AbstractOpenTelemetryMetricsReporterTest.class); + + private static final List TOPICS = Arrays.asList("foo", "bar", "baz", "qux"); + private static final Random RANDOM = new Random(); + + private static KafkaContainer kafka; + private static KafkaProducer producer; + private static KafkaConsumer consumer; + + @BeforeEach + void beforeAll() { + // only start the kafka container the first time this runs + if (kafka != null) { + return; + } + + kafka = + new KafkaContainer(DockerImageName.parse("confluentinc/cp-kafka:5.4.3")) + .withLogConsumer(new Slf4jLogConsumer(logger)) + .waitingFor(Wait.forLogMessage(".*started \\(kafka.server.KafkaServer\\).*", 1)) + .withStartupTimeout(Duration.ofMinutes(1)); + kafka.start(); + producer = new KafkaProducer<>(producerConfig()); + consumer = new KafkaConsumer<>(consumerConfig()); + } + + @AfterAll + static void afterAll() { + kafka.stop(); + producer.close(); + consumer.close(); + } + + @AfterEach + void tearDown() { + OpenTelemetryMetricsReporter.resetForTest(); + } + + protected abstract InstrumentationExtension testing(); + + protected abstract Map additionalConfig(); + + protected Map producerConfig() { + Map producerConfig = new HashMap<>(); + producerConfig.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, kafka.getBootstrapServers()); + producerConfig.put(ProducerConfig.CLIENT_ID_CONFIG, "sample-client-id"); + producerConfig.put( + ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, ByteArraySerializer.class.getName()); + producerConfig.put( + ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, ByteArraySerializer.class.getName()); + producerConfig.put(ProducerConfig.COMPRESSION_TYPE_CONFIG, "gzip"); + producerConfig.putAll(additionalConfig()); + producerConfig.merge( + CommonClientConfigs.METRIC_REPORTER_CLASSES_CONFIG, + TestMetricsReporter.class.getName(), + (o, o2) -> o + "," + o2); + return producerConfig; + } + + protected Map consumerConfig() { + Map consumerConfig = new HashMap<>(); + consumerConfig.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, kafka.getBootstrapServers()); + consumerConfig.put(ConsumerConfig.GROUP_ID_CONFIG, "sample-group"); + consumerConfig.put( + ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, ByteArrayDeserializer.class.getName()); + consumerConfig.put( + ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, ByteArrayDeserializer.class.getName()); + consumerConfig.put(ConsumerConfig.MAX_POLL_RECORDS_CONFIG, 2); + consumerConfig.putAll(additionalConfig()); + consumerConfig.merge( + CommonClientConfigs.METRIC_REPORTER_CLASSES_CONFIG, + TestMetricsReporter.class.getName(), + (o, o2) -> o + "," + o2); + return consumerConfig; + } + + @Test + void observeMetrics() { + produceRecords(); + consumeRecords(); + + Set expectedMetricNames = + new HashSet<>( + Arrays.asList( + "kafka.consumer.commit_latency_avg", + "kafka.consumer.commit_latency_max", + "kafka.consumer.commit_rate", + "kafka.consumer.commit_total", + "kafka.consumer.failed_rebalance_rate_per_hour", + "kafka.consumer.failed_rebalance_total", + "kafka.consumer.heartbeat_rate", + "kafka.consumer.heartbeat_response_time_max", + "kafka.consumer.heartbeat_total", + "kafka.consumer.join_rate", + "kafka.consumer.join_time_avg", + "kafka.consumer.join_time_max", + "kafka.consumer.join_total", + "kafka.consumer.last_heartbeat_seconds_ago", + "kafka.consumer.last_rebalance_seconds_ago", + "kafka.consumer.partition_assigned_latency_avg", + "kafka.consumer.partition_assigned_latency_max", + "kafka.consumer.partition_lost_latency_avg", + "kafka.consumer.partition_lost_latency_max", + "kafka.consumer.partition_revoked_latency_avg", + "kafka.consumer.partition_revoked_latency_max", + "kafka.consumer.rebalance_latency_avg", + "kafka.consumer.rebalance_latency_max", + "kafka.consumer.rebalance_latency_total", + "kafka.consumer.rebalance_rate_per_hour", + "kafka.consumer.rebalance_total", + "kafka.consumer.sync_rate", + "kafka.consumer.sync_time_avg", + "kafka.consumer.sync_time_max", + "kafka.consumer.sync_total", + "kafka.consumer.bytes_consumed_rate", + "kafka.consumer.bytes_consumed_total", + "kafka.consumer.fetch_latency_avg", + "kafka.consumer.fetch_latency_max", + "kafka.consumer.fetch_rate", + "kafka.consumer.fetch_size_avg", + "kafka.consumer.fetch_size_max", + "kafka.consumer.fetch_throttle_time_avg", + "kafka.consumer.fetch_throttle_time_max", + "kafka.consumer.fetch_total", + "kafka.consumer.records_consumed_rate", + "kafka.consumer.records_consumed_total", + "kafka.consumer.records_lag", + "kafka.consumer.records_lag_avg", + "kafka.consumer.records_lag_max", + "kafka.consumer.records_lead", + "kafka.consumer.records_lead_avg", + "kafka.consumer.records_lead_min", + "kafka.consumer.records_per_request_avg", + "kafka.consumer.connection_close_rate", + "kafka.consumer.connection_close_total", + "kafka.consumer.connection_count", + "kafka.consumer.connection_creation_rate", + "kafka.consumer.connection_creation_total", + "kafka.consumer.failed_authentication_rate", + "kafka.consumer.failed_authentication_total", + "kafka.consumer.failed_reauthentication_rate", + "kafka.consumer.failed_reauthentication_total", + "kafka.consumer.incoming_byte_rate", + "kafka.consumer.incoming_byte_total", + "kafka.consumer.io_ratio", + "kafka.consumer.io_time_ns_avg", + "kafka.consumer.io_wait_ratio", + "kafka.consumer.io_wait_time_ns_avg", + "kafka.consumer.io_waittime_total", + "kafka.consumer.iotime_total", + "kafka.consumer.last_poll_seconds_ago", + "kafka.consumer.network_io_rate", + "kafka.consumer.network_io_total", + "kafka.consumer.outgoing_byte_rate", + "kafka.consumer.outgoing_byte_total", + "kafka.consumer.poll_idle_ratio_avg", + "kafka.consumer.reauthentication_latency_avg", + "kafka.consumer.reauthentication_latency_max", + "kafka.consumer.request_rate", + "kafka.consumer.request_size_avg", + "kafka.consumer.request_size_max", + "kafka.consumer.request_total", + "kafka.consumer.response_rate", + "kafka.consumer.response_total", + "kafka.consumer.select_rate", + "kafka.consumer.select_total", + "kafka.consumer.successful_authentication_no_reauth_total", + "kafka.consumer.successful_authentication_rate", + "kafka.consumer.successful_authentication_total", + "kafka.consumer.successful_reauthentication_rate", + "kafka.consumer.successful_reauthentication_total", + "kafka.consumer.time_between_poll_avg", + "kafka.consumer.time_between_poll_max", + "kafka.consumer.request_latency_avg", + "kafka.consumer.request_latency_max", + "kafka.producer.batch_size_avg", + "kafka.producer.batch_size_max", + "kafka.producer.batch_split_rate", + "kafka.producer.batch_split_total", + "kafka.producer.buffer_available_bytes", + "kafka.producer.buffer_exhausted_rate", + "kafka.producer.buffer_exhausted_total", + "kafka.producer.buffer_total_bytes", + "kafka.producer.bufferpool_wait_ratio", + "kafka.producer.bufferpool_wait_time_total", + "kafka.producer.compression_rate_avg", + "kafka.producer.connection_close_rate", + "kafka.producer.connection_close_total", + "kafka.producer.connection_count", + "kafka.producer.connection_creation_rate", + "kafka.producer.connection_creation_total", + "kafka.producer.failed_authentication_rate", + "kafka.producer.failed_authentication_total", + "kafka.producer.failed_reauthentication_rate", + "kafka.producer.failed_reauthentication_total", + "kafka.producer.incoming_byte_rate", + "kafka.producer.incoming_byte_total", + "kafka.producer.io_ratio", + "kafka.producer.io_time_ns_avg", + "kafka.producer.io_wait_ratio", + "kafka.producer.io_wait_time_ns_avg", + "kafka.producer.io_waittime_total", + "kafka.producer.iotime_total", + "kafka.producer.metadata_age", + "kafka.producer.network_io_rate", + "kafka.producer.network_io_total", + "kafka.producer.outgoing_byte_rate", + "kafka.producer.outgoing_byte_total", + "kafka.producer.produce_throttle_time_avg", + "kafka.producer.produce_throttle_time_max", + "kafka.producer.reauthentication_latency_avg", + "kafka.producer.reauthentication_latency_max", + "kafka.producer.record_error_rate", + "kafka.producer.record_error_total", + "kafka.producer.record_queue_time_avg", + "kafka.producer.record_queue_time_max", + "kafka.producer.record_retry_rate", + "kafka.producer.record_retry_total", + "kafka.producer.record_send_rate", + "kafka.producer.record_send_total", + "kafka.producer.record_size_avg", + "kafka.producer.record_size_max", + "kafka.producer.records_per_request_avg", + "kafka.producer.request_latency_avg", + "kafka.producer.request_latency_max", + "kafka.producer.request_rate", + "kafka.producer.request_size_avg", + "kafka.producer.request_size_max", + "kafka.producer.request_total", + "kafka.producer.requests_in_flight", + "kafka.producer.response_rate", + "kafka.producer.response_total", + "kafka.producer.select_rate", + "kafka.producer.select_total", + "kafka.producer.successful_authentication_no_reauth_total", + "kafka.producer.successful_authentication_rate", + "kafka.producer.successful_authentication_total", + "kafka.producer.successful_reauthentication_rate", + "kafka.producer.successful_reauthentication_total", + "kafka.producer.waiting_threads", + "kafka.producer.byte_rate", + "kafka.producer.byte_total", + "kafka.producer.compression_rate")); + + List metrics = testing().metrics(); + Set metricNames = metrics.stream().map(MetricData::getName).collect(toSet()); + assertThat(metricNames).containsAll(expectedMetricNames); + + assertThat(metrics) + .allSatisfy( + metricData -> { + Set expectedKeys = + metricData.getData().getPoints().stream() + .findFirst() + .map( + point -> + point.getAttributes().asMap().keySet().stream() + .map(AttributeKey::getKey) + .collect(toSet())) + .orElse(Collections.emptySet()); + assertThat(metricData.getData().getPoints()) + .extracting(PointData::getAttributes) + .extracting( + attributes -> + attributes.asMap().keySet().stream() + .map(AttributeKey::getKey) + .collect(toSet())) + .allSatisfy(attributeKeys -> assertThat(attributeKeys).isEqualTo(expectedKeys)); + }); + + // Print mapping table + printMappingTable(); + } + + private static void produceRecords() { + for (int i = 0; i < 100; i++) { + producer.send( + new ProducerRecord<>( + TOPICS.get(RANDOM.nextInt(TOPICS.size())), + 0, + System.currentTimeMillis(), + "key".getBytes(StandardCharsets.UTF_8), + "value".getBytes(StandardCharsets.UTF_8))); + } + } + + private static void consumeRecords() { + consumer.subscribe(TOPICS); + Instant stopTime = Instant.now().plusSeconds(10); + while (Instant.now().isBefore(stopTime)) { + consumer.poll(1_000); + } + } + + /** + * Print a table mapping kafka metrics to equivalent OpenTelemetry metrics, in markdown format. + */ + private static void printMappingTable() { + StringBuilder sb = new StringBuilder(); + // Append table headers + sb.append( + "| Metric Group | Metric Name | Attribute Keys | Instrument Name | Instrument Description | Instrument Type |") + .append(lineSeparator()) + .append( + "|--------------|-------------|----------------|-----------------|------------------------|-----------------|") + .append(lineSeparator()); + Map> kafkaMetricsByGroup = + TestMetricsReporter.seenMetrics.stream().collect(groupingBy(KafkaMetricId::getGroup)); + List registeredObservables = + OpenTelemetryMetricsReporter.getRegisteredObservables(); + // Iterate through groups in alpha order + for (String group : kafkaMetricsByGroup.keySet().stream().sorted().collect(toList())) { + List kafkaMetricIds = + kafkaMetricsByGroup.get(group).stream() + .sorted( + comparing(KafkaMetricId::getName) + .thenComparing(kafkaMetricId -> kafkaMetricId.getAttributeKeys().size())) + .collect(toList()); + // Iterate through metrics in alpha order by name + for (KafkaMetricId kafkaMetricId : kafkaMetricIds) { + // Find first (there may be multiple) registered instrument that matches the kafkaMetricId + Optional descriptor = + registeredObservables.stream() + .filter( + registeredObservable -> + KafkaMetricId.create(registeredObservable.getKafkaMetricName()) + .equals(kafkaMetricId)) + .findFirst() + .map(RegisteredObservable::getInstrumentDescriptor); + // Append table row + sb.append( + String.format( + "| %s | %s | %s | %s | %s | %s |%n", + "`" + group + "`", + "`" + kafkaMetricId.getName() + "`", + kafkaMetricId.getAttributeKeys().stream() + .map(key -> "`" + key + "`") + .collect(joining(",")), + descriptor.map(i -> "`" + i.getName() + "`").orElse(""), + descriptor.map(InstrumentDescriptor::getDescription).orElse(""), + descriptor.map(i -> "`" + i.getInstrumentType() + "`").orElse(""))); + } + } + logger.info("Mapping table" + System.lineSeparator() + sb); + } + + /** + * This class is internal and is hence not for public use. Its APIs are unstable and can change at + * any time. + */ + public static class TestMetricsReporter implements MetricsReporter { + + private static final Set seenMetrics = new HashSet<>(); + + @Override + public void init(List list) { + list.forEach(this::metricChange); + } + + @Override + public void metricChange(KafkaMetric kafkaMetric) { + seenMetrics.add(KafkaMetricId.create(kafkaMetric.metricName())); + } + + @Override + public void metricRemoval(KafkaMetric kafkaMetric) {} + + @Override + public void close() {} + + @Override + public void configure(Map map) {} + } + + @AutoValue + abstract static class KafkaMetricId { + + abstract String getGroup(); + + abstract String getName(); + + abstract Set getAttributeKeys(); + + static KafkaMetricId create(MetricName metricName) { + return new AutoValue_AbstractOpenTelemetryMetricsReporterTest_KafkaMetricId( + metricName.group(), metricName.name(), metricName.tags().keySet()); + } + } +} 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 a7bf0e6cbde7..b25804f432cb 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 @@ -1,4 +1,75 @@ -# Kafka Metrics +# Library instrumentation for Kafka Clients version 2.6 and higher + +## Quickstart + +### 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). + +For Maven, add to your `pom.xml` dependencies: + +```xml + + + io.opentelemetry.instrumentation + opentelemetry-kafka-clients-2.6 + OPENTELEMETRY_VERSION + + +``` + +For Gradle, add to your dependencies: + +```groovy +implementation("io.opentelemetry.instrumentation:opentelemetry-kafka-clients-2.6:OPENTELEMETRY_VERSION") +``` + +### Usage (Tracing) + +There are two options for capturing traces, either using interceptors or wrapping clients, both described below. + +#### Using interceptors + +The Kafka clients API provides a way to "intercept" messages before they are sent to the brokers as well as messages received from the broker before being passed to the application. +The OpenTelemetry instrumented Kafka library provides two interceptors to be configured to add tracing information automatically. +The interceptor class has to be set in the properties bag used to create the Kafka client. + +Use the `TracingProducerInterceptor` for the producer in order to create a "send" span automatically, each time a message is sent. + +```java +props.setProperty(ProducerConfig.INTERCEPTOR_CLASSES_CONFIG, TracingProducerInterceptor.class.getName()); +``` + +Use the `TracingConsumerInterceptor` for the consumer in order to create a "receive" span automatically, each time a message is received. + +```java +props.setProperty(ConsumerConfig.INTERCEPTOR_CLASSES_CONFIG, TracingConsumerInterceptor.class.getName()); +``` + +#### Wrapping clients + +The other way is by wrapping the Kafka client with a tracing enabled Kafka client. + +Assuming you have a `Producer producer` instance, you can wrap it in the following way. + +```java +KafkaTelemetry telemetry = KafkaTelemetry.create(GlobalOpenTelemetry.get()); +Producer tracingProducer = telemetry.wrap(producer); +``` + +Then use the `tracingProducer` as usual for sending messages to the Kafka cluster. + +Assuming you have a `Consumer consumer` instance, you can wrap it in the following way. + +```java +KafkaTelemetry telemetry = KafkaTelemetry.create(GlobalOpenTelemetry.get()); +Consumer tracingConsumer = telemetry.wrap(this.consumer); +``` + +Then use the `tracingConsumer` as usual for receiving messages from the Kafka cluster. + +### Usage (Metrics) The Kafka client exposes metrics via `org.apache.kafka.common.metrics.MetricsReporter` interface. OpenTelemetry provides an implementation that bridges the metrics into OpenTelemetry. diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/KafkaTelemetry.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/KafkaTelemetry.java index 62cf703a80aa..11904a6a0fde 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/KafkaTelemetry.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/KafkaTelemetry.java @@ -18,6 +18,7 @@ import io.opentelemetry.instrumentation.kafka.internal.KafkaConsumerRecordGetter; import io.opentelemetry.instrumentation.kafka.internal.KafkaHeadersSetter; import io.opentelemetry.instrumentation.kafka.internal.OpenTelemetryMetricsReporter; +import java.lang.reflect.Proxy; import java.util.Collections; import java.util.HashMap; import java.util.Map; @@ -48,14 +49,17 @@ public final class KafkaTelemetry { private final OpenTelemetry openTelemetry; private final Instrumenter, Void> producerInstrumenter; private final Instrumenter, Void> consumerProcessInstrumenter; + private final boolean producerPropagationEnabled; KafkaTelemetry( OpenTelemetry openTelemetry, Instrumenter, Void> producerInstrumenter, - Instrumenter, Void> consumerProcessInstrumenter) { + Instrumenter, Void> consumerProcessInstrumenter, + boolean producerPropagationEnabled) { this.openTelemetry = openTelemetry; this.producerInstrumenter = producerInstrumenter; this.consumerProcessInstrumenter = consumerProcessInstrumenter; + this.producerPropagationEnabled = producerPropagationEnabled; } /** Returns a new {@link KafkaTelemetry} configured with the given {@link OpenTelemetry}. */ @@ -75,13 +79,46 @@ private TextMapPropagator propagator() { } /** Returns a decorated {@link Producer} that emits spans for each sent message. */ + @SuppressWarnings("unchecked") public Producer wrap(Producer producer) { - return new TracingProducer<>(producer, this); + return (Producer) + Proxy.newProxyInstance( + KafkaTelemetry.class.getClassLoader(), + new Class[] {Producer.class}, + (proxy, method, args) -> { + // Future send(ProducerRecord record) + // Future send(ProducerRecord record, Callback callback) + if ("send".equals(method.getName()) + && method.getParameterCount() >= 1 + && method.getParameterTypes()[0] == ProducerRecord.class) { + ProducerRecord record = (ProducerRecord) args[0]; + Callback callback = + method.getParameterCount() >= 2 + && method.getParameterTypes()[1] == Callback.class + ? (Callback) args[1] + : null; + return buildAndInjectSpan(record, callback, producer::send); + } + return method.invoke(producer, args); + }); } /** Returns a decorated {@link Consumer} that consumes spans for each received message. */ + @SuppressWarnings("unchecked") public Consumer wrap(Consumer consumer) { - return new TracingConsumer<>(consumer, this); + return (Consumer) + Proxy.newProxyInstance( + KafkaTelemetry.class.getClassLoader(), + new Class[] {Consumer.class}, + (proxy, method, args) -> { + Object result = method.invoke(consumer, args); + // ConsumerRecords poll(long timeout) + // ConsumerRecords poll(Duration duration) + if ("poll".equals(method.getName()) && result instanceof ConsumerRecords) { + buildAndFinishSpan((ConsumerRecords) result); + } + return result; + }); } /** @@ -116,6 +153,9 @@ public Consumer wrap(Consumer consumer) { CommonClientConfigs.METRIC_REPORTER_CLASSES_CONFIG, OpenTelemetryMetricsReporter.class.getName()); config.put(OpenTelemetryMetricsReporter.CONFIG_KEY_OPENTELEMETRY_INSTANCE, openTelemetry); + config.put( + OpenTelemetryMetricsReporter.CONFIG_KEY_OPENTELEMETRY_INSTRUMENTATION_NAME, + KafkaTelemetryBuilder.INSTRUMENTATION_NAME); return Collections.unmodifiableMap(config); } @@ -125,23 +165,22 @@ public Consumer wrap(Consumer consumer) { * @param record the producer record to inject span info. */ void buildAndInjectSpan(ProducerRecord record) { - Context currentContext = Context.current(); + Context parentContext = Context.current(); - if (!producerInstrumenter.shouldStart(currentContext, record)) { + if (!producerInstrumenter.shouldStart(parentContext, record)) { return; } - Context current = producerInstrumenter.start(currentContext, record); - try (Scope ignored = current.makeCurrent()) { + Context context = producerInstrumenter.start(parentContext, record); + if (producerPropagationEnabled) { try { - propagator().inject(current, record.headers(), SETTER); + propagator().inject(context, record.headers(), SETTER); } catch (Throwable t) { // it can happen if headers are read only (when record is sent second time) logger.log(WARNING, "failed to inject span context. sending record second time?", t); } } - - producerInstrumenter.end(current, record, null, null); + producerInstrumenter.end(context, record, null, null); } /** diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/KafkaTelemetryBuilder.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/KafkaTelemetryBuilder.java index 330c84bcff9d..3b85c656f6ec 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/KafkaTelemetryBuilder.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/KafkaTelemetryBuilder.java @@ -5,6 +5,9 @@ package io.opentelemetry.instrumentation.kafkaclients; +import static java.util.Collections.emptyList; + +import com.google.errorprone.annotations.CanIgnoreReturnValue; import io.opentelemetry.api.OpenTelemetry; import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor; import io.opentelemetry.instrumentation.api.instrumenter.messaging.MessageOperation; @@ -16,13 +19,14 @@ import org.apache.kafka.clients.producer.ProducerRecord; public final class KafkaTelemetryBuilder { - private static final String INSTRUMENTATION_NAME = "io.opentelemetry.kafka-clients-2.6"; + static final String INSTRUMENTATION_NAME = "io.opentelemetry.kafka-clients-2.6"; private final OpenTelemetry openTelemetry; private final List, Void>> producerAttributesExtractors = new ArrayList<>(); private final List, Void>> consumerAttributesExtractors = new ArrayList<>(); + private List capturedHeaders = emptyList(); private boolean captureExperimentalSpanAttributes = false; private boolean propagationEnabled = true; @@ -30,23 +34,37 @@ public final class KafkaTelemetryBuilder { this.openTelemetry = Objects.requireNonNull(openTelemetry); } + @CanIgnoreReturnValue public KafkaTelemetryBuilder addProducerAttributesExtractors( AttributesExtractor, Void> extractor) { producerAttributesExtractors.add(extractor); return this; } + @CanIgnoreReturnValue public KafkaTelemetryBuilder addConsumerAttributesExtractors( AttributesExtractor, Void> extractor) { consumerAttributesExtractors.add(extractor); return this; } + /** + * Configures the messaging headers that will be captured as span attributes. + * + * @param capturedHeaders A list of messaging header names. + */ + @CanIgnoreReturnValue + public KafkaTelemetryBuilder setCapturedHeaders(List capturedHeaders) { + this.capturedHeaders = capturedHeaders; + return this; + } + /** * Sets whether experimental attributes should be set to spans. These attributes may be changed or * removed in the future, so only enable this if you know you do not require attributes filled by * this instrumentation to be stable across versions. */ + @CanIgnoreReturnValue public KafkaTelemetryBuilder setCaptureExperimentalSpanAttributes( boolean captureExperimentalSpanAttributes) { this.captureExperimentalSpanAttributes = captureExperimentalSpanAttributes; @@ -54,9 +72,13 @@ public KafkaTelemetryBuilder setCaptureExperimentalSpanAttributes( } /** - * Sets whether the producer context should be propagated from the producer span to the consumer - * span. Enabled by default. + * Set whether to propagate trace context in producers. Enabled by default. + * + *

You will need to disable this if there are kafka consumers using kafka-clients version prior + * to 0.11, since those old versions do not support headers, and attaching trace context + * propagation headers upstream causes those consumers to fail when reading the messages. */ + @CanIgnoreReturnValue public KafkaTelemetryBuilder setPropagationEnabled(boolean propagationEnabled) { this.propagationEnabled = propagationEnabled; return this; @@ -65,13 +87,14 @@ public KafkaTelemetryBuilder setPropagationEnabled(boolean propagationEnabled) { public KafkaTelemetry build() { KafkaInstrumenterFactory instrumenterFactory = new KafkaInstrumenterFactory(openTelemetry, INSTRUMENTATION_NAME) - .setCaptureExperimentalSpanAttributes(captureExperimentalSpanAttributes) - .setPropagationEnabled(propagationEnabled); + .setCapturedHeaders(capturedHeaders) + .setCaptureExperimentalSpanAttributes(captureExperimentalSpanAttributes); return new KafkaTelemetry( openTelemetry, instrumenterFactory.createProducerInstrumenter(producerAttributesExtractors), instrumenterFactory.createConsumerOperationInstrumenter( - MessageOperation.RECEIVE, consumerAttributesExtractors)); + MessageOperation.RECEIVE, consumerAttributesExtractors), + propagationEnabled); } } diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/TracingConsumer.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/TracingConsumer.java deleted file mode 100644 index 857f438d60b3..000000000000 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/TracingConsumer.java +++ /dev/null @@ -1,282 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.instrumentation.kafkaclients; - -import java.time.Duration; -import java.util.Collection; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.TimeUnit; -import java.util.regex.Pattern; -import org.apache.kafka.clients.consumer.Consumer; -import org.apache.kafka.clients.consumer.ConsumerGroupMetadata; -import org.apache.kafka.clients.consumer.ConsumerRebalanceListener; -import org.apache.kafka.clients.consumer.ConsumerRecords; -import org.apache.kafka.clients.consumer.OffsetAndMetadata; -import org.apache.kafka.clients.consumer.OffsetAndTimestamp; -import org.apache.kafka.clients.consumer.OffsetCommitCallback; -import org.apache.kafka.common.Metric; -import org.apache.kafka.common.MetricName; -import org.apache.kafka.common.PartitionInfo; -import org.apache.kafka.common.TopicPartition; - -class TracingConsumer implements Consumer { - private final Consumer consumer; - private final KafkaTelemetry telemetry; - - TracingConsumer(Consumer consumer, KafkaTelemetry telemetry) { - this.consumer = consumer; - this.telemetry = telemetry; - } - - @Override - public Set assignment() { - return consumer.assignment(); - } - - @Override - public Set subscription() { - return consumer.subscription(); - } - - @Override - public void subscribe(Collection topics, ConsumerRebalanceListener listener) { - consumer.subscribe(topics, listener); - } - - @Override - public void subscribe(Collection topics) { - consumer.subscribe(topics); - } - - @Override - public void subscribe(Pattern pattern, ConsumerRebalanceListener listener) { - consumer.subscribe(pattern, listener); - } - - @Override - public void subscribe(Pattern pattern) { - consumer.subscribe(pattern); - } - - @Override - public void unsubscribe() { - consumer.unsubscribe(); - } - - @Override - public void assign(Collection partitions) { - consumer.assign(partitions); - } - - @Override - @Deprecated - public ConsumerRecords poll(long timeout) { - return poll(Duration.ofMillis(timeout)); - } - - @Override - public ConsumerRecords poll(Duration duration) { - ConsumerRecords records = consumer.poll(duration); - telemetry.buildAndFinishSpan(records); - return records; - } - - @Override - public void commitSync() { - consumer.commitSync(); - } - - @Override - public void commitSync(Duration duration) { - consumer.commitSync(duration); - } - - @Override - public void commitSync(Map offsets) { - consumer.commitSync(offsets); - } - - @Override - public void commitSync(Map map, Duration duration) { - consumer.commitSync(map, duration); - } - - @Override - public void commitAsync() { - consumer.commitAsync(); - } - - @Override - public void commitAsync(OffsetCommitCallback callback) { - consumer.commitAsync(callback); - } - - @Override - public void commitAsync( - Map offsets, OffsetCommitCallback callback) { - consumer.commitAsync(offsets, callback); - } - - @Override - public void seek(TopicPartition partition, long offset) { - consumer.seek(partition, offset); - } - - @Override - public void seek(TopicPartition partition, OffsetAndMetadata offsetAndMetadata) { - consumer.seek(partition, offsetAndMetadata); - } - - @Override - public void seekToBeginning(Collection partitions) { - consumer.seekToBeginning(partitions); - } - - @Override - public void seekToEnd(Collection partitions) { - consumer.seekToEnd(partitions); - } - - @Override - public long position(TopicPartition partition) { - return consumer.position(partition); - } - - @Override - public long position(TopicPartition topicPartition, Duration duration) { - return consumer.position(topicPartition, duration); - } - - @Override - @Deprecated - public OffsetAndMetadata committed(TopicPartition partition) { - return consumer.committed(partition); - } - - @Override - @Deprecated - public OffsetAndMetadata committed(TopicPartition topicPartition, Duration duration) { - return consumer.committed(topicPartition, duration); - } - - @Override - public Map committed(Set partitions) { - return consumer.committed(partitions); - } - - @Override - public Map committed( - Set partitions, Duration timeout) { - return consumer.committed(partitions, timeout); - } - - @Override - public Map metrics() { - return consumer.metrics(); - } - - @Override - public List partitionsFor(String topic) { - return consumer.partitionsFor(topic); - } - - @Override - public List partitionsFor(String s, Duration duration) { - return consumer.partitionsFor(s, duration); - } - - @Override - public Map> listTopics() { - return consumer.listTopics(); - } - - @Override - public Map> listTopics(Duration duration) { - return consumer.listTopics(duration); - } - - @Override - public void pause(Collection partitions) { - consumer.pause(partitions); - } - - @Override - public void resume(Collection partitions) { - consumer.resume(partitions); - } - - @Override - public Set paused() { - return consumer.paused(); - } - - @Override - public Map offsetsForTimes( - Map timestampsToSearch) { - return consumer.offsetsForTimes(timestampsToSearch); - } - - @Override - public Map offsetsForTimes( - Map map, Duration duration) { - return consumer.offsetsForTimes(map, duration); - } - - @Override - public Map beginningOffsets(Collection partitions) { - return consumer.beginningOffsets(partitions); - } - - @Override - public Map beginningOffsets( - Collection collection, Duration duration) { - return consumer.beginningOffsets(collection, duration); - } - - @Override - public Map endOffsets(Collection partitions) { - return consumer.endOffsets(partitions); - } - - @Override - public Map endOffsets( - Collection collection, Duration duration) { - return consumer.endOffsets(collection, duration); - } - - @Override - public ConsumerGroupMetadata groupMetadata() { - return consumer.groupMetadata(); - } - - @Override - public void enforceRebalance() { - consumer.enforceRebalance(); - } - - @Override - public void close() { - consumer.close(); - } - - @Override - @Deprecated - public void close(long l, TimeUnit timeUnit) { - consumer.close(l, timeUnit); - } - - @Override - public void close(Duration duration) { - consumer.close(duration); - } - - @Override - public void wakeup() { - consumer.wakeup(); - } -} diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/TracingProducer.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/TracingProducer.java deleted file mode 100644 index f6c6551b01b1..000000000000 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/TracingProducer.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.instrumentation.kafkaclients; - -import java.time.Duration; -import java.util.List; -import java.util.Map; -import java.util.concurrent.Future; -import org.apache.kafka.clients.consumer.ConsumerGroupMetadata; -import org.apache.kafka.clients.consumer.OffsetAndMetadata; -import org.apache.kafka.clients.producer.Callback; -import org.apache.kafka.clients.producer.Producer; -import org.apache.kafka.clients.producer.ProducerRecord; -import org.apache.kafka.clients.producer.RecordMetadata; -import org.apache.kafka.common.Metric; -import org.apache.kafka.common.MetricName; -import org.apache.kafka.common.PartitionInfo; -import org.apache.kafka.common.TopicPartition; - -class TracingProducer implements Producer { - private final Producer producer; - private final KafkaTelemetry telemetry; - - TracingProducer(Producer producer, KafkaTelemetry telemetry) { - this.producer = producer; - this.telemetry = telemetry; - } - - @Override - public void initTransactions() { - producer.initTransactions(); - } - - @Override - public void beginTransaction() { - producer.beginTransaction(); - } - - @Override - public void sendOffsetsToTransaction( - Map offsets, String consumerGroupId) { - producer.sendOffsetsToTransaction(offsets, consumerGroupId); - } - - @Override - public void sendOffsetsToTransaction( - Map offsets, ConsumerGroupMetadata groupMetadata) { - producer.sendOffsetsToTransaction(offsets, groupMetadata); - } - - @Override - public void commitTransaction() { - producer.commitTransaction(); - } - - @Override - public void abortTransaction() { - producer.abortTransaction(); - } - - @Override - public Future send(ProducerRecord record) { - return send(record, null); - } - - @Override - public Future send(ProducerRecord record, Callback callback) { - return telemetry.buildAndInjectSpan(record, callback, producer::send); - } - - @Override - public void flush() { - producer.flush(); - } - - @Override - public List partitionsFor(String topic) { - return producer.partitionsFor(topic); - } - - @Override - public Map metrics() { - return producer.metrics(); - } - - @Override - public void close() { - producer.close(); - } - - @Override - public void close(Duration duration) { - producer.close(duration); - } -} diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/groovy/io/opentelemetry/instrumentation/kafkaclients/InterceptorsTest.groovy b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/groovy/io/opentelemetry/instrumentation/kafkaclients/InterceptorsTest.groovy index 7b7042497e6b..11df3be889d1 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/groovy/io/opentelemetry/instrumentation/kafkaclients/InterceptorsTest.groovy +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/groovy/io/opentelemetry/instrumentation/kafkaclients/InterceptorsTest.groovy @@ -74,6 +74,7 @@ class InterceptorsTest extends KafkaClientBaseTest implements LibraryTestTrait { "$SemanticAttributes.MESSAGING_SYSTEM" "kafka" "$SemanticAttributes.MESSAGING_DESTINATION" SHARED_TOPIC "$SemanticAttributes.MESSAGING_DESTINATION_KIND" "topic" + "messaging.payload" greeting } } span(2) { @@ -87,6 +88,7 @@ class InterceptorsTest extends KafkaClientBaseTest implements LibraryTestTrait { "$SemanticAttributes.MESSAGING_OPERATION" "receive" "$SemanticAttributes.MESSAGING_MESSAGE_PAYLOAD_SIZE_BYTES" Long "$SemanticAttributes.MESSAGING_KAFKA_PARTITION" { it >= 0 } + "messaging.payload" greeting } } } diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/groovy/io/opentelemetry/instrumentation/kafkaclients/WrappersTest.groovy b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/groovy/io/opentelemetry/instrumentation/kafkaclients/WrappersTest.groovy index 3ea8e4ed0ce3..8d5f4c41afe3 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/groovy/io/opentelemetry/instrumentation/kafkaclients/WrappersTest.groovy +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/groovy/io/opentelemetry/instrumentation/kafkaclients/WrappersTest.groovy @@ -7,6 +7,7 @@ package io.opentelemetry.instrumentation.kafkaclients import io.opentelemetry.instrumentation.test.LibraryTestTrait import io.opentelemetry.semconv.trace.attributes.SemanticAttributes +import java.nio.charset.StandardCharsets import org.apache.kafka.clients.producer.ProducerRecord import spock.lang.Unroll @@ -15,21 +16,27 @@ import java.time.Duration import static io.opentelemetry.api.trace.SpanKind.CONSUMER import static io.opentelemetry.api.trace.SpanKind.INTERNAL import static io.opentelemetry.api.trace.SpanKind.PRODUCER +import static java.util.Collections.singletonList class WrappersTest extends KafkaClientBaseTest implements LibraryTestTrait { @Unroll - def "test wrappers"() throws Exception { + def "test wrappers, test headers: #testHeaders"() throws Exception { KafkaTelemetry telemetry = KafkaTelemetry.builder(getOpenTelemetry()) - // TODO run tests both with and without experimental span attributes - .setCaptureExperimentalSpanAttributes(true) - .build() + .setCapturedHeaders(singletonList("test-message-header")) + // TODO run tests both with and without experimental span attributes + .setCaptureExperimentalSpanAttributes(true) + .build() when: String greeting = "Hello Kafka!" def wrappedProducer = telemetry.wrap(producer) runWithSpan("parent") { - wrappedProducer.send(new ProducerRecord(SHARED_TOPIC, greeting)) { meta, ex -> + def producerRecord = new ProducerRecord(SHARED_TOPIC, greeting) + if (testHeaders) { + producerRecord.headers().add("test-message-header", "test".getBytes(StandardCharsets.UTF_8)) + } + wrappedProducer.send(producerRecord) { meta, ex -> if (ex == null) { runWithSpan("producer callback") {} } else { @@ -65,6 +72,10 @@ class WrappersTest extends KafkaClientBaseTest implements LibraryTestTrait { "$SemanticAttributes.MESSAGING_SYSTEM" "kafka" "$SemanticAttributes.MESSAGING_DESTINATION" SHARED_TOPIC "$SemanticAttributes.MESSAGING_DESTINATION_KIND" "topic" + if (testHeaders) { + "messaging.header.test_message_header" { it == ["test"] } + } + "messaging.payload" greeting } } span(2) { @@ -80,6 +91,10 @@ class WrappersTest extends KafkaClientBaseTest implements LibraryTestTrait { "$SemanticAttributes.MESSAGING_KAFKA_PARTITION" { it >= 0 } "kafka.offset" Long "kafka.record.queue_time_ms" { it >= 0 } + if (testHeaders) { + "messaging.header.test_message_header" { it == ["test"] } + } + "messaging.payload" greeting } } span(3) { @@ -89,6 +104,9 @@ class WrappersTest extends KafkaClientBaseTest implements LibraryTestTrait { } } } + + where: + testHeaders << [false, true] } } diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafka/internal/OpenTelemetryMetricsReporterTest.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafka/internal/OpenTelemetryMetricsReporterTest.java index ddf5bf6f3b13..578ab3824d75 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafka/internal/OpenTelemetryMetricsReporterTest.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafka/internal/OpenTelemetryMetricsReporterTest.java @@ -5,132 +5,30 @@ package io.opentelemetry.instrumentation.kafka.internal; -import static java.lang.System.lineSeparator; -import static java.util.Comparator.comparing; -import static java.util.stream.Collectors.groupingBy; -import static java.util.stream.Collectors.joining; -import static java.util.stream.Collectors.toList; -import static java.util.stream.Collectors.toSet; -import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; -import com.google.auto.value.AutoValue; -import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.instrumentation.kafkaclients.KafkaTelemetry; import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension; -import io.opentelemetry.sdk.metrics.data.MetricData; -import io.opentelemetry.sdk.metrics.data.PointData; -import java.nio.charset.StandardCharsets; -import java.time.Duration; -import java.time.Instant; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; import java.util.Map; -import java.util.Optional; -import java.util.Random; -import java.util.Set; -import org.apache.kafka.clients.CommonClientConfigs; -import org.apache.kafka.clients.consumer.ConsumerConfig; import org.apache.kafka.clients.consumer.KafkaConsumer; import org.apache.kafka.clients.producer.KafkaProducer; -import org.apache.kafka.clients.producer.ProducerConfig; -import org.apache.kafka.clients.producer.ProducerRecord; -import org.apache.kafka.common.MetricName; -import org.apache.kafka.common.metrics.KafkaMetric; -import org.apache.kafka.common.metrics.MetricsReporter; -import org.apache.kafka.common.serialization.ByteArrayDeserializer; -import org.apache.kafka.common.serialization.ByteArraySerializer; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.testcontainers.containers.KafkaContainer; -import org.testcontainers.containers.output.Slf4jLogConsumer; -import org.testcontainers.containers.wait.strategy.Wait; -import org.testcontainers.junit.jupiter.Testcontainers; -import org.testcontainers.utility.DockerImageName; -@Testcontainers -class OpenTelemetryMetricsReporterTest { - - private static final Logger logger = - LoggerFactory.getLogger(OpenTelemetryMetricsReporterTest.class); - - private static final List TOPICS = Arrays.asList("foo", "bar", "baz", "qux"); - private static final Random RANDOM = new Random(); +class OpenTelemetryMetricsReporterTest extends AbstractOpenTelemetryMetricsReporterTest { @RegisterExtension static final InstrumentationExtension testing = LibraryInstrumentationExtension.create(); - private static KafkaContainer kafka; - private static KafkaProducer producer; - private static KafkaConsumer consumer; - - @BeforeAll - static void beforeAll() { - kafka = - new KafkaContainer(DockerImageName.parse("confluentinc/cp-kafka:5.4.3")) - .withLogConsumer(new Slf4jLogConsumer(logger)) - .waitingFor(Wait.forLogMessage(".*started \\(kafka.server.KafkaServer\\).*", 1)) - .withStartupTimeout(Duration.ofMinutes(1)); - kafka.start(); - producer = new KafkaProducer<>(producerConfig()); - consumer = new KafkaConsumer<>(consumerConfig()); - } - - @AfterAll - static void afterAll() { - kafka.stop(); - producer.close(); - consumer.close(); - } - - @AfterEach - void tearDown() { - OpenTelemetryMetricsReporter.resetForTest(); - } - - private static Map producerConfig() { - Map producerConfig = new HashMap<>(); - producerConfig.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, kafka.getBootstrapServers()); - producerConfig.put(ProducerConfig.CLIENT_ID_CONFIG, "sample-client-id"); - producerConfig.put( - ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, ByteArraySerializer.class.getName()); - producerConfig.put( - ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, ByteArraySerializer.class.getName()); - producerConfig.put(ProducerConfig.COMPRESSION_TYPE_CONFIG, "gzip"); - producerConfig.putAll( - KafkaTelemetry.create(testing.getOpenTelemetry()).metricConfigProperties()); - producerConfig.merge( - CommonClientConfigs.METRIC_REPORTER_CLASSES_CONFIG, - TestMetricsReporter.class.getName(), - (o, o2) -> o + "," + o2); - return producerConfig; + @Override + protected InstrumentationExtension testing() { + return testing; } - private static Map consumerConfig() { - Map consumerConfig = new HashMap<>(); - consumerConfig.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, kafka.getBootstrapServers()); - consumerConfig.put(ConsumerConfig.GROUP_ID_CONFIG, "sample-group"); - consumerConfig.put( - ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, ByteArrayDeserializer.class.getName()); - consumerConfig.put( - ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, ByteArrayDeserializer.class.getName()); - consumerConfig.put(ConsumerConfig.MAX_POLL_RECORDS_CONFIG, 2); - consumerConfig.putAll( - KafkaTelemetry.create(testing.getOpenTelemetry()).metricConfigProperties()); - consumerConfig.merge( - CommonClientConfigs.METRIC_REPORTER_CLASSES_CONFIG, - TestMetricsReporter.class.getName(), - (o, o2) -> o + "," + o2); - return consumerConfig; + @Override + protected Map additionalConfig() { + return KafkaTelemetry.create(testing.getOpenTelemetry()).metricConfigProperties(); } @Test @@ -154,6 +52,26 @@ void badConfig() { .hasRootCauseInstanceOf(IllegalStateException.class) .hasRootCauseMessage( "Configuration property opentelemetry.instance is not instance of OpenTelemetry"); + assertThatThrownBy( + () -> { + Map producerConfig = producerConfig(); + producerConfig.remove( + OpenTelemetryMetricsReporter.CONFIG_KEY_OPENTELEMETRY_INSTRUMENTATION_NAME); + new KafkaProducer<>(producerConfig).close(); + }) + .hasRootCauseInstanceOf(IllegalStateException.class) + .hasRootCauseMessage( + "Missing required configuration property: opentelemetry.instrumentation_name"); + assertThatThrownBy( + () -> { + Map producerConfig = producerConfig(); + producerConfig.put( + OpenTelemetryMetricsReporter.CONFIG_KEY_OPENTELEMETRY_INSTRUMENTATION_NAME, 42); + new KafkaProducer<>(producerConfig).close(); + }) + .hasRootCauseInstanceOf(IllegalStateException.class) + .hasRootCauseMessage( + "Configuration property opentelemetry.instrumentation_name is not instance of String"); // Bad consumer config assertThatThrownBy( @@ -174,346 +92,25 @@ void badConfig() { .hasRootCauseInstanceOf(IllegalStateException.class) .hasRootCauseMessage( "Configuration property opentelemetry.instance is not instance of OpenTelemetry"); - } - - @Test - void observeMetrics() { - produceRecords(); - consumeRecords(); - - Set expectedMetricNames = - new HashSet<>( - Arrays.asList( - "kafka.consumer.commit_latency_avg", - "kafka.consumer.commit_latency_max", - "kafka.consumer.commit_rate", - "kafka.consumer.commit_total", - "kafka.consumer.failed_rebalance_rate_per_hour", - "kafka.consumer.failed_rebalance_total", - "kafka.consumer.heartbeat_rate", - "kafka.consumer.heartbeat_response_time_max", - "kafka.consumer.heartbeat_total", - "kafka.consumer.join_rate", - "kafka.consumer.join_time_avg", - "kafka.consumer.join_time_max", - "kafka.consumer.join_total", - "kafka.consumer.last_heartbeat_seconds_ago", - "kafka.consumer.last_rebalance_seconds_ago", - "kafka.consumer.partition_assigned_latency_avg", - "kafka.consumer.partition_assigned_latency_max", - "kafka.consumer.partition_lost_latency_avg", - "kafka.consumer.partition_lost_latency_max", - "kafka.consumer.partition_revoked_latency_avg", - "kafka.consumer.partition_revoked_latency_max", - "kafka.consumer.rebalance_latency_avg", - "kafka.consumer.rebalance_latency_max", - "kafka.consumer.rebalance_latency_total", - "kafka.consumer.rebalance_rate_per_hour", - "kafka.consumer.rebalance_total", - "kafka.consumer.sync_rate", - "kafka.consumer.sync_time_avg", - "kafka.consumer.sync_time_max", - "kafka.consumer.sync_total", - "kafka.consumer.bytes_consumed_rate", - "kafka.consumer.bytes_consumed_total", - "kafka.consumer.fetch_latency_avg", - "kafka.consumer.fetch_latency_max", - "kafka.consumer.fetch_rate", - "kafka.consumer.fetch_size_avg", - "kafka.consumer.fetch_size_max", - "kafka.consumer.fetch_throttle_time_avg", - "kafka.consumer.fetch_throttle_time_max", - "kafka.consumer.fetch_total", - "kafka.consumer.records_consumed_rate", - "kafka.consumer.records_consumed_total", - "kafka.consumer.records_lag", - "kafka.consumer.records_lag_avg", - "kafka.consumer.records_lag_max", - "kafka.consumer.records_lead", - "kafka.consumer.records_lead_avg", - "kafka.consumer.records_lead_min", - "kafka.consumer.records_per_request_avg", - "kafka.consumer.connection_close_rate", - "kafka.consumer.connection_close_total", - "kafka.consumer.connection_count", - "kafka.consumer.connection_creation_rate", - "kafka.consumer.connection_creation_total", - "kafka.consumer.failed_authentication_rate", - "kafka.consumer.failed_authentication_total", - "kafka.consumer.failed_reauthentication_rate", - "kafka.consumer.failed_reauthentication_total", - "kafka.consumer.incoming_byte_rate", - "kafka.consumer.incoming_byte_total", - "kafka.consumer.io_ratio", - "kafka.consumer.io_time_ns_avg", - "kafka.consumer.io_wait_ratio", - "kafka.consumer.io_wait_time_ns_avg", - "kafka.consumer.io_waittime_total", - "kafka.consumer.iotime_total", - "kafka.consumer.last_poll_seconds_ago", - "kafka.consumer.network_io_rate", - "kafka.consumer.network_io_total", - "kafka.consumer.outgoing_byte_rate", - "kafka.consumer.outgoing_byte_total", - "kafka.consumer.poll_idle_ratio_avg", - "kafka.consumer.reauthentication_latency_avg", - "kafka.consumer.reauthentication_latency_max", - "kafka.consumer.request_rate", - "kafka.consumer.request_size_avg", - "kafka.consumer.request_size_max", - "kafka.consumer.request_total", - "kafka.consumer.response_rate", - "kafka.consumer.response_total", - "kafka.consumer.select_rate", - "kafka.consumer.select_total", - "kafka.consumer.successful_authentication_no_reauth_total", - "kafka.consumer.successful_authentication_rate", - "kafka.consumer.successful_authentication_total", - "kafka.consumer.successful_reauthentication_rate", - "kafka.consumer.successful_reauthentication_total", - "kafka.consumer.time_between_poll_avg", - "kafka.consumer.time_between_poll_max", - "kafka.consumer.incoming_byte_rate", - "kafka.consumer.incoming_byte_total", - "kafka.consumer.outgoing_byte_rate", - "kafka.consumer.outgoing_byte_total", - "kafka.consumer.request_latency_avg", - "kafka.consumer.request_latency_max", - "kafka.consumer.request_rate", - "kafka.consumer.request_size_avg", - "kafka.consumer.request_size_max", - "kafka.consumer.request_total", - "kafka.consumer.response_rate", - "kafka.consumer.response_total", - "kafka.producer.batch_size_avg", - "kafka.producer.batch_size_max", - "kafka.producer.batch_split_rate", - "kafka.producer.batch_split_total", - "kafka.producer.buffer_available_bytes", - "kafka.producer.buffer_exhausted_rate", - "kafka.producer.buffer_exhausted_total", - "kafka.producer.buffer_total_bytes", - "kafka.producer.bufferpool_wait_ratio", - "kafka.producer.bufferpool_wait_time_total", - "kafka.producer.compression_rate_avg", - "kafka.producer.connection_close_rate", - "kafka.producer.connection_close_total", - "kafka.producer.connection_count", - "kafka.producer.connection_creation_rate", - "kafka.producer.connection_creation_total", - "kafka.producer.failed_authentication_rate", - "kafka.producer.failed_authentication_total", - "kafka.producer.failed_reauthentication_rate", - "kafka.producer.failed_reauthentication_total", - "kafka.producer.incoming_byte_rate", - "kafka.producer.incoming_byte_total", - "kafka.producer.io_ratio", - "kafka.producer.io_time_ns_avg", - "kafka.producer.io_wait_ratio", - "kafka.producer.io_wait_time_ns_avg", - "kafka.producer.io_waittime_total", - "kafka.producer.iotime_total", - "kafka.producer.metadata_age", - "kafka.producer.network_io_rate", - "kafka.producer.network_io_total", - "kafka.producer.outgoing_byte_rate", - "kafka.producer.outgoing_byte_total", - "kafka.producer.produce_throttle_time_avg", - "kafka.producer.produce_throttle_time_max", - "kafka.producer.reauthentication_latency_avg", - "kafka.producer.reauthentication_latency_max", - "kafka.producer.record_error_rate", - "kafka.producer.record_error_total", - "kafka.producer.record_queue_time_avg", - "kafka.producer.record_queue_time_max", - "kafka.producer.record_retry_rate", - "kafka.producer.record_retry_total", - "kafka.producer.record_send_rate", - "kafka.producer.record_send_total", - "kafka.producer.record_size_avg", - "kafka.producer.record_size_max", - "kafka.producer.records_per_request_avg", - "kafka.producer.request_latency_avg", - "kafka.producer.request_latency_max", - "kafka.producer.request_rate", - "kafka.producer.request_size_avg", - "kafka.producer.request_size_max", - "kafka.producer.request_total", - "kafka.producer.requests_in_flight", - "kafka.producer.response_rate", - "kafka.producer.response_total", - "kafka.producer.select_rate", - "kafka.producer.select_total", - "kafka.producer.successful_authentication_no_reauth_total", - "kafka.producer.successful_authentication_rate", - "kafka.producer.successful_authentication_total", - "kafka.producer.successful_reauthentication_rate", - "kafka.producer.successful_reauthentication_total", - "kafka.producer.waiting_threads", - "kafka.producer.incoming_byte_rate", - "kafka.producer.incoming_byte_total", - "kafka.producer.outgoing_byte_rate", - "kafka.producer.outgoing_byte_total", - "kafka.producer.request_latency_avg", - "kafka.producer.request_latency_max", - "kafka.producer.request_rate", - "kafka.producer.request_size_avg", - "kafka.producer.request_size_max", - "kafka.producer.request_total", - "kafka.producer.response_rate", - "kafka.producer.response_total", - "kafka.producer.byte_rate", - "kafka.producer.byte_total", - "kafka.producer.compression_rate", - "kafka.producer.record_error_rate", - "kafka.producer.record_error_total", - "kafka.producer.record_retry_rate", - "kafka.producer.record_retry_total", - "kafka.producer.record_send_rate", - "kafka.producer.record_send_total")); - - List metrics = testing.metrics(); - Set metricNames = metrics.stream().map(MetricData::getName).collect(toSet()); - assertThat(metricNames).containsAll(expectedMetricNames); - - assertThat(metrics) - .allSatisfy( - metricData -> { - Set expectedKeys = - metricData.getData().getPoints().stream() - .findFirst() - .map( - point -> - point.getAttributes().asMap().keySet().stream() - .map(AttributeKey::getKey) - .collect(toSet())) - .orElse(Collections.emptySet()); - assertThat(metricData.getData().getPoints()) - .extracting(PointData::getAttributes) - .extracting( - attributes -> - attributes.asMap().keySet().stream() - .map(AttributeKey::getKey) - .collect(toSet())) - .allSatisfy(attributeKeys -> assertThat(attributeKeys).isEqualTo(expectedKeys)); - }); - - // Print mapping table - printMappingTable(); - } - - private static void produceRecords() { - for (int i = 0; i < 100; i++) { - producer.send( - new ProducerRecord<>( - TOPICS.get(RANDOM.nextInt(TOPICS.size())), - 0, - System.currentTimeMillis(), - "key".getBytes(StandardCharsets.UTF_8), - "value".getBytes(StandardCharsets.UTF_8))); - } - } - - private static void consumeRecords() { - consumer.subscribe(TOPICS); - Instant stopTime = Instant.now().plusSeconds(10); - while (Instant.now().isBefore(stopTime)) { - consumer.poll(Duration.ofSeconds(1)); - } - } - - /** - * Print a table mapping kafka metrics to equivalent OpenTelemetry metrics, in markdown format. - */ - private static void printMappingTable() { - StringBuilder sb = new StringBuilder(); - // Append table headers - sb.append( - "| Metric Group | Metric Name | Attribute Keys | Instrument Name | Instrument Description | Instrument Type |") - .append(lineSeparator()) - .append( - "|--------------|-------------|----------------|-----------------|------------------------|-----------------|") - .append(lineSeparator()); - Map> kafkaMetricsByGroup = - TestMetricsReporter.seenMetrics.stream().collect(groupingBy(KafkaMetricId::getGroup)); - List registeredObservables = - OpenTelemetryMetricsReporter.getRegisteredObservables(); - // Iterate through groups in alpha order - for (String group : kafkaMetricsByGroup.keySet().stream().sorted().collect(toList())) { - List kafkaMetricIds = - kafkaMetricsByGroup.get(group).stream() - .sorted( - comparing(KafkaMetricId::getName) - .thenComparing(kafkaMetricId -> kafkaMetricId.getAttributeKeys().size())) - .collect(toList()); - // Iterate through metrics in alpha order by name - for (KafkaMetricId kafkaMetricId : kafkaMetricIds) { - // Find first (there may be multiple) registered instrument that matches the kafkaMetricId - Optional descriptor = - registeredObservables.stream() - .filter( - registeredObservable -> - KafkaMetricId.create(registeredObservable.getKafkaMetricName()) - .equals(kafkaMetricId)) - .findFirst() - .map(RegisteredObservable::getInstrumentDescriptor); - // Append table row - sb.append( - String.format( - "| %s | %s | %s | %s | %s | %s |%n", - "`" + group + "`", - "`" + kafkaMetricId.getName() + "`", - kafkaMetricId.getAttributeKeys().stream() - .map(key -> "`" + key + "`") - .collect(joining(",")), - descriptor.map(i -> "`" + i.getName() + "`").orElse(""), - descriptor.map(InstrumentDescriptor::getDescription).orElse(""), - descriptor.map(i -> "`" + i.getInstrumentType() + "`").orElse(""))); - } - } - logger.info("Mapping table" + System.lineSeparator() + sb); - } - - /** - * This class is internal and is hence not for public use. Its APIs are unstable and can change at - * any time. - */ - public static class TestMetricsReporter implements MetricsReporter { - - private static final Set seenMetrics = new HashSet<>(); - - @Override - public void init(List list) { - list.forEach(this::metricChange); - } - - @Override - public void metricChange(KafkaMetric kafkaMetric) { - seenMetrics.add(KafkaMetricId.create(kafkaMetric.metricName())); - } - - @Override - public void metricRemoval(KafkaMetric kafkaMetric) {} - - @Override - public void close() {} - - @Override - public void configure(Map map) {} - } - - @AutoValue - abstract static class KafkaMetricId { - - abstract String getGroup(); - - abstract String getName(); - - abstract Set getAttributeKeys(); - - static KafkaMetricId create(MetricName metricName) { - return new AutoValue_OpenTelemetryMetricsReporterTest_KafkaMetricId( - metricName.group(), metricName.name(), metricName.tags().keySet()); - } + assertThatThrownBy( + () -> { + Map consumerConfig = consumerConfig(); + consumerConfig.remove( + OpenTelemetryMetricsReporter.CONFIG_KEY_OPENTELEMETRY_INSTRUMENTATION_NAME); + new KafkaConsumer<>(consumerConfig).close(); + }) + .hasRootCauseInstanceOf(IllegalStateException.class) + .hasRootCauseMessage( + "Missing required configuration property: opentelemetry.instrumentation_name"); + assertThatThrownBy( + () -> { + Map consumerConfig = consumerConfig(); + consumerConfig.put( + OpenTelemetryMetricsReporter.CONFIG_KEY_OPENTELEMETRY_INSTRUMENTATION_NAME, 42); + new KafkaConsumer<>(consumerConfig).close(); + }) + .hasRootCauseInstanceOf(IllegalStateException.class) + .hasRootCauseMessage( + "Configuration property opentelemetry.instrumentation_name is not instance of String"); } } diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-common/library/src/main/java/io/opentelemetry/instrumentation/kafka/internal/KafkaBatchProcessAttributesGetter.java b/instrumentation/kafka/kafka-clients/kafka-clients-common/library/src/main/java/io/opentelemetry/instrumentation/kafka/internal/KafkaBatchProcessAttributesGetter.java index 15c4306dc1ae..8c3f12ca3bc8 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-common/library/src/main/java/io/opentelemetry/instrumentation/kafka/internal/KafkaBatchProcessAttributesGetter.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-common/library/src/main/java/io/opentelemetry/instrumentation/kafka/internal/KafkaBatchProcessAttributesGetter.java @@ -7,8 +7,11 @@ import io.opentelemetry.instrumentation.api.instrumenter.messaging.MessagingAttributesGetter; import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; +import java.nio.charset.StandardCharsets; +import java.util.List; import java.util.Set; import java.util.stream.Collectors; +import java.util.stream.StreamSupport; import javax.annotation.Nullable; import org.apache.kafka.clients.consumer.ConsumerRecords; import org.apache.kafka.common.TopicPartition; @@ -82,4 +85,20 @@ public Long messagePayloadCompressedSize(ConsumerRecords records) { public String messageId(ConsumerRecords records, @Nullable Void unused) { return null; } + + @Override + public List header(ConsumerRecords records, String name) { + return StreamSupport.stream(records.spliterator(), false) + .flatMap( + consumerRecord -> + StreamSupport.stream(consumerRecord.headers().headers(name).spliterator(), false)) + .map(header -> new String(header.value(), StandardCharsets.UTF_8)) + .collect(Collectors.toList()); + } + + @Nullable + @Override + public String messagePayload(ConsumerRecords consumerRecords) { + return null; + } } diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-common/library/src/main/java/io/opentelemetry/instrumentation/kafka/internal/KafkaBatchProcessSpanLinksExtractor.java b/instrumentation/kafka/kafka-clients/kafka-clients-common/library/src/main/java/io/opentelemetry/instrumentation/kafka/internal/KafkaBatchProcessSpanLinksExtractor.java index dbb638bec1e1..ce60593501b8 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-common/library/src/main/java/io/opentelemetry/instrumentation/kafka/internal/KafkaBatchProcessSpanLinksExtractor.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-common/library/src/main/java/io/opentelemetry/instrumentation/kafka/internal/KafkaBatchProcessSpanLinksExtractor.java @@ -9,6 +9,7 @@ import io.opentelemetry.context.propagation.TextMapPropagator; import io.opentelemetry.instrumentation.api.instrumenter.SpanLinksBuilder; import io.opentelemetry.instrumentation.api.instrumenter.SpanLinksExtractor; +import io.opentelemetry.instrumentation.api.internal.PropagatorBasedSpanLinksExtractor; import org.apache.kafka.clients.consumer.ConsumerRecord; import org.apache.kafka.clients.consumer.ConsumerRecords; @@ -19,7 +20,7 @@ final class KafkaBatchProcessSpanLinksExtractor KafkaBatchProcessSpanLinksExtractor(TextMapPropagator propagator) { this.singleRecordLinkExtractor = - SpanLinksExtractor.extractFromRequest(propagator, KafkaConsumerRecordGetter.INSTANCE); + new PropagatorBasedSpanLinksExtractor<>(propagator, KafkaConsumerRecordGetter.INSTANCE); } @Override diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-common/library/src/main/java/io/opentelemetry/instrumentation/kafka/internal/KafkaConsumerAttributesGetter.java b/instrumentation/kafka/kafka-clients/kafka-clients-common/library/src/main/java/io/opentelemetry/instrumentation/kafka/internal/KafkaConsumerAttributesGetter.java index e09bf35d9176..ea210d7b72d1 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-common/library/src/main/java/io/opentelemetry/instrumentation/kafka/internal/KafkaConsumerAttributesGetter.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-common/library/src/main/java/io/opentelemetry/instrumentation/kafka/internal/KafkaConsumerAttributesGetter.java @@ -7,6 +7,10 @@ import io.opentelemetry.instrumentation.api.instrumenter.messaging.MessagingAttributesGetter; import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; +import java.nio.charset.StandardCharsets; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.StreamSupport; import javax.annotation.Nullable; import org.apache.kafka.clients.consumer.ConsumerRecord; @@ -78,4 +82,17 @@ public Long messagePayloadCompressedSize(ConsumerRecord consumerRecord) { public String messageId(ConsumerRecord consumerRecord, @Nullable Void unused) { return null; } + + @Override + public List header(ConsumerRecord consumerRecord, String name) { + return StreamSupport.stream(consumerRecord.headers().headers(name).spliterator(), false) + .map(header -> new String(header.value(), StandardCharsets.UTF_8)) + .collect(Collectors.toList()); + } + + @Nullable + @Override + public String messagePayload(ConsumerRecord consumerRecord) { + return String.valueOf(consumerRecord.value()); + } } diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-common/library/src/main/java/io/opentelemetry/instrumentation/kafka/internal/KafkaInstrumenterFactory.java b/instrumentation/kafka/kafka-clients/kafka-clients-common/library/src/main/java/io/opentelemetry/instrumentation/kafka/internal/KafkaInstrumenterFactory.java index fe49522e9085..0e71e1a31c99 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-common/library/src/main/java/io/opentelemetry/instrumentation/kafka/internal/KafkaInstrumenterFactory.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-common/library/src/main/java/io/opentelemetry/instrumentation/kafka/internal/KafkaInstrumenterFactory.java @@ -5,17 +5,22 @@ package io.opentelemetry.instrumentation.kafka.internal; +import static java.util.Collections.emptyList; + +import com.google.errorprone.annotations.CanIgnoreReturnValue; import io.opentelemetry.api.OpenTelemetry; import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor; import io.opentelemetry.instrumentation.api.instrumenter.ErrorCauseExtractor; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; import io.opentelemetry.instrumentation.api.instrumenter.InstrumenterBuilder; import io.opentelemetry.instrumentation.api.instrumenter.SpanKindExtractor; -import io.opentelemetry.instrumentation.api.instrumenter.SpanLinksExtractor; import io.opentelemetry.instrumentation.api.instrumenter.messaging.MessageOperation; import io.opentelemetry.instrumentation.api.instrumenter.messaging.MessagingAttributesExtractor; +import io.opentelemetry.instrumentation.api.instrumenter.messaging.MessagingAttributesGetter; import io.opentelemetry.instrumentation.api.instrumenter.messaging.MessagingSpanNameExtractor; +import io.opentelemetry.instrumentation.api.internal.PropagatorBasedSpanLinksExtractor; import java.util.Collections; +import java.util.List; import org.apache.kafka.clients.consumer.ConsumerRecord; import org.apache.kafka.clients.consumer.ConsumerRecords; import org.apache.kafka.clients.producer.ProducerRecord; @@ -28,9 +33,9 @@ public final class KafkaInstrumenterFactory { private final OpenTelemetry openTelemetry; private final String instrumentationName; - private ErrorCauseExtractor errorCauseExtractor = ErrorCauseExtractor.jdk(); + private ErrorCauseExtractor errorCauseExtractor = ErrorCauseExtractor.getDefault(); + private List capturedHeaders = emptyList(); private boolean captureExperimentalSpanAttributes = false; - private boolean propagationEnabled = true; private boolean messagingReceiveInstrumentationEnabled = false; public KafkaInstrumenterFactory(OpenTelemetry openTelemetry, String instrumentationName) { @@ -38,22 +43,37 @@ public KafkaInstrumenterFactory(OpenTelemetry openTelemetry, String instrumentat this.instrumentationName = instrumentationName; } + @CanIgnoreReturnValue public KafkaInstrumenterFactory setErrorCauseExtractor(ErrorCauseExtractor errorCauseExtractor) { this.errorCauseExtractor = errorCauseExtractor; return this; } + @CanIgnoreReturnValue + public KafkaInstrumenterFactory setCapturedHeaders(List capturedHeaders) { + this.capturedHeaders = capturedHeaders; + return this; + } + + @CanIgnoreReturnValue public KafkaInstrumenterFactory setCaptureExperimentalSpanAttributes( boolean captureExperimentalSpanAttributes) { this.captureExperimentalSpanAttributes = captureExperimentalSpanAttributes; return this; } + /** + * @deprecated if you have a need for this configuration option please open an issue in the opentelemetry-java-instrumentation + * repository. + */ + @Deprecated + @CanIgnoreReturnValue public KafkaInstrumenterFactory setPropagationEnabled(boolean propagationEnabled) { - this.propagationEnabled = propagationEnabled; return this; } + @CanIgnoreReturnValue public KafkaInstrumenterFactory setMessagingReceiveInstrumentationEnabled( boolean messagingReceiveInstrumentationEnabled) { this.messagingReceiveInstrumentationEnabled = messagingReceiveInstrumentationEnabled; @@ -74,11 +94,12 @@ public KafkaInstrumenterFactory setMessagingReceiveInstrumentationEnabled( openTelemetry, instrumentationName, MessagingSpanNameExtractor.create(getter, operation)) - .addAttributesExtractor(MessagingAttributesExtractor.create(getter, operation)) + .addAttributesExtractor( + buildMessagingAttributesExtractor(getter, operation, capturedHeaders)) .addAttributesExtractors(extractors) .addAttributesExtractor(new KafkaProducerAdditionalAttributesExtractor()) .setErrorCauseExtractor(errorCauseExtractor) - .newInstrumenter(SpanKindExtractor.alwaysProducer()); + .buildInstrumenter(SpanKindExtractor.alwaysProducer()); } public Instrumenter, Void> createConsumerReceiveInstrumenter() { @@ -89,10 +110,11 @@ public KafkaInstrumenterFactory setMessagingReceiveInstrumentationEnabled( openTelemetry, instrumentationName, MessagingSpanNameExtractor.create(getter, operation)) - .addAttributesExtractor(MessagingAttributesExtractor.create(getter, operation)) + .addAttributesExtractor( + buildMessagingAttributesExtractor(getter, operation, capturedHeaders)) .setErrorCauseExtractor(errorCauseExtractor) .setEnabled(messagingReceiveInstrumentationEnabled) - .newInstrumenter(SpanKindExtractor.alwaysConsumer()); + .buildInstrumenter(SpanKindExtractor.alwaysConsumer()); } public Instrumenter, Void> createConsumerProcessInstrumenter() { @@ -110,7 +132,8 @@ public KafkaInstrumenterFactory setMessagingReceiveInstrumentationEnabled( openTelemetry, instrumentationName, MessagingSpanNameExtractor.create(getter, operation)) - .addAttributesExtractor(MessagingAttributesExtractor.create(getter, operation)) + .addAttributesExtractor( + buildMessagingAttributesExtractor(getter, operation, capturedHeaders)) .addAttributesExtractor(new KafkaConsumerAdditionalAttributesExtractor()) .addAttributesExtractors(extractors) .setErrorCauseExtractor(errorCauseExtractor); @@ -118,16 +141,14 @@ public KafkaInstrumenterFactory setMessagingReceiveInstrumentationEnabled( builder.addAttributesExtractor(new KafkaConsumerExperimentalAttributesExtractor()); } - if (!propagationEnabled) { - return builder.newInstrumenter(SpanKindExtractor.alwaysConsumer()); - } else if (messagingReceiveInstrumentationEnabled) { + if (messagingReceiveInstrumentationEnabled) { builder.addSpanLinksExtractor( - SpanLinksExtractor.extractFromRequest( + new PropagatorBasedSpanLinksExtractor>( openTelemetry.getPropagators().getTextMapPropagator(), KafkaConsumerRecordGetter.INSTANCE)); - return builder.newInstrumenter(SpanKindExtractor.alwaysConsumer()); + return builder.buildInstrumenter(SpanKindExtractor.alwaysConsumer()); } else { - return builder.newConsumerInstrumenter(KafkaConsumerRecordGetter.INSTANCE); + return builder.buildConsumerInstrumenter(KafkaConsumerRecordGetter.INSTANCE); } } @@ -139,11 +160,21 @@ public KafkaInstrumenterFactory setMessagingReceiveInstrumentationEnabled( openTelemetry, instrumentationName, MessagingSpanNameExtractor.create(getter, operation)) - .addAttributesExtractor(MessagingAttributesExtractor.create(getter, operation)) + .addAttributesExtractor( + buildMessagingAttributesExtractor(getter, operation, capturedHeaders)) .addSpanLinksExtractor( new KafkaBatchProcessSpanLinksExtractor( openTelemetry.getPropagators().getTextMapPropagator())) .setErrorCauseExtractor(errorCauseExtractor) - .newInstrumenter(SpanKindExtractor.alwaysConsumer()); + .buildInstrumenter(SpanKindExtractor.alwaysConsumer()); + } + + private static MessagingAttributesExtractor buildMessagingAttributesExtractor( + MessagingAttributesGetter getter, + MessageOperation operation, + List capturedHeaders) { + return MessagingAttributesExtractor.builder(getter, operation) + .setCapturedHeaders(capturedHeaders) + .build(); } } diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-common/library/src/main/java/io/opentelemetry/instrumentation/kafka/internal/KafkaProducerAttributesGetter.java b/instrumentation/kafka/kafka-clients/kafka-clients-common/library/src/main/java/io/opentelemetry/instrumentation/kafka/internal/KafkaProducerAttributesGetter.java index 9b5252e7b2ce..0fc98a7370c4 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-common/library/src/main/java/io/opentelemetry/instrumentation/kafka/internal/KafkaProducerAttributesGetter.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-common/library/src/main/java/io/opentelemetry/instrumentation/kafka/internal/KafkaProducerAttributesGetter.java @@ -7,6 +7,10 @@ import io.opentelemetry.instrumentation.api.instrumenter.messaging.MessagingAttributesGetter; import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; +import java.nio.charset.StandardCharsets; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.StreamSupport; import javax.annotation.Nullable; import org.apache.kafka.clients.producer.ProducerRecord; @@ -79,4 +83,21 @@ public Long messagePayloadCompressedSize(ProducerRecord producerRecord) { public String messageId(ProducerRecord producerRecord, @Nullable Void unused) { return null; } + + @Override + public List header(ProducerRecord producerRecord, String name) { + return StreamSupport.stream(producerRecord.headers().headers(name).spliterator(), false) + .map(header -> new String(header.value(), StandardCharsets.UTF_8)) + .collect(Collectors.toList()); + } + + @Nullable + @Override + public String messagePayload(ProducerRecord producerRecord) { + if (producerRecord.value() instanceof byte[]) { + return new String((byte[]) producerRecord.value(), StandardCharsets.UTF_8); + } + + return String.valueOf(producerRecord.value()); + } } diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-common/library/src/main/java/io/opentelemetry/instrumentation/kafka/internal/KafkaReceiveAttributesGetter.java b/instrumentation/kafka/kafka-clients/kafka-clients-common/library/src/main/java/io/opentelemetry/instrumentation/kafka/internal/KafkaReceiveAttributesGetter.java index 4365ba09f607..fd542817e7ee 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-common/library/src/main/java/io/opentelemetry/instrumentation/kafka/internal/KafkaReceiveAttributesGetter.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-common/library/src/main/java/io/opentelemetry/instrumentation/kafka/internal/KafkaReceiveAttributesGetter.java @@ -7,8 +7,11 @@ import io.opentelemetry.instrumentation.api.instrumenter.messaging.MessagingAttributesGetter; import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; +import java.nio.charset.StandardCharsets; +import java.util.List; import java.util.Set; import java.util.stream.Collectors; +import java.util.stream.StreamSupport; import javax.annotation.Nullable; import org.apache.kafka.clients.consumer.ConsumerRecords; import org.apache.kafka.common.TopicPartition; @@ -88,4 +91,20 @@ public Long messagePayloadCompressedSize(ConsumerRecords consumerRecords) public String messageId(ConsumerRecords consumerRecords, @Nullable Void unused) { return null; } + + @Override + public List header(ConsumerRecords records, String name) { + return StreamSupport.stream(records.spliterator(), false) + .flatMap( + consumerRecord -> + StreamSupport.stream(consumerRecord.headers().headers(name).spliterator(), false)) + .map(header -> new String(header.value(), StandardCharsets.UTF_8)) + .collect(Collectors.toList()); + } + + @Nullable + @Override + public String messagePayload(ConsumerRecords consumerRecords) { + return null; + } } diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-common/library/src/main/java/io/opentelemetry/instrumentation/kafka/internal/OpenTelemetryMetricsReporter.java b/instrumentation/kafka/kafka-clients/kafka-clients-common/library/src/main/java/io/opentelemetry/instrumentation/kafka/internal/OpenTelemetryMetricsReporter.java index 1dc14200dc20..baf6e11c1960 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-common/library/src/main/java/io/opentelemetry/instrumentation/kafka/internal/OpenTelemetryMetricsReporter.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-common/library/src/main/java/io/opentelemetry/instrumentation/kafka/internal/OpenTelemetryMetricsReporter.java @@ -8,6 +8,8 @@ import io.opentelemetry.api.OpenTelemetry; import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.api.metrics.Meter; +import io.opentelemetry.api.metrics.MeterBuilder; +import io.opentelemetry.instrumentation.api.internal.EmbeddedInstrumentationProperties; import io.opentelemetry.instrumentation.api.internal.GuardedBy; import java.util.ArrayList; import java.util.Iterator; @@ -25,7 +27,7 @@ *

To configure, use: * *

{@code
- * // KafkaTelemetry.KafkaTelemetry.create(OpenTelemetry).metricConfigProperties()
+ * // KafkaTelemetry.create(OpenTelemetry).metricConfigProperties()
  * }
* *

This class is internal and is hence not for public use. Its APIs are unstable and can change @@ -34,6 +36,8 @@ public final class OpenTelemetryMetricsReporter implements MetricsReporter { public static final String CONFIG_KEY_OPENTELEMETRY_INSTANCE = "opentelemetry.instance"; + public static final String CONFIG_KEY_OPENTELEMETRY_INSTRUMENTATION_NAME = + "opentelemetry.instrumentation_name"; private static final Logger logger = Logger.getLogger(OpenTelemetryMetricsReporter.class.getName()); @@ -45,7 +49,7 @@ public final class OpenTelemetryMetricsReporter implements MetricsReporter { private static final List registeredObservables = new ArrayList<>(); /** - * Reset for test by reseting the {@link #meter} to {@code null} and closing all registered + * Reset for test by resetting the {@link #meter} to {@code null} and closing all registered * instruments. */ static void resetForTest() { @@ -146,17 +150,30 @@ private static void closeInstrument(AutoCloseable observable) { @Override public void configure(Map configs) { - Object openTelemetry = configs.get(CONFIG_KEY_OPENTELEMETRY_INSTANCE); - if (openTelemetry == null) { - throw new IllegalStateException( - "Missing required configuration property: " + CONFIG_KEY_OPENTELEMETRY_INSTANCE); + OpenTelemetry openTelemetry = + getProperty(configs, CONFIG_KEY_OPENTELEMETRY_INSTANCE, OpenTelemetry.class); + String instrumentationName = + getProperty(configs, CONFIG_KEY_OPENTELEMETRY_INSTRUMENTATION_NAME, String.class); + String instrumentationVersion = + EmbeddedInstrumentationProperties.findVersion(instrumentationName); + + MeterBuilder meterBuilder = openTelemetry.meterBuilder(instrumentationName); + if (instrumentationVersion != null) { + meterBuilder.setInstrumentationVersion(instrumentationVersion); + } + meter = meterBuilder.build(); + } + + @SuppressWarnings("unchecked") + private static T getProperty(Map configs, String key, Class requiredType) { + Object value = configs.get(key); + if (value == null) { + throw new IllegalStateException("Missing required configuration property: " + key); } - if (!(openTelemetry instanceof OpenTelemetry)) { + if (!requiredType.isInstance(value)) { throw new IllegalStateException( - "Configuration property " - + CONFIG_KEY_OPENTELEMETRY_INSTANCE - + " is not instance of OpenTelemetry"); + "Configuration property " + key + " is not instance of " + requiredType.getSimpleName()); } - meter = ((OpenTelemetry) openTelemetry).getMeter("io.opentelemetry.kafka-clients"); + return (T) value; } } diff --git a/instrumentation/kafka/kafka-streams-0.11/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/kafkastreams/KafkaStreamsSingletons.java b/instrumentation/kafka/kafka-streams-0.11/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/kafkastreams/KafkaStreamsSingletons.java index b1209af17084..ffd91f51822a 100644 --- a/instrumentation/kafka/kafka-streams-0.11/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/kafkastreams/KafkaStreamsSingletons.java +++ b/instrumentation/kafka/kafka-streams-0.11/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/kafkastreams/KafkaStreamsSingletons.java @@ -18,12 +18,10 @@ public final class KafkaStreamsSingletons { private static final Instrumenter, Void> INSTRUMENTER = new KafkaInstrumenterFactory(GlobalOpenTelemetry.get(), INSTRUMENTATION_NAME) + .setCapturedHeaders(ExperimentalConfig.get().getMessagingHeaders()) .setCaptureExperimentalSpanAttributes( InstrumentationConfig.get() .getBoolean("otel.instrumentation.kafka.experimental-span-attributes", false)) - .setPropagationEnabled( - InstrumentationConfig.get() - .getBoolean("otel.instrumentation.kafka.client-propagation.enabled", true)) .setMessagingReceiveInstrumentationEnabled( ExperimentalConfig.get().messagingReceiveInstrumentationEnabled()) .createConsumerProcessInstrumenter(); diff --git a/instrumentation/kafka/kafka-streams-0.11/javaagent/src/test/groovy/KafkaStreamsDefaultTest.groovy b/instrumentation/kafka/kafka-streams-0.11/javaagent/src/test/groovy/KafkaStreamsDefaultTest.groovy index 4b7a38f4df68..9e440ee3821b 100644 --- a/instrumentation/kafka/kafka-streams-0.11/javaagent/src/test/groovy/KafkaStreamsDefaultTest.groovy +++ b/instrumentation/kafka/kafka-streams-0.11/javaagent/src/test/groovy/KafkaStreamsDefaultTest.groovy @@ -100,6 +100,7 @@ class KafkaStreamsDefaultTest extends KafkaStreamsBaseTest { "$SemanticAttributes.MESSAGING_SYSTEM" "kafka" "$SemanticAttributes.MESSAGING_DESTINATION" STREAM_PENDING "$SemanticAttributes.MESSAGING_DESTINATION_KIND" "topic" + "messaging.payload" greeting } } @@ -134,6 +135,7 @@ class KafkaStreamsDefaultTest extends KafkaStreamsBaseTest { "kafka.offset" 0 "kafka.record.queue_time_ms" { it >= 0 } "asdf" "testing" + "messaging.payload" greeting } } // kafka-clients PRODUCER @@ -145,6 +147,7 @@ class KafkaStreamsDefaultTest extends KafkaStreamsBaseTest { "$SemanticAttributes.MESSAGING_SYSTEM" "kafka" "$SemanticAttributes.MESSAGING_DESTINATION" STREAM_PROCESSED "$SemanticAttributes.MESSAGING_DESTINATION_KIND" "topic" + "messaging.payload" greeting.toLowerCase() } } @@ -179,6 +182,7 @@ class KafkaStreamsDefaultTest extends KafkaStreamsBaseTest { "kafka.offset" 0 "kafka.record.queue_time_ms" { it >= 0 } "testing" 123 + "messaging.payload" greeting.toLowerCase() } } } diff --git a/instrumentation/kafka/kafka-streams-0.11/javaagent/src/test/groovy/KafkaStreamsSuppressReceiveSpansTest.groovy b/instrumentation/kafka/kafka-streams-0.11/javaagent/src/test/groovy/KafkaStreamsSuppressReceiveSpansTest.groovy index a73ce55234ca..a39b00078817 100644 --- a/instrumentation/kafka/kafka-streams-0.11/javaagent/src/test/groovy/KafkaStreamsSuppressReceiveSpansTest.groovy +++ b/instrumentation/kafka/kafka-streams-0.11/javaagent/src/test/groovy/KafkaStreamsSuppressReceiveSpansTest.groovy @@ -95,6 +95,7 @@ class KafkaStreamsSuppressReceiveSpansTest extends KafkaStreamsBaseTest { "$SemanticAttributes.MESSAGING_SYSTEM" "kafka" "$SemanticAttributes.MESSAGING_DESTINATION" STREAM_PENDING "$SemanticAttributes.MESSAGING_DESTINATION_KIND" "topic" + "messaging.payload" greeting } } // kafka-stream CONSUMER @@ -112,6 +113,7 @@ class KafkaStreamsSuppressReceiveSpansTest extends KafkaStreamsBaseTest { "kafka.offset" 0 "kafka.record.queue_time_ms" { it >= 0 } "asdf" "testing" + "messaging.payload" greeting } } @@ -126,6 +128,7 @@ class KafkaStreamsSuppressReceiveSpansTest extends KafkaStreamsBaseTest { "$SemanticAttributes.MESSAGING_SYSTEM" "kafka" "$SemanticAttributes.MESSAGING_DESTINATION" STREAM_PROCESSED "$SemanticAttributes.MESSAGING_DESTINATION_KIND" "topic" + "messaging.payload" greeting.toLowerCase() } } // kafka-clients CONSUMER process @@ -143,6 +146,7 @@ class KafkaStreamsSuppressReceiveSpansTest extends KafkaStreamsBaseTest { "kafka.offset" 0 "kafka.record.queue_time_ms" { it >= 0 } "testing" 123 + "messaging.payload" greeting.toLowerCase() } } } diff --git a/instrumentation/kotlinx-coroutines/javaagent/src/test/kotlin/io/opentelemetry/javaagent/instrumentation/kotlinxcoroutines/KotlinCoroutinesInstrumentationTest.kt b/instrumentation/kotlinx-coroutines/javaagent/src/test/kotlin/io/opentelemetry/javaagent/instrumentation/kotlinxcoroutines/KotlinCoroutinesInstrumentationTest.kt index 4486778d3c55..8747df688cd4 100644 --- a/instrumentation/kotlinx-coroutines/javaagent/src/test/kotlin/io/opentelemetry/javaagent/instrumentation/kotlinxcoroutines/KotlinCoroutinesInstrumentationTest.kt +++ b/instrumentation/kotlinx-coroutines/javaagent/src/test/kotlin/io/opentelemetry/javaagent/instrumentation/kotlinxcoroutines/KotlinCoroutinesInstrumentationTest.kt @@ -9,8 +9,9 @@ import io.opentelemetry.context.Context import io.opentelemetry.extension.kotlin.asContextElement import io.opentelemetry.instrumentation.reactor.ContextPropagationOperator import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension +import io.opentelemetry.instrumentation.testing.util.TelemetryDataUtil.orderByRootSpanName import io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat -import io.opentelemetry.sdk.trace.data.SpanData +import io.opentelemetry.sdk.testing.assertj.TraceAssert import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope @@ -48,7 +49,6 @@ import org.junit.jupiter.params.provider.Arguments import org.junit.jupiter.params.provider.Arguments.arguments import org.junit.jupiter.params.provider.ArgumentsProvider import org.junit.jupiter.params.provider.ArgumentsSource -import java.time.Duration import java.util.concurrent.Executors import java.util.concurrent.TimeUnit import java.util.function.Consumer @@ -69,7 +69,8 @@ class KotlinCoroutinesInstrumentationTest { singleThread.shutdown() } - @RegisterExtension val testing = AgentInstrumentationExtension.create() + @RegisterExtension + val testing = AgentInstrumentationExtension.create() val tracer = testing.openTelemetry.getTracer("test") @@ -127,7 +128,7 @@ class KotlinCoroutinesInstrumentationTest { assertThat(it) .hasName("consume_2") .hasParent(trace.getSpan(0)) - }, + } ) } ) @@ -160,7 +161,7 @@ class KotlinCoroutinesInstrumentationTest { { it.hasName("preLaunch") .hasParent(trace.getSpan(0)) - }, + } ) } ) @@ -188,7 +189,7 @@ class KotlinCoroutinesInstrumentationTest { { it.hasName("nested") .hasParent(trace.getSpan(0)) - }, + } ) } ) @@ -244,7 +245,7 @@ class KotlinCoroutinesInstrumentationTest { Consumer { assertThat(it).hasName("brokenPromise") .hasParent(trace.getSpan(0)) - }, + } ) } ) @@ -300,7 +301,7 @@ class KotlinCoroutinesInstrumentationTest { assertThat(it) .hasName("timeout3") .hasParent(trace.getSpan(0)) - }, + } ) } ) @@ -325,42 +326,40 @@ class KotlinCoroutinesInstrumentationTest { // should have the same iteration number (attribute "iter"). // The traces are in some random order, so let's keep track and make sure we see // each iteration # exactly once - val assertions = mutableListOf>>() + val assertions = mutableListOf>() for (i in 0 until numIters) { assertions.add { trace -> - assertThat(trace).satisfiesExactly( - Consumer { - assertThat(it) - .hasName("a") + trace.hasSpansSatisfyingExactly( + { + it.hasName("a") .hasNoParent() }, - Consumer { - assertThat(it) - .hasName("a2") - .hasParent(trace.get(0)) - }, + { + it.hasName("a2") + .hasParent(trace.getSpan(0)) + } ) } + } + for (i in 0 until numIters) { assertions.add { trace -> - assertThat(trace).satisfiesExactly( - Consumer { - assertThat(it) - .hasName("b") + trace.hasSpansSatisfyingExactly( + { + it.hasName("b") .hasNoParent() }, - Consumer { - assertThat(it) - .hasName("b2") - .hasParent(trace.get(0)) - }, + { + it.hasName("b2") + .hasParent(trace.getSpan(0)) + } ) } } - await().atMost(Duration.ofSeconds(30)).untilAsserted { - val traces = testing.waitForTraces(assertions.size) - assertThat(traces).satisfiesExactlyInAnyOrder(*assertions.toTypedArray()) - } + testing.waitAndAssertSortedTraces( + orderByRootSpanName("a", "b"), + *assertions.toTypedArray() + ) } @ParameterizedTest @@ -382,7 +381,7 @@ class KotlinCoroutinesInstrumentationTest { { it.hasName("child") .hasParent(trace.getSpan(0)) - }, + } ) } ) @@ -417,7 +416,7 @@ class KotlinCoroutinesInstrumentationTest { { it.hasName("child") .hasParent(trace.getSpan(0)) - }, + } ) } ) @@ -454,7 +453,7 @@ class KotlinCoroutinesInstrumentationTest { { it.hasName("child_2") .hasParent(trace.getSpan(0)) - }, + } ) } ) @@ -526,7 +525,7 @@ class KotlinCoroutinesInstrumentationTest { arguments(DispatcherWrapper(Dispatchers.IO)), arguments(DispatcherWrapper(Dispatchers.Unconfined)), arguments(DispatcherWrapper(threadPool.asCoroutineDispatcher())), - arguments(DispatcherWrapper(singleThread.asCoroutineDispatcher())), + arguments(DispatcherWrapper(singleThread.asCoroutineDispatcher())) ) } diff --git a/instrumentation/ktor/ktor-1.0/library/README.md b/instrumentation/ktor/ktor-1.0/library/README.md index 36a0b69dc7ab..1d48805630fb 100644 --- a/instrumentation/ktor/ktor-1.0/library/README.md +++ b/instrumentation/ktor/ktor-1.0/library/README.md @@ -1,8 +1,33 @@ -# Ktor Instrumentation +# Library Instrumentation for Ktor versions 1.x This package contains libraries to help instrument Ktor. Currently, only server instrumentation is supported. -## Initializing server instrumentation +## Quickstart + +### 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). + +For Maven, add to your `pom.xml` dependencies: + +```xml + + + io.opentelemetry.instrumentation + opentelemetry-ktor-1.0 + OPENTELEMETRY_VERSION + + +``` + +For Gradle, add to your dependencies: + +```groovy +implementation("io.opentelemetry.instrumentation:opentelemetry-ktor-1.0:OPENTELEMETRY_VERSION") +``` + +## Usage Initialize instrumentation by installing the `KtorServerTracing` feature. You must set the `OpenTelemetry` to use with the feature. diff --git a/instrumentation/ktor/ktor-1.0/library/build.gradle.kts b/instrumentation/ktor/ktor-1.0/library/build.gradle.kts index f48168837728..85102d338c4a 100644 --- a/instrumentation/ktor/ktor-1.0/library/build.gradle.kts +++ b/instrumentation/ktor/ktor-1.0/library/build.gradle.kts @@ -18,8 +18,8 @@ dependencies { testLibrary("io.ktor:ktor-server-netty:1.0.0") - latestDepTestLibrary("io.ktor:ktor-server-core:1.+") - latestDepTestLibrary("io.ktor:ktor-server-netty:1.+") + latestDepTestLibrary("io.ktor:ktor-server-core:1.+") // see ktor-2.0 module + latestDepTestLibrary("io.ktor:ktor-server-netty:1.+") // see ktor-2.0 module } tasks { diff --git a/instrumentation/ktor/ktor-1.0/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/v1_0/KtorHttpServerAttributesGetter.kt b/instrumentation/ktor/ktor-1.0/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/v1_0/KtorHttpServerAttributesGetter.kt index 43c992429871..66752972b2cf 100644 --- a/instrumentation/ktor/ktor-1.0/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/v1_0/KtorHttpServerAttributesGetter.kt +++ b/instrumentation/ktor/ktor-1.0/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/v1_0/KtorHttpServerAttributesGetter.kt @@ -23,26 +23,10 @@ internal enum class KtorHttpServerAttributesGetter : return request.headers.getAll(name) ?: emptyList() } - override fun requestContentLength(request: ApplicationRequest, response: ApplicationResponse?): Long? { - return null - } - - override fun requestContentLengthUncompressed(request: ApplicationRequest, response: ApplicationResponse?): Long? { - return null - } - - override fun statusCode(request: ApplicationRequest, response: ApplicationResponse): Int? { + override fun statusCode(request: ApplicationRequest, response: ApplicationResponse, error: Throwable?): Int? { return response.status()?.value } - override fun responseContentLength(request: ApplicationRequest, response: ApplicationResponse): Long? { - return null - } - - override fun responseContentLengthUncompressed(request: ApplicationRequest, response: ApplicationResponse): Long? { - return null - } - override fun responseHeader(request: ApplicationRequest, response: ApplicationResponse, name: String): List { return response.headers.allValues().getAll(name) ?: emptyList() } @@ -66,8 +50,4 @@ internal enum class KtorHttpServerAttributesGetter : override fun scheme(request: ApplicationRequest): String { return request.origin.scheme } - - override fun serverName(request: ApplicationRequest): String? { - return null - } } diff --git a/instrumentation/ktor/ktor-1.0/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/v1_0/KtorNetServerAttributesGetter.kt b/instrumentation/ktor/ktor-1.0/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/v1_0/KtorNetServerAttributesGetter.kt index bc6bd76c10d7..8f82a1e46969 100644 --- a/instrumentation/ktor/ktor-1.0/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/v1_0/KtorNetServerAttributesGetter.kt +++ b/instrumentation/ktor/ktor-1.0/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/v1_0/KtorNetServerAttributesGetter.kt @@ -15,15 +15,19 @@ internal class KtorNetServerAttributesGetter : NetServerAttributesGetter>() - internal val httpAttributesExtractorBuilder = HttpServerAttributesExtractor.builder(KtorHttpServerAttributesGetter.INSTANCE) + internal val httpAttributesExtractorBuilder = HttpServerAttributesExtractor.builder(KtorHttpServerAttributesGetter.INSTANCE, KtorNetServerAttributesGetter()) internal var statusExtractor: (SpanStatusExtractor) -> SpanStatusExtractor = { a -> a } @@ -103,13 +102,12 @@ class KtorServerTracing private constructor( with(instrumenterBuilder) { setSpanStatusExtractor(configuration.statusExtractor(HttpSpanStatusExtractor.create(httpAttributesGetter))) - addAttributesExtractor(NetServerAttributesExtractor.create(KtorNetServerAttributesGetter())) addAttributesExtractor(configuration.httpAttributesExtractorBuilder.build()) addOperationMetrics(HttpServerMetrics.get()) addContextCustomizer(HttpRouteHolder.get()) } - val instrumenter = instrumenterBuilder.newServerInstrumenter(ApplicationRequestGetter) + val instrumenter = instrumenterBuilder.buildServerInstrumenter(ApplicationRequestGetter) val feature = KtorServerTracing(instrumenter) diff --git a/instrumentation/ktor/ktor-2.0/library/README.md b/instrumentation/ktor/ktor-2.0/library/README.md index 36a0b69dc7ab..6fcad026de96 100644 --- a/instrumentation/ktor/ktor-2.0/library/README.md +++ b/instrumentation/ktor/ktor-2.0/library/README.md @@ -1,7 +1,34 @@ -# Ktor Instrumentation +# Library Instrumentation for Ktor version 2.0 and higher This package contains libraries to help instrument Ktor. Currently, only server instrumentation is supported. +## Quickstart + +### 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). + +For Maven, add to your `pom.xml` dependencies: + +```xml + + + io.opentelemetry.instrumentation + opentelemetry-ktor-2.0 + OPENTELEMETRY_VERSION + + +``` + +For Gradle, add to your dependencies: + +```groovy +implementation("io.opentelemetry.instrumentation:opentelemetry-ktor-2.0:OPENTELEMETRY_VERSION") +``` + +## Usage + ## Initializing server instrumentation Initialize instrumentation by installing the `KtorServerTracing` feature. You must set the `OpenTelemetry` to use with diff --git a/instrumentation/ktor/ktor-2.0/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/v2_0/KtorHttpServerAttributesGetter.kt b/instrumentation/ktor/ktor-2.0/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/v2_0/KtorHttpServerAttributesGetter.kt index ee5ac98322fa..e65084c0aed2 100644 --- a/instrumentation/ktor/ktor-2.0/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/v2_0/KtorHttpServerAttributesGetter.kt +++ b/instrumentation/ktor/ktor-2.0/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/v2_0/KtorHttpServerAttributesGetter.kt @@ -23,26 +23,10 @@ internal enum class KtorHttpServerAttributesGetter : return request.headers.getAll(name) ?: emptyList() } - override fun requestContentLength(request: ApplicationRequest, response: ApplicationResponse?): Long? { - return null - } - - override fun requestContentLengthUncompressed(request: ApplicationRequest, response: ApplicationResponse?): Long? { - return null - } - - override fun statusCode(request: ApplicationRequest, response: ApplicationResponse): Int? { + override fun statusCode(request: ApplicationRequest, response: ApplicationResponse, error: Throwable?): Int? { return response.status()?.value } - override fun responseContentLength(request: ApplicationRequest, response: ApplicationResponse): Long? { - return null - } - - override fun responseContentLengthUncompressed(request: ApplicationRequest, response: ApplicationResponse): Long? { - return null - } - override fun responseHeader(request: ApplicationRequest, response: ApplicationResponse, name: String): List { return response.headers.allValues().getAll(name) ?: emptyList() } @@ -66,8 +50,4 @@ internal enum class KtorHttpServerAttributesGetter : override fun scheme(request: ApplicationRequest): String { return request.origin.scheme } - - override fun serverName(request: ApplicationRequest): String? { - return null - } } diff --git a/instrumentation/ktor/ktor-2.0/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/v2_0/KtorNetServerAttributesGetter.kt b/instrumentation/ktor/ktor-2.0/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/v2_0/KtorNetServerAttributesGetter.kt index 34c78570e8c5..3d9a78488dfa 100644 --- a/instrumentation/ktor/ktor-2.0/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/v2_0/KtorNetServerAttributesGetter.kt +++ b/instrumentation/ktor/ktor-2.0/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/v2_0/KtorNetServerAttributesGetter.kt @@ -15,15 +15,19 @@ internal class KtorNetServerAttributesGetter : NetServerAttributesGetter>() - internal val httpAttributesExtractorBuilder = HttpServerAttributesExtractor.builder(KtorHttpServerAttributesGetter.INSTANCE) + internal val httpAttributesExtractorBuilder = HttpServerAttributesExtractor.builder(KtorHttpServerAttributesGetter.INSTANCE, KtorNetServerAttributesGetter()) internal var statusExtractor: (SpanStatusExtractor) -> SpanStatusExtractor = { a -> a } @@ -77,7 +76,7 @@ class KtorServerTracing private constructor( } companion object Feature : BaseApplicationPlugin { - private val INSTRUMENTATION_NAME = "io.opentelemetry.ktor-1.0" + private val INSTRUMENTATION_NAME = "io.opentelemetry.ktor-2.0" private val contextKey = AttributeKey("OpenTelemetry") private val errorKey = AttributeKey("OpenTelemetryException") @@ -103,13 +102,12 @@ class KtorServerTracing private constructor( with(instrumenterBuilder) { setSpanStatusExtractor(configuration.statusExtractor(HttpSpanStatusExtractor.create(httpAttributesGetter))) - addAttributesExtractor(NetServerAttributesExtractor.create(KtorNetServerAttributesGetter())) addAttributesExtractor(configuration.httpAttributesExtractorBuilder.build()) addOperationMetrics(HttpServerMetrics.get()) addContextCustomizer(HttpRouteHolder.get()) } - val instrumenter = instrumenterBuilder.newServerInstrumenter(ApplicationRequestGetter) + val instrumenter = instrumenterBuilder.buildServerInstrumenter(ApplicationRequestGetter) val feature = KtorServerTracing(instrumenter) diff --git a/instrumentation/ktor/ktor-common/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/IpAddressUtil.kt b/instrumentation/ktor/ktor-common/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/IsIpAddress.kt similarity index 100% rename from instrumentation/ktor/ktor-common/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/IpAddressUtil.kt rename to instrumentation/ktor/ktor-common/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/IsIpAddress.kt diff --git a/instrumentation/kubernetes-client-7.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/kubernetesclient/KubernetesClientSingletons.java b/instrumentation/kubernetes-client-7.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/kubernetesclient/KubernetesClientSingletons.java index 3bd7c89dda1e..6e9c03c33f72 100644 --- a/instrumentation/kubernetes-client-7.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/kubernetesclient/KubernetesClientSingletons.java +++ b/instrumentation/kubernetes-client-7.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/kubernetesclient/KubernetesClientSingletons.java @@ -17,6 +17,7 @@ import io.opentelemetry.instrumentation.api.instrumenter.http.HttpClientAttributesExtractor; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanStatusExtractor; import io.opentelemetry.instrumentation.api.instrumenter.net.NetClientAttributesExtractor; +import io.opentelemetry.javaagent.bootstrap.internal.CommonConfig; import io.opentelemetry.javaagent.bootstrap.internal.InstrumentationConfig; import okhttp3.Request; @@ -39,7 +40,11 @@ public class KubernetesClientSingletons { "io.opentelemetry.kubernetes-client-7.0", spanNameExtractor) .setSpanStatusExtractor(HttpSpanStatusExtractor.create(httpAttributesGetter)) - .addAttributesExtractor(HttpClientAttributesExtractor.create(httpAttributesGetter)) + .addAttributesExtractor( + HttpClientAttributesExtractor.builder(httpAttributesGetter) + .setCapturedRequestHeaders(CommonConfig.get().getClientRequestHeaders()) + .setCapturedResponseHeaders(CommonConfig.get().getClientResponseHeaders()) + .build()) .addAttributesExtractor( NetClientAttributesExtractor.create(new KubernetesNetAttributesGetter())); @@ -49,7 +54,7 @@ public class KubernetesClientSingletons { // Initialize with .newInstrumenter(alwaysClient()) instead of .newClientInstrumenter(..) // because Request is immutable so context must be injected manually - INSTRUMENTER = instrumenterBuilder.newInstrumenter(alwaysClient()); + INSTRUMENTER = instrumenterBuilder.buildInstrumenter(alwaysClient()); CONTEXT_PROPAGATORS = GlobalOpenTelemetry.getPropagators(); } diff --git a/instrumentation/kubernetes-client-7.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/kubernetesclient/KubernetesHttpAttributesGetter.java b/instrumentation/kubernetes-client-7.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/kubernetesclient/KubernetesHttpAttributesGetter.java index aa39706afdd7..de72bb4ef5bb 100644 --- a/instrumentation/kubernetes-client-7.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/kubernetesclient/KubernetesHttpAttributesGetter.java +++ b/instrumentation/kubernetes-client-7.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/kubernetesclient/KubernetesHttpAttributesGetter.java @@ -32,41 +32,17 @@ public List requestHeader(Request request, String name) { return request.headers(name); } - @Nullable - @Override - public Long requestContentLength(Request request, @Nullable ApiResponse apiResponse) { - return null; - } - - @Nullable - @Override - public Long requestContentLengthUncompressed( - Request request, @Nullable ApiResponse apiResponse) { - return null; - } - @Override public String flavor(Request request, @Nullable ApiResponse apiResponse) { return SemanticAttributes.HttpFlavorValues.HTTP_1_1; } @Override - public Integer statusCode(Request request, ApiResponse apiResponse) { + public Integer statusCode( + Request request, ApiResponse apiResponse, @Nullable Throwable error) { return apiResponse.getStatusCode(); } - @Nullable - @Override - public Long responseContentLength(Request request, ApiResponse apiResponse) { - return null; - } - - @Nullable - @Override - public Long responseContentLengthUncompressed(Request request, ApiResponse apiResponse) { - return null; - } - @Override public List responseHeader(Request request, ApiResponse apiResponse, String name) { return apiResponse.getHeaders().getOrDefault(name, emptyList()); diff --git a/instrumentation/kubernetes-client-7.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/kubernetesclient/KubernetesNetAttributesGetter.java b/instrumentation/kubernetes-client-7.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/kubernetesclient/KubernetesNetAttributesGetter.java index 701a5dd4fcf3..401e7fa980d3 100644 --- a/instrumentation/kubernetes-client-7.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/kubernetesclient/KubernetesNetAttributesGetter.java +++ b/instrumentation/kubernetes-client-7.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/kubernetesclient/KubernetesNetAttributesGetter.java @@ -19,18 +19,12 @@ public String transport(Request request, @Nullable ApiResponse response) { } @Override - public String peerName(Request request, @Nullable ApiResponse response) { + public String peerName(Request request) { return request.url().host(); } @Override - public Integer peerPort(Request request, @Nullable ApiResponse response) { + public Integer peerPort(Request request) { return request.url().port(); } - - @Nullable - @Override - public String peerIp(Request request, @Nullable ApiResponse response) { - return null; - } } diff --git a/instrumentation/kubernetes-client-7.0/javaagent/src/test/groovy/KubernetesClientTest.groovy b/instrumentation/kubernetes-client-7.0/javaagent/src/test/groovy/KubernetesClientTest.groovy index ccdce4aed8c4..b72ba466b312 100644 --- a/instrumentation/kubernetes-client-7.0/javaagent/src/test/groovy/KubernetesClientTest.groovy +++ b/instrumentation/kubernetes-client-7.0/javaagent/src/test/groovy/KubernetesClientTest.groovy @@ -202,6 +202,7 @@ class KubernetesClientTest extends AgentInstrumentationSpecification { "$SemanticAttributes.NET_TRANSPORT" IP_TCP "$SemanticAttributes.NET_PEER_NAME" "127.0.0.1" "$SemanticAttributes.NET_PEER_PORT" server.httpPort() + "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" Long "kubernetes-client.namespace" "namespace" "kubernetes-client.name" "name" } diff --git a/instrumentation/lettuce/lettuce-4.0/javaagent/build.gradle.kts b/instrumentation/lettuce/lettuce-4.0/javaagent/build.gradle.kts index 34a4d1ff7b22..f37b027f5f1a 100644 --- a/instrumentation/lettuce/lettuce-4.0/javaagent/build.gradle.kts +++ b/instrumentation/lettuce/lettuce-4.0/javaagent/build.gradle.kts @@ -20,5 +20,6 @@ dependencies { tasks.withType().configureEach { // TODO run tests both with and without experimental span attributes jvmArgs("-Dotel.instrumentation.lettuce.experimental-span-attributes=true") + jvmArgs("-Dotel.instrumentation.common.db-statement-sanitizer.enabled=true") usesService(gradle.sharedServices.registrations["testcontainersBuildService"].getService()) } diff --git a/instrumentation/lettuce/lettuce-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/lettuce/v4_0/InstrumentationPoints.java b/instrumentation/lettuce/lettuce-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/lettuce/v4_0/InstrumentationPoints.java index c4d3c499325d..a6f0902635fe 100644 --- a/instrumentation/lettuce/lettuce-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/lettuce/v4_0/InstrumentationPoints.java +++ b/instrumentation/lettuce/lettuce-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/lettuce/v4_0/InstrumentationPoints.java @@ -75,4 +75,6 @@ private static boolean isNonInstrumentingCommand(ProtocolKeyword keyword) { private static boolean isNonInstrumentingKeyword(ProtocolKeyword keyword) { return keyword == SEGFAULT; } + + private InstrumentationPoints() {} } diff --git a/instrumentation/lettuce/lettuce-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/lettuce/v4_0/LettuceConnectNetAttributesGetter.java b/instrumentation/lettuce/lettuce-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/lettuce/v4_0/LettuceConnectNetAttributesGetter.java index 6eff970d3cbc..c4b8a7280a21 100644 --- a/instrumentation/lettuce/lettuce-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/lettuce/v4_0/LettuceConnectNetAttributesGetter.java +++ b/instrumentation/lettuce/lettuce-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/lettuce/v4_0/LettuceConnectNetAttributesGetter.java @@ -18,18 +18,12 @@ public String transport(RedisURI redisUri, @Nullable Void unused) { } @Override - public String peerName(RedisURI redisUri, @Nullable Void unused) { + public String peerName(RedisURI redisUri) { return redisUri.getHost(); } @Override - public Integer peerPort(RedisURI redisUri, @Nullable Void unused) { + public Integer peerPort(RedisURI redisUri) { return redisUri.getPort(); } - - @Override - @Nullable - public String peerIp(RedisURI redisUri, @Nullable Void unused) { - return null; - } } diff --git a/instrumentation/lettuce/lettuce-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/lettuce/v4_0/LettuceSingletons.java b/instrumentation/lettuce/lettuce-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/lettuce/v4_0/LettuceSingletons.java index ce712c011846..3e88ae511399 100644 --- a/instrumentation/lettuce/lettuce-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/lettuce/v4_0/LettuceSingletons.java +++ b/instrumentation/lettuce/lettuce-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/lettuce/v4_0/LettuceSingletons.java @@ -11,11 +11,12 @@ import io.opentelemetry.context.Context; import io.opentelemetry.context.ContextKey; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; -import io.opentelemetry.instrumentation.api.instrumenter.PeerServiceAttributesExtractor; import io.opentelemetry.instrumentation.api.instrumenter.SpanKindExtractor; import io.opentelemetry.instrumentation.api.instrumenter.db.DbClientAttributesExtractor; import io.opentelemetry.instrumentation.api.instrumenter.db.DbClientSpanNameExtractor; import io.opentelemetry.instrumentation.api.instrumenter.net.NetClientAttributesExtractor; +import io.opentelemetry.instrumentation.api.instrumenter.net.PeerServiceAttributesExtractor; +import io.opentelemetry.javaagent.bootstrap.internal.CommonConfig; public final class LettuceSingletons { private static final String INSTRUMENTATION_NAME = "io.opentelemetry.lettuce-4.0"; @@ -35,7 +36,7 @@ public final class LettuceSingletons { INSTRUMENTATION_NAME, DbClientSpanNameExtractor.create(dbAttributesGetter)) .addAttributesExtractor(DbClientAttributesExtractor.create(dbAttributesGetter)) - .newInstrumenter(SpanKindExtractor.alwaysClient()); + .buildInstrumenter(SpanKindExtractor.alwaysClient()); LettuceConnectNetAttributesGetter netAttributesGetter = new LettuceConnectNetAttributesGetter(); @@ -43,9 +44,11 @@ public final class LettuceSingletons { Instrumenter.builder( GlobalOpenTelemetry.get(), INSTRUMENTATION_NAME, redisUri -> "CONNECT") .addAttributesExtractor(NetClientAttributesExtractor.create(netAttributesGetter)) - .addAttributesExtractor(PeerServiceAttributesExtractor.create(netAttributesGetter)) + .addAttributesExtractor( + PeerServiceAttributesExtractor.create( + netAttributesGetter, CommonConfig.get().getPeerServiceMapping())) .addAttributesExtractor(new LettuceConnectAttributesExtractor()) - .newInstrumenter(SpanKindExtractor.alwaysClient()); + .buildInstrumenter(SpanKindExtractor.alwaysClient()); } public static Instrumenter, Void> instrumenter() { diff --git a/instrumentation/lettuce/lettuce-5.0/javaagent/build.gradle.kts b/instrumentation/lettuce/lettuce-5.0/javaagent/build.gradle.kts index 740295761b0c..5b2ef4be39f8 100644 --- a/instrumentation/lettuce/lettuce-5.0/javaagent/build.gradle.kts +++ b/instrumentation/lettuce/lettuce-5.0/javaagent/build.gradle.kts @@ -25,5 +25,6 @@ dependencies { tasks.withType().configureEach { // TODO run tests both with and without experimental span attributes jvmArgs("-Dotel.instrumentation.lettuce.experimental-span-attributes=true") + jvmArgs("-Dotel.instrumentation.common.db-statement-sanitizer.enabled=true") usesService(gradle.sharedServices.registrations["testcontainersBuildService"].getService()) } diff --git a/instrumentation/lettuce/lettuce-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/lettuce/v5_0/LettuceConnectNetAttributesGetter.java b/instrumentation/lettuce/lettuce-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/lettuce/v5_0/LettuceConnectNetAttributesGetter.java index 9a84b513e006..fdfcc2e37d5e 100644 --- a/instrumentation/lettuce/lettuce-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/lettuce/v5_0/LettuceConnectNetAttributesGetter.java +++ b/instrumentation/lettuce/lettuce-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/lettuce/v5_0/LettuceConnectNetAttributesGetter.java @@ -18,18 +18,12 @@ public String transport(RedisURI redisUri, @Nullable Void unused) { } @Override - public String peerName(RedisURI redisUri, @Nullable Void unused) { + public String peerName(RedisURI redisUri) { return redisUri.getHost(); } @Override - public Integer peerPort(RedisURI redisUri, @Nullable Void unused) { + public Integer peerPort(RedisURI redisUri) { return redisUri.getPort(); } - - @Override - @Nullable - public String peerIp(RedisURI redisUri, @Nullable Void unused) { - return null; - } } diff --git a/instrumentation/lettuce/lettuce-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/lettuce/v5_0/LettuceDbAttributesGetter.java b/instrumentation/lettuce/lettuce-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/lettuce/v5_0/LettuceDbAttributesGetter.java index 09aea5fd9a07..f1a55c8192bd 100644 --- a/instrumentation/lettuce/lettuce-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/lettuce/v5_0/LettuceDbAttributesGetter.java +++ b/instrumentation/lettuce/lettuce-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/lettuce/v5_0/LettuceDbAttributesGetter.java @@ -9,6 +9,7 @@ import io.opentelemetry.instrumentation.api.db.RedisCommandSanitizer; import io.opentelemetry.instrumentation.api.instrumenter.db.DbClientAttributesGetter; import io.opentelemetry.instrumentation.lettuce.common.LettuceArgSplitter; +import io.opentelemetry.javaagent.bootstrap.internal.CommonConfig; import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; import java.util.Collections; import java.util.List; @@ -16,6 +17,9 @@ final class LettuceDbAttributesGetter implements DbClientAttributesGetter> { + private static final RedisCommandSanitizer sanitizer = + RedisCommandSanitizer.create(CommonConfig.get().isStatementSanitizationEnabled()); + @Override public String system(RedisCommand request) { return SemanticAttributes.DbSystemValues.REDIS; @@ -46,7 +50,7 @@ public String statement(RedisCommand request) { request.getArgs() == null ? Collections.emptyList() : LettuceArgSplitter.splitArgs(request.getArgs().toCommandString()); - return RedisCommandSanitizer.sanitize(command, args); + return sanitizer.sanitize(command, args); } @Override diff --git a/instrumentation/lettuce/lettuce-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/lettuce/v5_0/LettuceInstrumentationUtil.java b/instrumentation/lettuce/lettuce-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/lettuce/v5_0/LettuceInstrumentationUtil.java index a433a0b092eb..10364ac2da43 100644 --- a/instrumentation/lettuce/lettuce-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/lettuce/v5_0/LettuceInstrumentationUtil.java +++ b/instrumentation/lettuce/lettuce-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/lettuce/v5_0/LettuceInstrumentationUtil.java @@ -11,7 +11,7 @@ import java.util.HashSet; import java.util.Set; -public class LettuceInstrumentationUtil { +public final class LettuceInstrumentationUtil { private static final Set nonInstrumentingCommands = Collections.unmodifiableSet( @@ -46,4 +46,6 @@ public static String getCommandName(RedisCommand command) { } return commandName; } + + private LettuceInstrumentationUtil() {} } diff --git a/instrumentation/lettuce/lettuce-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/lettuce/v5_0/LettuceSingletons.java b/instrumentation/lettuce/lettuce-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/lettuce/v5_0/LettuceSingletons.java index 814103624abc..c0727677f34b 100644 --- a/instrumentation/lettuce/lettuce-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/lettuce/v5_0/LettuceSingletons.java +++ b/instrumentation/lettuce/lettuce-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/lettuce/v5_0/LettuceSingletons.java @@ -11,11 +11,12 @@ import io.opentelemetry.context.Context; import io.opentelemetry.context.ContextKey; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; -import io.opentelemetry.instrumentation.api.instrumenter.PeerServiceAttributesExtractor; import io.opentelemetry.instrumentation.api.instrumenter.SpanKindExtractor; import io.opentelemetry.instrumentation.api.instrumenter.db.DbClientAttributesExtractor; import io.opentelemetry.instrumentation.api.instrumenter.db.DbClientSpanNameExtractor; import io.opentelemetry.instrumentation.api.instrumenter.net.NetClientAttributesExtractor; +import io.opentelemetry.instrumentation.api.instrumenter.net.PeerServiceAttributesExtractor; +import io.opentelemetry.javaagent.bootstrap.internal.CommonConfig; public final class LettuceSingletons { private static final String INSTRUMENTATION_NAME = "io.opentelemetry.lettuce-5.0"; @@ -35,7 +36,7 @@ public final class LettuceSingletons { INSTRUMENTATION_NAME, DbClientSpanNameExtractor.create(dbAttributesGetter)) .addAttributesExtractor(DbClientAttributesExtractor.create(dbAttributesGetter)) - .newInstrumenter(SpanKindExtractor.alwaysClient()); + .buildInstrumenter(SpanKindExtractor.alwaysClient()); LettuceConnectNetAttributesGetter connectNetAttributesGetter = new LettuceConnectNetAttributesGetter(); @@ -45,9 +46,10 @@ public final class LettuceSingletons { GlobalOpenTelemetry.get(), INSTRUMENTATION_NAME, redisUri -> "CONNECT") .addAttributesExtractor(NetClientAttributesExtractor.create(connectNetAttributesGetter)) .addAttributesExtractor( - PeerServiceAttributesExtractor.create(connectNetAttributesGetter)) + PeerServiceAttributesExtractor.create( + connectNetAttributesGetter, CommonConfig.get().getPeerServiceMapping())) .addAttributesExtractor(new LettuceConnectAttributesExtractor()) - .newInstrumenter(SpanKindExtractor.alwaysClient()); + .buildInstrumenter(SpanKindExtractor.alwaysClient()); } public static Instrumenter, Void> instrumenter() { diff --git a/instrumentation/lettuce/lettuce-5.1/javaagent/build.gradle.kts b/instrumentation/lettuce/lettuce-5.1/javaagent/build.gradle.kts index 0f5277dcb2bc..c430cd104249 100644 --- a/instrumentation/lettuce/lettuce-5.1/javaagent/build.gradle.kts +++ b/instrumentation/lettuce/lettuce-5.1/javaagent/build.gradle.kts @@ -26,6 +26,7 @@ dependencies { tasks { test { systemProperty("testLatestDeps", findProperty("testLatestDeps") as Boolean) + jvmArgs("-Dotel.instrumentation.common.db-statement-sanitizer.enabled=true") usesService(gradle.sharedServices.registrations["testcontainersBuildService"].getService()) } } diff --git a/instrumentation/lettuce/lettuce-5.1/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/lettuce/v5_1/LettuceReactiveClientTest.groovy b/instrumentation/lettuce/lettuce-5.1/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/lettuce/v5_1/LettuceReactiveClientTest.groovy index 46be972eabf0..827ff5361147 100644 --- a/instrumentation/lettuce/lettuce-5.1/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/lettuce/v5_1/LettuceReactiveClientTest.groovy +++ b/instrumentation/lettuce/lettuce-5.1/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/lettuce/v5_1/LettuceReactiveClientTest.groovy @@ -45,9 +45,9 @@ class LettuceReactiveClientTest extends AbstractLettuceReactiveClientTest implem childOf span(0) attributes { "$SemanticAttributes.NET_TRANSPORT" IP_TCP - "$SemanticAttributes.NET_PEER_NAME" expectedHostAttributeValue - "$SemanticAttributes.NET_PEER_IP" "127.0.0.1" - "$SemanticAttributes.NET_PEER_PORT" port + "$SemanticAttributes.NET_SOCK_PEER_ADDR" "127.0.0.1" + "$SemanticAttributes.NET_SOCK_PEER_NAME" expectedHostAttributeValue + "$SemanticAttributes.NET_SOCK_PEER_PORT" port "$SemanticAttributes.DB_SYSTEM" "redis" "$SemanticAttributes.DB_STATEMENT" "SET a ?" } @@ -64,9 +64,9 @@ class LettuceReactiveClientTest extends AbstractLettuceReactiveClientTest implem childOf span(0) attributes { "$SemanticAttributes.NET_TRANSPORT" IP_TCP - "$SemanticAttributes.NET_PEER_NAME" expectedHostAttributeValue - "$SemanticAttributes.NET_PEER_IP" "127.0.0.1" - "$SemanticAttributes.NET_PEER_PORT" port + "$SemanticAttributes.NET_SOCK_PEER_ADDR" "127.0.0.1" + "$SemanticAttributes.NET_SOCK_PEER_NAME" expectedHostAttributeValue + "$SemanticAttributes.NET_SOCK_PEER_PORT" port "$SemanticAttributes.DB_SYSTEM" "redis" "$SemanticAttributes.DB_STATEMENT" "GET a" } diff --git a/instrumentation/lettuce/lettuce-5.1/library/build.gradle.kts b/instrumentation/lettuce/lettuce-5.1/library/build.gradle.kts index 716295f4e574..6098b67bddbd 100644 --- a/instrumentation/lettuce/lettuce-5.1/library/build.gradle.kts +++ b/instrumentation/lettuce/lettuce-5.1/library/build.gradle.kts @@ -16,5 +16,6 @@ tasks { test { systemProperty("testLatestDeps", findProperty("testLatestDeps") as Boolean) usesService(gradle.sharedServices.registrations["testcontainersBuildService"].getService()) + jvmArgs("-Dotel.instrumentation.common.db-statement-sanitizer.enabled=true") } } diff --git a/instrumentation/lettuce/lettuce-5.1/library/src/main/java/io/opentelemetry/instrumentation/lettuce/v5_1/LettuceNetAttributesGetter.java b/instrumentation/lettuce/lettuce-5.1/library/src/main/java/io/opentelemetry/instrumentation/lettuce/v5_1/LettuceNetAttributesGetter.java index 9e1cd5f15c3e..af8b387e301b 100644 --- a/instrumentation/lettuce/lettuce-5.1/library/src/main/java/io/opentelemetry/instrumentation/lettuce/v5_1/LettuceNetAttributesGetter.java +++ b/instrumentation/lettuce/lettuce-5.1/library/src/main/java/io/opentelemetry/instrumentation/lettuce/v5_1/LettuceNetAttributesGetter.java @@ -7,12 +7,13 @@ import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.NetTransportValues.IP_TCP; -import io.opentelemetry.instrumentation.api.instrumenter.net.NetClientAttributesGetter; +import io.opentelemetry.instrumentation.api.instrumenter.net.InetSocketAddressNetClientAttributesGetter; import io.opentelemetry.instrumentation.lettuce.v5_1.OpenTelemetryTracing.OpenTelemetryEndpoint; +import java.net.InetSocketAddress; import javax.annotation.Nullable; final class LettuceNetAttributesGetter - implements NetClientAttributesGetter { + extends InetSocketAddressNetClientAttributesGetter { @Override public String transport(OpenTelemetryEndpoint endpoint, @Nullable Void unused) { @@ -21,18 +22,20 @@ public String transport(OpenTelemetryEndpoint endpoint, @Nullable Void unused) { @Nullable @Override - public String peerName(OpenTelemetryEndpoint endpoint, @Nullable Void unused) { - return endpoint.name; + public String peerName(OpenTelemetryEndpoint openTelemetryEndpoint) { + return null; } + @Nullable @Override - public Integer peerPort(OpenTelemetryEndpoint endpoint, @Nullable Void unused) { - return endpoint.port; + public Integer peerPort(OpenTelemetryEndpoint openTelemetryEndpoint) { + return null; } @Nullable @Override - public String peerIp(OpenTelemetryEndpoint endpoint, @Nullable Void unused) { - return endpoint.ip; + protected InetSocketAddress getPeerSocketAddress( + OpenTelemetryEndpoint openTelemetryEndpoint, @Nullable Void unused) { + return openTelemetryEndpoint.address; } } diff --git a/instrumentation/lettuce/lettuce-5.1/library/src/main/java/io/opentelemetry/instrumentation/lettuce/v5_1/LettuceTelemetry.java b/instrumentation/lettuce/lettuce-5.1/library/src/main/java/io/opentelemetry/instrumentation/lettuce/v5_1/LettuceTelemetry.java index 449f3ebdcce8..037e9215d993 100644 --- a/instrumentation/lettuce/lettuce-5.1/library/src/main/java/io/opentelemetry/instrumentation/lettuce/v5_1/LettuceTelemetry.java +++ b/instrumentation/lettuce/lettuce-5.1/library/src/main/java/io/opentelemetry/instrumentation/lettuce/v5_1/LettuceTelemetry.java @@ -9,6 +9,7 @@ import io.opentelemetry.api.OpenTelemetry; import io.opentelemetry.api.trace.Tracer; import io.opentelemetry.api.trace.TracerBuilder; +import io.opentelemetry.instrumentation.api.db.RedisCommandSanitizer; import io.opentelemetry.instrumentation.api.internal.EmbeddedInstrumentationProperties; /** Entrypoint for instrumenting Lettuce or clients. */ @@ -18,18 +19,27 @@ public final class LettuceTelemetry { /** Returns a new {@link LettuceTelemetry} configured with the given {@link OpenTelemetry}. */ public static LettuceTelemetry create(OpenTelemetry openTelemetry) { - return new LettuceTelemetry(openTelemetry); + return builder(openTelemetry).build(); + } + + /** + * Returns a new {@link LettuceTelemetryBuilder} configured with the given {@link OpenTelemetry}. + */ + public static LettuceTelemetryBuilder builder(OpenTelemetry openTelemetry) { + return new LettuceTelemetryBuilder(openTelemetry); } private final Tracer tracer; + private final RedisCommandSanitizer sanitizer; - private LettuceTelemetry(OpenTelemetry openTelemetry) { + LettuceTelemetry(OpenTelemetry openTelemetry, boolean statementSanitizationEnabled) { TracerBuilder tracerBuilder = openTelemetry.tracerBuilder(INSTRUMENTATION_NAME); String version = EmbeddedInstrumentationProperties.findVersion(INSTRUMENTATION_NAME); if (version != null) { tracerBuilder.setInstrumentationVersion(version); } tracer = tracerBuilder.build(); + sanitizer = RedisCommandSanitizer.create(statementSanitizationEnabled); } /** @@ -37,6 +47,6 @@ private LettuceTelemetry(OpenTelemetry openTelemetry) { * io.lettuce.core.resource.ClientResources.Builder#tracing(Tracing)}. */ public Tracing newTracing() { - return new OpenTelemetryTracing(tracer); + return new OpenTelemetryTracing(tracer, sanitizer); } } diff --git a/instrumentation/lettuce/lettuce-5.1/library/src/main/java/io/opentelemetry/instrumentation/lettuce/v5_1/LettuceTelemetryBuilder.java b/instrumentation/lettuce/lettuce-5.1/library/src/main/java/io/opentelemetry/instrumentation/lettuce/v5_1/LettuceTelemetryBuilder.java new file mode 100644 index 000000000000..d86c3f77628b --- /dev/null +++ b/instrumentation/lettuce/lettuce-5.1/library/src/main/java/io/opentelemetry/instrumentation/lettuce/v5_1/LettuceTelemetryBuilder.java @@ -0,0 +1,41 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.lettuce.v5_1; + +import com.google.errorprone.annotations.CanIgnoreReturnValue; +import io.opentelemetry.api.OpenTelemetry; + +/** A builder of {@link LettuceTelemetry}. */ +public final class LettuceTelemetryBuilder { + + private final OpenTelemetry openTelemetry; + + private boolean statementSanitizationEnabled = true; + + LettuceTelemetryBuilder(OpenTelemetry openTelemetry) { + this.openTelemetry = openTelemetry; + } + + /** + * Sets whether the {@code db.statement} attribute on the spans emitted by the constructed {@link + * LettuceTelemetry} should be sanitized. If set to {@code true}, all parameters that can + * potentially contain sensitive information will be masked. Enabled by default. + */ + @CanIgnoreReturnValue + public LettuceTelemetryBuilder setStatementSanitizationEnabled( + boolean statementSanitizationEnabled) { + this.statementSanitizationEnabled = statementSanitizationEnabled; + return this; + } + + /** + * Returns a new {@link LettuceTelemetry} with the settings of this {@link + * LettuceTelemetryBuilder}. + */ + public LettuceTelemetry build() { + return new LettuceTelemetry(openTelemetry, statementSanitizationEnabled); + } +} diff --git a/instrumentation/lettuce/lettuce-5.1/library/src/main/java/io/opentelemetry/instrumentation/lettuce/v5_1/OpenTelemetryTracing.java b/instrumentation/lettuce/lettuce-5.1/library/src/main/java/io/opentelemetry/instrumentation/lettuce/v5_1/OpenTelemetryTracing.java index 6a774492abda..45c90ecf6dd7 100644 --- a/instrumentation/lettuce/lettuce-5.1/library/src/main/java/io/opentelemetry/instrumentation/lettuce/v5_1/OpenTelemetryTracing.java +++ b/instrumentation/lettuce/lettuce-5.1/library/src/main/java/io/opentelemetry/instrumentation/lettuce/v5_1/OpenTelemetryTracing.java @@ -7,6 +7,7 @@ import static io.opentelemetry.instrumentation.lettuce.common.LettuceArgSplitter.splitArgs; +import com.google.errorprone.annotations.CanIgnoreReturnValue; import io.lettuce.core.output.CommandOutput; import io.lettuce.core.protocol.CompleteableCommand; import io.lettuce.core.protocol.RedisCommand; @@ -40,8 +41,8 @@ final class OpenTelemetryTracing implements Tracing { NetClientAttributesExtractor.create(new LettuceNetAttributesGetter()); private final TracerProvider tracerProvider; - OpenTelemetryTracing(io.opentelemetry.api.trace.Tracer tracer) { - this.tracerProvider = new OpenTelemetryTracerProvider(tracer); + OpenTelemetryTracing(io.opentelemetry.api.trace.Tracer tracer, RedisCommandSanitizer sanitizer) { + this.tracerProvider = new OpenTelemetryTracerProvider(tracer, sanitizer); } @Override @@ -69,10 +70,7 @@ public boolean includeCommandArgsInSpanTags() { @Nullable public Endpoint createEndpoint(SocketAddress socketAddress) { if (socketAddress instanceof InetSocketAddress) { - InetSocketAddress address = (InetSocketAddress) socketAddress; - - String ip = address.getAddress() == null ? null : address.getAddress().getHostAddress(); - return new OpenTelemetryEndpoint(ip, address.getPort(), address.getHostString()); + return new OpenTelemetryEndpoint((InetSocketAddress) socketAddress); } return null; } @@ -81,8 +79,9 @@ private static class OpenTelemetryTracerProvider implements TracerProvider { private final Tracer openTelemetryTracer; - OpenTelemetryTracerProvider(io.opentelemetry.api.trace.Tracer tracer) { - openTelemetryTracer = new OpenTelemetryTracer(tracer); + OpenTelemetryTracerProvider( + io.opentelemetry.api.trace.Tracer tracer, RedisCommandSanitizer sanitizer) { + openTelemetryTracer = new OpenTelemetryTracer(tracer, sanitizer); } @Override @@ -112,23 +111,21 @@ public Context getSpanContext() { } static class OpenTelemetryEndpoint implements Endpoint { - @Nullable final String ip; - final int port; - @Nullable final String name; + @Nullable final InetSocketAddress address; - OpenTelemetryEndpoint(@Nullable String ip, int port, @Nullable String name) { - this.ip = ip; - this.port = port; - this.name = name; + OpenTelemetryEndpoint(@Nullable InetSocketAddress address) { + this.address = address; } } private static class OpenTelemetryTracer extends Tracer { private final io.opentelemetry.api.trace.Tracer tracer; + private final RedisCommandSanitizer sanitizer; - OpenTelemetryTracer(io.opentelemetry.api.trace.Tracer tracer) { + OpenTelemetryTracer(io.opentelemetry.api.trace.Tracer tracer, RedisCommandSanitizer sanitizer) { this.tracer = tracer; + this.sanitizer = sanitizer; } @Override @@ -155,7 +152,7 @@ private OpenTelemetrySpan nextSpan(Context context) { .setSpanKind(SpanKind.CLIENT) .setParent(context) .setAttribute(SemanticAttributes.DB_SYSTEM, DbSystemValues.REDIS); - return new OpenTelemetrySpan(context, spanBuilder); + return new OpenTelemetrySpan(context, spanBuilder, sanitizer); } } @@ -167,6 +164,7 @@ private static class OpenTelemetrySpan extends Tracer.Span { private final Context context; private final SpanBuilder spanBuilder; + private final RedisCommandSanitizer sanitizer; @Nullable private String name; @Nullable private List events; @@ -174,12 +172,14 @@ private static class OpenTelemetrySpan extends Tracer.Span { @Nullable private Span span; @Nullable private String args; - OpenTelemetrySpan(Context context, SpanBuilder spanBuilder) { + OpenTelemetrySpan(Context context, SpanBuilder spanBuilder, RedisCommandSanitizer sanitizer) { this.context = context; this.spanBuilder = spanBuilder; + this.sanitizer = sanitizer; } @Override + @CanIgnoreReturnValue public synchronized Tracer.Span name(String name) { if (span != null) { span.updateName(name); @@ -191,6 +191,7 @@ public synchronized Tracer.Span name(String name) { } @Override + @CanIgnoreReturnValue public synchronized Tracer.Span remoteEndpoint(Endpoint endpoint) { if (endpoint instanceof OpenTelemetryEndpoint) { fillEndpoint((OpenTelemetryEndpoint) endpoint); @@ -211,6 +212,7 @@ private void fillEndpoint(OpenTelemetryEndpoint endpoint) { // Added and called in 6.0+ // @Override + @CanIgnoreReturnValue @SuppressWarnings("UnusedMethod") public synchronized Tracer.Span start(RedisCommand command) { start(); @@ -250,6 +252,7 @@ public synchronized Tracer.Span start(RedisCommand command) { // Not called by Lettuce in 6.0+ (though we call it ourselves above). @Override + @CanIgnoreReturnValue public synchronized Tracer.Span start() { span = spanBuilder.startSpan(); if (name != null) { @@ -273,6 +276,7 @@ public synchronized Tracer.Span start() { } @Override + @CanIgnoreReturnValue public synchronized Tracer.Span annotate(String value) { if (span != null) { span.addEvent(value); @@ -287,6 +291,7 @@ public synchronized Tracer.Span annotate(String value) { } @Override + @CanIgnoreReturnValue public synchronized Tracer.Span tag(String key, String value) { if (key.equals("redis.args")) { args = value; @@ -301,6 +306,7 @@ public synchronized Tracer.Span tag(String key, String value) { } @Override + @CanIgnoreReturnValue public synchronized Tracer.Span error(Throwable throwable) { if (span != null) { span.recordException(throwable); @@ -319,7 +325,7 @@ public synchronized void finish() { private void finish(Span span) { if (name != null) { - String statement = RedisCommandSanitizer.sanitize(name, splitArgs(args)); + String statement = sanitizer.sanitize(name, splitArgs(args)); span.setAttribute(SemanticAttributes.DB_STATEMENT, statement); } span.end(); diff --git a/instrumentation/lettuce/lettuce-5.1/testing/build.gradle.kts b/instrumentation/lettuce/lettuce-5.1/testing/build.gradle.kts index e8780d1fa018..c5a1cd5e3b95 100644 --- a/instrumentation/lettuce/lettuce-5.1/testing/build.gradle.kts +++ b/instrumentation/lettuce/lettuce-5.1/testing/build.gradle.kts @@ -14,3 +14,9 @@ dependencies { implementation("io.opentelemetry:opentelemetry-api") implementation("org.spockframework:spock-core") } + +tasks { + test { + jvmArgs("-Dotel.instrumentation.common.db-statement-sanitizer.enabled=true") + } +} diff --git a/instrumentation/lettuce/lettuce-5.1/testing/src/main/groovy/io/opentelemetry/instrumentation/lettuce/v5_1/AbstractLettuceAsyncClientTest.groovy b/instrumentation/lettuce/lettuce-5.1/testing/src/main/groovy/io/opentelemetry/instrumentation/lettuce/v5_1/AbstractLettuceAsyncClientTest.groovy index 047bae7631bd..48e5f750e305 100644 --- a/instrumentation/lettuce/lettuce-5.1/testing/src/main/groovy/io/opentelemetry/instrumentation/lettuce/v5_1/AbstractLettuceAsyncClientTest.groovy +++ b/instrumentation/lettuce/lettuce-5.1/testing/src/main/groovy/io/opentelemetry/instrumentation/lettuce/v5_1/AbstractLettuceAsyncClientTest.groovy @@ -33,7 +33,6 @@ import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.NetTr abstract class AbstractLettuceAsyncClientTest extends InstrumentationSpecification { public static final int DB_INDEX = 0 - public static final String loopback = "127.0.0.1" private static GenericContainer redisServer = new GenericContainer<>("redis:6.2.3-alpine").withExposedPorts(6379) @@ -71,7 +70,7 @@ abstract class AbstractLettuceAsyncClientTest extends InstrumentationSpecificati host = redisServer.getHost() String dbAddr = host + ":" + port + "/" + DB_INDEX embeddedDbUri = "redis://" + dbAddr - expectedHostAttributeValue = host == loopback ? null : host + expectedHostAttributeValue = host == "127.0.0.1" ? null : host incorrectPort = PortUtils.findOpenPort() String dbAddrNonExistent = host + ":" + incorrectPort + "/" + DB_INDEX @@ -161,9 +160,9 @@ abstract class AbstractLettuceAsyncClientTest extends InstrumentationSpecificati kind CLIENT attributes { "$SemanticAttributes.NET_TRANSPORT" IP_TCP - "$SemanticAttributes.NET_PEER_NAME" expectedHostAttributeValue - "$SemanticAttributes.NET_PEER_IP" loopback - "$SemanticAttributes.NET_PEER_PORT" port + "$SemanticAttributes.NET_SOCK_PEER_ADDR" "127.0.0.1" + "$SemanticAttributes.NET_SOCK_PEER_NAME" expectedHostAttributeValue + "$SemanticAttributes.NET_SOCK_PEER_PORT" port "$SemanticAttributes.DB_SYSTEM" "redis" "$SemanticAttributes.DB_STATEMENT" "SET TESTSETKEY ?" } @@ -212,9 +211,9 @@ abstract class AbstractLettuceAsyncClientTest extends InstrumentationSpecificati kind CLIENT attributes { "$SemanticAttributes.NET_TRANSPORT" IP_TCP - "$SemanticAttributes.NET_PEER_NAME" expectedHostAttributeValue - "$SemanticAttributes.NET_PEER_IP" loopback - "$SemanticAttributes.NET_PEER_PORT" port + "$SemanticAttributes.NET_SOCK_PEER_ADDR" "127.0.0.1" + "$SemanticAttributes.NET_SOCK_PEER_NAME" expectedHostAttributeValue + "$SemanticAttributes.NET_SOCK_PEER_PORT" port "$SemanticAttributes.DB_SYSTEM" "redis" "$SemanticAttributes.DB_STATEMENT" "GET TESTKEY" } @@ -287,9 +286,9 @@ abstract class AbstractLettuceAsyncClientTest extends InstrumentationSpecificati childOf(span(0)) attributes { "$SemanticAttributes.NET_TRANSPORT" IP_TCP - "$SemanticAttributes.NET_PEER_NAME" expectedHostAttributeValue - "$SemanticAttributes.NET_PEER_IP" loopback - "$SemanticAttributes.NET_PEER_PORT" port + "$SemanticAttributes.NET_SOCK_PEER_ADDR" "127.0.0.1" + "$SemanticAttributes.NET_SOCK_PEER_NAME" expectedHostAttributeValue + "$SemanticAttributes.NET_SOCK_PEER_PORT" port "$SemanticAttributes.DB_SYSTEM" "redis" "$SemanticAttributes.DB_STATEMENT" "GET NON_EXISTENT_KEY" } @@ -351,9 +350,9 @@ abstract class AbstractLettuceAsyncClientTest extends InstrumentationSpecificati childOf(span(0)) attributes { "$SemanticAttributes.NET_TRANSPORT" IP_TCP - "$SemanticAttributes.NET_PEER_NAME" expectedHostAttributeValue - "$SemanticAttributes.NET_PEER_IP" loopback - "$SemanticAttributes.NET_PEER_PORT" port + "$SemanticAttributes.NET_SOCK_PEER_ADDR" "127.0.0.1" + "$SemanticAttributes.NET_SOCK_PEER_NAME" expectedHostAttributeValue + "$SemanticAttributes.NET_SOCK_PEER_PORT" port "$SemanticAttributes.DB_STATEMENT" "RANDOMKEY" "$SemanticAttributes.DB_SYSTEM" "redis" } @@ -418,9 +417,9 @@ abstract class AbstractLettuceAsyncClientTest extends InstrumentationSpecificati kind CLIENT attributes { "$SemanticAttributes.NET_TRANSPORT" IP_TCP - "$SemanticAttributes.NET_PEER_NAME" expectedHostAttributeValue - "$SemanticAttributes.NET_PEER_IP" loopback - "$SemanticAttributes.NET_PEER_PORT" port + "$SemanticAttributes.NET_SOCK_PEER_ADDR" "127.0.0.1" + "$SemanticAttributes.NET_SOCK_PEER_NAME" expectedHostAttributeValue + "$SemanticAttributes.NET_SOCK_PEER_PORT" port "$SemanticAttributes.DB_SYSTEM" "redis" "$SemanticAttributes.DB_STATEMENT" "HMSET TESTHM firstname ? lastname ? age ?" } @@ -438,9 +437,9 @@ abstract class AbstractLettuceAsyncClientTest extends InstrumentationSpecificati kind CLIENT attributes { "$SemanticAttributes.NET_TRANSPORT" IP_TCP - "$SemanticAttributes.NET_PEER_NAME" expectedHostAttributeValue - "$SemanticAttributes.NET_PEER_IP" loopback - "$SemanticAttributes.NET_PEER_PORT" port + "$SemanticAttributes.NET_SOCK_PEER_ADDR" "127.0.0.1" + "$SemanticAttributes.NET_SOCK_PEER_NAME" expectedHostAttributeValue + "$SemanticAttributes.NET_SOCK_PEER_PORT" port "$SemanticAttributes.DB_SYSTEM" "redis" "$SemanticAttributes.DB_STATEMENT" "HGETALL TESTHM" } diff --git a/instrumentation/lettuce/lettuce-5.1/testing/src/main/groovy/io/opentelemetry/instrumentation/lettuce/v5_1/AbstractLettuceReactiveClientTest.groovy b/instrumentation/lettuce/lettuce-5.1/testing/src/main/groovy/io/opentelemetry/instrumentation/lettuce/v5_1/AbstractLettuceReactiveClientTest.groovy index e040b1b9375d..8fae19f62c53 100644 --- a/instrumentation/lettuce/lettuce-5.1/testing/src/main/groovy/io/opentelemetry/instrumentation/lettuce/v5_1/AbstractLettuceReactiveClientTest.groovy +++ b/instrumentation/lettuce/lettuce-5.1/testing/src/main/groovy/io/opentelemetry/instrumentation/lettuce/v5_1/AbstractLettuceReactiveClientTest.groovy @@ -23,7 +23,6 @@ import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.NetTr abstract class AbstractLettuceReactiveClientTest extends InstrumentationSpecification { public static final int DB_INDEX = 0 - public static final String loopback = "127.0.0.1" private static GenericContainer redisServer = new GenericContainer<>("redis:6.2.3-alpine").withExposedPorts(6379) @@ -48,7 +47,7 @@ abstract class AbstractLettuceReactiveClientTest extends InstrumentationSpecific String host = redisServer.getHost() String dbAddr = host + ":" + port + "/" + DB_INDEX embeddedDbUri = "redis://" + dbAddr - expectedHostAttributeValue = host == loopback ? null : host + expectedHostAttributeValue = host == "127.0.0.1" ? null : host redisClient = createClient(embeddedDbUri) redisClient.setOptions(LettuceTestUtil.CLIENT_OPTIONS) @@ -103,9 +102,9 @@ abstract class AbstractLettuceReactiveClientTest extends InstrumentationSpecific childOf(span(0)) attributes { "$SemanticAttributes.NET_TRANSPORT" IP_TCP - "$SemanticAttributes.NET_PEER_NAME" expectedHostAttributeValue - "$SemanticAttributes.NET_PEER_IP" loopback - "$SemanticAttributes.NET_PEER_PORT" port + "$SemanticAttributes.NET_SOCK_PEER_ADDR" "127.0.0.1" + "$SemanticAttributes.NET_SOCK_PEER_NAME" expectedHostAttributeValue + "$SemanticAttributes.NET_SOCK_PEER_PORT" port "$SemanticAttributes.DB_SYSTEM" "redis" "$SemanticAttributes.DB_STATEMENT" "SET TESTSETKEY ?" } @@ -141,9 +140,9 @@ abstract class AbstractLettuceReactiveClientTest extends InstrumentationSpecific kind CLIENT attributes { "$SemanticAttributes.NET_TRANSPORT" IP_TCP - "$SemanticAttributes.NET_PEER_NAME" expectedHostAttributeValue - "$SemanticAttributes.NET_PEER_IP" loopback - "$SemanticAttributes.NET_PEER_PORT" port + "$SemanticAttributes.NET_SOCK_PEER_ADDR" "127.0.0.1" + "$SemanticAttributes.NET_SOCK_PEER_NAME" expectedHostAttributeValue + "$SemanticAttributes.NET_SOCK_PEER_PORT" port "$SemanticAttributes.DB_SYSTEM" "redis" "$SemanticAttributes.DB_STATEMENT" "GET TESTKEY" } @@ -192,9 +191,9 @@ abstract class AbstractLettuceReactiveClientTest extends InstrumentationSpecific childOf(span(0)) attributes { "$SemanticAttributes.NET_TRANSPORT" IP_TCP - "$SemanticAttributes.NET_PEER_NAME" expectedHostAttributeValue - "$SemanticAttributes.NET_PEER_IP" loopback - "$SemanticAttributes.NET_PEER_PORT" port + "$SemanticAttributes.NET_SOCK_PEER_ADDR" "127.0.0.1" + "$SemanticAttributes.NET_SOCK_PEER_NAME" expectedHostAttributeValue + "$SemanticAttributes.NET_SOCK_PEER_PORT" port "$SemanticAttributes.DB_SYSTEM" "redis" "$SemanticAttributes.DB_STATEMENT" "GET NON_EXISTENT_KEY" } @@ -236,9 +235,9 @@ abstract class AbstractLettuceReactiveClientTest extends InstrumentationSpecific kind CLIENT attributes { "$SemanticAttributes.NET_TRANSPORT" IP_TCP - "$SemanticAttributes.NET_PEER_NAME" expectedHostAttributeValue - "$SemanticAttributes.NET_PEER_IP" loopback - "$SemanticAttributes.NET_PEER_PORT" port + "$SemanticAttributes.NET_SOCK_PEER_ADDR" "127.0.0.1" + "$SemanticAttributes.NET_SOCK_PEER_NAME" expectedHostAttributeValue + "$SemanticAttributes.NET_SOCK_PEER_PORT" port "$SemanticAttributes.DB_STATEMENT" "RANDOMKEY" "$SemanticAttributes.DB_SYSTEM" "redis" } @@ -265,9 +264,9 @@ abstract class AbstractLettuceReactiveClientTest extends InstrumentationSpecific kind CLIENT attributes { "$SemanticAttributes.NET_TRANSPORT" IP_TCP - "$SemanticAttributes.NET_PEER_NAME" expectedHostAttributeValue - "$SemanticAttributes.NET_PEER_IP" loopback - "$SemanticAttributes.NET_PEER_PORT" port + "$SemanticAttributes.NET_SOCK_PEER_ADDR" "127.0.0.1" + "$SemanticAttributes.NET_SOCK_PEER_NAME" expectedHostAttributeValue + "$SemanticAttributes.NET_SOCK_PEER_PORT" port "$SemanticAttributes.DB_STATEMENT" "COMMAND" "$SemanticAttributes.DB_SYSTEM" "redis" } @@ -313,9 +312,9 @@ abstract class AbstractLettuceReactiveClientTest extends InstrumentationSpecific childOf span(0) attributes { "$SemanticAttributes.NET_TRANSPORT" IP_TCP - "$SemanticAttributes.NET_PEER_NAME" expectedHostAttributeValue - "$SemanticAttributes.NET_PEER_IP" loopback - "$SemanticAttributes.NET_PEER_PORT" port + "$SemanticAttributes.NET_SOCK_PEER_ADDR" "127.0.0.1" + "$SemanticAttributes.NET_SOCK_PEER_NAME" expectedHostAttributeValue + "$SemanticAttributes.NET_SOCK_PEER_PORT" port "$SemanticAttributes.DB_SYSTEM" "redis" "$SemanticAttributes.DB_STATEMENT" "SET a ?" } @@ -332,9 +331,9 @@ abstract class AbstractLettuceReactiveClientTest extends InstrumentationSpecific childOf span(0) attributes { "$SemanticAttributes.NET_TRANSPORT" IP_TCP - "$SemanticAttributes.NET_PEER_NAME" expectedHostAttributeValue - "$SemanticAttributes.NET_PEER_IP" loopback - "$SemanticAttributes.NET_PEER_PORT" port + "$SemanticAttributes.NET_SOCK_PEER_ADDR" "127.0.0.1" + "$SemanticAttributes.NET_SOCK_PEER_NAME" expectedHostAttributeValue + "$SemanticAttributes.NET_SOCK_PEER_PORT" port "$SemanticAttributes.DB_SYSTEM" "redis" "$SemanticAttributes.DB_STATEMENT" "GET a" } @@ -371,9 +370,9 @@ abstract class AbstractLettuceReactiveClientTest extends InstrumentationSpecific childOf span(0) attributes { "$SemanticAttributes.NET_TRANSPORT" IP_TCP - "$SemanticAttributes.NET_PEER_NAME" expectedHostAttributeValue - "$SemanticAttributes.NET_PEER_IP" loopback - "$SemanticAttributes.NET_PEER_PORT" port + "$SemanticAttributes.NET_SOCK_PEER_ADDR" "127.0.0.1" + "$SemanticAttributes.NET_SOCK_PEER_NAME" expectedHostAttributeValue + "$SemanticAttributes.NET_SOCK_PEER_PORT" port "$SemanticAttributes.DB_SYSTEM" "redis" "$SemanticAttributes.DB_STATEMENT" "SET a ?" } @@ -390,9 +389,9 @@ abstract class AbstractLettuceReactiveClientTest extends InstrumentationSpecific childOf span(0) attributes { "$SemanticAttributes.NET_TRANSPORT" IP_TCP - "$SemanticAttributes.NET_PEER_NAME" expectedHostAttributeValue - "$SemanticAttributes.NET_PEER_IP" loopback - "$SemanticAttributes.NET_PEER_PORT" port + "$SemanticAttributes.NET_SOCK_PEER_ADDR" "127.0.0.1" + "$SemanticAttributes.NET_SOCK_PEER_NAME" expectedHostAttributeValue + "$SemanticAttributes.NET_SOCK_PEER_PORT" port "$SemanticAttributes.DB_SYSTEM" "redis" "$SemanticAttributes.DB_STATEMENT" "GET a" } diff --git a/instrumentation/lettuce/lettuce-5.1/testing/src/main/groovy/io/opentelemetry/instrumentation/lettuce/v5_1/AbstractLettuceSyncClientAuthTest.groovy b/instrumentation/lettuce/lettuce-5.1/testing/src/main/groovy/io/opentelemetry/instrumentation/lettuce/v5_1/AbstractLettuceSyncClientAuthTest.groovy index b65a77ae4f1c..3251ce57e25a 100644 --- a/instrumentation/lettuce/lettuce-5.1/testing/src/main/groovy/io/opentelemetry/instrumentation/lettuce/v5_1/AbstractLettuceSyncClientAuthTest.groovy +++ b/instrumentation/lettuce/lettuce-5.1/testing/src/main/groovy/io/opentelemetry/instrumentation/lettuce/v5_1/AbstractLettuceSyncClientAuthTest.groovy @@ -16,7 +16,6 @@ import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.NetTr abstract class AbstractLettuceSyncClientAuthTest extends InstrumentationSpecification { public static final int DB_INDEX = 0 - public static final String loopback = "127.0.0.1" private static GenericContainer redisServer = new GenericContainer<>("redis:6.2.3-alpine").withExposedPorts(6379) @@ -45,7 +44,7 @@ abstract class AbstractLettuceSyncClientAuthTest extends InstrumentationSpecific String host = redisServer.getHost() String dbAddr = host + ":" + port + "/" + DB_INDEX String embeddedDbUri = "redis://" + dbAddr - expectedHostAttributeValue = host == loopback ? null : host + expectedHostAttributeValue = host == "127.0.0.1" ? null : host redisClient = createClient(embeddedDbUri) redisClient.setOptions(LettuceTestUtil.CLIENT_OPTIONS) @@ -69,9 +68,9 @@ abstract class AbstractLettuceSyncClientAuthTest extends InstrumentationSpecific kind CLIENT attributes { "$SemanticAttributes.NET_TRANSPORT" IP_TCP - "$SemanticAttributes.NET_PEER_NAME" expectedHostAttributeValue - "$SemanticAttributes.NET_PEER_IP" loopback - "$SemanticAttributes.NET_PEER_PORT" port + "$SemanticAttributes.NET_SOCK_PEER_ADDR" "127.0.0.1" + "$SemanticAttributes.NET_SOCK_PEER_NAME" expectedHostAttributeValue + "$SemanticAttributes.NET_SOCK_PEER_PORT" port "$SemanticAttributes.DB_SYSTEM" "redis" "$SemanticAttributes.DB_STATEMENT" "AUTH ?" } diff --git a/instrumentation/lettuce/lettuce-5.1/testing/src/main/groovy/io/opentelemetry/instrumentation/lettuce/v5_1/AbstractLettuceSyncClientTest.groovy b/instrumentation/lettuce/lettuce-5.1/testing/src/main/groovy/io/opentelemetry/instrumentation/lettuce/v5_1/AbstractLettuceSyncClientTest.groovy index 608be43fea0c..e7dcc1cdbbc2 100644 --- a/instrumentation/lettuce/lettuce-5.1/testing/src/main/groovy/io/opentelemetry/instrumentation/lettuce/v5_1/AbstractLettuceSyncClientTest.groovy +++ b/instrumentation/lettuce/lettuce-5.1/testing/src/main/groovy/io/opentelemetry/instrumentation/lettuce/v5_1/AbstractLettuceSyncClientTest.groovy @@ -24,7 +24,6 @@ import static java.nio.charset.StandardCharsets.UTF_8 abstract class AbstractLettuceSyncClientTest extends InstrumentationSpecification { public static final int DB_INDEX = 0 - public static final String loopback = "127.0.0.1" private static GenericContainer redisServer = new GenericContainer<>("redis:6.2.3-alpine").withExposedPorts(6379) @@ -58,7 +57,7 @@ abstract class AbstractLettuceSyncClientTest extends InstrumentationSpecificatio String dbAddr = host + ":" + port + "/" + DB_INDEX String embeddedDbUri = "redis://" + dbAddr embeddedDbLocalhostUri = "redis://localhost:" + port + "/" + DB_INDEX - expectedHostAttributeValue = host == loopback ? null : host + expectedHostAttributeValue = host == "127.0.0.1" ? null : host int incorrectPort = PortUtils.findOpenPort() String dbAddrNonExistent = host + ":" + incorrectPort + "/" + DB_INDEX @@ -125,9 +124,9 @@ abstract class AbstractLettuceSyncClientTest extends InstrumentationSpecificatio kind CLIENT attributes { "$SemanticAttributes.NET_TRANSPORT" IP_TCP - "$SemanticAttributes.NET_PEER_NAME" expectedHostAttributeValue - "$SemanticAttributes.NET_PEER_IP" loopback - "$SemanticAttributes.NET_PEER_PORT" port + "$SemanticAttributes.NET_SOCK_PEER_ADDR" "127.0.0.1" + "$SemanticAttributes.NET_SOCK_PEER_NAME" expectedHostAttributeValue + "$SemanticAttributes.NET_SOCK_PEER_PORT" port "$SemanticAttributes.DB_SYSTEM" "redis" "$SemanticAttributes.DB_STATEMENT" "SET TESTSETKEY ?" } @@ -158,9 +157,9 @@ abstract class AbstractLettuceSyncClientTest extends InstrumentationSpecificatio kind CLIENT attributes { "$SemanticAttributes.NET_TRANSPORT" IP_TCP - "$SemanticAttributes.NET_PEER_IP" loopback - "$SemanticAttributes.NET_PEER_NAME" "localhost" - "$SemanticAttributes.NET_PEER_PORT" port + "$SemanticAttributes.NET_SOCK_PEER_ADDR" "127.0.0.1" + "$SemanticAttributes.NET_SOCK_PEER_NAME" expectedHostAttributeValue + "$SemanticAttributes.NET_SOCK_PEER_PORT" port "$SemanticAttributes.DB_SYSTEM" "redis" "$SemanticAttributes.DB_STATEMENT" "SET TESTSETKEY ?" } @@ -192,9 +191,9 @@ abstract class AbstractLettuceSyncClientTest extends InstrumentationSpecificatio kind CLIENT attributes { "$SemanticAttributes.NET_TRANSPORT" IP_TCP - "$SemanticAttributes.NET_PEER_NAME" expectedHostAttributeValue - "$SemanticAttributes.NET_PEER_IP" loopback - "$SemanticAttributes.NET_PEER_PORT" port + "$SemanticAttributes.NET_SOCK_PEER_ADDR" "127.0.0.1" + "$SemanticAttributes.NET_SOCK_PEER_NAME" expectedHostAttributeValue + "$SemanticAttributes.NET_SOCK_PEER_PORT" port "$SemanticAttributes.DB_SYSTEM" "redis" "$SemanticAttributes.DB_STATEMENT" "GET TESTKEY" } @@ -222,9 +221,9 @@ abstract class AbstractLettuceSyncClientTest extends InstrumentationSpecificatio kind CLIENT attributes { "$SemanticAttributes.NET_TRANSPORT" IP_TCP - "$SemanticAttributes.NET_PEER_NAME" expectedHostAttributeValue - "$SemanticAttributes.NET_PEER_IP" loopback - "$SemanticAttributes.NET_PEER_PORT" port + "$SemanticAttributes.NET_SOCK_PEER_ADDR" "127.0.0.1" + "$SemanticAttributes.NET_SOCK_PEER_NAME" expectedHostAttributeValue + "$SemanticAttributes.NET_SOCK_PEER_PORT" port "$SemanticAttributes.DB_SYSTEM" "redis" "$SemanticAttributes.DB_STATEMENT" "GET NON_EXISTENT_KEY" } @@ -252,9 +251,9 @@ abstract class AbstractLettuceSyncClientTest extends InstrumentationSpecificatio kind CLIENT attributes { "$SemanticAttributes.NET_TRANSPORT" IP_TCP - "$SemanticAttributes.NET_PEER_NAME" expectedHostAttributeValue - "$SemanticAttributes.NET_PEER_IP" loopback - "$SemanticAttributes.NET_PEER_PORT" port + "$SemanticAttributes.NET_SOCK_PEER_ADDR" "127.0.0.1" + "$SemanticAttributes.NET_SOCK_PEER_NAME" expectedHostAttributeValue + "$SemanticAttributes.NET_SOCK_PEER_PORT" port "$SemanticAttributes.DB_STATEMENT" "RANDOMKEY" "$SemanticAttributes.DB_SYSTEM" "redis" } @@ -282,9 +281,9 @@ abstract class AbstractLettuceSyncClientTest extends InstrumentationSpecificatio kind CLIENT attributes { "$SemanticAttributes.NET_TRANSPORT" IP_TCP - "$SemanticAttributes.NET_PEER_NAME" expectedHostAttributeValue - "$SemanticAttributes.NET_PEER_IP" loopback - "$SemanticAttributes.NET_PEER_PORT" port + "$SemanticAttributes.NET_SOCK_PEER_ADDR" "127.0.0.1" + "$SemanticAttributes.NET_SOCK_PEER_NAME" expectedHostAttributeValue + "$SemanticAttributes.NET_SOCK_PEER_PORT" port "$SemanticAttributes.DB_SYSTEM" "redis" "$SemanticAttributes.DB_STATEMENT" "LPUSH TESTLIST ?" } @@ -312,9 +311,9 @@ abstract class AbstractLettuceSyncClientTest extends InstrumentationSpecificatio kind CLIENT attributes { "$SemanticAttributes.NET_TRANSPORT" IP_TCP - "$SemanticAttributes.NET_PEER_NAME" expectedHostAttributeValue - "$SemanticAttributes.NET_PEER_IP" loopback - "$SemanticAttributes.NET_PEER_PORT" port + "$SemanticAttributes.NET_SOCK_PEER_ADDR" "127.0.0.1" + "$SemanticAttributes.NET_SOCK_PEER_NAME" expectedHostAttributeValue + "$SemanticAttributes.NET_SOCK_PEER_PORT" port "$SemanticAttributes.DB_SYSTEM" "redis" "$SemanticAttributes.DB_STATEMENT" "HMSET user firstname ? lastname ? age ?" } @@ -342,9 +341,9 @@ abstract class AbstractLettuceSyncClientTest extends InstrumentationSpecificatio kind CLIENT attributes { "$SemanticAttributes.NET_TRANSPORT" IP_TCP - "$SemanticAttributes.NET_PEER_NAME" expectedHostAttributeValue - "$SemanticAttributes.NET_PEER_IP" loopback - "$SemanticAttributes.NET_PEER_PORT" port + "$SemanticAttributes.NET_SOCK_PEER_ADDR" "127.0.0.1" + "$SemanticAttributes.NET_SOCK_PEER_NAME" expectedHostAttributeValue + "$SemanticAttributes.NET_SOCK_PEER_PORT" port "$SemanticAttributes.DB_SYSTEM" "redis" "$SemanticAttributes.DB_STATEMENT" "HGETALL TESTHM" } @@ -377,9 +376,9 @@ abstract class AbstractLettuceSyncClientTest extends InstrumentationSpecificatio kind CLIENT attributes { "$SemanticAttributes.NET_TRANSPORT" IP_TCP - "$SemanticAttributes.NET_PEER_NAME" expectedHostAttributeValue - "$SemanticAttributes.NET_PEER_IP" loopback - "$SemanticAttributes.NET_PEER_PORT" port + "$SemanticAttributes.NET_SOCK_PEER_ADDR" "127.0.0.1" + "$SemanticAttributes.NET_SOCK_PEER_NAME" expectedHostAttributeValue + "$SemanticAttributes.NET_SOCK_PEER_PORT" port "$SemanticAttributes.DB_SYSTEM" "redis" "$SemanticAttributes.DB_STATEMENT" "EVAL $b64Script 1 TESTLIST ? ?" } @@ -411,9 +410,9 @@ abstract class AbstractLettuceSyncClientTest extends InstrumentationSpecificatio kind CLIENT attributes { "$SemanticAttributes.NET_TRANSPORT" IP_TCP - "$SemanticAttributes.NET_PEER_NAME" expectedHostAttributeValue - "$SemanticAttributes.NET_PEER_IP" loopback - "$SemanticAttributes.NET_PEER_PORT" port + "$SemanticAttributes.NET_SOCK_PEER_ADDR" "127.0.0.1" + "$SemanticAttributes.NET_SOCK_PEER_NAME" expectedHostAttributeValue + "$SemanticAttributes.NET_SOCK_PEER_PORT" port "$SemanticAttributes.DB_SYSTEM" "redis" "$SemanticAttributes.DB_STATEMENT" "MSET key1 ? key2 ?" } @@ -441,9 +440,9 @@ abstract class AbstractLettuceSyncClientTest extends InstrumentationSpecificatio // Disconnect not an actual error even though an exception is recorded. attributes { "$SemanticAttributes.NET_TRANSPORT" IP_TCP - "$SemanticAttributes.NET_PEER_NAME" expectedHostAttributeValue - "$SemanticAttributes.NET_PEER_IP" loopback - "$SemanticAttributes.NET_PEER_PORT" port + "$SemanticAttributes.NET_SOCK_PEER_ADDR" "127.0.0.1" + "$SemanticAttributes.NET_SOCK_PEER_NAME" expectedHostAttributeValue + "$SemanticAttributes.NET_SOCK_PEER_PORT" port "$SemanticAttributes.DB_SYSTEM" "redis" "$SemanticAttributes.DB_STATEMENT" "DEBUG SEGFAULT" } @@ -477,9 +476,9 @@ abstract class AbstractLettuceSyncClientTest extends InstrumentationSpecificatio } attributes { "$SemanticAttributes.NET_TRANSPORT" IP_TCP - "$SemanticAttributes.NET_PEER_NAME" expectedHostAttributeValue - "$SemanticAttributes.NET_PEER_IP" loopback - "$SemanticAttributes.NET_PEER_PORT" port + "$SemanticAttributes.NET_SOCK_PEER_ADDR" "127.0.0.1" + "$SemanticAttributes.NET_SOCK_PEER_NAME" expectedHostAttributeValue + "$SemanticAttributes.NET_SOCK_PEER_PORT" port "$SemanticAttributes.DB_SYSTEM" "redis" "$SemanticAttributes.DB_STATEMENT" "SHUTDOWN NOSAVE" if (!Boolean.getBoolean("testLatestDeps")) { diff --git a/instrumentation/liberty/compile-stub/src/main/java/com/ibm/ws/http/dispatcher/internal/channel/HttpDispatcherLink.java b/instrumentation/liberty/compile-stub/src/main/java/com/ibm/ws/http/dispatcher/internal/channel/HttpDispatcherLink.java index 8d481ed40212..0f59dc23d6f5 100644 --- a/instrumentation/liberty/compile-stub/src/main/java/com/ibm/ws/http/dispatcher/internal/channel/HttpDispatcherLink.java +++ b/instrumentation/liberty/compile-stub/src/main/java/com/ibm/ws/http/dispatcher/internal/channel/HttpDispatcherLink.java @@ -11,15 +11,19 @@ @SuppressWarnings("OtelInternalJavadoc") public class HttpDispatcherLink { + public String getRemoteHostAddress() { + throw new UnsupportedOperationException(); + } + public int getRemotePort() { throw new UnsupportedOperationException(); } - public String getRemoteHostAddress() { + public String getLocalHostAddress() { throw new UnsupportedOperationException(); } - public int getRequestedPort() { + public int getLocalPort() { throw new UnsupportedOperationException(); } diff --git a/instrumentation/liberty/compile-stub/src/main/java/com/ibm/wsspi/http/channel/HttpRequestMessage.java b/instrumentation/liberty/compile-stub/src/main/java/com/ibm/wsspi/http/channel/HttpRequestMessage.java index e4357701db9a..5705c162a0a8 100644 --- a/instrumentation/liberty/compile-stub/src/main/java/com/ibm/wsspi/http/channel/HttpRequestMessage.java +++ b/instrumentation/liberty/compile-stub/src/main/java/com/ibm/wsspi/http/channel/HttpRequestMessage.java @@ -26,4 +26,8 @@ public interface HttpRequestMessage { String getVersion(); List getAllHeaderNames(); + + String getURLHost(); + + int getURLPort(); } diff --git a/instrumentation/liberty/liberty/javaagent/build.gradle.kts b/instrumentation/liberty/liberty-20.0/javaagent/build.gradle.kts similarity index 100% rename from instrumentation/liberty/liberty/javaagent/build.gradle.kts rename to instrumentation/liberty/liberty-20.0/javaagent/build.gradle.kts diff --git a/instrumentation/liberty/liberty/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/liberty/LibertyHelper.java b/instrumentation/liberty/liberty-20.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/liberty/LibertyHelper.java similarity index 92% rename from instrumentation/liberty/liberty/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/liberty/LibertyHelper.java rename to instrumentation/liberty/liberty-20.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/liberty/LibertyHelper.java index 7d8f5a2232f1..0688e70ba098 100644 --- a/instrumentation/liberty/liberty/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/liberty/LibertyHelper.java +++ b/instrumentation/liberty/liberty-20.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/liberty/LibertyHelper.java @@ -39,8 +39,7 @@ public void end( throwable = AppServerBridge.getException(context); } - ServletResponseContext responseContext = - new ServletResponseContext<>(response, throwable); + ServletResponseContext responseContext = new ServletResponseContext<>(response); if (throwable != null || mustEndOnHandlerMethodExit(request)) { instrumenter.end(context, requestContext, responseContext, throwable); } diff --git a/instrumentation/liberty/liberty/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/liberty/LibertyInstrumentationModule.java b/instrumentation/liberty/liberty-20.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/liberty/LibertyInstrumentationModule.java similarity index 100% rename from instrumentation/liberty/liberty/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/liberty/LibertyInstrumentationModule.java rename to instrumentation/liberty/liberty-20.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/liberty/LibertyInstrumentationModule.java diff --git a/instrumentation/liberty/liberty/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/liberty/LibertySingletons.java b/instrumentation/liberty/liberty-20.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/liberty/LibertySingletons.java similarity index 98% rename from instrumentation/liberty/liberty/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/liberty/LibertySingletons.java rename to instrumentation/liberty/liberty-20.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/liberty/LibertySingletons.java index 9d4e4aba4a7a..7468415faf50 100644 --- a/instrumentation/liberty/liberty/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/liberty/LibertySingletons.java +++ b/instrumentation/liberty/liberty-20.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/liberty/LibertySingletons.java @@ -15,7 +15,7 @@ import javax.servlet.http.HttpServletResponse; public final class LibertySingletons { - private static final String INSTRUMENTATION_NAME = "io.opentelemetry.liberty"; + private static final String INSTRUMENTATION_NAME = "io.opentelemetry.liberty-20.0"; private static final Instrumenter< ServletRequestContext, ServletResponseContext> diff --git a/instrumentation/liberty/liberty/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/liberty/LibertyWebAppInstrumentation.java b/instrumentation/liberty/liberty-20.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/liberty/LibertyWebAppInstrumentation.java similarity index 100% rename from instrumentation/liberty/liberty/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/liberty/LibertyWebAppInstrumentation.java rename to instrumentation/liberty/liberty-20.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/liberty/LibertyWebAppInstrumentation.java diff --git a/instrumentation/liberty/liberty/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/liberty/ThreadLocalContext.java b/instrumentation/liberty/liberty-20.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/liberty/ThreadLocalContext.java similarity index 100% rename from instrumentation/liberty/liberty/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/liberty/ThreadLocalContext.java rename to instrumentation/liberty/liberty-20.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/liberty/ThreadLocalContext.java diff --git a/instrumentation/liberty/liberty-dispatcher/javaagent/build.gradle.kts b/instrumentation/liberty/liberty-dispatcher-20.0/javaagent/build.gradle.kts similarity index 100% rename from instrumentation/liberty/liberty-dispatcher/javaagent/build.gradle.kts rename to instrumentation/liberty/liberty-dispatcher-20.0/javaagent/build.gradle.kts diff --git a/instrumentation/liberty/liberty-dispatcher/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/liberty/dispatcher/LibertyDispatcherHttpAttributesGetter.java b/instrumentation/liberty/liberty-dispatcher-20.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/liberty/dispatcher/LibertyDispatcherHttpAttributesGetter.java similarity index 70% rename from instrumentation/liberty/liberty-dispatcher/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/liberty/dispatcher/LibertyDispatcherHttpAttributesGetter.java rename to instrumentation/liberty/liberty-dispatcher-20.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/liberty/dispatcher/LibertyDispatcherHttpAttributesGetter.java index 7ce35be351b4..28c9380732c9 100644 --- a/instrumentation/liberty/liberty-dispatcher/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/liberty/dispatcher/LibertyDispatcherHttpAttributesGetter.java +++ b/instrumentation/liberty/liberty-dispatcher-20.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/liberty/dispatcher/LibertyDispatcherHttpAttributesGetter.java @@ -23,20 +23,6 @@ public List requestHeader(LibertyRequest libertyRequest, String name) { return libertyRequest.getHeaderValues(name); } - @Override - @Nullable - public Long requestContentLength( - LibertyRequest libertyRequest, @Nullable LibertyResponse libertyResponse) { - return null; - } - - @Override - @Nullable - public Long requestContentLengthUncompressed( - LibertyRequest libertyRequest, @Nullable LibertyResponse libertyResponse) { - return null; - } - @Override @Nullable public String flavor(LibertyRequest libertyRequest) { @@ -52,24 +38,11 @@ public String flavor(LibertyRequest libertyRequest) { @Override @Nullable - public Integer statusCode(LibertyRequest libertyRequest, LibertyResponse libertyResponse) { + public Integer statusCode( + LibertyRequest libertyRequest, LibertyResponse libertyResponse, @Nullable Throwable error) { return libertyResponse.getStatus(); } - @Override - @Nullable - public Long responseContentLength( - LibertyRequest libertyRequest, LibertyResponse libertyResponse) { - return null; - } - - @Override - @Nullable - public Long responseContentLengthUncompressed( - LibertyRequest libertyRequest, LibertyResponse libertyResponse) { - return null; - } - @Override public List responseHeader( LibertyRequest libertyRequest, LibertyResponse libertyResponse, String name) { @@ -98,10 +71,4 @@ public String scheme(LibertyRequest libertyRequest) { public String route(LibertyRequest libertyRequest) { return null; } - - @Override - @Nullable - public String serverName(LibertyRequest libertyRequest) { - return null; - } } diff --git a/instrumentation/liberty/liberty-dispatcher/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/liberty/dispatcher/LibertyDispatcherInstrumentationModule.java b/instrumentation/liberty/liberty-dispatcher-20.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/liberty/dispatcher/LibertyDispatcherInstrumentationModule.java similarity index 100% rename from instrumentation/liberty/liberty-dispatcher/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/liberty/dispatcher/LibertyDispatcherInstrumentationModule.java rename to instrumentation/liberty/liberty-dispatcher-20.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/liberty/dispatcher/LibertyDispatcherInstrumentationModule.java diff --git a/instrumentation/liberty/liberty-dispatcher/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/liberty/dispatcher/LibertyDispatcherLinkInstrumentation.java b/instrumentation/liberty/liberty-dispatcher-20.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/liberty/dispatcher/LibertyDispatcherLinkInstrumentation.java similarity index 100% rename from instrumentation/liberty/liberty-dispatcher/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/liberty/dispatcher/LibertyDispatcherLinkInstrumentation.java rename to instrumentation/liberty/liberty-dispatcher-20.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/liberty/dispatcher/LibertyDispatcherLinkInstrumentation.java diff --git a/instrumentation/liberty/liberty-dispatcher-20.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/liberty/dispatcher/LibertyDispatcherNetAttributesGetter.java b/instrumentation/liberty/liberty-dispatcher-20.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/liberty/dispatcher/LibertyDispatcherNetAttributesGetter.java new file mode 100644 index 000000000000..86652e6c5503 --- /dev/null +++ b/instrumentation/liberty/liberty-dispatcher-20.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/liberty/dispatcher/LibertyDispatcherNetAttributesGetter.java @@ -0,0 +1,53 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.liberty.dispatcher; + +import io.opentelemetry.instrumentation.api.instrumenter.net.NetServerAttributesGetter; +import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; +import javax.annotation.Nullable; + +public class LibertyDispatcherNetAttributesGetter + implements NetServerAttributesGetter { + + @Override + public String transport(LibertyRequest request) { + return SemanticAttributes.NetTransportValues.IP_TCP; + } + + @Nullable + @Override + public String hostName(LibertyRequest request) { + return request.request().getURLHost(); + } + + @Override + public Integer hostPort(LibertyRequest request) { + return request.request().getURLPort(); + } + + @Override + @Nullable + public String sockPeerAddr(LibertyRequest request) { + return request.dispatcher().getRemoteHostAddress(); + } + + @Override + public Integer sockPeerPort(LibertyRequest request) { + return request.dispatcher().getRemotePort(); + } + + @Nullable + @Override + public String sockHostAddr(LibertyRequest request) { + return request.dispatcher().getLocalHostAddress(); + } + + @Nullable + @Override + public Integer sockHostPort(LibertyRequest request) { + return request.dispatcher().getLocalPort(); + } +} diff --git a/instrumentation/liberty/liberty-dispatcher/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/liberty/dispatcher/LibertyDispatcherRequestGetter.java b/instrumentation/liberty/liberty-dispatcher-20.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/liberty/dispatcher/LibertyDispatcherRequestGetter.java similarity index 100% rename from instrumentation/liberty/liberty-dispatcher/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/liberty/dispatcher/LibertyDispatcherRequestGetter.java rename to instrumentation/liberty/liberty-dispatcher-20.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/liberty/dispatcher/LibertyDispatcherRequestGetter.java diff --git a/instrumentation/liberty/liberty-dispatcher/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/liberty/dispatcher/LibertyDispatcherSingletons.java b/instrumentation/liberty/liberty-dispatcher-20.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/liberty/dispatcher/LibertyDispatcherSingletons.java similarity index 76% rename from instrumentation/liberty/liberty-dispatcher/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/liberty/dispatcher/LibertyDispatcherSingletons.java rename to instrumentation/liberty/liberty-dispatcher-20.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/liberty/dispatcher/LibertyDispatcherSingletons.java index ffec261dd4a1..940975f35409 100644 --- a/instrumentation/liberty/liberty-dispatcher/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/liberty/dispatcher/LibertyDispatcherSingletons.java +++ b/instrumentation/liberty/liberty-dispatcher-20.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/liberty/dispatcher/LibertyDispatcherSingletons.java @@ -12,10 +12,10 @@ import io.opentelemetry.instrumentation.api.instrumenter.http.HttpServerMetrics; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanNameExtractor; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanStatusExtractor; -import io.opentelemetry.instrumentation.api.instrumenter.net.NetServerAttributesExtractor; +import io.opentelemetry.javaagent.bootstrap.internal.CommonConfig; public final class LibertyDispatcherSingletons { - private static final String INSTRUMENTATION_NAME = "io.opentelemetry.liberty-dispatcher"; + private static final String INSTRUMENTATION_NAME = "io.opentelemetry.liberty-dispatcher-20.0"; private static final Instrumenter INSTRUMENTER; @@ -31,11 +31,14 @@ public final class LibertyDispatcherSingletons { INSTRUMENTATION_NAME, HttpSpanNameExtractor.create(httpAttributesGetter)) .setSpanStatusExtractor(HttpSpanStatusExtractor.create(httpAttributesGetter)) - .addAttributesExtractor(HttpServerAttributesExtractor.create(httpAttributesGetter)) - .addAttributesExtractor(NetServerAttributesExtractor.create(netAttributesGetter)) + .addAttributesExtractor( + HttpServerAttributesExtractor.builder(httpAttributesGetter, netAttributesGetter) + .setCapturedRequestHeaders(CommonConfig.get().getServerRequestHeaders()) + .setCapturedResponseHeaders(CommonConfig.get().getServerResponseHeaders()) + .build()) .addContextCustomizer(HttpRouteHolder.get()) .addOperationMetrics(HttpServerMetrics.get()) - .newServerInstrumenter(LibertyDispatcherRequestGetter.INSTANCE); + .buildServerInstrumenter(LibertyDispatcherRequestGetter.INSTANCE); } public static Instrumenter instrumenter() { diff --git a/instrumentation/liberty/liberty-dispatcher/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/liberty/dispatcher/LibertyRequest.java b/instrumentation/liberty/liberty-dispatcher-20.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/liberty/dispatcher/LibertyRequest.java similarity index 88% rename from instrumentation/liberty/liberty-dispatcher/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/liberty/dispatcher/LibertyRequest.java rename to instrumentation/liberty/liberty-dispatcher-20.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/liberty/dispatcher/LibertyRequest.java index 8edc81bee185..245e77e6f229 100644 --- a/instrumentation/liberty/liberty-dispatcher/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/liberty/dispatcher/LibertyRequest.java +++ b/instrumentation/liberty/liberty-dispatcher-20.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/liberty/dispatcher/LibertyRequest.java @@ -38,10 +38,6 @@ public String getQueryString() { return httpRequestMessage.getQueryString(); } - public int getServerPort() { - return httpDispatcherLink.getRequestedPort(); - } - public List getAllHeaderNames() { return httpRequestMessage.getAllHeaderNames(); } @@ -63,15 +59,15 @@ public List getHeaderValues(String name) { return stringHeaders; } - public int peerPort() { - return httpDispatcherLink.getRemotePort(); + public String getProtocol() { + return httpRequestMessage.getVersion(); } - public String peerIp() { - return httpDispatcherLink.getRemoteHostAddress(); + public HttpDispatcherLink dispatcher() { + return httpDispatcherLink; } - public String getProtocol() { - return httpRequestMessage.getVersion(); + public HttpRequestMessage request() { + return httpRequestMessage; } } diff --git a/instrumentation/liberty/liberty-dispatcher/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/liberty/dispatcher/LibertyResponse.java b/instrumentation/liberty/liberty-dispatcher-20.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/liberty/dispatcher/LibertyResponse.java similarity index 100% rename from instrumentation/liberty/liberty-dispatcher/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/liberty/dispatcher/LibertyResponse.java rename to instrumentation/liberty/liberty-dispatcher-20.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/liberty/dispatcher/LibertyResponse.java diff --git a/instrumentation/liberty/liberty-dispatcher/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/liberty/dispatcher/LibertyDispatcherNetAttributesGetter.java b/instrumentation/liberty/liberty-dispatcher/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/liberty/dispatcher/LibertyDispatcherNetAttributesGetter.java deleted file mode 100644 index 50ebb9606dc5..000000000000 --- a/instrumentation/liberty/liberty-dispatcher/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/liberty/dispatcher/LibertyDispatcherNetAttributesGetter.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.javaagent.instrumentation.liberty.dispatcher; - -import io.opentelemetry.instrumentation.api.instrumenter.net.NetServerAttributesGetter; -import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; -import javax.annotation.Nullable; - -public class LibertyDispatcherNetAttributesGetter - implements NetServerAttributesGetter { - - @Override - public String transport(LibertyRequest libertyRequest) { - return SemanticAttributes.NetTransportValues.IP_TCP; - } - - @Override - @Nullable - public Integer peerPort(LibertyRequest libertyRequest) { - return libertyRequest.peerPort(); - } - - @Override - @Nullable - public String peerIp(LibertyRequest libertyRequest) { - return libertyRequest.peerIp(); - } -} 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 6b32996c3b04..67f2b2269ef0 100644 --- a/instrumentation/log4j/log4j-appender-1.2/javaagent/build.gradle.kts +++ b/instrumentation/log4j/log4j-appender-1.2/javaagent/build.gradle.kts @@ -16,7 +16,7 @@ dependencies { // 1.2 introduces MDC and there's no version earlier than 1.2.4 available library("log4j:log4j:1.2.4") - compileOnly(project(":instrumentation-appender-api-internal")) + compileOnly("io.opentelemetry:opentelemetry-api-logs") compileOnly(project(":javaagent-bootstrap")) testImplementation("org.awaitility:awaitility") 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 6c82d1c59131..1ec27c84bff7 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 @@ -11,7 +11,7 @@ import static net.bytebuddy.matcher.ElementMatchers.takesArgument; import static net.bytebuddy.matcher.ElementMatchers.takesArguments; -import io.opentelemetry.instrumentation.api.appender.internal.LogEmitterProvider; +import io.opentelemetry.api.logs.LoggerProvider; import io.opentelemetry.javaagent.bootstrap.CallDepth; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; @@ -54,7 +54,7 @@ public static void methodEnter( @Advice.Local("otelCallDepth") CallDepth callDepth) { // need to track call depth across all loggers to avoid double capture when one logging // framework delegates to another - callDepth = CallDepth.forClass(LogEmitterProvider.class); + callDepth = CallDepth.forClass(LoggerProvider.class); if (callDepth.getAndIncrement() == 0) { LogEventMapper.INSTANCE.capture(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 b63c71cbce12..2290c2442564 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 @@ -5,16 +5,19 @@ package io.opentelemetry.javaagent.instrumentation.log4j.appender.v1_2; +import static io.opentelemetry.instrumentation.api.log.LoggingContextConstants.HELIOS_INSTRUMENTED_INDICATION; import static java.util.Collections.emptyList; import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.common.AttributesBuilder; +import io.opentelemetry.api.logs.GlobalLoggerProvider; +import io.opentelemetry.api.logs.LogRecordBuilder; +import io.opentelemetry.api.logs.Severity; +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.api.trace.SpanContext; import io.opentelemetry.context.Context; -import io.opentelemetry.instrumentation.api.appender.internal.LogBuilder; -import io.opentelemetry.instrumentation.api.appender.internal.Severity; import io.opentelemetry.instrumentation.api.internal.cache.Cache; -import io.opentelemetry.javaagent.bootstrap.AgentLogEmitterProvider; import io.opentelemetry.javaagent.bootstrap.internal.InstrumentationConfig; import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; import java.io.PrintWriter; @@ -29,6 +32,21 @@ public final class LogEventMapper { + private static boolean heliosInstrumentedIndicator = false; + + private static void markInstrumentationIndicator(AttributesBuilder attributes) { + Context parentContext = Context.current(); + Span span = Span.fromContext(parentContext); + SpanContext parentSpanContext = span.getSpanContext(); + + if (!span.isRecording() || !parentSpanContext.isValid() || heliosInstrumentedIndicator) { + return; + } + + attributes.put(HELIOS_INSTRUMENTED_INDICATION, "log4j"); + heliosInstrumentedIndicator = true; + } + private static final Cache> mdcAttributeKeys = Cache.bounded(100); public static final LogEventMapper INSTANCE = new LogEventMapper(); @@ -63,8 +81,8 @@ public void capture(Category logger, Priority level, Object message, Throwable t if (instrumentationName == null || instrumentationName.isEmpty()) { instrumentationName = "ROOT"; } - LogBuilder builder = - AgentLogEmitterProvider.get().logEmitterBuilder(instrumentationName).build().logBuilder(); + LogRecordBuilder builder = + GlobalLoggerProvider.get().loggerBuilder(instrumentationName).build().logRecordBuilder(); // message if (message != null) { @@ -79,10 +97,12 @@ public void capture(Category logger, Priority level, Object message, Throwable t AttributesBuilder attributes = Attributes.builder(); + markInstrumentationIndicator(attributes); + // throwable if (throwable != null) { // TODO (trask) extract method for recording exception into - // instrumentation-appender-api-internal + // io.opentelemetry:opentelemetry-api-logs attributes.put(SemanticAttributes.EXCEPTION_TYPE, throwable.getClass().getName()); attributes.put(SemanticAttributes.EXCEPTION_MESSAGE, throwable.getMessage()); StringWriter writer = new StringWriter(); @@ -98,7 +118,7 @@ public void capture(Category logger, Priority level, Object message, Throwable t attributes.put(SemanticAttributes.THREAD_ID, currentThread.getId()); } - builder.setAttributes(attributes.build()); + builder.setAllAttributes(attributes.build()); // span context builder.setContext(Context.current()); diff --git a/instrumentation/log4j/log4j-appender-1.2/javaagent/src/test/groovy/Log4j1Test.groovy b/instrumentation/log4j/log4j-appender-1.2/javaagent/src/test/groovy/Log4j1Test.groovy index 91c305e5216e..2facc1702008 100644 --- a/instrumentation/log4j/log4j-appender-1.2/javaagent/src/test/groovy/Log4j1Test.groovy +++ b/instrumentation/log4j/log4j-appender-1.2/javaagent/src/test/groovy/Log4j1Test.groovy @@ -5,10 +5,11 @@ import io.opentelemetry.api.common.AttributeKey import io.opentelemetry.instrumentation.test.AgentInstrumentationSpecification -import io.opentelemetry.sdk.logs.data.Severity +import io.opentelemetry.api.logs.Severity import io.opentelemetry.semconv.trace.attributes.SemanticAttributes import org.apache.log4j.Logger import org.apache.log4j.MDC +import org.apache.log4j.helpers.Loader import spock.lang.Unroll import static org.assertj.core.api.Assertions.assertThat @@ -16,7 +17,16 @@ import static org.awaitility.Awaitility.await class Log4j1Test extends AgentInstrumentationSpecification { + static { + // this is needed because log4j1 incorrectly thinks the initial releases of Java 10-19 + // (which have no '.' in their versions since there is no minor version) are Java 1.1, + // which is before ThreadLocal was introduced and so log4j1 disables MDC functionality + // (and the MDC tests below fail) + Loader.java1 = false + } + private static final Logger logger = Logger.getLogger("abc") + private static isFirstLog = true @Unroll def "test method=#testMethod with exception=#exception and parent=#parent"() { @@ -38,28 +48,36 @@ class Log4j1Test extends AgentInstrumentationSpecification { } then: + def heliosInstrumentedIndication = "heliosLogInstrumented" + int heliosAttrsLength = 0 + if (parent) { waitForTraces(1) + + if (severity != null && isFirstLog) { + heliosAttrsLength++ + isFirstLog = false + } } if (severity != null) { await() .untilAsserted( () -> { - assertThat(logs).hasSize(1) + assertThat(logRecords).hasSize(1) }) - def log = logs.get(0) + def log = logRecords.get(0) assertThat(log.getBody().asString()).isEqualTo("xyz") assertThat(log.getInstrumentationScopeInfo().getName()).isEqualTo("abc") assertThat(log.getSeverity()).isEqualTo(severity) assertThat(log.getSeverityText()).isEqualTo(severityText) if (exception) { - assertThat(log.getAttributes().size()).isEqualTo(5) + assertThat(log.getAttributes().size()).isEqualTo(5 + heliosAttrsLength) assertThat(log.getAttributes().get(SemanticAttributes.EXCEPTION_TYPE)).isEqualTo(IllegalStateException.getName()) assertThat(log.getAttributes().get(SemanticAttributes.EXCEPTION_MESSAGE)).isEqualTo("hello") assertThat(log.getAttributes().get(SemanticAttributes.EXCEPTION_STACKTRACE)).contains(Log4j1Test.name) } else { - assertThat(log.getAttributes().size()).isEqualTo(2) + assertThat(log.getAttributes().size()).isEqualTo(2 + heliosAttrsLength) assertThat(log.getAttributes().get(SemanticAttributes.EXCEPTION_TYPE)).isNull() assertThat(log.getAttributes().get(SemanticAttributes.EXCEPTION_MESSAGE)).isNull() assertThat(log.getAttributes().get(SemanticAttributes.EXCEPTION_STACKTRACE)).isNull() @@ -68,12 +86,15 @@ class Log4j1Test extends AgentInstrumentationSpecification { assertThat(log.getAttributes().get(SemanticAttributes.THREAD_ID)).isEqualTo(Thread.currentThread().getId()) if (parent) { assertThat(log.getSpanContext()).isEqualTo(traces.get(0).get(0).getSpanContext()) + if (heliosAttrsLength == 1) { + assertThat(log.getAttributes().get(AttributeKey.stringKey(heliosInstrumentedIndication))).isEqualTo("log4j") + } } else { assertThat(log.getSpanContext().isValid()).isFalse() } } else { Thread.sleep(500) // sleep a bit just to make sure no log is captured - logs.size() == 0 + logRecords.size() == 0 } where: @@ -109,9 +130,9 @@ class Log4j1Test extends AgentInstrumentationSpecification { await() .untilAsserted( () -> { - assertThat(logs).hasSize(1) + assertThat(logRecords).hasSize(1) }) - def log = logs.get(0) + def log = logRecords.get(0) assertThat(log.getBody().asString()).isEqualTo("xyz") assertThat(log.getInstrumentationScopeInfo().getName()).isEqualTo("abc") assertThat(log.getSeverity()).isEqualTo(Severity.INFO) 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 88b4c34380df..01814acaaa5b 100644 --- a/instrumentation/log4j/log4j-appender-2.17/javaagent/build.gradle.kts +++ b/instrumentation/log4j/log4j-appender-2.17/javaagent/build.gradle.kts @@ -14,7 +14,7 @@ muzzle { dependencies { library("org.apache.logging.log4j:log4j-core:2.17.0") - compileOnly(project(":instrumentation-appender-api-internal")) + compileOnly("io.opentelemetry:opentelemetry-api-logs") compileOnly(project(":javaagent-bootstrap")) implementation(project(":instrumentation:log4j:log4j-appender-2.17:library")) @@ -40,4 +40,5 @@ tasks.withType().configureEach { jvmArgs("-Dotel.instrumentation.log4j-appender.experimental.capture-map-message-attributes=true") jvmArgs("-Dotel.instrumentation.log4j-appender.experimental.capture-context-data-attributes=*") jvmArgs("-Dotel.instrumentation.log4j-appender.experimental-log-attributes=true") + jvmArgs("-Dotel.instrumentation.log4j-appender.experimental.capture-marker-attribute=true") } diff --git a/instrumentation/log4j/log4j-appender-2.17/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/log4j/appender/v2_17/Log4jAppenderInstrumentation.java b/instrumentation/log4j/log4j-appender-2.17/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/log4j/appender/v2_17/Log4jAppenderInstrumentation.java index da13a1384e32..fe3d742bb42b 100644 --- a/instrumentation/log4j/log4j-appender-2.17/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/log4j/appender/v2_17/Log4jAppenderInstrumentation.java +++ b/instrumentation/log4j/log4j-appender-2.17/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/log4j/appender/v2_17/Log4jAppenderInstrumentation.java @@ -14,7 +14,7 @@ import static net.bytebuddy.matcher.ElementMatchers.takesArgument; import static net.bytebuddy.matcher.ElementMatchers.takesArguments; -import io.opentelemetry.instrumentation.api.appender.internal.LogEmitterProvider; +import io.opentelemetry.api.logs.LoggerProvider; import io.opentelemetry.javaagent.bootstrap.CallDepth; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; @@ -23,6 +23,7 @@ import net.bytebuddy.matcher.ElementMatcher; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.Marker; import org.apache.logging.log4j.message.Message; class Log4jAppenderInstrumentation implements TypeInstrumentation { @@ -60,14 +61,15 @@ public static class LogAdvice { public static void methodEnter( @Advice.This Logger logger, @Advice.Argument(0) Level level, + @Advice.Argument(1) Marker marker, @Advice.Argument(4) Message message, @Advice.Argument(5) Throwable t, @Advice.Local("otelCallDepth") CallDepth callDepth) { // need to track call depth across all loggers in order to avoid double capture when one // logging framework delegates to another - callDepth = CallDepth.forClass(LogEmitterProvider.class); + callDepth = CallDepth.forClass(LoggerProvider.class); if (callDepth.getAndIncrement() == 0) { - Log4jHelper.capture(logger, level, message, t); + Log4jHelper.capture(logger, level, marker, message, t); } } diff --git a/instrumentation/log4j/log4j-appender-2.17/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/log4j/appender/v2_17/Log4jHelper.java b/instrumentation/log4j/log4j-appender-2.17/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/log4j/appender/v2_17/Log4jHelper.java index 2a78fbc14352..d15cddca5fa8 100644 --- a/instrumentation/log4j/log4j-appender-2.17/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/log4j/appender/v2_17/Log4jHelper.java +++ b/instrumentation/log4j/log4j-appender-2.17/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/log4j/appender/v2_17/Log4jHelper.java @@ -5,32 +5,63 @@ package io.opentelemetry.javaagent.instrumentation.log4j.appender.v2_17; -import io.opentelemetry.instrumentation.api.appender.internal.LogBuilder; +import static java.util.Collections.emptyList; + +import io.opentelemetry.api.logs.GlobalLoggerProvider; +import io.opentelemetry.api.logs.LogRecordBuilder; import io.opentelemetry.instrumentation.log4j.appender.v2_17.internal.ContextDataAccessor; import io.opentelemetry.instrumentation.log4j.appender.v2_17.internal.LogEventMapper; -import io.opentelemetry.javaagent.bootstrap.AgentLogEmitterProvider; +import io.opentelemetry.javaagent.bootstrap.internal.InstrumentationConfig; +import java.util.List; import java.util.Map; import java.util.function.BiConsumer; import javax.annotation.Nullable; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.Marker; import org.apache.logging.log4j.ThreadContext; import org.apache.logging.log4j.message.Message; public final class Log4jHelper { - private static final LogEventMapper> mapper = - new LogEventMapper<>(ContextDataAccessorImpl.INSTANCE); + private static final LogEventMapper> mapper; + + static { + InstrumentationConfig config = InstrumentationConfig.get(); + + boolean captureExperimentalAttributes = + config.getBoolean("otel.instrumentation.log4j-appender.experimental-log-attributes", false); + boolean captureMapMessageAttributes = + config.getBoolean( + "otel.instrumentation.log4j-appender.experimental.capture-map-message-attributes", + false); + boolean captureMarkerAttribute = + config.getBoolean( + "otel.instrumentation.log4j-appender.experimental.capture-marker-attribute", false); + List captureContextDataAttributes = + config.getList( + "otel.instrumentation.log4j-appender.experimental.capture-context-data-attributes", + emptyList()); + + mapper = + new LogEventMapper<>( + ContextDataAccessorImpl.INSTANCE, + captureExperimentalAttributes, + captureMapMessageAttributes, + captureMarkerAttribute, + captureContextDataAttributes); + } - public static void capture(Logger logger, Level level, Message message, Throwable throwable) { + public static void capture( + Logger logger, Level level, Marker marker, Message message, Throwable throwable) { String instrumentationName = logger.getName(); if (instrumentationName == null || instrumentationName.isEmpty()) { instrumentationName = "ROOT"; } - LogBuilder builder = - AgentLogEmitterProvider.get().logEmitterBuilder(instrumentationName).build().logBuilder(); + LogRecordBuilder builder = + GlobalLoggerProvider.get().loggerBuilder(instrumentationName).build().logRecordBuilder(); Map contextData = ThreadContext.getImmutableContext(); - mapper.mapLogEvent(builder, message, level, throwable, contextData); + mapper.mapLogEvent(builder, message, level, marker, throwable, contextData); builder.emit(); } diff --git a/instrumentation/log4j/log4j-appender-2.17/javaagent/src/test/groovy/Log4j2Test.groovy b/instrumentation/log4j/log4j-appender-2.17/javaagent/src/test/groovy/Log4j2Test.groovy index e3a164460a49..f814c4d14e20 100644 --- a/instrumentation/log4j/log4j-appender-2.17/javaagent/src/test/groovy/Log4j2Test.groovy +++ b/instrumentation/log4j/log4j-appender-2.17/javaagent/src/test/groovy/Log4j2Test.groovy @@ -5,10 +5,12 @@ import io.opentelemetry.api.common.AttributeKey import io.opentelemetry.instrumentation.test.AgentInstrumentationSpecification -import io.opentelemetry.sdk.logs.data.Severity +import io.opentelemetry.api.logs.Severity +import io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions import io.opentelemetry.semconv.trace.attributes.SemanticAttributes import org.apache.logging.log4j.LogManager import org.apache.logging.log4j.Logger +import org.apache.logging.log4j.MarkerManager import org.apache.logging.log4j.ThreadContext import org.apache.logging.log4j.message.StringMapMessage import org.apache.logging.log4j.message.StructuredDataMessage @@ -20,6 +22,7 @@ import static org.awaitility.Awaitility.await class Log4j2Test extends AgentInstrumentationSpecification { private static final Logger logger = LogManager.getLogger("abc") + private static Boolean isFirstLog = true @Unroll def "test method=#testMethod with exception=#exception and parent=#parent"() { @@ -41,42 +44,53 @@ class Log4j2Test extends AgentInstrumentationSpecification { } then: + def heliosInstrumentedIndication = "heliosLogInstrumented" + int heliosAttrsLength = 0 + if (parent) { waitForTraces(1) + + if (severity != null && isFirstLog) { + heliosAttrsLength++ + isFirstLog = false + } } if (severity != null) { await() .untilAsserted( () -> { - assertThat(logs).hasSize(1) + assertThat(logRecords).hasSize(1) }) - def log = logs.get(0) + def log = logRecords.get(0) assertThat(log.getBody().asString()).isEqualTo("xyz: 123") assertThat(log.getInstrumentationScopeInfo().getName()).isEqualTo("abc") assertThat(log.getSeverity()).isEqualTo(severity) assertThat(log.getSeverityText()).isEqualTo(severityText) if (exception) { - assertThat(log.getAttributes().size()).isEqualTo(5) - assertThat(log.getAttributes().get(SemanticAttributes.EXCEPTION_TYPE)).isEqualTo(IllegalStateException.getName()) - assertThat(log.getAttributes().get(SemanticAttributes.EXCEPTION_MESSAGE)).isEqualTo("hello") - assertThat(log.getAttributes().get(SemanticAttributes.EXCEPTION_STACKTRACE)).contains(Log4j2Test.name) + assertThat(log.getAttributes().size()).isEqualTo(5 + heliosAttrsLength) + OpenTelemetryAssertions.assertThat(log.getAttributes()).containsEntry(SemanticAttributes.EXCEPTION_TYPE, IllegalStateException.getName()) + OpenTelemetryAssertions.assertThat(log.getAttributes()).containsEntry(SemanticAttributes.EXCEPTION_MESSAGE, "hello") + OpenTelemetryAssertions.assertThat(log.getAttributes().get(SemanticAttributes.EXCEPTION_STACKTRACE)).contains(Log4j2Test.name) } else { - assertThat(log.getAttributes().size()).isEqualTo(2) + assertThat(log.getAttributes().size()).isEqualTo(2 + heliosAttrsLength) assertThat(log.getAttributes().get(SemanticAttributes.EXCEPTION_TYPE)).isNull() assertThat(log.getAttributes().get(SemanticAttributes.EXCEPTION_MESSAGE)).isNull() assertThat(log.getAttributes().get(SemanticAttributes.EXCEPTION_STACKTRACE)).isNull() } - assertThat(log.getAttributes().get(SemanticAttributes.THREAD_NAME)).isEqualTo(Thread.currentThread().getName()) - assertThat(log.getAttributes().get(SemanticAttributes.THREAD_ID)).isEqualTo(Thread.currentThread().getId()) + OpenTelemetryAssertions.assertThat(log.getAttributes()).containsEntry(SemanticAttributes.THREAD_NAME, Thread.currentThread().getName()) + OpenTelemetryAssertions.assertThat(log.getAttributes()).containsEntry(SemanticAttributes.THREAD_ID, Thread.currentThread().getId()) if (parent) { assertThat(log.getSpanContext()).isEqualTo(traces.get(0).get(0).getSpanContext()) + if (heliosAttrsLength == 1) { + assertThat(log.getAttributes().get(AttributeKey.stringKey(heliosInstrumentedIndication))).isEqualTo("log4j2") + } } else { assertThat(log.getSpanContext().isValid()).isFalse() } } else { Thread.sleep(500) // sleep a bit just to make sure no log is captured - logs.size() == 0 + logRecords.size() == 0 } where: @@ -111,18 +125,18 @@ class Log4j2Test extends AgentInstrumentationSpecification { await() .untilAsserted( () -> { - assertThat(logs).hasSize(1) + assertThat(logRecords).hasSize(1) }) - def log = logs.get(0) + def log = logRecords.get(0) assertThat(log.getBody().asString()).isEqualTo("xyz: 123") assertThat(log.getInstrumentationScopeInfo().getName()).isEqualTo("abc") assertThat(log.getSeverity()).isEqualTo(Severity.INFO) assertThat(log.getSeverityText()).isEqualTo("INFO") assertThat(log.getAttributes().size()).isEqualTo(4) - assertThat(log.getAttributes().get(AttributeKey.stringKey("log4j.context_data.key1"))).isEqualTo("val1") - assertThat(log.getAttributes().get(AttributeKey.stringKey("log4j.context_data.key2"))).isEqualTo("val2") - assertThat(log.getAttributes().get(SemanticAttributes.THREAD_NAME)).isEqualTo(Thread.currentThread().getName()) - assertThat(log.getAttributes().get(SemanticAttributes.THREAD_ID)).isEqualTo(Thread.currentThread().getId()) + OpenTelemetryAssertions.assertThat(log.getAttributes()).containsEntry("log4j.context_data.key1", "val1") + OpenTelemetryAssertions.assertThat(log.getAttributes()).containsEntry("log4j.context_data.key2", "val2") + OpenTelemetryAssertions.assertThat(log.getAttributes()).containsEntry(SemanticAttributes.THREAD_NAME, Thread.currentThread().getName()) + OpenTelemetryAssertions.assertThat(log.getAttributes()).containsEntry(SemanticAttributes.THREAD_ID, Thread.currentThread().getId()) } def "test string map message"() { @@ -137,18 +151,18 @@ class Log4j2Test extends AgentInstrumentationSpecification { await() .untilAsserted( () -> { - assertThat(logs).hasSize(1) + assertThat(logRecords).hasSize(1) }) - def log = logs.get(0) + def log = logRecords.get(0) assertThat(log.getBody().asString()).isEmpty() assertThat(log.getInstrumentationScopeInfo().getName()).isEqualTo("abc") assertThat(log.getSeverity()).isEqualTo(Severity.INFO) assertThat(log.getSeverityText()).isEqualTo("INFO") assertThat(log.getAttributes().size()).isEqualTo(4) - assertThat(log.getAttributes().get(AttributeKey.stringKey("key1"))).isEqualTo("val1") - assertThat(log.getAttributes().get(AttributeKey.stringKey("key2"))).isEqualTo("val2") - assertThat(log.getAttributes().get(SemanticAttributes.THREAD_NAME)).isEqualTo(Thread.currentThread().getName()) - assertThat(log.getAttributes().get(SemanticAttributes.THREAD_ID)).isEqualTo(Thread.currentThread().getId()) + OpenTelemetryAssertions.assertThat(log.getAttributes()).containsEntry("key1", "val1") + OpenTelemetryAssertions.assertThat(log.getAttributes()).containsEntry("key2", "val2") + OpenTelemetryAssertions.assertThat(log.getAttributes()).containsEntry(SemanticAttributes.THREAD_NAME, Thread.currentThread().getName()) + OpenTelemetryAssertions.assertThat(log.getAttributes()).containsEntry(SemanticAttributes.THREAD_ID, Thread.currentThread().getId()) } def "test string map message with special attribute"() { @@ -163,17 +177,17 @@ class Log4j2Test extends AgentInstrumentationSpecification { await() .untilAsserted( () -> { - assertThat(logs).hasSize(1) + assertThat(logRecords).hasSize(1) }) - def log = logs.get(0) + def log = logRecords.get(0) assertThat(log.getBody().asString()).isEqualTo("val2") assertThat(log.getInstrumentationScopeInfo().getName()).isEqualTo("abc") assertThat(log.getSeverity()).isEqualTo(Severity.INFO) assertThat(log.getSeverityText()).isEqualTo("INFO") assertThat(log.getAttributes().size()).isEqualTo(3) - assertThat(log.getAttributes().get(AttributeKey.stringKey("key1"))).isEqualTo("val1") - assertThat(log.getAttributes().get(SemanticAttributes.THREAD_NAME)).isEqualTo(Thread.currentThread().getName()) - assertThat(log.getAttributes().get(SemanticAttributes.THREAD_ID)).isEqualTo(Thread.currentThread().getId()) + OpenTelemetryAssertions.assertThat(log.getAttributes()).containsEntry("key1", "val1") + OpenTelemetryAssertions.assertThat(log.getAttributes()).containsEntry(SemanticAttributes.THREAD_NAME, Thread.currentThread().getName()) + OpenTelemetryAssertions.assertThat(log.getAttributes()).containsEntry(SemanticAttributes.THREAD_ID, Thread.currentThread().getId()) } def "test structured data map message"() { @@ -188,17 +202,35 @@ class Log4j2Test extends AgentInstrumentationSpecification { await() .untilAsserted( () -> { - assertThat(logs).hasSize(1) + assertThat(logRecords).hasSize(1) }) - def log = logs.get(0) + def log = logRecords.get(0) assertThat(log.getBody().asString()).isEqualTo("a message") assertThat(log.getInstrumentationScopeInfo().getName()).isEqualTo("abc") assertThat(log.getSeverity()).isEqualTo(Severity.INFO) assertThat(log.getSeverityText()).isEqualTo("INFO") assertThat(log.getAttributes().size()).isEqualTo(4) - assertThat(log.getAttributes().get(AttributeKey.stringKey("key1"))).isEqualTo("val1") - assertThat(log.getAttributes().get(AttributeKey.stringKey("key2"))).isEqualTo("val2") - assertThat(log.getAttributes().get(SemanticAttributes.THREAD_NAME)).isEqualTo(Thread.currentThread().getName()) - assertThat(log.getAttributes().get(SemanticAttributes.THREAD_ID)).isEqualTo(Thread.currentThread().getId()) + OpenTelemetryAssertions.assertThat(log.getAttributes()) + .containsEntry("key1","val1") + .containsEntry("key2", "val2") + .containsEntry(SemanticAttributes.THREAD_NAME, Thread.currentThread().getName()) + .containsEntry(SemanticAttributes.THREAD_ID, Thread.currentThread().getId()) + } + + @Unroll + def "marker test"() { + def markerName = "aMarker" + def marker = MarkerManager.getMarker(markerName) + when: + logger.info(marker, "message") + + then: + await() + .untilAsserted( + () -> { + assertThat(logRecords).hasSize(1) + def log = logRecords.get(0) + OpenTelemetryAssertions.assertThat(log.getAttributes()).containsEntry("log4j.marker", markerName) + }) } } diff --git a/instrumentation/log4j/log4j-appender-2.17/library/README.md b/instrumentation/log4j/log4j-appender-2.17/library/README.md index 5fa74e799d4f..e8d261c0ffda 100644 --- a/instrumentation/log4j/log4j-appender-2.17/library/README.md +++ b/instrumentation/log4j/log4j-appender-2.17/library/README.md @@ -1,35 +1,37 @@ -# Log4j2 Appender +# Appender Instrumentation for Log4j2 version 2.17 and higher This module provides a Log4j2 [appender](https://logging.apache.org/log4j/2.x/manual/appenders.html) which forwards Log4j2 log events to the [OpenTelemetry Log SDK](https://github.com/open-telemetry/opentelemetry-java/tree/main/sdk/logs). -To use it, add the following modules to your application's classpath. +## Quickstart -Replace `OPENTELEMETRY_VERSION` with the latest -stable [release](https://search.maven.org/search?q=g:io.opentelemetry.instrumentation). +### Add these dependencies to your project: -**Maven** +Replace `OPENTELEMETRY_VERSION` with the [latest +release](https://search.maven.org/search?q=g:io.opentelemetry.instrumentation%20AND%20a:opentelemetry-log4j-appender-2.17). -```xml +For Maven, add to your `pom.xml` dependencies: +```xml io.opentelemetry.instrumentation opentelemetry-log4j-appender-2.17 OPENTELEMETRY_VERSION + runtime ``` -**Gradle** +For Gradle, add to your dependencies: -```kotlin -dependencies { - runtimeOnly("io.opentelemetry.instrumentation:opentelemetry-log4j-appender-2.17:OPENTELEMETRY_VERSION") -} +```groovy +runtimeOnly("io.opentelemetry.instrumentation:opentelemetry-log4j-appender-2.17:OPENTELEMETRY_VERSION") ``` +### Usage + The following demonstrates how you might configure the appender in your `log4j.xml` configuration: ```xml @@ -51,19 +53,17 @@ The following demonstrates how you might configure the appender in your `log4j.x ``` -Next, associate the `OpenTelemetryAppender` configured via `log4j2.xml` with -an `SdkLogEmitterProvider` in your application: +Next, configure `GlobalLoggerProvider` with an `SdkLoggerProvider` in your application. ``` -SdkLogEmitterProvider logEmitterProvider = - SdkLogEmitterProvider.builder() +SdkLoggerProvider sdkLoggerProvider = + SdkLoggerProvider.builder() .setResource(Resource.create(...)) .addLogProcessor(...) .build(); -OpenTelemetryAppender.setSdkLogEmitterProvider(logEmitterProvider); +GlobalLoggerProvider.set(sdkLoggerProvider); ``` In this example Log4j2 log events will be sent to both the console appender and -the `OpenTelemetryAppender`, which will drop the logs until -`OpenTelemetryAppender.setSdkLogEmitterProvider(..)` is called. Once initialized, logs will be -emitted to a `LogEmitter` obtained from the `SdkLogEmitterProvider`. +the `OpenTelemetryAppender`, which will drop the logs until `GlobalLoggerProvider.set(..)` is +called. Once initialized, logs will be emitted to a `Logger` obtained from the `SdkLoggerProvider`. 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 9901fb4bdd49..0ea0e1b629d0 100644 --- a/instrumentation/log4j/log4j-appender-2.17/library/build.gradle.kts +++ b/instrumentation/log4j/log4j-appender-2.17/library/build.gradle.kts @@ -3,18 +3,10 @@ plugins { } dependencies { - implementation(project(":instrumentation-appender-api-internal")) - implementation(project(":instrumentation-appender-sdk-internal")) + implementation("io.opentelemetry:opentelemetry-api-logs") library("org.apache.logging.log4j:log4j-core:2.17.0") - testImplementation(project(":instrumentation-appender-sdk-internal")) testImplementation("io.opentelemetry:opentelemetry-sdk-logs") testImplementation("io.opentelemetry:opentelemetry-sdk-testing") } - -tasks.withType().configureEach { - // TODO run tests both with and without experimental log attributes - jvmArgs("-Dotel.instrumentation.log4j-appender.experimental.capture-map-message-attributes=true") - jvmArgs("-Dotel.instrumentation.log4j-appender.experimental.capture-context-data-attributes=*") -} diff --git a/instrumentation/log4j/log4j-appender-2.17/library/src/main/java/io/opentelemetry/instrumentation/log4j/appender/v2_17/OpenTelemetryAppender.java b/instrumentation/log4j/log4j-appender-2.17/library/src/main/java/io/opentelemetry/instrumentation/log4j/appender/v2_17/OpenTelemetryAppender.java index 664e5cdc68d5..4f0e0d4275f3 100644 --- a/instrumentation/log4j/log4j-appender-2.17/library/src/main/java/io/opentelemetry/instrumentation/log4j/appender/v2_17/OpenTelemetryAppender.java +++ b/instrumentation/log4j/log4j-appender-2.17/library/src/main/java/io/opentelemetry/instrumentation/log4j/appender/v2_17/OpenTelemetryAppender.java @@ -5,17 +5,20 @@ package io.opentelemetry.instrumentation.log4j.appender.v2_17; -import io.opentelemetry.instrumentation.api.appender.internal.LogBuilder; -import io.opentelemetry.instrumentation.api.appender.internal.LogEmitterProvider; -import io.opentelemetry.instrumentation.api.appender.internal.LogEmitterProviderHolder; +import static java.util.Collections.emptyList; + +import io.opentelemetry.api.logs.GlobalLoggerProvider; +import io.opentelemetry.api.logs.LogRecordBuilder; import io.opentelemetry.instrumentation.log4j.appender.v2_17.internal.ContextDataAccessor; import io.opentelemetry.instrumentation.log4j.appender.v2_17.internal.LogEventMapper; -import io.opentelemetry.instrumentation.sdk.appender.internal.DelegatingLogEmitterProvider; -import io.opentelemetry.sdk.logs.SdkLogEmitterProvider; import java.io.Serializable; +import java.util.Arrays; +import java.util.List; import java.util.concurrent.TimeUnit; import java.util.function.BiConsumer; +import java.util.stream.Collectors; import javax.annotation.Nullable; +import org.apache.logging.log4j.ThreadContext; import org.apache.logging.log4j.core.Appender; import org.apache.logging.log4j.core.Core; import org.apache.logging.log4j.core.Filter; @@ -24,8 +27,10 @@ import org.apache.logging.log4j.core.appender.AbstractAppender; import org.apache.logging.log4j.core.config.Property; import org.apache.logging.log4j.core.config.plugins.Plugin; +import org.apache.logging.log4j.core.config.plugins.PluginBuilderAttribute; import org.apache.logging.log4j.core.config.plugins.PluginBuilderFactory; import org.apache.logging.log4j.core.time.Instant; +import org.apache.logging.log4j.message.MapMessage; import org.apache.logging.log4j.util.ReadOnlyStringMap; @Plugin( @@ -36,11 +41,7 @@ public class OpenTelemetryAppender extends AbstractAppender { static final String PLUGIN_NAME = "OpenTelemetry"; - private static final LogEmitterProviderHolder logEmitterProviderHolder = - new LogEmitterProviderHolder(); - - private static final LogEventMapper mapper = - new LogEventMapper<>(ContextDataAccessorImpl.INSTANCE); + private final LogEventMapper mapper; @PluginBuilderFactory public static > B builder() { @@ -50,10 +51,55 @@ public static > B builder() { static class Builder> extends AbstractAppender.Builder implements org.apache.logging.log4j.core.util.Builder { + @PluginBuilderAttribute private boolean captureExperimentalAttributes; + @PluginBuilderAttribute private boolean captureMapMessageAttributes; + @PluginBuilderAttribute private boolean captureMarkerAttribute; + @PluginBuilderAttribute private String captureContextDataAttributes; + + /** + * Sets whether experimental attributes should be set to logs. These attributes may be changed + * or removed in the future, so only enable this if you know you do not require attributes + * filled by this instrumentation to be stable across versions. + */ + public B setCaptureExperimentalAttributes(boolean captureExperimentalAttributes) { + this.captureExperimentalAttributes = captureExperimentalAttributes; + return asBuilder(); + } + + /** Sets whether log4j {@link MapMessage} attributes should be copied to logs. */ + public B setCaptureMapMessageAttributes(boolean captureMapMessageAttributes) { + this.captureMapMessageAttributes = captureMapMessageAttributes; + return asBuilder(); + } + + /** + * Sets whether the marker attribute should be set to logs. + * + * @param captureMarkerAttribute To enable or disable the marker attribute + */ + public B setCaptureMarkerAttribute(boolean captureMarkerAttribute) { + this.captureMarkerAttribute = captureMarkerAttribute; + return asBuilder(); + } + + /** Configures the {@link ThreadContext} attributes that will be copied to logs. */ + public B setCaptureContextDataAttributes(String captureContextDataAttributes) { + this.captureContextDataAttributes = captureContextDataAttributes; + return asBuilder(); + } + @Override public OpenTelemetryAppender build() { return new OpenTelemetryAppender( - getName(), getLayout(), getFilter(), isIgnoreExceptions(), getPropertyArray()); + getName(), + getLayout(), + getFilter(), + isIgnoreExceptions(), + getPropertyArray(), + captureExperimentalAttributes, + captureMapMessageAttributes, + captureMarkerAttribute, + captureContextDataAttributes); } } @@ -62,8 +108,30 @@ private OpenTelemetryAppender( Layout layout, Filter filter, boolean ignoreExceptions, - Property[] properties) { + Property[] properties, + boolean captureExperimentalAttributes, + boolean captureMapMessageAttributes, + boolean captureMarkerAttribute, + String captureContextDataAttributes) { + super(name, filter, layout, ignoreExceptions, properties); + this.mapper = + new LogEventMapper<>( + ContextDataAccessorImpl.INSTANCE, + captureExperimentalAttributes, + captureMapMessageAttributes, + captureMarkerAttribute, + splitAndFilterBlanksAndNulls(captureContextDataAttributes)); + } + + private static List splitAndFilterBlanksAndNulls(String value) { + if (value == null) { + return emptyList(); + } + return Arrays.stream(value.split(",")) + .map(String::trim) + .filter(s -> !s.isEmpty()) + .collect(Collectors.toList()); } @Override @@ -72,11 +140,16 @@ public void append(LogEvent event) { if (instrumentationName == null || instrumentationName.isEmpty()) { instrumentationName = "ROOT"; } - LogBuilder builder = - logEmitterProviderHolder.get().logEmitterBuilder(instrumentationName).build().logBuilder(); + LogRecordBuilder builder = + GlobalLoggerProvider.get().loggerBuilder(instrumentationName).build().logRecordBuilder(); ReadOnlyStringMap contextData = event.getContextData(); mapper.mapLogEvent( - builder, event.getMessage(), event.getLevel(), event.getThrown(), contextData); + builder, + event.getMessage(), + event.getLevel(), + event.getMarker(), + event.getThrown(), + contextData); Instant timestamp = event.getInstant(); if (timestamp != null) { @@ -88,25 +161,6 @@ public void append(LogEvent event) { builder.emit(); } - /** - * This should be called once as early as possible in your application initialization logic, often - * in a {@code static} block in your main class. It should only be called once - an attempt to - * call it a second time will result in an error. If trying to set the {@link - * SdkLogEmitterProvider} multiple times in tests, use {@link - * OpenTelemetryAppender#resetSdkLogEmitterProviderForTest()} between them. - */ - public static void setSdkLogEmitterProvider(SdkLogEmitterProvider sdkLogEmitterProvider) { - logEmitterProviderHolder.set(DelegatingLogEmitterProvider.from(sdkLogEmitterProvider)); - } - - /** - * Unsets the global {@link LogEmitterProvider}. This is only meant to be used from tests which - * need to reconfigure {@link LogEmitterProvider}. - */ - public static void resetSdkLogEmitterProviderForTest() { - logEmitterProviderHolder.resetForTest(); - } - private enum ContextDataAccessorImpl implements ContextDataAccessor { INSTANCE; 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 fdacce92a1d2..ea6e40176d71 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 @@ -5,15 +5,16 @@ package io.opentelemetry.instrumentation.log4j.appender.v2_17.internal; -import static java.util.Collections.emptyList; +import static io.opentelemetry.instrumentation.api.log.LoggingContextConstants.HELIOS_INSTRUMENTED_INDICATION; import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.common.AttributesBuilder; +import io.opentelemetry.api.logs.LogRecordBuilder; +import io.opentelemetry.api.logs.Severity; +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.api.trace.SpanContext; import io.opentelemetry.context.Context; -import io.opentelemetry.instrumentation.api.appender.internal.LogBuilder; -import io.opentelemetry.instrumentation.api.appender.internal.Severity; -import io.opentelemetry.instrumentation.api.config.Config; import io.opentelemetry.instrumentation.api.internal.cache.Cache; import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; import java.io.PrintWriter; @@ -21,6 +22,7 @@ import java.util.List; import javax.annotation.Nullable; import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.Marker; import org.apache.logging.log4j.core.LogEvent; import org.apache.logging.log4j.message.MapMessage; import org.apache.logging.log4j.message.Message; @@ -33,52 +35,54 @@ public final class LogEventMapper { private static final String SPECIAL_MAP_MESSAGE_ATTRIBUTE = "message"; - private static final boolean captureExperimentalAttributes = - Config.get() - .getBoolean("otel.instrumentation.log4j-appender.experimental-log-attributes", false); - private static final Cache> contextDataAttributeKeyCache = Cache.bounded(100); private static final Cache> mapMessageAttributeKeyCache = Cache.bounded(100); - private final boolean captureMapMessageAttributes; + private static final AttributeKey LOG_MARKER = AttributeKey.stringKey("log4j.marker"); - private final List captureContextDataAttributes; + private static boolean heliosInstrumentedIndicator = false; - // cached as an optimization - private final boolean captureAllContextDataAttributes; + private static void markInstrumentationIndicator(AttributesBuilder attributes) { + Context parentContext = Context.current(); + Span span = Span.fromContext(parentContext); + SpanContext parentSpanContext = span.getSpanContext(); - private final ContextDataAccessor contextDataAccessor; + if (!span.isRecording() || !parentSpanContext.isValid() || heliosInstrumentedIndicator) { + return; + } - public LogEventMapper(ContextDataAccessor contextDataAccessor) { - this( - contextDataAccessor, - Config.get() - .getBoolean( - "otel.instrumentation.log4j-appender.experimental.capture-map-message-attributes", - false), - Config.get() - .getList( - "otel.instrumentation.log4j-appender.experimental.capture-context-data-attributes", - emptyList())); + attributes.put(HELIOS_INSTRUMENTED_INDICATION, "log4j2"); + heliosInstrumentedIndicator = true; } - // visible for testing - LogEventMapper( + private final ContextDataAccessor contextDataAccessor; + + private final boolean captureExperimentalAttributes; + private final boolean captureMapMessageAttributes; + private final boolean captureMarkerAttribute; + private final List captureContextDataAttributes; + private final boolean captureAllContextDataAttributes; + + public LogEventMapper( ContextDataAccessor contextDataAccessor, + boolean captureExperimentalAttributes, boolean captureMapMessageAttributes, + boolean captureMarkerAttribute, List captureContextDataAttributes) { this.contextDataAccessor = contextDataAccessor; + this.captureExperimentalAttributes = captureExperimentalAttributes; this.captureMapMessageAttributes = captureMapMessageAttributes; + this.captureMarkerAttribute = captureMarkerAttribute; this.captureContextDataAttributes = captureContextDataAttributes; this.captureAllContextDataAttributes = captureContextDataAttributes.size() == 1 && captureContextDataAttributes.get(0).equals("*"); } /** - * Map the {@link LogEvent} data model onto the {@link LogBuilder}. Unmapped fields include: + * Map the {@link LogEvent} data model onto the {@link LogRecordBuilder}. Unmapped fields include: * *
    *
  • Fully qualified class name - {@link LogEvent#getLoggerFqcn()} @@ -90,16 +94,26 @@ public LogEventMapper(ContextDataAccessor contextDataAccessor) { *
*/ public void mapLogEvent( - LogBuilder builder, + LogRecordBuilder builder, Message message, Level level, + @Nullable Marker marker, @Nullable Throwable throwable, T contextData) { AttributesBuilder attributes = Attributes.builder(); + markInstrumentationIndicator(attributes); + captureMessage(builder, attributes, message); + if (captureMarkerAttribute) { + if (marker != null) { + String markerName = marker.getName(); + attributes.put(LOG_MARKER, markerName); + } + } + if (level != null) { builder.setSeverity(levelToSeverity(level)); builder.setSeverityText(level.name()); @@ -117,13 +131,13 @@ public void mapLogEvent( attributes.put(SemanticAttributes.THREAD_ID, currentThread.getId()); } - builder.setAttributes(attributes.build()); + builder.setAllAttributes(attributes.build()); builder.setContext(Context.current()); } // visible for testing - void captureMessage(LogBuilder builder, AttributesBuilder attributes, Message message) { + void captureMessage(LogRecordBuilder builder, AttributesBuilder attributes, Message message) { if (message == null) { return; } @@ -190,7 +204,7 @@ public static AttributeKey getContextDataAttributeKey(String key) { private static void setThrowable(AttributesBuilder attributes, Throwable throwable) { // TODO (trask) extract method for recording exception into - // instrumentation-appender-api-internal + // io.opentelemetry:opentelemetry-api-logs attributes.put(SemanticAttributes.EXCEPTION_TYPE, throwable.getClass().getName()); attributes.put(SemanticAttributes.EXCEPTION_MESSAGE, throwable.getMessage()); StringWriter writer = new StringWriter(); diff --git a/instrumentation/log4j/log4j-appender-2.17/library/src/test/java/io/opentelemetry/instrumentation/log4j/appender/v2_17/OpenTelemetryAppenderConfigTest.java b/instrumentation/log4j/log4j-appender-2.17/library/src/test/java/io/opentelemetry/instrumentation/log4j/appender/v2_17/OpenTelemetryAppenderConfigTest.java index 346524b762ed..2ecda4cf0624 100644 --- a/instrumentation/log4j/log4j-appender-2.17/library/src/test/java/io/opentelemetry/instrumentation/log4j/appender/v2_17/OpenTelemetryAppenderConfigTest.java +++ b/instrumentation/log4j/log4j-appender-2.17/library/src/test/java/io/opentelemetry/instrumentation/log4j/appender/v2_17/OpenTelemetryAppenderConfigTest.java @@ -9,15 +9,16 @@ import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.logs.GlobalLoggerProvider; +import io.opentelemetry.api.logs.Severity; import io.opentelemetry.api.trace.Span; import io.opentelemetry.api.trace.SpanContext; import io.opentelemetry.context.Scope; import io.opentelemetry.sdk.common.InstrumentationScopeInfo; -import io.opentelemetry.sdk.logs.SdkLogEmitterProvider; -import io.opentelemetry.sdk.logs.data.LogData; -import io.opentelemetry.sdk.logs.data.Severity; -import io.opentelemetry.sdk.logs.export.InMemoryLogExporter; -import io.opentelemetry.sdk.logs.export.SimpleLogProcessor; +import io.opentelemetry.sdk.logs.SdkLoggerProvider; +import io.opentelemetry.sdk.logs.data.LogRecordData; +import io.opentelemetry.sdk.logs.export.InMemoryLogRecordExporter; +import io.opentelemetry.sdk.logs.export.SimpleLogRecordProcessor; import io.opentelemetry.sdk.resources.Resource; import io.opentelemetry.sdk.trace.SdkTracerProvider; import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; @@ -26,6 +27,8 @@ import java.util.concurrent.TimeUnit; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.Marker; +import org.apache.logging.log4j.MarkerManager; import org.apache.logging.log4j.ThreadContext; import org.apache.logging.log4j.message.StringMapMessage; import org.apache.logging.log4j.message.StructuredDataMessage; @@ -37,29 +40,29 @@ class OpenTelemetryAppenderConfigTest { private static final Logger logger = LogManager.getLogger("TestLogger"); - private static InMemoryLogExporter logExporter; + private static InMemoryLogRecordExporter logRecordExporter; private static Resource resource; private static InstrumentationScopeInfo instrumentationScopeInfo; @BeforeAll static void setupAll() { - logExporter = InMemoryLogExporter.create(); + logRecordExporter = InMemoryLogRecordExporter.create(); resource = Resource.getDefault(); instrumentationScopeInfo = InstrumentationScopeInfo.create("TestLogger"); - SdkLogEmitterProvider logEmitterProvider = - SdkLogEmitterProvider.builder() + SdkLoggerProvider loggerProvider = + SdkLoggerProvider.builder() .setResource(resource) - .addLogProcessor(SimpleLogProcessor.create(logExporter)) + .addLogRecordProcessor(SimpleLogRecordProcessor.create(logRecordExporter)) .build(); - OpenTelemetryAppender.resetSdkLogEmitterProviderForTest(); - OpenTelemetryAppender.setSdkLogEmitterProvider(logEmitterProvider); + GlobalLoggerProvider.resetForTest(); + GlobalLoggerProvider.set(loggerProvider); } @BeforeEach void setup() { - logExporter.reset(); + logRecordExporter.reset(); ThreadContext.clearAll(); } @@ -67,9 +70,9 @@ void setup() { void logNoSpan() { logger.info("log message 1"); - List logDataList = logExporter.getFinishedLogItems(); + List logDataList = logRecordExporter.getFinishedLogItems(); assertThat(logDataList).hasSize(1); - LogData logData = logDataList.get(0); + LogRecordData logData = logDataList.get(0); assertThat(logData.getResource()).isEqualTo(resource); assertThat(logData.getInstrumentationScopeInfo()).isEqualTo(instrumentationScopeInfo); assertThat(logData.getBody().asString()).isEqualTo("log message 1"); @@ -84,7 +87,7 @@ void logWithSpan() { Span span2 = runWithSpan("span2", () -> logger.info("log message 3")); - List logDataList = logExporter.getFinishedLogItems(); + List logDataList = logRecordExporter.getFinishedLogItems(); assertThat(logDataList).hasSize(3); assertThat(logDataList.get(0).getSpanContext()).isEqualTo(span1.getSpanContext()); assertThat(logDataList.get(1).getSpanContext()).isEqualTo(SpanContext.getInvalid()); @@ -106,9 +109,9 @@ void logWithExtras() { Instant start = Instant.now(); logger.info("log message 1", new IllegalStateException("Error!")); - List logDataList = logExporter.getFinishedLogItems(); + List logDataList = logRecordExporter.getFinishedLogItems(); assertThat(logDataList).hasSize(1); - LogData logData = logDataList.get(0); + LogRecordData logData = logDataList.get(0); assertThat(logData.getResource()).isEqualTo(resource); assertThat(logData.getInstrumentationScopeInfo()).isEqualTo(instrumentationScopeInfo); assertThat(logData.getBody().asString()).isEqualTo("log message 1"); @@ -136,9 +139,9 @@ void logContextData() { ThreadContext.clearMap(); } - List logDataList = logExporter.getFinishedLogItems(); + List logDataList = logRecordExporter.getFinishedLogItems(); assertThat(logDataList).hasSize(1); - LogData logData = logDataList.get(0); + LogRecordData logData = logDataList.get(0); assertThat(logData.getResource()).isEqualTo(resource); assertThat(logData.getInstrumentationScopeInfo()).isEqualTo(instrumentationScopeInfo); assertThat(logData.getBody().asString()).isEqualTo("log message 1"); @@ -156,9 +159,9 @@ void logStringMapMessage() { message.put("key2", "val2"); logger.info(message); - List logDataList = logExporter.getFinishedLogItems(); + List logDataList = logRecordExporter.getFinishedLogItems(); assertThat(logDataList).hasSize(1); - LogData logData = logDataList.get(0); + LogRecordData logData = logDataList.get(0); assertThat(logData.getResource()).isEqualTo(resource); assertThat(logData.getInstrumentationScopeInfo()).isEqualTo(instrumentationScopeInfo); assertThat(logData.getBody().asString()).isEmpty(); @@ -174,9 +177,9 @@ void logStringMapMessageWithSpecialAttribute() { message.put("message", "val2"); logger.info(message); - List logDataList = logExporter.getFinishedLogItems(); + List logDataList = logRecordExporter.getFinishedLogItems(); assertThat(logDataList).hasSize(1); - LogData logData = logDataList.get(0); + LogRecordData logData = logDataList.get(0); assertThat(logData.getResource()).isEqualTo(resource); assertThat(logData.getInstrumentationScopeInfo()).isEqualTo(instrumentationScopeInfo); assertThat(logData.getBody().asString()).isEqualTo("val2"); @@ -184,6 +187,19 @@ void logStringMapMessageWithSpecialAttribute() { assertThat(logData.getAttributes().get(AttributeKey.stringKey("key1"))).isEqualTo("val1"); } + @Test + void testCaptureMarkerAttribute() { + String markerName = "aMarker"; + Marker marker = MarkerManager.getMarker(markerName); + + logger.info(marker, "Message"); + + List logDataList = logRecordExporter.getFinishedLogItems(); + LogRecordData logData = logDataList.get(0); + assertThat(logData.getAttributes().get(AttributeKey.stringKey("log4j.marker"))) + .isEqualTo(markerName); + } + @Test void logStructuredDataMessage() { StructuredDataMessage message = new StructuredDataMessage("an id", "a message", "a type"); @@ -191,9 +207,9 @@ void logStructuredDataMessage() { message.put("key2", "val2"); logger.info(message); - List logDataList = logExporter.getFinishedLogItems(); + List logDataList = logRecordExporter.getFinishedLogItems(); assertThat(logDataList).hasSize(1); - LogData logData = logDataList.get(0); + LogRecordData logData = logDataList.get(0); assertThat(logData.getResource()).isEqualTo(resource); assertThat(logData.getInstrumentationScopeInfo()).isEqualTo(instrumentationScopeInfo); assertThat(logData.getBody().asString()).isEqualTo("a message"); diff --git a/instrumentation/log4j/log4j-appender-2.17/library/src/test/java/io/opentelemetry/instrumentation/log4j/appender/v2_17/internal/LogEventMapperTest.java b/instrumentation/log4j/log4j-appender-2.17/library/src/test/java/io/opentelemetry/instrumentation/log4j/appender/v2_17/internal/LogEventMapperTest.java index 4140bd72a488..4e2c430a0a4a 100644 --- a/instrumentation/log4j/log4j-appender-2.17/library/src/test/java/io/opentelemetry/instrumentation/log4j/appender/v2_17/internal/LogEventMapperTest.java +++ b/instrumentation/log4j/log4j-appender-2.17/library/src/test/java/io/opentelemetry/instrumentation/log4j/appender/v2_17/internal/LogEventMapperTest.java @@ -17,14 +17,14 @@ import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.common.AttributesBuilder; -import io.opentelemetry.instrumentation.api.appender.internal.LogBuilder; +import io.opentelemetry.api.logs.LogRecordBuilder; import java.util.HashMap; import java.util.Map; import java.util.function.BiConsumer; import javax.annotation.Nullable; import org.apache.logging.log4j.message.StringMapMessage; import org.apache.logging.log4j.message.StructuredDataMessage; -import org.junit.Test; +import org.junit.jupiter.api.Test; class LogEventMapperTest { @@ -32,7 +32,7 @@ class LogEventMapperTest { void testDefault() { // given LogEventMapper> mapper = - new LogEventMapper<>(ContextDataAccessorImpl.INSTANCE, false, emptyList()); + new LogEventMapper<>(ContextDataAccessorImpl.INSTANCE, false, false, false, emptyList()); Map contextData = new HashMap<>(); contextData.put("key1", "value1"); contextData.put("key2", "value2"); @@ -49,7 +49,8 @@ void testDefault() { void testSome() { // given LogEventMapper> mapper = - new LogEventMapper<>(ContextDataAccessorImpl.INSTANCE, false, singletonList("key2")); + new LogEventMapper<>( + ContextDataAccessorImpl.INSTANCE, false, false, false, singletonList("key2")); Map contextData = new HashMap<>(); contextData.put("key1", "value1"); contextData.put("key2", "value2"); @@ -67,7 +68,8 @@ void testSome() { void testAll() { // given LogEventMapper> mapper = - new LogEventMapper<>(ContextDataAccessorImpl.INSTANCE, false, singletonList("*")); + new LogEventMapper<>( + ContextDataAccessorImpl.INSTANCE, false, false, false, singletonList("*")); Map contextData = new HashMap<>(); contextData.put("key1", "value1"); contextData.put("key2", "value2"); @@ -87,20 +89,21 @@ void testAll() { void testCaptureMapMessageDisabled() { // given LogEventMapper> mapper = - new LogEventMapper<>(ContextDataAccessorImpl.INSTANCE, false, singletonList("*")); + new LogEventMapper<>( + ContextDataAccessorImpl.INSTANCE, false, false, false, singletonList("*")); StringMapMessage message = new StringMapMessage(); message.put("key1", "value1"); message.put("message", "value2"); - LogBuilder logBuilder = mock(LogBuilder.class); + LogRecordBuilder logRecordBuilder = mock(LogRecordBuilder.class); AttributesBuilder attributes = Attributes.builder(); // when - mapper.captureMessage(logBuilder, attributes, message); + mapper.captureMessage(logRecordBuilder, attributes, message); // then - verify(logBuilder).setBody("value2"); + verify(logRecordBuilder).setBody("value2"); assertThat(attributes.build()).isEmpty(); } @@ -108,20 +111,21 @@ void testCaptureMapMessageDisabled() { void testCaptureMapMessageWithSpecialAttribute() { // given LogEventMapper> mapper = - new LogEventMapper<>(ContextDataAccessorImpl.INSTANCE, true, singletonList("*")); + new LogEventMapper<>( + ContextDataAccessorImpl.INSTANCE, false, true, false, singletonList("*")); StringMapMessage message = new StringMapMessage(); message.put("key1", "value1"); message.put("message", "value2"); - LogBuilder logBuilder = mock(LogBuilder.class); + LogRecordBuilder logRecordBuilder = mock(LogRecordBuilder.class); AttributesBuilder attributes = Attributes.builder(); // when - mapper.captureMessage(logBuilder, attributes, message); + mapper.captureMessage(logRecordBuilder, attributes, message); // then - verify(logBuilder).setBody("value2"); + verify(logRecordBuilder).setBody("value2"); assertThat(attributes.build()).containsOnly(entry(AttributeKey.stringKey("key1"), "value1")); } @@ -129,20 +133,21 @@ void testCaptureMapMessageWithSpecialAttribute() { void testCaptureMapMessageWithoutSpecialAttribute() { // given LogEventMapper> mapper = - new LogEventMapper<>(ContextDataAccessorImpl.INSTANCE, true, singletonList("*")); + new LogEventMapper<>( + ContextDataAccessorImpl.INSTANCE, false, true, false, singletonList("*")); StringMapMessage message = new StringMapMessage(); message.put("key1", "value1"); message.put("key2", "value2"); - LogBuilder logBuilder = mock(LogBuilder.class); + LogRecordBuilder logRecordBuilder = mock(LogRecordBuilder.class); AttributesBuilder attributes = Attributes.builder(); // when - mapper.captureMessage(logBuilder, attributes, message); + mapper.captureMessage(logRecordBuilder, attributes, message); // then - verify(logBuilder, never()).setBody(anyString()); + verify(logRecordBuilder, never()).setBody(anyString()); assertThat(attributes.build()) .containsOnly( entry(AttributeKey.stringKey("key1"), "value1"), @@ -153,20 +158,21 @@ void testCaptureMapMessageWithoutSpecialAttribute() { void testCaptureStructuredDataMessage() { // given LogEventMapper> mapper = - new LogEventMapper<>(ContextDataAccessorImpl.INSTANCE, true, singletonList("*")); + new LogEventMapper<>( + ContextDataAccessorImpl.INSTANCE, false, true, false, singletonList("*")); StructuredDataMessage message = new StructuredDataMessage("an id", "a message", "a type"); message.put("key1", "value1"); message.put("message", "value2"); - LogBuilder logBuilder = mock(LogBuilder.class); + LogRecordBuilder logRecordBuilder = mock(LogRecordBuilder.class); AttributesBuilder attributes = Attributes.builder(); // when - mapper.captureMessage(logBuilder, attributes, message); + mapper.captureMessage(logRecordBuilder, attributes, message); // then - verify(logBuilder).setBody("a message"); + verify(logRecordBuilder).setBody("a message"); assertThat(attributes.build()) .containsOnly( entry(AttributeKey.stringKey("key1"), "value1"), diff --git a/instrumentation/log4j/log4j-appender-2.17/library/src/test/resources/log4j2-test.xml b/instrumentation/log4j/log4j-appender-2.17/library/src/test/resources/log4j2-test.xml index 643ef0ba9771..47be48e7ccac 100644 --- a/instrumentation/log4j/log4j-appender-2.17/library/src/test/resources/log4j2-test.xml +++ b/instrumentation/log4j/log4j-appender-2.17/library/src/test/resources/log4j2-test.xml @@ -6,13 +6,12 @@ - - + + - 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 6b514b6ec260..027df75c1d88 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 @@ -1,34 +1,36 @@ -# Log4j2 Autoconfigure Integration +# ContextData Instrumentation for Log4j2 version 2.17 and higher This module provides a Log4j2 `ContextDataProvider` that injects trace context from active spans into log context. -To use it, add the module to your application's runtime classpath. +## Quickstart -Replace `OPENTELEMETRY_VERSION` with the latest -stable [release](https://search.maven.org/search?q=g:io.opentelemetry.instrumentation). +### Add these dependencies to your project: -**Maven** +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). -```xml +For Maven, add to your `pom.xml` dependencies: +```xml io.opentelemetry.instrumentation opentelemetry-log4j-context-data-2.17-autoconfigure OPENTELEMETRY_VERSION + runtime ``` -**Gradle** +For Gradle, add to your dependencies: -```kotlin -dependencies { - runtimeOnly("io.opentelemetry.instrumentation:opentelemetry-log4j-context-data-2.17-autoconfigure:OPENTELEMETRY_VERSION") -} +```groovy +runtimeOnly("io.opentelemetry.instrumentation:opentelemetry-log4j-context-data-2.17-autoconfigure:OPENTELEMETRY_VERSION") ``` +### Usage + `OpenTelemetryContextDataProvider` implements the Log4j2 `ContextDataProvider` SPI, and injects the trace ID and span ID from an active span into Log4j's [context data](https://logging.apache.org/log4j/2.x/manual/thread-context.html). diff --git a/instrumentation/log4j/log4j-context-data/log4j-context-data-2.17/library-autoconfigure/src/main/java/io/opentelemetry/instrumentation/log4j/contextdata/v2_17/OpenTelemetryContextDataProvider.java b/instrumentation/log4j/log4j-context-data/log4j-context-data-2.17/library-autoconfigure/src/main/java/io/opentelemetry/instrumentation/log4j/contextdata/v2_17/OpenTelemetryContextDataProvider.java index 8aa905c02937..e6dc41941ea0 100644 --- a/instrumentation/log4j/log4j-context-data/log4j-context-data-2.17/library-autoconfigure/src/main/java/io/opentelemetry/instrumentation/log4j/contextdata/v2_17/OpenTelemetryContextDataProvider.java +++ b/instrumentation/log4j/log4j-context-data/log4j-context-data-2.17/library-autoconfigure/src/main/java/io/opentelemetry/instrumentation/log4j/contextdata/v2_17/OpenTelemetryContextDataProvider.java @@ -5,12 +5,14 @@ package io.opentelemetry.instrumentation.log4j.contextdata.v2_17; +import static io.opentelemetry.instrumentation.api.log.LoggingContextConstants.HELIOS_INSTRUMENTED_INDICATION; import static io.opentelemetry.instrumentation.api.log.LoggingContextConstants.SPAN_ID; import static io.opentelemetry.instrumentation.api.log.LoggingContextConstants.TRACE_FLAGS; import static io.opentelemetry.instrumentation.api.log.LoggingContextConstants.TRACE_ID; import io.opentelemetry.api.trace.Span; import io.opentelemetry.api.trace.SpanContext; +import io.opentelemetry.context.Context; import java.util.Collections; import java.util.HashMap; import java.util.Map; @@ -22,6 +24,21 @@ */ public class OpenTelemetryContextDataProvider implements ContextDataProvider { + private static boolean heliosInstrumentedIndicator = false; + + private static void markInstrumentationIndicator() { + Context parentContext = Context.current(); + Span span = Span.fromContext(parentContext); + SpanContext parentSpanContext = span.getSpanContext(); + + if (!span.isRecording() || !parentSpanContext.isValid() || heliosInstrumentedIndicator) { + return; + } + + span.setAttribute(HELIOS_INSTRUMENTED_INDICATION, "log4j2"); + heliosInstrumentedIndicator = true; + } + /** * Returns context from the current span when available. * @@ -34,6 +51,7 @@ public Map supplyContextData() { if (!currentSpan.getSpanContext().isValid()) { return Collections.emptyMap(); } + markInstrumentationIndicator(); Map contextData = new HashMap<>(); SpanContext spanContext = currentSpan.getSpanContext(); 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 8abd67392792..1bf995beec1d 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 @@ -18,5 +18,5 @@ dependencies { testImplementation(project(":instrumentation:log4j:log4j-context-data:log4j-context-data-common:testing")) - latestDepTestLibrary("org.apache.logging.log4j:log4j-core:2.16.+") // see log4j-context-data-2.17 + latestDepTestLibrary("org.apache.logging.log4j:log4j-core:2.16.+") // see log4j-context-data-2.17 module } diff --git a/instrumentation/log4j/log4j-context-data/log4j-context-data-2.7/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/log4j/contextdata/v2_7/SpanDecoratingContextDataInjector.java b/instrumentation/log4j/log4j-context-data/log4j-context-data-2.7/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/log4j/contextdata/v2_7/SpanDecoratingContextDataInjector.java index d597faf36e32..9b8c949a7cf2 100644 --- a/instrumentation/log4j/log4j-context-data/log4j-context-data-2.7/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/log4j/contextdata/v2_7/SpanDecoratingContextDataInjector.java +++ b/instrumentation/log4j/log4j-context-data/log4j-context-data-2.7/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/log4j/contextdata/v2_7/SpanDecoratingContextDataInjector.java @@ -5,11 +5,14 @@ package io.opentelemetry.javaagent.instrumentation.log4j.contextdata.v2_7; +import static io.opentelemetry.instrumentation.api.log.LoggingContextConstants.HELIOS_INSTRUMENTED_INDICATION; import static io.opentelemetry.instrumentation.api.log.LoggingContextConstants.SPAN_ID; import static io.opentelemetry.instrumentation.api.log.LoggingContextConstants.TRACE_FLAGS; import static io.opentelemetry.instrumentation.api.log.LoggingContextConstants.TRACE_ID; +import io.opentelemetry.api.trace.Span; import io.opentelemetry.api.trace.SpanContext; +import io.opentelemetry.context.Context; import io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge; import java.util.List; import org.apache.logging.log4j.core.ContextDataInjector; @@ -19,6 +22,22 @@ import org.apache.logging.log4j.util.StringMap; public final class SpanDecoratingContextDataInjector implements ContextDataInjector { + + private static boolean heliosInstrumentedIndicator = false; + + private static void markInstrumentationIndicator() { + Context parentContext = Context.current(); + Span span = Span.fromContext(parentContext); + SpanContext parentSpanContext = span.getSpanContext(); + + if (!span.isRecording() || !parentSpanContext.isValid() || heliosInstrumentedIndicator) { + return; + } + + span.setAttribute(HELIOS_INSTRUMENTED_INDICATION, "log4j2"); + heliosInstrumentedIndicator = true; + } + private final ContextDataInjector delegate; public SpanDecoratingContextDataInjector(ContextDataInjector delegate) { @@ -34,11 +53,14 @@ public StringMap injectContextData(List list, StringMap stringMap) { return contextData; } - SpanContext currentContext = Java8BytecodeBridge.currentSpan().getSpanContext(); + Span span = Java8BytecodeBridge.currentSpan(); + SpanContext currentContext = span.getSpanContext(); if (!currentContext.isValid()) { return contextData; } + markInstrumentationIndicator(); + StringMap newContextData = new SortedArrayStringMap(contextData); newContextData.putValue(TRACE_ID, currentContext.getTraceId()); newContextData.putValue(SPAN_ID, currentContext.getSpanId()); diff --git a/instrumentation/log4j/log4j-context-data/log4j-context-data-common/testing/src/main/groovy/Log4j2Test.groovy b/instrumentation/log4j/log4j-context-data/log4j-context-data-common/testing/src/main/groovy/Log4j2Test.groovy index 091e38914654..d754d2a9f03e 100644 --- a/instrumentation/log4j/log4j-context-data/log4j-context-data-common/testing/src/main/groovy/Log4j2Test.groovy +++ b/instrumentation/log4j/log4j-context-data/log4j-context-data-common/testing/src/main/groovy/Log4j2Test.groovy @@ -34,6 +34,8 @@ abstract class Log4j2Test extends InstrumentationSpecification { events[1].contextData["trace_id"] == null events[1].contextData["span_id"] == null events[1].contextData["trace_flags"] == null + + assertTraces(0) {} } def "ids when span"() { @@ -71,5 +73,20 @@ abstract class Log4j2Test extends InstrumentationSpecification { events[2].contextData["trace_id"] == span2.spanContext.traceId events[2].contextData["span_id"] == span2.spanContext.spanId events[2].contextData["trace_flags"] == "01" + + assertTraces(2) { + trace(0, 1) { + span(0) { + attributes { + "heliosLogInstrumented" "log4j2" + } + } + } + trace(1, 1) { + span(0) { + attributes {} + } + } + } } } diff --git a/instrumentation/logback/logback-appender-1.0/javaagent/build.gradle.kts b/instrumentation/logback/logback-appender-1.0/javaagent/build.gradle.kts index 2c7a9c72abc6..10106082a188 100644 --- a/instrumentation/logback/logback-appender-1.0/javaagent/build.gradle.kts +++ b/instrumentation/logback/logback-appender-1.0/javaagent/build.gradle.kts @@ -15,13 +15,39 @@ muzzle { } dependencies { - library("ch.qos.logback:logback-classic:0.9.16") + // pin the version strictly to avoid overriding by dependencyManagement versions + compileOnly("ch.qos.logback:logback-classic") { + version { + strictly("1.0.0") + } + } + compileOnly("org.slf4j:slf4j-api") { + version { + strictly("1.5.8") + } + } + + if (findProperty("testLatestDeps") as Boolean) { + testImplementation("ch.qos.logback:logback-classic:+") + } else { + testImplementation("ch.qos.logback:logback-classic") { + version { + strictly("1.0.0") + } + } + testImplementation("org.slf4j:slf4j-api") { + version { + strictly("1.7.36") + } + } + } - compileOnly(project(":instrumentation-appender-api-internal")) + compileOnly("io.opentelemetry:opentelemetry-api-logs") compileOnly(project(":javaagent-bootstrap")) implementation(project(":instrumentation:logback:logback-appender-1.0:library")) + testImplementation("io.opentelemetry:opentelemetry-sdk-logs-testing") testImplementation("org.awaitility:awaitility") } @@ -29,4 +55,6 @@ tasks.withType().configureEach { // TODO run tests both with and without experimental log attributes jvmArgs("-Dotel.instrumentation.logback-appender.experimental.capture-mdc-attributes=*") jvmArgs("-Dotel.instrumentation.logback-appender.experimental-log-attributes=true") + jvmArgs("-Dotel.instrumentation.logback-appender.experimental.capture-code-attributes=true") + jvmArgs("-Dotel.instrumentation.logback-appender.experimental.capture-marker-attribute=true") } diff --git a/instrumentation/logback/logback-appender-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/logback/appender/v1_0/LogbackInstrumentation.java b/instrumentation/logback/logback-appender-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/logback/appender/v1_0/LogbackInstrumentation.java index fed3485f0c70..57bf5df94b9f 100644 --- a/instrumentation/logback/logback-appender-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/logback/appender/v1_0/LogbackInstrumentation.java +++ b/instrumentation/logback/logback-appender-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/logback/appender/v1_0/LogbackInstrumentation.java @@ -5,6 +5,7 @@ package io.opentelemetry.javaagent.instrumentation.logback.appender.v1_0; +import static io.opentelemetry.javaagent.instrumentation.logback.appender.v1_0.LogbackSingletons.mapper; import static net.bytebuddy.matcher.ElementMatchers.isMethod; import static net.bytebuddy.matcher.ElementMatchers.isPublic; import static net.bytebuddy.matcher.ElementMatchers.named; @@ -12,9 +13,8 @@ import static net.bytebuddy.matcher.ElementMatchers.takesArguments; import ch.qos.logback.classic.spi.ILoggingEvent; -import io.opentelemetry.instrumentation.api.appender.internal.LogEmitterProvider; -import io.opentelemetry.instrumentation.logback.appender.v1_0.internal.LoggingEventMapper; -import io.opentelemetry.javaagent.bootstrap.AgentLogEmitterProvider; +import io.opentelemetry.api.logs.GlobalLoggerProvider; +import io.opentelemetry.api.logs.LoggerProvider; import io.opentelemetry.javaagent.bootstrap.CallDepth; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; @@ -49,9 +49,9 @@ public static void methodEnter( @Advice.Local("otelCallDepth") CallDepth callDepth) { // need to track call depth across all loggers in order to avoid double capture when one // logging framework delegates to another - callDepth = CallDepth.forClass(LogEmitterProvider.class); + callDepth = CallDepth.forClass(LoggerProvider.class); if (callDepth.getAndIncrement() == 0) { - LoggingEventMapper.INSTANCE.emit(AgentLogEmitterProvider.get(), event); + mapper().emit(GlobalLoggerProvider.get(), event); } } diff --git a/instrumentation/logback/logback-appender-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/logback/appender/v1_0/LogbackSingletons.java b/instrumentation/logback/logback-appender-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/logback/appender/v1_0/LogbackSingletons.java new file mode 100644 index 000000000000..a91bf7fcf742 --- /dev/null +++ b/instrumentation/logback/logback-appender-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/logback/appender/v1_0/LogbackSingletons.java @@ -0,0 +1,48 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.logback.appender.v1_0; + +import static java.util.Collections.emptyList; + +import io.opentelemetry.instrumentation.logback.appender.v1_0.internal.LoggingEventMapper; +import io.opentelemetry.javaagent.bootstrap.internal.InstrumentationConfig; +import java.util.List; + +public final class LogbackSingletons { + + private static final LoggingEventMapper mapper; + + static { + InstrumentationConfig config = InstrumentationConfig.get(); + + boolean captureExperimentalAttributes = + config.getBoolean( + "otel.instrumentation.logback-appender.experimental-log-attributes", false); + boolean captureCodeAttributes = + config.getBoolean( + "otel.instrumentation.logback-appender.experimental.capture-code-attributes", false); + boolean captureMarkerAttribute = + config.getBoolean( + "otel.instrumentation.logback-appender.experimental.capture-marker-attribute", false); + List captureMdcAttributes = + config.getList( + "otel.instrumentation.logback-appender.experimental.capture-mdc-attributes", + emptyList()); + + mapper = + new LoggingEventMapper( + captureExperimentalAttributes, + captureMdcAttributes, + captureCodeAttributes, + captureMarkerAttribute); + } + + public static LoggingEventMapper mapper() { + return mapper; + } + + private LogbackSingletons() {} +} diff --git a/instrumentation/logback/logback-appender-1.0/javaagent/src/test/groovy/LogbackTest.groovy b/instrumentation/logback/logback-appender-1.0/javaagent/src/test/groovy/LogbackTest.groovy deleted file mode 100644 index fc62df876ab4..000000000000 --- a/instrumentation/logback/logback-appender-1.0/javaagent/src/test/groovy/LogbackTest.groovy +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -import io.opentelemetry.api.common.AttributeKey -import io.opentelemetry.instrumentation.test.AgentInstrumentationSpecification -import io.opentelemetry.sdk.logs.data.Severity -import io.opentelemetry.semconv.trace.attributes.SemanticAttributes -import org.slf4j.Logger -import org.slf4j.LoggerFactory -import org.slf4j.MDC -import spock.lang.Unroll - -import static org.assertj.core.api.Assertions.assertThat -import static org.awaitility.Awaitility.await - -class LogbackTest extends AgentInstrumentationSpecification { - - private static final Logger abcLogger = LoggerFactory.getLogger("abc") - private static final Logger defLogger = LoggerFactory.getLogger("def") - - @Unroll - def "test logger=#loggerName method=#testMethod with exception=#exception and parent=#parent"() { - when: - if (parent) { - runWithSpan("parent") { - if (exception) { - logger."$testMethod"("xyz: {}", 123, new IllegalStateException("hello")) - } else { - logger."$testMethod"("xyz: {}", 123) - } - } - } else { - if (exception) { - logger."$testMethod"("xyz: {}", 123, new IllegalStateException("hello")) - } else { - logger."$testMethod"("xyz: {}", 123) - } - } - - then: - if (parent) { - waitForTraces(1) - } - - if (severity != null) { - await() - .untilAsserted( - () -> { - assertThat(logs).hasSize(1) - }) - def log = logs.get(0) - assertThat(log.getBody().asString()).isEqualTo("xyz: 123") - assertThat(log.getInstrumentationScopeInfo().getName()).isEqualTo(loggerName) - assertThat(log.getSeverity()).isEqualTo(severity) - assertThat(log.getSeverityText()).isEqualTo(severityText) - if (exception) { - assertThat(log.getAttributes().size()).isEqualTo(5) - assertThat(log.getAttributes().get(SemanticAttributes.EXCEPTION_TYPE)).isEqualTo(IllegalStateException.getName()) - assertThat(log.getAttributes().get(SemanticAttributes.EXCEPTION_MESSAGE)).isEqualTo("hello") - assertThat(log.getAttributes().get(SemanticAttributes.EXCEPTION_STACKTRACE)).contains(LogbackTest.name) - } else { - assertThat(log.getAttributes().size()).isEqualTo(2) - assertThat(log.getAttributes().get(SemanticAttributes.EXCEPTION_TYPE)).isNull() - assertThat(log.getAttributes().get(SemanticAttributes.EXCEPTION_MESSAGE)).isNull() - assertThat(log.getAttributes().get(SemanticAttributes.EXCEPTION_STACKTRACE)).isNull() - } - assertThat(log.getAttributes().get(SemanticAttributes.THREAD_NAME)).isEqualTo(Thread.currentThread().getName()) - assertThat(log.getAttributes().get(SemanticAttributes.THREAD_ID)).isEqualTo(Thread.currentThread().getId()) - if (parent) { - assertThat(log.getSpanContext()).isEqualTo(traces.get(0).get(0).getSpanContext()) - } else { - assertThat(log.getSpanContext().isValid()).isFalse() - } - } else { - Thread.sleep(500) // sleep a bit just to make sure no span is captured - logs.size() == 0 - } - - where: - [args, exception, parent] << [ - [ - [abcLogger, "abc", "debug", null, null], - [abcLogger, "abc", "info", Severity.INFO, "INFO"], - [abcLogger, "abc", "warn", Severity.WARN, "WARN"], - [abcLogger, "abc", "error", Severity.ERROR, "ERROR"], - [defLogger, "def", "debug", null, null], - [defLogger, "def", "info", null, null], - [defLogger, "def", "warn", Severity.WARN, "WARN"], - [defLogger, "def", "error", Severity.ERROR, "ERROR"] - ], - [true, false], - [true, false] - ].combinations() - - logger = args[0] - loggerName = args[1] - testMethod = args[2] - severity = args[3] - severityText = args[4] - } - - def "test mdc"() { - when: - MDC.put("key1", "val1") - MDC.put("key2", "val2") - try { - abcLogger.info("xyz: {}", 123) - } finally { - MDC.clear() - } - - then: - - await() - .untilAsserted( - () -> { - assertThat(logs).hasSize(1) - }) - def log = logs.get(0) - assertThat(log.getBody().asString()).isEqualTo("xyz: 123") - assertThat(log.getInstrumentationScopeInfo().getName()).isEqualTo("abc") - assertThat(log.getSeverity()).isEqualTo(Severity.INFO) - assertThat(log.getSeverityText()).isEqualTo("INFO") - assertThat(log.getAttributes().size()).isEqualTo(4) - assertThat(log.getAttributes().get(AttributeKey.stringKey("logback.mdc.key1"))).isEqualTo("val1") - assertThat(log.getAttributes().get(AttributeKey.stringKey("logback.mdc.key2"))).isEqualTo("val2") - assertThat(log.getAttributes().get(SemanticAttributes.THREAD_NAME)).isEqualTo(Thread.currentThread().getName()) - assertThat(log.getAttributes().get(SemanticAttributes.THREAD_ID)).isEqualTo(Thread.currentThread().getId()) - } -} diff --git a/instrumentation/logback/logback-appender-1.0/javaagent/src/test/java/io/opentelemetry/instrumentation/logback/appender/v1_0/LogbackTest.java b/instrumentation/logback/logback-appender-1.0/javaagent/src/test/java/io/opentelemetry/instrumentation/logback/appender/v1_0/LogbackTest.java new file mode 100644 index 000000000000..d3aefb07949e --- /dev/null +++ b/instrumentation/logback/logback-appender-1.0/javaagent/src/test/java/io/opentelemetry/instrumentation/logback/appender/v1_0/LogbackTest.java @@ -0,0 +1,272 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.logback.appender.v1_0; + +import static io.opentelemetry.sdk.testing.assertj.LogAssertions.assertThat; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; +import static org.awaitility.Awaitility.await; + +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.api.logs.Severity; +import io.opentelemetry.instrumentation.test.AgentInstrumentationSpecification; +import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.sdk.common.InstrumentationScopeInfo; +import io.opentelemetry.sdk.logs.data.LogRecordData; +import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; +import java.util.stream.Stream; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.slf4j.MDC; +import org.slf4j.Marker; +import org.slf4j.MarkerFactory; + +class LogbackTest extends AgentInstrumentationSpecification { + + @RegisterExtension + static final InstrumentationExtension testing = AgentInstrumentationExtension.create(); + + private static final Logger abcLogger = LoggerFactory.getLogger("abc"); + private static final Logger defLogger = LoggerFactory.getLogger("def"); + private static Boolean isFirstLog = true; + + private static Stream provideParameters() { + return Stream.of( + Arguments.of(false, false), + Arguments.of(false, true), + Arguments.of(true, false), + Arguments.of(true, true)); + } + + @ParameterizedTest + @MethodSource("provideParameters") + public void test(boolean logException, boolean withParent) throws InterruptedException { + test(abcLogger, Logger::debug, Logger::debug, logException, withParent, null, null, null); + testing.clearData(); + test( + abcLogger, + Logger::info, + Logger::info, + logException, + withParent, + "abc", + Severity.INFO, + "INFO"); + testing.clearData(); + test( + abcLogger, + Logger::warn, + Logger::warn, + logException, + withParent, + "abc", + Severity.WARN, + "WARN"); + testing.clearData(); + test( + abcLogger, + Logger::error, + Logger::error, + logException, + withParent, + "abc", + Severity.ERROR, + "ERROR"); + testing.clearData(); + test(defLogger, Logger::debug, Logger::debug, logException, withParent, null, null, null); + testing.clearData(); + test(defLogger, Logger::info, Logger::info, logException, withParent, null, null, null); + testing.clearData(); + test( + defLogger, + Logger::warn, + Logger::warn, + logException, + withParent, + "def", + Severity.WARN, + "WARN"); + testing.clearData(); + test( + defLogger, + Logger::error, + Logger::error, + logException, + withParent, + "def", + Severity.ERROR, + "ERROR"); + testing.clearData(); + } + + private static void test( + Logger logger, + OneArgLoggerMethod oneArgLoggerMethod, + TwoArgLoggerMethod twoArgLoggerMethod, + boolean logException, + boolean withParent, + String expectedLoggerName, + Severity expectedSeverity, + String expectedSeverityText) + throws InterruptedException { + + // when + if (withParent) { + testing.runWithSpan( + "parent", + () -> { + performLogging(logger, oneArgLoggerMethod, twoArgLoggerMethod, logException); + }); + } else { + performLogging(logger, oneArgLoggerMethod, twoArgLoggerMethod, logException); + } + + // then + int numOfHeliosAttributes = 0; + if (withParent) { + testing.waitForTraces(1); + } + + if (expectedSeverity != null) { + await().untilAsserted(() -> assertThat(testing.logRecords().size()).isEqualTo(1)); + + LogRecordData log = testing.logRecords().get(0); + assertThat(log) + .hasBody("xyz: 123") + // TODO (trask) why is version "" instead of null? + .hasInstrumentationScope( + InstrumentationScopeInfo.builder(expectedLoggerName).setVersion("").build()) + .hasSeverity(expectedSeverity) + .hasSeverityText(expectedSeverityText); + + if (withParent && isFirstLog) { + numOfHeliosAttributes++; + isFirstLog = false; + } + + if (logException) { + assertThat(log.getAttributes()).hasSize(9 + numOfHeliosAttributes); + assertThat(log) + .hasAttributesSatisfying( + attributes -> + assertThat(attributes) + .containsEntry( + SemanticAttributes.EXCEPTION_TYPE, + IllegalStateException.class.getName()) + .containsEntry(SemanticAttributes.EXCEPTION_MESSAGE, "hello") + .hasEntrySatisfying( + SemanticAttributes.EXCEPTION_STACKTRACE, + value -> assertThat(value).contains(LogbackTest.class.getName()))); + } else { + assertThat(log.getAttributes()).hasSize(6 + numOfHeliosAttributes); + } + + assertThat(log) + .hasAttributesSatisfying( + attributes -> + assertThat(attributes) + .containsEntry( + SemanticAttributes.THREAD_NAME, Thread.currentThread().getName()) + .containsEntry(SemanticAttributes.THREAD_ID, Thread.currentThread().getId()) + .containsEntry(SemanticAttributes.CODE_NAMESPACE, LogbackTest.class.getName()) + .containsEntry(SemanticAttributes.CODE_FUNCTION, "performLogging") + .hasEntrySatisfying( + SemanticAttributes.CODE_LINENO, value -> assertThat(value).isPositive()) + .containsEntry(SemanticAttributes.CODE_FILEPATH, "LogbackTest.java")); + + if (withParent) { + assertThat(log.getSpanContext()).isEqualTo(testing.spans().get(0).getSpanContext()); + } else { + assertThat(log.getSpanContext().isValid()).isFalse(); + } + + } else { + Thread.sleep(500); // sleep a bit just to make sure no log is captured + assertThat(testing.logRecords()).isEmpty(); + } + } + + @Test + void testMdc() { + MDC.put("key1", "val1"); + MDC.put("key2", "val2"); + try { + abcLogger.info("xyz: {}", 123); + } finally { + MDC.clear(); + } + + await().untilAsserted(() -> assertThat(testing.logRecords().size()).isEqualTo(1)); + + LogRecordData log = getLogRecords().get(0); + assertThat(log) + .hasBody("xyz: 123") + // TODO (trask) why is version "" instead of null? + .hasInstrumentationScope(InstrumentationScopeInfo.builder("abc").setVersion("").build()) + .hasSeverity(Severity.INFO) + .hasSeverityText("INFO") + // TODO (trask) convert to hasAttributesSatisfyingExactly once that's available for logs + .hasAttributesSatisfying( + attributes -> + assertThat(attributes) + .hasSize(8) + .containsEntry(AttributeKey.stringKey("logback.mdc.key1"), "val1") + .containsEntry(AttributeKey.stringKey("logback.mdc.key2"), "val2") + .containsEntry(SemanticAttributes.THREAD_NAME, Thread.currentThread().getName()) + .containsEntry(SemanticAttributes.THREAD_ID, Thread.currentThread().getId()) + .containsEntry(SemanticAttributes.CODE_NAMESPACE, LogbackTest.class.getName()) + .containsEntry(SemanticAttributes.CODE_FUNCTION, "testMdc") + .hasEntrySatisfying( + SemanticAttributes.CODE_LINENO, value -> assertThat(value).isPositive()) + .containsEntry(SemanticAttributes.CODE_FILEPATH, "LogbackTest.java")); + } + + @Test + public void testMarker() { + + String markerName = "aMarker"; + Marker marker = MarkerFactory.getMarker(markerName); + + abcLogger.info(marker, "Message"); + + await().untilAsserted(() -> assertThat(testing.logRecords().size()).isEqualTo(1)); + + LogRecordData log = getLogRecords().get(0); + + assertThat(log) + .hasAttributesSatisfying( + attributes -> + assertThat(attributes) + .containsEntry(AttributeKey.stringKey("logback.marker"), markerName)); + } + + private static void performLogging( + Logger logger, + OneArgLoggerMethod oneArgLoggerMethod, + TwoArgLoggerMethod twoArgLoggerMethod, + boolean logException) { + if (logException) { + twoArgLoggerMethod.call(logger, "xyz: {}", 123, new IllegalStateException("hello")); + } else { + oneArgLoggerMethod.call(logger, "xyz: {}", 123); + } + } + + @FunctionalInterface + interface OneArgLoggerMethod { + void call(Logger logger, String msg, Object arg); + } + + @FunctionalInterface + interface TwoArgLoggerMethod { + void call(Logger logger, String msg, Object arg1, Object arg2); + } +} diff --git a/instrumentation/logback/logback-appender-1.0/library/README.md b/instrumentation/logback/logback-appender-1.0/library/README.md index 97cf3362c929..05a010b7b77d 100644 --- a/instrumentation/logback/logback-appender-1.0/library/README.md +++ b/instrumentation/logback/logback-appender-1.0/library/README.md @@ -1,15 +1,17 @@ -# Logback Appender +# Appender Instrumentation for Logback version 1.0 and higher This module provides a Logback [appender](https://logback.qos.ch/manual/appenders.html) which forwards Logback log events to the [OpenTelemetry Log SDK](https://github.com/open-telemetry/opentelemetry-java/tree/main/sdk/logs). -To use it, add the following modules to your application's classpath. +## Quickstart -Replace `OPENTELEMETRY_VERSION` with the latest -stable [release](https://search.maven.org/search?q=g:io.opentelemetry.instrumentation). +### Add these dependencies to your project: -**Maven** +Replace `OPENTELEMETRY_VERSION` with the [latest +release](https://search.maven.org/search?q=g:io.opentelemetry.instrumentation%20AND%20a:opentelemetry-logback-appender-1.0). + +For Maven, add to your `pom.xml` dependencies: ```xml @@ -17,18 +19,19 @@ stable [release](https://search.maven.org/search?q=g:io.opentelemetry.instrument io.opentelemetry.instrumentation opentelemetry-logback-appender-1.0 OPENTELEMETRY_VERSION + runtime ``` -**Gradle** +For Gradle, add to your dependencies: -```kotlin -dependencies { - runtimeOnly("io.opentelemetry.instrumentation:opentelemetry-logback-appender-1.0:OPENTELEMETRY_VERSION") -} +```groovy +runtimeOnly("io.opentelemetry.instrumentation:opentelemetry-logback-appender-1.0:OPENTELEMETRY_VERSION") ``` +### Usage + The following demonstrates how you might configure the appender in your `logback.xml` configuration: ```xml @@ -54,19 +57,17 @@ The following demonstrates how you might configure the appender in your `logback ``` -Next, associate the `OpenTelemetryAppender` configured via `logback.xml` with -an `SdkLogEmitterProvider` in your application: +Next, configure `GlobalLoggerProvider` with an `SdkLoggerProvider` in your application. ``` -SdkLogEmitterProvider logEmitterProvider = - SdkLogEmitterProvider.builder() +SdkLoggerProvider sdkLoggerProvider = + SdkLoggerProvider.builder() .setResource(Resource.create(...)) .addLogProcessor(...) .build(); -OpenTelemetryAppender.setSdkLogEmitterProvider(logEmitterProvider); +GlobalLoggerProvider.set(sdkLoggerProvider); ``` In this example Logback log events will be sent to both the console appender and -the `OpenTelemetryAppender`, which will drop the logs until -`OpenTelemetryAppender.setSdkLogEmitterProvider(..)` is called. Once initialized, logs will be -emitted to a `LogEmitter` obtained from the `SdkLogEmitterProvider`. +the `OpenTelemetryAppender`, which will drop the logs until `GlobalLoggerProvider.set(..)` is +called. Once initialized, logs will be emitted to a `Logger` obtained from the `SdkLoggerProvider`. 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 f5d4a647185d..f8ac0480e4aa 100644 --- a/instrumentation/logback/logback-appender-1.0/library/build.gradle.kts +++ b/instrumentation/logback/logback-appender-1.0/library/build.gradle.kts @@ -3,16 +3,35 @@ plugins { } dependencies { - implementation(project(":instrumentation-appender-api-internal")) - implementation(project(":instrumentation-appender-sdk-internal")) + implementation("io.opentelemetry:opentelemetry-api-logs") - library("ch.qos.logback:logback-classic:0.9.16") + // pin the version strictly to avoid overriding by dependencyManagement versions + compileOnly("ch.qos.logback:logback-classic") { + version { + strictly("1.0.0") + } + } + compileOnly("org.slf4j:slf4j-api") { + version { + strictly("1.6.4") + } + } + + if (findProperty("testLatestDeps") as Boolean) { + testImplementation("ch.qos.logback:logback-classic:+") + } else { + testImplementation("ch.qos.logback:logback-classic") { + version { + strictly("1.0.0") + } + } + testImplementation("org.slf4j:slf4j-api") { + version { + strictly("1.6.4") + } + } + } testImplementation("io.opentelemetry:opentelemetry-sdk-logs") testImplementation("io.opentelemetry:opentelemetry-sdk-testing") } - -tasks.withType().configureEach { - // TODO run tests both with and without experimental log attributes - jvmArgs("-Dotel.instrumentation.logback-appender.experimental.capture-mdc-attributes=*") -} diff --git a/instrumentation/logback/logback-appender-1.0/library/src/main/java/io/opentelemetry/instrumentation/logback/appender/v1_0/OpenTelemetryAppender.java b/instrumentation/logback/logback-appender-1.0/library/src/main/java/io/opentelemetry/instrumentation/logback/appender/v1_0/OpenTelemetryAppender.java index 77c9ba2f885e..12c6d191cda7 100644 --- a/instrumentation/logback/logback-appender-1.0/library/src/main/java/io/opentelemetry/instrumentation/logback/appender/v1_0/OpenTelemetryAppender.java +++ b/instrumentation/logback/logback-appender-1.0/library/src/main/java/io/opentelemetry/instrumentation/logback/appender/v1_0/OpenTelemetryAppender.java @@ -5,42 +5,88 @@ package io.opentelemetry.instrumentation.logback.appender.v1_0; +import static java.util.Collections.emptyList; + import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.core.UnsynchronizedAppenderBase; -import io.opentelemetry.instrumentation.api.appender.internal.LogEmitterProvider; -import io.opentelemetry.instrumentation.api.appender.internal.LogEmitterProviderHolder; +import io.opentelemetry.api.logs.GlobalLoggerProvider; import io.opentelemetry.instrumentation.logback.appender.v1_0.internal.LoggingEventMapper; -import io.opentelemetry.instrumentation.sdk.appender.internal.DelegatingLogEmitterProvider; -import io.opentelemetry.sdk.logs.SdkLogEmitterProvider; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; +import org.slf4j.MDC; public class OpenTelemetryAppender extends UnsynchronizedAppenderBase { - private static final LogEmitterProviderHolder logEmitterProviderHolder = - new LogEmitterProviderHolder(); + private volatile boolean captureExperimentalAttributes = false; + private volatile boolean captureCodeAttributes = false; + private volatile boolean captureMarkerAttribute = false; + private volatile List captureMdcAttributes = emptyList(); + + private volatile LoggingEventMapper mapper; public OpenTelemetryAppender() {} + @Override + public void start() { + mapper = + new LoggingEventMapper( + captureExperimentalAttributes, + captureMdcAttributes, + captureCodeAttributes, + captureMarkerAttribute); + super.start(); + } + @Override protected void append(ILoggingEvent event) { - LoggingEventMapper.INSTANCE.emit(logEmitterProviderHolder.get(), event); + mapper.emit(GlobalLoggerProvider.get(), event); } /** - * This should be called once as early as possible in your application initialization logic, often - * in a {@code static} block in your main class. It should only be called once - an attempt to - * call it a second time will result in an error. If trying to set the {@link - * SdkLogEmitterProvider} multiple times in tests, use {@link - * OpenTelemetryAppender#resetSdkLogEmitterProviderForTest()} between them. + * Sets whether experimental attributes should be set to logs. These attributes may be changed or + * removed in the future, so only enable this if you know you do not require attributes filled by + * this instrumentation to be stable across versions. */ - public static void setSdkLogEmitterProvider(SdkLogEmitterProvider sdkLogEmitterProvider) { - logEmitterProviderHolder.set(DelegatingLogEmitterProvider.from(sdkLogEmitterProvider)); + public void setCaptureExperimentalAttributes(boolean captureExperimentalAttributes) { + this.captureExperimentalAttributes = captureExperimentalAttributes; } /** - * Unsets the global {@link LogEmitterProvider}. This is only meant to be used from tests which - * need to reconfigure {@link LogEmitterProvider}. + * Sets whether the code attributes (file name, class name, method name and line number) should be + * set to logs. Enabling these attributes can potentially impact performance (see + * https://logback.qos.ch/manual/layouts.html). + * + * @param captureCodeAttributes To enable or disable the code attributes (file name, class name, + * method name and line number) */ - public static void resetSdkLogEmitterProviderForTest() { - logEmitterProviderHolder.resetForTest(); + public void setCaptureCodeAttributes(boolean captureCodeAttributes) { + this.captureCodeAttributes = captureCodeAttributes; + } + + /** + * Sets whether the marker attribute should be set to logs. + * + * @param captureMarkerAttribute To enable or disable the marker attribute + */ + public void setCaptureMarkerAttribute(boolean captureMarkerAttribute) { + this.captureMarkerAttribute = captureMarkerAttribute; + } + + /** Configures the {@link MDC} attributes that will be copied to logs. */ + public void setCaptureMdcAttributes(String attributes) { + if (attributes != null) { + captureMdcAttributes = filterBlanksAndNulls(attributes.split(",")); + } else { + captureMdcAttributes = emptyList(); + } + } + + // copied from SDK's DefaultConfigProperties + private static List filterBlanksAndNulls(String[] values) { + return Arrays.stream(values) + .map(String::trim) + .filter(s -> !s.isEmpty()) + .collect(Collectors.toList()); } } 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 730a1e59853d..1a050e460386 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 @@ -5,7 +5,7 @@ package io.opentelemetry.instrumentation.logback.appender.v1_0.internal; -import static java.util.Collections.emptyList; +import static io.opentelemetry.instrumentation.api.log.LoggingContextConstants.HELIOS_INSTRUMENTED_INDICATION; import ch.qos.logback.classic.Level; import ch.qos.logback.classic.spi.ILoggingEvent; @@ -13,11 +13,12 @@ import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.common.AttributesBuilder; +import io.opentelemetry.api.logs.LogRecordBuilder; +import io.opentelemetry.api.logs.LoggerProvider; +import io.opentelemetry.api.logs.Severity; +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.api.trace.SpanContext; import io.opentelemetry.context.Context; -import io.opentelemetry.instrumentation.api.appender.internal.LogBuilder; -import io.opentelemetry.instrumentation.api.appender.internal.LogEmitterProvider; -import io.opentelemetry.instrumentation.api.appender.internal.Severity; -import io.opentelemetry.instrumentation.api.config.Config; import io.opentelemetry.instrumentation.api.internal.cache.Cache; import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; import java.io.PrintWriter; @@ -25,6 +26,7 @@ import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; +import org.slf4j.Marker; /** * This class is internal and is hence not for public use. Its APIs are unstable and can change at @@ -32,47 +34,58 @@ */ public final class LoggingEventMapper { - public static final LoggingEventMapper INSTANCE = new LoggingEventMapper(); + private static boolean heliosInstrumentedIndicator = false; - private static final boolean captureExperimentalAttributes = - Config.get() - .getBoolean("otel.instrumentation.logback-appender.experimental-log-attributes", false); + private static void markInstrumentationIndicator(AttributesBuilder attributes) { + Context parentContext = Context.current(); + Span span = Span.fromContext(parentContext); + SpanContext parentSpanContext = span.getSpanContext(); - private static final Cache> mdcAttributeKeys = Cache.bounded(100); + if (!span.isRecording() || !parentSpanContext.isValid() || heliosInstrumentedIndicator) { + return; + } - private final List captureMdcAttributes; + attributes.put(HELIOS_INSTRUMENTED_INDICATION, "logback"); + heliosInstrumentedIndicator = true; + } - // cached as an optimization - private final boolean captureAllMdcAttributes; + private static final Cache> mdcAttributeKeys = Cache.bounded(100); - private LoggingEventMapper() { - this( - Config.get() - .getList( - "otel.instrumentation.logback-appender.experimental.capture-mdc-attributes", - emptyList())); - } + private static final AttributeKey LOG_MARKER = AttributeKey.stringKey("logback.marker"); - // visible for testing - LoggingEventMapper(List captureMdcAttributes) { + private final boolean captureExperimentalAttributes; + private final List captureMdcAttributes; + private final boolean captureAllMdcAttributes; + private final boolean captureCodeAttributes; + private final boolean captureMarkerAttribute; + + public LoggingEventMapper( + boolean captureExperimentalAttributes, + List captureMdcAttributes, + boolean captureCodeAttributes, + boolean captureMarkerAttribute) { + this.captureExperimentalAttributes = captureExperimentalAttributes; + this.captureCodeAttributes = captureCodeAttributes; this.captureMdcAttributes = captureMdcAttributes; + this.captureMarkerAttribute = captureMarkerAttribute; this.captureAllMdcAttributes = captureMdcAttributes.size() == 1 && captureMdcAttributes.get(0).equals("*"); } - public void emit(LogEmitterProvider logEmitterProvider, ILoggingEvent event) { + public void emit(LoggerProvider loggerProvider, ILoggingEvent event) { String instrumentationName = event.getLoggerName(); if (instrumentationName == null || instrumentationName.isEmpty()) { instrumentationName = "ROOT"; } - LogBuilder builder = - logEmitterProvider.logEmitterBuilder(instrumentationName).build().logBuilder(); + LogRecordBuilder builder = + loggerProvider.loggerBuilder(instrumentationName).build().logRecordBuilder(); mapLoggingEvent(builder, event); builder.emit(); } /** - * Map the {@link ILoggingEvent} data model onto the {@link LogBuilder}. Unmapped fields include: + * Map the {@link ILoggingEvent} data model onto the {@link LogRecordBuilder}. Unmapped fields + * include: * *
    *
  • Thread name - {@link ILoggingEvent#getThreadName()} @@ -80,7 +93,7 @@ public void emit(LogEmitterProvider logEmitterProvider, ILoggingEvent event) { *
  • Mapped diagnostic context - {@link ILoggingEvent#getMDCPropertyMap()} *
*/ - private void mapLoggingEvent(LogBuilder builder, ILoggingEvent loggingEvent) { + private void mapLoggingEvent(LogRecordBuilder builder, ILoggingEvent loggingEvent) { // message String message = loggingEvent.getFormattedMessage(); if (message != null) { @@ -100,6 +113,8 @@ private void mapLoggingEvent(LogBuilder builder, ILoggingEvent loggingEvent) { AttributesBuilder attributes = Attributes.builder(); + markInstrumentationIndicator(attributes); + // throwable Object throwableProxy = loggingEvent.getThrowableProxy(); Throwable throwable = null; @@ -120,7 +135,32 @@ private void mapLoggingEvent(LogBuilder builder, ILoggingEvent loggingEvent) { attributes.put(SemanticAttributes.THREAD_ID, currentThread.getId()); } - builder.setAttributes(attributes.build()); + if (captureCodeAttributes) { + StackTraceElement[] callerData = loggingEvent.getCallerData(); + if (callerData != null && callerData.length > 0) { + StackTraceElement firstStackElement = callerData[0]; + String fileName = firstStackElement.getFileName(); + if (fileName != null) { + attributes.put(SemanticAttributes.CODE_FILEPATH, fileName); + } + attributes.put(SemanticAttributes.CODE_NAMESPACE, firstStackElement.getClassName()); + attributes.put(SemanticAttributes.CODE_FUNCTION, firstStackElement.getMethodName()); + int lineNumber = firstStackElement.getLineNumber(); + if (lineNumber > 0) { + attributes.put(SemanticAttributes.CODE_LINENO, lineNumber); + } + } + } + + if (captureMarkerAttribute) { + Marker marker = loggingEvent.getMarker(); + if (marker != null) { + String markerName = marker.getName(); + attributes.put(LOG_MARKER, markerName); + } + } + + builder.setAllAttributes(attributes.build()); // span context builder.setContext(Context.current()); @@ -150,7 +190,7 @@ public static AttributeKey getMdcAttributeKey(String key) { private static void setThrowable(AttributesBuilder attributes, Throwable throwable) { // TODO (trask) extract method for recording exception into - // instrumentation-appender-api-internal + // io.opentelemetry:opentelemetry-api-logs attributes.put(SemanticAttributes.EXCEPTION_TYPE, throwable.getClass().getName()); attributes.put(SemanticAttributes.EXCEPTION_MESSAGE, throwable.getMessage()); StringWriter writer = new StringWriter(); diff --git a/instrumentation/logback/logback-appender-1.0/library/src/test/java/io/opentelemetry/instrumentation/logback/appender/v1_0/OpenTelemetryAppenderConfigTest.java b/instrumentation/logback/logback-appender-1.0/library/src/test/java/io/opentelemetry/instrumentation/logback/appender/v1_0/OpenTelemetryAppenderConfigTest.java index c8be519d719a..73fcfeee7a47 100644 --- a/instrumentation/logback/logback-appender-1.0/library/src/test/java/io/opentelemetry/instrumentation/logback/appender/v1_0/OpenTelemetryAppenderConfigTest.java +++ b/instrumentation/logback/logback-appender-1.0/library/src/test/java/io/opentelemetry/instrumentation/logback/appender/v1_0/OpenTelemetryAppenderConfigTest.java @@ -8,16 +8,16 @@ import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; import io.opentelemetry.api.common.AttributeKey; -import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.logs.GlobalLoggerProvider; +import io.opentelemetry.api.logs.Severity; import io.opentelemetry.api.trace.Span; import io.opentelemetry.api.trace.SpanContext; import io.opentelemetry.context.Scope; import io.opentelemetry.sdk.common.InstrumentationScopeInfo; -import io.opentelemetry.sdk.logs.SdkLogEmitterProvider; -import io.opentelemetry.sdk.logs.data.LogData; -import io.opentelemetry.sdk.logs.data.Severity; -import io.opentelemetry.sdk.logs.export.InMemoryLogExporter; -import io.opentelemetry.sdk.logs.export.SimpleLogProcessor; +import io.opentelemetry.sdk.logs.SdkLoggerProvider; +import io.opentelemetry.sdk.logs.data.LogRecordData; +import io.opentelemetry.sdk.logs.export.InMemoryLogRecordExporter; +import io.opentelemetry.sdk.logs.export.SimpleLogRecordProcessor; import io.opentelemetry.sdk.resources.Resource; import io.opentelemetry.sdk.trace.SdkTracerProvider; import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; @@ -31,47 +31,49 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.slf4j.MDC; +import org.slf4j.Marker; +import org.slf4j.MarkerFactory; class OpenTelemetryAppenderConfigTest { private static final Logger logger = LoggerFactory.getLogger("TestLogger"); - private static InMemoryLogExporter logExporter; + private static InMemoryLogRecordExporter logRecordExporter; private static Resource resource; private static InstrumentationScopeInfo instrumentationScopeInfo; @BeforeAll static void setupAll() { - logExporter = InMemoryLogExporter.create(); + logRecordExporter = InMemoryLogRecordExporter.create(); resource = Resource.getDefault(); instrumentationScopeInfo = InstrumentationScopeInfo.create("TestLogger"); - SdkLogEmitterProvider logEmitterProvider = - SdkLogEmitterProvider.builder() + SdkLoggerProvider loggerProvider = + SdkLoggerProvider.builder() .setResource(resource) - .addLogProcessor(SimpleLogProcessor.create(logExporter)) + .addLogRecordProcessor(SimpleLogRecordProcessor.create(logRecordExporter)) .build(); - OpenTelemetryAppender.resetSdkLogEmitterProviderForTest(); - OpenTelemetryAppender.setSdkLogEmitterProvider(logEmitterProvider); + GlobalLoggerProvider.resetForTest(); + GlobalLoggerProvider.set(loggerProvider); } @BeforeEach void setup() { - logExporter.reset(); + logRecordExporter.reset(); } @Test void logNoSpan() { logger.info("log message 1"); - List logDataList = logExporter.getFinishedLogItems(); + List logDataList = logRecordExporter.getFinishedLogItems(); assertThat(logDataList).hasSize(1); - LogData logData = logDataList.get(0); + LogRecordData logData = logDataList.get(0); assertThat(logData.getResource()).isEqualTo(resource); assertThat(logData.getInstrumentationScopeInfo()).isEqualTo(instrumentationScopeInfo); assertThat(logData.getBody().asString()).isEqualTo("log message 1"); - assertThat(logData.getAttributes()).isEqualTo(Attributes.empty()); + assertThat(logData.getAttributes().size()).isEqualTo(4); // 4 code attributes } @Test @@ -82,7 +84,7 @@ void logWithSpan() { Span span2 = runWithSpan("span2", () -> logger.info("log message 3")); - List logDataList = logExporter.getFinishedLogItems(); + List logDataList = logRecordExporter.getFinishedLogItems(); assertThat(logDataList).hasSize(3); assertThat(logDataList.get(0).getSpanContext()).isEqualTo(span1.getSpanContext()); assertThat(logDataList.get(1).getSpanContext()).isEqualTo(SpanContext.getInvalid()); @@ -102,11 +104,13 @@ private static Span runWithSpan(String spanName, Runnable runnable) { @Test void logWithExtras() { Instant start = Instant.now(); - logger.info("log message 1", new IllegalStateException("Error!")); + String markerName = "aMarker"; + Marker marker = MarkerFactory.getMarker(markerName); + logger.info(marker, "log message 1", new IllegalStateException("Error!")); - List logDataList = logExporter.getFinishedLogItems(); + List logDataList = logRecordExporter.getFinishedLogItems(); assertThat(logDataList).hasSize(1); - LogData logData = logDataList.get(0); + LogRecordData logData = logDataList.get(0); assertThat(logData.getResource()).isEqualTo(resource); assertThat(logData.getInstrumentationScopeInfo()).isEqualTo(instrumentationScopeInfo); assertThat(logData.getBody().asString()).isEqualTo("log message 1"); @@ -115,13 +119,31 @@ void logWithExtras() { .isLessThan(TimeUnit.MILLISECONDS.toNanos(Instant.now().toEpochMilli())); assertThat(logData.getSeverity()).isEqualTo(Severity.INFO); assertThat(logData.getSeverityText()).isEqualTo("INFO"); - assertThat(logData.getAttributes().size()).isEqualTo(3); + assertThat(logData.getAttributes().size()) + .isEqualTo(3 + 4 + 1); // 3 exception attributes, 4 code attributes, 1 marker attribute assertThat(logData.getAttributes().get(SemanticAttributes.EXCEPTION_TYPE)) .isEqualTo(IllegalStateException.class.getName()); assertThat(logData.getAttributes().get(SemanticAttributes.EXCEPTION_MESSAGE)) .isEqualTo("Error!"); assertThat(logData.getAttributes().get(SemanticAttributes.EXCEPTION_STACKTRACE)) .contains("logWithExtras"); + + String file = logData.getAttributes().get(SemanticAttributes.CODE_FILEPATH); + assertThat(file).isEqualTo("OpenTelemetryAppenderConfigTest.java"); + + String codeClass = logData.getAttributes().get(SemanticAttributes.CODE_NAMESPACE); + assertThat(codeClass) + .isEqualTo( + "io.opentelemetry.instrumentation.logback.appender.v1_0.OpenTelemetryAppenderConfigTest"); + + String method = logData.getAttributes().get(SemanticAttributes.CODE_FUNCTION); + assertThat(method).isEqualTo("logWithExtras"); + + Long lineNumber = logData.getAttributes().get(SemanticAttributes.CODE_LINENO); + assertThat(lineNumber).isGreaterThan(1); + + String logMarker = logData.getAttributes().get(AttributeKey.stringKey("logback.marker")); + assertThat(logMarker).isEqualTo(markerName); } @Test @@ -134,13 +156,13 @@ void logContextData() { MDC.clear(); } - List logDataList = logExporter.getFinishedLogItems(); + List logDataList = logRecordExporter.getFinishedLogItems(); assertThat(logDataList).hasSize(1); - LogData logData = logDataList.get(0); + LogRecordData logData = logDataList.get(0); assertThat(logData.getResource()).isEqualTo(resource); assertThat(logData.getInstrumentationScopeInfo()).isEqualTo(instrumentationScopeInfo); assertThat(logData.getBody().asString()).isEqualTo("log message 1"); - assertThat(logData.getAttributes().size()).isEqualTo(2); + assertThat(logData.getAttributes().size()).isEqualTo(2 + 4); // 4 code attributes AssertionsForClassTypes.assertThat( logData.getAttributes().get(AttributeKey.stringKey("logback.mdc.key1"))) .isEqualTo("val1"); diff --git a/instrumentation/logback/logback-appender-1.0/library/src/test/java/io/opentelemetry/instrumentation/logback/appender/v1_0/internal/LoggingEventMapperTest.java b/instrumentation/logback/logback-appender-1.0/library/src/test/java/io/opentelemetry/instrumentation/logback/appender/v1_0/internal/LoggingEventMapperTest.java index 364e58d29498..5c0567f6b6d6 100644 --- a/instrumentation/logback/logback-appender-1.0/library/src/test/java/io/opentelemetry/instrumentation/logback/appender/v1_0/internal/LoggingEventMapperTest.java +++ b/instrumentation/logback/logback-appender-1.0/library/src/test/java/io/opentelemetry/instrumentation/logback/appender/v1_0/internal/LoggingEventMapperTest.java @@ -22,7 +22,7 @@ class LoggingEventMapperTest { @Test void testDefault() { // given - LoggingEventMapper mapper = new LoggingEventMapper(emptyList()); + LoggingEventMapper mapper = new LoggingEventMapper(false, emptyList(), false, false); Map contextData = new HashMap<>(); contextData.put("key1", "value1"); contextData.put("key2", "value2"); @@ -38,7 +38,7 @@ void testDefault() { @Test void testSome() { // given - LoggingEventMapper mapper = new LoggingEventMapper(singletonList("key2")); + LoggingEventMapper mapper = new LoggingEventMapper(false, singletonList("key2"), false, false); Map contextData = new HashMap<>(); contextData.put("key1", "value1"); contextData.put("key2", "value2"); @@ -55,7 +55,7 @@ void testSome() { @Test void testAll() { // given - LoggingEventMapper mapper = new LoggingEventMapper(singletonList("*")); + LoggingEventMapper mapper = new LoggingEventMapper(false, singletonList("*"), false, false); Map contextData = new HashMap<>(); contextData.put("key1", "value1"); contextData.put("key2", "value2"); diff --git a/instrumentation/logback/logback-appender-1.0/library/src/test/resources/logback-test.xml b/instrumentation/logback/logback-appender-1.0/library/src/test/resources/logback-test.xml index 1255f7cfe3ec..a3a4273a1222 100644 --- a/instrumentation/logback/logback-appender-1.0/library/src/test/resources/logback-test.xml +++ b/instrumentation/logback/logback-appender-1.0/library/src/test/resources/logback-test.xml @@ -8,12 +8,17 @@ - + + false + true + true + * - + diff --git a/instrumentation/logback/logback-mdc-1.0/javaagent/build.gradle.kts b/instrumentation/logback/logback-mdc-1.0/javaagent/build.gradle.kts index 926f2a6c1b84..5c315ed3394f 100644 --- a/instrumentation/logback/logback-mdc-1.0/javaagent/build.gradle.kts +++ b/instrumentation/logback/logback-mdc-1.0/javaagent/build.gradle.kts @@ -13,7 +13,32 @@ muzzle { dependencies { implementation(project(":instrumentation:logback:logback-mdc-1.0:library")) - library("ch.qos.logback:logback-classic:1.0.0") + // pin the version strictly to avoid overriding by dependencyManagement versions + compileOnly("ch.qos.logback:logback-classic") { + version { + strictly("1.0.0") + } + } + compileOnly("org.slf4j:slf4j-api") { + version { + strictly("1.6.4") + } + } + + if (findProperty("testLatestDeps") as Boolean) { + testImplementation("ch.qos.logback:logback-classic:+") + } else { + testImplementation("ch.qos.logback:logback-classic") { + version { + strictly("1.0.0") + } + } + testImplementation("org.slf4j:slf4j-api") { + version { + strictly("1.6.4") + } + } + } testImplementation(project(":instrumentation:logback:logback-mdc-1.0:testing")) } diff --git a/instrumentation/logback/logback-mdc-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/logback/mdc/v1_0/LoggingEventInstrumentation.java b/instrumentation/logback/logback-mdc-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/logback/mdc/v1_0/LoggingEventInstrumentation.java index 7d1ea2c8bf2b..2fbd7442f952 100644 --- a/instrumentation/logback/logback-mdc-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/logback/mdc/v1_0/LoggingEventInstrumentation.java +++ b/instrumentation/logback/logback-mdc-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/logback/mdc/v1_0/LoggingEventInstrumentation.java @@ -5,6 +5,7 @@ package io.opentelemetry.javaagent.instrumentation.logback.mdc.v1_0; +import static io.opentelemetry.instrumentation.api.log.LoggingContextConstants.HELIOS_INSTRUMENTED_INDICATION; import static io.opentelemetry.instrumentation.api.log.LoggingContextConstants.SPAN_ID; import static io.opentelemetry.instrumentation.api.log.LoggingContextConstants.TRACE_FLAGS; import static io.opentelemetry.instrumentation.api.log.LoggingContextConstants.TRACE_ID; @@ -17,6 +18,7 @@ import static net.bytebuddy.matcher.ElementMatchers.takesArguments; import ch.qos.logback.classic.spi.ILoggingEvent; +import io.opentelemetry.api.trace.Span; import io.opentelemetry.api.trace.SpanContext; import io.opentelemetry.context.Context; import io.opentelemetry.instrumentation.api.util.VirtualField; @@ -32,6 +34,7 @@ import net.bytebuddy.matcher.ElementMatcher; public class LoggingEventInstrumentation implements TypeInstrumentation { + @Override public ElementMatcher classLoaderOptimization() { return hasClassesNamed("ch.qos.logback.classic.spi.ILoggingEvent"); @@ -55,6 +58,17 @@ public void transform(TypeTransformer transformer) { @SuppressWarnings("unused") public static class GetMdcAdvice { + public static boolean heliosInstrumentedIndicator = false; + + public static void markInstrumentationIndicator(Span span, SpanContext spanContext) { + if (!span.isRecording() || !spanContext.isValid() || heliosInstrumentedIndicator) { + return; + } + + span.setAttribute(HELIOS_INSTRUMENTED_INDICATION, "logback"); + heliosInstrumentedIndicator = true; + } + @Advice.OnMethodExit(suppress = Throwable.class) public static void onExit( @Advice.This ILoggingEvent event, @@ -69,11 +83,14 @@ public static void onExit( return; } - SpanContext spanContext = Java8BytecodeBridge.spanFromContext(context).getSpanContext(); + Span span = Java8BytecodeBridge.spanFromContext(context); + SpanContext spanContext = span.getSpanContext(); if (!spanContext.isValid()) { return; } + markInstrumentationIndicator(span, spanContext); + Map spanContextData = new HashMap<>(); spanContextData.put(TRACE_ID, spanContext.getTraceId()); spanContextData.put(SPAN_ID, spanContext.getSpanId()); diff --git a/instrumentation/logback/logback-mdc-1.0/library/README.md b/instrumentation/logback/logback-mdc-1.0/library/README.md index 9288780a093f..d395d455e646 100644 --- a/instrumentation/logback/logback-mdc-1.0/library/README.md +++ b/instrumentation/logback/logback-mdc-1.0/library/README.md @@ -1,18 +1,18 @@ -# Logback Integration +# MDC Instrumentation for Logback version 1.0 and higher This module integrates instrumentation with Logback by injecting the trace ID and span ID from a mounted span using a custom Logback appender. -To use it, add the module to your application's runtime classpath and add the appender to your -`logback.xml`. +## Quickstart -Replace `OPENTELEMETRY_VERSION` with the latest -stable [release](https://search.maven.org/search?q=g:io.opentelemetry.instrumentation). +### Add these dependencies to your project: -**Maven** +Replace `OPENTELEMETRY_VERSION` with the [latest +release](https://search.maven.org/search?q=g:io.opentelemetry.instrumentation%20AND%20a:opentelemetry-logback-mdc-1.0). -```xml +For Maven, add to your `pom.xml` dependencies: +```xml io.opentelemetry.instrumentation @@ -22,7 +22,7 @@ stable [release](https://search.maven.org/search?q=g:io.opentelemetry.instrument ``` -**Gradle** +For Gradle, add to your dependencies: ```kotlin dependencies { @@ -30,6 +30,8 @@ dependencies { } ``` +### Usage + **logback.xml** ```xml diff --git a/instrumentation/logback/logback-mdc-1.0/library/build.gradle.kts b/instrumentation/logback/logback-mdc-1.0/library/build.gradle.kts index 71d2940829d5..6bf9d8c2bf2e 100644 --- a/instrumentation/logback/logback-mdc-1.0/library/build.gradle.kts +++ b/instrumentation/logback/logback-mdc-1.0/library/build.gradle.kts @@ -3,7 +3,32 @@ plugins { } dependencies { - library("ch.qos.logback:logback-classic:1.0.0") + // pin the version strictly to avoid overriding by dependencyManagement versions + compileOnly("ch.qos.logback:logback-classic") { + version { + strictly("1.0.0") + } + } + compileOnly("org.slf4j:slf4j-api") { + version { + strictly("1.6.4") + } + } + + if (findProperty("testLatestDeps") as Boolean) { + testImplementation("ch.qos.logback:logback-classic:+") + } else { + testImplementation("ch.qos.logback:logback-classic") { + version { + strictly("1.0.0") + } + } + testImplementation("org.slf4j:slf4j-api") { + version { + strictly("1.6.4") + } + } + } testImplementation(project(":instrumentation:logback:logback-mdc-1.0:testing")) } diff --git a/instrumentation/logback/logback-mdc-1.0/library/src/main/java/io/opentelemetry/instrumentation/logback/v1_0/OpenTelemetryAppender.java b/instrumentation/logback/logback-mdc-1.0/library/src/main/java/io/opentelemetry/instrumentation/logback/v1_0/OpenTelemetryAppender.java index a6a59f1d634e..bebd57abb696 100644 --- a/instrumentation/logback/logback-mdc-1.0/library/src/main/java/io/opentelemetry/instrumentation/logback/v1_0/OpenTelemetryAppender.java +++ b/instrumentation/logback/logback-mdc-1.0/library/src/main/java/io/opentelemetry/instrumentation/logback/v1_0/OpenTelemetryAppender.java @@ -5,6 +5,7 @@ package io.opentelemetry.instrumentation.logback.v1_0; +import static io.opentelemetry.instrumentation.api.log.LoggingContextConstants.HELIOS_INSTRUMENTED_INDICATION; import static io.opentelemetry.instrumentation.api.log.LoggingContextConstants.SPAN_ID; import static io.opentelemetry.instrumentation.api.log.LoggingContextConstants.TRACE_FLAGS; import static io.opentelemetry.instrumentation.api.log.LoggingContextConstants.TRACE_ID; @@ -26,9 +27,24 @@ public class OpenTelemetryAppender extends UnsynchronizedAppenderBase aai = new AppenderAttachableImpl<>(); + private static boolean heliosInstrumentedIndicator = false; + + private static void markInstrumentationIndicator() { + Span span = Span.current(); + SpanContext parentSpanContext = span.getSpanContext(); + + if (!span.isRecording() || !parentSpanContext.isValid() || heliosInstrumentedIndicator) { + return; + } + + span.setAttribute(HELIOS_INSTRUMENTED_INDICATION, "logback"); + heliosInstrumentedIndicator = true; + } + public static ILoggingEvent wrapEvent(ILoggingEvent event) { Span currentSpan = Span.current(); - if (!currentSpan.getSpanContext().isValid()) { + SpanContext spanContext = currentSpan.getSpanContext(); + if (!spanContext.isValid()) { return event; } @@ -38,8 +54,9 @@ public static ILoggingEvent wrapEvent(ILoggingEvent event) { return event; } + markInstrumentationIndicator(); + Map contextData = new HashMap<>(); - SpanContext spanContext = currentSpan.getSpanContext(); contextData.put(TRACE_ID, spanContext.getTraceId()); contextData.put(SPAN_ID, spanContext.getSpanId()); contextData.put(TRACE_FLAGS, spanContext.getTraceFlags().asHex()); diff --git a/instrumentation/logback/logback-mdc-1.0/testing/src/main/groovy/io/opentelemetry/instrumentation/logback/v1_0/AbstractLogbackTest.groovy b/instrumentation/logback/logback-mdc-1.0/testing/src/main/groovy/io/opentelemetry/instrumentation/logback/v1_0/AbstractLogbackTest.groovy index bf10d4d6b2db..3eb371c2d032 100644 --- a/instrumentation/logback/logback-mdc-1.0/testing/src/main/groovy/io/opentelemetry/instrumentation/logback/v1_0/AbstractLogbackTest.groovy +++ b/instrumentation/logback/logback-mdc-1.0/testing/src/main/groovy/io/opentelemetry/instrumentation/logback/v1_0/AbstractLogbackTest.groovy @@ -5,6 +5,8 @@ package io.opentelemetry.instrumentation.logback.v1_0 +import static io.opentelemetry.instrumentation.api.log.LoggingContextConstants.HELIOS_INSTRUMENTED_INDICATION + import ch.qos.logback.classic.spi.ILoggingEvent import ch.qos.logback.core.read.ListAppender import io.opentelemetry.api.trace.Span @@ -89,5 +91,20 @@ abstract class AbstractLogbackTest extends InstrumentationSpecification { events[2].getMDCPropertyMap().get("trace_id") == span2.spanContext.traceId events[2].getMDCPropertyMap().get("span_id") == span2.spanContext.spanId events[2].getMDCPropertyMap().get("trace_flags") == "01" + + assertTraces(2) { + trace(0, 1) { + span(0) { + attributes { + "$HELIOS_INSTRUMENTED_INDICATION" "logback" + } + } + } + trace(1, 1) { + span(0) { + attributes {} + } + } + } } } diff --git a/instrumentation/methods/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/methods/MethodInstrumentation.java b/instrumentation/methods/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/methods/MethodInstrumentation.java index 73244701a97f..95880d990d81 100644 --- a/instrumentation/methods/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/methods/MethodInstrumentation.java +++ b/instrumentation/methods/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/methods/MethodInstrumentation.java @@ -15,7 +15,7 @@ import io.opentelemetry.context.Context; import io.opentelemetry.context.Scope; import io.opentelemetry.instrumentation.api.annotation.support.async.AsyncOperationEndSupport; -import io.opentelemetry.instrumentation.api.util.ClassAndMethod; +import io.opentelemetry.instrumentation.api.instrumenter.util.ClassAndMethod; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; import java.lang.reflect.Method; diff --git a/instrumentation/methods/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/methods/MethodSingletons.java b/instrumentation/methods/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/methods/MethodSingletons.java index 0ea1661eafc2..a50893bbb656 100644 --- a/instrumentation/methods/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/methods/MethodSingletons.java +++ b/instrumentation/methods/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/methods/MethodSingletons.java @@ -8,9 +8,10 @@ import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; import io.opentelemetry.instrumentation.api.instrumenter.SpanKindExtractor; -import io.opentelemetry.instrumentation.api.instrumenter.SpanNameExtractor; -import io.opentelemetry.instrumentation.api.util.ClassAndMethod; -import io.opentelemetry.instrumentation.api.util.SpanNames; +import io.opentelemetry.instrumentation.api.instrumenter.code.CodeAttributesExtractor; +import io.opentelemetry.instrumentation.api.instrumenter.code.CodeAttributesGetter; +import io.opentelemetry.instrumentation.api.instrumenter.code.CodeSpanNameExtractor; +import io.opentelemetry.instrumentation.api.instrumenter.util.ClassAndMethod; public final class MethodSingletons { private static final String INSTRUMENTATION_NAME = "io.opentelemetry.methods"; @@ -18,12 +19,16 @@ public final class MethodSingletons { private static final Instrumenter INSTRUMENTER; static { - SpanNameExtractor spanName = SpanNames::fromMethod; + CodeAttributesGetter codeAttributesGetter = + ClassAndMethod.codeAttributesGetter(); INSTRUMENTER = Instrumenter.builder( - GlobalOpenTelemetry.get(), INSTRUMENTATION_NAME, spanName) - .newInstrumenter(SpanKindExtractor.alwaysInternal()); + GlobalOpenTelemetry.get(), + INSTRUMENTATION_NAME, + CodeSpanNameExtractor.create(codeAttributesGetter)) + .addAttributesExtractor(CodeAttributesExtractor.create(codeAttributesGetter)) + .buildInstrumenter(SpanKindExtractor.alwaysInternal()); } public static Instrumenter instrumenter() { diff --git a/instrumentation/methods/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/methods/MethodTest.java b/instrumentation/methods/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/methods/MethodTest.java index 29d1c35cf1d9..41c75e870c75 100644 --- a/instrumentation/methods/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/methods/MethodTest.java +++ b/instrumentation/methods/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/methods/MethodTest.java @@ -6,8 +6,10 @@ package io.opentelemetry.javaagent.instrumentation.methods; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; +import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.CODE_FUNCTION; +import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.CODE_NAMESPACE; -import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.trace.Span; import io.opentelemetry.api.trace.SpanKind; import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension; @@ -33,7 +35,9 @@ void methodTraced() { span -> span.hasName("ConfigTracedCallable.call") .hasKind(SpanKind.INTERNAL) - .hasAttributes(Attributes.empty()))); + .hasAttributesSatisfyingExactly( + equalTo(CODE_NAMESPACE, ConfigTracedCallable.class.getName()), + equalTo(CODE_FUNCTION, "call")))); } static class ConfigTracedCallable implements Callable { @@ -62,7 +66,9 @@ void methodTracedWithAsyncStop() throws Exception { span -> span.hasName("ConfigTracedCompletableFuture.getResult") .hasKind(SpanKind.INTERNAL) - .hasAttributes(Attributes.empty()))); + .hasAttributesSatisfyingExactly( + equalTo(CODE_NAMESPACE, ConfigTracedCompletableFuture.class.getName()), + equalTo(CODE_FUNCTION, "getResult")))); } static class ConfigTracedCompletableFuture { diff --git a/instrumentation/micrometer/micrometer-1.5/javaagent/build.gradle.kts b/instrumentation/micrometer/micrometer-1.5/javaagent/build.gradle.kts index e979065b857f..cc2ba4246dfe 100644 --- a/instrumentation/micrometer/micrometer-1.5/javaagent/build.gradle.kts +++ b/instrumentation/micrometer/micrometer-1.5/javaagent/build.gradle.kts @@ -14,10 +14,9 @@ muzzle { dependencies { library("io.micrometer:micrometer-core:1.5.0") - implementation("io.opentelemetry:opentelemetry-micrometer1-shim") { - // just get the instrumentation, without micrometer itself - exclude("io.micrometer", "micrometer-core") - } + implementation(project(":instrumentation:micrometer:micrometer-1.5:library")) + + testImplementation(project(":instrumentation:micrometer:micrometer-1.5:testing")) } tasks { diff --git a/instrumentation/micrometer/micrometer-1.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/micrometer/v1_5/MicrometerInstrumentationModule.java b/instrumentation/micrometer/micrometer-1.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/micrometer/v1_5/MicrometerInstrumentationModule.java index 8de7580d983a..22e6e0096711 100644 --- a/instrumentation/micrometer/micrometer-1.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/micrometer/v1_5/MicrometerInstrumentationModule.java +++ b/instrumentation/micrometer/micrometer-1.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/micrometer/v1_5/MicrometerInstrumentationModule.java @@ -27,11 +27,6 @@ public ElementMatcher.Junction classLoaderMatcher() { return hasClassesNamed("io.micrometer.core.instrument.config.validate.Validated"); } - @Override - public boolean isHelperClass(String className) { - return className.startsWith("io.opentelemetry.micrometer1shim."); - } - @Override public List typeInstrumentations() { return Collections.singletonList(new MetricsInstrumentation()); diff --git a/instrumentation/micrometer/micrometer-1.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/micrometer/v1_5/MicrometerSingletons.java b/instrumentation/micrometer/micrometer-1.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/micrometer/v1_5/MicrometerSingletons.java index 21d1189de61f..bc4e2b714607 100644 --- a/instrumentation/micrometer/micrometer-1.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/micrometer/v1_5/MicrometerSingletons.java +++ b/instrumentation/micrometer/micrometer-1.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/micrometer/v1_5/MicrometerSingletons.java @@ -7,8 +7,8 @@ import io.micrometer.core.instrument.MeterRegistry; import io.opentelemetry.api.GlobalOpenTelemetry; +import io.opentelemetry.instrumentation.micrometer.v1_5.OpenTelemetryMeterRegistry; import io.opentelemetry.javaagent.bootstrap.internal.InstrumentationConfig; -import io.opentelemetry.micrometer1shim.OpenTelemetryMeterRegistry; public final class MicrometerSingletons { diff --git a/instrumentation/micrometer/micrometer-1.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/micrometer/v1_5/TimeUnitParser.java b/instrumentation/micrometer/micrometer-1.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/micrometer/v1_5/TimeUnitParser.java index 9d6637118578..e4a47994d334 100644 --- a/instrumentation/micrometer/micrometer-1.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/micrometer/v1_5/TimeUnitParser.java +++ b/instrumentation/micrometer/micrometer-1.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/micrometer/v1_5/TimeUnitParser.java @@ -7,7 +7,7 @@ import static java.util.logging.Level.WARNING; -import io.opentelemetry.micrometer1shim.OpenTelemetryMeterRegistry; +import io.opentelemetry.instrumentation.micrometer.v1_5.OpenTelemetryMeterRegistry; import java.util.Locale; import java.util.concurrent.TimeUnit; import java.util.logging.Logger; diff --git a/instrumentation/micrometer/micrometer-1.5/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/micrometer/v1_5/CounterTest.java b/instrumentation/micrometer/micrometer-1.5/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/micrometer/v1_5/CounterTest.java index 592faff1114d..cdc8d8ae2016 100644 --- a/instrumentation/micrometer/micrometer-1.5/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/micrometer/v1_5/CounterTest.java +++ b/instrumentation/micrometer/micrometer-1.5/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/micrometer/v1_5/CounterTest.java @@ -5,75 +5,25 @@ package io.opentelemetry.javaagent.instrumentation.micrometer.v1_5; -import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; -import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; - -import io.micrometer.core.instrument.Counter; import io.micrometer.core.instrument.Metrics; -import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.instrumentation.micrometer.v1_5.AbstractCounterTest; import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension; import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; -import org.assertj.core.api.AbstractIterableAssert; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.extension.RegisterExtension; -class CounterTest { - - static final String INSTRUMENTATION_NAME = "io.opentelemetry.micrometer1shim"; +class CounterTest extends AbstractCounterTest { @RegisterExtension static final InstrumentationExtension testing = AgentInstrumentationExtension.create(); - @BeforeEach - void cleanupMeters() { + @AfterEach + public void cleanup() { Metrics.globalRegistry.forEachMeter(Metrics.globalRegistry::remove); } - @Test - void testCounter() throws Exception { - // given - Counter counter = - Counter.builder("testCounter") - .description("This is a test counter") - .tags("tag", "value") - .baseUnit("items") - .register(Metrics.globalRegistry); - - // when - counter.increment(); - counter.increment(2); - - // then - testing.waitAndAssertMetrics( - INSTRUMENTATION_NAME, - "testCounter", - metrics -> - metrics.anySatisfy( - metric -> - assertThat(metric) - .hasDescription("This is a test counter") - .hasUnit("items") - .hasDoubleSumSatisfying( - sum -> - sum.isMonotonic() - .hasPointsSatisfying( - point -> - point - .hasValue(3) - .hasAttributesSatisfying( - equalTo( - AttributeKey.stringKey("tag"), - "value")))))); - testing.clearData(); - - // when - Metrics.globalRegistry.remove(counter); - counter.increment(); - - // then - Thread.sleep(100); // interval of the test metrics exporter - testing.waitAndAssertMetrics( - INSTRUMENTATION_NAME, "testCounter", AbstractIterableAssert::isEmpty); + @Override + protected InstrumentationExtension testing() { + return testing; } } diff --git a/instrumentation/micrometer/micrometer-1.5/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/micrometer/v1_5/DistributionSummaryTest.java b/instrumentation/micrometer/micrometer-1.5/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/micrometer/v1_5/DistributionSummaryTest.java index ef0f78b37c0e..80594c7eac94 100644 --- a/instrumentation/micrometer/micrometer-1.5/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/micrometer/v1_5/DistributionSummaryTest.java +++ b/instrumentation/micrometer/micrometer-1.5/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/micrometer/v1_5/DistributionSummaryTest.java @@ -5,214 +5,25 @@ package io.opentelemetry.javaagent.instrumentation.micrometer.v1_5; -import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; -import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; - -import io.micrometer.core.instrument.DistributionSummary; import io.micrometer.core.instrument.Metrics; -import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.instrumentation.micrometer.v1_5.AbstractDistributionSummaryTest; import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension; import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; -import org.assertj.core.api.AbstractIterableAssert; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.extension.RegisterExtension; -class DistributionSummaryTest { - - static final String INSTRUMENTATION_NAME = "io.opentelemetry.micrometer1shim"; +class DistributionSummaryTest extends AbstractDistributionSummaryTest { @RegisterExtension static final InstrumentationExtension testing = AgentInstrumentationExtension.create(); - @BeforeEach - void cleanupMeters() { + @AfterEach + public void cleanup() { Metrics.globalRegistry.forEachMeter(Metrics.globalRegistry::remove); } - @Test - void testDistributionSummary() throws Exception { - // given - DistributionSummary summary = - DistributionSummary.builder("testSummary") - .description("This is a test distribution summary") - .baseUnit("things") - .scale(2.0) - .tags("tag", "value") - .register(Metrics.globalRegistry); - - // when - summary.record(21); - - // then - testing.waitAndAssertMetrics( - INSTRUMENTATION_NAME, - "testSummary", - metrics -> - metrics.anySatisfy( - metric -> - assertThat(metric) - .hasDescription("This is a test distribution summary") - .hasUnit("things") - .hasHistogramSatisfying( - histogram -> - histogram.hasPointsSatisfying( - point -> - point - .hasSum(42) - .hasCount(1) - .hasAttributesSatisfying( - equalTo( - AttributeKey.stringKey("tag"), "value")))))); - testing.clearData(); - - // when - Metrics.globalRegistry.remove(summary); - summary.record(6); - - // then - Thread.sleep(100); // interval of the test metrics exporter - testing.waitAndAssertMetrics( - INSTRUMENTATION_NAME, "testSummary", AbstractIterableAssert::isEmpty); - } - - @Test - void testMicrometerHistogram() { - // given - DistributionSummary summary = - DistributionSummary.builder("testSummaryHistogram") - .description("This is a test distribution summary") - .baseUnit("things") - .tags("tag", "value") - .serviceLevelObjectives(1, 10, 100, 1000) - .distributionStatisticBufferLength(10) - .register(Metrics.globalRegistry); - - // when - summary.record(0.5); - summary.record(5); - summary.record(50); - summary.record(500); - - // then - testing.waitAndAssertMetrics( - INSTRUMENTATION_NAME, - "testSummaryHistogram.histogram", - metrics -> - metrics.anySatisfy( - metric -> - assertThat(metric) - .hasDoubleGaugeSatisfying( - gauge -> - gauge.hasPointsSatisfying( - point -> - point - .hasValue(1) - .hasAttributesSatisfying( - equalTo(AttributeKey.stringKey("tag"), "value"), - equalTo(AttributeKey.stringKey("le"), "1")), - point -> - point - .hasValue(2) - .hasAttributesSatisfying( - equalTo(AttributeKey.stringKey("tag"), "value"), - equalTo(AttributeKey.stringKey("le"), "10")), - point -> - point - .hasValue(3) - .hasAttributesSatisfying( - equalTo(AttributeKey.stringKey("tag"), "value"), - equalTo(AttributeKey.stringKey("le"), "100")), - point -> - point - .hasValue(4) - .hasAttributesSatisfying( - equalTo(AttributeKey.stringKey("tag"), "value"), - equalTo(AttributeKey.stringKey("le"), "1000")))))); - } - - @Test - void testMicrometerPercentiles() { - // given - DistributionSummary summary = - DistributionSummary.builder("testSummaryPercentiles") - .description("This is a test distribution summary") - .baseUnit("things") - .tags("tag", "value") - .publishPercentiles(0.5, 0.95, 0.99) - .register(Metrics.globalRegistry); - - // when - summary.record(50); - summary.record(100); - - // then - testing.waitAndAssertMetrics( - INSTRUMENTATION_NAME, - "testSummaryPercentiles.percentile", - metrics -> - metrics.anySatisfy( - metric -> - assertThat(metric) - .hasDoubleGaugeSatisfying( - gauge -> - gauge.hasPointsSatisfying( - point -> - point.hasAttributesSatisfying( - equalTo(AttributeKey.stringKey("tag"), "value"), - equalTo(AttributeKey.stringKey("phi"), "0.5")), - point -> - point.hasAttributesSatisfying( - equalTo(AttributeKey.stringKey("tag"), "value"), - equalTo(AttributeKey.stringKey("phi"), "0.95")), - point -> - point.hasAttributesSatisfying( - equalTo(AttributeKey.stringKey("tag"), "value"), - equalTo(AttributeKey.stringKey("phi"), "0.99")))))); - } - - @Test - void testMicrometerMax() throws InterruptedException { - // given - DistributionSummary summary = - DistributionSummary.builder("testSummaryMax") - .description("This is a test distribution summary") - .baseUnit("things") - .tags("tag", "value") - .register(Metrics.globalRegistry); - - // when - summary.record(1); - summary.record(2); - summary.record(4); - - // then - testing.waitAndAssertMetrics( - INSTRUMENTATION_NAME, - "testSummaryMax.max", - metrics -> - metrics.anySatisfy( - metric -> - assertThat(metric) - .hasDescription("This is a test distribution summary") - .hasDoubleGaugeSatisfying( - gauge -> - gauge.hasPointsSatisfying( - point -> - point - .hasValue(4) - .hasAttributesSatisfying( - equalTo( - AttributeKey.stringKey("tag"), "value")))))); - - // when - Metrics.globalRegistry.remove(summary); - Thread.sleep(100); // give time for any inflight metric export to be received - testing.clearData(); - - // then - Thread.sleep(100); // interval of the test metrics exporter - testing.waitAndAssertMetrics( - INSTRUMENTATION_NAME, "testSummaryMax.max", AbstractIterableAssert::isEmpty); + @Override + protected InstrumentationExtension testing() { + return testing; } } diff --git a/instrumentation/micrometer/micrometer-1.5/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/micrometer/v1_5/FunctionCounterTest.java b/instrumentation/micrometer/micrometer-1.5/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/micrometer/v1_5/FunctionCounterTest.java index 946a4af6f7c9..d0df46135d85 100644 --- a/instrumentation/micrometer/micrometer-1.5/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/micrometer/v1_5/FunctionCounterTest.java +++ b/instrumentation/micrometer/micrometer-1.5/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/micrometer/v1_5/FunctionCounterTest.java @@ -5,116 +5,25 @@ package io.opentelemetry.javaagent.instrumentation.micrometer.v1_5; -import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; -import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; - -import io.micrometer.core.instrument.FunctionCounter; import io.micrometer.core.instrument.Metrics; -import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.instrumentation.micrometer.v1_5.AbstractFunctionCounterTest; import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension; import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; -import java.util.concurrent.atomic.AtomicLong; -import org.assertj.core.api.AbstractIterableAssert; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.extension.RegisterExtension; -class FunctionCounterTest { - - static final String INSTRUMENTATION_NAME = "io.opentelemetry.micrometer1shim"; +class FunctionCounterTest extends AbstractFunctionCounterTest { @RegisterExtension static final InstrumentationExtension testing = AgentInstrumentationExtension.create(); - final AtomicLong num = new AtomicLong(12); - final AtomicLong anotherNum = new AtomicLong(13); - - @BeforeEach - void cleanupMeters() { + @AfterEach + public void cleanup() { Metrics.globalRegistry.forEachMeter(Metrics.globalRegistry::remove); } - @Test - void testFunctionCounter() throws InterruptedException { - // when - FunctionCounter counter = - FunctionCounter.builder("testFunctionCounter", num, AtomicLong::get) - .description("This is a test function counter") - .tags("tag", "value") - .baseUnit("items") - .register(Metrics.globalRegistry); - - // then - testing.waitAndAssertMetrics( - INSTRUMENTATION_NAME, - "testFunctionCounter", - metrics -> - metrics.anySatisfy( - metric -> - assertThat(metric) - .hasDescription("This is a test function counter") - .hasUnit("items") - .hasDoubleSumSatisfying( - sum -> - sum.isMonotonic() - .hasPointsSatisfying( - point -> - point - .hasValue(12) - .hasAttributesSatisfying( - equalTo( - AttributeKey.stringKey("tag"), - "value")))))); - - // when - Metrics.globalRegistry.remove(counter); - Thread.sleep(100); // give time for any inflight metric export to be received - testing.clearData(); - - // then - Thread.sleep(100); // interval of the test metrics exporter - testing.waitAndAssertMetrics( - INSTRUMENTATION_NAME, "testFunctionCounter", AbstractIterableAssert::isEmpty); - } - - @Test - void functionCountersWithSameNameAndDifferentTags() { - // when - FunctionCounter.builder("testFunctionCounterWithTags", num, AtomicLong::get) - .description("This is a test function counter") - .tags("tag", "1") - .baseUnit("items") - .register(Metrics.globalRegistry); - FunctionCounter.builder("testFunctionCounterWithTags", anotherNum, AtomicLong::get) - .description("This is a test function counter") - .tags("tag", "2") - .baseUnit("items") - .register(Metrics.globalRegistry); - - // then - testing.waitAndAssertMetrics( - INSTRUMENTATION_NAME, - "testFunctionCounterWithTags", - metrics -> - metrics.anySatisfy( - metric -> - assertThat(metric) - .hasDescription("This is a test function counter") - .hasUnit("items") - .hasDoubleSumSatisfying( - sum -> - sum.isMonotonic() - .hasPointsSatisfying( - point -> - point - .hasValue(12) - .hasAttributesSatisfying( - equalTo(AttributeKey.stringKey("tag"), "1")), - point -> - point - .hasValue(13) - .hasAttributesSatisfying( - equalTo( - AttributeKey.stringKey("tag"), "2")))))); + @Override + protected InstrumentationExtension testing() { + return testing; } } diff --git a/instrumentation/micrometer/micrometer-1.5/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/micrometer/v1_5/FunctionTimerSecondsTest.java b/instrumentation/micrometer/micrometer-1.5/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/micrometer/v1_5/FunctionTimerSecondsTest.java index 6295bf5f626f..80d9b1806c32 100644 --- a/instrumentation/micrometer/micrometer-1.5/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/micrometer/v1_5/FunctionTimerSecondsTest.java +++ b/instrumentation/micrometer/micrometer-1.5/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/micrometer/v1_5/FunctionTimerSecondsTest.java @@ -5,102 +5,25 @@ package io.opentelemetry.javaagent.instrumentation.micrometer.v1_5; -import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; -import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; - -import io.micrometer.core.instrument.FunctionTimer; import io.micrometer.core.instrument.Metrics; -import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.instrumentation.micrometer.v1_5.AbstractFunctionTimerSecondsTest; import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension; import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; -import java.util.concurrent.TimeUnit; -import org.assertj.core.api.AbstractIterableAssert; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.extension.RegisterExtension; -class FunctionTimerSecondsTest { - - static final String INSTRUMENTATION_NAME = "io.opentelemetry.micrometer1shim"; +class FunctionTimerSecondsTest extends AbstractFunctionTimerSecondsTest { @RegisterExtension static final InstrumentationExtension testing = AgentInstrumentationExtension.create(); - final TestTimer timerObj = new TestTimer(); - - @BeforeEach - void cleanupMeters() { - timerObj.reset(); + @AfterEach + public void cleanup() { Metrics.globalRegistry.forEachMeter(Metrics.globalRegistry::remove); } - @Test - void testFunctionTimerWithBaseUnitSeconds() throws InterruptedException { - // given - FunctionTimer functionTimer = - FunctionTimer.builder( - "testFunctionTimerSeconds", - timerObj, - TestTimer::getCount, - TestTimer::getTotalTimeNanos, - TimeUnit.NANOSECONDS) - .description("This is a test function timer") - .tags("tag", "value") - .register(Metrics.globalRegistry); - - // when - timerObj.add(42, TimeUnit.SECONDS); - - // then - testing.waitAndAssertMetrics( - INSTRUMENTATION_NAME, - "testFunctionTimerSeconds.count", - metrics -> - metrics.anySatisfy( - metric -> - assertThat(metric) - .hasDescription("This is a test function timer") - .hasUnit("1") - .hasLongSumSatisfying( - sum -> - sum.isMonotonic() - .hasPointsSatisfying( - point -> - point - .hasValue(1) - .hasAttributesSatisfying( - equalTo( - AttributeKey.stringKey("tag"), - "value")))))); - testing.waitAndAssertMetrics( - INSTRUMENTATION_NAME, - "testFunctionTimerSeconds.sum", - metrics -> - metrics.anySatisfy( - metric -> - assertThat(metric) - .hasDescription("This is a test function timer") - .hasUnit("s") - .hasDoubleSumSatisfying( - sum -> - sum.hasPointsSatisfying( - point -> - point - .hasValue(42) - .hasAttributesSatisfying( - equalTo( - AttributeKey.stringKey("tag"), "value")))))); - - // when - Metrics.globalRegistry.remove(functionTimer); - Thread.sleep(100); // give time for any inflight metric export to be received - testing.clearData(); - - // then - Thread.sleep(100); // interval of the test metrics exporter - testing.waitAndAssertMetrics( - INSTRUMENTATION_NAME, "testFunctionTimerSeconds.count", AbstractIterableAssert::isEmpty); - testing.waitAndAssertMetrics( - INSTRUMENTATION_NAME, "testFunctionTimerSeconds.sum", AbstractIterableAssert::isEmpty); + @Override + protected InstrumentationExtension testing() { + return testing; } } diff --git a/instrumentation/micrometer/micrometer-1.5/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/micrometer/v1_5/FunctionTimerTest.java b/instrumentation/micrometer/micrometer-1.5/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/micrometer/v1_5/FunctionTimerTest.java index c0170159dba5..b33453376961 100644 --- a/instrumentation/micrometer/micrometer-1.5/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/micrometer/v1_5/FunctionTimerTest.java +++ b/instrumentation/micrometer/micrometer-1.5/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/micrometer/v1_5/FunctionTimerTest.java @@ -5,180 +5,25 @@ package io.opentelemetry.javaagent.instrumentation.micrometer.v1_5; -import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; -import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; - -import io.micrometer.core.instrument.FunctionTimer; import io.micrometer.core.instrument.Metrics; -import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.instrumentation.micrometer.v1_5.AbstractFunctionTimerTest; import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension; import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; -import java.util.concurrent.TimeUnit; -import org.assertj.core.api.AbstractIterableAssert; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.extension.RegisterExtension; -class FunctionTimerTest { - - static final String INSTRUMENTATION_NAME = "io.opentelemetry.micrometer1shim"; +class FunctionTimerTest extends AbstractFunctionTimerTest { @RegisterExtension static final InstrumentationExtension testing = AgentInstrumentationExtension.create(); - final TestTimer timerObj = new TestTimer(); - final TestTimer anotherTimerObj = new TestTimer(); - - @BeforeEach - void cleanupMeters() { - timerObj.reset(); - anotherTimerObj.reset(); + @AfterEach + public void cleanup() { Metrics.globalRegistry.forEachMeter(Metrics.globalRegistry::remove); } - @Test - void testFunctionTimer() throws InterruptedException { - // given - FunctionTimer functionTimer = - FunctionTimer.builder( - "testFunctionTimer", - timerObj, - TestTimer::getCount, - TestTimer::getTotalTimeNanos, - TimeUnit.NANOSECONDS) - .description("This is a test function timer") - .tags("tag", "value") - .register(Metrics.globalRegistry); - - // when - timerObj.add(42, TimeUnit.SECONDS); - - // then - testing.waitAndAssertMetrics( - INSTRUMENTATION_NAME, - "testFunctionTimer.count", - metrics -> - metrics.anySatisfy( - metric -> - assertThat(metric) - .hasDescription("This is a test function timer") - .hasUnit("1") - .hasLongSumSatisfying( - sum -> - sum.isMonotonic() - .hasPointsSatisfying( - point -> - point - .hasValue(1) - .hasAttributesSatisfying( - equalTo( - AttributeKey.stringKey("tag"), - "value")))))); - testing.waitAndAssertMetrics( - INSTRUMENTATION_NAME, - "testFunctionTimer.sum", - metrics -> - metrics.anySatisfy( - metric -> - assertThat(metric) - .hasDescription("This is a test function timer") - .hasUnit("ms") - .hasDoubleSumSatisfying( - sum -> - sum.hasPointsSatisfying( - point -> - point - .hasValue(42_000) - .hasAttributesSatisfying( - equalTo( - AttributeKey.stringKey("tag"), "value")))))); - - // when - Metrics.globalRegistry.remove(functionTimer); - Thread.sleep(100); // give time for any inflight metric export to be received - testing.clearData(); - - // then - Thread.sleep(100); // interval of the test metrics exporter - testing.waitAndAssertMetrics( - INSTRUMENTATION_NAME, "testFunctionTimer.count", AbstractIterableAssert::isEmpty); - testing.waitAndAssertMetrics( - INSTRUMENTATION_NAME, "testFunctionTimer.sum", AbstractIterableAssert::isEmpty); - } - - @Test - void testNanoPrecision() { - // given - FunctionTimer.builder( - "testNanoFunctionTimer", - timerObj, - TestTimer::getCount, - TestTimer::getTotalTimeNanos, - TimeUnit.NANOSECONDS) - .register(Metrics.globalRegistry); - - // when - timerObj.add(1_234_000, TimeUnit.NANOSECONDS); - - // then - testing.waitAndAssertMetrics( - INSTRUMENTATION_NAME, - "testNanoFunctionTimer.sum", - metrics -> - metrics.anySatisfy( - metric -> - assertThat(metric) - .hasUnit("ms") - .hasDoubleSumSatisfying( - sum -> sum.hasPointsSatisfying(point -> point.hasValue(1.234))))); - } - - @Test - void functionTimersWithSameNameAndDifferentTags() { - // given - FunctionTimer.builder( - "testFunctionTimerWithTags", - timerObj, - TestTimer::getCount, - TestTimer::getTotalTimeNanos, - TimeUnit.NANOSECONDS) - .tags("tag", "1") - .register(Metrics.globalRegistry); - - FunctionTimer.builder( - "testFunctionTimerWithTags", - anotherTimerObj, - TestTimer::getCount, - TestTimer::getTotalTimeNanos, - TimeUnit.NANOSECONDS) - .tags("tag", "2") - .register(Metrics.globalRegistry); - - // when - timerObj.add(12, TimeUnit.SECONDS); - anotherTimerObj.add(42, TimeUnit.SECONDS); - - // then - testing.waitAndAssertMetrics( - INSTRUMENTATION_NAME, - "testFunctionTimerWithTags.sum", - metrics -> - metrics.anySatisfy( - metric -> - assertThat(metric) - .hasUnit("ms") - .hasDoubleSumSatisfying( - sum -> - sum.hasPointsSatisfying( - point -> - point - .hasValue(12_000) - .hasAttributesSatisfying( - equalTo(AttributeKey.stringKey("tag"), "1")), - point -> - point - .hasValue(42_000) - .hasAttributesSatisfying( - equalTo(AttributeKey.stringKey("tag"), "2")))))); + @Override + protected InstrumentationExtension testing() { + return testing; } } diff --git a/instrumentation/micrometer/micrometer-1.5/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/micrometer/v1_5/GaugeTest.java b/instrumentation/micrometer/micrometer-1.5/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/micrometer/v1_5/GaugeTest.java index d431782e0541..90672d0d261f 100644 --- a/instrumentation/micrometer/micrometer-1.5/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/micrometer/v1_5/GaugeTest.java +++ b/instrumentation/micrometer/micrometer-1.5/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/micrometer/v1_5/GaugeTest.java @@ -5,143 +5,25 @@ package io.opentelemetry.javaagent.instrumentation.micrometer.v1_5; -import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; -import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; - -import io.micrometer.core.instrument.Gauge; import io.micrometer.core.instrument.Metrics; -import io.opentelemetry.api.common.AttributeKey; -import io.opentelemetry.instrumentation.test.utils.GcUtils; +import io.opentelemetry.instrumentation.micrometer.v1_5.AbstractGaugeTest; import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension; import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; -import java.lang.ref.WeakReference; -import java.util.concurrent.atomic.AtomicLong; -import org.assertj.core.api.AbstractIterableAssert; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.extension.RegisterExtension; -class GaugeTest { - - static final String INSTRUMENTATION_NAME = "io.opentelemetry.micrometer1shim"; +class GaugeTest extends AbstractGaugeTest { @RegisterExtension static final InstrumentationExtension testing = AgentInstrumentationExtension.create(); - @BeforeEach - void cleanupMeters() { + @AfterEach + public void cleanup() { Metrics.globalRegistry.forEachMeter(Metrics.globalRegistry::remove); } - @Test - void testGauge() throws InterruptedException { - // when - Gauge gauge = - Gauge.builder("testGauge", () -> 42) - .description("This is a test gauge") - .tags("tag", "value") - .baseUnit("items") - .register(Metrics.globalRegistry); - - // then - testing.waitAndAssertMetrics( - INSTRUMENTATION_NAME, - "testGauge", - metrics -> - metrics.anySatisfy( - metric -> - assertThat(metric) - .hasDescription("This is a test gauge") - .hasUnit("items") - .hasDoubleGaugeSatisfying( - g -> - g.hasPointsSatisfying( - point -> - point - .hasValue(42) - .hasAttributesSatisfying( - equalTo( - AttributeKey.stringKey("tag"), "value")))))); - - // when - Metrics.globalRegistry.remove(gauge); - Thread.sleep(100); // give time for any inflight metric export to be received - testing.clearData(); - - // then - Thread.sleep(100); // interval of the test metrics exporter - testing.waitAndAssertMetrics( - INSTRUMENTATION_NAME, "testGauge", AbstractIterableAssert::isEmpty); - } - - @Test - void gaugesWithSameNameAndDifferentTags() { - // when - Gauge.builder("testGaugeWithTags", () -> 12) - .description("This is a test gauge") - .baseUnit("items") - .tags("tag", "1") - .register(Metrics.globalRegistry); - Gauge.builder("testGaugeWithTags", () -> 42) - .description("This is a test gauge") - .baseUnit("items") - .tags("tag", "2") - .register(Metrics.globalRegistry); - - // then - testing.waitAndAssertMetrics( - INSTRUMENTATION_NAME, - "testGaugeWithTags", - metrics -> - metrics.anySatisfy( - metric -> - assertThat(metric) - .hasDescription("This is a test gauge") - .hasUnit("items") - .hasDoubleGaugeSatisfying( - gauge -> - gauge.hasPointsSatisfying( - point -> - point - .hasValue(12) - .hasAttributesSatisfying( - equalTo(AttributeKey.stringKey("tag"), "1")), - point -> - point - .hasValue(42) - .hasAttributesSatisfying( - equalTo(AttributeKey.stringKey("tag"), "2")))))); - } - - @Test - void testWeakRefGauge() throws InterruptedException { - // when - AtomicLong num = new AtomicLong(42); - Gauge.builder("testWeakRefGauge", num, AtomicLong::get) - .strongReference(false) - .register(Metrics.globalRegistry); - - // then - testing.waitAndAssertMetrics( - INSTRUMENTATION_NAME, - "testWeakRefGauge", - metrics -> - metrics.anySatisfy( - metric -> - assertThat(metric) - .hasDoubleGaugeSatisfying( - gauge -> gauge.hasPointsSatisfying(point -> point.hasValue(42))))); - - // when - WeakReference numWeakRef = new WeakReference<>(num); - num = null; - GcUtils.awaitGc(numWeakRef); - - // then - Thread.sleep(100); // interval of the test metrics exporter - testing.clearData(); - Thread.sleep(100); // interval of the test metrics exporter - testing.waitAndAssertMetrics( - INSTRUMENTATION_NAME, "testWeakRefGauge", AbstractIterableAssert::isEmpty); + @Override + protected InstrumentationExtension testing() { + return testing; } } diff --git a/instrumentation/micrometer/micrometer-1.5/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/micrometer/v1_5/LongTaskTimerSecondsTest.java b/instrumentation/micrometer/micrometer-1.5/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/micrometer/v1_5/LongTaskTimerSecondsTest.java index c16f01e59c9d..23a8249fb66b 100644 --- a/instrumentation/micrometer/micrometer-1.5/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/micrometer/v1_5/LongTaskTimerSecondsTest.java +++ b/instrumentation/micrometer/micrometer-1.5/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/micrometer/v1_5/LongTaskTimerSecondsTest.java @@ -5,135 +5,25 @@ package io.opentelemetry.javaagent.instrumentation.micrometer.v1_5; -import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; -import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; - -import io.micrometer.core.instrument.LongTaskTimer; import io.micrometer.core.instrument.Metrics; -import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.instrumentation.micrometer.v1_5.AbstractLongTaskTimerSecondsTest; import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension; import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; -import java.util.concurrent.TimeUnit; -import org.assertj.core.api.AbstractIterableAssert; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.extension.RegisterExtension; -class LongTaskTimerSecondsTest { - - static final String INSTRUMENTATION_NAME = "io.opentelemetry.micrometer1shim"; +class LongTaskTimerSecondsTest extends AbstractLongTaskTimerSecondsTest { @RegisterExtension static final InstrumentationExtension testing = AgentInstrumentationExtension.create(); - @BeforeEach - void cleanupMeters() { + @AfterEach + public void cleanup() { Metrics.globalRegistry.forEachMeter(Metrics.globalRegistry::remove); } - @Test - void testLongTaskTimerWithBaseUnitSeconds() throws InterruptedException { - // given - LongTaskTimer timer = - LongTaskTimer.builder("testLongTaskTimerSeconds") - .description("This is a test long task timer") - .tags("tag", "value") - .register(Metrics.globalRegistry); - - // when - LongTaskTimer.Sample sample = timer.start(); - - // then - testing.waitAndAssertMetrics( - INSTRUMENTATION_NAME, - "testLongTaskTimerSeconds.active", - metrics -> - metrics.anySatisfy( - metric -> - assertThat(metric) - .hasDescription("This is a test long task timer") - .hasUnit("tasks") - .hasLongSumSatisfying( - sum -> - sum.isNotMonotonic() - .hasPointsSatisfying( - point -> - point - .hasValue(1) - .hasAttributesSatisfying( - equalTo( - AttributeKey.stringKey("tag"), - "value")))))); - testing.waitAndAssertMetrics( - INSTRUMENTATION_NAME, - "testLongTaskTimerSeconds.duration", - metrics -> - metrics.anySatisfy( - metric -> - assertThat(metric) - .hasDescription("This is a test long task timer") - .hasUnit("s") - .hasDoubleSumSatisfying( - sum -> - sum.isNotMonotonic() - .hasPointsSatisfying( - point -> { - point.hasAttributesSatisfying( - equalTo(AttributeKey.stringKey("tag"), "value")); - // any value >0 - duration of currently running tasks - assertThat(metric.getDoubleSumData().getPoints()) - .satisfiesExactly( - p -> assertThat(p.getValue()).isPositive()); - })))); - - // when - TimeUnit.MILLISECONDS.sleep(100); - sample.stop(); - - // then - testing.waitAndAssertMetrics( - INSTRUMENTATION_NAME, - "testLongTaskTimerSeconds.active", - metrics -> - metrics.anySatisfy( - metric -> - assertThat(metric) - .hasLongSumSatisfying( - sum -> - sum.hasPointsSatisfying( - point -> - point - .hasValue(0) - .hasAttributesSatisfying( - equalTo( - AttributeKey.stringKey("tag"), "value")))))); - testing.waitAndAssertMetrics( - INSTRUMENTATION_NAME, - "testLongTaskTimerSeconds.duration", - metrics -> - metrics.anySatisfy( - metric -> - assertThat(metric) - .hasDoubleSumSatisfying( - sum -> - sum.hasPointsSatisfying( - point -> - point - .hasValue(0) - .hasAttributesSatisfying( - equalTo( - AttributeKey.stringKey("tag"), "value")))))); - - // when timer is removed from the registry - Metrics.globalRegistry.remove(timer); - Thread.sleep(100); // give time for any in flight metric export to be received - testing.clearData(); - timer.start(); - - // then no tasks are active after starting a new sample - testing.waitAndAssertMetrics( - INSTRUMENTATION_NAME, "testLongTaskTimerSeconds.active", AbstractIterableAssert::isEmpty); - testing.waitAndAssertMetrics( - INSTRUMENTATION_NAME, "testLongTaskTimerSeconds.duration", AbstractIterableAssert::isEmpty); + @Override + protected InstrumentationExtension testing() { + return testing; } } diff --git a/instrumentation/micrometer/micrometer-1.5/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/micrometer/v1_5/LongTaskTimerTest.java b/instrumentation/micrometer/micrometer-1.5/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/micrometer/v1_5/LongTaskTimerTest.java index d2ae6109cf48..74373909c355 100644 --- a/instrumentation/micrometer/micrometer-1.5/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/micrometer/v1_5/LongTaskTimerTest.java +++ b/instrumentation/micrometer/micrometer-1.5/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/micrometer/v1_5/LongTaskTimerTest.java @@ -5,135 +5,25 @@ package io.opentelemetry.javaagent.instrumentation.micrometer.v1_5; -import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; -import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; - -import io.micrometer.core.instrument.LongTaskTimer; import io.micrometer.core.instrument.Metrics; -import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.instrumentation.micrometer.v1_5.AbstractLongTaskTimerTest; import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension; import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; -import java.util.concurrent.TimeUnit; -import org.assertj.core.api.AbstractIterableAssert; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.extension.RegisterExtension; -class LongTaskTimerTest { - - static final String INSTRUMENTATION_NAME = "io.opentelemetry.micrometer1shim"; +class LongTaskTimerTest extends AbstractLongTaskTimerTest { @RegisterExtension static final InstrumentationExtension testing = AgentInstrumentationExtension.create(); - @BeforeEach - void cleanupMeters() { + @AfterEach + public void cleanup() { Metrics.globalRegistry.forEachMeter(Metrics.globalRegistry::remove); } - @Test - void testLongTaskTimer() throws InterruptedException { - // given - LongTaskTimer timer = - LongTaskTimer.builder("testLongTaskTimer") - .description("This is a test long task timer") - .tags("tag", "value") - .register(Metrics.globalRegistry); - - // when - LongTaskTimer.Sample sample = timer.start(); - - // then - testing.waitAndAssertMetrics( - INSTRUMENTATION_NAME, - "testLongTaskTimer.active", - metrics -> - metrics.anySatisfy( - metric -> - assertThat(metric) - .hasDescription("This is a test long task timer") - .hasUnit("tasks") - .hasLongSumSatisfying( - sum -> - sum.isNotMonotonic() - .hasPointsSatisfying( - point -> - point - .hasValue(1) - .hasAttributesSatisfying( - equalTo( - AttributeKey.stringKey("tag"), - "value")))))); - testing.waitAndAssertMetrics( - INSTRUMENTATION_NAME, - "testLongTaskTimer.duration", - metrics -> - metrics.anySatisfy( - metric -> - assertThat(metric) - .hasDescription("This is a test long task timer") - .hasUnit("ms") - .hasDoubleSumSatisfying( - sum -> - sum.isNotMonotonic() - .hasPointsSatisfying( - point -> { - point.hasAttributesSatisfying( - equalTo(AttributeKey.stringKey("tag"), "value")); - // any value >0 - duration of currently running tasks - assertThat(metric.getDoubleSumData().getPoints()) - .satisfiesExactly( - p -> assertThat(p.getValue()).isPositive()); - })))); - - // when - TimeUnit.MILLISECONDS.sleep(100); - sample.stop(); - - // then - testing.waitAndAssertMetrics( - INSTRUMENTATION_NAME, - "testLongTaskTimer.active", - metrics -> - metrics.anySatisfy( - metric -> - assertThat(metric) - .hasLongSumSatisfying( - sum -> - sum.hasPointsSatisfying( - point -> - point - .hasValue(0) - .hasAttributesSatisfying( - equalTo( - AttributeKey.stringKey("tag"), "value")))))); - testing.waitAndAssertMetrics( - INSTRUMENTATION_NAME, - "testLongTaskTimer.duration", - metrics -> - metrics.anySatisfy( - metric -> - assertThat(metric) - .hasDoubleSumSatisfying( - sum -> - sum.hasPointsSatisfying( - point -> - point - .hasValue(0) - .hasAttributesSatisfying( - equalTo( - AttributeKey.stringKey("tag"), "value")))))); - - // when timer is removed from the registry - Metrics.globalRegistry.remove(timer); - Thread.sleep(100); // give time for any in flight metric export to be received - testing.clearData(); - timer.start(); - - // then no tasks are active after starting a new sample - testing.waitAndAssertMetrics( - INSTRUMENTATION_NAME, "testLongTaskTimer.active", AbstractIterableAssert::isEmpty); - testing.waitAndAssertMetrics( - INSTRUMENTATION_NAME, "testLongTaskTimer.duration", AbstractIterableAssert::isEmpty); + @Override + protected InstrumentationExtension testing() { + return testing; } } diff --git a/instrumentation/micrometer/micrometer-1.5/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/micrometer/v1_5/MeterTest.java b/instrumentation/micrometer/micrometer-1.5/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/micrometer/v1_5/MeterTest.java index 8ffd2786c02e..414a21984bda 100644 --- a/instrumentation/micrometer/micrometer-1.5/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/micrometer/v1_5/MeterTest.java +++ b/instrumentation/micrometer/micrometer-1.5/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/micrometer/v1_5/MeterTest.java @@ -5,234 +5,25 @@ package io.opentelemetry.javaagent.instrumentation.micrometer.v1_5; -import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; -import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; - -import io.micrometer.core.instrument.Measurement; -import io.micrometer.core.instrument.Meter; import io.micrometer.core.instrument.Metrics; -import io.micrometer.core.instrument.Statistic; -import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.instrumentation.micrometer.v1_5.AbstractMeterTest; import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension; import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; -import java.util.Arrays; -import java.util.List; -import java.util.concurrent.atomic.AtomicReference; -import org.assertj.core.api.AbstractIterableAssert; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.extension.RegisterExtension; -class MeterTest { - - static final String INSTRUMENTATION_NAME = "io.opentelemetry.micrometer1shim"; +class MeterTest extends AbstractMeterTest { @RegisterExtension static final InstrumentationExtension testing = AgentInstrumentationExtension.create(); - @BeforeEach - void cleanupMeters() { + @AfterEach + public void cleanup() { Metrics.globalRegistry.forEachMeter(Metrics.globalRegistry::remove); } - @Test - void testMeter() throws InterruptedException { - // given - AtomicReference number = new AtomicReference<>(12345.0); - - List measurements = - Arrays.asList( - new Measurement(number::get, Statistic.TOTAL), - new Measurement(number::get, Statistic.TOTAL_TIME), - new Measurement(number::get, Statistic.COUNT), - new Measurement(number::get, Statistic.ACTIVE_TASKS), - new Measurement(number::get, Statistic.DURATION), - new Measurement(number::get, Statistic.MAX), - new Measurement(number::get, Statistic.VALUE), - new Measurement(number::get, Statistic.UNKNOWN)); - - // when - Meter meter = - Meter.builder("testMeter", Meter.Type.OTHER, measurements) - .description("This is a test meter") - .baseUnit("things") - .tag("tag", "value") - .register(Metrics.globalRegistry); - - // then - testing.waitAndAssertMetrics( - INSTRUMENTATION_NAME, - "testMeter.total", - metrics -> - metrics.anySatisfy( - metric -> - assertThat(metric) - .hasDescription("This is a test meter") - .hasUnit("things") - .hasDoubleSumSatisfying( - sum -> - sum.isMonotonic() - .hasPointsSatisfying( - point -> - point - .hasValue(12345) - .hasAttributesSatisfying( - equalTo( - AttributeKey.stringKey("tag"), - "value")))))); - testing.waitAndAssertMetrics( - INSTRUMENTATION_NAME, - "testMeter.total_time", - metrics -> - metrics.anySatisfy( - metric -> - assertThat(metric) - .hasDescription("This is a test meter") - .hasUnit("things") - .hasDoubleSumSatisfying( - sum -> - sum.isMonotonic() - .hasPointsSatisfying( - point -> - point - .hasValue(12345) - .hasAttributesSatisfying( - equalTo( - AttributeKey.stringKey("tag"), - "value")))))); - testing.waitAndAssertMetrics( - INSTRUMENTATION_NAME, - "testMeter.count", - metrics -> - metrics.anySatisfy( - metric -> - assertThat(metric) - .hasDescription("This is a test meter") - .hasUnit("things") - .hasDoubleSumSatisfying( - sum -> - sum.isMonotonic() - .hasPointsSatisfying( - point -> - point - .hasValue(12345) - .hasAttributesSatisfying( - equalTo( - AttributeKey.stringKey("tag"), - "value")))))); - testing.waitAndAssertMetrics( - INSTRUMENTATION_NAME, - "testMeter.active", - metrics -> - metrics.anySatisfy( - metric -> - assertThat(metric) - .hasDescription("This is a test meter") - .hasUnit("things") - .hasDoubleSumSatisfying( - sum -> - sum.isNotMonotonic() - .hasPointsSatisfying( - point -> - point - .hasValue(12345) - .hasAttributesSatisfying( - equalTo( - AttributeKey.stringKey("tag"), - "value")))))); - testing.waitAndAssertMetrics( - INSTRUMENTATION_NAME, - "testMeter.duration", - metrics -> - metrics.anySatisfy( - metric -> - assertThat(metric) - .hasDescription("This is a test meter") - .hasUnit("things") - .hasDoubleGaugeSatisfying( - gauge -> - gauge.hasPointsSatisfying( - point -> - point - .hasValue(12345) - .hasAttributesSatisfying( - equalTo( - AttributeKey.stringKey("tag"), "value")))))); - testing.waitAndAssertMetrics( - INSTRUMENTATION_NAME, - "testMeter.max", - metrics -> - metrics.anySatisfy( - metric -> - assertThat(metric) - .hasDescription("This is a test meter") - .hasUnit("things") - .hasDoubleGaugeSatisfying( - gauge -> - gauge.hasPointsSatisfying( - point -> - point - .hasValue(12345) - .hasAttributesSatisfying( - equalTo( - AttributeKey.stringKey("tag"), "value")))))); - testing.waitAndAssertMetrics( - INSTRUMENTATION_NAME, - "testMeter.value", - metrics -> - metrics.anySatisfy( - metric -> - assertThat(metric) - .hasDescription("This is a test meter") - .hasUnit("things") - .hasDoubleGaugeSatisfying( - gauge -> - gauge.hasPointsSatisfying( - point -> - point - .hasValue(12345) - .hasAttributesSatisfying( - equalTo( - AttributeKey.stringKey("tag"), "value")))))); - testing.waitAndAssertMetrics( - INSTRUMENTATION_NAME, - "testMeter.unknown", - metrics -> - metrics.anySatisfy( - metric -> - assertThat(metric) - .hasDescription("This is a test meter") - .hasUnit("things") - .hasDoubleGaugeSatisfying( - gauge -> - gauge.hasPointsSatisfying( - point -> - point - .hasValue(12345) - .hasAttributesSatisfying( - equalTo( - AttributeKey.stringKey("tag"), "value")))))); - - // when - Metrics.globalRegistry.remove(meter); - Thread.sleep(100); // give time for any inflight metric export to be received - testing.clearData(); - - testing.waitAndAssertMetrics( - INSTRUMENTATION_NAME, "testMeter.total", AbstractIterableAssert::isEmpty); - testing.waitAndAssertMetrics( - INSTRUMENTATION_NAME, "testMeter.total_time", AbstractIterableAssert::isEmpty); - testing.waitAndAssertMetrics( - INSTRUMENTATION_NAME, "testMeter.count", AbstractIterableAssert::isEmpty); - testing.waitAndAssertMetrics( - INSTRUMENTATION_NAME, "testMeter.active", AbstractIterableAssert::isEmpty); - testing.waitAndAssertMetrics( - INSTRUMENTATION_NAME, "testMeter.duration", AbstractIterableAssert::isEmpty); - testing.waitAndAssertMetrics( - INSTRUMENTATION_NAME, "testMeter.max", AbstractIterableAssert::isEmpty); - testing.waitAndAssertMetrics( - INSTRUMENTATION_NAME, "testMeter.value", AbstractIterableAssert::isEmpty); - testing.waitAndAssertMetrics( - INSTRUMENTATION_NAME, "testMeter.unknown", AbstractIterableAssert::isEmpty); + @Override + protected InstrumentationExtension testing() { + return testing; } } diff --git a/instrumentation/micrometer/micrometer-1.5/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/micrometer/v1_5/PrometheusModeTest.java b/instrumentation/micrometer/micrometer-1.5/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/micrometer/v1_5/PrometheusModeTest.java index 7a6677fc7b74..c2e57d2c5732 100644 --- a/instrumentation/micrometer/micrometer-1.5/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/micrometer/v1_5/PrometheusModeTest.java +++ b/instrumentation/micrometer/micrometer-1.5/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/micrometer/v1_5/PrometheusModeTest.java @@ -5,361 +5,25 @@ package io.opentelemetry.javaagent.instrumentation.micrometer.v1_5; -import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; -import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; - -import io.micrometer.core.instrument.Counter; -import io.micrometer.core.instrument.DistributionSummary; -import io.micrometer.core.instrument.FunctionTimer; -import io.micrometer.core.instrument.Gauge; -import io.micrometer.core.instrument.LongTaskTimer; import io.micrometer.core.instrument.Metrics; -import io.micrometer.core.instrument.Timer; -import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.instrumentation.micrometer.v1_5.AbstractPrometheusModeTest; import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension; import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; -import java.util.concurrent.TimeUnit; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.extension.RegisterExtension; -@SuppressWarnings("PreferJavaTimeOverload") -class PrometheusModeTest { - - static final String INSTRUMENTATION_NAME = "io.opentelemetry.micrometer1shim"; +class PrometheusModeTest extends AbstractPrometheusModeTest { @RegisterExtension static final InstrumentationExtension testing = AgentInstrumentationExtension.create(); - final TestTimer timerObj = new TestTimer(); - - @BeforeEach - void cleanupMeters() { + @AfterEach + public void cleanup() { Metrics.globalRegistry.forEachMeter(Metrics.globalRegistry::remove); } - @Test - void testCounter() { - // given - Counter counter = - Counter.builder("testPrometheusCounter") - .description("This is a test counter") - .tags("tag", "value") - .baseUnit("items") - .register(Metrics.globalRegistry); - - // when - counter.increment(12); - - // then - testing.waitAndAssertMetrics( - INSTRUMENTATION_NAME, - "testPrometheusCounter.items", - metrics -> - metrics.anySatisfy( - metric -> - assertThat(metric) - .hasDescription("This is a test counter") - .hasUnit("items") - .hasDoubleSumSatisfying( - sum -> - sum.isMonotonic() - .hasPointsSatisfying( - point -> - point - .hasValue(12) - .hasAttributesSatisfying( - equalTo( - AttributeKey.stringKey("tag"), - "value")))))); - } - - @Test - void testDistributionSummary() { - // given - DistributionSummary summary = - DistributionSummary.builder("testPrometheusSummary") - .description("This is a test summary") - .baseUnit("items") - .tag("tag", "value") - .register(Metrics.globalRegistry); - - // when - summary.record(12); - summary.record(42); - - // then - testing.waitAndAssertMetrics( - INSTRUMENTATION_NAME, - "testPrometheusSummary.items", - metrics -> - metrics.anySatisfy( - metric -> - assertThat(metric) - .hasDescription("This is a test summary") - .hasUnit("items") - .hasHistogramSatisfying( - histogram -> - histogram.hasPointsSatisfying( - point -> - point - .hasSum(54) - .hasCount(2) - .hasAttributesSatisfying( - equalTo( - AttributeKey.stringKey("tag"), "value")))))); - testing.waitAndAssertMetrics( - INSTRUMENTATION_NAME, - "testPrometheusSummary.items.max", - metrics -> - metrics.anySatisfy( - metric -> - assertThat(metric) - .hasDescription("This is a test summary") - .hasUnit("items") - .hasDoubleGaugeSatisfying( - gauge -> - gauge.hasPointsSatisfying( - point -> - point - .hasValue(42) - .hasAttributesSatisfying( - equalTo( - AttributeKey.stringKey("tag"), "value")))))); - } - - @Test - void testFunctionTimer() { - // given - FunctionTimer.builder( - "testPrometheusFunctionTimer", - timerObj, - TestTimer::getCount, - TestTimer::getTotalTimeNanos, - TimeUnit.NANOSECONDS) - .description("This is a test function timer") - .tags("tag", "value") - .register(Metrics.globalRegistry); - - // when - timerObj.add(42, TimeUnit.SECONDS); - - // then - testing.waitAndAssertMetrics( - INSTRUMENTATION_NAME, - "testPrometheusFunctionTimer.seconds.count", - metrics -> - metrics.anySatisfy( - metric -> - assertThat(metric) - .hasDescription("This is a test function timer") - .hasUnit("1") - .hasLongSumSatisfying( - sum -> - sum.isMonotonic() - .hasPointsSatisfying( - point -> - point - .hasValue(1) - .hasAttributesSatisfying( - equalTo( - AttributeKey.stringKey("tag"), - "value")))))); - testing.waitAndAssertMetrics( - INSTRUMENTATION_NAME, - "testPrometheusFunctionTimer.seconds.sum", - metrics -> - metrics.anySatisfy( - metric -> - assertThat(metric) - .hasDescription("This is a test function timer") - .hasUnit("s") - .hasDoubleSumSatisfying( - sum -> - sum.hasPointsSatisfying( - point -> - point - .hasValue(42) - .hasAttributesSatisfying( - equalTo( - AttributeKey.stringKey("tag"), "value")))))); - } - - @Test - void testGauge() { - // when - Gauge.builder("testPrometheusGauge", () -> 42) - .description("This is a test gauge") - .tags("tag", "value") - .baseUnit("items") - .register(Metrics.globalRegistry); - - // then - testing.waitAndAssertMetrics( - INSTRUMENTATION_NAME, - "testPrometheusGauge.items", - metrics -> - metrics.anySatisfy( - metric -> - assertThat(metric) - .hasDescription("This is a test gauge") - .hasUnit("items") - .hasDoubleGaugeSatisfying( - gauge -> - gauge.hasPointsSatisfying( - point -> - point - .hasValue(42) - .hasAttributesSatisfying( - equalTo( - AttributeKey.stringKey("tag"), "value")))))); - } - - @Test - void testLongTaskTimer() throws InterruptedException { - // given - LongTaskTimer timer = - LongTaskTimer.builder("testPrometheusLongTaskTimer") - .description("This is a test long task timer") - .tags("tag", "value") - .register(Metrics.globalRegistry); - - // when - LongTaskTimer.Sample sample = timer.start(); - - // then - testing.waitAndAssertMetrics( - INSTRUMENTATION_NAME, - "testPrometheusLongTaskTimer.seconds.active", - metrics -> - metrics.anySatisfy( - metric -> - assertThat(metric) - .hasDescription("This is a test long task timer") - .hasUnit("tasks") - .hasLongSumSatisfying( - sum -> - sum.isNotMonotonic() - .hasPointsSatisfying( - point -> - point - .hasValue(1) - .hasAttributesSatisfying( - equalTo( - AttributeKey.stringKey("tag"), - "value")))))); - testing.waitAndAssertMetrics( - INSTRUMENTATION_NAME, - "testPrometheusLongTaskTimer.seconds.duration", - metrics -> - metrics.anySatisfy( - metric -> - assertThat(metric) - .hasDescription("This is a test long task timer") - .hasUnit("s") - .hasDoubleSumSatisfying( - sum -> - sum.isNotMonotonic() - .hasPointsSatisfying( - point -> { - point.hasAttributesSatisfying( - equalTo(AttributeKey.stringKey("tag"), "value")); - // any value >0 - duration of currently running tasks - assertThat(metric.getDoubleSumData().getPoints()) - .satisfiesExactly( - p -> assertThat(p.getValue()).isPositive()); - })))); - - // when - TimeUnit.MILLISECONDS.sleep(100); - sample.stop(); - - // then - testing.waitAndAssertMetrics( - INSTRUMENTATION_NAME, - "testPrometheusLongTaskTimer.seconds.active", - metrics -> - metrics.anySatisfy( - metric -> - assertThat(metric) - .hasLongSumSatisfying( - sum -> - sum.hasPointsSatisfying( - point -> - point - .hasValue(0) - .hasAttributesSatisfying( - equalTo( - AttributeKey.stringKey("tag"), "value")))))); - testing.waitAndAssertMetrics( - INSTRUMENTATION_NAME, - "testPrometheusLongTaskTimer.seconds.duration", - metrics -> - metrics.anySatisfy( - metric -> - assertThat(metric) - .hasDoubleSumSatisfying( - sum -> - sum.hasPointsSatisfying( - point -> - point - .hasValue(0) - .hasAttributesSatisfying( - equalTo( - AttributeKey.stringKey("tag"), "value")))))); - } - - @Test - void testTimer() { - // given - Timer timer = - Timer.builder("testPrometheusTimer") - .description("This is a test timer") - .tags("tag", "value") - .register(Metrics.globalRegistry); - - // when - timer.record(1, TimeUnit.SECONDS); - timer.record(5, TimeUnit.SECONDS); - timer.record(10_789, TimeUnit.MILLISECONDS); - - // then - testing.waitAndAssertMetrics( - INSTRUMENTATION_NAME, - "testPrometheusTimer.seconds", - metrics -> - metrics.anySatisfy( - metric -> - assertThat(metric) - .hasDescription("This is a test timer") - .hasUnit("s") - .hasHistogramSatisfying( - histogram -> - histogram.hasPointsSatisfying( - point -> - point - .hasSum(16.789) - .hasCount(3) - .hasAttributesSatisfying( - equalTo( - AttributeKey.stringKey("tag"), "value")))))); - testing.waitAndAssertMetrics( - INSTRUMENTATION_NAME, - "testPrometheusTimer.seconds.max", - metrics -> - metrics.anySatisfy( - metric -> - assertThat(metric) - .hasDescription("This is a test timer") - .hasUnit("s") - .hasDoubleGaugeSatisfying( - gauge -> - gauge.hasPointsSatisfying( - point -> - point - .hasValue(10.789) - .hasAttributesSatisfying( - equalTo( - AttributeKey.stringKey("tag"), "value")))))); + @Override + protected InstrumentationExtension testing() { + return testing; } } diff --git a/instrumentation/micrometer/micrometer-1.5/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/micrometer/v1_5/TimerSecondsTest.java b/instrumentation/micrometer/micrometer-1.5/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/micrometer/v1_5/TimerSecondsTest.java index 2e9a154848fa..f3bdb113a0dc 100644 --- a/instrumentation/micrometer/micrometer-1.5/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/micrometer/v1_5/TimerSecondsTest.java +++ b/instrumentation/micrometer/micrometer-1.5/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/micrometer/v1_5/TimerSecondsTest.java @@ -5,98 +5,25 @@ package io.opentelemetry.javaagent.instrumentation.micrometer.v1_5; -import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; -import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; - import io.micrometer.core.instrument.Metrics; -import io.micrometer.core.instrument.Timer; -import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.instrumentation.micrometer.v1_5.AbstractTimerSecondsTest; import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension; import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; -import java.util.concurrent.TimeUnit; -import org.assertj.core.api.AbstractIterableAssert; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.extension.RegisterExtension; -@SuppressWarnings("PreferJavaTimeOverload") -class TimerSecondsTest { - - static final String INSTRUMENTATION_NAME = "io.opentelemetry.micrometer1shim"; +class TimerSecondsTest extends AbstractTimerSecondsTest { @RegisterExtension static final InstrumentationExtension testing = AgentInstrumentationExtension.create(); - @BeforeEach - void cleanupMeters() { + @AfterEach + public void cleanup() { Metrics.globalRegistry.forEachMeter(Metrics.globalRegistry::remove); } - @Test - void testTimerWithBaseUnitSeconds() throws InterruptedException { - // given - Timer timer = - Timer.builder("testTimerSeconds") - .description("This is a test timer") - .tags("tag", "value") - .register(Metrics.globalRegistry); - - // when - timer.record(1, TimeUnit.SECONDS); - timer.record(10, TimeUnit.SECONDS); - timer.record(12_345, TimeUnit.MILLISECONDS); - - // then - testing.waitAndAssertMetrics( - INSTRUMENTATION_NAME, - "testTimerSeconds", - metrics -> - metrics.anySatisfy( - metric -> - assertThat(metric) - .hasDescription("This is a test timer") - .hasUnit("s") - .hasHistogramSatisfying( - histogram -> - histogram.hasPointsSatisfying( - point -> - point - .hasSum(23.345) - .hasCount(3) - .hasAttributesSatisfying( - equalTo( - AttributeKey.stringKey("tag"), "value")))))); - testing.waitAndAssertMetrics( - INSTRUMENTATION_NAME, - "testTimerSeconds.max", - metrics -> - metrics.anySatisfy( - metric -> - assertThat(metric) - .hasDescription("This is a test timer") - .hasUnit("s") - .hasDoubleGaugeSatisfying( - gauge -> - gauge.hasPointsSatisfying( - point -> - point - .hasValue(12.345) - .hasAttributesSatisfying( - equalTo( - AttributeKey.stringKey("tag"), "value")))))); - testing.clearData(); - - // when - Metrics.globalRegistry.remove(timer); - timer.record(12, TimeUnit.SECONDS); - Thread.sleep(100); // give time for any inflight metric export to be received - testing.clearData(); - - // then - Thread.sleep(100); // interval of the test metrics exporter - testing.waitAndAssertMetrics( - INSTRUMENTATION_NAME, "testTimerSeconds", AbstractIterableAssert::isEmpty); - testing.waitAndAssertMetrics( - INSTRUMENTATION_NAME, "testTimerMax.max", AbstractIterableAssert::isEmpty); + @Override + protected InstrumentationExtension testing() { + return testing; } } diff --git a/instrumentation/micrometer/micrometer-1.5/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/micrometer/v1_5/TimerTest.java b/instrumentation/micrometer/micrometer-1.5/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/micrometer/v1_5/TimerTest.java index 5205602e5ce5..3e0d13a2e57f 100644 --- a/instrumentation/micrometer/micrometer-1.5/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/micrometer/v1_5/TimerTest.java +++ b/instrumentation/micrometer/micrometer-1.5/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/micrometer/v1_5/TimerTest.java @@ -5,240 +5,25 @@ package io.opentelemetry.javaagent.instrumentation.micrometer.v1_5; -import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; -import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; - import io.micrometer.core.instrument.Metrics; -import io.micrometer.core.instrument.Timer; -import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.instrumentation.micrometer.v1_5.AbstractTimerTest; import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension; import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; -import java.time.Duration; -import java.util.concurrent.TimeUnit; -import org.assertj.core.api.AbstractIterableAssert; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.extension.RegisterExtension; -@SuppressWarnings("PreferJavaTimeOverload") -class TimerTest { - - static final String INSTRUMENTATION_NAME = "io.opentelemetry.micrometer1shim"; +class TimerTest extends AbstractTimerTest { @RegisterExtension static final InstrumentationExtension testing = AgentInstrumentationExtension.create(); - @BeforeEach - void cleanupMeters() { + @AfterEach + public void cleanup() { Metrics.globalRegistry.forEachMeter(Metrics.globalRegistry::remove); } - @Test - void testTimer() throws Exception { - // given - Timer timer = - Timer.builder("testTimer") - .description("This is a test timer") - .tags("tag", "value") - .register(Metrics.globalRegistry); - - // when - timer.record(42, TimeUnit.SECONDS); - - // then - testing.waitAndAssertMetrics( - INSTRUMENTATION_NAME, - "testTimer", - metrics -> - metrics.anySatisfy( - metric -> - assertThat(metric) - .hasDescription("This is a test timer") - .hasUnit("ms") - .hasHistogramSatisfying( - histogram -> - histogram.hasPointsSatisfying( - point -> - point - .hasSum(42_000) - .hasCount(1) - .hasAttributesSatisfying( - equalTo( - AttributeKey.stringKey("tag"), "value")))))); - testing.clearData(); - - // when - Metrics.globalRegistry.remove(timer); - timer.record(12, TimeUnit.SECONDS); - - // then - Thread.sleep(100); // interval of the test metrics exporter - testing.waitAndAssertMetrics( - INSTRUMENTATION_NAME, "testTimerMax", AbstractIterableAssert::isEmpty); - } - - @Test - void testNanoPrecision() { - // given - Timer timer = Timer.builder("testNanoTimer").register(Metrics.globalRegistry); - - // when - timer.record(1_234_000, TimeUnit.NANOSECONDS); - - // then - testing.waitAndAssertMetrics( - INSTRUMENTATION_NAME, - "testNanoTimer", - metrics -> - metrics.anySatisfy( - metric -> - assertThat(metric) - .hasUnit("ms") - .hasHistogramSatisfying( - histogram -> - histogram.hasPointsSatisfying( - point -> point.hasSum(1.234).hasCount(1))))); - } - - @Test - void testMicrometerHistogram() { - // given - Timer timer = - Timer.builder("testTimerHistogram") - .description("This is a test timer") - .tags("tag", "value") - .serviceLevelObjectives( - Duration.ofSeconds(1), - Duration.ofSeconds(10), - Duration.ofSeconds(100), - Duration.ofSeconds(1000)) - .distributionStatisticBufferLength(10) - .register(Metrics.globalRegistry); - - // when - timer.record(500, TimeUnit.MILLISECONDS); - timer.record(5, TimeUnit.SECONDS); - timer.record(50, TimeUnit.SECONDS); - timer.record(500, TimeUnit.SECONDS); - - // then - testing.waitAndAssertMetrics( - INSTRUMENTATION_NAME, - "testTimerHistogram.histogram", - metrics -> - metrics.anySatisfy( - metric -> - assertThat(metric) - .hasDoubleGaugeSatisfying( - gauge -> - gauge.hasPointsSatisfying( - point -> - point - .hasValue(1) - .hasAttributesSatisfying( - equalTo(AttributeKey.stringKey("tag"), "value"), - equalTo(AttributeKey.stringKey("le"), "1000")), - point -> - point - .hasValue(2) - .hasAttributesSatisfying( - equalTo(AttributeKey.stringKey("tag"), "value"), - equalTo(AttributeKey.stringKey("le"), "10000")), - point -> - point - .hasValue(3) - .hasAttributesSatisfying( - equalTo(AttributeKey.stringKey("tag"), "value"), - equalTo(AttributeKey.stringKey("le"), "100000")), - point -> - point - .hasValue(4) - .hasAttributesSatisfying( - equalTo(AttributeKey.stringKey("tag"), "value"), - equalTo( - AttributeKey.stringKey("le"), "1000000")))))); - } - - @Test - void testMicrometerPercentiles() { - // given - Timer timer = - Timer.builder("testTimerPercentiles") - .description("This is a test timer") - .tags("tag", "value") - .publishPercentiles(0.5, 0.95, 0.99) - .register(Metrics.globalRegistry); - - // when - timer.record(50, TimeUnit.MILLISECONDS); - timer.record(100, TimeUnit.MILLISECONDS); - - // then - testing.waitAndAssertMetrics( - INSTRUMENTATION_NAME, - "testTimerPercentiles.percentile", - metrics -> - metrics.anySatisfy( - metric -> - assertThat(metric) - .hasDoubleGaugeSatisfying( - gauge -> - gauge.hasPointsSatisfying( - point -> - point.hasAttributesSatisfying( - equalTo(AttributeKey.stringKey("tag"), "value"), - equalTo(AttributeKey.stringKey("phi"), "0.5")), - point -> - point.hasAttributesSatisfying( - equalTo(AttributeKey.stringKey("tag"), "value"), - equalTo(AttributeKey.stringKey("phi"), "0.95")), - point -> - point.hasAttributesSatisfying( - equalTo(AttributeKey.stringKey("tag"), "value"), - equalTo(AttributeKey.stringKey("phi"), "0.99")))))); - } - - @Test - void testMicrometerMax() throws InterruptedException { - // given - Timer timer = - Timer.builder("testTimerMax") - .description("This is a test timer") - .tags("tag", "value") - .register(Metrics.globalRegistry); - - // when - timer.record(1, TimeUnit.SECONDS); - timer.record(2, TimeUnit.SECONDS); - timer.record(4, TimeUnit.SECONDS); - - // then - testing.waitAndAssertMetrics( - INSTRUMENTATION_NAME, - "testTimerMax.max", - metrics -> - metrics.anySatisfy( - metric -> - assertThat(metric) - .hasDescription("This is a test timer") - .hasDoubleGaugeSatisfying( - gauge -> - gauge.hasPointsSatisfying( - point -> - point - .hasValue(4_000) - .hasAttributesSatisfying( - equalTo( - AttributeKey.stringKey("tag"), "value")))))); - - // when - Metrics.globalRegistry.remove(timer); - Thread.sleep(100); // give time for any inflight metric export to be received - testing.clearData(); - - // then - Thread.sleep(100); // interval of the test metrics exporter - testing.waitAndAssertMetrics( - INSTRUMENTATION_NAME, "testTimerMax.max", AbstractIterableAssert::isEmpty); + @Override + protected InstrumentationExtension testing() { + return testing; } } diff --git a/instrumentation/micrometer/micrometer-1.5/library/build.gradle.kts b/instrumentation/micrometer/micrometer-1.5/library/build.gradle.kts new file mode 100644 index 000000000000..3cc08bafa0a2 --- /dev/null +++ b/instrumentation/micrometer/micrometer-1.5/library/build.gradle.kts @@ -0,0 +1,9 @@ +plugins { + id("otel.library-instrumentation") +} + +dependencies { + library("io.micrometer:micrometer-core:1.5.0") + + testImplementation(project(":instrumentation:micrometer:micrometer-1.5:testing")) +} diff --git a/instrumentation/micrometer/micrometer-1.5/library/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/Bridging.java b/instrumentation/micrometer/micrometer-1.5/library/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/Bridging.java new file mode 100644 index 000000000000..8ff9abd223ea --- /dev/null +++ b/instrumentation/micrometer/micrometer-1.5/library/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/Bridging.java @@ -0,0 +1,63 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.micrometer.v1_5; + +import io.micrometer.core.instrument.Meter; +import io.micrometer.core.instrument.Statistic; +import io.micrometer.core.instrument.Tag; +import io.micrometer.core.instrument.config.NamingConvention; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.common.AttributesBuilder; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +final class Bridging { + + private static final ConcurrentMap descriptionsCache = new ConcurrentHashMap<>(); + + static Attributes tagsAsAttributes(Meter.Id id, NamingConvention namingConvention) { + Iterable tags = id.getTagsAsIterable(); + if (!tags.iterator().hasNext()) { + return Attributes.empty(); + } + AttributesBuilder builder = Attributes.builder(); + for (Tag tag : tags) { + String tagKey = namingConvention.tagKey(tag.getKey()); + String tagValue = namingConvention.tagValue(tag.getValue()); + builder.put(tagKey, tagValue); + } + return builder.build(); + } + + static String name(Meter.Id id, NamingConvention namingConvention) { + return namingConvention.name(id.getName(), id.getType(), id.getBaseUnit()); + } + + static String description(Meter.Id id) { + return descriptionsCache.computeIfAbsent( + id.getName(), + n -> { + String description = id.getDescription(); + return description != null ? description : ""; + }); + } + + static String baseUnit(Meter.Id id) { + String baseUnit = id.getBaseUnit(); + return baseUnit == null ? "1" : baseUnit; + } + + static String statisticInstrumentName( + Meter.Id id, Statistic statistic, NamingConvention namingConvention) { + String prefix = id.getName() + "."; + // use "total_time" instead of "total" to avoid clashing with Statistic.TOTAL + String statisticStr = + statistic == Statistic.TOTAL_TIME ? "total_time" : statistic.getTagValueRepresentation(); + return namingConvention.name(prefix + statisticStr, id.getType(), id.getBaseUnit()); + } + + private Bridging() {} +} diff --git a/instrumentation/micrometer/micrometer-1.5/library/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/DoubleMeasurementRecorder.java b/instrumentation/micrometer/micrometer-1.5/library/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/DoubleMeasurementRecorder.java new file mode 100644 index 000000000000..5eddf0473a5c --- /dev/null +++ b/instrumentation/micrometer/micrometer-1.5/library/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/DoubleMeasurementRecorder.java @@ -0,0 +1,38 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.micrometer.v1_5; + +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.metrics.ObservableDoubleMeasurement; +import java.lang.ref.WeakReference; +import java.util.function.Consumer; +import java.util.function.ToDoubleFunction; +import javax.annotation.Nullable; + +final class DoubleMeasurementRecorder implements Consumer { + + // using a weak reference here so that the existence of the micrometer Meter does not block the + // measured object from being GC'd; e.g. a Gauge (or any other async instrument) must not block + // garbage collection of the object that it measures + private final WeakReference objWeakRef; + private final ToDoubleFunction metricFunction; + private final Attributes attributes; + + DoubleMeasurementRecorder( + @Nullable T obj, ToDoubleFunction metricFunction, Attributes attributes) { + this.objWeakRef = new WeakReference<>(obj); + this.metricFunction = metricFunction; + this.attributes = attributes; + } + + @Override + public void accept(ObservableDoubleMeasurement measurement) { + T obj = objWeakRef.get(); + if (obj != null) { + measurement.record(metricFunction.applyAsDouble(obj), attributes); + } + } +} diff --git a/instrumentation/micrometer/micrometer-1.5/library/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/LongMeasurementRecorder.java b/instrumentation/micrometer/micrometer-1.5/library/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/LongMeasurementRecorder.java new file mode 100644 index 000000000000..75ecd5f031c4 --- /dev/null +++ b/instrumentation/micrometer/micrometer-1.5/library/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/LongMeasurementRecorder.java @@ -0,0 +1,38 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.micrometer.v1_5; + +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.metrics.ObservableLongMeasurement; +import java.lang.ref.WeakReference; +import java.util.function.Consumer; +import java.util.function.ToLongFunction; +import javax.annotation.Nullable; + +final class LongMeasurementRecorder implements Consumer { + + // using a weak reference here so that the existence of the micrometer Meter does not block the + // measured object from being GC'd; e.g. a Gauge (or any other async instrument) must not block + // garbage collection of the object that it measures + private final WeakReference objWeakRef; + private final ToLongFunction metricFunction; + private final Attributes attributes; + + LongMeasurementRecorder( + @Nullable T obj, ToLongFunction metricFunction, Attributes attributes) { + this.objWeakRef = new WeakReference<>(obj); + this.metricFunction = metricFunction; + this.attributes = attributes; + } + + @Override + public void accept(ObservableLongMeasurement measurement) { + T obj = objWeakRef.get(); + if (obj != null) { + measurement.record(metricFunction.applyAsLong(obj), attributes); + } + } +} diff --git a/instrumentation/micrometer/micrometer-1.5/library/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/OpenTelemetryCounter.java b/instrumentation/micrometer/micrometer-1.5/library/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/OpenTelemetryCounter.java new file mode 100644 index 000000000000..a21ab4b6a9f4 --- /dev/null +++ b/instrumentation/micrometer/micrometer-1.5/library/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/OpenTelemetryCounter.java @@ -0,0 +1,67 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.micrometer.v1_5; + +import static io.opentelemetry.instrumentation.micrometer.v1_5.Bridging.baseUnit; +import static io.opentelemetry.instrumentation.micrometer.v1_5.Bridging.name; +import static io.opentelemetry.instrumentation.micrometer.v1_5.Bridging.tagsAsAttributes; + +import io.micrometer.core.instrument.AbstractMeter; +import io.micrometer.core.instrument.Counter; +import io.micrometer.core.instrument.Measurement; +import io.micrometer.core.instrument.config.NamingConvention; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.metrics.DoubleCounter; +import io.opentelemetry.api.metrics.Meter; +import java.util.Collections; + +final class OpenTelemetryCounter extends AbstractMeter implements Counter, RemovableMeter { + + // TODO: use bound instruments when they're available + private final DoubleCounter otelCounter; + private final Attributes attributes; + + private volatile boolean removed = false; + + OpenTelemetryCounter(Id id, NamingConvention namingConvention, Meter otelMeter) { + super(id); + + this.attributes = tagsAsAttributes(id, namingConvention); + String conventionName = name(id, namingConvention); + this.otelCounter = + otelMeter + .counterBuilder(conventionName) + .setDescription(Bridging.description(id)) + .setUnit(baseUnit(id)) + .ofDoubles() + .build(); + } + + @Override + public void increment(double v) { + if (removed) { + return; + } + otelCounter.add(v, attributes); + } + + @Override + public double count() { + UnsupportedReadLogger.logWarning(); + return Double.NaN; + } + + @Override + public Iterable measure() { + UnsupportedReadLogger.logWarning(); + return Collections.emptyList(); + } + + @Override + public void onRemove() { + removed = true; + } +} diff --git a/instrumentation/micrometer/micrometer-1.5/library/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/OpenTelemetryDistributionSummary.java b/instrumentation/micrometer/micrometer-1.5/library/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/OpenTelemetryDistributionSummary.java new file mode 100644 index 000000000000..5b58713b8670 --- /dev/null +++ b/instrumentation/micrometer/micrometer-1.5/library/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/OpenTelemetryDistributionSummary.java @@ -0,0 +1,165 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.micrometer.v1_5; + +import static io.opentelemetry.instrumentation.micrometer.v1_5.Bridging.baseUnit; +import static io.opentelemetry.instrumentation.micrometer.v1_5.Bridging.name; +import static io.opentelemetry.instrumentation.micrometer.v1_5.Bridging.tagsAsAttributes; + +import io.micrometer.core.instrument.AbstractDistributionSummary; +import io.micrometer.core.instrument.Clock; +import io.micrometer.core.instrument.Measurement; +import io.micrometer.core.instrument.config.NamingConvention; +import io.micrometer.core.instrument.distribution.DistributionStatisticConfig; +import io.micrometer.core.instrument.distribution.NoopHistogram; +import io.micrometer.core.instrument.distribution.TimeWindowMax; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.metrics.DoubleHistogram; +import io.opentelemetry.api.metrics.Meter; +import io.opentelemetry.api.metrics.ObservableDoubleGauge; +import java.util.Collections; +import java.util.concurrent.atomic.DoubleAdder; +import java.util.concurrent.atomic.LongAdder; + +final class OpenTelemetryDistributionSummary extends AbstractDistributionSummary + implements RemovableMeter { + + private final Measurements measurements; + private final TimeWindowMax max; + // TODO: use bound instruments when they're available + private final DoubleHistogram otelHistogram; + private final Attributes attributes; + private final ObservableDoubleGauge observableMax; + + private volatile boolean removed = false; + + OpenTelemetryDistributionSummary( + Id id, + NamingConvention namingConvention, + Clock clock, + DistributionStatisticConfig distributionStatisticConfig, + double scale, + Meter otelMeter) { + super(id, clock, distributionStatisticConfig, scale, false); + + if (isUsingMicrometerHistograms()) { + measurements = new MicrometerHistogramMeasurements(); + } else { + measurements = NoopMeasurements.INSTANCE; + } + max = new TimeWindowMax(clock, distributionStatisticConfig); + + this.attributes = tagsAsAttributes(id, namingConvention); + + String name = name(id, namingConvention); + this.otelHistogram = + otelMeter + .histogramBuilder(name) + .setDescription(Bridging.description(id)) + .setUnit(baseUnit(id)) + .build(); + this.observableMax = + otelMeter + .gaugeBuilder(name + ".max") + .setDescription(Bridging.description(id)) + .setUnit(baseUnit(id)) + .buildWithCallback( + new DoubleMeasurementRecorder<>(max, TimeWindowMax::poll, attributes)); + } + + boolean isUsingMicrometerHistograms() { + return histogram != NoopHistogram.INSTANCE; + } + + @Override + protected void recordNonNegative(double amount) { + if (!removed) { + otelHistogram.record(amount, attributes); + measurements.record(amount); + max.record(amount); + } + } + + @Override + public long count() { + return measurements.count(); + } + + @Override + public double totalAmount() { + return measurements.totalAmount(); + } + + @Override + public double max() { + return max.poll(); + } + + @Override + public Iterable measure() { + UnsupportedReadLogger.logWarning(); + return Collections.emptyList(); + } + + @Override + public void onRemove() { + removed = true; + observableMax.close(); + } + + private interface Measurements { + void record(double amount); + + long count(); + + double totalAmount(); + } + + // if micrometer histograms are not being used then there's no need to keep any local state + // OpenTelemetry metrics bridge does not support reading measurements + enum NoopMeasurements implements Measurements { + INSTANCE; + + @Override + public void record(double amount) {} + + @Override + public long count() { + UnsupportedReadLogger.logWarning(); + return 0; + } + + @Override + public double totalAmount() { + UnsupportedReadLogger.logWarning(); + return Double.NaN; + } + } + + // calculate count and totalAmount value for the use of micrometer histograms + // kinda similar to how DropwizardDistributionSummary does that + private static final class MicrometerHistogramMeasurements implements Measurements { + + private final LongAdder count = new LongAdder(); + private final DoubleAdder totalAmount = new DoubleAdder(); + + @Override + public void record(double amount) { + count.increment(); + totalAmount.add(amount); + } + + @Override + public long count() { + return count.sum(); + } + + @Override + public double totalAmount() { + return totalAmount.sum(); + } + } +} diff --git a/instrumentation/micrometer/micrometer-1.5/library/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/OpenTelemetryFunctionCounter.java b/instrumentation/micrometer/micrometer-1.5/library/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/OpenTelemetryFunctionCounter.java new file mode 100644 index 000000000000..352009c3a5b3 --- /dev/null +++ b/instrumentation/micrometer/micrometer-1.5/library/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/OpenTelemetryFunctionCounter.java @@ -0,0 +1,62 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.micrometer.v1_5; + +import static io.opentelemetry.instrumentation.micrometer.v1_5.Bridging.baseUnit; +import static io.opentelemetry.instrumentation.micrometer.v1_5.Bridging.name; +import static io.opentelemetry.instrumentation.micrometer.v1_5.Bridging.tagsAsAttributes; + +import io.micrometer.core.instrument.AbstractMeter; +import io.micrometer.core.instrument.FunctionCounter; +import io.micrometer.core.instrument.Measurement; +import io.micrometer.core.instrument.config.NamingConvention; +import io.opentelemetry.api.metrics.Meter; +import io.opentelemetry.api.metrics.ObservableDoubleCounter; +import java.util.Collections; +import java.util.function.ToDoubleFunction; + +final class OpenTelemetryFunctionCounter extends AbstractMeter + implements FunctionCounter, RemovableMeter { + + private final ObservableDoubleCounter observableCount; + + OpenTelemetryFunctionCounter( + Id id, + NamingConvention namingConvention, + T obj, + ToDoubleFunction countFunction, + Meter otelMeter) { + super(id); + + String name = name(id, namingConvention); + observableCount = + otelMeter + .counterBuilder(name) + .ofDoubles() + .setDescription(Bridging.description(id)) + .setUnit(baseUnit(id)) + .buildWithCallback( + new DoubleMeasurementRecorder<>( + obj, countFunction, tagsAsAttributes(id, namingConvention))); + } + + @Override + public double count() { + UnsupportedReadLogger.logWarning(); + return Double.NaN; + } + + @Override + public Iterable measure() { + UnsupportedReadLogger.logWarning(); + return Collections.emptyList(); + } + + @Override + public void onRemove() { + observableCount.close(); + } +} diff --git a/instrumentation/micrometer/micrometer-1.5/library/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/OpenTelemetryFunctionTimer.java b/instrumentation/micrometer/micrometer-1.5/library/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/OpenTelemetryFunctionTimer.java new file mode 100644 index 000000000000..91fb153f577a --- /dev/null +++ b/instrumentation/micrometer/micrometer-1.5/library/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/OpenTelemetryFunctionTimer.java @@ -0,0 +1,101 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.micrometer.v1_5; + +import io.micrometer.core.instrument.AbstractMeter; +import io.micrometer.core.instrument.FunctionTimer; +import io.micrometer.core.instrument.Measurement; +import io.micrometer.core.instrument.config.NamingConvention; +import io.micrometer.core.instrument.util.TimeUtils; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.metrics.Meter; +import io.opentelemetry.api.metrics.ObservableDoubleCounter; +import io.opentelemetry.api.metrics.ObservableLongCounter; +import java.util.Collections; +import java.util.concurrent.TimeUnit; +import java.util.function.ToDoubleFunction; +import java.util.function.ToLongFunction; + +final class OpenTelemetryFunctionTimer extends AbstractMeter + implements FunctionTimer, RemovableMeter { + private final TimeUnit baseTimeUnit; + private final ObservableLongCounter observableCount; + private final ObservableDoubleCounter observableTotalTime; + + OpenTelemetryFunctionTimer( + Id id, + NamingConvention namingConvention, + T obj, + ToLongFunction countFunction, + ToDoubleFunction totalTimeFunction, + TimeUnit totalTimeFunctionUnit, + TimeUnit baseTimeUnit, + Meter otelMeter) { + super(id); + this.baseTimeUnit = baseTimeUnit; + + String name = Bridging.name(id, namingConvention); + Attributes attributes = Bridging.tagsAsAttributes(id, namingConvention); + + this.observableCount = + otelMeter + .counterBuilder(name + ".count") + .setDescription(Bridging.description(id)) + .setUnit("1") + .buildWithCallback(new LongMeasurementRecorder<>(obj, countFunction, attributes)); + + this.observableTotalTime = + otelMeter + .counterBuilder(name + ".sum") + .ofDoubles() + .setDescription(Bridging.description(id)) + .setUnit(TimeUnitHelper.getUnitString(baseTimeUnit)) + .buildWithCallback( + new DoubleMeasurementRecorder<>( + obj, + val -> + TimeUtils.convert( + totalTimeFunction.applyAsDouble(val), + totalTimeFunctionUnit, + baseTimeUnit), + attributes)); + } + + @Override + public double count() { + UnsupportedReadLogger.logWarning(); + return Double.NaN; + } + + @Override + public double totalTime(TimeUnit unit) { + UnsupportedReadLogger.logWarning(); + return Double.NaN; + } + + @Override + public double mean(TimeUnit unit) { + UnsupportedReadLogger.logWarning(); + return Double.NaN; + } + + @Override + public TimeUnit baseTimeUnit() { + return baseTimeUnit; + } + + @Override + public Iterable measure() { + UnsupportedReadLogger.logWarning(); + return Collections.emptyList(); + } + + @Override + public void onRemove() { + observableCount.close(); + observableTotalTime.close(); + } +} diff --git a/instrumentation/micrometer/micrometer-1.5/library/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/OpenTelemetryGauge.java b/instrumentation/micrometer/micrometer-1.5/library/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/OpenTelemetryGauge.java new file mode 100644 index 000000000000..ce9efa803aca --- /dev/null +++ b/instrumentation/micrometer/micrometer-1.5/library/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/OpenTelemetryGauge.java @@ -0,0 +1,61 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.micrometer.v1_5; + +import static io.opentelemetry.instrumentation.micrometer.v1_5.Bridging.baseUnit; +import static io.opentelemetry.instrumentation.micrometer.v1_5.Bridging.name; +import static io.opentelemetry.instrumentation.micrometer.v1_5.Bridging.tagsAsAttributes; + +import io.micrometer.core.instrument.AbstractMeter; +import io.micrometer.core.instrument.Gauge; +import io.micrometer.core.instrument.Measurement; +import io.micrometer.core.instrument.config.NamingConvention; +import io.opentelemetry.api.metrics.Meter; +import io.opentelemetry.api.metrics.ObservableDoubleGauge; +import java.util.Collections; +import java.util.function.ToDoubleFunction; +import javax.annotation.Nullable; + +final class OpenTelemetryGauge extends AbstractMeter implements Gauge, RemovableMeter { + + private final ObservableDoubleGauge observableGauge; + + OpenTelemetryGauge( + Id id, + NamingConvention namingConvention, + @Nullable T obj, + ToDoubleFunction objMetric, + Meter otelMeter) { + super(id); + + String name = name(id, namingConvention); + observableGauge = + otelMeter + .gaugeBuilder(name) + .setDescription(Bridging.description(id)) + .setUnit(baseUnit(id)) + .buildWithCallback( + new DoubleMeasurementRecorder<>( + obj, objMetric, tagsAsAttributes(id, namingConvention))); + } + + @Override + public double value() { + UnsupportedReadLogger.logWarning(); + return Double.NaN; + } + + @Override + public Iterable measure() { + UnsupportedReadLogger.logWarning(); + return Collections.emptyList(); + } + + @Override + public void onRemove() { + observableGauge.close(); + } +} diff --git a/instrumentation/micrometer/micrometer-1.5/library/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/OpenTelemetryLongTaskTimer.java b/instrumentation/micrometer/micrometer-1.5/library/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/OpenTelemetryLongTaskTimer.java new file mode 100644 index 000000000000..ed4d1e1dec16 --- /dev/null +++ b/instrumentation/micrometer/micrometer-1.5/library/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/OpenTelemetryLongTaskTimer.java @@ -0,0 +1,77 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.micrometer.v1_5; + +import static io.opentelemetry.instrumentation.micrometer.v1_5.Bridging.name; +import static io.opentelemetry.instrumentation.micrometer.v1_5.Bridging.tagsAsAttributes; + +import io.micrometer.core.instrument.Clock; +import io.micrometer.core.instrument.Measurement; +import io.micrometer.core.instrument.config.NamingConvention; +import io.micrometer.core.instrument.distribution.DistributionStatisticConfig; +import io.micrometer.core.instrument.internal.DefaultLongTaskTimer; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.metrics.Meter; +import io.opentelemetry.api.metrics.ObservableDoubleUpDownCounter; +import io.opentelemetry.api.metrics.ObservableLongUpDownCounter; +import java.util.Collections; +import java.util.concurrent.TimeUnit; + +final class OpenTelemetryLongTaskTimer extends DefaultLongTaskTimer implements RemovableMeter { + + private final DistributionStatisticConfig distributionStatisticConfig; + private final ObservableLongUpDownCounter observableActiveTasks; + private final ObservableDoubleUpDownCounter observableDuration; + + OpenTelemetryLongTaskTimer( + Id id, + NamingConvention namingConvention, + Clock clock, + TimeUnit baseTimeUnit, + DistributionStatisticConfig distributionStatisticConfig, + Meter otelMeter) { + super(id, clock, baseTimeUnit, distributionStatisticConfig, false); + + this.distributionStatisticConfig = distributionStatisticConfig; + + String name = name(id, namingConvention); + Attributes attributes = tagsAsAttributes(id, namingConvention); + + this.observableActiveTasks = + otelMeter + .upDownCounterBuilder(name + ".active") + .setDescription(Bridging.description(id)) + .setUnit("{tasks}") + .buildWithCallback( + new LongMeasurementRecorder<>(this, DefaultLongTaskTimer::activeTasks, attributes)); + this.observableDuration = + otelMeter + .upDownCounterBuilder(name + ".duration") + .ofDoubles() + .setDescription(Bridging.description(id)) + .setUnit(TimeUnitHelper.getUnitString(baseTimeUnit)) + .buildWithCallback( + new DoubleMeasurementRecorder<>( + this, t -> t.duration(t.baseTimeUnit()), attributes)); + } + + @Override + public Iterable measure() { + UnsupportedReadLogger.logWarning(); + return Collections.emptyList(); + } + + @Override + public void onRemove() { + observableActiveTasks.close(); + observableDuration.close(); + } + + boolean isUsingMicrometerHistograms() { + return distributionStatisticConfig.isPublishingPercentiles() + || distributionStatisticConfig.isPublishingHistogram(); + } +} diff --git a/instrumentation/micrometer/micrometer-1.5/library/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/OpenTelemetryMeter.java b/instrumentation/micrometer/micrometer-1.5/library/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/OpenTelemetryMeter.java new file mode 100644 index 000000000000..93a0bf07581d --- /dev/null +++ b/instrumentation/micrometer/micrometer-1.5/library/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/OpenTelemetryMeter.java @@ -0,0 +1,98 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.micrometer.v1_5; + +import static io.opentelemetry.instrumentation.micrometer.v1_5.Bridging.baseUnit; +import static io.opentelemetry.instrumentation.micrometer.v1_5.Bridging.statisticInstrumentName; +import static io.opentelemetry.instrumentation.micrometer.v1_5.Bridging.tagsAsAttributes; + +import io.micrometer.core.instrument.AbstractMeter; +import io.micrometer.core.instrument.Measurement; +import io.micrometer.core.instrument.Meter; +import io.micrometer.core.instrument.config.NamingConvention; +import io.opentelemetry.api.common.Attributes; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +final class OpenTelemetryMeter extends AbstractMeter implements Meter, RemovableMeter { + + private final List observableInstruments; + + OpenTelemetryMeter( + Id id, + NamingConvention namingConvention, + Iterable measurements, + io.opentelemetry.api.metrics.Meter otelMeter) { + super(id); + Attributes attributes = tagsAsAttributes(id, namingConvention); + + List observableInstruments = new ArrayList<>(); + for (Measurement measurement : measurements) { + String name = statisticInstrumentName(id, measurement.getStatistic(), namingConvention); + String description = Bridging.description(id); + String baseUnit = baseUnit(id); + DoubleMeasurementRecorder callback = + new DoubleMeasurementRecorder<>(measurement, Measurement::getValue, attributes); + + switch (measurement.getStatistic()) { + case TOTAL: + // fall through + case TOTAL_TIME: + case COUNT: + observableInstruments.add( + otelMeter + .counterBuilder(name) + .ofDoubles() + .setDescription(description) + .setUnit(baseUnit) + .buildWithCallback(callback)); + break; + + case ACTIVE_TASKS: + observableInstruments.add( + otelMeter + .upDownCounterBuilder(name) + .ofDoubles() + .setDescription(description) + .setUnit(baseUnit) + .buildWithCallback(callback)); + break; + + case DURATION: + // fall through + case MAX: + case VALUE: + case UNKNOWN: + observableInstruments.add( + otelMeter + .gaugeBuilder(name) + .setDescription(description) + .setUnit(baseUnit) + .buildWithCallback(callback)); + break; + } + } + this.observableInstruments = observableInstruments; + } + + @Override + public Iterable measure() { + UnsupportedReadLogger.logWarning(); + return Collections.emptyList(); + } + + @Override + public void onRemove() { + try { + for (AutoCloseable observableInstrument : observableInstruments) { + observableInstrument.close(); + } + } catch (Exception e) { + throw new IllegalStateException("SDK instruments should never throw on close()", e); + } + } +} diff --git a/instrumentation/micrometer/micrometer-1.5/library/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/OpenTelemetryMeterRegistry.java b/instrumentation/micrometer/micrometer-1.5/library/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/OpenTelemetryMeterRegistry.java new file mode 100644 index 000000000000..5c4e5e342cb8 --- /dev/null +++ b/instrumentation/micrometer/micrometer-1.5/library/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/OpenTelemetryMeterRegistry.java @@ -0,0 +1,173 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.micrometer.v1_5; + +import io.micrometer.core.instrument.Clock; +import io.micrometer.core.instrument.Counter; +import io.micrometer.core.instrument.DistributionSummary; +import io.micrometer.core.instrument.FunctionCounter; +import io.micrometer.core.instrument.FunctionTimer; +import io.micrometer.core.instrument.Gauge; +import io.micrometer.core.instrument.LongTaskTimer; +import io.micrometer.core.instrument.Measurement; +import io.micrometer.core.instrument.Meter; +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.Timer; +import io.micrometer.core.instrument.config.NamingConvention; +import io.micrometer.core.instrument.distribution.DistributionStatisticConfig; +import io.micrometer.core.instrument.distribution.HistogramGauges; +import io.micrometer.core.instrument.distribution.pause.PauseDetector; +import io.opentelemetry.api.OpenTelemetry; +import java.util.concurrent.TimeUnit; +import java.util.function.ToDoubleFunction; +import java.util.function.ToLongFunction; +import javax.annotation.Nullable; + +/** + * A {@link MeterRegistry} implementation that forwards all the captured metrics to the {@linkplain + * io.opentelemetry.api.metrics.Meter OpenTelemetry Meter} obtained from the passed {@link + * OpenTelemetry} instance. + */ +public final class OpenTelemetryMeterRegistry extends MeterRegistry { + + /** + * Returns a new {@link OpenTelemetryMeterRegistry} configured with the given {@link + * OpenTelemetry}. + */ + public static MeterRegistry create(OpenTelemetry openTelemetry) { + return builder(openTelemetry).build(); + } + + /** + * Returns a new {@link OpenTelemetryMeterRegistryBuilder} configured with the given {@link + * OpenTelemetry}. + */ + public static OpenTelemetryMeterRegistryBuilder builder(OpenTelemetry openTelemetry) { + return new OpenTelemetryMeterRegistryBuilder(openTelemetry); + } + + private final TimeUnit baseTimeUnit; + private final io.opentelemetry.api.metrics.Meter otelMeter; + + OpenTelemetryMeterRegistry( + Clock clock, + TimeUnit baseTimeUnit, + NamingConvention namingConvention, + io.opentelemetry.api.metrics.Meter otelMeter) { + super(clock); + this.baseTimeUnit = baseTimeUnit; + this.otelMeter = otelMeter; + + this.config() + .namingConvention(namingConvention) + .onMeterRemoved(OpenTelemetryMeterRegistry::onMeterRemoved); + } + + @Override + protected Gauge newGauge(Meter.Id id, @Nullable T obj, ToDoubleFunction valueFunction) { + return new OpenTelemetryGauge<>(id, config().namingConvention(), obj, valueFunction, otelMeter); + } + + @Override + protected Counter newCounter(Meter.Id id) { + return new OpenTelemetryCounter(id, config().namingConvention(), otelMeter); + } + + @Override + protected LongTaskTimer newLongTaskTimer( + Meter.Id id, DistributionStatisticConfig distributionStatisticConfig) { + OpenTelemetryLongTaskTimer timer = + new OpenTelemetryLongTaskTimer( + id, + config().namingConvention(), + clock, + getBaseTimeUnit(), + distributionStatisticConfig, + otelMeter); + if (timer.isUsingMicrometerHistograms()) { + HistogramGauges.registerWithCommonFormat(timer, this); + } + return timer; + } + + @Override + protected Timer newTimer( + Meter.Id id, + DistributionStatisticConfig distributionStatisticConfig, + PauseDetector pauseDetector) { + OpenTelemetryTimer timer = + new OpenTelemetryTimer( + id, + config().namingConvention(), + clock, + distributionStatisticConfig, + pauseDetector, + getBaseTimeUnit(), + otelMeter); + if (timer.isUsingMicrometerHistograms()) { + HistogramGauges.registerWithCommonFormat(timer, this); + } + return timer; + } + + @Override + protected DistributionSummary newDistributionSummary( + Meter.Id id, DistributionStatisticConfig distributionStatisticConfig, double scale) { + OpenTelemetryDistributionSummary distributionSummary = + new OpenTelemetryDistributionSummary( + id, config().namingConvention(), clock, distributionStatisticConfig, scale, otelMeter); + if (distributionSummary.isUsingMicrometerHistograms()) { + HistogramGauges.registerWithCommonFormat(distributionSummary, this); + } + return distributionSummary; + } + + @Override + protected Meter newMeter(Meter.Id id, Meter.Type type, Iterable measurements) { + return new OpenTelemetryMeter(id, config().namingConvention(), measurements, otelMeter); + } + + @Override + protected FunctionTimer newFunctionTimer( + Meter.Id id, + T obj, + ToLongFunction countFunction, + ToDoubleFunction totalTimeFunction, + TimeUnit totalTimeFunctionUnit) { + return new OpenTelemetryFunctionTimer<>( + id, + config().namingConvention(), + obj, + countFunction, + totalTimeFunction, + totalTimeFunctionUnit, + getBaseTimeUnit(), + otelMeter); + } + + @Override + protected FunctionCounter newFunctionCounter( + Meter.Id id, T obj, ToDoubleFunction countFunction) { + return new OpenTelemetryFunctionCounter<>( + id, config().namingConvention(), obj, countFunction, otelMeter); + } + + @Override + protected TimeUnit getBaseTimeUnit() { + return baseTimeUnit; + } + + @Override + protected DistributionStatisticConfig defaultHistogramConfig() { + return DistributionStatisticConfig.DEFAULT; + } + + private static void onMeterRemoved(Meter meter) { + if (meter instanceof RemovableMeter) { + ((RemovableMeter) meter).onRemove(); + } + } +} diff --git a/instrumentation/micrometer/micrometer-1.5/library/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/OpenTelemetryMeterRegistryBuilder.java b/instrumentation/micrometer/micrometer-1.5/library/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/OpenTelemetryMeterRegistryBuilder.java new file mode 100644 index 000000000000..7cb896f977e6 --- /dev/null +++ b/instrumentation/micrometer/micrometer-1.5/library/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/OpenTelemetryMeterRegistryBuilder.java @@ -0,0 +1,73 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.micrometer.v1_5; + +import com.google.errorprone.annotations.CanIgnoreReturnValue; +import io.micrometer.core.instrument.Clock; +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.config.NamingConvention; +import io.opentelemetry.api.OpenTelemetry; +import java.util.concurrent.TimeUnit; + +/** A builder of {@link OpenTelemetryMeterRegistry}. */ +public final class OpenTelemetryMeterRegistryBuilder { + + // Visible for testing + private static final String INSTRUMENTATION_NAME = "io.opentelemetry.micrometer-1.5"; + + private final OpenTelemetry openTelemetry; + private Clock clock = Clock.SYSTEM; + private TimeUnit baseTimeUnit = TimeUnit.MILLISECONDS; + private boolean prometheusMode = false; + + OpenTelemetryMeterRegistryBuilder(OpenTelemetry openTelemetry) { + this.openTelemetry = openTelemetry; + } + + /** Sets a custom {@link Clock}. Useful for testing. */ + @CanIgnoreReturnValue + public OpenTelemetryMeterRegistryBuilder setClock(Clock clock) { + this.clock = clock; + return this; + } + + /** Sets the base time unit. */ + @CanIgnoreReturnValue + public OpenTelemetryMeterRegistryBuilder setBaseTimeUnit(TimeUnit baseTimeUnit) { + this.baseTimeUnit = baseTimeUnit; + return this; + } + + /** + * Enables the "Prometheus mode" - this will simulate the behavior of Micrometer's {@code + * PrometheusMeterRegistry}. The instruments will be renamed to match Micrometer instrument + * naming, and the base time unit will be set to seconds. + * + *

Set this to {@code true} if you are using the Prometheus metrics exporter. + */ + @CanIgnoreReturnValue + public OpenTelemetryMeterRegistryBuilder setPrometheusMode(boolean prometheusMode) { + this.prometheusMode = prometheusMode; + return this; + } + + /** + * Returns a new {@link OpenTelemetryMeterRegistry} with the settings of this {@link + * OpenTelemetryMeterRegistryBuilder}. + */ + public MeterRegistry build() { + // prometheus mode overrides any unit settings with SECONDS + TimeUnit baseTimeUnit = prometheusMode ? TimeUnit.SECONDS : this.baseTimeUnit; + NamingConvention namingConvention = + prometheusMode ? PrometheusModeNamingConvention.INSTANCE : NamingConvention.identity; + + return new OpenTelemetryMeterRegistry( + clock, + baseTimeUnit, + namingConvention, + openTelemetry.getMeterProvider().get(INSTRUMENTATION_NAME)); + } +} diff --git a/instrumentation/micrometer/micrometer-1.5/library/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/OpenTelemetryTimer.java b/instrumentation/micrometer/micrometer-1.5/library/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/OpenTelemetryTimer.java new file mode 100644 index 000000000000..86df5008a8f9 --- /dev/null +++ b/instrumentation/micrometer/micrometer-1.5/library/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/OpenTelemetryTimer.java @@ -0,0 +1,171 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.micrometer.v1_5; + +import static io.opentelemetry.instrumentation.micrometer.v1_5.Bridging.name; +import static io.opentelemetry.instrumentation.micrometer.v1_5.Bridging.tagsAsAttributes; + +import io.micrometer.core.instrument.AbstractTimer; +import io.micrometer.core.instrument.Clock; +import io.micrometer.core.instrument.Measurement; +import io.micrometer.core.instrument.config.NamingConvention; +import io.micrometer.core.instrument.distribution.DistributionStatisticConfig; +import io.micrometer.core.instrument.distribution.NoopHistogram; +import io.micrometer.core.instrument.distribution.TimeWindowMax; +import io.micrometer.core.instrument.distribution.pause.PauseDetector; +import io.micrometer.core.instrument.util.TimeUtils; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.metrics.DoubleHistogram; +import io.opentelemetry.api.metrics.Meter; +import io.opentelemetry.api.metrics.ObservableDoubleGauge; +import java.util.Collections; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.DoubleAdder; +import java.util.concurrent.atomic.LongAdder; + +final class OpenTelemetryTimer extends AbstractTimer implements RemovableMeter { + + private final Measurements measurements; + private final TimeWindowMax max; + private final TimeUnit baseTimeUnit; + // TODO: use bound instruments when they're available + private final DoubleHistogram otelHistogram; + private final Attributes attributes; + private final ObservableDoubleGauge observableMax; + + private volatile boolean removed = false; + + OpenTelemetryTimer( + Id id, + NamingConvention namingConvention, + Clock clock, + DistributionStatisticConfig distributionStatisticConfig, + PauseDetector pauseDetector, + TimeUnit baseTimeUnit, + Meter otelMeter) { + super(id, clock, distributionStatisticConfig, pauseDetector, TimeUnit.MILLISECONDS, false); + + if (isUsingMicrometerHistograms()) { + measurements = new MicrometerHistogramMeasurements(); + } else { + measurements = NoopMeasurements.INSTANCE; + } + max = new TimeWindowMax(clock, distributionStatisticConfig); + + this.baseTimeUnit = baseTimeUnit; + this.attributes = tagsAsAttributes(id, namingConvention); + + String name = name(id, namingConvention); + this.otelHistogram = + otelMeter + .histogramBuilder(name) + .setDescription(Bridging.description(id)) + .setUnit(TimeUnitHelper.getUnitString(baseTimeUnit)) + .build(); + this.observableMax = + otelMeter + .gaugeBuilder(name + ".max") + .setDescription(Bridging.description(id)) + .setUnit(TimeUnitHelper.getUnitString(baseTimeUnit)) + .buildWithCallback( + new DoubleMeasurementRecorder<>(max, m -> m.poll(baseTimeUnit), attributes)); + } + + boolean isUsingMicrometerHistograms() { + return histogram != NoopHistogram.INSTANCE; + } + + @Override + protected void recordNonNegative(long amount, TimeUnit unit) { + if (!removed) { + double nanos = (double) unit.toNanos(amount); + double time = TimeUtils.nanosToUnit(nanos, baseTimeUnit); + otelHistogram.record(time, attributes); + measurements.record(nanos); + max.record(nanos, TimeUnit.NANOSECONDS); + } + } + + @Override + public long count() { + return measurements.count(); + } + + @Override + public double totalTime(TimeUnit unit) { + return measurements.totalTime(unit); + } + + @Override + public double max(TimeUnit unit) { + return max.poll(unit); + } + + @Override + public Iterable measure() { + UnsupportedReadLogger.logWarning(); + return Collections.emptyList(); + } + + @Override + public void onRemove() { + removed = true; + observableMax.close(); + } + + private interface Measurements { + void record(double nanos); + + long count(); + + double totalTime(TimeUnit unit); + } + + // if micrometer histograms are not being used then there's no need to keep any local state + // OpenTelemetry metrics bridge does not support reading measurements + enum NoopMeasurements implements Measurements { + INSTANCE; + + @Override + public void record(double nanos) {} + + @Override + public long count() { + UnsupportedReadLogger.logWarning(); + return 0; + } + + @Override + public double totalTime(TimeUnit unit) { + UnsupportedReadLogger.logWarning(); + return Double.NaN; + } + } + + // calculate count and totalTime value for the use of micrometer histograms + // kinda similar to how DropwizardTimer does that + private static final class MicrometerHistogramMeasurements implements Measurements { + + private final LongAdder count = new LongAdder(); + private final DoubleAdder totalTime = new DoubleAdder(); + + @Override + public void record(double nanos) { + count.increment(); + totalTime.add(nanos); + } + + @Override + public long count() { + return count.sum(); + } + + @Override + public double totalTime(TimeUnit unit) { + return TimeUtils.nanosToUnit(totalTime.sum(), unit); + } + } +} diff --git a/instrumentation/micrometer/micrometer-1.5/library/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/PrometheusModeNamingConvention.java b/instrumentation/micrometer/micrometer-1.5/library/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/PrometheusModeNamingConvention.java new file mode 100644 index 000000000000..2a4143512756 --- /dev/null +++ b/instrumentation/micrometer/micrometer-1.5/library/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/PrometheusModeNamingConvention.java @@ -0,0 +1,36 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.micrometer.v1_5; + +import io.micrometer.core.instrument.Meter; +import io.micrometer.core.instrument.config.NamingConvention; +import javax.annotation.Nullable; + +// This naming strategy does not replace '.' with '_', and it does not append '_total' to counter +// names - the reason behind it is that this is already done by the Prometheus exporter; see the +// io.opentelemetry.exporter.prometheus.MetricAdapter class +enum PrometheusModeNamingConvention implements NamingConvention { + INSTANCE; + + @Override + public String name(String name, Meter.Type type, @Nullable String baseUnit) { + if (type == Meter.Type.COUNTER + || type == Meter.Type.DISTRIBUTION_SUMMARY + || type == Meter.Type.GAUGE) { + if (baseUnit != null && !name.endsWith("." + baseUnit)) { + name = name + "." + baseUnit; + } + } + + if (type == Meter.Type.LONG_TASK_TIMER || type == Meter.Type.TIMER) { + if (!name.endsWith(".seconds")) { + name = name + ".seconds"; + } + } + + return name; + } +} diff --git a/instrumentation/micrometer/micrometer-1.5/library/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/RemovableMeter.java b/instrumentation/micrometer/micrometer-1.5/library/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/RemovableMeter.java new file mode 100644 index 000000000000..37fde71110b2 --- /dev/null +++ b/instrumentation/micrometer/micrometer-1.5/library/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/RemovableMeter.java @@ -0,0 +1,11 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.micrometer.v1_5; + +interface RemovableMeter { + + void onRemove(); +} diff --git a/instrumentation/micrometer/micrometer-1.5/library/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/TimeUnitHelper.java b/instrumentation/micrometer/micrometer-1.5/library/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/TimeUnitHelper.java new file mode 100644 index 000000000000..326144520a18 --- /dev/null +++ b/instrumentation/micrometer/micrometer-1.5/library/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/TimeUnitHelper.java @@ -0,0 +1,33 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.micrometer.v1_5; + +import java.util.concurrent.TimeUnit; + +final class TimeUnitHelper { + + static String getUnitString(TimeUnit unit) { + switch (unit) { + case NANOSECONDS: + return "ns"; + case MICROSECONDS: + return "us"; + case MILLISECONDS: + return "ms"; + case SECONDS: + return "s"; + case MINUTES: + return "min"; + case HOURS: + return "h"; + case DAYS: + return "d"; + } + throw new IllegalStateException("Should not ever happen"); + } + + private TimeUnitHelper() {} +} diff --git a/instrumentation/micrometer/micrometer-1.5/library/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/UnsupportedReadLogger.java b/instrumentation/micrometer/micrometer-1.5/library/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/UnsupportedReadLogger.java new file mode 100644 index 000000000000..529f84ce74a5 --- /dev/null +++ b/instrumentation/micrometer/micrometer-1.5/library/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/UnsupportedReadLogger.java @@ -0,0 +1,23 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.micrometer.v1_5; + +import java.util.logging.Level; +import java.util.logging.Logger; + +final class UnsupportedReadLogger { + + static { + Logger logger = Logger.getLogger(OpenTelemetryMeterRegistry.class.getName()); + logger.log(Level.WARNING, "OpenTelemetry metrics bridge does not support reading measurements"); + } + + static void logWarning() { + // do nothing; the warning will be logged exactly once when this class is loaded + } + + private UnsupportedReadLogger() {} +} diff --git a/instrumentation/micrometer/micrometer-1.5/library/src/test/java/io/opentelemetry/instrumentation/micrometer/v1_5/CounterTest.java b/instrumentation/micrometer/micrometer-1.5/library/src/test/java/io/opentelemetry/instrumentation/micrometer/v1_5/CounterTest.java new file mode 100644 index 000000000000..c344bc3aa467 --- /dev/null +++ b/instrumentation/micrometer/micrometer-1.5/library/src/test/java/io/opentelemetry/instrumentation/micrometer/v1_5/CounterTest.java @@ -0,0 +1,25 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.micrometer.v1_5; + +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension; +import org.junit.jupiter.api.extension.RegisterExtension; + +class CounterTest extends AbstractCounterTest { + + @RegisterExtension + static final InstrumentationExtension testing = LibraryInstrumentationExtension.create(); + + @RegisterExtension + static final MicrometerTestingExtension micrometerExtension = + new MicrometerTestingExtension(testing); + + @Override + protected InstrumentationExtension testing() { + return testing; + } +} diff --git a/instrumentation/micrometer/micrometer-1.5/library/src/test/java/io/opentelemetry/instrumentation/micrometer/v1_5/DistributionSummaryTest.java b/instrumentation/micrometer/micrometer-1.5/library/src/test/java/io/opentelemetry/instrumentation/micrometer/v1_5/DistributionSummaryTest.java new file mode 100644 index 000000000000..1ba5b5d71bdb --- /dev/null +++ b/instrumentation/micrometer/micrometer-1.5/library/src/test/java/io/opentelemetry/instrumentation/micrometer/v1_5/DistributionSummaryTest.java @@ -0,0 +1,25 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.micrometer.v1_5; + +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension; +import org.junit.jupiter.api.extension.RegisterExtension; + +class DistributionSummaryTest extends AbstractDistributionSummaryTest { + + @RegisterExtension + static final InstrumentationExtension testing = LibraryInstrumentationExtension.create(); + + @RegisterExtension + static final MicrometerTestingExtension micrometerExtension = + new MicrometerTestingExtension(testing); + + @Override + protected InstrumentationExtension testing() { + return testing; + } +} diff --git a/instrumentation/micrometer/micrometer-1.5/library/src/test/java/io/opentelemetry/instrumentation/micrometer/v1_5/FunctionCounterTest.java b/instrumentation/micrometer/micrometer-1.5/library/src/test/java/io/opentelemetry/instrumentation/micrometer/v1_5/FunctionCounterTest.java new file mode 100644 index 000000000000..938679601290 --- /dev/null +++ b/instrumentation/micrometer/micrometer-1.5/library/src/test/java/io/opentelemetry/instrumentation/micrometer/v1_5/FunctionCounterTest.java @@ -0,0 +1,25 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.micrometer.v1_5; + +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension; +import org.junit.jupiter.api.extension.RegisterExtension; + +class FunctionCounterTest extends AbstractFunctionCounterTest { + + @RegisterExtension + static final InstrumentationExtension testing = LibraryInstrumentationExtension.create(); + + @RegisterExtension + static final MicrometerTestingExtension micrometerExtension = + new MicrometerTestingExtension(testing); + + @Override + protected InstrumentationExtension testing() { + return testing; + } +} diff --git a/instrumentation/micrometer/micrometer-1.5/library/src/test/java/io/opentelemetry/instrumentation/micrometer/v1_5/FunctionTimerSecondsTest.java b/instrumentation/micrometer/micrometer-1.5/library/src/test/java/io/opentelemetry/instrumentation/micrometer/v1_5/FunctionTimerSecondsTest.java new file mode 100644 index 000000000000..c418be394b87 --- /dev/null +++ b/instrumentation/micrometer/micrometer-1.5/library/src/test/java/io/opentelemetry/instrumentation/micrometer/v1_5/FunctionTimerSecondsTest.java @@ -0,0 +1,32 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.micrometer.v1_5; + +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension; +import java.util.concurrent.TimeUnit; +import org.junit.jupiter.api.extension.RegisterExtension; + +class FunctionTimerSecondsTest extends AbstractFunctionTimerSecondsTest { + + @RegisterExtension + static final InstrumentationExtension testing = LibraryInstrumentationExtension.create(); + + @RegisterExtension + static final MicrometerTestingExtension micrometerExtension = + new MicrometerTestingExtension(testing) { + @Override + OpenTelemetryMeterRegistryBuilder configureOtelRegistry( + OpenTelemetryMeterRegistryBuilder registry) { + return registry.setBaseTimeUnit(TimeUnit.SECONDS); + } + }; + + @Override + protected InstrumentationExtension testing() { + return testing; + } +} diff --git a/instrumentation/micrometer/micrometer-1.5/library/src/test/java/io/opentelemetry/instrumentation/micrometer/v1_5/FunctionTimerTest.java b/instrumentation/micrometer/micrometer-1.5/library/src/test/java/io/opentelemetry/instrumentation/micrometer/v1_5/FunctionTimerTest.java new file mode 100644 index 000000000000..5d924bfbc92b --- /dev/null +++ b/instrumentation/micrometer/micrometer-1.5/library/src/test/java/io/opentelemetry/instrumentation/micrometer/v1_5/FunctionTimerTest.java @@ -0,0 +1,25 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.micrometer.v1_5; + +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension; +import org.junit.jupiter.api.extension.RegisterExtension; + +class FunctionTimerTest extends AbstractFunctionTimerTest { + + @RegisterExtension + static final InstrumentationExtension testing = LibraryInstrumentationExtension.create(); + + @RegisterExtension + static final MicrometerTestingExtension micrometerExtension = + new MicrometerTestingExtension(testing); + + @Override + protected InstrumentationExtension testing() { + return testing; + } +} diff --git a/instrumentation/micrometer/micrometer-1.5/library/src/test/java/io/opentelemetry/instrumentation/micrometer/v1_5/GaugeTest.java b/instrumentation/micrometer/micrometer-1.5/library/src/test/java/io/opentelemetry/instrumentation/micrometer/v1_5/GaugeTest.java new file mode 100644 index 000000000000..fec02d5009b1 --- /dev/null +++ b/instrumentation/micrometer/micrometer-1.5/library/src/test/java/io/opentelemetry/instrumentation/micrometer/v1_5/GaugeTest.java @@ -0,0 +1,25 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.micrometer.v1_5; + +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension; +import org.junit.jupiter.api.extension.RegisterExtension; + +class GaugeTest extends AbstractGaugeTest { + + @RegisterExtension + static final InstrumentationExtension testing = LibraryInstrumentationExtension.create(); + + @RegisterExtension + static final MicrometerTestingExtension micrometerExtension = + new MicrometerTestingExtension(testing); + + @Override + protected InstrumentationExtension testing() { + return testing; + } +} diff --git a/instrumentation/micrometer/micrometer-1.5/library/src/test/java/io/opentelemetry/instrumentation/micrometer/v1_5/LongTaskTimerHistogramTest.java b/instrumentation/micrometer/micrometer-1.5/library/src/test/java/io/opentelemetry/instrumentation/micrometer/v1_5/LongTaskTimerHistogramTest.java new file mode 100644 index 000000000000..48500347fae9 --- /dev/null +++ b/instrumentation/micrometer/micrometer-1.5/library/src/test/java/io/opentelemetry/instrumentation/micrometer/v1_5/LongTaskTimerHistogramTest.java @@ -0,0 +1,39 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.micrometer.v1_5; + +import io.micrometer.core.instrument.MockClock; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension; +import org.junit.jupiter.api.extension.RegisterExtension; + +class LongTaskTimerHistogramTest extends AbstractLongTaskTimerHistogramTest { + + private static final MockClock clock = new MockClock(); + + @RegisterExtension + static final InstrumentationExtension testing = LibraryInstrumentationExtension.create(); + + @RegisterExtension + static final MicrometerTestingExtension micrometerExtension = + new MicrometerTestingExtension(testing) { + @Override + OpenTelemetryMeterRegistryBuilder configureOtelRegistry( + OpenTelemetryMeterRegistryBuilder registry) { + return registry.setClock(clock); + } + }; + + @Override + protected InstrumentationExtension testing() { + return testing; + } + + @Override + protected MockClock clock() { + return clock; + } +} diff --git a/instrumentation/micrometer/micrometer-1.5/library/src/test/java/io/opentelemetry/instrumentation/micrometer/v1_5/LongTaskTimerSecondsTest.java b/instrumentation/micrometer/micrometer-1.5/library/src/test/java/io/opentelemetry/instrumentation/micrometer/v1_5/LongTaskTimerSecondsTest.java new file mode 100644 index 000000000000..f92871073ad7 --- /dev/null +++ b/instrumentation/micrometer/micrometer-1.5/library/src/test/java/io/opentelemetry/instrumentation/micrometer/v1_5/LongTaskTimerSecondsTest.java @@ -0,0 +1,32 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.micrometer.v1_5; + +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension; +import java.util.concurrent.TimeUnit; +import org.junit.jupiter.api.extension.RegisterExtension; + +class LongTaskTimerSecondsTest extends AbstractLongTaskTimerSecondsTest { + + @RegisterExtension + static final InstrumentationExtension testing = LibraryInstrumentationExtension.create(); + + @RegisterExtension + static final MicrometerTestingExtension micrometerExtension = + new MicrometerTestingExtension(testing) { + @Override + OpenTelemetryMeterRegistryBuilder configureOtelRegistry( + OpenTelemetryMeterRegistryBuilder registry) { + return registry.setBaseTimeUnit(TimeUnit.SECONDS); + } + }; + + @Override + protected InstrumentationExtension testing() { + return testing; + } +} diff --git a/instrumentation/micrometer/micrometer-1.5/library/src/test/java/io/opentelemetry/instrumentation/micrometer/v1_5/LongTaskTimerTest.java b/instrumentation/micrometer/micrometer-1.5/library/src/test/java/io/opentelemetry/instrumentation/micrometer/v1_5/LongTaskTimerTest.java new file mode 100644 index 000000000000..e084b46b99ce --- /dev/null +++ b/instrumentation/micrometer/micrometer-1.5/library/src/test/java/io/opentelemetry/instrumentation/micrometer/v1_5/LongTaskTimerTest.java @@ -0,0 +1,25 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.micrometer.v1_5; + +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension; +import org.junit.jupiter.api.extension.RegisterExtension; + +class LongTaskTimerTest extends AbstractLongTaskTimerTest { + + @RegisterExtension + static final InstrumentationExtension testing = LibraryInstrumentationExtension.create(); + + @RegisterExtension + static final MicrometerTestingExtension micrometerExtension = + new MicrometerTestingExtension(testing); + + @Override + protected InstrumentationExtension testing() { + return testing; + } +} diff --git a/instrumentation/micrometer/micrometer-1.5/library/src/test/java/io/opentelemetry/instrumentation/micrometer/v1_5/MeterTest.java b/instrumentation/micrometer/micrometer-1.5/library/src/test/java/io/opentelemetry/instrumentation/micrometer/v1_5/MeterTest.java new file mode 100644 index 000000000000..c32730ed7747 --- /dev/null +++ b/instrumentation/micrometer/micrometer-1.5/library/src/test/java/io/opentelemetry/instrumentation/micrometer/v1_5/MeterTest.java @@ -0,0 +1,25 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.micrometer.v1_5; + +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension; +import org.junit.jupiter.api.extension.RegisterExtension; + +class MeterTest extends AbstractMeterTest { + + @RegisterExtension + static final InstrumentationExtension testing = LibraryInstrumentationExtension.create(); + + @RegisterExtension + static final MicrometerTestingExtension micrometerExtension = + new MicrometerTestingExtension(testing); + + @Override + protected InstrumentationExtension testing() { + return testing; + } +} diff --git a/instrumentation/micrometer/micrometer-1.5/library/src/test/java/io/opentelemetry/instrumentation/micrometer/v1_5/MicrometerTestingExtension.java b/instrumentation/micrometer/micrometer-1.5/library/src/test/java/io/opentelemetry/instrumentation/micrometer/v1_5/MicrometerTestingExtension.java new file mode 100644 index 000000000000..fdf2f481ea42 --- /dev/null +++ b/instrumentation/micrometer/micrometer-1.5/library/src/test/java/io/opentelemetry/instrumentation/micrometer/v1_5/MicrometerTestingExtension.java @@ -0,0 +1,60 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.micrometer.v1_5; + +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.Metrics; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.sdk.metrics.internal.SdkMeterProviderUtil; +import org.junit.jupiter.api.extension.AfterEachCallback; +import org.junit.jupiter.api.extension.BeforeEachCallback; +import org.junit.jupiter.api.extension.ExtensionContext; + +class MicrometerTestingExtension implements AfterEachCallback, BeforeEachCallback { + + private static final ExtensionContext.Namespace NAMESPACE = + ExtensionContext.Namespace.create(MicrometerTestingExtension.class); + + private final InstrumentationExtension testing; + + MicrometerTestingExtension(InstrumentationExtension testing) { + this.testing = testing; + } + + @Override + public void beforeEach(ExtensionContext context) { + ExtensionContext.Store store = context.getStore(NAMESPACE); + + MeterRegistry otelMeterRegistry = + configureOtelRegistry(OpenTelemetryMeterRegistry.builder(testing.getOpenTelemetry())) + .build(); + configureMeterRegistry(otelMeterRegistry); + + store.put(MeterRegistry.class, otelMeterRegistry); + + Metrics.addRegistry(otelMeterRegistry); + } + + @Override + public void afterEach(ExtensionContext context) { + ExtensionContext.Store store = context.getStore(NAMESPACE); + MeterRegistry otelMeterRegistry = store.get(MeterRegistry.class, MeterRegistry.class); + + Metrics.removeRegistry(otelMeterRegistry); + SdkMeterProviderUtil.resetForTest(testing.getOpenTelemetrySdk().getSdkMeterProvider()); + + Metrics.globalRegistry.forEachMeter(Metrics.globalRegistry::remove); + } + + OpenTelemetryMeterRegistryBuilder configureOtelRegistry( + OpenTelemetryMeterRegistryBuilder registry) { + return registry; + } + + MeterRegistry configureMeterRegistry(MeterRegistry registry) { + return registry; + } +} diff --git a/instrumentation/micrometer/micrometer-1.5/library/src/test/java/io/opentelemetry/instrumentation/micrometer/v1_5/NamingConventionTest.java b/instrumentation/micrometer/micrometer-1.5/library/src/test/java/io/opentelemetry/instrumentation/micrometer/v1_5/NamingConventionTest.java new file mode 100644 index 000000000000..0487167ad9d1 --- /dev/null +++ b/instrumentation/micrometer/micrometer-1.5/library/src/test/java/io/opentelemetry/instrumentation/micrometer/v1_5/NamingConventionTest.java @@ -0,0 +1,32 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.micrometer.v1_5; + +import io.micrometer.core.instrument.MeterRegistry; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension; +import org.junit.jupiter.api.extension.RegisterExtension; + +public class NamingConventionTest extends AbstractNamingConventionTest { + + @RegisterExtension + static final InstrumentationExtension testing = LibraryInstrumentationExtension.create(); + + @RegisterExtension + static final MicrometerTestingExtension micrometerExtension = + new MicrometerTestingExtension(testing) { + @Override + MeterRegistry configureMeterRegistry(MeterRegistry registry) { + registry.config().namingConvention(AbstractNamingConventionTest.namingConvention()); + return registry; + } + }; + + @Override + protected InstrumentationExtension testing() { + return testing; + } +} diff --git a/instrumentation/micrometer/micrometer-1.5/library/src/test/java/io/opentelemetry/instrumentation/micrometer/v1_5/PrometheusModeTest.java b/instrumentation/micrometer/micrometer-1.5/library/src/test/java/io/opentelemetry/instrumentation/micrometer/v1_5/PrometheusModeTest.java new file mode 100644 index 000000000000..00ec0fd0475d --- /dev/null +++ b/instrumentation/micrometer/micrometer-1.5/library/src/test/java/io/opentelemetry/instrumentation/micrometer/v1_5/PrometheusModeTest.java @@ -0,0 +1,31 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.micrometer.v1_5; + +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension; +import org.junit.jupiter.api.extension.RegisterExtension; + +class PrometheusModeTest extends AbstractPrometheusModeTest { + + @RegisterExtension + static final InstrumentationExtension testing = LibraryInstrumentationExtension.create(); + + @RegisterExtension + static final MicrometerTestingExtension micrometerExtension = + new MicrometerTestingExtension(testing) { + @Override + OpenTelemetryMeterRegistryBuilder configureOtelRegistry( + OpenTelemetryMeterRegistryBuilder registry) { + return registry.setPrometheusMode(true); + } + }; + + @Override + protected InstrumentationExtension testing() { + return testing; + } +} diff --git a/instrumentation/micrometer/micrometer-1.5/library/src/test/java/io/opentelemetry/instrumentation/micrometer/v1_5/TimerSecondsTest.java b/instrumentation/micrometer/micrometer-1.5/library/src/test/java/io/opentelemetry/instrumentation/micrometer/v1_5/TimerSecondsTest.java new file mode 100644 index 000000000000..4ab97b60f392 --- /dev/null +++ b/instrumentation/micrometer/micrometer-1.5/library/src/test/java/io/opentelemetry/instrumentation/micrometer/v1_5/TimerSecondsTest.java @@ -0,0 +1,32 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.micrometer.v1_5; + +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension; +import java.util.concurrent.TimeUnit; +import org.junit.jupiter.api.extension.RegisterExtension; + +class TimerSecondsTest extends AbstractTimerSecondsTest { + + @RegisterExtension + static final InstrumentationExtension testing = LibraryInstrumentationExtension.create(); + + @RegisterExtension + static final MicrometerTestingExtension micrometerExtension = + new MicrometerTestingExtension(testing) { + @Override + OpenTelemetryMeterRegistryBuilder configureOtelRegistry( + OpenTelemetryMeterRegistryBuilder registry) { + return registry.setBaseTimeUnit(TimeUnit.SECONDS); + } + }; + + @Override + protected InstrumentationExtension testing() { + return testing; + } +} diff --git a/instrumentation/micrometer/micrometer-1.5/library/src/test/java/io/opentelemetry/instrumentation/micrometer/v1_5/TimerTest.java b/instrumentation/micrometer/micrometer-1.5/library/src/test/java/io/opentelemetry/instrumentation/micrometer/v1_5/TimerTest.java new file mode 100644 index 000000000000..2a2b52b3512d --- /dev/null +++ b/instrumentation/micrometer/micrometer-1.5/library/src/test/java/io/opentelemetry/instrumentation/micrometer/v1_5/TimerTest.java @@ -0,0 +1,25 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.micrometer.v1_5; + +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension; +import org.junit.jupiter.api.extension.RegisterExtension; + +class TimerTest extends AbstractTimerTest { + + @RegisterExtension + static final InstrumentationExtension testing = LibraryInstrumentationExtension.create(); + + @RegisterExtension + static final MicrometerTestingExtension micrometerExtension = + new MicrometerTestingExtension(testing); + + @Override + protected InstrumentationExtension testing() { + return testing; + } +} diff --git a/instrumentation/micrometer/micrometer-1.5/testing/build.gradle.kts b/instrumentation/micrometer/micrometer-1.5/testing/build.gradle.kts new file mode 100644 index 000000000000..86fabbe0e11a --- /dev/null +++ b/instrumentation/micrometer/micrometer-1.5/testing/build.gradle.kts @@ -0,0 +1,9 @@ +plugins { + id("otel.java-conventions") +} + +dependencies { + api(project(":testing-common")) + + api("io.micrometer:micrometer-core:1.5.0") +} diff --git a/instrumentation/micrometer/micrometer-1.5/testing/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/AbstractCounterTest.java b/instrumentation/micrometer/micrometer-1.5/testing/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/AbstractCounterTest.java new file mode 100644 index 000000000000..cbd63d1c4cf0 --- /dev/null +++ b/instrumentation/micrometer/micrometer-1.5/testing/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/AbstractCounterTest.java @@ -0,0 +1,73 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.micrometer.v1_5; + +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.attributeEntry; + +import io.micrometer.core.instrument.Counter; +import io.micrometer.core.instrument.Metrics; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import org.assertj.core.api.AbstractIterableAssert; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public abstract class AbstractCounterTest { + + static final String INSTRUMENTATION_NAME = "io.opentelemetry.micrometer-1.5"; + + protected abstract InstrumentationExtension testing(); + + @BeforeEach + void cleanupMeters() { + Metrics.globalRegistry.forEachMeter(Metrics.globalRegistry::remove); + } + + @Test + void testCounter() { + // given + Counter counter = + Counter.builder("testCounter") + .description("This is a test counter") + .tags("tag", "value") + .baseUnit("items") + .register(Metrics.globalRegistry); + + // when + counter.increment(); + counter.increment(2); + + // then + testing() + .waitAndAssertMetrics( + INSTRUMENTATION_NAME, + "testCounter", + metrics -> + metrics.anySatisfy( + metric -> + assertThat(metric) + .hasDescription("This is a test counter") + .hasUnit("items") + .hasDoubleSumSatisfying( + sum -> + sum.isMonotonic() + .hasPointsSatisfying( + point -> + point + .hasValue(3) + .hasAttributes( + attributeEntry("tag", "value")))))); + + // when + Metrics.globalRegistry.remove(counter); + testing().clearData(); + counter.increment(); + + // then + testing() + .waitAndAssertMetrics(INSTRUMENTATION_NAME, "testCounter", AbstractIterableAssert::isEmpty); + } +} diff --git a/instrumentation/micrometer/micrometer-1.5/testing/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/AbstractDistributionSummaryTest.java b/instrumentation/micrometer/micrometer-1.5/testing/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/AbstractDistributionSummaryTest.java new file mode 100644 index 000000000000..bf64e0d632ee --- /dev/null +++ b/instrumentation/micrometer/micrometer-1.5/testing/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/AbstractDistributionSummaryTest.java @@ -0,0 +1,266 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.micrometer.v1_5; + +import static io.opentelemetry.instrumentation.micrometer.v1_5.AbstractCounterTest.INSTRUMENTATION_NAME; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.attributeEntry; + +import io.micrometer.core.instrument.DistributionSummary; +import io.micrometer.core.instrument.Metrics; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public abstract class AbstractDistributionSummaryTest { + + protected abstract InstrumentationExtension testing(); + + @BeforeEach + void cleanupMeters() { + Metrics.globalRegistry.forEachMeter(Metrics.globalRegistry::remove); + } + + @Test + void testMicrometerDistributionSummary() { + // given + DistributionSummary summary = + DistributionSummary.builder("testSummary") + .description("This is a test distribution summary") + .baseUnit("things") + .tags("tag", "value") + .register(Metrics.globalRegistry); + + // when + summary.record(1); + summary.record(2); + summary.record(4); + + // then + testing() + .waitAndAssertMetrics( + INSTRUMENTATION_NAME, + "testSummary", + metrics -> + metrics.anySatisfy( + metric -> + assertThat(metric) + .hasDescription("This is a test distribution summary") + .hasUnit("things") + .hasHistogramSatisfying( + histogram -> + histogram.hasPointsSatisfying( + point -> + point + .hasSum(7) + .hasCount(3) + .hasAttributes(attributeEntry("tag", "value")))))); + testing() + .waitAndAssertMetrics( + INSTRUMENTATION_NAME, + "testSummary.max", + metrics -> + metrics.anySatisfy( + metric -> + assertThat(metric) + .hasDescription("This is a test distribution summary") + .hasDoubleGaugeSatisfying( + gauge -> + gauge.hasPointsSatisfying( + point -> + point + .hasValue(4) + .hasAttributes(attributeEntry("tag", "value")))))); + + // when + Metrics.globalRegistry.remove(summary); + + // then + // Histogram is synchronous and returns previous value after removal, max is asynchronous and is + // removed completely. + testing() + .waitAndAssertMetrics( + INSTRUMENTATION_NAME, + "testSummary", + metrics -> + metrics.anySatisfy( + metric -> + assertThat(metric) + .hasHistogramSatisfying( + histogram -> + histogram.hasPointsSatisfying( + point -> + point + .hasSum(7) + .hasCount(3) + .hasAttributes(attributeEntry("tag", "value")))))); + } + + @Test + void testMicrometerHistogram() { + // given + DistributionSummary summary = + DistributionSummary.builder("testSummary") + .description("This is a test distribution summary") + .baseUnit("things") + .tags("tag", "value") + .serviceLevelObjectives(1, 10, 100, 1000) + .distributionStatisticBufferLength(10) + .register(Metrics.globalRegistry); + + // when + summary.record(0.5); + summary.record(5); + summary.record(50); + summary.record(500); + + // then + testing() + .waitAndAssertMetrics( + INSTRUMENTATION_NAME, + "testSummary", + metrics -> + metrics.anySatisfy( + metric -> + assertThat(metric) + .hasDescription("This is a test distribution summary") + .hasUnit("things") + .hasHistogramSatisfying( + histogram -> + histogram.hasPointsSatisfying( + points -> + points + .hasSum(555.5) + .hasCount(4) + .hasAttributes(attributeEntry("tag", "value")))))); + testing() + .waitAndAssertMetrics( + INSTRUMENTATION_NAME, + "testSummary.max", + metrics -> + metrics.anySatisfy( + metric -> + assertThat(metric) + .hasDescription("This is a test distribution summary") + .hasDoubleGaugeSatisfying( + gauge -> + gauge.hasPointsSatisfying( + point -> + point + .hasValue(500) + .hasAttributes(attributeEntry("tag", "value")))))); + testing() + .waitAndAssertMetrics( + INSTRUMENTATION_NAME, + "testSummary.histogram", + metrics -> + metrics.anySatisfy( + metric -> + assertThat(metric) + .hasDoubleGaugeSatisfying( + gauge -> + gauge.hasPointsSatisfying( + point -> + point + .hasValue(1) + .hasAttributes( + attributeEntry("le", "1"), + attributeEntry("tag", "value")), + point -> + point + .hasValue(2) + .hasAttributes( + attributeEntry("le", "10"), + attributeEntry("tag", "value")), + point -> + point + .hasValue(3) + .hasAttributes( + attributeEntry("le", "100"), + attributeEntry("tag", "value")), + point -> + point + .hasValue(4) + .hasAttributes( + attributeEntry("le", "1000"), + attributeEntry("tag", "value")))))); + } + + @Test + void testMicrometerPercentiles() { + // given + DistributionSummary summary = + DistributionSummary.builder("testSummary") + .description("This is a test distribution summary") + .baseUnit("things") + .tags("tag", "value") + .publishPercentiles(0.5, 0.95, 0.99) + .register(Metrics.globalRegistry); + + // when + summary.record(50); + summary.record(100); + + // then + testing() + .waitAndAssertMetrics( + INSTRUMENTATION_NAME, + "testSummary", + metrics -> + metrics.anySatisfy( + metric -> + assertThat(metric) + .hasDescription("This is a test distribution summary") + .hasUnit("things") + .hasHistogramSatisfying( + histogram -> + histogram.hasPointsSatisfying( + point -> + point + .hasSum(150) + .hasCount(2) + .hasAttributes(attributeEntry("tag", "value")))))); + testing() + .waitAndAssertMetrics( + INSTRUMENTATION_NAME, + "testSummary.max", + metrics -> + metrics.anySatisfy( + metric -> + assertThat(metric) + .hasDescription("This is a test distribution summary") + .hasDoubleGaugeSatisfying( + gauge -> + gauge.hasPointsSatisfying( + point -> + point + .hasValue(100) + .hasAttributes(attributeEntry("tag", "value")))))); + testing() + .waitAndAssertMetrics( + INSTRUMENTATION_NAME, + "testSummary.percentile", + metrics -> + metrics.anySatisfy( + metric -> + assertThat(metric) + .hasDoubleGaugeSatisfying( + gauge -> + gauge.hasPointsSatisfying( + point -> + point.hasAttributes( + attributeEntry("phi", "0.5"), + attributeEntry("tag", "value")), + point -> + point.hasAttributes( + attributeEntry("phi", "0.95"), + attributeEntry("tag", "value")), + point -> + point.hasAttributes( + attributeEntry("phi", "0.99"), + attributeEntry("tag", "value")))))); + } +} diff --git a/instrumentation/micrometer/micrometer-1.5/testing/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/AbstractFunctionCounterTest.java b/instrumentation/micrometer/micrometer-1.5/testing/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/AbstractFunctionCounterTest.java new file mode 100644 index 000000000000..36e3f43401eb --- /dev/null +++ b/instrumentation/micrometer/micrometer-1.5/testing/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/AbstractFunctionCounterTest.java @@ -0,0 +1,109 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.micrometer.v1_5; + +import static io.opentelemetry.instrumentation.micrometer.v1_5.AbstractCounterTest.INSTRUMENTATION_NAME; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.attributeEntry; + +import io.micrometer.core.instrument.FunctionCounter; +import io.micrometer.core.instrument.Metrics; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import java.util.concurrent.atomic.AtomicLong; +import org.assertj.core.api.AbstractIterableAssert; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public abstract class AbstractFunctionCounterTest { + + protected abstract InstrumentationExtension testing(); + + @BeforeEach + void cleanupMeters() { + Metrics.globalRegistry.forEachMeter(Metrics.globalRegistry::remove); + } + + final AtomicLong num = new AtomicLong(12); + final AtomicLong anotherNum = new AtomicLong(13); + + @Test + void testFunctionCounter() throws InterruptedException { + // given + FunctionCounter counter = + FunctionCounter.builder("testFunctionCounter", num, AtomicLong::get) + .description("This is a test function counter") + .tags("tag", "value") + .baseUnit("items") + .register(Metrics.globalRegistry); + + // then + testing() + .waitAndAssertMetrics( + INSTRUMENTATION_NAME, + "testFunctionCounter", + metrics -> + metrics.anySatisfy( + metric -> + assertThat(metric) + .hasDescription("This is a test function counter") + .hasUnit("items") + .hasDoubleSumSatisfying( + sum -> + sum.hasPointsSatisfying( + point -> + point + .hasValue(12) + .hasAttributes(attributeEntry("tag", "value")))))); + + // when + Metrics.globalRegistry.remove(counter); + testing().clearData(); + + // then + testing() + .waitAndAssertMetrics( + INSTRUMENTATION_NAME, "testFunctionCounter", AbstractIterableAssert::isEmpty); + } + + @Test + void functionCountersWithSameNameAndDifferentTags() { + // given + FunctionCounter.builder("testFunctionCounterWithTags", num, AtomicLong::get) + .description("First description") + .tags("tag", "1") + .baseUnit("items") + .register(Metrics.globalRegistry); + FunctionCounter.builder("testFunctionCounterWithTags", anotherNum, AtomicLong::get) + .description("ignored") + .tags("tag", "2") + .baseUnit("items") + .register(Metrics.globalRegistry); + + // then + testing() + .waitAndAssertMetrics( + INSTRUMENTATION_NAME, + "testFunctionCounterWithTags", + metrics -> + metrics.anySatisfy( + metric -> + assertThat(metric) + .hasDescription("First description") + .hasUnit("items") + .hasDoubleSumSatisfying( + sum -> + sum.isMonotonic() + .hasPointsSatisfying( + point -> + point + .hasValue(12) + .hasAttributes(attributeEntry("tag", "1")), + point -> + point + .hasValue(13) + .hasAttributes(attributeEntry("tag", "2")))))); + } +} diff --git a/instrumentation/micrometer/micrometer-1.5/testing/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/AbstractFunctionTimerSecondsTest.java b/instrumentation/micrometer/micrometer-1.5/testing/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/AbstractFunctionTimerSecondsTest.java new file mode 100644 index 000000000000..e96d4db60f90 --- /dev/null +++ b/instrumentation/micrometer/micrometer-1.5/testing/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/AbstractFunctionTimerSecondsTest.java @@ -0,0 +1,98 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.micrometer.v1_5; + +import static io.opentelemetry.instrumentation.micrometer.v1_5.AbstractCounterTest.INSTRUMENTATION_NAME; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.attributeEntry; + +import io.micrometer.core.instrument.FunctionTimer; +import io.micrometer.core.instrument.Metrics; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import java.util.concurrent.TimeUnit; +import org.assertj.core.api.AbstractIterableAssert; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public abstract class AbstractFunctionTimerSecondsTest { + + protected abstract InstrumentationExtension testing(); + + final TestTimer timerObj = new TestTimer(); + + @BeforeEach + void cleanupTimer() { + timerObj.reset(); + Metrics.globalRegistry.forEachMeter(Metrics.globalRegistry::remove); + } + + @Test + void testFunctionTimerWithBaseUnitSeconds() { + // given + FunctionTimer functionTimer = + FunctionTimer.builder( + "testFunctionTimerSeconds", + timerObj, + TestTimer::getCount, + TestTimer::getTotalTimeNanos, + TimeUnit.NANOSECONDS) + .description("This is a test function timer") + .tags("tag", "value") + .register(Metrics.globalRegistry); + + // when + timerObj.add(42, TimeUnit.SECONDS); + + // then + testing() + .waitAndAssertMetrics( + INSTRUMENTATION_NAME, + "testFunctionTimerSeconds.count", + metrics -> + metrics.anySatisfy( + metric -> + assertThat(metric) + .hasDescription("This is a test function timer") + .hasUnit("1") + .hasLongSumSatisfying( + sum -> + sum.isMonotonic() + .hasPointsSatisfying( + point -> + point + .hasValue(1) + .hasAttributes( + attributeEntry("tag", "value")))))); + testing() + .waitAndAssertMetrics( + INSTRUMENTATION_NAME, + "testFunctionTimerSeconds.sum", + metrics -> + metrics.anySatisfy( + metric -> + assertThat(metric) + .hasDescription("This is a test function timer") + .hasUnit("s") + .hasDoubleSumSatisfying( + sum -> + sum.hasPointsSatisfying( + point -> + point + .hasValue(42) + .hasAttributes(attributeEntry("tag", "value")))))); + + // when + Metrics.globalRegistry.remove(functionTimer); + testing().clearData(); + + // then + testing() + .waitAndAssertMetrics( + INSTRUMENTATION_NAME, + "testFunctionTimerSeconds.count", + AbstractIterableAssert::isEmpty); + } +} diff --git a/instrumentation/micrometer/micrometer-1.5/testing/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/AbstractFunctionTimerTest.java b/instrumentation/micrometer/micrometer-1.5/testing/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/AbstractFunctionTimerTest.java new file mode 100644 index 000000000000..5f3ec61fb7d9 --- /dev/null +++ b/instrumentation/micrometer/micrometer-1.5/testing/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/AbstractFunctionTimerTest.java @@ -0,0 +1,179 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.micrometer.v1_5; + +import static io.opentelemetry.instrumentation.micrometer.v1_5.AbstractCounterTest.INSTRUMENTATION_NAME; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.attributeEntry; + +import io.micrometer.core.instrument.FunctionTimer; +import io.micrometer.core.instrument.Metrics; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import java.util.concurrent.TimeUnit; +import org.assertj.core.api.AbstractIterableAssert; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public abstract class AbstractFunctionTimerTest { + + protected abstract InstrumentationExtension testing(); + + final TestTimer timerObj = new TestTimer(); + final TestTimer anotherTimerObj = new TestTimer(); + + @BeforeEach + void cleanupTimers() { + timerObj.reset(); + anotherTimerObj.reset(); + Metrics.globalRegistry.forEachMeter(Metrics.globalRegistry::remove); + } + + @Test + void testFunctionTimer() { + // given + FunctionTimer functionTimer = + FunctionTimer.builder( + "testFunctionTimer", + timerObj, + TestTimer::getCount, + TestTimer::getTotalTimeNanos, + TimeUnit.NANOSECONDS) + .description("This is a test function timer") + .tags("tag", "value") + .register(Metrics.globalRegistry); + + // when + timerObj.add(42, TimeUnit.SECONDS); + + // then + testing() + .waitAndAssertMetrics( + INSTRUMENTATION_NAME, + "testFunctionTimer.count", + metrics -> + metrics.anySatisfy( + metric -> + assertThat(metric) + .hasDescription("This is a test function timer") + .hasUnit("1") + .hasLongSumSatisfying( + sum -> + sum.isMonotonic() + .hasPointsSatisfying( + point -> + point + .hasValue(1) + .hasAttributes( + attributeEntry("tag", "value")))))); + testing() + .waitAndAssertMetrics( + INSTRUMENTATION_NAME, + "testFunctionTimer.sum", + metrics -> + metrics.anySatisfy( + metric -> + assertThat(metric) + .hasDescription("This is a test function timer") + .hasUnit("ms") + .hasDoubleSumSatisfying( + sum -> + sum.hasPointsSatisfying( + point -> + point + .hasValue(42_000) + .hasAttributes(attributeEntry("tag", "value")))))); + + // when + Metrics.globalRegistry.remove(functionTimer); + testing().clearData(); + + // then + testing() + .waitAndAssertMetrics( + INSTRUMENTATION_NAME, "testFunctionTimer.count", AbstractIterableAssert::isEmpty); + } + + @Test + void testNanoPrecision() { + // given + FunctionTimer.builder( + "testNanoFunctionTimer", + timerObj, + TestTimer::getCount, + TestTimer::getTotalTimeNanos, + TimeUnit.NANOSECONDS) + .register(Metrics.globalRegistry); + + // when + timerObj.add(1_234_000, TimeUnit.NANOSECONDS); + + // then + testing() + .waitAndAssertMetrics( + INSTRUMENTATION_NAME, + "testNanoFunctionTimer.sum", + metrics -> + metrics.anySatisfy( + metric -> + assertThat(metric) + .hasUnit("ms") + .hasDoubleSumSatisfying( + sum -> + sum.hasPointsSatisfying( + point -> + point + .hasValue(1.234) + .hasAttributes(Attributes.empty()))))); + } + + @Test + void functionTimersWithSameNameAndDifferentTags() { + // given + FunctionTimer.builder( + "testFunctionTimerWithTags", + timerObj, + TestTimer::getCount, + TestTimer::getTotalTimeNanos, + TimeUnit.NANOSECONDS) + .tags("tag", "1") + .register(Metrics.globalRegistry); + FunctionTimer.builder( + "testFunctionTimerWithTags", + anotherTimerObj, + TestTimer::getCount, + TestTimer::getTotalTimeNanos, + TimeUnit.NANOSECONDS) + .tags("tag", "2") + .register(Metrics.globalRegistry); + + // when + timerObj.add(12, TimeUnit.SECONDS); + anotherTimerObj.add(42, TimeUnit.SECONDS); + + // then + testing() + .waitAndAssertMetrics( + INSTRUMENTATION_NAME, + "testFunctionTimerWithTags.sum", + metrics -> + metrics.anySatisfy( + metric -> + assertThat(metric) + .hasUnit("ms") + .hasDoubleSumSatisfying( + sum -> + sum.hasPointsSatisfying( + point -> + point + .hasValue(12_000) + .hasAttributes(attributeEntry("tag", "1")), + point -> + point + .hasValue(42_000) + .hasAttributes(attributeEntry("tag", "2")))))); + } +} diff --git a/instrumentation/micrometer/micrometer-1.5/testing/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/AbstractGaugeTest.java b/instrumentation/micrometer/micrometer-1.5/testing/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/AbstractGaugeTest.java new file mode 100644 index 000000000000..95b526211432 --- /dev/null +++ b/instrumentation/micrometer/micrometer-1.5/testing/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/AbstractGaugeTest.java @@ -0,0 +1,141 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.micrometer.v1_5; + +import static io.opentelemetry.instrumentation.micrometer.v1_5.AbstractCounterTest.INSTRUMENTATION_NAME; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.attributeEntry; + +import io.micrometer.core.instrument.Gauge; +import io.micrometer.core.instrument.Metrics; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import java.lang.ref.WeakReference; +import java.util.concurrent.atomic.AtomicLong; +import org.assertj.core.api.AbstractIterableAssert; +import org.junit.jupiter.api.Test; + +public abstract class AbstractGaugeTest { + + protected abstract InstrumentationExtension testing(); + + @Test + void testGauge() { + // given + Gauge gauge = + Gauge.builder("testGauge", () -> 42) + .description("This is a test gauge") + .tags("tag", "value") + .baseUnit("items") + .register(Metrics.globalRegistry); + + // then + testing() + .waitAndAssertMetrics( + INSTRUMENTATION_NAME, + "testGauge", + metrics -> + metrics.anySatisfy( + metric -> + assertThat(metric) + .hasDescription("This is a test gauge") + .hasUnit("items") + .hasDoubleGaugeSatisfying( + doubleGauge -> + doubleGauge.hasPointsSatisfying( + point -> + point + .hasValue(42) + .hasAttributes(attributeEntry("tag", "value")))))); + + // when + Metrics.globalRegistry.remove(gauge); + testing().clearData(); + + // then + testing() + .waitAndAssertMetrics(INSTRUMENTATION_NAME, "testGauge", AbstractIterableAssert::isEmpty); + } + + @Test + void gaugesWithSameNameAndDifferentTags() { + // given + Gauge.builder("testGaugeWithTags", () -> 12) + .description("First description wins") + .baseUnit("items") + .tags("tag", "1") + .register(Metrics.globalRegistry); + Gauge.builder("testGaugeWithTags", () -> 42) + .description("ignored") + .baseUnit("items") + .tags("tag", "2") + .register(Metrics.globalRegistry); + + // then + testing() + .waitAndAssertMetrics( + INSTRUMENTATION_NAME, + "testGaugeWithTags", + metrics -> + metrics.anySatisfy( + metric -> + assertThat(metric) + .hasDescription("First description wins") + .hasUnit("items") + .hasDoubleGaugeSatisfying( + gauge -> + gauge.hasPointsSatisfying( + point -> + point + .hasValue(12) + .hasAttributes(attributeEntry("tag", "1")), + point -> + point + .hasValue(42) + .hasAttributes(attributeEntry("tag", "2")))))); + } + + @Test + void testWeakRefGauge() throws InterruptedException { + // given + AtomicLong num = new AtomicLong(42); + Gauge.builder("testWeakRefGauge", num, AtomicLong::get) + .strongReference(false) + .register(Metrics.globalRegistry); + + // then + testing() + .waitAndAssertMetrics( + INSTRUMENTATION_NAME, + "testWeakRefGauge", + metrics -> + metrics.anySatisfy( + metric -> + assertThat(metric) + .hasDoubleGaugeSatisfying( + gauge -> gauge.hasPointsSatisfying(point -> point.hasValue(42))))); + + // when + WeakReference numWeakRef = new WeakReference<>(num); + num = null; + awaitGc(numWeakRef); + testing().clearData(); + + // then + testing() + .waitAndAssertMetrics( + INSTRUMENTATION_NAME, "testWeakRefGauge", AbstractIterableAssert::isEmpty); + } + + private static void awaitGc(WeakReference ref) throws InterruptedException { + while (ref.get() != null) { + if (Thread.interrupted()) { + throw new InterruptedException(); + } + System.gc(); + System.runFinalization(); + } + } +} diff --git a/instrumentation/micrometer/micrometer-1.5/testing/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/AbstractLongTaskTimerHistogramTest.java b/instrumentation/micrometer/micrometer-1.5/testing/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/AbstractLongTaskTimerHistogramTest.java new file mode 100644 index 000000000000..4d3d1288654a --- /dev/null +++ b/instrumentation/micrometer/micrometer-1.5/testing/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/AbstractLongTaskTimerHistogramTest.java @@ -0,0 +1,168 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.micrometer.v1_5; + +import static io.opentelemetry.instrumentation.micrometer.v1_5.AbstractCounterTest.INSTRUMENTATION_NAME; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.attributeEntry; + +import io.micrometer.core.instrument.LongTaskTimer; +import io.micrometer.core.instrument.Metrics; +import io.micrometer.core.instrument.MockClock; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import java.time.Duration; +import org.junit.jupiter.api.Test; + +public abstract class AbstractLongTaskTimerHistogramTest { + + protected abstract InstrumentationExtension testing(); + + protected abstract MockClock clock(); + + @Test + void testMicrometerHistogram() { + // given + LongTaskTimer timer = + LongTaskTimer.builder("testLongTaskTimerHistogram") + .description("This is a test timer") + .serviceLevelObjectives(Duration.ofMillis(100), Duration.ofMillis(1000)) + .distributionStatisticBufferLength(10) + .register(Metrics.globalRegistry); + + // when + LongTaskTimer.Sample sample1 = timer.start(); + // only active tasks count + timer.start().stop(); + clock().add(Duration.ofMillis(100)); + LongTaskTimer.Sample sample2 = timer.start(); + LongTaskTimer.Sample sample3 = timer.start(); + clock().add(Duration.ofMillis(10)); + + // then + testing() + .waitAndAssertMetrics( + INSTRUMENTATION_NAME, + "testLongTaskTimerHistogram.active", + metrics -> + metrics.anySatisfy( + metric -> + assertThat(metric) + .hasDescription("This is a test timer") + .hasUnit("{tasks}") + .hasLongSumSatisfying( + sum -> + sum.isNotMonotonic() + .hasPointsSatisfying( + point -> + point + .hasValue(3) + .hasAttributes(Attributes.empty()))))); + testing() + .waitAndAssertMetrics( + INSTRUMENTATION_NAME, + "testLongTaskTimerHistogram.duration", + metrics -> + metrics.anySatisfy( + metric -> + assertThat(metric) + .hasDescription("This is a test timer") + .hasUnit("ms") + .hasDoubleSumSatisfying( + sum -> + sum.isNotMonotonic() + .hasPointsSatisfying( + point -> + point + .hasAttributes(Attributes.empty()) + .satisfies( + pointData -> + assertThat(pointData.getValue()) + .isPositive()))))); + testing() + .waitAndAssertMetrics( + INSTRUMENTATION_NAME, + "testLongTaskTimerHistogram.histogram", + metrics -> + metrics.anySatisfy( + metric -> + assertThat(metric) + .hasDoubleGaugeSatisfying( + gauge -> + gauge.hasPointsSatisfying( + point -> + point + .hasAttributes(attributeEntry("le", "100")) + .hasValue(2), + point -> + point + .hasAttributes(attributeEntry("le", "1000")) + .hasValue(3))))); + + // when + sample1.stop(); + sample2.stop(); + sample3.stop(); + + // then + // Continues to report 0 after stopped. + testing() + .waitAndAssertMetrics( + INSTRUMENTATION_NAME, + "testLongTaskTimerHistogram.active", + metrics -> + metrics.anySatisfy( + metric -> + assertThat(metric) + .hasDescription("This is a test timer") + .hasUnit("{tasks}") + .hasLongSumSatisfying( + sum -> + sum.isNotMonotonic() + .hasPointsSatisfying( + point -> + point + .hasValue(0) + .hasAttributes(Attributes.empty()))))); + testing() + .waitAndAssertMetrics( + INSTRUMENTATION_NAME, + "testLongTaskTimerHistogram.duration", + metrics -> + metrics.anySatisfy( + metric -> + assertThat(metric) + .hasDescription("This is a test timer") + .hasUnit("ms") + .hasDoubleSumSatisfying( + sum -> + sum.isNotMonotonic() + .hasPointsSatisfying( + point -> + point + .hasValue(0) + .hasAttributes(Attributes.empty()))))); + testing() + .waitAndAssertMetrics( + INSTRUMENTATION_NAME, + "testLongTaskTimerHistogram.histogram", + metrics -> + metrics.anySatisfy( + metric -> + assertThat(metric) + .hasDoubleGaugeSatisfying( + gauge -> + gauge.hasPointsSatisfying( + point -> + point + .hasValue(0) + .hasAttributes(attributeEntry("le", "100")), + point -> + point + .hasValue(0) + .hasAttributes(attributeEntry("le", "1000")))))); + } +} diff --git a/instrumentation/micrometer/micrometer-1.5/testing/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/AbstractLongTaskTimerSecondsTest.java b/instrumentation/micrometer/micrometer-1.5/testing/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/AbstractLongTaskTimerSecondsTest.java new file mode 100644 index 000000000000..9225c791d34f --- /dev/null +++ b/instrumentation/micrometer/micrometer-1.5/testing/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/AbstractLongTaskTimerSecondsTest.java @@ -0,0 +1,125 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.micrometer.v1_5; + +import static io.opentelemetry.instrumentation.micrometer.v1_5.AbstractCounterTest.INSTRUMENTATION_NAME; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.attributeEntry; + +import io.micrometer.core.instrument.LongTaskTimer; +import io.micrometer.core.instrument.Metrics; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import java.util.concurrent.TimeUnit; +import org.assertj.core.api.AbstractIterableAssert; +import org.junit.jupiter.api.Test; + +public abstract class AbstractLongTaskTimerSecondsTest { + + protected abstract InstrumentationExtension testing(); + + @Test + void testLongTaskTimerWithBaseUnitSeconds() throws InterruptedException { + // given + LongTaskTimer timer = + LongTaskTimer.builder("testLongTaskTimerSeconds") + .description("This is a test long task timer") + .tags("tag", "value") + .register(Metrics.globalRegistry); + + // when + LongTaskTimer.Sample sample = timer.start(); + + // then + testing() + .waitAndAssertMetrics( + INSTRUMENTATION_NAME, + "testLongTaskTimerSeconds.active", + metrics -> + metrics.anySatisfy( + metric -> + assertThat(metric) + .hasDescription("This is a test long task timer") + .hasUnit("{tasks}") + .hasLongSumSatisfying( + sum -> + sum.isNotMonotonic() + .hasPointsSatisfying( + point -> + point + .hasValue(1) + .hasAttributes( + attributeEntry("tag", "value")))))); + testing() + .waitAndAssertMetrics( + INSTRUMENTATION_NAME, + "testLongTaskTimerSeconds.duration", + metrics -> + metrics.anySatisfy( + metric -> + assertThat(metric) + .hasDescription("This is a test long task timer") + .hasUnit("s") + .hasDoubleSumSatisfying( + sum -> + sum.isNotMonotonic() + .hasPointsSatisfying( + point -> + point + .hasAttributes(attributeEntry("tag", "value")) + .satisfies( + pointData -> + assertThat(pointData.getValue()) + .isPositive()))))); + + // when + TimeUnit.MILLISECONDS.sleep(100); + sample.stop(); + + // then + testing() + .waitAndAssertMetrics( + INSTRUMENTATION_NAME, + "testLongTaskTimerSeconds.active", + metrics -> + metrics.anySatisfy( + metric -> + assertThat(metric) + .hasLongSumSatisfying( + sum -> + sum.hasPointsSatisfying( + point -> + point + .hasValue(0) + .hasAttributes(attributeEntry("tag", "value")))))); + testing() + .waitAndAssertMetrics( + INSTRUMENTATION_NAME, + "testLongTaskTimerSeconds.duration", + metrics -> + metrics.anySatisfy( + metric -> + assertThat(metric) + .hasDoubleSumSatisfying( + sum -> + sum.hasPointsSatisfying( + point -> + point + .hasValue(0) + .hasAttributes(attributeEntry("tag", "value")))))); + + // when timer is removed from the registry + Metrics.globalRegistry.remove(timer); + testing().clearData(); + timer.start(); + + // then no tasks are active after starting a new sample + testing() + .waitAndAssertMetrics( + INSTRUMENTATION_NAME, + "testLongTaskTimerSeconds.active", + AbstractIterableAssert::isEmpty); + } +} diff --git a/instrumentation/micrometer/micrometer-1.5/testing/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/AbstractLongTaskTimerTest.java b/instrumentation/micrometer/micrometer-1.5/testing/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/AbstractLongTaskTimerTest.java new file mode 100644 index 000000000000..87b1f3e36d7c --- /dev/null +++ b/instrumentation/micrometer/micrometer-1.5/testing/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/AbstractLongTaskTimerTest.java @@ -0,0 +1,123 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.micrometer.v1_5; + +import static io.opentelemetry.instrumentation.micrometer.v1_5.AbstractCounterTest.INSTRUMENTATION_NAME; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.attributeEntry; + +import io.micrometer.core.instrument.LongTaskTimer; +import io.micrometer.core.instrument.Metrics; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import java.util.concurrent.TimeUnit; +import org.assertj.core.api.AbstractIterableAssert; +import org.junit.jupiter.api.Test; + +public abstract class AbstractLongTaskTimerTest { + + protected abstract InstrumentationExtension testing(); + + @Test + void testLongTaskTimer() throws InterruptedException { + // given + LongTaskTimer timer = + LongTaskTimer.builder("testLongTaskTimer") + .description("This is a test long task timer") + .tags("tag", "value") + .register(Metrics.globalRegistry); + + // when + LongTaskTimer.Sample sample = timer.start(); + + // then + testing() + .waitAndAssertMetrics( + INSTRUMENTATION_NAME, + "testLongTaskTimer.active", + metrics -> + metrics.anySatisfy( + metric -> + assertThat(metric) + .hasDescription("This is a test long task timer") + .hasUnit("{tasks}") + .hasLongSumSatisfying( + sum -> + sum.isNotMonotonic() + .hasPointsSatisfying( + point -> + point + .hasValue(1) + .hasAttributes( + attributeEntry("tag", "value")))))); + testing() + .waitAndAssertMetrics( + INSTRUMENTATION_NAME, + "testLongTaskTimer.duration", + metrics -> + metrics.anySatisfy( + metric -> + assertThat(metric) + .hasDescription("This is a test long task timer") + .hasUnit("ms") + .hasDoubleSumSatisfying( + sum -> + sum.isNotMonotonic() + .hasPointsSatisfying( + point -> + point + .hasAttributes(attributeEntry("tag", "value")) + .satisfies( + pointData -> + assertThat(pointData.getValue()) + .isPositive()))))); + + // when + TimeUnit.MILLISECONDS.sleep(100); + sample.stop(); + + // then + testing() + .waitAndAssertMetrics( + INSTRUMENTATION_NAME, + "testLongTaskTimer.active", + metrics -> + metrics.anySatisfy( + metric -> + assertThat(metric) + .hasLongSumSatisfying( + sum -> + sum.hasPointsSatisfying( + point -> + point + .hasValue(0) + .hasAttributes(attributeEntry("tag", "value")))))); + testing() + .waitAndAssertMetrics( + INSTRUMENTATION_NAME, + "testLongTaskTimer.duration", + metrics -> + metrics.anySatisfy( + metric -> + assertThat(metric) + .hasDoubleSumSatisfying( + sum -> + sum.hasPointsSatisfying( + point -> + point + .hasValue(0) + .hasAttributes(attributeEntry("tag", "value")))))); + + // when timer is removed from the registry + Metrics.globalRegistry.remove(timer); + testing().clearData(); + timer.start(); + + // then no tasks are active after starting a new sample + testing() + .waitAndAssertMetrics( + INSTRUMENTATION_NAME, "testLongTaskTimer.active", AbstractIterableAssert::isEmpty); + } +} diff --git a/instrumentation/micrometer/micrometer-1.5/testing/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/AbstractMeterTest.java b/instrumentation/micrometer/micrometer-1.5/testing/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/AbstractMeterTest.java new file mode 100644 index 000000000000..61a570882e93 --- /dev/null +++ b/instrumentation/micrometer/micrometer-1.5/testing/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/AbstractMeterTest.java @@ -0,0 +1,203 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.micrometer.v1_5; + +import static io.opentelemetry.instrumentation.micrometer.v1_5.AbstractCounterTest.INSTRUMENTATION_NAME; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.attributeEntry; + +import io.micrometer.core.instrument.Measurement; +import io.micrometer.core.instrument.Meter; +import io.micrometer.core.instrument.Metrics; +import io.micrometer.core.instrument.Statistic; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.atomic.AtomicReference; +import org.assertj.core.api.AbstractIterableAssert; +import org.junit.jupiter.api.Test; + +public abstract class AbstractMeterTest { + + protected abstract InstrumentationExtension testing(); + + @Test + void testMeter() { + // given + AtomicReference number = new AtomicReference<>(12345.0); + List measurements = + Arrays.asList( + new Measurement(number::get, Statistic.TOTAL), + new Measurement(number::get, Statistic.TOTAL_TIME), + new Measurement(number::get, Statistic.COUNT), + new Measurement(number::get, Statistic.ACTIVE_TASKS), + new Measurement(number::get, Statistic.DURATION), + new Measurement(number::get, Statistic.MAX), + new Measurement(number::get, Statistic.VALUE), + new Measurement(number::get, Statistic.UNKNOWN)); + Meter meter = + Meter.builder("testMeter", Meter.Type.OTHER, measurements) + .description("This is a test meter") + .baseUnit("things") + .tag("tag", "value") + .register(Metrics.globalRegistry); + + // then + testing() + .waitAndAssertMetrics( + INSTRUMENTATION_NAME, + "testMeter.total", + metrics -> + metrics.anySatisfy( + metric -> + assertThat(metric) + .hasDescription("This is a test meter") + .hasUnit("things") + .hasDoubleSumSatisfying( + sum -> + sum.isMonotonic() + .hasPointsSatisfying( + point -> + point + .hasValue(12345) + .hasAttributes( + attributeEntry("tag", "value")))))); + testing() + .waitAndAssertMetrics( + INSTRUMENTATION_NAME, + "testMeter.total_time", + metrics -> + metrics.anySatisfy( + metric -> + assertThat(metric) + .hasDescription("This is a test meter") + .hasUnit("things") + .hasDoubleSumSatisfying( + sum -> + sum.isMonotonic() + .hasPointsSatisfying( + point -> + point + .hasValue(12345) + .hasAttributes( + attributeEntry("tag", "value")))))); + testing() + .waitAndAssertMetrics( + INSTRUMENTATION_NAME, + "testMeter.count", + metrics -> + metrics.anySatisfy( + metric -> + assertThat(metric) + .hasDescription("This is a test meter") + .hasUnit("things") + .hasDoubleSumSatisfying( + sum -> + sum.isMonotonic() + .hasPointsSatisfying( + point -> + point + .hasValue(12345) + .hasAttributes( + attributeEntry("tag", "value")))))); + testing() + .waitAndAssertMetrics( + INSTRUMENTATION_NAME, + "testMeter.active", + metrics -> + metrics.anySatisfy( + metric -> + assertThat(metric) + .hasDescription("This is a test meter") + .hasUnit("things") + .hasDoubleSumSatisfying( + sum -> + sum.isNotMonotonic() + .hasPointsSatisfying( + point -> + point + .hasValue(12345) + .hasAttributes( + attributeEntry("tag", "value")))))); + testing() + .waitAndAssertMetrics( + INSTRUMENTATION_NAME, + "testMeter.duration", + metrics -> + metrics.anySatisfy( + metric -> + assertThat(metric) + .hasDescription("This is a test meter") + .hasUnit("things") + .hasDoubleGaugeSatisfying( + gauge -> + gauge.hasPointsSatisfying( + point -> + point + .hasValue(12345) + .hasAttributes(attributeEntry("tag", "value")))))); + testing() + .waitAndAssertMetrics( + INSTRUMENTATION_NAME, + "testMeter.max", + metrics -> + metrics.anySatisfy( + metric -> + assertThat(metric) + .hasDescription("This is a test meter") + .hasUnit("things") + .hasDoubleGaugeSatisfying( + gauge -> + gauge.hasPointsSatisfying( + point -> + point + .hasValue(12345) + .hasAttributes(attributeEntry("tag", "value")))))); + testing() + .waitAndAssertMetrics( + INSTRUMENTATION_NAME, + "testMeter.value", + metrics -> + metrics.anySatisfy( + metric -> + assertThat(metric) + .hasDescription("This is a test meter") + .hasUnit("things") + .hasDoubleGaugeSatisfying( + gauge -> + gauge.hasPointsSatisfying( + point -> + point + .hasValue(12345) + .hasAttributes(attributeEntry("tag", "value")))))); + testing() + .waitAndAssertMetrics( + INSTRUMENTATION_NAME, + "testMeter.unknown", + metrics -> + metrics.anySatisfy( + metric -> + assertThat(metric) + .hasDescription("This is a test meter") + .hasUnit("things") + .hasDoubleGaugeSatisfying( + gauge -> + gauge.hasPointsSatisfying( + point -> + point + .hasValue(12345) + .hasAttributes(attributeEntry("tag", "value")))))); + + // when + Metrics.globalRegistry.remove(meter); + testing().clearData(); + + // then + testing() + .waitAndAssertMetrics( + INSTRUMENTATION_NAME, "testMeter.total", AbstractIterableAssert::isEmpty); + } +} diff --git a/instrumentation/micrometer/micrometer-1.5/testing/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/AbstractNamingConventionTest.java b/instrumentation/micrometer/micrometer-1.5/testing/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/AbstractNamingConventionTest.java new file mode 100644 index 000000000000..478ec9622154 --- /dev/null +++ b/instrumentation/micrometer/micrometer-1.5/testing/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/AbstractNamingConventionTest.java @@ -0,0 +1,279 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.micrometer.v1_5; + +import static io.opentelemetry.instrumentation.micrometer.v1_5.AbstractCounterTest.INSTRUMENTATION_NAME; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.attributeEntry; + +import io.micrometer.core.instrument.Counter; +import io.micrometer.core.instrument.DistributionSummary; +import io.micrometer.core.instrument.LongTaskTimer; +import io.micrometer.core.instrument.Meter; +import io.micrometer.core.instrument.Metrics; +import io.micrometer.core.instrument.Tags; +import io.micrometer.core.instrument.Timer; +import io.micrometer.core.instrument.config.NamingConvention; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicLong; +import org.junit.jupiter.api.Test; + +@SuppressWarnings("PreferJavaTimeOverload") +public abstract class AbstractNamingConventionTest { + + protected abstract InstrumentationExtension testing(); + + protected static NamingConvention namingConvention() { + return new NamingConvention() { + @Override + public String name(String name, Meter.Type type, String baseUnit) { + return "test." + name; + } + + @Override + public String tagKey(String key) { + return "test." + key; + } + + @Override + public String tagValue(String value) { + return "test." + value; + } + }; + } + + final AtomicLong num = new AtomicLong(42); + + @Test + void renameCounter() { + // given + Counter counter = Metrics.counter("renamedCounter", "tag", "value"); + + // when + counter.increment(); + + // then + testing() + .waitAndAssertMetrics( + INSTRUMENTATION_NAME, + "test.renamedCounter", + metrics -> + metrics.anySatisfy( + metric -> + assertThat(metric) + .hasDoubleSumSatisfying( + sum -> + sum.hasPointsSatisfying( + point -> + point.hasAttributes( + attributeEntry("test.tag", "test.value")))))); + } + + @Test + void renameDistributionSummary() { + // given + DistributionSummary summary = Metrics.summary("renamedSummary", "tag", "value"); + + // when + summary.record(42); + + // then + testing() + .waitAndAssertMetrics( + INSTRUMENTATION_NAME, + "test.renamedSummary", + metrics -> + metrics.anySatisfy( + metric -> + assertThat(metric) + .hasHistogramSatisfying( + histogram -> + histogram.hasPointsSatisfying( + point -> + point.hasAttributes( + attributeEntry("test.tag", "test.value")))))); + testing() + .waitAndAssertMetrics( + INSTRUMENTATION_NAME, + "test.renamedSummary.max", + metrics -> + metrics.anySatisfy( + metric -> + assertThat(metric) + .hasDoubleGaugeSatisfying( + gauge -> + gauge.hasPointsSatisfying( + point -> + point.hasAttributes( + attributeEntry("test.tag", "test.value")))))); + } + + @Test + void renameFunctionCounter() { + // given + Metrics.more().counter("renamedFunctionCounter", Tags.of("tag", "value"), num); + + // then + testing() + .waitAndAssertMetrics( + INSTRUMENTATION_NAME, + "test.renamedFunctionCounter", + metrics -> + metrics.anySatisfy( + metric -> + assertThat(metric) + .hasDoubleSumSatisfying( + sum -> + sum.hasPointsSatisfying( + point -> + point.hasAttributes( + attributeEntry("test.tag", "test.value")))))); + } + + @Test + void renameFunctionTimer() { + // given + Metrics.more() + .timer( + "renamedFunctionTimer", + Tags.of("tag", "value"), + num, + AtomicLong::longValue, + AtomicLong::doubleValue, + TimeUnit.SECONDS); + + // then + testing() + .waitAndAssertMetrics( + INSTRUMENTATION_NAME, + "test.renamedFunctionTimer.count", + metrics -> + metrics.anySatisfy( + metric -> + assertThat(metric) + .hasLongSumSatisfying( + sum -> + sum.hasPointsSatisfying( + point -> + point.hasAttributes( + attributeEntry("test.tag", "test.value")))))); + testing() + .waitAndAssertMetrics( + INSTRUMENTATION_NAME, + "test.renamedFunctionTimer.sum", + metrics -> + metrics.anySatisfy( + metric -> + assertThat(metric) + .hasDoubleSumSatisfying( + sum -> + sum.hasPointsSatisfying( + point -> + point.hasAttributes( + attributeEntry("test.tag", "test.value")))))); + } + + @Test + void renameGauge() { + // given + Metrics.gauge("renamedGauge", Tags.of("tag", "value"), num); + + // then + testing() + .waitAndAssertMetrics( + INSTRUMENTATION_NAME, + "test.renamedGauge", + metrics -> + metrics.anySatisfy( + metric -> + assertThat(metric) + .hasDoubleGaugeSatisfying( + gauge -> + gauge.hasPointsSatisfying( + point -> + point.hasAttributes( + attributeEntry("test.tag", "test.value")))))); + } + + @Test + void renameLongTaskTimer() { + // given + LongTaskTimer timer = Metrics.more().longTaskTimer("renamedLongTaskTimer", "tag", "value"); + + // when + timer.start().stop(); + + // then + testing() + .waitAndAssertMetrics( + INSTRUMENTATION_NAME, + "test.renamedLongTaskTimer.active", + metrics -> + metrics.anySatisfy( + metric -> + assertThat(metric) + .hasLongSumSatisfying( + sum -> + sum.hasPointsSatisfying( + point -> + point.hasAttributes( + attributeEntry("test.tag", "test.value")))))); + testing() + .waitAndAssertMetrics( + INSTRUMENTATION_NAME, + "test.renamedLongTaskTimer.duration", + metrics -> + metrics.anySatisfy( + metric -> + assertThat(metric) + .hasDoubleSumSatisfying( + sum -> + sum.hasPointsSatisfying( + point -> + point.hasAttributes( + attributeEntry("test.tag", "test.value")))))); + } + + @Test + void renameTimer() { + // given + Timer timer = Metrics.timer("renamedTimer", "tag", "value"); + + // when + timer.record(10, TimeUnit.SECONDS); + + // then + testing() + .waitAndAssertMetrics( + INSTRUMENTATION_NAME, + "test.renamedTimer", + metrics -> + metrics.anySatisfy( + metric -> + assertThat(metric) + .hasHistogramSatisfying( + histogram -> + histogram.hasPointsSatisfying( + point -> + point.hasAttributes( + attributeEntry("test.tag", "test.value")))))); + testing() + .waitAndAssertMetrics( + INSTRUMENTATION_NAME, + "test.renamedTimer.max", + metrics -> + metrics.anySatisfy( + metric -> + assertThat(metric) + .hasDoubleGaugeSatisfying( + gauge -> + gauge.hasPointsSatisfying( + point -> + point.hasAttributes( + attributeEntry("test.tag", "test.value")))))); + } +} diff --git a/instrumentation/micrometer/micrometer-1.5/testing/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/AbstractPrometheusModeTest.java b/instrumentation/micrometer/micrometer-1.5/testing/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/AbstractPrometheusModeTest.java new file mode 100644 index 000000000000..7bb782e10a76 --- /dev/null +++ b/instrumentation/micrometer/micrometer-1.5/testing/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/AbstractPrometheusModeTest.java @@ -0,0 +1,343 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.micrometer.v1_5; + +import static io.opentelemetry.instrumentation.micrometer.v1_5.AbstractCounterTest.INSTRUMENTATION_NAME; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.attributeEntry; + +import io.micrometer.core.instrument.Counter; +import io.micrometer.core.instrument.DistributionSummary; +import io.micrometer.core.instrument.FunctionTimer; +import io.micrometer.core.instrument.Gauge; +import io.micrometer.core.instrument.LongTaskTimer; +import io.micrometer.core.instrument.Metrics; +import io.micrometer.core.instrument.Timer; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import java.util.concurrent.TimeUnit; +import org.junit.jupiter.api.Test; + +@SuppressWarnings("PreferJavaTimeOverload") +public abstract class AbstractPrometheusModeTest { + + protected abstract InstrumentationExtension testing(); + + final TestTimer timerObj = new TestTimer(); + + @Test + void testCounter() { + // given + Counter counter = + Counter.builder("testPrometheusCounter") + .description("This is a test counter") + .tags("tag", "value") + .baseUnit("items") + .register(Metrics.globalRegistry); + + // when + counter.increment(12); + + // then + testing() + .waitAndAssertMetrics( + INSTRUMENTATION_NAME, + "testPrometheusCounter.items", + metrics -> + metrics.anySatisfy( + metric -> + assertThat(metric) + .hasDescription("This is a test counter") + .hasUnit("items") + .hasDoubleSumSatisfying( + sum -> + sum.isMonotonic() + .hasPointsSatisfying( + point -> + point + .hasValue(12) + .hasAttributes( + attributeEntry("tag", "value")))))); + } + + @Test + void testDistributionSummary() { + // given + DistributionSummary summary = + DistributionSummary.builder("testPrometheusSummary") + .description("This is a test summary") + .baseUnit("items") + .tag("tag", "value") + .register(Metrics.globalRegistry); + + // when + summary.record(12); + summary.record(42); + + // then + testing() + .waitAndAssertMetrics( + INSTRUMENTATION_NAME, + "testPrometheusSummary.items", + metrics -> + metrics.anySatisfy( + metric -> + assertThat(metric) + .hasDescription("This is a test summary") + .hasUnit("items") + .hasHistogramSatisfying( + histogram -> + histogram.hasPointsSatisfying( + point -> + point + .hasSum(54) + .hasCount(2) + .hasAttributes(attributeEntry("tag", "value")))))); + testing() + .waitAndAssertMetrics( + INSTRUMENTATION_NAME, + "testPrometheusSummary.items.max", + metrics -> + metrics.anySatisfy( + metric -> + assertThat(metric) + .hasDescription("This is a test summary") + .hasUnit("items") + .hasDoubleGaugeSatisfying( + gauge -> + gauge.hasPointsSatisfying( + point -> + point + .hasValue(42) + .hasAttributes(attributeEntry("tag", "value")))))); + } + + @Test + void testFunctionTimer() { + // given + FunctionTimer.builder( + "testPrometheusFunctionTimer", + timerObj, + TestTimer::getCount, + TestTimer::getTotalTimeNanos, + TimeUnit.NANOSECONDS) + .description("This is a test function timer") + .tags("tag", "value") + .register(Metrics.globalRegistry); + + // when + timerObj.add(42, TimeUnit.SECONDS); + + // then + testing() + .waitAndAssertMetrics( + INSTRUMENTATION_NAME, + "testPrometheusFunctionTimer.seconds.count", + metrics -> + metrics.anySatisfy( + metric -> + assertThat(metric) + .hasDescription("This is a test function timer") + .hasUnit("1") + .hasLongSumSatisfying( + sum -> + sum.isMonotonic() + .hasPointsSatisfying( + point -> + point + .hasValue(1) + .hasAttributes( + attributeEntry("tag", "value")))))); + testing() + .waitAndAssertMetrics( + INSTRUMENTATION_NAME, + "testPrometheusFunctionTimer.seconds.sum", + metrics -> + metrics.anySatisfy( + metric -> + assertThat(metric) + .hasDescription("This is a test function timer") + .hasUnit("s") + .hasDoubleSumSatisfying( + sum -> + sum.hasPointsSatisfying( + point -> + point + .hasValue(42) + .hasAttributes(attributeEntry("tag", "value")))))); + } + + @Test + void testGauge() { + // when + Gauge.builder("testPrometheusGauge", () -> 42) + .description("This is a test gauge") + .tags("tag", "value") + .baseUnit("items") + .register(Metrics.globalRegistry); + + // then + testing() + .waitAndAssertMetrics( + INSTRUMENTATION_NAME, + "testPrometheusGauge.items", + metrics -> + metrics.anySatisfy( + metric -> + assertThat(metric) + .hasDescription("This is a test gauge") + .hasUnit("items") + .hasDoubleGaugeSatisfying( + gauge -> + gauge.hasPointsSatisfying( + point -> + point + .hasValue(42) + .hasAttributes(attributeEntry("tag", "value")))))); + } + + @Test + void testLongTaskTimer() throws InterruptedException { + // given + LongTaskTimer timer = + LongTaskTimer.builder("testPrometheusLongTaskTimer") + .description("This is a test long task timer") + .tags("tag", "value") + .register(Metrics.globalRegistry); + + // when + LongTaskTimer.Sample sample = timer.start(); + + // then + testing() + .waitAndAssertMetrics( + INSTRUMENTATION_NAME, + "testPrometheusLongTaskTimer.seconds.active", + metrics -> + metrics.anySatisfy( + metric -> + assertThat(metric) + .hasDescription("This is a test long task timer") + .hasUnit("{tasks}") + .hasLongSumSatisfying( + sum -> + sum.isNotMonotonic() + .hasPointsSatisfying( + point -> + point + .hasValue(1) + .hasAttributes( + attributeEntry("tag", "value")))))); + testing() + .waitAndAssertMetrics( + INSTRUMENTATION_NAME, + "testPrometheusLongTaskTimer.seconds.duration", + metrics -> + metrics.anySatisfy( + metric -> + assertThat(metric) + .hasDescription("This is a test long task timer") + .hasUnit("s") + .hasDoubleSumSatisfying( + sum -> + sum.isNotMonotonic() + .hasPointsSatisfying( + point -> + point + .hasAttributes(attributeEntry("tag", "value")) + .satisfies( + pointData -> + assertThat(pointData.getValue()) + .isPositive()))))); + + // when + TimeUnit.MILLISECONDS.sleep(100); + sample.stop(); + + // then + testing() + .waitAndAssertMetrics( + INSTRUMENTATION_NAME, + "testPrometheusLongTaskTimer.seconds.active", + metrics -> + metrics.anySatisfy( + metric -> + assertThat(metric) + .hasLongSumSatisfying( + sum -> + sum.hasPointsSatisfying( + point -> + point + .hasValue(0) + .hasAttributes(attributeEntry("tag", "value")))))); + testing() + .waitAndAssertMetrics( + INSTRUMENTATION_NAME, + "testPrometheusLongTaskTimer.seconds.duration", + metrics -> + metrics.anySatisfy( + metric -> + assertThat(metric) + .hasDoubleSumSatisfying( + sum -> + sum.hasPointsSatisfying( + point -> + point + .hasValue(0) + .hasAttributes(attributeEntry("tag", "value")))))); + } + + @Test + void testTimer() { + // given + Timer timer = + Timer.builder("testPrometheusTimer") + .description("This is a test timer") + .tags("tag", "value") + .register(Metrics.globalRegistry); + + // when + timer.record(1, TimeUnit.SECONDS); + timer.record(5, TimeUnit.SECONDS); + timer.record(10_789, TimeUnit.MILLISECONDS); + + // then + testing() + .waitAndAssertMetrics( + INSTRUMENTATION_NAME, + "testPrometheusTimer.seconds", + metrics -> + metrics.anySatisfy( + metric -> + assertThat(metric) + .hasDescription("This is a test timer") + .hasUnit("s") + .hasHistogramSatisfying( + histogram -> + histogram.hasPointsSatisfying( + point -> + point + .hasSum(16.789) + .hasCount(3) + .hasAttributes(attributeEntry("tag", "value")))))); + testing() + .waitAndAssertMetrics( + INSTRUMENTATION_NAME, + "testPrometheusTimer.seconds.max", + metrics -> + metrics.anySatisfy( + metric -> + assertThat(metric) + .hasDescription("This is a test timer") + .hasUnit("s") + .hasDoubleGaugeSatisfying( + gauge -> + gauge.hasPointsSatisfying( + point -> + point + .hasValue(10.789) + .hasAttributes(attributeEntry("tag", "value")))))); + } +} diff --git a/instrumentation/micrometer/micrometer-1.5/testing/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/AbstractTimerSecondsTest.java b/instrumentation/micrometer/micrometer-1.5/testing/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/AbstractTimerSecondsTest.java new file mode 100644 index 000000000000..7f42c0d5a67d --- /dev/null +++ b/instrumentation/micrometer/micrometer-1.5/testing/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/AbstractTimerSecondsTest.java @@ -0,0 +1,85 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.micrometer.v1_5; + +import static io.opentelemetry.instrumentation.micrometer.v1_5.AbstractCounterTest.INSTRUMENTATION_NAME; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.attributeEntry; + +import io.micrometer.core.instrument.Metrics; +import io.micrometer.core.instrument.Timer; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import java.util.concurrent.TimeUnit; +import org.assertj.core.api.AbstractIterableAssert; +import org.junit.jupiter.api.Test; + +@SuppressWarnings("PreferJavaTimeOverload") +public abstract class AbstractTimerSecondsTest { + + protected abstract InstrumentationExtension testing(); + + @Test + void testTimerWithBaseUnitSeconds() { + // given + Timer timer = + Timer.builder("testTimerSeconds") + .description("This is a test timer") + .tags("tag", "value") + .register(Metrics.globalRegistry); + + // when + timer.record(1, TimeUnit.SECONDS); + timer.record(10, TimeUnit.SECONDS); + timer.record(12_345, TimeUnit.MILLISECONDS); + + // then + testing() + .waitAndAssertMetrics( + INSTRUMENTATION_NAME, + "testTimerSeconds", + metrics -> + metrics.anySatisfy( + metric -> + assertThat(metric) + .hasDescription("This is a test timer") + .hasUnit("s") + .hasHistogramSatisfying( + histogram -> + histogram.hasPointsSatisfying( + point -> + point + .hasSum(23.345) + .hasCount(3) + .hasAttributes(attributeEntry("tag", "value")))))); + testing() + .waitAndAssertMetrics( + INSTRUMENTATION_NAME, + "testTimerSeconds.max", + metrics -> + metrics.anySatisfy( + metric -> + assertThat(metric) + .hasDescription("This is a test timer") + .hasUnit("s") + .hasDoubleGaugeSatisfying( + gauge -> + gauge.hasPointsSatisfying( + point -> + point + .hasValue(12.345) + .hasAttributes(attributeEntry("tag", "value")))))); + + // when + Metrics.globalRegistry.remove(timer); + testing().clearData(); + timer.record(12, TimeUnit.SECONDS); + + // then + testing() + .waitAndAssertMetrics( + INSTRUMENTATION_NAME, "testTimerSeconds", AbstractIterableAssert::isEmpty); + } +} diff --git a/instrumentation/micrometer/micrometer-1.5/testing/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/AbstractTimerTest.java b/instrumentation/micrometer/micrometer-1.5/testing/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/AbstractTimerTest.java new file mode 100644 index 000000000000..84d4b07f142b --- /dev/null +++ b/instrumentation/micrometer/micrometer-1.5/testing/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/AbstractTimerTest.java @@ -0,0 +1,293 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.micrometer.v1_5; + +import static io.opentelemetry.instrumentation.micrometer.v1_5.AbstractCounterTest.INSTRUMENTATION_NAME; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.attributeEntry; + +import io.micrometer.core.instrument.Metrics; +import io.micrometer.core.instrument.Timer; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import java.time.Duration; +import java.util.concurrent.TimeUnit; +import org.assertj.core.api.AbstractIterableAssert; +import org.junit.jupiter.api.Test; + +@SuppressWarnings("PreferJavaTimeOverload") +public abstract class AbstractTimerTest { + + protected abstract InstrumentationExtension testing(); + + @Test + void testTimer() { + // given + Timer timer = + Timer.builder("testTimer") + .description("This is a test timer") + .tags("tag", "value") + .register(Metrics.globalRegistry); + + // when + timer.record(42, TimeUnit.SECONDS); + + // then + testing() + .waitAndAssertMetrics( + INSTRUMENTATION_NAME, + "testTimer", + metrics -> + metrics.anySatisfy( + metric -> + assertThat(metric) + .hasDescription("This is a test timer") + .hasUnit("ms") + .hasHistogramSatisfying( + histogram -> + histogram.hasPointsSatisfying( + point -> + point + .hasSum(42_000) + .hasCount(1) + .hasAttributes(attributeEntry("tag", "value")))))); + testing() + .waitAndAssertMetrics( + INSTRUMENTATION_NAME, + "testTimer.max", + metrics -> + metrics.anySatisfy( + metric -> + assertThat(metric) + .hasDescription("This is a test timer") + .hasDoubleGaugeSatisfying( + gauge -> + gauge.hasPointsSatisfying( + point -> + point + .hasValue(42_000) + .hasAttributes(attributeEntry("tag", "value")))))); + + // when + Metrics.globalRegistry.remove(timer); + testing().clearData(); + timer.record(12, TimeUnit.SECONDS); + + // then + testing() + .waitAndAssertMetrics(INSTRUMENTATION_NAME, "testTimer", AbstractIterableAssert::isEmpty); + } + + @Test + void testNanoPrecision() { + // given + Timer timer = Timer.builder("testNanoTimer").register(Metrics.globalRegistry); + + // when + timer.record(1_234_000, TimeUnit.NANOSECONDS); + + // then + testing() + .waitAndAssertMetrics( + INSTRUMENTATION_NAME, + "testNanoTimer", + metrics -> + metrics.anySatisfy( + metric -> + assertThat(metric) + .hasUnit("ms") + .hasHistogramSatisfying( + histogram -> + histogram.hasPointsSatisfying( + point -> + point + .hasSum(1.234) + .hasCount(1) + .hasAttributes(Attributes.empty()))))); + testing() + .waitAndAssertMetrics( + INSTRUMENTATION_NAME, + "testNanoTimer.max", + metrics -> + metrics.anySatisfy( + metric -> + assertThat(metric) + .hasDoubleGaugeSatisfying( + gauge -> + gauge.hasPointsSatisfying( + point -> + point + .hasValue(1.234) + .hasAttributes(Attributes.empty()))))); + } + + @Test + void testMicrometerHistogram() { + // given + Timer timer = + Timer.builder("testTimer") + .description("This is a test timer") + .tags("tag", "value") + .serviceLevelObjectives( + Duration.ofSeconds(1), + Duration.ofSeconds(10), + Duration.ofSeconds(100), + Duration.ofSeconds(1000)) + .distributionStatisticBufferLength(10) + .register(Metrics.globalRegistry); + + // when + timer.record(500, TimeUnit.MILLISECONDS); + timer.record(5, TimeUnit.SECONDS); + timer.record(50, TimeUnit.SECONDS); + timer.record(500, TimeUnit.SECONDS); + + // then + testing() + .waitAndAssertMetrics( + INSTRUMENTATION_NAME, + "testTimer", + metrics -> + metrics.anySatisfy( + metric -> + assertThat(metric) + .hasDescription("This is a test timer") + .hasUnit("ms") + .hasHistogramSatisfying( + histogram -> + histogram.hasPointsSatisfying( + point -> + point + .hasSum(555500) + .hasCount(4) + .hasAttributes(attributeEntry("tag", "value")))))); + testing() + .waitAndAssertMetrics( + INSTRUMENTATION_NAME, + "testTimer.max", + metrics -> + metrics.anySatisfy( + metric -> + assertThat(metric) + .hasDescription("This is a test timer") + .hasDoubleGaugeSatisfying( + gauge -> + gauge.hasPointsSatisfying( + point -> + point + .hasValue(500000) + .hasAttributes(attributeEntry("tag", "value")))))); + testing() + .waitAndAssertMetrics( + INSTRUMENTATION_NAME, + "testTimer.histogram", + metrics -> + metrics.anySatisfy( + metric -> + assertThat(metric) + .hasDoubleGaugeSatisfying( + gauge -> + gauge.hasPointsSatisfying( + point -> + point + .hasValue(1) + .hasAttributes( + attributeEntry("le", "1000"), + attributeEntry("tag", "value")), + point -> + point + .hasValue(2) + .hasAttributes( + attributeEntry("le", "10000"), + attributeEntry("tag", "value")), + point -> + point + .hasValue(3) + .hasAttributes( + attributeEntry("le", "100000"), + attributeEntry("tag", "value")), + point -> + point + .hasValue(4) + .hasAttributes( + attributeEntry("le", "1000000"), + attributeEntry("tag", "value")))))); + } + + @Test + void testMicrometerPercentiles() { + // given + Timer timer = + Timer.builder("testTimer") + .description("This is a test timer") + .tags("tag", "value") + .publishPercentiles(0.5, 0.95, 0.99) + .register(Metrics.globalRegistry); + + // when + timer.record(50, TimeUnit.MILLISECONDS); + timer.record(100, TimeUnit.MILLISECONDS); + + // then + testing() + .waitAndAssertMetrics( + INSTRUMENTATION_NAME, + "testTimer", + metrics -> + metrics.anySatisfy( + metric -> + assertThat(metric) + .hasDescription("This is a test timer") + .hasUnit("ms") + .hasHistogramSatisfying( + histogram -> + histogram.hasPointsSatisfying( + point -> + point + .hasSum(150) + .hasCount(2) + .hasAttributes(attributeEntry("tag", "value")))))); + testing() + .waitAndAssertMetrics( + INSTRUMENTATION_NAME, + "testTimer.max", + metrics -> + metrics.anySatisfy( + metric -> + assertThat(metric) + .hasDescription("This is a test timer") + .hasDoubleGaugeSatisfying( + gauge -> + gauge.hasPointsSatisfying( + point -> + point + .hasValue(100) + .hasAttributes(attributeEntry("tag", "value")))))); + testing() + .waitAndAssertMetrics( + INSTRUMENTATION_NAME, + "testTimer.percentile", + metrics -> + metrics.anySatisfy( + metric -> + assertThat(metric) + .hasDoubleGaugeSatisfying( + gauge -> + gauge.hasPointsSatisfying( + point -> + point.hasAttributes( + attributeEntry("phi", "0.5"), + attributeEntry("tag", "value")), + point -> + point.hasAttributes( + attributeEntry("phi", "0.95"), + attributeEntry("tag", "value")), + point -> + point.hasAttributes( + attributeEntry("phi", "0.99"), + attributeEntry("tag", "value")))))); + } +} diff --git a/instrumentation/micrometer/micrometer-1.5/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/micrometer/v1_5/TestTimer.java b/instrumentation/micrometer/micrometer-1.5/testing/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/TestTimer.java similarity index 86% rename from instrumentation/micrometer/micrometer-1.5/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/micrometer/v1_5/TestTimer.java rename to instrumentation/micrometer/micrometer-1.5/testing/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/TestTimer.java index f16e886a99be..c29c6341b579 100644 --- a/instrumentation/micrometer/micrometer-1.5/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/micrometer/v1_5/TestTimer.java +++ b/instrumentation/micrometer/micrometer-1.5/testing/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/TestTimer.java @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.javaagent.instrumentation.micrometer.v1_5; +package io.opentelemetry.instrumentation.micrometer.v1_5; import java.util.concurrent.TimeUnit; diff --git a/instrumentation/mongo/mongo-3.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/mongo/v3_1/MongoClientInstrumentationModule.java b/instrumentation/mongo/mongo-3.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/mongo/v3_1/MongoClientInstrumentationModule.java index d027261f1d3d..297dc69a4d2f 100644 --- a/instrumentation/mongo/mongo-3.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/mongo/v3_1/MongoClientInstrumentationModule.java +++ b/instrumentation/mongo/mongo-3.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/mongo/v3_1/MongoClientInstrumentationModule.java @@ -66,7 +66,7 @@ public static void injectTraceListener( @Advice.This MongoClientOptions.Builder builder, @Advice.FieldValue("commandListeners") List commandListeners) { for (CommandListener commandListener : commandListeners) { - if (commandListener == MongoInstrumentationSingletons.LISTENER) { + if (MongoInstrumentationSingletons.isTracingListener(commandListener)) { return; } } diff --git a/instrumentation/mongo/mongo-3.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/mongo/v3_1/MongoInstrumentationSingletons.java b/instrumentation/mongo/mongo-3.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/mongo/v3_1/MongoInstrumentationSingletons.java index d17068b3e954..f9357d0fa3e6 100644 --- a/instrumentation/mongo/mongo-3.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/mongo/v3_1/MongoInstrumentationSingletons.java +++ b/instrumentation/mongo/mongo-3.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/mongo/v3_1/MongoInstrumentationSingletons.java @@ -8,11 +8,24 @@ import com.mongodb.event.CommandListener; import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.instrumentation.mongo.v3_1.MongoTelemetry; +import io.opentelemetry.javaagent.bootstrap.internal.CommonConfig; +import io.opentelemetry.javaagent.bootstrap.internal.InstrumentationConfig; public final class MongoInstrumentationSingletons { public static final CommandListener LISTENER = - MongoTelemetry.create(GlobalOpenTelemetry.get()).newCommandListener(); + MongoTelemetry.builder(GlobalOpenTelemetry.get()) + .setStatementSanitizationEnabled( + InstrumentationConfig.get() + .getBoolean( + "otel.instrumentation.mongo.statement-sanitizer.enabled", + CommonConfig.get().isStatementSanitizationEnabled())) + .build() + .newCommandListener(); + + public static boolean isTracingListener(CommandListener listener) { + return listener.getClass().getName().equals(LISTENER.getClass().getName()); + } private MongoInstrumentationSingletons() {} } diff --git a/instrumentation/mongo/mongo-3.1/library/src/main/java/io/opentelemetry/instrumentation/mongo/v3_1/MongoDbAttributesGetter.java b/instrumentation/mongo/mongo-3.1/library/src/main/java/io/opentelemetry/instrumentation/mongo/v3_1/MongoDbAttributesGetter.java index 8bea66d51aac..6ffab51cd418 100644 --- a/instrumentation/mongo/mongo-3.1/library/src/main/java/io/opentelemetry/instrumentation/mongo/v3_1/MongoDbAttributesGetter.java +++ b/instrumentation/mongo/mongo-3.1/library/src/main/java/io/opentelemetry/instrumentation/mongo/v3_1/MongoDbAttributesGetter.java @@ -10,7 +10,6 @@ import com.mongodb.event.CommandStartedEvent; import io.opentelemetry.instrumentation.api.instrumenter.db.DbClientAttributesGetter; import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; -import java.io.StringWriter; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Arrays; @@ -20,6 +19,8 @@ import org.bson.BsonArray; import org.bson.BsonDocument; import org.bson.BsonValue; +import org.bson.codecs.BsonDocumentCodec; +import org.bson.codecs.EncoderContext; import org.bson.json.JsonWriter; import org.bson.json.JsonWriterSettings; @@ -36,10 +37,12 @@ class MongoDbAttributesGetter implements DbClientAttributesGetter= 3.7, the substring invocation will be a no-op due to use of // JsonWriterSettings.Builder.maxLength in the static initializer for JSON_WRITER_SETTINGS - StringBuffer buf = stringWriter.getBuffer(); + StringBuilder buf = stringWriter.getBuilder(); if (buf.length() <= maxNormalizedQueryLength) { return buf.toString(); } diff --git a/instrumentation/mongo/mongo-3.1/library/src/main/java/io/opentelemetry/instrumentation/mongo/v3_1/MongoInstrumenterFactory.java b/instrumentation/mongo/mongo-3.1/library/src/main/java/io/opentelemetry/instrumentation/mongo/v3_1/MongoInstrumenterFactory.java index 47f758ea1a14..ca45054a004f 100644 --- a/instrumentation/mongo/mongo-3.1/library/src/main/java/io/opentelemetry/instrumentation/mongo/v3_1/MongoInstrumenterFactory.java +++ b/instrumentation/mongo/mongo-3.1/library/src/main/java/io/opentelemetry/instrumentation/mongo/v3_1/MongoInstrumenterFactory.java @@ -21,10 +21,12 @@ class MongoInstrumenterFactory { netAttributesExtractor = NetClientAttributesExtractor.create(new MongoNetAttributesGetter()); static Instrumenter createInstrumenter( - OpenTelemetry openTelemetry, int maxNormalizedQueryLength) { + OpenTelemetry openTelemetry, + boolean statementSanitizationEnabled, + int maxNormalizedQueryLength) { MongoDbAttributesGetter dbAttributesGetter = - new MongoDbAttributesGetter(maxNormalizedQueryLength); + new MongoDbAttributesGetter(statementSanitizationEnabled, maxNormalizedQueryLength); SpanNameExtractor spanNameExtractor = new MongoSpanNameExtractor(dbAttributesGetter, attributesExtractor); @@ -33,6 +35,8 @@ static Instrumenter createInstrumenter( .addAttributesExtractor(DbClientAttributesExtractor.create(dbAttributesGetter)) .addAttributesExtractor(netAttributesExtractor) .addAttributesExtractor(attributesExtractor) - .newInstrumenter(SpanKindExtractor.alwaysClient()); + .buildInstrumenter(SpanKindExtractor.alwaysClient()); } + + private MongoInstrumenterFactory() {} } diff --git a/instrumentation/mongo/mongo-3.1/library/src/main/java/io/opentelemetry/instrumentation/mongo/v3_1/MongoNetAttributesGetter.java b/instrumentation/mongo/mongo-3.1/library/src/main/java/io/opentelemetry/instrumentation/mongo/v3_1/MongoNetAttributesGetter.java index 787ec3272cc1..feb83336ea91 100644 --- a/instrumentation/mongo/mongo-3.1/library/src/main/java/io/opentelemetry/instrumentation/mongo/v3_1/MongoNetAttributesGetter.java +++ b/instrumentation/mongo/mongo-3.1/library/src/main/java/io/opentelemetry/instrumentation/mongo/v3_1/MongoNetAttributesGetter.java @@ -6,26 +6,34 @@ package io.opentelemetry.instrumentation.mongo.v3_1; import com.mongodb.event.CommandStartedEvent; -import io.opentelemetry.instrumentation.api.instrumenter.net.InetSocketAddressNetClientAttributesGetter; -import java.net.InetSocketAddress; +import io.opentelemetry.instrumentation.api.instrumenter.net.NetClientAttributesGetter; import javax.annotation.Nullable; -class MongoNetAttributesGetter - extends InetSocketAddressNetClientAttributesGetter { +class MongoNetAttributesGetter implements NetClientAttributesGetter { + @Override @Nullable - public InetSocketAddress getAddress(CommandStartedEvent event, @Nullable Void unused) { + public String transport(CommandStartedEvent event, @Nullable Void unused) { + return null; + } + + @Nullable + @Override + public String peerName(CommandStartedEvent event) { if (event.getConnectionDescription() != null && event.getConnectionDescription().getServerAddress() != null) { - return event.getConnectionDescription().getServerAddress().getSocketAddress(); - } else { - return null; + return event.getConnectionDescription().getServerAddress().getHost(); } + return null; } - @Override @Nullable - public String transport(CommandStartedEvent commandStartedEvent, @Nullable Void unused) { + @Override + public Integer peerPort(CommandStartedEvent event) { + if (event.getConnectionDescription() != null + && event.getConnectionDescription().getServerAddress() != null) { + return event.getConnectionDescription().getServerAddress().getPort(); + } return null; } } diff --git a/instrumentation/mongo/mongo-3.1/library/src/main/java/io/opentelemetry/instrumentation/mongo/v3_1/MongoTelemetry.java b/instrumentation/mongo/mongo-3.1/library/src/main/java/io/opentelemetry/instrumentation/mongo/v3_1/MongoTelemetry.java index ad3085020968..d1c27ef1c6a4 100644 --- a/instrumentation/mongo/mongo-3.1/library/src/main/java/io/opentelemetry/instrumentation/mongo/v3_1/MongoTelemetry.java +++ b/instrumentation/mongo/mongo-3.1/library/src/main/java/io/opentelemetry/instrumentation/mongo/v3_1/MongoTelemetry.java @@ -28,9 +28,13 @@ public static MongoTelemetryBuilder builder(OpenTelemetry openTelemetry) { private final Instrumenter instrumenter; - MongoTelemetry(OpenTelemetry openTelemetry, int maxNormalizedQueryLength) { + MongoTelemetry( + OpenTelemetry openTelemetry, + boolean statementSanitizationEnabled, + int maxNormalizedQueryLength) { this.instrumenter = - MongoInstrumenterFactory.createInstrumenter(openTelemetry, maxNormalizedQueryLength); + MongoInstrumenterFactory.createInstrumenter( + openTelemetry, statementSanitizationEnabled, maxNormalizedQueryLength); } /** diff --git a/instrumentation/mongo/mongo-3.1/library/src/main/java/io/opentelemetry/instrumentation/mongo/v3_1/MongoTelemetryBuilder.java b/instrumentation/mongo/mongo-3.1/library/src/main/java/io/opentelemetry/instrumentation/mongo/v3_1/MongoTelemetryBuilder.java index c756af1a04a9..e7fa6d715dc5 100644 --- a/instrumentation/mongo/mongo-3.1/library/src/main/java/io/opentelemetry/instrumentation/mongo/v3_1/MongoTelemetryBuilder.java +++ b/instrumentation/mongo/mongo-3.1/library/src/main/java/io/opentelemetry/instrumentation/mongo/v3_1/MongoTelemetryBuilder.java @@ -5,6 +5,7 @@ package io.opentelemetry.instrumentation.mongo.v3_1; +import com.google.errorprone.annotations.CanIgnoreReturnValue; import io.opentelemetry.api.OpenTelemetry; /** A builder of {@link MongoTelemetry}. */ @@ -15,16 +16,30 @@ public final class MongoTelemetryBuilder { private final OpenTelemetry openTelemetry; + private boolean statementSanitizationEnabled = true; private int maxNormalizedQueryLength = DEFAULT_MAX_NORMALIZED_QUERY_LENGTH; MongoTelemetryBuilder(OpenTelemetry openTelemetry) { this.openTelemetry = openTelemetry; } + /** + * Sets whether the {@code db.statement} attribute on the spans emitted by the constructed {@link + * MongoTelemetry} should be sanitized. If set to {@code true}, all parameters that can + * potentially contain sensitive information will be masked. Enabled by default. + */ + @CanIgnoreReturnValue + public MongoTelemetryBuilder setStatementSanitizationEnabled( + boolean statementSanitizationEnabled) { + this.statementSanitizationEnabled = statementSanitizationEnabled; + return this; + } + /** * Sets the max length of recorded queries after normalization. Defaults to {@value * DEFAULT_MAX_NORMALIZED_QUERY_LENGTH}. */ + @CanIgnoreReturnValue public MongoTelemetryBuilder setMaxNormalizedQueryLength(int maxNormalizedQueryLength) { this.maxNormalizedQueryLength = maxNormalizedQueryLength; return this; @@ -34,6 +49,7 @@ public MongoTelemetryBuilder setMaxNormalizedQueryLength(int maxNormalizedQueryL * Returns a new {@link MongoTelemetry} with the settings of this {@link MongoTelemetryBuilder}. */ public MongoTelemetry build() { - return new MongoTelemetry(openTelemetry, maxNormalizedQueryLength); + return new MongoTelemetry( + openTelemetry, statementSanitizationEnabled, maxNormalizedQueryLength); } } diff --git a/instrumentation/mongo/mongo-3.1/library/src/main/java/io/opentelemetry/instrumentation/mongo/v3_1/StringBuilderWriter.java b/instrumentation/mongo/mongo-3.1/library/src/main/java/io/opentelemetry/instrumentation/mongo/v3_1/StringBuilderWriter.java new file mode 100644 index 000000000000..1178b76bd224 --- /dev/null +++ b/instrumentation/mongo/mongo-3.1/library/src/main/java/io/opentelemetry/instrumentation/mongo/v3_1/StringBuilderWriter.java @@ -0,0 +1,33 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.mongo.v3_1; + +import java.io.Writer; + +// because StringWriter uses the synchronized StringBuffer +class StringBuilderWriter extends Writer { + + private final StringBuilder sb; + + StringBuilderWriter(int initialSize) { + sb = new StringBuilder(initialSize); + } + + @Override + public void write(char[] cbuf, int off, int len) { + sb.append(cbuf, off, len); + } + + @Override + public void flush() {} + + @Override + public void close() {} + + public StringBuilder getBuilder() { + return sb; + } +} diff --git a/instrumentation/mongo/mongo-3.1/library/src/test/groovy/io/opentelemetry/instrumentation/mongo/v3_1/MongoDbAttributesGetterTest.groovy b/instrumentation/mongo/mongo-3.1/library/src/test/groovy/io/opentelemetry/instrumentation/mongo/v3_1/MongoDbAttributesGetterTest.groovy index 1eb8695692d2..85a887b14c5a 100644 --- a/instrumentation/mongo/mongo-3.1/library/src/test/groovy/io/opentelemetry/instrumentation/mongo/v3_1/MongoDbAttributesGetterTest.groovy +++ b/instrumentation/mongo/mongo-3.1/library/src/test/groovy/io/opentelemetry/instrumentation/mongo/v3_1/MongoDbAttributesGetterTest.groovy @@ -18,7 +18,7 @@ class MongoDbAttributesGetterTest extends Specification { def 'should sanitize statements to json'() { setup: - def extractor = new MongoDbAttributesGetter(DEFAULT_MAX_NORMALIZED_QUERY_LENGTH) + def extractor = new MongoDbAttributesGetter(true, DEFAULT_MAX_NORMALIZED_QUERY_LENGTH) expect: sanitizeStatementAcrossVersions(extractor, @@ -38,7 +38,7 @@ class MongoDbAttributesGetterTest extends Specification { def 'should only preserve string value if it is the value of the first top-level key'() { setup: - def extractor = new MongoDbAttributesGetter(DEFAULT_MAX_NORMALIZED_QUERY_LENGTH) + def extractor = new MongoDbAttributesGetter(true, DEFAULT_MAX_NORMALIZED_QUERY_LENGTH) expect: sanitizeStatementAcrossVersions(extractor, @@ -50,7 +50,7 @@ class MongoDbAttributesGetterTest extends Specification { def 'should truncate simple command'() { setup: - def extractor = new MongoDbAttributesGetter(20) + def extractor = new MongoDbAttributesGetter(true, 20) def normalized = sanitizeStatementAcrossVersions(extractor, new BsonDocument("cmd", new BsonString("c")) @@ -63,7 +63,7 @@ class MongoDbAttributesGetterTest extends Specification { def 'should truncate array'() { setup: - def extractor = new MongoDbAttributesGetter(27) + def extractor = new MongoDbAttributesGetter(true, 27) def normalized = sanitizeStatementAcrossVersions(extractor, new BsonDocument("cmd", new BsonString("c")) diff --git a/instrumentation/mongo/mongo-3.1/library/src/test/groovy/io/opentelemetry/instrumentation/mongo/v3_1/MongoSpanNameExtractorTest.groovy b/instrumentation/mongo/mongo-3.1/library/src/test/groovy/io/opentelemetry/instrumentation/mongo/v3_1/MongoSpanNameExtractorTest.groovy index 06ffbb133e57..368e41337e6e 100644 --- a/instrumentation/mongo/mongo-3.1/library/src/test/groovy/io/opentelemetry/instrumentation/mongo/v3_1/MongoSpanNameExtractorTest.groovy +++ b/instrumentation/mongo/mongo-3.1/library/src/test/groovy/io/opentelemetry/instrumentation/mongo/v3_1/MongoSpanNameExtractorTest.groovy @@ -16,7 +16,7 @@ class MongoSpanNameExtractorTest extends Specification { def 'test span name with no dbName'() { setup: - def nameExtractor = new MongoSpanNameExtractor(new MongoDbAttributesGetter(DEFAULT_MAX_NORMALIZED_QUERY_LENGTH), new MongoAttributesExtractor()) + def nameExtractor = new MongoSpanNameExtractor(new MongoDbAttributesGetter(true, DEFAULT_MAX_NORMALIZED_QUERY_LENGTH), new MongoAttributesExtractor()) def event = new CommandStartedEvent( 0, null, null, command, new BsonDocument(command, new BsonInt32(1))) diff --git a/instrumentation/mongo/mongo-3.7/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/mongo/v3_7/MongoClientSettingsBuilderInstrumentation.java b/instrumentation/mongo/mongo-3.7/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/mongo/v3_7/MongoClientSettingsBuilderInstrumentation.java index 82fac89822da..24f77675b41a 100644 --- a/instrumentation/mongo/mongo-3.7/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/mongo/v3_7/MongoClientSettingsBuilderInstrumentation.java +++ b/instrumentation/mongo/mongo-3.7/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/mongo/v3_7/MongoClientSettingsBuilderInstrumentation.java @@ -49,7 +49,7 @@ public static void injectTraceListener( @Advice.This MongoClientSettings.Builder builder, @Advice.FieldValue("commandListeners") List commandListeners) { for (CommandListener commandListener : commandListeners) { - if (commandListener == MongoInstrumentationSingletons.LISTENER) { + if (MongoInstrumentationSingletons.isTracingListener(commandListener)) { return; } } diff --git a/instrumentation/mongo/mongo-3.7/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/mongo/v3_7/MongoInstrumentationSingletons.java b/instrumentation/mongo/mongo-3.7/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/mongo/v3_7/MongoInstrumentationSingletons.java index 7ed8dbed9893..044ce2f45a5f 100644 --- a/instrumentation/mongo/mongo-3.7/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/mongo/v3_7/MongoInstrumentationSingletons.java +++ b/instrumentation/mongo/mongo-3.7/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/mongo/v3_7/MongoInstrumentationSingletons.java @@ -8,11 +8,24 @@ import com.mongodb.event.CommandListener; import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.instrumentation.mongo.v3_1.MongoTelemetry; +import io.opentelemetry.javaagent.bootstrap.internal.CommonConfig; +import io.opentelemetry.javaagent.bootstrap.internal.InstrumentationConfig; public final class MongoInstrumentationSingletons { public static final CommandListener LISTENER = - MongoTelemetry.create(GlobalOpenTelemetry.get()).newCommandListener(); + MongoTelemetry.builder(GlobalOpenTelemetry.get()) + .setStatementSanitizationEnabled( + InstrumentationConfig.get() + .getBoolean( + "otel.instrumentation.mongo.statement-sanitizer.enabled", + CommonConfig.get().isStatementSanitizationEnabled())) + .build() + .newCommandListener(); + + public static boolean isTracingListener(CommandListener listener) { + return listener.getClass().getName().equals(LISTENER.getClass().getName()); + } private MongoInstrumentationSingletons() {} } diff --git a/instrumentation/mongo/mongo-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/mongo/v4_0/DefaultConnectionPoolInstrumentation.java b/instrumentation/mongo/mongo-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/mongo/v4_0/DefaultConnectionPoolInstrumentation.java index 5385eb88edeb..7b65e6ed7b82 100644 --- a/instrumentation/mongo/mongo-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/mongo/v4_0/DefaultConnectionPoolInstrumentation.java +++ b/instrumentation/mongo/mongo-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/mongo/v4_0/DefaultConnectionPoolInstrumentation.java @@ -31,6 +31,7 @@ public void transform(TypeTransformer transformer) { this.getClass().getName() + "$SingleResultCallbackAdvice"); } + @SuppressWarnings("unused") public static class SingleResultCallbackAdvice { @Advice.OnMethodEnter(suppress = Throwable.class) diff --git a/instrumentation/mongo/mongo-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/mongo/v4_0/MongoClientSettingsBuilderInstrumentation.java b/instrumentation/mongo/mongo-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/mongo/v4_0/MongoClientSettingsBuilderInstrumentation.java index 6060031bd9b9..065b3423923c 100644 --- a/instrumentation/mongo/mongo-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/mongo/v4_0/MongoClientSettingsBuilderInstrumentation.java +++ b/instrumentation/mongo/mongo-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/mongo/v4_0/MongoClientSettingsBuilderInstrumentation.java @@ -49,7 +49,7 @@ public static void injectTraceListener( @Advice.This MongoClientSettings.Builder builder, @Advice.FieldValue("commandListeners") List commandListeners) { for (CommandListener commandListener : commandListeners) { - if (commandListener == MongoInstrumentationSingletons.LISTENER) { + if (MongoInstrumentationSingletons.isTracingListener(commandListener)) { return; } } diff --git a/instrumentation/mongo/mongo-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/mongo/v4_0/MongoInstrumentationSingletons.java b/instrumentation/mongo/mongo-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/mongo/v4_0/MongoInstrumentationSingletons.java index 47195e2de124..74f3bf6cb0b8 100644 --- a/instrumentation/mongo/mongo-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/mongo/v4_0/MongoInstrumentationSingletons.java +++ b/instrumentation/mongo/mongo-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/mongo/v4_0/MongoInstrumentationSingletons.java @@ -8,11 +8,24 @@ import com.mongodb.event.CommandListener; import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.instrumentation.mongo.v3_1.MongoTelemetry; +import io.opentelemetry.javaagent.bootstrap.internal.CommonConfig; +import io.opentelemetry.javaagent.bootstrap.internal.InstrumentationConfig; public final class MongoInstrumentationSingletons { public static final CommandListener LISTENER = - MongoTelemetry.create(GlobalOpenTelemetry.get()).newCommandListener(); + MongoTelemetry.builder(GlobalOpenTelemetry.get()) + .setStatementSanitizationEnabled( + InstrumentationConfig.get() + .getBoolean( + "otel.instrumentation.mongo.statement-sanitizer.enabled", + CommonConfig.get().isStatementSanitizationEnabled())) + .build() + .newCommandListener(); + + public static boolean isTracingListener(CommandListener listener) { + return listener.getClass().getName().equals(LISTENER.getClass().getName()); + } private MongoInstrumentationSingletons() {} } diff --git a/instrumentation/mongo/mongo-async-3.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/mongoasync/v3_3/MongoClientSettingsBuildersInstrumentation.java b/instrumentation/mongo/mongo-async-3.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/mongoasync/v3_3/MongoClientSettingsBuildersInstrumentation.java index 2a82fd93b48a..81f23d16cf3e 100644 --- a/instrumentation/mongo/mongo-async-3.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/mongoasync/v3_3/MongoClientSettingsBuildersInstrumentation.java +++ b/instrumentation/mongo/mongo-async-3.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/mongoasync/v3_3/MongoClientSettingsBuildersInstrumentation.java @@ -51,7 +51,7 @@ public static void injectTraceListener( @Advice.This MongoClientSettings.Builder builder, @Advice.FieldValue("commandListeners") List commandListeners) { for (CommandListener commandListener : commandListeners) { - if (commandListener == MongoInstrumentationSingletons.LISTENER) { + if (MongoInstrumentationSingletons.isTracingListener(commandListener)) { return; } } diff --git a/instrumentation/mongo/mongo-async-3.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/mongoasync/v3_3/MongoInstrumentationSingletons.java b/instrumentation/mongo/mongo-async-3.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/mongoasync/v3_3/MongoInstrumentationSingletons.java index 388d8d776f35..f502009eb409 100644 --- a/instrumentation/mongo/mongo-async-3.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/mongoasync/v3_3/MongoInstrumentationSingletons.java +++ b/instrumentation/mongo/mongo-async-3.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/mongoasync/v3_3/MongoInstrumentationSingletons.java @@ -8,11 +8,24 @@ import com.mongodb.event.CommandListener; import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.instrumentation.mongo.v3_1.MongoTelemetry; +import io.opentelemetry.javaagent.bootstrap.internal.CommonConfig; +import io.opentelemetry.javaagent.bootstrap.internal.InstrumentationConfig; public final class MongoInstrumentationSingletons { public static final CommandListener LISTENER = - MongoTelemetry.create(GlobalOpenTelemetry.get()).newCommandListener(); + MongoTelemetry.builder(GlobalOpenTelemetry.get()) + .setStatementSanitizationEnabled( + InstrumentationConfig.get() + .getBoolean( + "otel.instrumentation.mongo.statement-sanitizer.enabled", + CommonConfig.get().isStatementSanitizationEnabled())) + .build() + .newCommandListener(); + + public static boolean isTracingListener(CommandListener listener) { + return listener.getClass().getName().equals(LISTENER.getClass().getName()); + } private MongoInstrumentationSingletons() {} } diff --git a/instrumentation/mongo/mongo-common/testing/build.gradle.kts b/instrumentation/mongo/mongo-common/testing/build.gradle.kts index 6dab72c61bfc..b49b201e25e3 100644 --- a/instrumentation/mongo/mongo-common/testing/build.gradle.kts +++ b/instrumentation/mongo/mongo-common/testing/build.gradle.kts @@ -2,13 +2,17 @@ plugins { id("otel.java-conventions") } -val versions: Map by project - dependencies { api(project(":testing-common")) - api("org.testcontainers:mongodb:${versions["org.testcontainers"]}") + api("org.testcontainers:mongodb") implementation("org.apache.groovy:groovy") implementation("io.opentelemetry:opentelemetry-api") implementation("org.spockframework:spock-core") } + +tasks { + test { + jvmArgs("-Dotel.instrumentation.common.db-statement-sanitizer.enabled=true") + } +} diff --git a/instrumentation/mongo/mongo-common/testing/src/main/groovy/io/opentelemetry/instrumentation/mongo/testing/AbstractMongoClientTest.groovy b/instrumentation/mongo/mongo-common/testing/src/main/groovy/io/opentelemetry/instrumentation/mongo/testing/AbstractMongoClientTest.groovy index ef731b217b8a..4d12f5aad546 100644 --- a/instrumentation/mongo/mongo-common/testing/src/main/groovy/io/opentelemetry/instrumentation/mongo/testing/AbstractMongoClientTest.groovy +++ b/instrumentation/mongo/mongo-common/testing/src/main/groovy/io/opentelemetry/instrumentation/mongo/testing/AbstractMongoClientTest.groovy @@ -388,7 +388,6 @@ abstract class AbstractMongoClientTest extends InstrumentationSpecification { } attributes { "$SemanticAttributes.NET_PEER_NAME" "localhost" - "$SemanticAttributes.NET_PEER_IP" "127.0.0.1" "$SemanticAttributes.NET_PEER_PORT" port "$SemanticAttributes.DB_STATEMENT" { statementEval.call(it.replaceAll(" ", "")) diff --git a/instrumentation/netty/netty-3.8/javaagent/build.gradle.kts b/instrumentation/netty/netty-3.8/javaagent/build.gradle.kts index 2b02fee54d1b..2d1f7745aeb5 100644 --- a/instrumentation/netty/netty-3.8/javaagent/build.gradle.kts +++ b/instrumentation/netty/netty-3.8/javaagent/build.gradle.kts @@ -21,7 +21,7 @@ dependencies { compileOnly("com.google.auto.value:auto-value-annotations") annotationProcessor("com.google.auto.value:auto-value") - implementation(project(":instrumentation:netty:netty-common:javaagent")) + implementation(project(":instrumentation:netty:netty-common:library")) compileOnly("io.netty:netty:3.8.0.Final") diff --git a/instrumentation/netty/netty-3.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v3_8/ChannelPipelineUtil.java b/instrumentation/netty/netty-3.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v3_8/ChannelPipelineUtil.java new file mode 100644 index 000000000000..a949f52e6905 --- /dev/null +++ b/instrumentation/netty/netty-3.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v3_8/ChannelPipelineUtil.java @@ -0,0 +1,54 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.netty.v3_8; + +import io.opentelemetry.javaagent.instrumentation.netty.v3_8.client.HttpClientRequestTracingHandler; +import io.opentelemetry.javaagent.instrumentation.netty.v3_8.client.HttpClientResponseTracingHandler; +import io.opentelemetry.javaagent.instrumentation.netty.v3_8.client.HttpClientTracingHandler; +import io.opentelemetry.javaagent.instrumentation.netty.v3_8.server.HttpServerRequestTracingHandler; +import io.opentelemetry.javaagent.instrumentation.netty.v3_8.server.HttpServerResponseTracingHandler; +import io.opentelemetry.javaagent.instrumentation.netty.v3_8.server.HttpServerTracingHandler; +import org.jboss.netty.channel.ChannelHandler; +import org.jboss.netty.channel.ChannelPipeline; +import org.jboss.netty.handler.codec.http.HttpClientCodec; +import org.jboss.netty.handler.codec.http.HttpRequestDecoder; +import org.jboss.netty.handler.codec.http.HttpRequestEncoder; +import org.jboss.netty.handler.codec.http.HttpResponseDecoder; +import org.jboss.netty.handler.codec.http.HttpResponseEncoder; +import org.jboss.netty.handler.codec.http.HttpServerCodec; + +/** + * When certain handlers are added to the pipeline, we want to add our corresponding tracing + * handlers. If those handlers are later removed, we may want to remove our handlers. That is not + * currently implemented. + */ +public final class ChannelPipelineUtil { + + public static void wrapHandler(ChannelPipeline pipeline, ChannelHandler handler) { + // Server pipeline handlers + if (handler instanceof HttpServerCodec) { + pipeline.addLast(HttpServerTracingHandler.class.getName(), new HttpServerTracingHandler()); + } else if (handler instanceof HttpRequestDecoder) { + pipeline.addLast( + HttpServerRequestTracingHandler.class.getName(), new HttpServerRequestTracingHandler()); + } else if (handler instanceof HttpResponseEncoder) { + pipeline.addLast( + HttpServerResponseTracingHandler.class.getName(), new HttpServerResponseTracingHandler()); + } else + // Client pipeline handlers + if (handler instanceof HttpClientCodec) { + pipeline.addLast(HttpClientTracingHandler.class.getName(), new HttpClientTracingHandler()); + } else if (handler instanceof HttpRequestEncoder) { + pipeline.addLast( + HttpClientRequestTracingHandler.class.getName(), new HttpClientRequestTracingHandler()); + } else if (handler instanceof HttpResponseDecoder) { + pipeline.addLast( + HttpClientResponseTracingHandler.class.getName(), new HttpClientResponseTracingHandler()); + } + } + + private ChannelPipelineUtil() {} +} diff --git a/instrumentation/netty/netty-3.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v3_8/DefaultChannelPipelineInstrumentation.java b/instrumentation/netty/netty-3.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v3_8/DefaultChannelPipelineInstrumentation.java index 5086b98a4e4a..b140a68aaa5d 100644 --- a/instrumentation/netty/netty-3.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v3_8/DefaultChannelPipelineInstrumentation.java +++ b/instrumentation/netty/netty-3.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v3_8/DefaultChannelPipelineInstrumentation.java @@ -9,10 +9,10 @@ import static net.bytebuddy.matcher.ElementMatchers.named; import static net.bytebuddy.matcher.ElementMatchers.takesArgument; +import io.opentelemetry.instrumentation.netty.common.internal.NettyErrorHolder; import io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; -import io.opentelemetry.javaagent.instrumentation.netty.common.NettyErrorHolder; import net.bytebuddy.asm.Advice; import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.matcher.ElementMatcher; diff --git a/instrumentation/netty/netty-3.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v3_8/NettyChannelInstrumentation.java b/instrumentation/netty/netty-3.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v3_8/NettyChannelInstrumentation.java index 7140b5fe179f..ae09fe1377e7 100644 --- a/instrumentation/netty/netty-3.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v3_8/NettyChannelInstrumentation.java +++ b/instrumentation/netty/netty-3.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v3_8/NettyChannelInstrumentation.java @@ -17,11 +17,11 @@ import io.opentelemetry.context.Context; import io.opentelemetry.instrumentation.api.internal.InstrumenterUtil; import io.opentelemetry.instrumentation.api.util.VirtualField; +import io.opentelemetry.instrumentation.netty.common.internal.NettyConnectionRequest; +import io.opentelemetry.instrumentation.netty.common.internal.Timer; import io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; -import io.opentelemetry.javaagent.instrumentation.netty.common.NettyConnectionRequest; -import io.opentelemetry.javaagent.instrumentation.netty.common.Timer; import io.opentelemetry.javaagent.instrumentation.netty.v3_8.client.ConnectionListener; import java.net.SocketAddress; import net.bytebuddy.asm.Advice; diff --git a/instrumentation/netty/netty-3.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v3_8/NettyChannelPipelineInstrumentation.java b/instrumentation/netty/netty-3.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v3_8/NettyChannelPipelineInstrumentation.java index 7a302056cbd4..0c9b546f7cb1 100644 --- a/instrumentation/netty/netty-3.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v3_8/NettyChannelPipelineInstrumentation.java +++ b/instrumentation/netty/netty-3.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v3_8/NettyChannelPipelineInstrumentation.java @@ -15,23 +15,11 @@ 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.netty.v3_8.client.HttpClientRequestTracingHandler; -import io.opentelemetry.javaagent.instrumentation.netty.v3_8.client.HttpClientResponseTracingHandler; -import io.opentelemetry.javaagent.instrumentation.netty.v3_8.client.HttpClientTracingHandler; -import io.opentelemetry.javaagent.instrumentation.netty.v3_8.server.HttpServerRequestTracingHandler; -import io.opentelemetry.javaagent.instrumentation.netty.v3_8.server.HttpServerResponseTracingHandler; -import io.opentelemetry.javaagent.instrumentation.netty.v3_8.server.HttpServerTracingHandler; import net.bytebuddy.asm.Advice; import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.matcher.ElementMatcher; import org.jboss.netty.channel.ChannelHandler; import org.jboss.netty.channel.ChannelPipeline; -import org.jboss.netty.handler.codec.http.HttpClientCodec; -import org.jboss.netty.handler.codec.http.HttpRequestDecoder; -import org.jboss.netty.handler.codec.http.HttpRequestEncoder; -import org.jboss.netty.handler.codec.http.HttpResponseDecoder; -import org.jboss.netty.handler.codec.http.HttpResponseEncoder; -import org.jboss.netty.handler.codec.http.HttpServerCodec; public class NettyChannelPipelineInstrumentation implements TypeInstrumentation { @@ -59,38 +47,6 @@ public void transform(TypeTransformer transformer) { NettyChannelPipelineInstrumentation.class.getName() + "$ChannelPipelineAdd3ArgsAdvice"); } - /** - * When certain handlers are added to the pipeline, we want to add our corresponding tracing - * handlers. If those handlers are later removed, we may want to remove our handlers. That is not - * currently implemented. - */ - public static class ChannelPipelineAdviceUtil { - public static void wrapHandler(ChannelPipeline pipeline, ChannelHandler handler) { - // Server pipeline handlers - if (handler instanceof HttpServerCodec) { - pipeline.addLast(HttpServerTracingHandler.class.getName(), new HttpServerTracingHandler()); - } else if (handler instanceof HttpRequestDecoder) { - pipeline.addLast( - HttpServerRequestTracingHandler.class.getName(), new HttpServerRequestTracingHandler()); - } else if (handler instanceof HttpResponseEncoder) { - pipeline.addLast( - HttpServerResponseTracingHandler.class.getName(), - new HttpServerResponseTracingHandler()); - } else - // Client pipeline handlers - if (handler instanceof HttpClientCodec) { - pipeline.addLast(HttpClientTracingHandler.class.getName(), new HttpClientTracingHandler()); - } else if (handler instanceof HttpRequestEncoder) { - pipeline.addLast( - HttpClientRequestTracingHandler.class.getName(), new HttpClientRequestTracingHandler()); - } else if (handler instanceof HttpResponseDecoder) { - pipeline.addLast( - HttpClientResponseTracingHandler.class.getName(), - new HttpClientResponseTracingHandler()); - } - } - } - @SuppressWarnings("unused") public static class ChannelPipelineAdd2ArgsAdvice { @@ -118,7 +74,7 @@ public static void addHandler( return; } - ChannelPipelineAdviceUtil.wrapHandler(pipeline, handler); + ChannelPipelineUtil.wrapHandler(pipeline, handler); } } @@ -149,7 +105,7 @@ public static void addHandler( return; } - ChannelPipelineAdviceUtil.wrapHandler(pipeline, handler); + ChannelPipelineUtil.wrapHandler(pipeline, handler); } } } diff --git a/instrumentation/netty/netty-3.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v3_8/client/ConnectionListener.java b/instrumentation/netty/netty-3.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v3_8/client/ConnectionListener.java index e95b2b18b7bc..472241811620 100644 --- a/instrumentation/netty/netty-3.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v3_8/client/ConnectionListener.java +++ b/instrumentation/netty/netty-3.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v3_8/client/ConnectionListener.java @@ -9,8 +9,8 @@ import io.opentelemetry.context.Context; import io.opentelemetry.instrumentation.api.internal.InstrumenterUtil; -import io.opentelemetry.javaagent.instrumentation.netty.common.NettyConnectionRequest; -import io.opentelemetry.javaagent.instrumentation.netty.common.Timer; +import io.opentelemetry.instrumentation.netty.common.internal.NettyConnectionRequest; +import io.opentelemetry.instrumentation.netty.common.internal.Timer; import org.jboss.netty.channel.ChannelFuture; import org.jboss.netty.channel.ChannelFutureListener; diff --git a/instrumentation/netty/netty-3.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v3_8/client/HttpClientResponseTracingHandler.java b/instrumentation/netty/netty-3.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v3_8/client/HttpClientResponseTracingHandler.java index 72ebb2a9f637..924bb6738391 100644 --- a/instrumentation/netty/netty-3.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v3_8/client/HttpClientResponseTracingHandler.java +++ b/instrumentation/netty/netty-3.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v3_8/client/HttpClientResponseTracingHandler.java @@ -9,7 +9,7 @@ import io.opentelemetry.context.Scope; import io.opentelemetry.instrumentation.api.util.VirtualField; -import io.opentelemetry.javaagent.instrumentation.netty.common.NettyErrorHolder; +import io.opentelemetry.instrumentation.netty.common.internal.NettyErrorHolder; import org.jboss.netty.channel.Channel; import org.jboss.netty.channel.ChannelHandlerContext; import org.jboss.netty.channel.MessageEvent; diff --git a/instrumentation/netty/netty-3.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v3_8/client/NettyClientSingletons.java b/instrumentation/netty/netty-3.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v3_8/client/NettyClientSingletons.java index f20075127285..05ef6144fb3b 100644 --- a/instrumentation/netty/netty-3.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v3_8/client/NettyClientSingletons.java +++ b/instrumentation/netty/netty-3.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v3_8/client/NettyClientSingletons.java @@ -7,16 +7,17 @@ import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; -import io.opentelemetry.instrumentation.api.instrumenter.PeerServiceAttributesExtractor; import io.opentelemetry.instrumentation.api.instrumenter.SpanKindExtractor; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpClientAttributesExtractor; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpClientMetrics; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanNameExtractor; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanStatusExtractor; import io.opentelemetry.instrumentation.api.instrumenter.net.NetClientAttributesExtractor; -import io.opentelemetry.javaagent.instrumentation.netty.common.HttpClientSpanKeyAttributesExtractor; -import io.opentelemetry.javaagent.instrumentation.netty.common.NettyConnectionRequest; -import io.opentelemetry.javaagent.instrumentation.netty.common.NettyErrorHolder; +import io.opentelemetry.instrumentation.api.instrumenter.net.PeerServiceAttributesExtractor; +import io.opentelemetry.instrumentation.netty.common.internal.HttpClientSpanKeyAttributesExtractor; +import io.opentelemetry.instrumentation.netty.common.internal.NettyConnectionRequest; +import io.opentelemetry.instrumentation.netty.common.internal.NettyErrorHolder; +import io.opentelemetry.javaagent.bootstrap.internal.CommonConfig; import io.opentelemetry.javaagent.instrumentation.netty.v3_8.HttpRequestAndChannel; import org.jboss.netty.channel.Channel; import org.jboss.netty.handler.codec.http.HttpResponse; @@ -40,14 +41,18 @@ public final class NettyClientSingletons { HttpSpanNameExtractor.create(httpClientAttributesGetter)) .setSpanStatusExtractor(HttpSpanStatusExtractor.create(httpClientAttributesGetter)) .addAttributesExtractor( - HttpClientAttributesExtractor.create(httpClientAttributesGetter)) + HttpClientAttributesExtractor.builder(httpClientAttributesGetter) + .setCapturedRequestHeaders(CommonConfig.get().getClientRequestHeaders()) + .setCapturedResponseHeaders(CommonConfig.get().getClientResponseHeaders()) + .build()) .addAttributesExtractor(NetClientAttributesExtractor.create(netClientAttributesGetter)) .addAttributesExtractor( - PeerServiceAttributesExtractor.create(netClientAttributesGetter)) + PeerServiceAttributesExtractor.create( + netClientAttributesGetter, CommonConfig.get().getPeerServiceMapping())) .addOperationMetrics(HttpClientMetrics.get()) .addContextCustomizer( (context, requestAndChannel, startAttributes) -> NettyErrorHolder.init(context)) - .newClientInstrumenter(HttpRequestHeadersSetter.INSTANCE); + .buildClientInstrumenter(HttpRequestHeadersSetter.INSTANCE); NettyConnectNetAttributesGetter nettyConnectAttributesGetter = new NettyConnectNetAttributesGetter(); @@ -59,8 +64,9 @@ public final class NettyClientSingletons { .addAttributesExtractor(nettyConnectAttributesExtractor) .addAttributesExtractor(HttpClientSpanKeyAttributesExtractor.INSTANCE) .addAttributesExtractor( - PeerServiceAttributesExtractor.create(nettyConnectAttributesGetter)) - .newInstrumenter(SpanKindExtractor.alwaysClient()); + PeerServiceAttributesExtractor.create( + nettyConnectAttributesGetter, CommonConfig.get().getPeerServiceMapping())) + .buildInstrumenter(SpanKindExtractor.alwaysClient()); } public static Instrumenter instrumenter() { diff --git a/instrumentation/netty/netty-3.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v3_8/client/NettyConnectNetAttributesGetter.java b/instrumentation/netty/netty-3.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v3_8/client/NettyConnectNetAttributesGetter.java index e15feaa5c129..176962881b59 100644 --- a/instrumentation/netty/netty-3.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v3_8/client/NettyConnectNetAttributesGetter.java +++ b/instrumentation/netty/netty-3.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v3_8/client/NettyConnectNetAttributesGetter.java @@ -9,7 +9,7 @@ import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.NetTransportValues.IP_UDP; import io.opentelemetry.instrumentation.api.instrumenter.net.InetSocketAddressNetClientAttributesGetter; -import io.opentelemetry.javaagent.instrumentation.netty.common.NettyConnectionRequest; +import io.opentelemetry.instrumentation.netty.common.internal.NettyConnectionRequest; import java.net.InetSocketAddress; import java.net.SocketAddress; import javax.annotation.Nullable; @@ -19,25 +19,42 @@ final class NettyConnectNetAttributesGetter extends InetSocketAddressNetClientAttributesGetter { + @Override + public String transport(NettyConnectionRequest request, @Nullable Channel channel) { + return channel instanceof DatagramChannel ? IP_UDP : IP_TCP; + } + @Nullable @Override - public InetSocketAddress getAddress(NettyConnectionRequest request, @Nullable Channel channel) { - SocketAddress remoteAddress = null; - if (channel != null) { - remoteAddress = channel.getRemoteAddress(); - } - // remote address on end() may be null when connection hasn't been established - if (remoteAddress == null) { - remoteAddress = request.remoteAddressOnStart(); + public String peerName(NettyConnectionRequest request) { + SocketAddress requestedAddress = request.remoteAddressOnStart(); + if (requestedAddress instanceof InetSocketAddress) { + return ((InetSocketAddress) requestedAddress).getHostString(); } - if (remoteAddress instanceof InetSocketAddress) { - return (InetSocketAddress) remoteAddress; + return null; + } + + @Nullable + @Override + public Integer peerPort(NettyConnectionRequest request) { + SocketAddress requestedAddress = request.remoteAddressOnStart(); + if (requestedAddress instanceof InetSocketAddress) { + return ((InetSocketAddress) requestedAddress).getPort(); } return null; } + @Nullable @Override - public String transport(NettyConnectionRequest request, @Nullable Channel channel) { - return channel instanceof DatagramChannel ? IP_UDP : IP_TCP; + protected InetSocketAddress getPeerSocketAddress( + NettyConnectionRequest request, @Nullable Channel channel) { + if (channel == null) { + return null; + } + SocketAddress remoteAddress = channel.getRemoteAddress(); + if (remoteAddress instanceof InetSocketAddress) { + return (InetSocketAddress) remoteAddress; + } + return null; } } diff --git a/instrumentation/netty/netty-3.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v3_8/client/NettyHttpClientAttributesGetter.java b/instrumentation/netty/netty-3.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v3_8/client/NettyHttpClientAttributesGetter.java index b83f2e77db11..d61477eb8a58 100644 --- a/instrumentation/netty/netty-3.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v3_8/client/NettyHttpClientAttributesGetter.java +++ b/instrumentation/netty/netty-3.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v3_8/client/NettyHttpClientAttributesGetter.java @@ -59,38 +59,11 @@ public List requestHeader(HttpRequestAndChannel requestAndChannel, Strin } @Override - @Nullable - public Long requestContentLength( - HttpRequestAndChannel requestAndChannel, @Nullable HttpResponse response) { - return null; - } - - @Override - @Nullable - public Long requestContentLengthUncompressed( - HttpRequestAndChannel requestAndChannel, @Nullable HttpResponse response) { - return null; - } - - @Override - public Integer statusCode(HttpRequestAndChannel requestAndChannel, HttpResponse response) { + public Integer statusCode( + HttpRequestAndChannel requestAndChannel, HttpResponse response, @Nullable Throwable error) { return response.getStatus().getCode(); } - @Override - @Nullable - public Long responseContentLength( - HttpRequestAndChannel requestAndChannel, HttpResponse response) { - return null; - } - - @Override - @Nullable - public Long responseContentLengthUncompressed( - HttpRequestAndChannel requestAndChannel, HttpResponse response) { - return null; - } - @Override public List responseHeader( HttpRequestAndChannel requestAndChannel, HttpResponse response, String name) { diff --git a/instrumentation/netty/netty-3.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v3_8/client/NettyNetClientAttributesGetter.java b/instrumentation/netty/netty-3.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v3_8/client/NettyNetClientAttributesGetter.java index 8364398b0618..3f9fe7b82a1e 100644 --- a/instrumentation/netty/netty-3.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v3_8/client/NettyNetClientAttributesGetter.java +++ b/instrumentation/netty/netty-3.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v3_8/client/NettyNetClientAttributesGetter.java @@ -5,31 +5,46 @@ package io.opentelemetry.javaagent.instrumentation.netty.v3_8.client; +import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.NetTransportValues.IP_TCP; +import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.NetTransportValues.IP_UDP; + import io.opentelemetry.instrumentation.api.instrumenter.net.InetSocketAddressNetClientAttributesGetter; import io.opentelemetry.javaagent.instrumentation.netty.v3_8.HttpRequestAndChannel; import java.net.InetSocketAddress; import java.net.SocketAddress; import javax.annotation.Nullable; +import org.jboss.netty.channel.socket.DatagramChannel; import org.jboss.netty.handler.codec.http.HttpResponse; final class NettyNetClientAttributesGetter extends InetSocketAddressNetClientAttributesGetter { @Override - @Nullable - public InetSocketAddress getAddress( + public String transport( HttpRequestAndChannel requestAndChannel, @Nullable HttpResponse response) { - SocketAddress address = requestAndChannel.channel().getRemoteAddress(); - if (address instanceof InetSocketAddress) { - return (InetSocketAddress) address; - } + return requestAndChannel.channel() instanceof DatagramChannel ? IP_UDP : IP_TCP; + } + + @Nullable + @Override + public String peerName(HttpRequestAndChannel requestAndChannel) { + return null; + } + + @Nullable + @Override + public Integer peerPort(HttpRequestAndChannel requestAndChannel) { return null; } @Override @Nullable - public String transport( + protected InetSocketAddress getPeerSocketAddress( HttpRequestAndChannel requestAndChannel, @Nullable HttpResponse response) { + SocketAddress address = requestAndChannel.channel().getRemoteAddress(); + if (address instanceof InetSocketAddress) { + return (InetSocketAddress) address; + } return null; } } diff --git a/instrumentation/netty/netty-3.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v3_8/server/HttpServerResponseTracingHandler.java b/instrumentation/netty/netty-3.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v3_8/server/HttpServerResponseTracingHandler.java index 77dac4441e1a..7adb80d27197 100644 --- a/instrumentation/netty/netty-3.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v3_8/server/HttpServerResponseTracingHandler.java +++ b/instrumentation/netty/netty-3.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v3_8/server/HttpServerResponseTracingHandler.java @@ -10,7 +10,7 @@ import io.opentelemetry.context.Context; import io.opentelemetry.context.Scope; import io.opentelemetry.instrumentation.api.util.VirtualField; -import io.opentelemetry.javaagent.instrumentation.netty.common.NettyErrorHolder; +import io.opentelemetry.instrumentation.netty.common.internal.NettyErrorHolder; import io.opentelemetry.javaagent.instrumentation.netty.v3_8.HttpRequestAndChannel; import org.jboss.netty.channel.Channel; import org.jboss.netty.channel.ChannelHandlerContext; diff --git a/instrumentation/netty/netty-3.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v3_8/server/NettyHttpServerAttributesGetter.java b/instrumentation/netty/netty-3.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v3_8/server/NettyHttpServerAttributesGetter.java index b43a192025cc..6a954c8fd8c3 100644 --- a/instrumentation/netty/netty-3.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v3_8/server/NettyHttpServerAttributesGetter.java +++ b/instrumentation/netty/netty-3.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v3_8/server/NettyHttpServerAttributesGetter.java @@ -27,38 +27,11 @@ public List requestHeader(HttpRequestAndChannel requestAndChannel, Strin } @Override - @Nullable - public Long requestContentLength( - HttpRequestAndChannel requestAndChannel, @Nullable HttpResponse response) { - return null; - } - - @Override - @Nullable - public Long requestContentLengthUncompressed( - HttpRequestAndChannel requestAndChannel, @Nullable HttpResponse response) { - return null; - } - - @Override - public Integer statusCode(HttpRequestAndChannel requestAndChannel, HttpResponse response) { + public Integer statusCode( + HttpRequestAndChannel requestAndChannel, HttpResponse response, @Nullable Throwable error) { return response.getStatus().getCode(); } - @Override - @Nullable - public Long responseContentLength( - HttpRequestAndChannel requestAndChannel, HttpResponse response) { - return null; - } - - @Override - @Nullable - public Long responseContentLengthUncompressed( - HttpRequestAndChannel requestAndChannel, HttpResponse response) { - return null; - } - @Override public List responseHeader( HttpRequestAndChannel requestAndChannel, HttpResponse response, String name) { @@ -89,10 +62,4 @@ public String route(HttpRequestAndChannel requestAndChannel) { public String scheme(HttpRequestAndChannel requestAndChannel) { return getScheme(requestAndChannel); } - - @Override - @Nullable - public String serverName(HttpRequestAndChannel requestAndChannel) { - return null; - } } diff --git a/instrumentation/netty/netty-3.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v3_8/server/NettyNetServerAttributesGetter.java b/instrumentation/netty/netty-3.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v3_8/server/NettyNetServerAttributesGetter.java index bed71eb95b02..25be717c9fb7 100644 --- a/instrumentation/netty/netty-3.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v3_8/server/NettyNetServerAttributesGetter.java +++ b/instrumentation/netty/netty-3.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v3_8/server/NettyNetServerAttributesGetter.java @@ -19,18 +19,39 @@ final class NettyNetServerAttributesGetter extends InetSocketAddressNetServerAttributesGetter { @Override - @Nullable public String transport(HttpRequestAndChannel requestAndChannel) { return requestAndChannel.channel() instanceof DatagramChannel ? IP_UDP : IP_TCP; } + @Nullable @Override + public String hostName(HttpRequestAndChannel requestAndChannel) { + return null; + } + @Nullable - public InetSocketAddress getAddress(HttpRequestAndChannel requestAndChannel) { + @Override + public Integer hostPort(HttpRequestAndChannel requestAndChannel) { + return null; + } + + @Override + @Nullable + protected InetSocketAddress getPeerSocketAddress(HttpRequestAndChannel requestAndChannel) { SocketAddress address = requestAndChannel.channel().getRemoteAddress(); if (address instanceof InetSocketAddress) { return (InetSocketAddress) address; } return null; } + + @Nullable + @Override + protected InetSocketAddress getHostSocketAddress(HttpRequestAndChannel requestAndChannel) { + SocketAddress address = requestAndChannel.channel().getLocalAddress(); + if (address instanceof InetSocketAddress) { + return (InetSocketAddress) address; + } + return null; + } } diff --git a/instrumentation/netty/netty-3.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v3_8/server/NettyServerSingletons.java b/instrumentation/netty/netty-3.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v3_8/server/NettyServerSingletons.java index d93faf54ff95..73bcc241d8ca 100644 --- a/instrumentation/netty/netty-3.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v3_8/server/NettyServerSingletons.java +++ b/instrumentation/netty/netty-3.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v3_8/server/NettyServerSingletons.java @@ -12,8 +12,8 @@ import io.opentelemetry.instrumentation.api.instrumenter.http.HttpServerMetrics; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanNameExtractor; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanStatusExtractor; -import io.opentelemetry.instrumentation.api.instrumenter.net.NetServerAttributesExtractor; -import io.opentelemetry.javaagent.instrumentation.netty.common.NettyErrorHolder; +import io.opentelemetry.instrumentation.netty.common.internal.NettyErrorHolder; +import io.opentelemetry.javaagent.bootstrap.internal.CommonConfig; import io.opentelemetry.javaagent.instrumentation.netty.v3_8.HttpRequestAndChannel; import org.jboss.netty.handler.codec.http.HttpResponse; @@ -32,14 +32,16 @@ final class NettyServerSingletons { HttpSpanNameExtractor.create(httpServerAttributesGetter)) .setSpanStatusExtractor(HttpSpanStatusExtractor.create(httpServerAttributesGetter)) .addAttributesExtractor( - HttpServerAttributesExtractor.create(httpServerAttributesGetter)) - .addAttributesExtractor( - NetServerAttributesExtractor.create(new NettyNetServerAttributesGetter())) + HttpServerAttributesExtractor.builder( + httpServerAttributesGetter, new NettyNetServerAttributesGetter()) + .setCapturedRequestHeaders(CommonConfig.get().getServerRequestHeaders()) + .setCapturedResponseHeaders(CommonConfig.get().getServerResponseHeaders()) + .build()) .addOperationMetrics(HttpServerMetrics.get()) .addContextCustomizer( (context, requestAndChannel, startAttributes) -> NettyErrorHolder.init(context)) .addContextCustomizer(HttpRouteHolder.get()) - .newServerInstrumenter(NettyHeadersGetter.INSTANCE); + .buildServerInstrumenter(NettyHeadersGetter.INSTANCE); } public static Instrumenter instrumenter() { diff --git a/instrumentation/netty/netty-3.8/javaagent/src/test/groovy/Netty38ClientTest.groovy b/instrumentation/netty/netty-3.8/javaagent/src/test/groovy/Netty38ClientTest.groovy index a199eb4d7852..71991eb3ce81 100644 --- a/instrumentation/netty/netty-3.8/javaagent/src/test/groovy/Netty38ClientTest.groovy +++ b/instrumentation/netty/netty-3.8/javaagent/src/test/groovy/Netty38ClientTest.groovy @@ -15,6 +15,7 @@ import io.opentelemetry.context.Scope import io.opentelemetry.instrumentation.test.AgentTestTrait import io.opentelemetry.instrumentation.test.base.HttpClientTest import io.opentelemetry.instrumentation.testing.junit.http.AbstractHttpClientTest +import io.opentelemetry.semconv.trace.attributes.SemanticAttributes import spock.lang.AutoCleanup import spock.lang.Shared @@ -128,7 +129,10 @@ class Netty38ClientTest extends HttpClientTest implements AgentTestTrai case "http://192.0.2.1/": // non routable address return [] } - return super.httpAttributes(uri) + def attributes = super.httpAttributes(uri) + attributes.remove(SemanticAttributes.NET_PEER_NAME) + attributes.remove(SemanticAttributes.NET_PEER_PORT) + return attributes } @Override diff --git a/instrumentation/netty/netty-4-common/javaagent/build.gradle.kts b/instrumentation/netty/netty-4-common/javaagent/build.gradle.kts index 509ae21bba4f..4e9f1dbdb54e 100644 --- a/instrumentation/netty/netty-4-common/javaagent/build.gradle.kts +++ b/instrumentation/netty/netty-4-common/javaagent/build.gradle.kts @@ -3,10 +3,8 @@ plugins { } dependencies { - compileOnly("com.google.auto.value:auto-value-annotations") - annotationProcessor("com.google.auto.value:auto-value") - - api(project(":instrumentation:netty:netty-common:javaagent")) + implementation(project(":instrumentation:netty:netty-4-common:library")) + implementation(project(":instrumentation:netty:netty-common:library")) compileOnly("io.netty:netty-codec-http:4.0.0.Final") } diff --git a/instrumentation/netty/netty-4-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4/common/AbstractNettyChannelPipelineInstrumentation.java b/instrumentation/netty/netty-4-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4/common/AbstractNettyChannelPipelineInstrumentation.java index dc9b701f9562..c2c65fb42d92 100644 --- a/instrumentation/netty/netty-4-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4/common/AbstractNettyChannelPipelineInstrumentation.java +++ b/instrumentation/netty/netty-4-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4/common/AbstractNettyChannelPipelineInstrumentation.java @@ -153,6 +153,11 @@ public static void removeHandler( .getName() .startsWith("io.opentelemetry.javaagent.instrumentation.netty.")) { pipeline.removeLast(); + } else if (handler + .getClass() + .getName() + .startsWith("io.opentelemetry.instrumentation.netty.")) { + pipeline.removeLast(); } } } diff --git a/instrumentation/netty/netty-4-common/library/build.gradle.kts b/instrumentation/netty/netty-4-common/library/build.gradle.kts new file mode 100644 index 000000000000..feefae65cfe1 --- /dev/null +++ b/instrumentation/netty/netty-4-common/library/build.gradle.kts @@ -0,0 +1,12 @@ +plugins { + id("otel.library-instrumentation") +} + +dependencies { + compileOnly("com.google.auto.value:auto-value-annotations") + annotationProcessor("com.google.auto.value:auto-value") + + implementation(project(":instrumentation:netty:netty-common:library")) + + compileOnly("io.netty:netty-codec-http:4.0.0.Final") +} diff --git a/instrumentation/netty/netty-4-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4/common/HttpRequestAndChannel.java b/instrumentation/netty/netty-4-common/library/src/main/java/io/opentelemetry/instrumentation/netty/v4/common/HttpRequestAndChannel.java similarity index 52% rename from instrumentation/netty/netty-4-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4/common/HttpRequestAndChannel.java rename to instrumentation/netty/netty-4-common/library/src/main/java/io/opentelemetry/instrumentation/netty/v4/common/HttpRequestAndChannel.java index 70743998f76f..4afe458d5ba9 100644 --- a/instrumentation/netty/netty-4-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4/common/HttpRequestAndChannel.java +++ b/instrumentation/netty/netty-4-common/library/src/main/java/io/opentelemetry/instrumentation/netty/v4/common/HttpRequestAndChannel.java @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.javaagent.instrumentation.netty.v4.common; +package io.opentelemetry.instrumentation.netty.v4.common; import com.google.auto.value.AutoValue; import io.netty.channel.Channel; @@ -11,19 +11,29 @@ import java.net.SocketAddress; import javax.annotation.Nullable; +/** A tuple of an {@link HttpRequest} and a {@link Channel}. */ @AutoValue public abstract class HttpRequestAndChannel { + /** Create a new {@link HttpRequestAndChannel}. */ public static HttpRequestAndChannel create(HttpRequest request, Channel channel) { return new AutoValue_HttpRequestAndChannel(request, channel, channel.remoteAddress()); } + /** Returns the {@link HttpRequest}. */ public abstract HttpRequest request(); + /** Returns the {@link Channel}. */ public abstract Channel channel(); - // we're capturing the remote address early because in case of timeouts or other connection issues - // netty may return null when calling Channel.remoteAddress() at the end of processing + /** + * Return the {@link Channel#remoteAddress()} present when this {@link HttpRequestAndChannel} was + * created. + * + *

We capture the remote address early because netty may return null when calling {@link + * Channel#remoteAddress()} at the end of processing in cases of timeouts or other connection + * issues. + */ @Nullable public abstract SocketAddress remoteAddress(); } diff --git a/instrumentation/netty/netty-4-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4/common/HttpSchemeUtil.java b/instrumentation/netty/netty-4-common/library/src/main/java/io/opentelemetry/instrumentation/netty/v4/common/internal/HttpSchemeUtil.java similarity index 78% rename from instrumentation/netty/netty-4-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4/common/HttpSchemeUtil.java rename to instrumentation/netty/netty-4-common/library/src/main/java/io/opentelemetry/instrumentation/netty/v4/common/internal/HttpSchemeUtil.java index 9b08b508b7db..3b54f71fd095 100644 --- a/instrumentation/netty/netty-4-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4/common/HttpSchemeUtil.java +++ b/instrumentation/netty/netty-4-common/library/src/main/java/io/opentelemetry/instrumentation/netty/v4/common/internal/HttpSchemeUtil.java @@ -3,10 +3,15 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.javaagent.instrumentation.netty.v4.common; +package io.opentelemetry.instrumentation.netty.v4.common.internal; import io.netty.channel.ChannelHandler; +import io.opentelemetry.instrumentation.netty.v4.common.HttpRequestAndChannel; +/** + * This class is internal and is hence not for public use. Its APIs are unstable and can change at + * any time. + */ public final class HttpSchemeUtil { private static final Class sslHandlerClass = getSslHandlerClass(); diff --git a/instrumentation/netty/netty-4-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4/common/client/ConnectionCompleteListener.java b/instrumentation/netty/netty-4-common/library/src/main/java/io/opentelemetry/instrumentation/netty/v4/common/internal/client/ConnectionCompleteListener.java similarity index 78% rename from instrumentation/netty/netty-4-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4/common/client/ConnectionCompleteListener.java rename to instrumentation/netty/netty-4-common/library/src/main/java/io/opentelemetry/instrumentation/netty/v4/common/internal/client/ConnectionCompleteListener.java index c937c9db4801..bf7c1b6d7b79 100644 --- a/instrumentation/netty/netty-4-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4/common/client/ConnectionCompleteListener.java +++ b/instrumentation/netty/netty-4-common/library/src/main/java/io/opentelemetry/instrumentation/netty/v4/common/internal/client/ConnectionCompleteListener.java @@ -3,15 +3,19 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.javaagent.instrumentation.netty.v4.common.client; +package io.opentelemetry.instrumentation.netty.v4.common.internal.client; import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; import io.netty.util.concurrent.Future; import io.netty.util.concurrent.GenericFutureListener; import io.opentelemetry.context.Context; -import io.opentelemetry.javaagent.instrumentation.netty.common.NettyConnectionRequest; +import io.opentelemetry.instrumentation.netty.common.internal.NettyConnectionRequest; +/** + * This class is internal and is hence not for public use. Its APIs are unstable and can change at + * any time. + */ public class ConnectionCompleteListener implements GenericFutureListener> { private final NettyConnectionInstrumenter instrumenter; private final Context context; diff --git a/instrumentation/netty/netty-4-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4/common/client/HttpRequestHeadersSetter.java b/instrumentation/netty/netty-4-common/library/src/main/java/io/opentelemetry/instrumentation/netty/v4/common/internal/client/HttpRequestHeadersSetter.java similarity index 71% rename from instrumentation/netty/netty-4-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4/common/client/HttpRequestHeadersSetter.java rename to instrumentation/netty/netty-4-common/library/src/main/java/io/opentelemetry/instrumentation/netty/v4/common/internal/client/HttpRequestHeadersSetter.java index bd625c7aeece..b56a4d8bb1e6 100644 --- a/instrumentation/netty/netty-4-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4/common/client/HttpRequestHeadersSetter.java +++ b/instrumentation/netty/netty-4-common/library/src/main/java/io/opentelemetry/instrumentation/netty/v4/common/internal/client/HttpRequestHeadersSetter.java @@ -3,10 +3,10 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.javaagent.instrumentation.netty.v4.common.client; +package io.opentelemetry.instrumentation.netty.v4.common.internal.client; import io.opentelemetry.context.propagation.TextMapSetter; -import io.opentelemetry.javaagent.instrumentation.netty.v4.common.HttpRequestAndChannel; +import io.opentelemetry.instrumentation.netty.v4.common.HttpRequestAndChannel; enum HttpRequestHeadersSetter implements TextMapSetter { INSTANCE; diff --git a/instrumentation/netty/netty-4-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4/common/client/NettyClientInstrumenterFactory.java b/instrumentation/netty/netty-4-common/library/src/main/java/io/opentelemetry/instrumentation/netty/v4/common/internal/client/NettyClientInstrumenterFactory.java similarity index 62% rename from instrumentation/netty/netty-4-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4/common/client/NettyClientInstrumenterFactory.java rename to instrumentation/netty/netty-4-common/library/src/main/java/io/opentelemetry/instrumentation/netty/v4/common/internal/client/NettyClientInstrumenterFactory.java index a2ca2fe8f171..2e250067bd89 100644 --- a/instrumentation/netty/netty-4-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4/common/client/NettyClientInstrumenterFactory.java +++ b/instrumentation/netty/netty-4-common/library/src/main/java/io/opentelemetry/instrumentation/netty/v4/common/internal/client/NettyClientInstrumenterFactory.java @@ -3,52 +3,77 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.javaagent.instrumentation.netty.v4.common.client; +package io.opentelemetry.instrumentation.netty.v4.common.internal.client; import io.netty.channel.Channel; import io.netty.handler.codec.http.HttpResponse; -import io.opentelemetry.api.GlobalOpenTelemetry; +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; import io.opentelemetry.instrumentation.api.instrumenter.InstrumenterBuilder; -import io.opentelemetry.instrumentation.api.instrumenter.PeerServiceAttributesExtractor; import io.opentelemetry.instrumentation.api.instrumenter.SpanKindExtractor; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpClientAttributesExtractor; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpClientMetrics; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanNameExtractor; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanStatusExtractor; import io.opentelemetry.instrumentation.api.instrumenter.net.NetClientAttributesExtractor; -import io.opentelemetry.javaagent.instrumentation.netty.common.HttpClientSpanKeyAttributesExtractor; -import io.opentelemetry.javaagent.instrumentation.netty.common.NettyConnectionRequest; -import io.opentelemetry.javaagent.instrumentation.netty.v4.common.HttpRequestAndChannel; +import io.opentelemetry.instrumentation.api.instrumenter.net.PeerServiceAttributesExtractor; +import io.opentelemetry.instrumentation.netty.common.internal.HttpClientSpanKeyAttributesExtractor; +import io.opentelemetry.instrumentation.netty.common.internal.NettyConnectionRequest; +import io.opentelemetry.instrumentation.netty.v4.common.HttpRequestAndChannel; +import java.util.List; +import java.util.Map; +/** + * This class is internal and is hence not for public use. Its APIs are unstable and can change at + * any time. + */ public final class NettyClientInstrumenterFactory { + private final OpenTelemetry openTelemetry; private final String instrumentationName; private final boolean connectionTelemetryEnabled; private final boolean sslTelemetryEnabled; + private final Map peerServiceMapping; public NettyClientInstrumenterFactory( - String instrumentationName, boolean connectionTelemetryEnabled, boolean sslTelemetryEnabled) { + OpenTelemetry openTelemetry, + String instrumentationName, + boolean connectionTelemetryEnabled, + boolean sslTelemetryEnabled, + Map peerServiceMapping) { + this.openTelemetry = openTelemetry; this.instrumentationName = instrumentationName; this.connectionTelemetryEnabled = connectionTelemetryEnabled; this.sslTelemetryEnabled = sslTelemetryEnabled; + this.peerServiceMapping = peerServiceMapping; } - public Instrumenter createHttpInstrumenter() { + public Instrumenter createHttpInstrumenter( + List capturedRequestHeaders, + List capturedResponseHeaders, + List> + additionalHttpAttributeExtractors) { NettyHttpClientAttributesGetter httpClientAttributesGetter = new NettyHttpClientAttributesGetter(); NettyNetClientAttributesGetter netAttributesGetter = new NettyNetClientAttributesGetter(); return Instrumenter.builder( - GlobalOpenTelemetry.get(), + openTelemetry, instrumentationName, HttpSpanNameExtractor.create(httpClientAttributesGetter)) .setSpanStatusExtractor(HttpSpanStatusExtractor.create(httpClientAttributesGetter)) - .addAttributesExtractor(HttpClientAttributesExtractor.create(httpClientAttributesGetter)) + .addAttributesExtractor( + HttpClientAttributesExtractor.builder(httpClientAttributesGetter) + .setCapturedRequestHeaders(capturedRequestHeaders) + .setCapturedResponseHeaders(capturedResponseHeaders) + .build()) .addAttributesExtractor(NetClientAttributesExtractor.create(netAttributesGetter)) - .addAttributesExtractor(PeerServiceAttributesExtractor.create(netAttributesGetter)) + .addAttributesExtractor( + PeerServiceAttributesExtractor.create(netAttributesGetter, peerServiceMapping)) + .addAttributesExtractors(additionalHttpAttributeExtractors) .addOperationMetrics(HttpClientMetrics.get()) - .newClientInstrumenter(HttpRequestHeadersSetter.INSTANCE); + .buildClientInstrumenter(HttpRequestHeadersSetter.INSTANCE); } public NettyConnectionInstrumenter createConnectionInstrumenter() { @@ -56,9 +81,10 @@ public NettyConnectionInstrumenter createConnectionInstrumenter() { InstrumenterBuilder instrumenterBuilder = Instrumenter.builder( - GlobalOpenTelemetry.get(), instrumentationName, NettyConnectionRequest::spanName) + openTelemetry, instrumentationName, NettyConnectionRequest::spanName) .addAttributesExtractor(NetClientAttributesExtractor.create(netAttributesGetter)) - .addAttributesExtractor(PeerServiceAttributesExtractor.create(netAttributesGetter)); + .addAttributesExtractor( + PeerServiceAttributesExtractor.create(netAttributesGetter, peerServiceMapping)); if (!connectionTelemetryEnabled) { // when the connection telemetry is not enabled, netty creates CONNECT spans whenever a // connection error occurs - because there is no HTTP span in that scenario, if raw netty @@ -71,7 +97,7 @@ public NettyConnectionInstrumenter createConnectionInstrumenter() { } Instrumenter instrumenter = - instrumenterBuilder.newInstrumenter( + instrumenterBuilder.buildInstrumenter( connectionTelemetryEnabled ? SpanKindExtractor.alwaysInternal() : SpanKindExtractor.alwaysClient()); @@ -85,10 +111,11 @@ public NettySslInstrumenter createSslInstrumenter() { NettySslNetAttributesGetter netAttributesGetter = new NettySslNetAttributesGetter(); Instrumenter instrumenter = Instrumenter.builder( - GlobalOpenTelemetry.get(), instrumentationName, NettySslRequest::spanName) + openTelemetry, instrumentationName, NettySslRequest::spanName) .addAttributesExtractor(NetClientAttributesExtractor.create(netAttributesGetter)) - .addAttributesExtractor(PeerServiceAttributesExtractor.create(netAttributesGetter)) - .newInstrumenter( + .addAttributesExtractor( + PeerServiceAttributesExtractor.create(netAttributesGetter, peerServiceMapping)) + .buildInstrumenter( sslTelemetryEnabled ? SpanKindExtractor.alwaysInternal() : SpanKindExtractor.alwaysClient()); diff --git a/instrumentation/netty/netty-4-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4/common/client/NettyConnectNetAttributesGetter.java b/instrumentation/netty/netty-4-common/library/src/main/java/io/opentelemetry/instrumentation/netty/v4/common/internal/client/NettyConnectNetAttributesGetter.java similarity index 51% rename from instrumentation/netty/netty-4-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4/common/client/NettyConnectNetAttributesGetter.java rename to instrumentation/netty/netty-4-common/library/src/main/java/io/opentelemetry/instrumentation/netty/v4/common/internal/client/NettyConnectNetAttributesGetter.java index e4eed780c643..00707eac27c4 100644 --- a/instrumentation/netty/netty-4-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4/common/client/NettyConnectNetAttributesGetter.java +++ b/instrumentation/netty/netty-4-common/library/src/main/java/io/opentelemetry/instrumentation/netty/v4/common/internal/client/NettyConnectNetAttributesGetter.java @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.javaagent.instrumentation.netty.v4.common.client; +package io.opentelemetry.instrumentation.netty.v4.common.internal.client; import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.NetTransportValues.IP_TCP; import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.NetTransportValues.IP_UDP; @@ -11,7 +11,7 @@ import io.netty.channel.Channel; import io.netty.channel.socket.DatagramChannel; import io.opentelemetry.instrumentation.api.instrumenter.net.InetSocketAddressNetClientAttributesGetter; -import io.opentelemetry.javaagent.instrumentation.netty.common.NettyConnectionRequest; +import io.opentelemetry.instrumentation.netty.common.internal.NettyConnectionRequest; import java.net.InetSocketAddress; import java.net.SocketAddress; import javax.annotation.Nullable; @@ -19,25 +19,42 @@ final class NettyConnectNetAttributesGetter extends InetSocketAddressNetClientAttributesGetter { + @Override + public String transport(NettyConnectionRequest request, @Nullable Channel channel) { + return channel instanceof DatagramChannel ? IP_UDP : IP_TCP; + } + @Nullable @Override - public InetSocketAddress getAddress(NettyConnectionRequest request, @Nullable Channel channel) { - SocketAddress remoteAddress = null; - if (channel != null) { - remoteAddress = channel.remoteAddress(); - } - // remote address on end() may be null when connection hasn't been established - if (remoteAddress == null) { - remoteAddress = request.remoteAddressOnStart(); + public String peerName(NettyConnectionRequest request) { + SocketAddress requestedAddress = request.remoteAddressOnStart(); + if (requestedAddress instanceof InetSocketAddress) { + return ((InetSocketAddress) requestedAddress).getHostString(); } - if (remoteAddress instanceof InetSocketAddress) { - return (InetSocketAddress) remoteAddress; + return null; + } + + @Nullable + @Override + public Integer peerPort(NettyConnectionRequest request) { + SocketAddress requestedAddress = request.remoteAddressOnStart(); + if (requestedAddress instanceof InetSocketAddress) { + return ((InetSocketAddress) requestedAddress).getPort(); } return null; } + @Nullable @Override - public String transport(NettyConnectionRequest request, @Nullable Channel channel) { - return channel instanceof DatagramChannel ? IP_UDP : IP_TCP; + protected InetSocketAddress getPeerSocketAddress( + NettyConnectionRequest request, @Nullable Channel channel) { + if (channel == null) { + return null; + } + SocketAddress remoteAddress = channel.remoteAddress(); + if (remoteAddress instanceof InetSocketAddress) { + return (InetSocketAddress) remoteAddress; + } + return null; } } diff --git a/instrumentation/netty/netty-4-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4/common/client/NettyConnectionInstrumenter.java b/instrumentation/netty/netty-4-common/library/src/main/java/io/opentelemetry/instrumentation/netty/v4/common/internal/client/NettyConnectionInstrumenter.java similarity index 64% rename from instrumentation/netty/netty-4-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4/common/client/NettyConnectionInstrumenter.java rename to instrumentation/netty/netty-4-common/library/src/main/java/io/opentelemetry/instrumentation/netty/v4/common/internal/client/NettyConnectionInstrumenter.java index 387cda04955f..5f671971fc44 100644 --- a/instrumentation/netty/netty-4-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4/common/client/NettyConnectionInstrumenter.java +++ b/instrumentation/netty/netty-4-common/library/src/main/java/io/opentelemetry/instrumentation/netty/v4/common/internal/client/NettyConnectionInstrumenter.java @@ -3,13 +3,17 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.javaagent.instrumentation.netty.v4.common.client; +package io.opentelemetry.instrumentation.netty.v4.common.internal.client; import io.netty.channel.Channel; import io.opentelemetry.context.Context; -import io.opentelemetry.javaagent.instrumentation.netty.common.NettyConnectionRequest; +import io.opentelemetry.instrumentation.netty.common.internal.NettyConnectionRequest; 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 interface NettyConnectionInstrumenter { boolean shouldStart(Context parentContext, NettyConnectionRequest request); diff --git a/instrumentation/netty/netty-4-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4/common/client/NettyConnectionInstrumenterImpl.java b/instrumentation/netty/netty-4-common/library/src/main/java/io/opentelemetry/instrumentation/netty/v4/common/internal/client/NettyConnectionInstrumenterImpl.java similarity index 86% rename from instrumentation/netty/netty-4-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4/common/client/NettyConnectionInstrumenterImpl.java rename to instrumentation/netty/netty-4-common/library/src/main/java/io/opentelemetry/instrumentation/netty/v4/common/internal/client/NettyConnectionInstrumenterImpl.java index 2b4c09624688..7c9f77f6bd47 100644 --- a/instrumentation/netty/netty-4-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4/common/client/NettyConnectionInstrumenterImpl.java +++ b/instrumentation/netty/netty-4-common/library/src/main/java/io/opentelemetry/instrumentation/netty/v4/common/internal/client/NettyConnectionInstrumenterImpl.java @@ -3,12 +3,12 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.javaagent.instrumentation.netty.v4.common.client; +package io.opentelemetry.instrumentation.netty.v4.common.internal.client; import io.netty.channel.Channel; import io.opentelemetry.context.Context; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; -import io.opentelemetry.javaagent.instrumentation.netty.common.NettyConnectionRequest; +import io.opentelemetry.instrumentation.netty.common.internal.NettyConnectionRequest; import javax.annotation.Nullable; final class NettyConnectionInstrumenterImpl implements NettyConnectionInstrumenter { diff --git a/instrumentation/netty/netty-4-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4/common/client/NettyErrorOnlyConnectionInstrumenter.java b/instrumentation/netty/netty-4-common/library/src/main/java/io/opentelemetry/instrumentation/netty/v4/common/internal/client/NettyErrorOnlyConnectionInstrumenter.java similarity index 85% rename from instrumentation/netty/netty-4-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4/common/client/NettyErrorOnlyConnectionInstrumenter.java rename to instrumentation/netty/netty-4-common/library/src/main/java/io/opentelemetry/instrumentation/netty/v4/common/internal/client/NettyErrorOnlyConnectionInstrumenter.java index 19d88e91b074..5ffd126cc83c 100644 --- a/instrumentation/netty/netty-4-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4/common/client/NettyErrorOnlyConnectionInstrumenter.java +++ b/instrumentation/netty/netty-4-common/library/src/main/java/io/opentelemetry/instrumentation/netty/v4/common/internal/client/NettyErrorOnlyConnectionInstrumenter.java @@ -3,14 +3,14 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.javaagent.instrumentation.netty.v4.common.client; +package io.opentelemetry.instrumentation.netty.v4.common.internal.client; import io.netty.channel.Channel; import io.opentelemetry.context.Context; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; import io.opentelemetry.instrumentation.api.internal.InstrumenterUtil; -import io.opentelemetry.javaagent.instrumentation.netty.common.NettyConnectionRequest; -import io.opentelemetry.javaagent.instrumentation.netty.common.Timer; +import io.opentelemetry.instrumentation.netty.common.internal.NettyConnectionRequest; +import io.opentelemetry.instrumentation.netty.common.internal.Timer; import javax.annotation.Nullable; final class NettyErrorOnlyConnectionInstrumenter implements NettyConnectionInstrumenter { diff --git a/instrumentation/netty/netty-4-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4/common/client/NettyHttpClientAttributesGetter.java b/instrumentation/netty/netty-4-common/library/src/main/java/io/opentelemetry/instrumentation/netty/v4/common/internal/client/NettyHttpClientAttributesGetter.java similarity index 68% rename from instrumentation/netty/netty-4-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4/common/client/NettyHttpClientAttributesGetter.java rename to instrumentation/netty/netty-4-common/library/src/main/java/io/opentelemetry/instrumentation/netty/v4/common/internal/client/NettyHttpClientAttributesGetter.java index 57c10e72042b..f6c3c0dbd85b 100644 --- a/instrumentation/netty/netty-4-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4/common/client/NettyHttpClientAttributesGetter.java +++ b/instrumentation/netty/netty-4-common/library/src/main/java/io/opentelemetry/instrumentation/netty/v4/common/internal/client/NettyHttpClientAttributesGetter.java @@ -3,13 +3,13 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.javaagent.instrumentation.netty.v4.common.client; +package io.opentelemetry.instrumentation.netty.v4.common.internal.client; -import static io.opentelemetry.javaagent.instrumentation.netty.v4.common.HttpSchemeUtil.getScheme; +import static io.opentelemetry.instrumentation.netty.v4.common.internal.HttpSchemeUtil.getScheme; import io.netty.handler.codec.http.HttpResponse; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpClientAttributesGetter; -import io.opentelemetry.javaagent.instrumentation.netty.v4.common.HttpRequestAndChannel; +import io.opentelemetry.instrumentation.netty.v4.common.HttpRequestAndChannel; import java.net.URI; import java.net.URISyntaxException; import java.util.List; @@ -59,38 +59,11 @@ public List requestHeader(HttpRequestAndChannel requestAndChannel, Strin } @Override - @Nullable - public Long requestContentLength( - HttpRequestAndChannel requestAndChannel, @Nullable HttpResponse response) { - return null; - } - - @Override - @Nullable - public Long requestContentLengthUncompressed( - HttpRequestAndChannel requestAndChannel, @Nullable HttpResponse response) { - return null; - } - - @Override - public Integer statusCode(HttpRequestAndChannel requestAndChannel, HttpResponse response) { + public Integer statusCode( + HttpRequestAndChannel requestAndChannel, HttpResponse response, @Nullable Throwable error) { return response.getStatus().code(); } - @Override - @Nullable - public Long responseContentLength( - HttpRequestAndChannel requestAndChannel, HttpResponse response) { - return null; - } - - @Override - @Nullable - public Long responseContentLengthUncompressed( - HttpRequestAndChannel requestAndChannel, HttpResponse response) { - return null; - } - @Override public List responseHeader( HttpRequestAndChannel requestAndChannel, HttpResponse response, String name) { diff --git a/instrumentation/netty/netty-4-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4/common/client/NettyNetClientAttributesGetter.java b/instrumentation/netty/netty-4-common/library/src/main/java/io/opentelemetry/instrumentation/netty/v4/common/internal/client/NettyNetClientAttributesGetter.java similarity index 55% rename from instrumentation/netty/netty-4-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4/common/client/NettyNetClientAttributesGetter.java rename to instrumentation/netty/netty-4-common/library/src/main/java/io/opentelemetry/instrumentation/netty/v4/common/internal/client/NettyNetClientAttributesGetter.java index eaa541fd7b5b..5607dd52238e 100644 --- a/instrumentation/netty/netty-4-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4/common/client/NettyNetClientAttributesGetter.java +++ b/instrumentation/netty/netty-4-common/library/src/main/java/io/opentelemetry/instrumentation/netty/v4/common/internal/client/NettyNetClientAttributesGetter.java @@ -3,11 +3,15 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.javaagent.instrumentation.netty.v4.common.client; +package io.opentelemetry.instrumentation.netty.v4.common.internal.client; +import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.NetTransportValues.IP_TCP; +import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.NetTransportValues.IP_UDP; + +import io.netty.channel.socket.DatagramChannel; import io.netty.handler.codec.http.HttpResponse; import io.opentelemetry.instrumentation.api.instrumenter.net.InetSocketAddressNetClientAttributesGetter; -import io.opentelemetry.javaagent.instrumentation.netty.v4.common.HttpRequestAndChannel; +import io.opentelemetry.instrumentation.netty.v4.common.HttpRequestAndChannel; import java.net.InetSocketAddress; import java.net.SocketAddress; import javax.annotation.Nullable; @@ -16,20 +20,31 @@ final class NettyNetClientAttributesGetter extends InetSocketAddressNetClientAttributesGetter { @Override - @Nullable - public InetSocketAddress getAddress( + public String transport( HttpRequestAndChannel requestAndChannel, @Nullable HttpResponse response) { - SocketAddress address = requestAndChannel.remoteAddress(); - if (address instanceof InetSocketAddress) { - return (InetSocketAddress) address; - } + return requestAndChannel.channel() instanceof DatagramChannel ? IP_UDP : IP_TCP; + } + + @Nullable + @Override + public String peerName(HttpRequestAndChannel requestAndChannel) { + return null; + } + + @Nullable + @Override + public Integer peerPort(HttpRequestAndChannel requestAndChannel) { return null; } @Override @Nullable - public String transport( + protected InetSocketAddress getPeerSocketAddress( HttpRequestAndChannel requestAndChannel, @Nullable HttpResponse response) { + SocketAddress address = requestAndChannel.remoteAddress(); + if (address instanceof InetSocketAddress) { + return (InetSocketAddress) address; + } return null; } } diff --git a/instrumentation/netty/netty-4-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4/common/client/NettySslErrorOnlyInstrumenter.java b/instrumentation/netty/netty-4-common/library/src/main/java/io/opentelemetry/instrumentation/netty/v4/common/internal/client/NettySslErrorOnlyInstrumenter.java similarity index 89% rename from instrumentation/netty/netty-4-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4/common/client/NettySslErrorOnlyInstrumenter.java rename to instrumentation/netty/netty-4-common/library/src/main/java/io/opentelemetry/instrumentation/netty/v4/common/internal/client/NettySslErrorOnlyInstrumenter.java index cf7fdc16d468..076954e52a37 100644 --- a/instrumentation/netty/netty-4-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4/common/client/NettySslErrorOnlyInstrumenter.java +++ b/instrumentation/netty/netty-4-common/library/src/main/java/io/opentelemetry/instrumentation/netty/v4/common/internal/client/NettySslErrorOnlyInstrumenter.java @@ -3,12 +3,12 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.javaagent.instrumentation.netty.v4.common.client; +package io.opentelemetry.instrumentation.netty.v4.common.internal.client; import io.opentelemetry.context.Context; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; import io.opentelemetry.instrumentation.api.internal.InstrumenterUtil; -import io.opentelemetry.javaagent.instrumentation.netty.common.Timer; +import io.opentelemetry.instrumentation.netty.common.internal.Timer; import javax.annotation.Nullable; final class NettySslErrorOnlyInstrumenter implements NettySslInstrumenter { diff --git a/instrumentation/netty/netty-4-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4/common/client/NettySslInstrumentationHandler.java b/instrumentation/netty/netty-4-common/library/src/main/java/io/opentelemetry/instrumentation/netty/v4/common/internal/client/NettySslInstrumentationHandler.java similarity index 78% rename from instrumentation/netty/netty-4-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4/common/client/NettySslInstrumentationHandler.java rename to instrumentation/netty/netty-4-common/library/src/main/java/io/opentelemetry/instrumentation/netty/v4/common/internal/client/NettySslInstrumentationHandler.java index 4e46e8df28e0..54c6fa19a14c 100644 --- a/instrumentation/netty/netty-4-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4/common/client/NettySslInstrumentationHandler.java +++ b/instrumentation/netty/netty-4-common/library/src/main/java/io/opentelemetry/instrumentation/netty/v4/common/internal/client/NettySslInstrumentationHandler.java @@ -3,23 +3,34 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.javaagent.instrumentation.netty.v4.common.client; +package io.opentelemetry.instrumentation.netty.v4.common.internal.client; import io.netty.channel.ChannelDuplexHandler; +import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelPromise; import io.opentelemetry.context.Context; +import io.opentelemetry.instrumentation.api.util.VirtualField; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; import java.net.SocketAddress; +/** + * This class is internal and is hence not for public use. Its APIs are unstable and can change at + * any time. + */ // inspired by reactor-netty SslProvider.SslReadHandler public final class NettySslInstrumentationHandler extends ChannelDuplexHandler { private static final Class SSL_HANDSHAKE_COMPLETION_EVENT; private static final MethodHandle GET_CAUSE; + // this is used elsewhere to manage the link between the underlying (user) handler and our handler + // which is needed below so that we can unlink this handler when we remove it below + private static final VirtualField instrumentationHandlerField = + VirtualField.find(ChannelHandler.class, ChannelHandler.class); + static { Class sslHandshakeCompletionEvent = null; MethodHandle getCause = null; @@ -41,12 +52,15 @@ public final class NettySslInstrumentationHandler extends ChannelDuplexHandler { } private final NettySslInstrumenter instrumenter; + private final ChannelHandler realHandler; private Context parentContext; private NettySslRequest request; private Context context; - public NettySslInstrumentationHandler(NettySslInstrumenter instrumenter) { + public NettySslInstrumentationHandler( + NettySslInstrumenter instrumenter, ChannelHandler realHandler) { this.instrumenter = instrumenter; + this.realHandler = realHandler; } @Override @@ -55,6 +69,7 @@ public void channelRegistered(ChannelHandlerContext ctx) { // are on classpath); checking just to be extra safe if (SSL_HANDSHAKE_COMPLETION_EVENT == null) { ctx.pipeline().remove(this); + instrumentationHandlerField.set(realHandler, null); ctx.fireChannelRegistered(); return; } @@ -94,6 +109,7 @@ public void userEventTriggered(ChannelHandlerContext ctx, Object evt) { if (SSL_HANDSHAKE_COMPLETION_EVENT.isInstance(evt)) { if (ctx.pipeline().context(this) != null) { ctx.pipeline().remove(this); + instrumentationHandlerField.set(realHandler, null); } if (context != null) { diff --git a/instrumentation/netty/netty-4-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4/common/client/NettySslInstrumenter.java b/instrumentation/netty/netty-4-common/library/src/main/java/io/opentelemetry/instrumentation/netty/v4/common/internal/client/NettySslInstrumenter.java similarity index 68% rename from instrumentation/netty/netty-4-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4/common/client/NettySslInstrumenter.java rename to instrumentation/netty/netty-4-common/library/src/main/java/io/opentelemetry/instrumentation/netty/v4/common/internal/client/NettySslInstrumenter.java index c7943e16cd3b..556b63dedadd 100644 --- a/instrumentation/netty/netty-4-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4/common/client/NettySslInstrumenter.java +++ b/instrumentation/netty/netty-4-common/library/src/main/java/io/opentelemetry/instrumentation/netty/v4/common/internal/client/NettySslInstrumenter.java @@ -3,11 +3,15 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.javaagent.instrumentation.netty.v4.common.client; +package io.opentelemetry.instrumentation.netty.v4.common.internal.client; import io.opentelemetry.context.Context; 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 interface NettySslInstrumenter { boolean shouldStart(Context parentContext, NettySslRequest request); diff --git a/instrumentation/netty/netty-4-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4/common/client/NettySslInstrumenterImpl.java b/instrumentation/netty/netty-4-common/library/src/main/java/io/opentelemetry/instrumentation/netty/v4/common/internal/client/NettySslInstrumenterImpl.java similarity index 92% rename from instrumentation/netty/netty-4-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4/common/client/NettySslInstrumenterImpl.java rename to instrumentation/netty/netty-4-common/library/src/main/java/io/opentelemetry/instrumentation/netty/v4/common/internal/client/NettySslInstrumenterImpl.java index a3466eb0a81d..f281140ef53d 100644 --- a/instrumentation/netty/netty-4-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4/common/client/NettySslInstrumenterImpl.java +++ b/instrumentation/netty/netty-4-common/library/src/main/java/io/opentelemetry/instrumentation/netty/v4/common/internal/client/NettySslInstrumenterImpl.java @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.javaagent.instrumentation.netty.v4.common.client; +package io.opentelemetry.instrumentation.netty.v4.common.internal.client; import io.opentelemetry.context.Context; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; diff --git a/instrumentation/netty/netty-4-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4/common/client/NettySslNetAttributesGetter.java b/instrumentation/netty/netty-4-common/library/src/main/java/io/opentelemetry/instrumentation/netty/v4/common/internal/client/NettySslNetAttributesGetter.java similarity index 71% rename from instrumentation/netty/netty-4-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4/common/client/NettySslNetAttributesGetter.java rename to instrumentation/netty/netty-4-common/library/src/main/java/io/opentelemetry/instrumentation/netty/v4/common/internal/client/NettySslNetAttributesGetter.java index a836893583d9..de58ef52ab03 100644 --- a/instrumentation/netty/netty-4-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4/common/client/NettySslNetAttributesGetter.java +++ b/instrumentation/netty/netty-4-common/library/src/main/java/io/opentelemetry/instrumentation/netty/v4/common/internal/client/NettySslNetAttributesGetter.java @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.javaagent.instrumentation.netty.v4.common.client; +package io.opentelemetry.instrumentation.netty.v4.common.internal.client; import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.NetTransportValues.IP_TCP; import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.NetTransportValues.IP_UDP; @@ -16,17 +16,29 @@ final class NettySslNetAttributesGetter extends InetSocketAddressNetClientAttributesGetter { + @Override + public String transport(NettySslRequest request, @Nullable Void unused) { + return request.channel() instanceof DatagramChannel ? IP_UDP : IP_TCP; + } + @Nullable @Override - public InetSocketAddress getAddress(NettySslRequest request, @Nullable Void unused) { - if (request.remoteAddress() instanceof InetSocketAddress) { - return (InetSocketAddress) request.remoteAddress(); - } + public String peerName(NettySslRequest nettySslRequest) { return null; } + @Nullable @Override - public String transport(NettySslRequest request, @Nullable Void unused) { - return request.channel() instanceof DatagramChannel ? IP_UDP : IP_TCP; + public Integer peerPort(NettySslRequest nettySslRequest) { + return null; + } + + @Nullable + @Override + protected InetSocketAddress getPeerSocketAddress(NettySslRequest request, @Nullable Void unused) { + if (request.remoteAddress() instanceof InetSocketAddress) { + return (InetSocketAddress) request.remoteAddress(); + } + return null; } } diff --git a/instrumentation/netty/netty-4-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4/common/client/NettySslRequest.java b/instrumentation/netty/netty-4-common/library/src/main/java/io/opentelemetry/instrumentation/netty/v4/common/internal/client/NettySslRequest.java similarity index 73% rename from instrumentation/netty/netty-4-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4/common/client/NettySslRequest.java rename to instrumentation/netty/netty-4-common/library/src/main/java/io/opentelemetry/instrumentation/netty/v4/common/internal/client/NettySslRequest.java index 50a88887d292..738408e4f6b2 100644 --- a/instrumentation/netty/netty-4-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4/common/client/NettySslRequest.java +++ b/instrumentation/netty/netty-4-common/library/src/main/java/io/opentelemetry/instrumentation/netty/v4/common/internal/client/NettySslRequest.java @@ -3,13 +3,17 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.javaagent.instrumentation.netty.v4.common.client; +package io.opentelemetry.instrumentation.netty.v4.common.internal.client; import com.google.auto.value.AutoValue; import io.netty.channel.Channel; import java.net.SocketAddress; import javax.annotation.Nullable; +/** + * This class is internal and is hence not for public use. Its APIs are unstable and can change at + * any time. + */ @AutoValue public abstract class NettySslRequest { diff --git a/instrumentation/netty/netty-4-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4/common/server/HttpRequestHeadersGetter.java b/instrumentation/netty/netty-4-common/library/src/main/java/io/opentelemetry/instrumentation/netty/v4/common/internal/server/HttpRequestHeadersGetter.java similarity index 77% rename from instrumentation/netty/netty-4-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4/common/server/HttpRequestHeadersGetter.java rename to instrumentation/netty/netty-4-common/library/src/main/java/io/opentelemetry/instrumentation/netty/v4/common/internal/server/HttpRequestHeadersGetter.java index b3966d784a4a..431168c05c27 100644 --- a/instrumentation/netty/netty-4-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4/common/server/HttpRequestHeadersGetter.java +++ b/instrumentation/netty/netty-4-common/library/src/main/java/io/opentelemetry/instrumentation/netty/v4/common/internal/server/HttpRequestHeadersGetter.java @@ -3,10 +3,10 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.javaagent.instrumentation.netty.v4.common.server; +package io.opentelemetry.instrumentation.netty.v4.common.internal.server; import io.opentelemetry.context.propagation.TextMapGetter; -import io.opentelemetry.javaagent.instrumentation.netty.v4.common.HttpRequestAndChannel; +import io.opentelemetry.instrumentation.netty.v4.common.HttpRequestAndChannel; import javax.annotation.Nullable; enum HttpRequestHeadersGetter implements TextMapGetter { diff --git a/instrumentation/netty/netty-4-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4/common/server/NettyHttpServerAttributesGetter.java b/instrumentation/netty/netty-4-common/library/src/main/java/io/opentelemetry/instrumentation/netty/v4/common/internal/server/NettyHttpServerAttributesGetter.java similarity index 61% rename from instrumentation/netty/netty-4-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4/common/server/NettyHttpServerAttributesGetter.java rename to instrumentation/netty/netty-4-common/library/src/main/java/io/opentelemetry/instrumentation/netty/v4/common/internal/server/NettyHttpServerAttributesGetter.java index 0c1dd32b6511..96870a39db2a 100644 --- a/instrumentation/netty/netty-4-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4/common/server/NettyHttpServerAttributesGetter.java +++ b/instrumentation/netty/netty-4-common/library/src/main/java/io/opentelemetry/instrumentation/netty/v4/common/internal/server/NettyHttpServerAttributesGetter.java @@ -3,13 +3,13 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.javaagent.instrumentation.netty.v4.common.server; +package io.opentelemetry.instrumentation.netty.v4.common.internal.server; -import static io.opentelemetry.javaagent.instrumentation.netty.v4.common.HttpSchemeUtil.getScheme; +import static io.opentelemetry.instrumentation.netty.v4.common.internal.HttpSchemeUtil.getScheme; import io.netty.handler.codec.http.HttpResponse; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpServerAttributesGetter; -import io.opentelemetry.javaagent.instrumentation.netty.v4.common.HttpRequestAndChannel; +import io.opentelemetry.instrumentation.netty.v4.common.HttpRequestAndChannel; import java.util.List; import javax.annotation.Nullable; @@ -27,38 +27,11 @@ public List requestHeader(HttpRequestAndChannel requestAndChannel, Strin } @Override - @Nullable - public Long requestContentLength( - HttpRequestAndChannel requestAndChannel, @Nullable HttpResponse response) { - return null; - } - - @Override - @Nullable - public Long requestContentLengthUncompressed( - HttpRequestAndChannel requestAndChannel, @Nullable HttpResponse response) { - return null; - } - - @Override - public Integer statusCode(HttpRequestAndChannel requestAndChannel, HttpResponse response) { + public Integer statusCode( + HttpRequestAndChannel requestAndChannel, HttpResponse response, @Nullable Throwable error) { return response.getStatus().code(); } - @Override - @Nullable - public Long responseContentLength( - HttpRequestAndChannel requestAndChannel, HttpResponse response) { - return null; - } - - @Override - @Nullable - public Long responseContentLengthUncompressed( - HttpRequestAndChannel requestAndChannel, HttpResponse response) { - return null; - } - @Override public List responseHeader( HttpRequestAndChannel requestAndChannel, HttpResponse response, String name) { @@ -89,10 +62,4 @@ public String route(HttpRequestAndChannel requestAndChannel) { public String scheme(HttpRequestAndChannel requestAndChannel) { return getScheme(requestAndChannel); } - - @Override - @Nullable - public String serverName(HttpRequestAndChannel requestAndChannel) { - return null; - } } diff --git a/instrumentation/netty/netty-4-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4/common/server/NettyNetServerAttributesGetter.java b/instrumentation/netty/netty-4-common/library/src/main/java/io/opentelemetry/instrumentation/netty/v4/common/internal/server/NettyNetServerAttributesGetter.java similarity index 57% rename from instrumentation/netty/netty-4-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4/common/server/NettyNetServerAttributesGetter.java rename to instrumentation/netty/netty-4-common/library/src/main/java/io/opentelemetry/instrumentation/netty/v4/common/internal/server/NettyNetServerAttributesGetter.java index c0247d1ef672..a63a281cac2b 100644 --- a/instrumentation/netty/netty-4-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4/common/server/NettyNetServerAttributesGetter.java +++ b/instrumentation/netty/netty-4-common/library/src/main/java/io/opentelemetry/instrumentation/netty/v4/common/internal/server/NettyNetServerAttributesGetter.java @@ -3,14 +3,14 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.javaagent.instrumentation.netty.v4.common.server; +package io.opentelemetry.instrumentation.netty.v4.common.internal.server; import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.NetTransportValues.IP_TCP; import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.NetTransportValues.IP_UDP; import io.netty.channel.socket.DatagramChannel; import io.opentelemetry.instrumentation.api.instrumenter.net.InetSocketAddressNetServerAttributesGetter; -import io.opentelemetry.javaagent.instrumentation.netty.v4.common.HttpRequestAndChannel; +import io.opentelemetry.instrumentation.netty.v4.common.HttpRequestAndChannel; import java.net.InetSocketAddress; import java.net.SocketAddress; import javax.annotation.Nullable; @@ -19,18 +19,39 @@ final class NettyNetServerAttributesGetter extends InetSocketAddressNetServerAttributesGetter { @Override - @Nullable public String transport(HttpRequestAndChannel requestAndChannel) { return requestAndChannel.channel() instanceof DatagramChannel ? IP_UDP : IP_TCP; } + @Nullable @Override + public String hostName(HttpRequestAndChannel requestAndChannel) { + return null; + } + @Nullable - public InetSocketAddress getAddress(HttpRequestAndChannel requestAndChannel) { + @Override + public Integer hostPort(HttpRequestAndChannel requestAndChannel) { + return null; + } + + @Override + @Nullable + protected InetSocketAddress getPeerSocketAddress(HttpRequestAndChannel requestAndChannel) { SocketAddress address = requestAndChannel.remoteAddress(); if (address instanceof InetSocketAddress) { return (InetSocketAddress) address; } return null; } + + @Nullable + @Override + protected InetSocketAddress getHostSocketAddress(HttpRequestAndChannel requestAndChannel) { + SocketAddress address = requestAndChannel.channel().localAddress(); + if (address instanceof InetSocketAddress) { + return (InetSocketAddress) address; + } + return null; + } } diff --git a/instrumentation/netty/netty-4-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4/common/server/NettyServerInstrumenterFactory.java b/instrumentation/netty/netty-4-common/library/src/main/java/io/opentelemetry/instrumentation/netty/v4/common/internal/server/NettyServerInstrumenterFactory.java similarity index 55% rename from instrumentation/netty/netty-4-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4/common/server/NettyServerInstrumenterFactory.java rename to instrumentation/netty/netty-4-common/library/src/main/java/io/opentelemetry/instrumentation/netty/v4/common/internal/server/NettyServerInstrumenterFactory.java index 6c474603c0db..3655196e3041 100644 --- a/instrumentation/netty/netty-4-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4/common/server/NettyServerInstrumenterFactory.java +++ b/instrumentation/netty/netty-4-common/library/src/main/java/io/opentelemetry/instrumentation/netty/v4/common/internal/server/NettyServerInstrumenterFactory.java @@ -3,39 +3,47 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.javaagent.instrumentation.netty.v4.common.server; +package io.opentelemetry.instrumentation.netty.v4.common.internal.server; import io.netty.handler.codec.http.HttpResponse; -import io.opentelemetry.api.GlobalOpenTelemetry; +import io.opentelemetry.api.OpenTelemetry; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpRouteHolder; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpServerAttributesExtractor; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpServerMetrics; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanNameExtractor; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanStatusExtractor; -import io.opentelemetry.instrumentation.api.instrumenter.net.NetServerAttributesExtractor; -import io.opentelemetry.javaagent.instrumentation.netty.common.NettyErrorHolder; -import io.opentelemetry.javaagent.instrumentation.netty.v4.common.HttpRequestAndChannel; +import io.opentelemetry.instrumentation.netty.common.internal.NettyErrorHolder; +import io.opentelemetry.instrumentation.netty.v4.common.HttpRequestAndChannel; +import java.util.List; +/** + * This class is internal and is hence not for public use. Its APIs are unstable and can change at + * any time. + */ public final class NettyServerInstrumenterFactory { public static Instrumenter create( - String instrumentationName) { + OpenTelemetry openTelemetry, + String instrumentationName, + List capturedRequestHeaders, + List capturedResponseHeaders) { NettyHttpServerAttributesGetter httpAttributesGetter = new NettyHttpServerAttributesGetter(); return Instrumenter.builder( - GlobalOpenTelemetry.get(), - instrumentationName, - HttpSpanNameExtractor.create(httpAttributesGetter)) + openTelemetry, instrumentationName, HttpSpanNameExtractor.create(httpAttributesGetter)) .setSpanStatusExtractor(HttpSpanStatusExtractor.create(httpAttributesGetter)) - .addAttributesExtractor(HttpServerAttributesExtractor.create(httpAttributesGetter)) .addAttributesExtractor( - NetServerAttributesExtractor.create(new NettyNetServerAttributesGetter())) + HttpServerAttributesExtractor.builder( + httpAttributesGetter, new NettyNetServerAttributesGetter()) + .setCapturedRequestHeaders(capturedRequestHeaders) + .setCapturedResponseHeaders(capturedResponseHeaders) + .build()) .addOperationMetrics(HttpServerMetrics.get()) .addContextCustomizer((context, request, attributes) -> NettyErrorHolder.init(context)) .addContextCustomizer(HttpRouteHolder.get()) - .newServerInstrumenter(HttpRequestHeadersGetter.INSTANCE); + .buildServerInstrumenter(HttpRequestHeadersGetter.INSTANCE); } private NettyServerInstrumenterFactory() {} diff --git a/instrumentation/netty/netty-4.0/javaagent/build.gradle.kts b/instrumentation/netty/netty-4.0/javaagent/build.gradle.kts index b088c23d3d4f..09451d3cfdb4 100644 --- a/instrumentation/netty/netty-4.0/javaagent/build.gradle.kts +++ b/instrumentation/netty/netty-4.0/javaagent/build.gradle.kts @@ -26,11 +26,13 @@ muzzle { dependencies { library("io.netty:netty-codec-http:4.0.0.Final") implementation(project(":instrumentation:netty:netty-4-common:javaagent")) + implementation(project(":instrumentation:netty:netty-4-common:library")) + implementation(project(":instrumentation:netty:netty-common:library")) testInstrumentation(project(":instrumentation:netty:netty-3.8:javaagent")) testInstrumentation(project(":instrumentation:netty:netty-4.1:javaagent")) - latestDepTestLibrary("io.netty:netty-codec-http:4.0.+") + latestDepTestLibrary("io.netty:netty-codec-http:4.0.+") // see netty-4.1 module } tasks { diff --git a/instrumentation/netty/netty-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_0/AbstractChannelHandlerContextInstrumentation.java b/instrumentation/netty/netty-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_0/AbstractChannelHandlerContextInstrumentation.java index 1dc611ed1bc0..58d5addb2370 100644 --- a/instrumentation/netty/netty-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_0/AbstractChannelHandlerContextInstrumentation.java +++ b/instrumentation/netty/netty-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_0/AbstractChannelHandlerContextInstrumentation.java @@ -14,10 +14,10 @@ import io.netty.channel.ChannelHandlerContext; import io.netty.util.Attribute; import io.opentelemetry.context.Context; +import io.opentelemetry.instrumentation.netty.common.internal.NettyErrorHolder; +import io.opentelemetry.instrumentation.netty.v4.common.HttpRequestAndChannel; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; -import io.opentelemetry.javaagent.instrumentation.netty.common.NettyErrorHolder; -import io.opentelemetry.javaagent.instrumentation.netty.v4.common.HttpRequestAndChannel; import net.bytebuddy.asm.Advice; import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.matcher.ElementMatcher; diff --git a/instrumentation/netty/netty-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_0/AttributeKeys.java b/instrumentation/netty/netty-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_0/AttributeKeys.java index 6214f60e2ee4..d598dfd40515 100644 --- a/instrumentation/netty/netty-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_0/AttributeKeys.java +++ b/instrumentation/netty/netty-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_0/AttributeKeys.java @@ -8,7 +8,7 @@ import io.netty.handler.codec.http.HttpResponse; import io.netty.util.AttributeKey; import io.opentelemetry.context.Context; -import io.opentelemetry.javaagent.instrumentation.netty.v4.common.HttpRequestAndChannel; +import io.opentelemetry.instrumentation.netty.v4.common.HttpRequestAndChannel; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; @@ -58,4 +58,6 @@ public static AttributeKey attributeKey(String key) { ConcurrentMap> classLoaderMap = mapSupplier.get(AttributeKey.class); return (AttributeKey) classLoaderMap.computeIfAbsent(key, AttributeKey::new); } + + private AttributeKeys() {} } diff --git a/instrumentation/netty/netty-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_0/BootstrapInstrumentation.java b/instrumentation/netty/netty-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_0/BootstrapInstrumentation.java index a05e101356d2..e7260ed353be 100644 --- a/instrumentation/netty/netty-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_0/BootstrapInstrumentation.java +++ b/instrumentation/netty/netty-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_0/BootstrapInstrumentation.java @@ -12,11 +12,11 @@ import io.netty.channel.ChannelPromise; import io.opentelemetry.context.Context; import io.opentelemetry.context.Scope; +import io.opentelemetry.instrumentation.netty.common.internal.NettyConnectionRequest; +import io.opentelemetry.instrumentation.netty.v4.common.internal.client.ConnectionCompleteListener; import io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; -import io.opentelemetry.javaagent.instrumentation.netty.common.NettyConnectionRequest; -import io.opentelemetry.javaagent.instrumentation.netty.v4.common.client.ConnectionCompleteListener; import java.net.SocketAddress; import net.bytebuddy.asm.Advice; import net.bytebuddy.description.type.TypeDescription; diff --git a/instrumentation/netty/netty-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_0/NettyChannelPipelineInstrumentation.java b/instrumentation/netty/netty-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_0/NettyChannelPipelineInstrumentation.java index faa016c85ade..f3dcd2eb463b 100644 --- a/instrumentation/netty/netty-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_0/NettyChannelPipelineInstrumentation.java +++ b/instrumentation/netty/netty-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_0/NettyChannelPipelineInstrumentation.java @@ -20,10 +20,10 @@ import io.netty.handler.codec.http.HttpResponseEncoder; import io.netty.handler.codec.http.HttpServerCodec; import io.opentelemetry.instrumentation.api.util.VirtualField; +import io.opentelemetry.instrumentation.netty.v4.common.internal.client.NettySslInstrumentationHandler; import io.opentelemetry.javaagent.bootstrap.CallDepth; import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; import io.opentelemetry.javaagent.instrumentation.netty.v4.common.AbstractNettyChannelPipelineInstrumentation; -import io.opentelemetry.javaagent.instrumentation.netty.v4.common.client.NettySslInstrumentationHandler; import io.opentelemetry.javaagent.instrumentation.netty.v4_0.client.HttpClientRequestTracingHandler; import io.opentelemetry.javaagent.instrumentation.netty.v4_0.client.HttpClientResponseTracingHandler; import io.opentelemetry.javaagent.instrumentation.netty.v4_0.client.HttpClientTracingHandler; @@ -95,7 +95,7 @@ public static void addHandler( // the SslHandler lives in the netty-handler module, using class name comparison to avoid // adding a dependency } else if (handler.getClass().getName().equals("io.netty.handler.ssl.SslHandler")) { - ourHandler = new NettySslInstrumentationHandler(sslInstrumenter()); + ourHandler = new NettySslInstrumentationHandler(sslInstrumenter(), handler); } if (ourHandler != null) { diff --git a/instrumentation/netty/netty-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_0/client/HttpClientRequestTracingHandler.java b/instrumentation/netty/netty-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_0/client/HttpClientRequestTracingHandler.java index 2c7506268ee0..6ba14db4801d 100644 --- a/instrumentation/netty/netty-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_0/client/HttpClientRequestTracingHandler.java +++ b/instrumentation/netty/netty-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_0/client/HttpClientRequestTracingHandler.java @@ -14,7 +14,7 @@ import io.netty.util.Attribute; import io.opentelemetry.context.Context; import io.opentelemetry.context.Scope; -import io.opentelemetry.javaagent.instrumentation.netty.v4.common.HttpRequestAndChannel; +import io.opentelemetry.instrumentation.netty.v4.common.HttpRequestAndChannel; import io.opentelemetry.javaagent.instrumentation.netty.v4_0.AttributeKeys; public class HttpClientRequestTracingHandler extends ChannelOutboundHandlerAdapter { diff --git a/instrumentation/netty/netty-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_0/client/HttpClientResponseTracingHandler.java b/instrumentation/netty/netty-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_0/client/HttpClientResponseTracingHandler.java index 10637bd7b90b..b0cdd86dbbd1 100644 --- a/instrumentation/netty/netty-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_0/client/HttpClientResponseTracingHandler.java +++ b/instrumentation/netty/netty-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_0/client/HttpClientResponseTracingHandler.java @@ -15,7 +15,7 @@ import io.netty.util.Attribute; import io.opentelemetry.context.Context; import io.opentelemetry.context.Scope; -import io.opentelemetry.javaagent.instrumentation.netty.v4.common.HttpRequestAndChannel; +import io.opentelemetry.instrumentation.netty.v4.common.HttpRequestAndChannel; import io.opentelemetry.javaagent.instrumentation.netty.v4_0.AttributeKeys; public class HttpClientResponseTracingHandler extends ChannelInboundHandlerAdapter { diff --git a/instrumentation/netty/netty-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_0/client/NettyClientSingletons.java b/instrumentation/netty/netty-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_0/client/NettyClientSingletons.java index 3c7dc8e2d7eb..620f46487e23 100644 --- a/instrumentation/netty/netty-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_0/client/NettyClientSingletons.java +++ b/instrumentation/netty/netty-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_0/client/NettyClientSingletons.java @@ -6,13 +6,16 @@ package io.opentelemetry.javaagent.instrumentation.netty.v4_0.client; import io.netty.handler.codec.http.HttpResponse; +import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; -import io.opentelemetry.javaagent.bootstrap.internal.DeprecatedConfigPropertyWarning; +import io.opentelemetry.instrumentation.netty.v4.common.HttpRequestAndChannel; +import io.opentelemetry.instrumentation.netty.v4.common.internal.client.NettyClientInstrumenterFactory; +import io.opentelemetry.instrumentation.netty.v4.common.internal.client.NettyConnectionInstrumenter; +import io.opentelemetry.instrumentation.netty.v4.common.internal.client.NettySslInstrumenter; +import io.opentelemetry.javaagent.bootstrap.internal.CommonConfig; +import io.opentelemetry.javaagent.bootstrap.internal.DeprecatedConfigProperties; import io.opentelemetry.javaagent.bootstrap.internal.InstrumentationConfig; -import io.opentelemetry.javaagent.instrumentation.netty.v4.common.HttpRequestAndChannel; -import io.opentelemetry.javaagent.instrumentation.netty.v4.common.client.NettyClientInstrumenterFactory; -import io.opentelemetry.javaagent.instrumentation.netty.v4.common.client.NettyConnectionInstrumenter; -import io.opentelemetry.javaagent.instrumentation.netty.v4.common.client.NettySslInstrumenter; +import java.util.Collections; public final class NettyClientSingletons { @@ -21,15 +24,12 @@ public final class NettyClientSingletons { static { InstrumentationConfig config = InstrumentationConfig.get(); - DeprecatedConfigPropertyWarning.warnIfUsed( - config, - "otel.instrumentation.netty.always-create-connect-span", - "otel.instrumentation.netty.connection-telemetry.enabled"); - boolean alwaysCreateConnectSpan = - config.getBoolean("otel.instrumentation.netty.always-create-connect-span", false); connectionTelemetryEnabled = - config.getBoolean( - "otel.instrumentation.netty.connection-telemetry.enabled", alwaysCreateConnectSpan); + DeprecatedConfigProperties.getBoolean( + config, + "otel.instrumentation.netty.always-create-connect-span", + "otel.instrumentation.netty.connection-telemetry.enabled", + false); sslTelemetryEnabled = config.getBoolean("otel.instrumentation.netty.ssl-telemetry.enabled", false); } @@ -41,8 +41,16 @@ public final class NettyClientSingletons { static { NettyClientInstrumenterFactory factory = new NettyClientInstrumenterFactory( - "io.opentelemetry.netty-4.0", connectionTelemetryEnabled, sslTelemetryEnabled); - INSTRUMENTER = factory.createHttpInstrumenter(); + GlobalOpenTelemetry.get(), + "io.opentelemetry.netty-4.0", + connectionTelemetryEnabled, + sslTelemetryEnabled, + CommonConfig.get().getPeerServiceMapping()); + INSTRUMENTER = + factory.createHttpInstrumenter( + CommonConfig.get().getClientRequestHeaders(), + CommonConfig.get().getClientResponseHeaders(), + Collections.emptyList()); CONNECTION_INSTRUMENTER = factory.createConnectionInstrumenter(); SSL_INSTRUMENTER = factory.createSslInstrumenter(); } diff --git a/instrumentation/netty/netty-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_0/server/HttpServerRequestTracingHandler.java b/instrumentation/netty/netty-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_0/server/HttpServerRequestTracingHandler.java index 8c0dc12ed02d..c5a5c109a11e 100644 --- a/instrumentation/netty/netty-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_0/server/HttpServerRequestTracingHandler.java +++ b/instrumentation/netty/netty-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_0/server/HttpServerRequestTracingHandler.java @@ -14,7 +14,7 @@ import io.netty.util.Attribute; import io.opentelemetry.context.Context; import io.opentelemetry.context.Scope; -import io.opentelemetry.javaagent.instrumentation.netty.v4.common.HttpRequestAndChannel; +import io.opentelemetry.instrumentation.netty.v4.common.HttpRequestAndChannel; import io.opentelemetry.javaagent.instrumentation.netty.v4_0.AttributeKeys; public class HttpServerRequestTracingHandler extends ChannelInboundHandlerAdapter { diff --git a/instrumentation/netty/netty-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_0/server/HttpServerResponseTracingHandler.java b/instrumentation/netty/netty-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_0/server/HttpServerResponseTracingHandler.java index a1f3867e1f10..8a4712223142 100644 --- a/instrumentation/netty/netty-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_0/server/HttpServerResponseTracingHandler.java +++ b/instrumentation/netty/netty-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_0/server/HttpServerResponseTracingHandler.java @@ -14,8 +14,8 @@ import io.netty.handler.codec.http.HttpResponse; import io.opentelemetry.context.Context; import io.opentelemetry.context.Scope; -import io.opentelemetry.javaagent.instrumentation.netty.common.NettyErrorHolder; -import io.opentelemetry.javaagent.instrumentation.netty.v4.common.HttpRequestAndChannel; +import io.opentelemetry.instrumentation.netty.common.internal.NettyErrorHolder; +import io.opentelemetry.instrumentation.netty.v4.common.HttpRequestAndChannel; import io.opentelemetry.javaagent.instrumentation.netty.v4_0.AttributeKeys; import javax.annotation.Nullable; diff --git a/instrumentation/netty/netty-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_0/server/NettyServerSingletons.java b/instrumentation/netty/netty-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_0/server/NettyServerSingletons.java index 3c7a79114838..6af51fad8b4d 100644 --- a/instrumentation/netty/netty-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_0/server/NettyServerSingletons.java +++ b/instrumentation/netty/netty-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_0/server/NettyServerSingletons.java @@ -6,14 +6,20 @@ package io.opentelemetry.javaagent.instrumentation.netty.v4_0.server; import io.netty.handler.codec.http.HttpResponse; +import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; -import io.opentelemetry.javaagent.instrumentation.netty.v4.common.HttpRequestAndChannel; -import io.opentelemetry.javaagent.instrumentation.netty.v4.common.server.NettyServerInstrumenterFactory; +import io.opentelemetry.instrumentation.netty.v4.common.HttpRequestAndChannel; +import io.opentelemetry.instrumentation.netty.v4.common.internal.server.NettyServerInstrumenterFactory; +import io.opentelemetry.javaagent.bootstrap.internal.CommonConfig; public final class NettyServerSingletons { private static final Instrumenter INSTRUMENTER = - NettyServerInstrumenterFactory.create("io.opentelemetry.netty-4.0"); + NettyServerInstrumenterFactory.create( + GlobalOpenTelemetry.get(), + "io.opentelemetry.netty-4.0", + CommonConfig.get().getServerRequestHeaders(), + CommonConfig.get().getServerResponseHeaders()); public static Instrumenter instrumenter() { return INSTRUMENTER; diff --git a/instrumentation/netty/netty-4.0/javaagent/src/test/groovy/Netty40ClientSslTest.groovy b/instrumentation/netty/netty-4.0/javaagent/src/test/groovy/Netty40ClientSslTest.groovy index e3bbdb15e8ee..48a5a7d51c38 100644 --- a/instrumentation/netty/netty-4.0/javaagent/src/test/groovy/Netty40ClientSslTest.groovy +++ b/instrumentation/netty/netty-4.0/javaagent/src/test/groovy/Netty40ClientSslTest.groovy @@ -92,7 +92,7 @@ class Netty40ClientSslTest extends AgentInstrumentationSpecification { "$SemanticAttributes.NET_TRANSPORT" IP_TCP "$SemanticAttributes.NET_PEER_NAME" uri.host "$SemanticAttributes.NET_PEER_PORT" uri.port - "$SemanticAttributes.NET_PEER_IP" { it == null || it == "127.0.0.1" } + "$SemanticAttributes.NET_SOCK_PEER_ADDR" { it == "127.0.0.1" || it == null } } } span(2) { @@ -104,9 +104,9 @@ class Netty40ClientSslTest extends AgentInstrumentationSpecification { errorEventWithAnyMessage(SSLHandshakeException) attributes { "$SemanticAttributes.NET_TRANSPORT" IP_TCP - "$SemanticAttributes.NET_PEER_NAME" uri.host - "$SemanticAttributes.NET_PEER_PORT" uri.port - "$SemanticAttributes.NET_PEER_IP" { it == null || it == "127.0.0.1" } + "$SemanticAttributes.NET_SOCK_PEER_ADDR" { it == "127.0.0.1" || it == null } + "$SemanticAttributes.NET_SOCK_PEER_NAME" uri.host + "$SemanticAttributes.NET_SOCK_PEER_PORT" uri.port } } } @@ -148,7 +148,7 @@ class Netty40ClientSslTest extends AgentInstrumentationSpecification { "$SemanticAttributes.NET_TRANSPORT" IP_TCP "$SemanticAttributes.NET_PEER_NAME" uri.host "$SemanticAttributes.NET_PEER_PORT" uri.port - "$SemanticAttributes.NET_PEER_IP" { it == null || it == "127.0.0.1" } + "$SemanticAttributes.NET_SOCK_PEER_ADDR" { it == "127.0.0.1" || it == null } } } span(2) { @@ -157,9 +157,9 @@ class Netty40ClientSslTest extends AgentInstrumentationSpecification { childOf span(0) attributes { "$SemanticAttributes.NET_TRANSPORT" IP_TCP - "$SemanticAttributes.NET_PEER_NAME" uri.host - "$SemanticAttributes.NET_PEER_PORT" uri.port - "$SemanticAttributes.NET_PEER_IP" { it == null || it == "127.0.0.1" } + "$SemanticAttributes.NET_SOCK_PEER_ADDR" { it == "127.0.0.1" || it == null } + "$SemanticAttributes.NET_SOCK_PEER_NAME" uri.host + "$SemanticAttributes.NET_SOCK_PEER_PORT" uri.port } } span(3) { diff --git a/instrumentation/netty/netty-4.0/javaagent/src/test/groovy/Netty40ClientTest.groovy b/instrumentation/netty/netty-4.0/javaagent/src/test/groovy/Netty40ClientTest.groovy index 37b5707c2ea3..1fe13741b222 100644 --- a/instrumentation/netty/netty-4.0/javaagent/src/test/groovy/Netty40ClientTest.groovy +++ b/instrumentation/netty/netty-4.0/javaagent/src/test/groovy/Netty40ClientTest.groovy @@ -23,6 +23,7 @@ import io.opentelemetry.api.common.AttributeKey import io.opentelemetry.instrumentation.test.AgentTestTrait import io.opentelemetry.instrumentation.test.base.HttpClientTest import io.opentelemetry.instrumentation.testing.junit.http.AbstractHttpClientTest +import io.opentelemetry.semconv.trace.attributes.SemanticAttributes import spock.lang.Shared import java.util.concurrent.CompletableFuture @@ -126,7 +127,10 @@ class Netty40ClientTest extends HttpClientTest implement case "http://192.0.2.1/": // non routable address return [] } - return super.httpAttributes(uri) + def attributes = super.httpAttributes(uri) + attributes.remove(SemanticAttributes.NET_PEER_NAME) + attributes.remove(SemanticAttributes.NET_PEER_PORT) + return attributes } @Override diff --git a/instrumentation/netty/netty-4.0/javaagent/src/test/groovy/Netty40ConnectionSpanTest.groovy b/instrumentation/netty/netty-4.0/javaagent/src/test/groovy/Netty40ConnectionSpanTest.groovy index 905c7c6ee5c1..2aa830e80324 100644 --- a/instrumentation/netty/netty-4.0/javaagent/src/test/groovy/Netty40ConnectionSpanTest.groovy +++ b/instrumentation/netty/netty-4.0/javaagent/src/test/groovy/Netty40ConnectionSpanTest.groovy @@ -108,7 +108,7 @@ class Netty40ConnectionSpanTest extends InstrumentationSpecification implements "$SemanticAttributes.NET_TRANSPORT" IP_TCP "$SemanticAttributes.NET_PEER_NAME" uri.host "$SemanticAttributes.NET_PEER_PORT" uri.port - "$SemanticAttributes.NET_PEER_IP" "127.0.0.1" + "$SemanticAttributes.NET_SOCK_PEER_ADDR" "127.0.0.1" } } span(2) { @@ -156,7 +156,7 @@ class Netty40ConnectionSpanTest extends InstrumentationSpecification implements "$SemanticAttributes.NET_TRANSPORT" IP_TCP "$SemanticAttributes.NET_PEER_NAME" uri.host "$SemanticAttributes.NET_PEER_PORT" uri.port - "$SemanticAttributes.NET_PEER_IP" { it == null || it == "127.0.0.1" } + "$SemanticAttributes.NET_SOCK_PEER_ADDR" { it == "127.0.0.1" || it == null } } } } diff --git a/instrumentation/netty/netty-4.1/javaagent/build.gradle.kts b/instrumentation/netty/netty-4.1/javaagent/build.gradle.kts index 18b4a3181fc3..7d4f80ba35cf 100644 --- a/instrumentation/netty/netty-4.1/javaagent/build.gradle.kts +++ b/instrumentation/netty/netty-4.1/javaagent/build.gradle.kts @@ -25,8 +25,12 @@ muzzle { dependencies { library("io.netty:netty-codec-http:4.1.0.Final") - api(project(":instrumentation:netty:netty-4-common:javaagent")) + implementation(project(":instrumentation:netty:netty-4.1:library")) + implementation(project(":instrumentation:netty:netty-4-common:javaagent")) + implementation(project(":instrumentation:netty:netty-4-common:library")) + implementation(project(":instrumentation:netty:netty-common:library")) + testImplementation(project(":instrumentation:netty:netty-4.1:testing")) testInstrumentation(project(":instrumentation:netty:netty-3.8:javaagent")) testInstrumentation(project(":instrumentation:netty:netty-4.0:javaagent")) diff --git a/instrumentation/netty/netty-4.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_1/AbstractChannelHandlerContextInstrumentation.java b/instrumentation/netty/netty-4.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_1/AbstractChannelHandlerContextInstrumentation.java index dc7c010edb26..ad9534622dd3 100644 --- a/instrumentation/netty/netty-4.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_1/AbstractChannelHandlerContextInstrumentation.java +++ b/instrumentation/netty/netty-4.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_1/AbstractChannelHandlerContextInstrumentation.java @@ -5,7 +5,8 @@ package io.opentelemetry.javaagent.instrumentation.netty.v4_1; -import static io.opentelemetry.javaagent.instrumentation.netty.v4_1.client.NettyClientSingletons.instrumenter; +import static io.opentelemetry.instrumentation.netty.v4_1.internal.client.HttpClientRequestTracingHandler.HTTP_REQUEST; +import static io.opentelemetry.javaagent.instrumentation.netty.v4_1.NettyClientSingletons.instrumenter; import static net.bytebuddy.matcher.ElementMatchers.isMethod; import static net.bytebuddy.matcher.ElementMatchers.named; import static net.bytebuddy.matcher.ElementMatchers.takesArgument; @@ -13,11 +14,11 @@ import io.netty.channel.ChannelHandlerContext; import io.netty.util.Attribute; import io.opentelemetry.context.Context; +import io.opentelemetry.instrumentation.netty.common.internal.NettyErrorHolder; +import io.opentelemetry.instrumentation.netty.v4.common.HttpRequestAndChannel; +import io.opentelemetry.instrumentation.netty.v4_1.internal.AttributeKeys; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; -import io.opentelemetry.javaagent.instrumentation.netty.common.NettyErrorHolder; -import io.opentelemetry.javaagent.instrumentation.netty.v4.common.HttpRequestAndChannel; -import io.opentelemetry.javaagent.instrumentation.netty.v4_1.client.NettyClientSingletons; import net.bytebuddy.asm.Advice; import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.matcher.ElementMatcher; @@ -51,8 +52,7 @@ public static void onEnter( if (clientContext != null) { ctx.channel().attr(AttributeKeys.CLIENT_PARENT_CONTEXT).remove(); contextAttr.remove(); - HttpRequestAndChannel request = - ctx.channel().attr(NettyClientSingletons.HTTP_REQUEST).getAndRemove(); + HttpRequestAndChannel request = ctx.channel().attr(HTTP_REQUEST).getAndRemove(); instrumenter().end(clientContext, request, null, throwable); return; } diff --git a/instrumentation/netty/netty-4.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_1/BootstrapInstrumentation.java b/instrumentation/netty/netty-4.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_1/BootstrapInstrumentation.java index d19d7519853f..aa91de4d1bb3 100644 --- a/instrumentation/netty/netty-4.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_1/BootstrapInstrumentation.java +++ b/instrumentation/netty/netty-4.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_1/BootstrapInstrumentation.java @@ -5,7 +5,7 @@ package io.opentelemetry.javaagent.instrumentation.netty.v4_1; -import static io.opentelemetry.javaagent.instrumentation.netty.v4_1.client.NettyClientSingletons.connectionInstrumenter; +import static io.opentelemetry.javaagent.instrumentation.netty.v4_1.NettyClientSingletons.connectionInstrumenter; import static net.bytebuddy.matcher.ElementMatchers.isConstructor; import static net.bytebuddy.matcher.ElementMatchers.named; import static net.bytebuddy.matcher.ElementMatchers.takesArgument; @@ -17,11 +17,11 @@ import io.netty.resolver.DefaultAddressResolverGroup; import io.opentelemetry.context.Context; import io.opentelemetry.context.Scope; +import io.opentelemetry.instrumentation.netty.common.internal.NettyConnectionRequest; +import io.opentelemetry.instrumentation.netty.v4.common.internal.client.ConnectionCompleteListener; import io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; -import io.opentelemetry.javaagent.instrumentation.netty.common.NettyConnectionRequest; -import io.opentelemetry.javaagent.instrumentation.netty.v4.common.client.ConnectionCompleteListener; import java.net.SocketAddress; import net.bytebuddy.asm.Advice; import net.bytebuddy.description.type.TypeDescription; diff --git a/instrumentation/netty/netty-4.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_1/ChannelInstrumentation.java b/instrumentation/netty/netty-4.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_1/ChannelInstrumentation.java index e17db0ee271c..059906028d1f 100644 --- a/instrumentation/netty/netty-4.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_1/ChannelInstrumentation.java +++ b/instrumentation/netty/netty-4.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_1/ChannelInstrumentation.java @@ -12,6 +12,7 @@ import static net.bytebuddy.matcher.ElementMatchers.namedOneOf; import io.netty.channel.Channel; +import io.opentelemetry.instrumentation.netty.v4_1.internal.AttributeKeys; import io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; diff --git a/instrumentation/netty/netty-4.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_1/InstrumentedAddressResolverGroup.java b/instrumentation/netty/netty-4.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_1/InstrumentedAddressResolverGroup.java index d74ecc208bdb..f2eed8a95cdb 100644 --- a/instrumentation/netty/netty-4.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_1/InstrumentedAddressResolverGroup.java +++ b/instrumentation/netty/netty-4.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_1/InstrumentedAddressResolverGroup.java @@ -11,8 +11,8 @@ import io.netty.util.concurrent.Future; import io.netty.util.concurrent.Promise; import io.opentelemetry.context.Context; -import io.opentelemetry.javaagent.instrumentation.netty.common.NettyConnectionRequest; -import io.opentelemetry.javaagent.instrumentation.netty.v4.common.client.NettyConnectionInstrumenter; +import io.opentelemetry.instrumentation.netty.common.internal.NettyConnectionRequest; +import io.opentelemetry.instrumentation.netty.v4.common.internal.client.NettyConnectionInstrumenter; import java.net.SocketAddress; import java.util.List; import java.util.function.Supplier; diff --git a/instrumentation/netty/netty-4.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_1/NettyChannelPipelineInstrumentation.java b/instrumentation/netty/netty-4.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_1/NettyChannelPipelineInstrumentation.java index 0cd76a87bf3e..73ff517ddcf5 100644 --- a/instrumentation/netty/netty-4.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_1/NettyChannelPipelineInstrumentation.java +++ b/instrumentation/netty/netty-4.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_1/NettyChannelPipelineInstrumentation.java @@ -5,7 +5,7 @@ package io.opentelemetry.javaagent.instrumentation.netty.v4_1; -import static io.opentelemetry.javaagent.instrumentation.netty.v4_1.client.NettyClientSingletons.sslInstrumenter; +import static io.opentelemetry.javaagent.instrumentation.netty.v4_1.NettyClientSingletons.sslInstrumenter; import static net.bytebuddy.matcher.ElementMatchers.isMethod; import static net.bytebuddy.matcher.ElementMatchers.nameStartsWith; import static net.bytebuddy.matcher.ElementMatchers.named; @@ -21,16 +21,16 @@ import io.netty.handler.codec.http.HttpResponseEncoder; import io.netty.handler.codec.http.HttpServerCodec; import io.opentelemetry.instrumentation.api.util.VirtualField; +import io.opentelemetry.instrumentation.netty.v4.common.internal.client.NettySslInstrumentationHandler; +import io.opentelemetry.instrumentation.netty.v4_1.internal.client.HttpClientRequestTracingHandler; +import io.opentelemetry.instrumentation.netty.v4_1.internal.client.HttpClientResponseTracingHandler; +import io.opentelemetry.instrumentation.netty.v4_1.internal.client.HttpClientTracingHandler; +import io.opentelemetry.instrumentation.netty.v4_1.internal.server.HttpServerRequestTracingHandler; +import io.opentelemetry.instrumentation.netty.v4_1.internal.server.HttpServerResponseTracingHandler; +import io.opentelemetry.instrumentation.netty.v4_1.internal.server.HttpServerTracingHandler; import io.opentelemetry.javaagent.bootstrap.CallDepth; import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; import io.opentelemetry.javaagent.instrumentation.netty.v4.common.AbstractNettyChannelPipelineInstrumentation; -import io.opentelemetry.javaagent.instrumentation.netty.v4.common.client.NettySslInstrumentationHandler; -import io.opentelemetry.javaagent.instrumentation.netty.v4_1.client.HttpClientRequestTracingHandler; -import io.opentelemetry.javaagent.instrumentation.netty.v4_1.client.HttpClientResponseTracingHandler; -import io.opentelemetry.javaagent.instrumentation.netty.v4_1.client.HttpClientTracingHandler; -import io.opentelemetry.javaagent.instrumentation.netty.v4_1.server.HttpServerRequestTracingHandler; -import io.opentelemetry.javaagent.instrumentation.netty.v4_1.server.HttpServerResponseTracingHandler; -import io.opentelemetry.javaagent.instrumentation.netty.v4_1.server.HttpServerTracingHandler; import net.bytebuddy.asm.Advice; public class NettyChannelPipelineInstrumentation @@ -105,22 +105,22 @@ public static void addHandler( ChannelHandler ourHandler = null; // Server pipeline handlers if (handler instanceof HttpServerCodec) { - ourHandler = new HttpServerTracingHandler(); + ourHandler = new HttpServerTracingHandler(NettyServerSingletons.instrumenter()); } else if (handler instanceof HttpRequestDecoder) { - ourHandler = new HttpServerRequestTracingHandler(); + ourHandler = new HttpServerRequestTracingHandler(NettyServerSingletons.instrumenter()); } else if (handler instanceof HttpResponseEncoder) { - ourHandler = new HttpServerResponseTracingHandler(); + ourHandler = new HttpServerResponseTracingHandler(NettyServerSingletons.instrumenter()); // Client pipeline handlers } else if (handler instanceof HttpClientCodec) { - ourHandler = new HttpClientTracingHandler(); + ourHandler = new HttpClientTracingHandler(NettyClientSingletons.instrumenter()); } else if (handler instanceof HttpRequestEncoder) { - ourHandler = new HttpClientRequestTracingHandler(); + ourHandler = new HttpClientRequestTracingHandler(NettyClientSingletons.instrumenter()); } else if (handler instanceof HttpResponseDecoder) { - ourHandler = new HttpClientResponseTracingHandler(); + ourHandler = new HttpClientResponseTracingHandler(NettyClientSingletons.instrumenter()); // the SslHandler lives in the netty-handler module, using class name comparison to avoid // adding a dependency } else if (handler.getClass().getName().equals("io.netty.handler.ssl.SslHandler")) { - ourHandler = new NettySslInstrumentationHandler(sslInstrumenter()); + ourHandler = new NettySslInstrumentationHandler(sslInstrumenter(), handler); } if (ourHandler != null) { diff --git a/instrumentation/netty/netty-4.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_1/client/NettyClientSingletons.java b/instrumentation/netty/netty-4.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_1/NettyClientSingletons.java similarity index 54% rename from instrumentation/netty/netty-4.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_1/client/NettyClientSingletons.java rename to instrumentation/netty/netty-4.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_1/NettyClientSingletons.java index d64875bfb6fb..98d7dc07575b 100644 --- a/instrumentation/netty/netty-4.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_1/client/NettyClientSingletons.java +++ b/instrumentation/netty/netty-4.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_1/NettyClientSingletons.java @@ -3,39 +3,33 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.javaagent.instrumentation.netty.v4_1.client; +package io.opentelemetry.javaagent.instrumentation.netty.v4_1; import io.netty.handler.codec.http.HttpResponse; -import io.netty.util.AttributeKey; +import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; -import io.opentelemetry.javaagent.bootstrap.internal.DeprecatedConfigPropertyWarning; +import io.opentelemetry.instrumentation.netty.v4.common.HttpRequestAndChannel; +import io.opentelemetry.instrumentation.netty.v4.common.internal.client.NettyClientInstrumenterFactory; +import io.opentelemetry.instrumentation.netty.v4.common.internal.client.NettyConnectionInstrumenter; +import io.opentelemetry.instrumentation.netty.v4.common.internal.client.NettySslInstrumenter; +import io.opentelemetry.javaagent.bootstrap.internal.CommonConfig; +import io.opentelemetry.javaagent.bootstrap.internal.DeprecatedConfigProperties; import io.opentelemetry.javaagent.bootstrap.internal.InstrumentationConfig; -import io.opentelemetry.javaagent.instrumentation.netty.v4.common.HttpRequestAndChannel; -import io.opentelemetry.javaagent.instrumentation.netty.v4.common.client.NettyClientInstrumenterFactory; -import io.opentelemetry.javaagent.instrumentation.netty.v4.common.client.NettyConnectionInstrumenter; -import io.opentelemetry.javaagent.instrumentation.netty.v4.common.client.NettySslInstrumenter; +import java.util.Collections; public final class NettyClientSingletons { - public static final AttributeKey HTTP_REQUEST = - AttributeKey.valueOf(NettyClientSingletons.class, "http-client-request"); - static final AttributeKey HTTP_RESPONSE = - AttributeKey.valueOf(NettyClientSingletons.class, "http-client-response"); - private static final boolean connectionTelemetryEnabled; private static final boolean sslTelemetryEnabled; static { InstrumentationConfig config = InstrumentationConfig.get(); - DeprecatedConfigPropertyWarning.warnIfUsed( - config, - "otel.instrumentation.netty.always-create-connect-span", - "otel.instrumentation.netty.connection-telemetry.enabled"); - boolean alwaysCreateConnectSpan = - config.getBoolean("otel.instrumentation.netty.always-create-connect-span", false); connectionTelemetryEnabled = - config.getBoolean( - "otel.instrumentation.netty.connection-telemetry.enabled", alwaysCreateConnectSpan); + DeprecatedConfigProperties.getBoolean( + config, + "otel.instrumentation.netty.always-create-connect-span", + "otel.instrumentation.netty.connection-telemetry.enabled", + false); sslTelemetryEnabled = config.getBoolean("otel.instrumentation.netty.ssl-telemetry.enabled", false); } @@ -47,8 +41,16 @@ public final class NettyClientSingletons { static { NettyClientInstrumenterFactory factory = new NettyClientInstrumenterFactory( - "io.opentelemetry.netty-4.1", connectionTelemetryEnabled, sslTelemetryEnabled); - INSTRUMENTER = factory.createHttpInstrumenter(); + GlobalOpenTelemetry.get(), + "io.opentelemetry.netty-4.1", + connectionTelemetryEnabled, + sslTelemetryEnabled, + CommonConfig.get().getPeerServiceMapping()); + INSTRUMENTER = + factory.createHttpInstrumenter( + CommonConfig.get().getClientRequestHeaders(), + CommonConfig.get().getClientResponseHeaders(), + Collections.emptyList()); CONNECTION_INSTRUMENTER = factory.createConnectionInstrumenter(); SSL_INSTRUMENTER = factory.createSslInstrumenter(); } diff --git a/instrumentation/netty/netty-4.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_1/NettyServerSingletons.java b/instrumentation/netty/netty-4.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_1/NettyServerSingletons.java new file mode 100644 index 000000000000..c4239f6b0d40 --- /dev/null +++ b/instrumentation/netty/netty-4.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_1/NettyServerSingletons.java @@ -0,0 +1,29 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.netty.v4_1; + +import io.netty.handler.codec.http.HttpResponse; +import io.opentelemetry.api.GlobalOpenTelemetry; +import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; +import io.opentelemetry.instrumentation.netty.v4.common.HttpRequestAndChannel; +import io.opentelemetry.instrumentation.netty.v4.common.internal.server.NettyServerInstrumenterFactory; +import io.opentelemetry.javaagent.bootstrap.internal.CommonConfig; + +public final class NettyServerSingletons { + + private static final Instrumenter INSTRUMENTER = + NettyServerInstrumenterFactory.create( + GlobalOpenTelemetry.get(), + "io.opentelemetry.netty-4.1", + CommonConfig.get().getServerRequestHeaders(), + CommonConfig.get().getServerResponseHeaders()); + + public static Instrumenter instrumenter() { + return INSTRUMENTER; + } + + private NettyServerSingletons() {} +} diff --git a/instrumentation/netty/netty-4.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_1/client/HttpClientTracingHandler.java b/instrumentation/netty/netty-4.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_1/client/HttpClientTracingHandler.java deleted file mode 100644 index d549aa823a3e..000000000000 --- a/instrumentation/netty/netty-4.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_1/client/HttpClientTracingHandler.java +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.javaagent.instrumentation.netty.v4_1.client; - -import io.netty.channel.CombinedChannelDuplexHandler; - -public class HttpClientTracingHandler - extends CombinedChannelDuplexHandler< - HttpClientResponseTracingHandler, HttpClientRequestTracingHandler> { - - public HttpClientTracingHandler() { - super(new HttpClientResponseTracingHandler(), new HttpClientRequestTracingHandler()); - } -} diff --git a/instrumentation/netty/netty-4.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_1/server/HttpServerTracingHandler.java b/instrumentation/netty/netty-4.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_1/server/HttpServerTracingHandler.java deleted file mode 100644 index cdb1be0fc0e3..000000000000 --- a/instrumentation/netty/netty-4.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_1/server/HttpServerTracingHandler.java +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.javaagent.instrumentation.netty.v4_1.server; - -import io.netty.channel.CombinedChannelDuplexHandler; - -public class HttpServerTracingHandler - extends CombinedChannelDuplexHandler< - HttpServerRequestTracingHandler, HttpServerResponseTracingHandler> { - - public HttpServerTracingHandler() { - super(new HttpServerRequestTracingHandler(), new HttpServerResponseTracingHandler()); - } -} diff --git a/instrumentation/netty/netty-4.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_1/server/NettyServerSingletons.java b/instrumentation/netty/netty-4.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_1/server/NettyServerSingletons.java deleted file mode 100644 index e1801523da69..000000000000 --- a/instrumentation/netty/netty-4.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_1/server/NettyServerSingletons.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.javaagent.instrumentation.netty.v4_1.server; - -import io.netty.handler.codec.http.HttpResponse; -import io.netty.util.AttributeKey; -import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; -import io.opentelemetry.javaagent.instrumentation.netty.v4.common.HttpRequestAndChannel; -import io.opentelemetry.javaagent.instrumentation.netty.v4.common.server.NettyServerInstrumenterFactory; - -public final class NettyServerSingletons { - - static final AttributeKey HTTP_REQUEST = - AttributeKey.valueOf(NettyServerSingletons.class, "http-server-request"); - static final AttributeKey HTTP_RESPONSE = - AttributeKey.valueOf(NettyServerSingletons.class, "http-server-response"); - - private static final Instrumenter INSTRUMENTER = - NettyServerInstrumenterFactory.create("io.opentelemetry.netty-4.1"); - - public static Instrumenter instrumenter() { - return INSTRUMENTER; - } - - private NettyServerSingletons() {} -} diff --git a/instrumentation/netty/netty-4.1/javaagent/src/test/groovy/ChannelPipelineTest.groovy b/instrumentation/netty/netty-4.1/javaagent/src/test/groovy/ChannelPipelineTest.groovy index b08a4eff1694..6d0d79060eca 100644 --- a/instrumentation/netty/netty-4.1/javaagent/src/test/groovy/ChannelPipelineTest.groovy +++ b/instrumentation/netty/netty-4.1/javaagent/src/test/groovy/ChannelPipelineTest.groovy @@ -8,7 +8,7 @@ import io.netty.channel.DefaultChannelPipeline import io.netty.channel.embedded.EmbeddedChannel import io.netty.handler.codec.http.HttpClientCodec import io.opentelemetry.instrumentation.test.AgentInstrumentationSpecification -import io.opentelemetry.javaagent.instrumentation.netty.v4_1.client.HttpClientTracingHandler +import io.opentelemetry.instrumentation.netty.v4_1.internal.client.HttpClientTracingHandler import spock.lang.Unroll @Unroll @@ -31,7 +31,7 @@ class ChannelPipelineTest extends AgentInstrumentationSpecification { channelPipeline.addLast("http", handler) channelPipeline.first() == handler // our handler was also added - channelPipeline.last().getClass() == HttpClientTracingHandler + channelPipeline.last().getClass().simpleName == "HttpClientTracingHandler" and: removeMethod.call(channelPipeline, handler) @@ -70,7 +70,7 @@ class ChannelPipelineTest extends AgentInstrumentationSpecification { then: "noop handler was removed; http and instrumentation handlers were added" channelPipeline.size() == 2 channelPipeline.first() == httpHandler - channelPipeline.last().getClass() == HttpClientTracingHandler + channelPipeline.last().getClass().simpleName == "HttpClientTracingHandler" when: def anotherNoopHandler = new NoopChannelHandler() @@ -103,7 +103,7 @@ class ChannelPipelineTest extends AgentInstrumentationSpecification { then: "add http and instrumentation handlers" channelPipeline.size() == 2 channelPipeline.first() == httpHandler - channelPipeline.last().getClass() == HttpClientTracingHandler + channelPipeline.last().getClass().simpleName == "HttpClientTracingHandler" when: def noopHandler = new NoopChannelHandler() @@ -120,7 +120,7 @@ class ChannelPipelineTest extends AgentInstrumentationSpecification { then: "http and instrumentation handlers will be remained" channelPipeline.size() == 2 channelPipeline.first() == httpHandler - channelPipeline.last().getClass() == HttpClientTracingHandler + channelPipeline.last().getClass().simpleName == "HttpClientTracingHandler" when: channelPipeline.removeLast() diff --git a/instrumentation/netty/netty-4.1/javaagent/src/test/groovy/Netty41ClientSslTest.groovy b/instrumentation/netty/netty-4.1/javaagent/src/test/groovy/Netty41ClientSslTest.groovy index c521e22cf655..6a95e8547531 100644 --- a/instrumentation/netty/netty-4.1/javaagent/src/test/groovy/Netty41ClientSslTest.groovy +++ b/instrumentation/netty/netty-4.1/javaagent/src/test/groovy/Netty41ClientSslTest.groovy @@ -20,6 +20,7 @@ import io.netty.handler.codec.http.HttpVersion import io.netty.handler.ssl.SslContext import io.netty.handler.ssl.SslContextBuilder import io.netty.handler.ssl.SslHandler +import io.opentelemetry.instrumentation.netty.v4_1.ClientHandler import io.opentelemetry.instrumentation.test.AgentInstrumentationSpecification import io.opentelemetry.instrumentation.testing.junit.http.HttpClientTestServer import io.opentelemetry.semconv.trace.attributes.SemanticAttributes @@ -106,7 +107,7 @@ class Netty41ClientSslTest extends AgentInstrumentationSpecification { "$SemanticAttributes.NET_TRANSPORT" IP_TCP "$SemanticAttributes.NET_PEER_NAME" uri.host "$SemanticAttributes.NET_PEER_PORT" uri.port - "$SemanticAttributes.NET_PEER_IP" { it == null || it == "127.0.0.1" } + "$SemanticAttributes.NET_SOCK_PEER_ADDR" { it == "127.0.0.1" || it == null } } } span(3) { @@ -118,9 +119,9 @@ class Netty41ClientSslTest extends AgentInstrumentationSpecification { errorEventWithAnyMessage(SSLHandshakeException) attributes { "$SemanticAttributes.NET_TRANSPORT" IP_TCP - "$SemanticAttributes.NET_PEER_NAME" uri.host - "$SemanticAttributes.NET_PEER_PORT" uri.port - "$SemanticAttributes.NET_PEER_IP" { it == null || it == "127.0.0.1" } + "$SemanticAttributes.NET_SOCK_PEER_ADDR" { it == "127.0.0.1" || it == null } + "$SemanticAttributes.NET_SOCK_PEER_NAME" uri.host + "$SemanticAttributes.NET_SOCK_PEER_PORT" uri.port } } } @@ -174,7 +175,7 @@ class Netty41ClientSslTest extends AgentInstrumentationSpecification { "$SemanticAttributes.NET_TRANSPORT" IP_TCP "$SemanticAttributes.NET_PEER_NAME" uri.host "$SemanticAttributes.NET_PEER_PORT" uri.port - "$SemanticAttributes.NET_PEER_IP" { it == null || it == "127.0.0.1" } + "$SemanticAttributes.NET_SOCK_PEER_ADDR" { it == "127.0.0.1" || it == null } } } span(3) { @@ -183,9 +184,9 @@ class Netty41ClientSslTest extends AgentInstrumentationSpecification { childOf span(0) attributes { "$SemanticAttributes.NET_TRANSPORT" IP_TCP - "$SemanticAttributes.NET_PEER_NAME" uri.host - "$SemanticAttributes.NET_PEER_PORT" uri.port - "$SemanticAttributes.NET_PEER_IP" { it == null || it == "127.0.0.1" } + "$SemanticAttributes.NET_SOCK_PEER_ADDR" { it == "127.0.0.1" || it == null } + "$SemanticAttributes.NET_SOCK_PEER_NAME" uri.host + "$SemanticAttributes.NET_SOCK_PEER_PORT" uri.port } } span(4) { diff --git a/instrumentation/netty/netty-4.1/javaagent/src/test/groovy/Netty41ClientTest.groovy b/instrumentation/netty/netty-4.1/javaagent/src/test/groovy/Netty41ClientTest.groovy index 99268b5c84ba..f6510ee0f4ea 100644 --- a/instrumentation/netty/netty-4.1/javaagent/src/test/groovy/Netty41ClientTest.groovy +++ b/instrumentation/netty/netty-4.1/javaagent/src/test/groovy/Netty41ClientTest.groovy @@ -9,7 +9,6 @@ import io.netty.channel.Channel import io.netty.channel.ChannelHandler import io.netty.channel.ChannelHandlerContext import io.netty.channel.ChannelInitializer -import io.netty.channel.ChannelOption import io.netty.channel.ChannelPipeline import io.netty.channel.EventLoopGroup import io.netty.channel.embedded.EmbeddedChannel @@ -21,16 +20,10 @@ import io.netty.handler.codec.http.HttpClientCodec import io.netty.handler.codec.http.HttpHeaderNames import io.netty.handler.codec.http.HttpMethod import io.netty.handler.codec.http.HttpVersion -import io.netty.handler.ssl.SslContext -import io.netty.handler.ssl.SslContextBuilder -import io.netty.handler.timeout.ReadTimeoutHandler -import io.opentelemetry.api.common.AttributeKey import io.opentelemetry.api.trace.SpanKind -import io.opentelemetry.instrumentation.test.AgentTestTrait -import io.opentelemetry.instrumentation.test.base.HttpClientTest -import io.opentelemetry.instrumentation.testing.junit.http.AbstractHttpClientTest -import io.opentelemetry.instrumentation.testing.junit.http.SingleConnection -import io.opentelemetry.javaagent.instrumentation.netty.v4_1.client.HttpClientTracingHandler +import io.opentelemetry.instrumentation.netty.v4_1.ClientHandler +import io.opentelemetry.instrumentation.test.AgentInstrumentationSpecification +import io.opentelemetry.instrumentation.testing.junit.http.HttpClientTestServer import spock.lang.Shared import spock.lang.Unroll @@ -40,131 +33,18 @@ import java.util.concurrent.TimeUnit import static org.junit.jupiter.api.Assumptions.assumeTrue @Unroll -class Netty41ClientTest extends HttpClientTest implements AgentTestTrait { +class Netty41ClientTest extends AgentInstrumentationSpecification { @Shared - private EventLoopGroup eventLoopGroup = buildEventLoopGroup() + private HttpClientTestServer server - @Shared - private Bootstrap bootstrap = buildBootstrap(false) - - @Shared - private Bootstrap httpsBootstrap = buildBootstrap(true) - - @Shared - private Bootstrap readTimeoutBootstrap = buildBootstrap(false, true) - - def cleanupSpec() { - eventLoopGroup?.shutdownGracefully() - } - - Bootstrap buildBootstrap(boolean https, boolean readTimeout = false) { - Bootstrap bootstrap = new Bootstrap() - bootstrap.group(eventLoopGroup) - .channel(getChannelClass()) - .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, CONNECT_TIMEOUT_MS) - .handler(new ChannelInitializer() { - @Override - protected void initChannel(SocketChannel socketChannel) throws Exception { - ChannelPipeline pipeline = socketChannel.pipeline() - if (https) { - SslContext sslContext = SslContextBuilder.forClient().build() - pipeline.addLast(sslContext.newHandler(socketChannel.alloc())) - } - if (readTimeout) { - pipeline.addLast(new ReadTimeoutHandler(READ_TIMEOUT_MS, TimeUnit.MILLISECONDS)) - } - pipeline.addLast(new HttpClientCodec()) - } - }) - - return bootstrap - } - - EventLoopGroup buildEventLoopGroup() { - return new NioEventLoopGroup() - } - - Class getChannelClass() { - return NioSocketChannel - } - - Bootstrap getBootstrap(URI uri) { - if (uri.getScheme() == "https") { - return httpsBootstrap - } else if (uri.getPath() == "/read-timeout") { - return readTimeoutBootstrap - } - return bootstrap - } - - @Override - DefaultFullHttpRequest buildRequest(String method, URI uri, Map headers) { - def target = uri.path - if (uri.query != null) { - target += "?" + uri.query - } - def request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.valueOf(method), target, Unpooled.EMPTY_BUFFER) - request.headers().set(HttpHeaderNames.HOST, uri.host + ":" + uri.port) - headers.each { k, v -> request.headers().set(k, v) } - return request - } - - @Override - int sendRequest(DefaultFullHttpRequest request, String method, URI uri, Map headers) { - def channel = getBootstrap(uri).connect(uri.host, getPort(uri)).sync().channel() - def result = new CompletableFuture() - channel.pipeline().addLast(new ClientHandler(result)) - channel.writeAndFlush(request).get() - return result.get(20, TimeUnit.SECONDS) + def setupSpec() { + server = new HttpClientTestServer(openTelemetry) + server.start() } - @Override - void sendRequestWithCallback(DefaultFullHttpRequest request, String method, URI uri, Map headers, AbstractHttpClientTest.RequestResult requestResult) { - Channel ch - try { - ch = getBootstrap(uri).connect(uri.host, getPort(uri)).sync().channel() - } catch (Exception exception) { - requestResult.complete(exception) - return - } - def result = new CompletableFuture() - result.whenComplete { status, throwable -> - requestResult.complete({ status }, throwable) - } - ch.pipeline().addLast(new ClientHandler(result)) - ch.writeAndFlush(request) - } - - @Override - String expectedClientSpanName(URI uri, String method) { - switch (uri.toString()) { - case "http://localhost:61/": // unopened port - case "https://192.0.2.1/": // non routable address - return "CONNECT" - default: - return super.expectedClientSpanName(uri, method) - } - } - - @Override - Set> httpAttributes(URI uri) { - switch (uri.toString()) { - case "http://localhost:61/": // unopened port - case "https://192.0.2.1/": // non routable address - return [] - } - return super.httpAttributes(uri) - } - - @Override - boolean testRedirects() { - false - } - - @Override - boolean testReadTimeout() { - true + def cleanupSpec() { + server.stop() } def "test connection reuse and second request with lazy execute"() { @@ -201,8 +81,14 @@ class Netty41ClientTest extends HttpClientTest implement kind SpanKind.INTERNAL hasNoParent() } - clientSpan(it, 1, span(0)) - serverSpan(it, 2, span(1)) + span(1) { + kind SpanKind.CLIENT + childOf span(0) + } + span(2) { + kind SpanKind.SERVER + childOf span(1) + } } } @@ -220,8 +106,14 @@ class Netty41ClientTest extends HttpClientTest implement kind SpanKind.INTERNAL hasNoParent() } - clientSpan(it, 1, span(0)) - serverSpan(it, 2, span(1)) + span(1) { + kind SpanKind.CLIENT + childOf span(0) + } + span(2) { + kind SpanKind.SERVER + childOf span(1) + } } trace(1, 3) { span(0) { @@ -229,8 +121,14 @@ class Netty41ClientTest extends HttpClientTest implement kind SpanKind.INTERNAL hasNoParent() } - clientSpan(it, 1, span(0)) - serverSpan(it, 2, span(1)) + span(1) { + kind SpanKind.CLIENT + childOf span(0) + } + span(2) { + kind SpanKind.SERVER + childOf span(1) + } } } @@ -249,7 +147,7 @@ class Netty41ClientTest extends HttpClientTest implement then: // The first one returns the removed tracing handler - pipeline.remove(HttpClientTracingHandler.getName()) != null + pipeline.remove("io.opentelemetry.javaagent.shaded.instrumentation.netty.v4_1.internal.client.HttpClientTracingHandler") != null } def "when a handler is added to the netty pipeline we add ONLY ONE tracing handler"() { @@ -260,9 +158,9 @@ class Netty41ClientTest extends HttpClientTest implement when: pipeline.addLast("name", new HttpClientCodec()) // The first one returns the removed tracing handler - pipeline.remove(HttpClientTracingHandler.getName()) + pipeline.remove("io.opentelemetry.javaagent.shaded.instrumentation.netty.v4_1.internal.client.HttpClientTracingHandler") // There is only one - pipeline.remove(HttpClientTracingHandler.getName()) == null + pipeline.remove("io.opentelemetry.javaagent.shaded.instrumentation.netty.v4_1.internal.client.HttpClientTracingHandler") == null then: thrown NoSuchElementException @@ -279,7 +177,7 @@ class Netty41ClientTest extends HttpClientTest implement then: // The first one returns the removed tracing handler - null != pipeline.remove(HttpClientTracingHandler.getName()) + null != pipeline.remove("io.opentelemetry.javaagent.shaded.instrumentation.netty.v4_1.internal.client.HttpClientTracingHandler") null != pipeline.remove("some_handler") null != pipeline.remove("a_traced_handler") } @@ -307,9 +205,9 @@ class Netty41ClientTest extends HttpClientTest implement channel.pipeline().addLast(new TracedHandlerFromInitializerHandler()) then: - null != channel.pipeline().get(HttpClientTracingHandler.getName()) + null != channel.pipeline().get("io.opentelemetry.javaagent.shaded.instrumentation.netty.v4_1.internal.client.HttpClientTracingHandler") null != channel.pipeline().remove("added_in_initializer") - null == channel.pipeline().get(HttpClientTracingHandler.getName()) + null == channel.pipeline().get("io.opentelemetry.javaagent.shaded.instrumentation.netty.v4_1.internal.client.HttpClientTracingHandler") } def "request with trace annotated method #method"() { @@ -336,20 +234,47 @@ class Netty41ClientTest extends HttpClientTest implement attributes { } } - clientSpan(it, 2, span(1), method) - serverSpan(it, 3, span(2)) + span(2) { + childOf span(1) + kind SpanKind.CLIENT + } + span(3) { + childOf span(2) + kind SpanKind.SERVER + } } } where: - method << BODY_METHODS + method << ["POST", "PUT"] } class TracedClass { + private final Bootstrap bootstrap + + private TracedClass() { + EventLoopGroup group = new NioEventLoopGroup() + bootstrap = new Bootstrap() + bootstrap.group(group) + .channel(NioSocketChannel) + .handler(new ChannelInitializer() { + @Override + protected void initChannel(SocketChannel socketChannel) throws Exception { + ChannelPipeline pipeline = socketChannel.pipeline() + pipeline.addLast(new HttpClientCodec()) + } + }) + } + int tracedMethod(String method) { - def uri = resolveAddress("/success") runWithSpan("tracedMethod") { - doRequest(method, uri) + def request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.valueOf(method), "/success", Unpooled.EMPTY_BUFFER) + request.headers().set(HttpHeaderNames.HOST, "localhost:" + server.httpPort()) + def ch = bootstrap.connect("localhost", server.httpPort()).sync().channel() + def result = new CompletableFuture() + ch.pipeline().addLast(new ClientHandler(result)) + ch.writeAndFlush(request).get() + return result.get(20, TimeUnit.SECONDS) } } } @@ -389,9 +314,4 @@ class Netty41ClientTest extends HttpClientTest implement ch.pipeline().addLast("added_in_initializer", new HttpClientCodec()) } } - - @Override - SingleConnection createSingleConnection(String host, int port) { - return new SingleNettyConnection(host, port) - } } diff --git a/instrumentation/netty/netty-4.1/javaagent/src/test/groovy/Netty41ConnectionSpanTest.groovy b/instrumentation/netty/netty-4.1/javaagent/src/test/groovy/Netty41ConnectionSpanTest.groovy index 496035acf8ae..71e161ce4295 100644 --- a/instrumentation/netty/netty-4.1/javaagent/src/test/groovy/Netty41ConnectionSpanTest.groovy +++ b/instrumentation/netty/netty-4.1/javaagent/src/test/groovy/Netty41ConnectionSpanTest.groovy @@ -16,6 +16,7 @@ import io.netty.handler.codec.http.HttpClientCodec import io.netty.handler.codec.http.HttpHeaderNames import io.netty.handler.codec.http.HttpMethod import io.netty.handler.codec.http.HttpVersion +import io.opentelemetry.instrumentation.netty.v4_1.ClientHandler import io.opentelemetry.instrumentation.test.AgentTestTrait import io.opentelemetry.instrumentation.test.InstrumentationSpecification import io.opentelemetry.instrumentation.test.utils.PortUtils @@ -120,7 +121,7 @@ class Netty41ConnectionSpanTest extends InstrumentationSpecification implements "$SemanticAttributes.NET_TRANSPORT" IP_TCP "$SemanticAttributes.NET_PEER_NAME" uri.host "$SemanticAttributes.NET_PEER_PORT" uri.port - "$SemanticAttributes.NET_PEER_IP" "127.0.0.1" + "$SemanticAttributes.NET_SOCK_PEER_ADDR" "127.0.0.1" } } span(3) { @@ -180,7 +181,7 @@ class Netty41ConnectionSpanTest extends InstrumentationSpecification implements "$SemanticAttributes.NET_TRANSPORT" IP_TCP "$SemanticAttributes.NET_PEER_NAME" uri.host "$SemanticAttributes.NET_PEER_PORT" uri.port - "$SemanticAttributes.NET_PEER_IP" { it == null || it == "127.0.0.1" } + "$SemanticAttributes.NET_SOCK_PEER_ADDR" { it == "127.0.0.1" || it == null } } } } diff --git a/instrumentation/netty/netty-4.1/javaagent/src/test/groovy/Netty41NativeClientTest.groovy b/instrumentation/netty/netty-4.1/javaagent/src/test/groovy/Netty41NativeClientTest.groovy deleted file mode 100644 index 15eb04568261..000000000000 --- a/instrumentation/netty/netty-4.1/javaagent/src/test/groovy/Netty41NativeClientTest.groovy +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -import io.netty.channel.Channel -import io.netty.channel.EventLoopGroup -import io.netty.channel.epoll.Epoll -import io.netty.channel.epoll.EpollEventLoopGroup -import io.netty.channel.epoll.EpollSocketChannel -import io.netty.channel.kqueue.KQueue -import io.netty.channel.kqueue.KQueueEventLoopGroup -import io.netty.channel.kqueue.KQueueSocketChannel -import org.junit.jupiter.api.Assumptions - -// netty client test with epoll/kqueue native library -class Netty41NativeClientTest extends Netty41ClientTest { - - EventLoopGroup buildEventLoopGroup() { - // linux - if (Epoll.isAvailable()) { - return new EpollEventLoopGroup() - } - // mac - if (KQueueHelper.isAvailable()) { - return new KQueueEventLoopGroup() - } - - // skip test when native library was not found - Assumptions.assumeTrue(false, "Native library was not found") - return super.buildEventLoopGroup() - } - - @Override - Class getChannelClass() { - if (Epoll.isAvailable()) { - return EpollSocketChannel - } - if (KQueueHelper.isAvailable()) { - return KQueueSocketChannel - } - return null - } - - static class KQueueHelper { - static boolean isAvailable() { - try { - return KQueue.isAvailable() - } catch (NoClassDefFoundError error) { - // kqueue is available only in latest dep tests - // in regular tests we only have a compile time dependency because kqueue support was added - // after 4.1.0 - return false - } - } - } -} \ No newline at end of file diff --git a/instrumentation/netty/netty-4.1/javaagent/src/test/groovy/Netty41ServerTest.groovy b/instrumentation/netty/netty-4.1/javaagent/src/test/groovy/Netty41ServerTest.groovy deleted file mode 100644 index 79501b00c611..000000000000 --- a/instrumentation/netty/netty-4.1/javaagent/src/test/groovy/Netty41ServerTest.groovy +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -import io.netty.bootstrap.ServerBootstrap -import io.netty.buffer.ByteBuf -import io.netty.buffer.Unpooled -import io.netty.channel.ChannelHandlerContext -import io.netty.channel.ChannelInitializer -import io.netty.channel.ChannelPipeline -import io.netty.channel.EventLoopGroup -import io.netty.channel.SimpleChannelInboundHandler -import io.netty.channel.nio.NioEventLoopGroup -import io.netty.channel.socket.nio.NioServerSocketChannel -import io.netty.handler.codec.http.DefaultFullHttpResponse -import io.netty.handler.codec.http.FullHttpResponse -import io.netty.handler.codec.http.HttpHeaderNames -import io.netty.handler.codec.http.HttpRequest -import io.netty.handler.codec.http.HttpResponseStatus -import io.netty.handler.codec.http.HttpServerCodec -import io.netty.handler.codec.http.QueryStringDecoder -import io.netty.handler.logging.LogLevel -import io.netty.handler.logging.LoggingHandler -import io.netty.util.CharsetUtil -import io.opentelemetry.api.common.AttributeKey -import io.opentelemetry.instrumentation.test.AgentTestTrait -import io.opentelemetry.instrumentation.test.base.HttpServerTest -import io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint -import io.opentelemetry.semconv.trace.attributes.SemanticAttributes - -import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_LENGTH -import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_TYPE -import static io.netty.handler.codec.http.HttpResponseStatus.INTERNAL_SERVER_ERROR -import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1 -import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.CAPTURE_HEADERS -import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.ERROR -import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.EXCEPTION -import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.INDEXED_CHILD -import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.NOT_FOUND -import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.QUERY_PARAM -import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.REDIRECT -import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.SUCCESS - -class Netty41ServerTest extends HttpServerTest implements AgentTestTrait { - - static final LoggingHandler LOGGING_HANDLER = new LoggingHandler(SERVER_LOGGER.name, LogLevel.DEBUG) - - @Override - EventLoopGroup startServer(int port) { - def eventLoopGroup = new NioEventLoopGroup() - - ServerBootstrap bootstrap = new ServerBootstrap() - .group(eventLoopGroup) - .handler(LOGGING_HANDLER) - .childHandler([ - initChannel: { ch -> - ChannelPipeline pipeline = ch.pipeline() - pipeline.addFirst("logger", LOGGING_HANDLER) - - def handlers = [new HttpServerCodec()] - handlers.each { pipeline.addLast(it) } - pipeline.addLast(new SimpleChannelInboundHandler() { - - @Override - protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception { - if (msg instanceof HttpRequest) { - def request = msg as HttpRequest - def uri = URI.create(request.uri()) - ServerEndpoint endpoint = ServerEndpoint.forPath(uri.path) - ctx.write controller(endpoint) { - ByteBuf content = null - FullHttpResponse response - switch (endpoint) { - case SUCCESS: - case ERROR: - content = Unpooled.copiedBuffer(endpoint.body, CharsetUtil.UTF_8) - response = new DefaultFullHttpResponse(HTTP_1_1, HttpResponseStatus.valueOf(endpoint.status), content) - break - case INDEXED_CHILD: - content = Unpooled.EMPTY_BUFFER - endpoint.collectSpanAttributes { new QueryStringDecoder(uri).parameters().get(it).find() } - response = new DefaultFullHttpResponse(HTTP_1_1, HttpResponseStatus.valueOf(endpoint.status), content) - break - case QUERY_PARAM: - content = Unpooled.copiedBuffer(uri.query, CharsetUtil.UTF_8) - response = new DefaultFullHttpResponse(HTTP_1_1, HttpResponseStatus.valueOf(endpoint.status), content) - break - case REDIRECT: - content = Unpooled.EMPTY_BUFFER - response = new DefaultFullHttpResponse(HTTP_1_1, HttpResponseStatus.valueOf(endpoint.status), content) - response.headers().set(HttpHeaderNames.LOCATION, endpoint.body) - break - case CAPTURE_HEADERS: - content = Unpooled.copiedBuffer(endpoint.body, CharsetUtil.UTF_8) - response = new DefaultFullHttpResponse(HTTP_1_1, HttpResponseStatus.valueOf(endpoint.status), content) - response.headers().set("X-Test-Response", request.headers().get("X-Test-Request")) - break - case EXCEPTION: - throw new Exception(endpoint.body) - default: - content = Unpooled.copiedBuffer(NOT_FOUND.body, CharsetUtil.UTF_8) - response = new DefaultFullHttpResponse(HTTP_1_1, HttpResponseStatus.valueOf(NOT_FOUND.status), content) - break - } - response.headers().set(CONTENT_TYPE, "text/plain") - if (content) { - response.headers().set(CONTENT_LENGTH, content.readableBytes()) - } - return response - } - } - } - - @Override - void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { - ByteBuf content = Unpooled.copiedBuffer(cause.message, CharsetUtil.UTF_8) - FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, INTERNAL_SERVER_ERROR, content) - response.headers().set(CONTENT_TYPE, "text/plain") - response.headers().set(CONTENT_LENGTH, content.readableBytes()) - ctx.write(response) - } - - @Override - void channelReadComplete(ChannelHandlerContext ctx) throws Exception { - ctx.flush() - } - }) - } - ] as ChannelInitializer).channel(NioServerSocketChannel) - bootstrap.bind(port).sync() - - return eventLoopGroup - } - - @Override - void stopServer(EventLoopGroup server) { - server?.shutdownGracefully() - } - - @Override - Set> httpAttributes(ServerEndpoint endpoint) { - def attributes = super.httpAttributes(endpoint) - attributes.remove(SemanticAttributes.HTTP_ROUTE) - attributes - } -} diff --git a/instrumentation/netty/netty-4.1/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/netty/v4_1/Netty41ClientTest.java b/instrumentation/netty/netty-4.1/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/netty/v4_1/Netty41ClientTest.java new file mode 100644 index 000000000000..c2af0681b08c --- /dev/null +++ b/instrumentation/netty/netty-4.1/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/netty/v4_1/Netty41ClientTest.java @@ -0,0 +1,36 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.netty.v4_1; + +import io.netty.channel.Channel; +import io.opentelemetry.instrumentation.netty.v4_1.AbstractNetty41ClientTest; +import io.opentelemetry.instrumentation.netty.v4_1.Netty41ClientExtension; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.http.HttpClientInstrumentationExtension; +import org.junit.jupiter.api.extension.RegisterExtension; + +public class Netty41ClientTest extends AbstractNetty41ClientTest { + + @RegisterExtension + static final InstrumentationExtension testing = HttpClientInstrumentationExtension.forAgent(); + + @RegisterExtension + static final Netty41ClientExtension clientExtension = + new Netty41ClientExtension(channelPipeline -> {}); + + @Override + protected Netty41ClientExtension clientExtension() { + return clientExtension; + } + + @Override + protected void configureChannel(Channel channel) {} + + @Override + protected boolean testReadTimeout() { + return true; + } +} diff --git a/instrumentation/netty/netty-4.1/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/netty/v4_1/Netty41NativeClientTest.java b/instrumentation/netty/netty-4.1/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/netty/v4_1/Netty41NativeClientTest.java new file mode 100644 index 000000000000..71aee9d3d2c4 --- /dev/null +++ b/instrumentation/netty/netty-4.1/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/netty/v4_1/Netty41NativeClientTest.java @@ -0,0 +1,86 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.netty.v4_1; + +import io.netty.channel.Channel; +import io.netty.channel.EventLoopGroup; +import io.netty.channel.epoll.Epoll; +import io.netty.channel.epoll.EpollEventLoopGroup; +import io.netty.channel.epoll.EpollSocketChannel; +import io.netty.channel.kqueue.KQueue; +import io.netty.channel.kqueue.KQueueEventLoopGroup; +import io.netty.channel.kqueue.KQueueSocketChannel; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.nio.NioSocketChannel; +import io.opentelemetry.instrumentation.netty.v4_1.AbstractNetty41ClientTest; +import io.opentelemetry.instrumentation.netty.v4_1.Netty41ClientExtension; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.http.HttpClientInstrumentationExtension; +import org.junit.jupiter.api.Assumptions; +import org.junit.jupiter.api.extension.RegisterExtension; + +/** Netty client test with epoll/kqueue native library. */ +public class Netty41NativeClientTest extends AbstractNetty41ClientTest { + + @RegisterExtension + static final InstrumentationExtension testing = HttpClientInstrumentationExtension.forAgent(); + + @RegisterExtension + static final Netty41ClientExtension clientExtension = + new Netty41ClientExtension( + channelPipeline -> {}, Netty41NativeClientTest::buildEventLoopGroup, getChannelClass()); + + private static EventLoopGroup buildEventLoopGroup() { + // linux + if (Epoll.isAvailable()) { + return new EpollEventLoopGroup(); + } + // mac + if (KQueueHelper.isAvailable()) { + return new KQueueEventLoopGroup(); + } + // skip test when native library was not found + Assumptions.assumeTrue(false, "Native library was not found"); + return new NioEventLoopGroup(); + } + + @SuppressWarnings("AbbreviationAsWordInName") + private static class KQueueHelper { + static boolean isAvailable() { + try { + return KQueue.isAvailable(); + } catch (NoClassDefFoundError error) { + // kqueue is available only in latest dep tests + // in regular tests we only have a compile time dependency because kqueue support was added + // after 4.1.0 + return false; + } + } + } + + private static Class getChannelClass() { + if (Epoll.isAvailable()) { + return EpollSocketChannel.class; + } + if (KQueueHelper.isAvailable()) { + return KQueueSocketChannel.class; + } + return NioSocketChannel.class; + } + + @Override + protected Netty41ClientExtension clientExtension() { + return clientExtension; + } + + @Override + protected void configureChannel(Channel channel) {} + + @Override + protected boolean testReadTimeout() { + return true; + } +} diff --git a/instrumentation/netty/netty-4.1/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/netty/v4_1/Netty41ServerTest.java b/instrumentation/netty/netty-4.1/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/netty/v4_1/Netty41ServerTest.java new file mode 100644 index 000000000000..0bed30e1fcbd --- /dev/null +++ b/instrumentation/netty/netty-4.1/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/netty/v4_1/Netty41ServerTest.java @@ -0,0 +1,21 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.netty.v4_1; + +import io.netty.channel.ChannelPipeline; +import io.opentelemetry.instrumentation.netty.v4_1.AbstractNetty41ServerTest; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.http.HttpServerInstrumentationExtension; +import org.junit.jupiter.api.extension.RegisterExtension; + +public class Netty41ServerTest extends AbstractNetty41ServerTest { + + @RegisterExtension + static final InstrumentationExtension testing = HttpServerInstrumentationExtension.forAgent(); + + @Override + protected void configurePipeline(ChannelPipeline channelPipeline) {} +} diff --git a/instrumentation/netty/netty-4.1/library/build.gradle.kts b/instrumentation/netty/netty-4.1/library/build.gradle.kts new file mode 100644 index 000000000000..eb20fd9db5a8 --- /dev/null +++ b/instrumentation/netty/netty-4.1/library/build.gradle.kts @@ -0,0 +1,11 @@ +plugins { + id("otel.library-instrumentation") +} + +dependencies { + library("io.netty:netty-codec-http:4.1.0.Final") + implementation(project(":instrumentation:netty:netty-4-common:library")) + implementation(project(":instrumentation:netty:netty-common:library")) + + testImplementation(project(":instrumentation:netty:netty-4.1:testing")) +} diff --git a/instrumentation/netty/netty-4.1/library/src/main/java/io/opentelemetry/instrumentation/netty/v4_1/NettyClientTelemetry.java b/instrumentation/netty/netty-4.1/library/src/main/java/io/opentelemetry/instrumentation/netty/v4_1/NettyClientTelemetry.java new file mode 100644 index 000000000000..fcdda75106b3 --- /dev/null +++ b/instrumentation/netty/netty-4.1/library/src/main/java/io/opentelemetry/instrumentation/netty/v4_1/NettyClientTelemetry.java @@ -0,0 +1,77 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.netty.v4_1; + +import io.netty.channel.Channel; +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.channel.ChannelOutboundHandlerAdapter; +import io.netty.channel.CombinedChannelDuplexHandler; +import io.netty.handler.codec.http.HttpResponse; +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.context.Context; +import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; +import io.opentelemetry.instrumentation.netty.v4.common.HttpRequestAndChannel; +import io.opentelemetry.instrumentation.netty.v4_1.internal.AttributeKeys; +import io.opentelemetry.instrumentation.netty.v4_1.internal.client.HttpClientRequestTracingHandler; +import io.opentelemetry.instrumentation.netty.v4_1.internal.client.HttpClientResponseTracingHandler; +import io.opentelemetry.instrumentation.netty.v4_1.internal.client.HttpClientTracingHandler; + +/** Entrypoint for instrumenting Netty HTTP clients. */ +public final class NettyClientTelemetry { + + private final Instrumenter instrumenter; + + NettyClientTelemetry(Instrumenter instrumenter) { + this.instrumenter = instrumenter; + } + + /** Returns a new {@link NettyClientTelemetry} configured with the given {@link OpenTelemetry}. */ + public static NettyClientTelemetry create(OpenTelemetry openTelemetry) { + return builder(openTelemetry).build(); + } + + /** + * Returns a new {@link NettyClientTelemetryBuilder} configured with the given {@link + * OpenTelemetry}. + */ + public static NettyClientTelemetryBuilder builder(OpenTelemetry openTelemetry) { + return new NettyClientTelemetryBuilder(openTelemetry); + } + + /** + * /** Returns a new {@link ChannelOutboundHandlerAdapter} that generates telemetry for outgoing + * HTTP requests. Must be paired with {@link #createResponseHandler()}. + */ + public ChannelOutboundHandlerAdapter createRequestHandler() { + return new HttpClientRequestTracingHandler(instrumenter); + } + + /** + * Returns a new {@link ChannelInboundHandlerAdapter} that generates telemetry for incoming HTTP + * responses. Must be paired with {@link #createRequestHandler()}. + */ + public ChannelInboundHandlerAdapter createResponseHandler() { + return new HttpClientResponseTracingHandler(instrumenter); + } + + /** + * Returns a new {@link CombinedChannelDuplexHandler} that generates telemetry for outgoing HTTP + * requests and incoming responses in a single handler. + */ + public CombinedChannelDuplexHandler< + ? extends ChannelInboundHandlerAdapter, ? extends ChannelOutboundHandlerAdapter> + createCombinedHandler() { + return new HttpClientTracingHandler(instrumenter); + } + + /** + * Propagate the {@link Context} to the {@link Channel}. This MUST be called before each HTTP + * request executed on a {@link Channel}. + */ + public static void setChannelContext(Channel channel, Context context) { + channel.attr(AttributeKeys.WRITE_CONTEXT).set(context); + } +} diff --git a/instrumentation/netty/netty-4.1/library/src/main/java/io/opentelemetry/instrumentation/netty/v4_1/NettyClientTelemetryBuilder.java b/instrumentation/netty/netty-4.1/library/src/main/java/io/opentelemetry/instrumentation/netty/v4_1/NettyClientTelemetryBuilder.java new file mode 100644 index 000000000000..bf3a9080dd01 --- /dev/null +++ b/instrumentation/netty/netty-4.1/library/src/main/java/io/opentelemetry/instrumentation/netty/v4_1/NettyClientTelemetryBuilder.java @@ -0,0 +1,74 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.netty.v4_1; + +import com.google.errorprone.annotations.CanIgnoreReturnValue; +import io.netty.handler.codec.http.HttpResponse; +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor; +import io.opentelemetry.instrumentation.netty.v4.common.HttpRequestAndChannel; +import io.opentelemetry.instrumentation.netty.v4.common.internal.client.NettyClientInstrumenterFactory; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** A builder of {@link NettyClientTelemetry}. */ +public final class NettyClientTelemetryBuilder { + + private final OpenTelemetry openTelemetry; + private List capturedRequestHeaders = Collections.emptyList(); + private List capturedResponseHeaders = Collections.emptyList(); + private final List> + additionalAttributesExtractors = new ArrayList<>(); + + NettyClientTelemetryBuilder(OpenTelemetry openTelemetry) { + this.openTelemetry = openTelemetry; + } + + /** + * Configures the HTTP request headers that will be captured as span attributes. + * + * @param capturedRequestHeaders A list of HTTP header names. + */ + @CanIgnoreReturnValue + public NettyClientTelemetryBuilder setCapturedRequestHeaders( + List capturedRequestHeaders) { + this.capturedRequestHeaders = capturedRequestHeaders; + return this; + } + + /** + * Configures the HTTP response headers that will be captured as span attributes. + * + * @param capturedResponseHeaders A list of HTTP header names. + */ + @CanIgnoreReturnValue + public NettyClientTelemetryBuilder setCapturedResponseHeaders( + List capturedResponseHeaders) { + this.capturedResponseHeaders = capturedResponseHeaders; + return this; + } + + /** + * Adds an additional {@link AttributesExtractor} to invoke to set attributes to instrumented + * items. + */ + @CanIgnoreReturnValue + public NettyClientTelemetryBuilder addAttributesExtractor( + AttributesExtractor attributesExtractor) { + additionalAttributesExtractors.add(attributesExtractor); + return this; + } + + /** Returns a new {@link NettyClientTelemetry} with the given configuration. */ + public NettyClientTelemetry build() { + return new NettyClientTelemetry( + new NettyClientInstrumenterFactory( + openTelemetry, "io.opentelemetry.netty-4.1", false, false, Collections.emptyMap()) + .createHttpInstrumenter( + capturedRequestHeaders, capturedResponseHeaders, additionalAttributesExtractors)); + } +} diff --git a/instrumentation/netty/netty-4.1/library/src/main/java/io/opentelemetry/instrumentation/netty/v4_1/NettyServerTelemetry.java b/instrumentation/netty/netty-4.1/library/src/main/java/io/opentelemetry/instrumentation/netty/v4_1/NettyServerTelemetry.java new file mode 100644 index 000000000000..f78abcb67a8d --- /dev/null +++ b/instrumentation/netty/netty-4.1/library/src/main/java/io/opentelemetry/instrumentation/netty/v4_1/NettyServerTelemetry.java @@ -0,0 +1,66 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.netty.v4_1; + +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.channel.ChannelOutboundHandlerAdapter; +import io.netty.channel.CombinedChannelDuplexHandler; +import io.netty.handler.codec.http.HttpResponse; +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; +import io.opentelemetry.instrumentation.netty.v4.common.HttpRequestAndChannel; +import io.opentelemetry.instrumentation.netty.v4_1.internal.server.HttpServerRequestTracingHandler; +import io.opentelemetry.instrumentation.netty.v4_1.internal.server.HttpServerResponseTracingHandler; +import io.opentelemetry.instrumentation.netty.v4_1.internal.server.HttpServerTracingHandler; + +/** Entrypoint for instrumenting Netty HTTP servers. */ +public final class NettyServerTelemetry { + + private final Instrumenter instrumenter; + + NettyServerTelemetry(Instrumenter instrumenter) { + this.instrumenter = instrumenter; + } + + /** Returns a new {@link NettyServerTelemetry} configured with the given {@link OpenTelemetry}. */ + public static NettyServerTelemetry create(OpenTelemetry openTelemetry) { + return builder(openTelemetry).build(); + } + + /** + * Returns a new {@link NettyServerTelemetryBuilder} configured with the given {@link + * OpenTelemetry}. + */ + public static NettyServerTelemetryBuilder builder(OpenTelemetry openTelemetry) { + return new NettyServerTelemetryBuilder(openTelemetry); + } + + /** + * Returns a new {@link ChannelInboundHandlerAdapter} that generates telemetry for incoming HTTP + * requests. Must be paired with {@link #createResponseHandler()}. + */ + public ChannelInboundHandlerAdapter createRequestHandler() { + return new HttpServerRequestTracingHandler(instrumenter); + } + + /** + * Returns a new {@link ChannelOutboundHandlerAdapter} that generates telemetry for outgoing HTTP + * responses. Must be paired with {@link #createRequestHandler()}. + */ + public ChannelOutboundHandlerAdapter createResponseHandler() { + return new HttpServerResponseTracingHandler(instrumenter); + } + + /** + * Returns a new {@link CombinedChannelDuplexHandler} that generates telemetry for incoming HTTP + * requests and outgoing responses in a single handler. + */ + public CombinedChannelDuplexHandler< + ? extends ChannelInboundHandlerAdapter, ? extends ChannelOutboundHandlerAdapter> + createCombinedHandler() { + return new HttpServerTracingHandler(instrumenter); + } +} diff --git a/instrumentation/netty/netty-4.1/library/src/main/java/io/opentelemetry/instrumentation/netty/v4_1/NettyServerTelemetryBuilder.java b/instrumentation/netty/netty-4.1/library/src/main/java/io/opentelemetry/instrumentation/netty/v4_1/NettyServerTelemetryBuilder.java new file mode 100644 index 000000000000..9e802cfae9c2 --- /dev/null +++ b/instrumentation/netty/netty-4.1/library/src/main/java/io/opentelemetry/instrumentation/netty/v4_1/NettyServerTelemetryBuilder.java @@ -0,0 +1,58 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.netty.v4_1; + +import com.google.errorprone.annotations.CanIgnoreReturnValue; +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.instrumentation.netty.v4.common.internal.server.NettyServerInstrumenterFactory; +import java.util.Collections; +import java.util.List; + +/** A builder of {@link NettyServerTelemetry}. */ +public final class NettyServerTelemetryBuilder { + + private final OpenTelemetry openTelemetry; + private List capturedRequestHeaders = Collections.emptyList(); + private List capturedResponseHeaders = Collections.emptyList(); + + NettyServerTelemetryBuilder(OpenTelemetry openTelemetry) { + this.openTelemetry = openTelemetry; + } + + /** + * Configures the HTTP request headers that will be captured as span attributes. + * + * @param capturedRequestHeaders A list of HTTP header names. + */ + @CanIgnoreReturnValue + public NettyServerTelemetryBuilder setCapturedRequestHeaders( + List capturedRequestHeaders) { + this.capturedRequestHeaders = capturedRequestHeaders; + return this; + } + + /** + * Configures the HTTP response headers that will be captured as span attributes. + * + * @param capturedResponseHeaders A list of HTTP header names. + */ + @CanIgnoreReturnValue + public NettyServerTelemetryBuilder setCapturedResponseHeaders( + List capturedResponseHeaders) { + this.capturedResponseHeaders = capturedResponseHeaders; + return this; + } + + /** Returns a new {@link NettyServerTelemetry} with the given configuration. */ + public NettyServerTelemetry build() { + return new NettyServerTelemetry( + NettyServerInstrumenterFactory.create( + openTelemetry, + "io.opentelemetry.netty-4.1", + capturedRequestHeaders, + capturedResponseHeaders)); + } +} diff --git a/instrumentation/netty/netty-4.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_1/AttributeKeys.java b/instrumentation/netty/netty-4.1/library/src/main/java/io/opentelemetry/instrumentation/netty/v4_1/internal/AttributeKeys.java similarity index 82% rename from instrumentation/netty/netty-4.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_1/AttributeKeys.java rename to instrumentation/netty/netty-4.1/library/src/main/java/io/opentelemetry/instrumentation/netty/v4_1/internal/AttributeKeys.java index 73d46c5fe1a6..b06edb0582f8 100644 --- a/instrumentation/netty/netty-4.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_1/AttributeKeys.java +++ b/instrumentation/netty/netty-4.1/library/src/main/java/io/opentelemetry/instrumentation/netty/v4_1/internal/AttributeKeys.java @@ -3,11 +3,15 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.javaagent.instrumentation.netty.v4_1; +package io.opentelemetry.instrumentation.netty.v4_1.internal; import io.netty.util.AttributeKey; import io.opentelemetry.context.Context; +/** + * This class is internal and is hence not for public use. Its APIs are unstable and can change at + * any time. + */ public final class AttributeKeys { public static final AttributeKey WRITE_CONTEXT = diff --git a/instrumentation/netty/netty-4.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_1/client/HttpClientRequestTracingHandler.java b/instrumentation/netty/netty-4.1/library/src/main/java/io/opentelemetry/instrumentation/netty/v4_1/internal/client/HttpClientRequestTracingHandler.java similarity index 66% rename from instrumentation/netty/netty-4.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_1/client/HttpClientRequestTracingHandler.java rename to instrumentation/netty/netty-4.1/library/src/main/java/io/opentelemetry/instrumentation/netty/v4_1/internal/client/HttpClientRequestTracingHandler.java index 73682c22ca7b..9c1317465b7e 100644 --- a/instrumentation/netty/netty-4.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_1/client/HttpClientRequestTracingHandler.java +++ b/instrumentation/netty/netty-4.1/library/src/main/java/io/opentelemetry/instrumentation/netty/v4_1/internal/client/HttpClientRequestTracingHandler.java @@ -3,23 +3,37 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.javaagent.instrumentation.netty.v4_1.client; - -import static io.opentelemetry.javaagent.instrumentation.netty.v4_1.client.NettyClientSingletons.HTTP_REQUEST; -import static io.opentelemetry.javaagent.instrumentation.netty.v4_1.client.NettyClientSingletons.instrumenter; +package io.opentelemetry.instrumentation.netty.v4_1.internal.client; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelOutboundHandlerAdapter; import io.netty.channel.ChannelPromise; import io.netty.handler.codec.http.HttpRequest; +import io.netty.handler.codec.http.HttpResponse; import io.netty.util.Attribute; +import io.netty.util.AttributeKey; import io.opentelemetry.context.Context; import io.opentelemetry.context.Scope; -import io.opentelemetry.javaagent.instrumentation.netty.v4.common.HttpRequestAndChannel; -import io.opentelemetry.javaagent.instrumentation.netty.v4_1.AttributeKeys; +import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; +import io.opentelemetry.instrumentation.netty.v4.common.HttpRequestAndChannel; +import io.opentelemetry.instrumentation.netty.v4_1.internal.AttributeKeys; +/** + * This class is internal and is hence not for public use. Its APIs are unstable and can change at + * any time. + */ public class HttpClientRequestTracingHandler extends ChannelOutboundHandlerAdapter { + public static final AttributeKey HTTP_REQUEST = + AttributeKey.valueOf(HttpClientRequestTracingHandler.class, "http-client-request"); + + private final Instrumenter instrumenter; + + public HttpClientRequestTracingHandler( + Instrumenter instrumenter) { + this.instrumenter = instrumenter; + } + @Override public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise prm) { if (!(msg instanceof HttpRequest)) { @@ -27,13 +41,13 @@ public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise prm) { return; } - Context parentContext = ctx.channel().attr(AttributeKeys.WRITE_CONTEXT).getAndRemove(); + Context parentContext = ctx.channel().attr(AttributeKeys.WRITE_CONTEXT).getAndSet(null); if (parentContext == null) { parentContext = Context.current(); } HttpRequestAndChannel request = HttpRequestAndChannel.create((HttpRequest) msg, ctx.channel()); - if (!instrumenter().shouldStart(parentContext, request) || isAwsRequest(request)) { + if (!instrumenter.shouldStart(parentContext, request) || isAwsRequest(request)) { ctx.write(msg, prm); return; } @@ -42,7 +56,7 @@ public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise prm) { Attribute contextAttr = ctx.channel().attr(AttributeKeys.CLIENT_CONTEXT); Attribute requestAttr = ctx.channel().attr(HTTP_REQUEST); - Context context = instrumenter().start(parentContext, request); + Context context = instrumenter.start(parentContext, request); parentContextAttr.set(parentContext); contextAttr.set(context); requestAttr.set(request); @@ -51,8 +65,8 @@ public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise prm) { ctx.write(msg, prm); // span is ended normally in HttpClientResponseTracingHandler } catch (Throwable throwable) { - instrumenter().end(contextAttr.getAndRemove(), requestAttr.getAndRemove(), null, throwable); - parentContextAttr.remove(); + instrumenter.end(contextAttr.getAndSet(null), requestAttr.getAndSet(null), null, throwable); + parentContextAttr.set(null); throw throwable; } } diff --git a/instrumentation/netty/netty-4.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_1/client/HttpClientResponseTracingHandler.java b/instrumentation/netty/netty-4.1/library/src/main/java/io/opentelemetry/instrumentation/netty/v4_1/internal/client/HttpClientResponseTracingHandler.java similarity index 56% rename from instrumentation/netty/netty-4.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_1/client/HttpClientResponseTracingHandler.java rename to instrumentation/netty/netty-4.1/library/src/main/java/io/opentelemetry/instrumentation/netty/v4_1/internal/client/HttpClientResponseTracingHandler.java index 98c91bad6158..30a03d8718dd 100644 --- a/instrumentation/netty/netty-4.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_1/client/HttpClientResponseTracingHandler.java +++ b/instrumentation/netty/netty-4.1/library/src/main/java/io/opentelemetry/instrumentation/netty/v4_1/internal/client/HttpClientResponseTracingHandler.java @@ -3,10 +3,9 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.javaagent.instrumentation.netty.v4_1.client; +package io.opentelemetry.instrumentation.netty.v4_1.internal.client; -import static io.opentelemetry.javaagent.instrumentation.netty.v4_1.client.NettyClientSingletons.HTTP_RESPONSE; -import static io.opentelemetry.javaagent.instrumentation.netty.v4_1.client.NettyClientSingletons.instrumenter; +import static io.opentelemetry.instrumentation.netty.v4_1.internal.client.HttpClientRequestTracingHandler.HTTP_REQUEST; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; @@ -14,13 +13,29 @@ import io.netty.handler.codec.http.HttpResponse; import io.netty.handler.codec.http.LastHttpContent; import io.netty.util.Attribute; +import io.netty.util.AttributeKey; import io.opentelemetry.context.Context; import io.opentelemetry.context.Scope; -import io.opentelemetry.javaagent.instrumentation.netty.v4.common.HttpRequestAndChannel; -import io.opentelemetry.javaagent.instrumentation.netty.v4_1.AttributeKeys; +import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; +import io.opentelemetry.instrumentation.netty.v4.common.HttpRequestAndChannel; +import io.opentelemetry.instrumentation.netty.v4_1.internal.AttributeKeys; +/** + * This class is internal and is hence not for public use. Its APIs are unstable and can change at + * any time. + */ public class HttpClientResponseTracingHandler extends ChannelInboundHandlerAdapter { + private static final AttributeKey HTTP_RESPONSE = + AttributeKey.valueOf(HttpClientResponseTracingHandler.class, "http-client-response"); + + private final Instrumenter instrumenter; + + public HttpClientResponseTracingHandler( + Instrumenter instrumenter) { + this.instrumenter = instrumenter; + } + @Override public void channelRead(ChannelHandlerContext ctx, Object msg) { Attribute contextAttr = ctx.channel().attr(AttributeKeys.CLIENT_CONTEXT); @@ -31,25 +46,24 @@ public void channelRead(ChannelHandlerContext ctx, Object msg) { } Attribute parentContextAttr = ctx.channel().attr(AttributeKeys.CLIENT_PARENT_CONTEXT); - Attribute requestAttr = - ctx.channel().attr(NettyClientSingletons.HTTP_REQUEST); + Attribute requestAttr = ctx.channel().attr(HTTP_REQUEST); Context parentContext = parentContextAttr.get(); HttpRequestAndChannel request = requestAttr.get(); if (msg instanceof FullHttpResponse) { - parentContextAttr.remove(); - contextAttr.remove(); - requestAttr.remove(); + parentContextAttr.set(null); + contextAttr.set(null); + requestAttr.set(null); } else if (msg instanceof HttpResponse) { // Headers before body have been received, store them to use when finishing the span. ctx.channel().attr(HTTP_RESPONSE).set((HttpResponse) msg); } else if (msg instanceof LastHttpContent) { // Not a FullHttpResponse so this is content that has been received after headers. Finish the // span using what we stored in attrs. - parentContextAttr.remove(); - contextAttr.remove(); - requestAttr.remove(); + parentContextAttr.set(null); + contextAttr.set(null); + requestAttr.set(null); } // We want the callback in the scope of the parent, not the client span @@ -62,9 +76,9 @@ public void channelRead(ChannelHandlerContext ctx, Object msg) { } if (msg instanceof FullHttpResponse) { - instrumenter().end(context, request, (HttpResponse) msg, null); + instrumenter.end(context, request, (HttpResponse) msg, null); } else if (msg instanceof LastHttpContent) { - instrumenter().end(context, request, ctx.channel().attr(HTTP_RESPONSE).getAndRemove(), null); + instrumenter.end(context, request, ctx.channel().attr(HTTP_RESPONSE).getAndSet(null), null); } } } diff --git a/instrumentation/netty/netty-4.1/library/src/main/java/io/opentelemetry/instrumentation/netty/v4_1/internal/client/HttpClientTracingHandler.java b/instrumentation/netty/netty-4.1/library/src/main/java/io/opentelemetry/instrumentation/netty/v4_1/internal/client/HttpClientTracingHandler.java new file mode 100644 index 000000000000..aa7cc2f39adb --- /dev/null +++ b/instrumentation/netty/netty-4.1/library/src/main/java/io/opentelemetry/instrumentation/netty/v4_1/internal/client/HttpClientTracingHandler.java @@ -0,0 +1,26 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.netty.v4_1.internal.client; + +import io.netty.channel.CombinedChannelDuplexHandler; +import io.netty.handler.codec.http.HttpResponse; +import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; +import io.opentelemetry.instrumentation.netty.v4.common.HttpRequestAndChannel; + +/** + * This class is internal and is hence not for public use. Its APIs are unstable and can change at + * any time. + */ +public class HttpClientTracingHandler + extends CombinedChannelDuplexHandler< + HttpClientResponseTracingHandler, HttpClientRequestTracingHandler> { + + public HttpClientTracingHandler(Instrumenter instrumenter) { + super( + new HttpClientResponseTracingHandler(instrumenter), + new HttpClientRequestTracingHandler(instrumenter)); + } +} diff --git a/instrumentation/netty/netty-4.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_1/server/HttpServerRequestTracingHandler.java b/instrumentation/netty/netty-4.1/library/src/main/java/io/opentelemetry/instrumentation/netty/v4_1/internal/server/HttpServerRequestTracingHandler.java similarity index 59% rename from instrumentation/netty/netty-4.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_1/server/HttpServerRequestTracingHandler.java rename to instrumentation/netty/netty-4.1/library/src/main/java/io/opentelemetry/instrumentation/netty/v4_1/internal/server/HttpServerRequestTracingHandler.java index 4ae4e24ca1b4..0a666b162302 100644 --- a/instrumentation/netty/netty-4.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_1/server/HttpServerRequestTracingHandler.java +++ b/instrumentation/netty/netty-4.1/library/src/main/java/io/opentelemetry/instrumentation/netty/v4_1/internal/server/HttpServerRequestTracingHandler.java @@ -3,27 +3,42 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.javaagent.instrumentation.netty.v4_1.server; - -import static io.opentelemetry.javaagent.instrumentation.netty.v4_1.server.NettyServerSingletons.instrumenter; +package io.opentelemetry.instrumentation.netty.v4_1.internal.server; import io.netty.channel.Channel; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.handler.codec.http.HttpRequest; +import io.netty.handler.codec.http.HttpResponse; import io.netty.util.Attribute; +import io.netty.util.AttributeKey; import io.opentelemetry.context.Context; import io.opentelemetry.context.Scope; -import io.opentelemetry.javaagent.instrumentation.netty.v4.common.HttpRequestAndChannel; -import io.opentelemetry.javaagent.instrumentation.netty.v4_1.AttributeKeys; +import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; +import io.opentelemetry.instrumentation.netty.v4.common.HttpRequestAndChannel; +import io.opentelemetry.instrumentation.netty.v4_1.internal.AttributeKeys; +/** + * This class is internal and is hence not for public use. Its APIs are unstable and can change at + * any time. + */ public class HttpServerRequestTracingHandler extends ChannelInboundHandlerAdapter { + static final AttributeKey HTTP_REQUEST = + AttributeKey.valueOf(HttpServerRequestTracingHandler.class, "http-server-request"); + + private final Instrumenter instrumenter; + + public HttpServerRequestTracingHandler( + Instrumenter instrumenter) { + this.instrumenter = instrumenter; + } + @Override public void channelRead(ChannelHandlerContext ctx, Object msg) { Channel channel = ctx.channel(); Attribute contextAttr = channel.attr(AttributeKeys.SERVER_CONTEXT); - Attribute requestAttr = channel.attr(NettyServerSingletons.HTTP_REQUEST); + Attribute requestAttr = channel.attr(HTTP_REQUEST); if (!(msg instanceof HttpRequest)) { Context serverContext = contextAttr.get(); @@ -43,12 +58,12 @@ public void channelRead(ChannelHandlerContext ctx, Object msg) { } HttpRequestAndChannel request = HttpRequestAndChannel.create((HttpRequest) msg, channel); - if (!instrumenter().shouldStart(parentContext, request)) { + if (!instrumenter.shouldStart(parentContext, request)) { ctx.fireChannelRead(msg); return; } - Context context = instrumenter().start(parentContext, request); + Context context = instrumenter.start(parentContext, request); contextAttr.set(context); requestAttr.set(request); @@ -57,7 +72,7 @@ public void channelRead(ChannelHandlerContext ctx, Object msg) { // the span is ended normally in HttpServerResponseTracingHandler } catch (Throwable throwable) { // make sure to remove the server context on end() call - instrumenter().end(contextAttr.getAndRemove(), requestAttr.getAndRemove(), null, throwable); + instrumenter.end(contextAttr.getAndSet(null), requestAttr.getAndSet(null), null, throwable); throw throwable; } } diff --git a/instrumentation/netty/netty-4.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_1/server/HttpServerResponseTracingHandler.java b/instrumentation/netty/netty-4.1/library/src/main/java/io/opentelemetry/instrumentation/netty/v4_1/internal/server/HttpServerResponseTracingHandler.java similarity index 65% rename from instrumentation/netty/netty-4.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_1/server/HttpServerResponseTracingHandler.java rename to instrumentation/netty/netty-4.1/library/src/main/java/io/opentelemetry/instrumentation/netty/v4_1/internal/server/HttpServerResponseTracingHandler.java index fcbea1d63527..95b562bd9f75 100644 --- a/instrumentation/netty/netty-4.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_1/server/HttpServerResponseTracingHandler.java +++ b/instrumentation/netty/netty-4.1/library/src/main/java/io/opentelemetry/instrumentation/netty/v4_1/internal/server/HttpServerResponseTracingHandler.java @@ -3,9 +3,9 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.javaagent.instrumentation.netty.v4_1.server; +package io.opentelemetry.instrumentation.netty.v4_1.internal.server; -import static io.opentelemetry.javaagent.instrumentation.netty.v4_1.server.NettyServerSingletons.instrumenter; +import static io.opentelemetry.instrumentation.netty.v4_1.internal.server.HttpServerRequestTracingHandler.HTTP_REQUEST; import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; @@ -16,15 +16,31 @@ import io.netty.handler.codec.http.HttpResponse; import io.netty.handler.codec.http.LastHttpContent; import io.netty.util.Attribute; +import io.netty.util.AttributeKey; import io.opentelemetry.context.Context; import io.opentelemetry.context.Scope; -import io.opentelemetry.javaagent.instrumentation.netty.common.NettyErrorHolder; -import io.opentelemetry.javaagent.instrumentation.netty.v4.common.HttpRequestAndChannel; -import io.opentelemetry.javaagent.instrumentation.netty.v4_1.AttributeKeys; +import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; +import io.opentelemetry.instrumentation.netty.common.internal.NettyErrorHolder; +import io.opentelemetry.instrumentation.netty.v4.common.HttpRequestAndChannel; +import io.opentelemetry.instrumentation.netty.v4_1.internal.AttributeKeys; import 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 HttpServerResponseTracingHandler extends ChannelOutboundHandlerAdapter { + static final AttributeKey HTTP_RESPONSE = + AttributeKey.valueOf(HttpServerResponseTracingHandler.class, "http-server-response"); + + private final Instrumenter instrumenter; + + public HttpServerResponseTracingHandler( + Instrumenter instrumenter) { + this.instrumenter = instrumenter; + } + @Override public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise prm) { Attribute contextAttr = ctx.channel().attr(AttributeKeys.SERVER_CONTEXT); @@ -59,14 +75,14 @@ public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise prm) { future -> end( ctx.channel(), - ctx.channel().attr(NettyServerSingletons.HTTP_RESPONSE).getAndRemove(), + ctx.channel().attr(HTTP_RESPONSE).getAndSet(null), writePromise)); } } else { writePromise = prm; if (msg instanceof HttpResponse) { // Headers before body has been sent, store them to use when finishing the span. - ctx.channel().attr(NettyServerSingletons.HTTP_RESPONSE).set((HttpResponse) msg); + ctx.channel().attr(HTTP_RESPONSE).set((HttpResponse) msg); } } @@ -78,17 +94,16 @@ public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise prm) { } } - private static void end(Channel channel, HttpResponse response, ChannelFuture future) { + private void end(Channel channel, HttpResponse response, ChannelFuture future) { Throwable error = future.isSuccess() ? null : future.cause(); end(channel, response, error); } // make sure to remove the server context on end() call - private static void end( - Channel channel, @Nullable HttpResponse response, @Nullable Throwable error) { - Context context = channel.attr(AttributeKeys.SERVER_CONTEXT).getAndRemove(); - HttpRequestAndChannel request = channel.attr(NettyServerSingletons.HTTP_REQUEST).getAndRemove(); + private void end(Channel channel, @Nullable HttpResponse response, @Nullable Throwable error) { + Context context = channel.attr(AttributeKeys.SERVER_CONTEXT).getAndSet(null); + HttpRequestAndChannel request = channel.attr(HTTP_REQUEST).getAndSet(null); error = NettyErrorHolder.getOrDefault(context, error); - instrumenter().end(context, request, response, error); + instrumenter.end(context, request, response, error); } } diff --git a/instrumentation/netty/netty-4.1/library/src/main/java/io/opentelemetry/instrumentation/netty/v4_1/internal/server/HttpServerTracingHandler.java b/instrumentation/netty/netty-4.1/library/src/main/java/io/opentelemetry/instrumentation/netty/v4_1/internal/server/HttpServerTracingHandler.java new file mode 100644 index 000000000000..817e3b08c631 --- /dev/null +++ b/instrumentation/netty/netty-4.1/library/src/main/java/io/opentelemetry/instrumentation/netty/v4_1/internal/server/HttpServerTracingHandler.java @@ -0,0 +1,26 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.netty.v4_1.internal.server; + +import io.netty.channel.CombinedChannelDuplexHandler; +import io.netty.handler.codec.http.HttpResponse; +import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; +import io.opentelemetry.instrumentation.netty.v4.common.HttpRequestAndChannel; + +/** + * This class is internal and is hence not for public use. Its APIs are unstable and can change at + * any time. + */ +public class HttpServerTracingHandler + extends CombinedChannelDuplexHandler< + HttpServerRequestTracingHandler, HttpServerResponseTracingHandler> { + + public HttpServerTracingHandler(Instrumenter instrumenter) { + super( + new HttpServerRequestTracingHandler(instrumenter), + new HttpServerResponseTracingHandler(instrumenter)); + } +} diff --git a/instrumentation/netty/netty-4.1/library/src/test/java/io/opentelemetry/instrumentation/netty/v4_1/Netty41ClientTest.java b/instrumentation/netty/netty-4.1/library/src/test/java/io/opentelemetry/instrumentation/netty/v4_1/Netty41ClientTest.java new file mode 100644 index 000000000000..c41edbe2d091 --- /dev/null +++ b/instrumentation/netty/netty-4.1/library/src/test/java/io/opentelemetry/instrumentation/netty/v4_1/Netty41ClientTest.java @@ -0,0 +1,68 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.netty.v4_1; + +import io.netty.channel.Channel; +import io.opentelemetry.context.Context; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.http.HttpClientInstrumentationExtension; +import org.junit.jupiter.api.extension.RegisterExtension; + +public class Netty41ClientTest extends AbstractNetty41ClientTest { + + @RegisterExtension + static final InstrumentationExtension testing = HttpClientInstrumentationExtension.forLibrary(); + + @RegisterExtension + static final Netty41ClientExtension clientExtension = + new Netty41ClientExtension( + channelPipeline -> + channelPipeline.addLast( + NettyClientTelemetry.builder(testing.getOpenTelemetry()) + .build() + .createCombinedHandler())); + + @Override + protected Netty41ClientExtension clientExtension() { + return clientExtension; + } + + @Override + protected void configureChannel(Channel channel) { + // Current context must be propagated to the channel + NettyClientTelemetry.setChannelContext(channel, Context.current()); + } + + @Override + protected boolean testReadTimeout() { + return false; + } + + @Override + protected boolean testErrorWithCallback() { + return false; + } + + @Override + protected boolean testCallbackWithParent() { + return false; + } + + @Override + protected boolean testWithClientParent() { + return false; + } + + @Override + protected boolean testConnectionFailure() { + return false; + } + + @Override + protected boolean testRemoteConnection() { + return false; + } +} diff --git a/instrumentation/netty/netty-4.1/library/src/test/java/io/opentelemetry/instrumentation/netty/v4_1/Netty41ServerTest.java b/instrumentation/netty/netty-4.1/library/src/test/java/io/opentelemetry/instrumentation/netty/v4_1/Netty41ServerTest.java new file mode 100644 index 000000000000..89dacb672f94 --- /dev/null +++ b/instrumentation/netty/netty-4.1/library/src/test/java/io/opentelemetry/instrumentation/netty/v4_1/Netty41ServerTest.java @@ -0,0 +1,34 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.netty.v4_1; + +import io.netty.channel.ChannelPipeline; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.http.AbstractHttpServerTest; +import io.opentelemetry.instrumentation.testing.junit.http.HttpServerInstrumentationExtension; +import io.opentelemetry.testing.internal.io.netty.handler.codec.http.HttpServerCodec; +import java.util.Collections; +import org.junit.jupiter.api.extension.RegisterExtension; + +public class Netty41ServerTest extends AbstractNetty41ServerTest { + + @RegisterExtension + static final InstrumentationExtension testing = HttpServerInstrumentationExtension.forLibrary(); + + @Override + protected void configurePipeline(ChannelPipeline channelPipeline) { + channelPipeline.addAfter( + HttpServerCodec.class.getSimpleName() + "#0", + NettyServerTelemetry.class.getName(), + NettyServerTelemetry.builder(testing.getOpenTelemetry()) + .setCapturedRequestHeaders( + Collections.singletonList(AbstractHttpServerTest.TEST_REQUEST_HEADER)) + .setCapturedResponseHeaders( + Collections.singletonList(AbstractHttpServerTest.TEST_RESPONSE_HEADER)) + .build() + .createCombinedHandler()); + } +} diff --git a/instrumentation/netty/netty-4.1/testing/build.gradle.kts b/instrumentation/netty/netty-4.1/testing/build.gradle.kts new file mode 100644 index 000000000000..8b6a72e1ba5a --- /dev/null +++ b/instrumentation/netty/netty-4.1/testing/build.gradle.kts @@ -0,0 +1,9 @@ +plugins { + id("otel.java-conventions") +} + +dependencies { + api(project(":testing-common")) + + api("io.netty:netty-codec-http:4.1.0.Final") +} diff --git a/instrumentation/netty/netty-4.1/testing/src/main/java/io/opentelemetry/instrumentation/netty/v4_1/AbstractNetty41ClientTest.java b/instrumentation/netty/netty-4.1/testing/src/main/java/io/opentelemetry/instrumentation/netty/v4_1/AbstractNetty41ClientTest.java new file mode 100644 index 000000000000..7985123b037c --- /dev/null +++ b/instrumentation/netty/netty-4.1/testing/src/main/java/io/opentelemetry/instrumentation/netty/v4_1/AbstractNetty41ClientTest.java @@ -0,0 +1,128 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.netty.v4_1; + +import static io.opentelemetry.instrumentation.test.base.HttpClientTest.getPort; + +import io.netty.buffer.Unpooled; +import io.netty.channel.Channel; +import io.netty.handler.codec.http.DefaultFullHttpRequest; +import io.netty.handler.codec.http.HttpHeaderNames; +import io.netty.handler.codec.http.HttpMethod; +import io.netty.handler.codec.http.HttpVersion; +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.instrumentation.testing.junit.http.AbstractHttpClientTest; +import io.opentelemetry.instrumentation.testing.junit.http.SingleConnection; +import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; +import java.net.URI; +import java.util.Collections; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +public abstract class AbstractNetty41ClientTest + extends AbstractHttpClientTest { + + protected abstract Netty41ClientExtension clientExtension(); + + protected abstract void configureChannel(Channel channel); + + @Override + protected boolean testRedirects() { + return false; + } + + @Override + protected DefaultFullHttpRequest buildRequest( + String method, URI uri, Map headers) { + String target = uri.getPath(); + if (uri.getQuery() != null) { + target += "?" + uri.getQuery(); + } + DefaultFullHttpRequest request = + new DefaultFullHttpRequest( + HttpVersion.HTTP_1_1, HttpMethod.valueOf(method), target, Unpooled.EMPTY_BUFFER); + request.headers().set(HttpHeaderNames.HOST, uri.getHost() + ":" + uri.getPort()); + headers.forEach((k, v) -> request.headers().set(k, v)); + return request; + } + + @Override + protected int sendRequest( + DefaultFullHttpRequest defaultFullHttpRequest, + String method, + URI uri, + Map headers) + throws InterruptedException, ExecutionException, TimeoutException { + Channel channel = + clientExtension().getBootstrap(uri).connect(uri.getHost(), getPort(uri)).sync().channel(); + configureChannel(channel); + CompletableFuture result = new CompletableFuture<>(); + channel.pipeline().addLast(new ClientHandler(result)); + channel.writeAndFlush(defaultFullHttpRequest).get(); + return result.get(20, TimeUnit.SECONDS); + } + + @Override + @SuppressWarnings( + "CatchingUnchecked") // Checked exception is thrown when connecting to unopened port + protected void sendRequestWithCallback( + DefaultFullHttpRequest defaultFullHttpRequest, + String method, + URI uri, + Map headers, + RequestResult requestResult) { + Channel ch; + try { + ch = + clientExtension().getBootstrap(uri).connect(uri.getHost(), getPort(uri)).sync().channel(); + } catch (InterruptedException exception) { + Thread.currentThread().interrupt(); + return; + } catch (Exception exception) { + requestResult.complete(exception); + return; + } + configureChannel(ch); + CompletableFuture result = new CompletableFuture<>(); + result.whenComplete((status, throwable) -> requestResult.complete(() -> status, throwable)); + ch.pipeline().addLast(new ClientHandler(result)); + ch.writeAndFlush(defaultFullHttpRequest); + } + + @Override + protected String expectedClientSpanName(URI uri, String method) { + switch (uri.toString()) { + case "http://localhost:61/": // unopened port + case "https://192.0.2.1/": // non routable address + return "CONNECT"; + default: + return super.expectedClientSpanName(uri, method); + } + } + + @Override + protected Set> httpAttributes(URI uri) { + String uriString = uri.toString(); + // http://localhost:61/ => unopened port, https://192.0.2.1/ => non routable address + if ("http://localhost:61/".equals(uriString) || "https://192.0.2.1/".equals(uriString)) { + return Collections.emptySet(); + } + Set> attributes = super.httpAttributes(uri); + attributes.remove(SemanticAttributes.NET_PEER_NAME); + attributes.remove(SemanticAttributes.NET_PEER_PORT); + return attributes; + } + + @Override + protected SingleConnection createSingleConnection(String host, int port) { + return new SingleNettyConnection( + clientExtension().buildBootstrap(false, false), host, port, this::configureChannel); + } +} diff --git a/instrumentation/netty/netty-4.1/testing/src/main/java/io/opentelemetry/instrumentation/netty/v4_1/AbstractNetty41ServerTest.java b/instrumentation/netty/netty-4.1/testing/src/main/java/io/opentelemetry/instrumentation/netty/v4_1/AbstractNetty41ServerTest.java new file mode 100644 index 000000000000..0d517276d82d --- /dev/null +++ b/instrumentation/netty/netty-4.1/testing/src/main/java/io/opentelemetry/instrumentation/netty/v4_1/AbstractNetty41ServerTest.java @@ -0,0 +1,176 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.netty.v4_1; + +import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_LENGTH; +import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_TYPE; +import static io.netty.handler.codec.http.HttpResponseStatus.INTERNAL_SERVER_ERROR; +import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1; +import static io.opentelemetry.instrumentation.testing.junit.http.HttpServerTestOptions.DEFAULT_HTTP_ATTRIBUTES; +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.NOT_FOUND; + +import com.google.common.collect.Sets; +import io.netty.bootstrap.ServerBootstrap; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInitializer; +import io.netty.channel.ChannelPipeline; +import io.netty.channel.EventLoopGroup; +import io.netty.channel.SimpleChannelInboundHandler; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.SocketChannel; +import io.netty.channel.socket.nio.NioServerSocketChannel; +import io.netty.handler.codec.http.DefaultFullHttpResponse; +import io.netty.handler.codec.http.FullHttpResponse; +import io.netty.handler.codec.http.HttpHeaderNames; +import io.netty.handler.codec.http.HttpRequest; +import io.netty.handler.codec.http.HttpResponseStatus; +import io.netty.handler.codec.http.HttpServerCodec; +import io.netty.handler.codec.http.QueryStringDecoder; +import io.netty.handler.logging.LogLevel; +import io.netty.handler.logging.LoggingHandler; +import io.netty.util.CharsetUtil; +import io.opentelemetry.instrumentation.testing.junit.http.AbstractHttpServerTest; +import io.opentelemetry.instrumentation.testing.junit.http.HttpServerTestOptions; +import io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint; +import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; +import java.net.URI; +import java.util.Collections; + +public abstract class AbstractNetty41ServerTest extends AbstractHttpServerTest { + + static final LoggingHandler LOGGING_HANDLER = + new LoggingHandler(AbstractNetty41ServerTest.class, LogLevel.DEBUG); + + protected abstract void configurePipeline(ChannelPipeline channelPipeline); + + @Override + protected void configure(HttpServerTestOptions options) { + options.setTestException(false); + options.setHttpAttributes( + unused -> + Sets.difference( + DEFAULT_HTTP_ATTRIBUTES, Collections.singleton(SemanticAttributes.HTTP_ROUTE))); + } + + @Override + protected EventLoopGroup setupServer() { + NioEventLoopGroup eventLoopGroup = new NioEventLoopGroup(); + + ServerBootstrap bootstrap = + new ServerBootstrap() + .group(eventLoopGroup) + .handler(LOGGING_HANDLER) + .childHandler( + new ChannelInitializer() { + @Override + protected void initChannel(SocketChannel socketChannel) { + ChannelPipeline pipeline = socketChannel.pipeline(); + pipeline.addFirst("logger", LOGGING_HANDLER); + pipeline.addLast(new HttpServerCodec()); + pipeline.addLast(new HttpHandler()); + configurePipeline(pipeline); + } + }) + .channel(NioServerSocketChannel.class); + try { + bootstrap.bind(port).sync(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + + return eventLoopGroup; + } + + @Override + protected void stopServer(EventLoopGroup server) { + server.shutdownGracefully(); + } + + private static class HttpHandler extends SimpleChannelInboundHandler { + + @Override + protected void channelRead0(ChannelHandlerContext ctx, Object msg) { + if (msg instanceof HttpRequest) { + HttpRequest request = (HttpRequest) msg; + URI uri = URI.create(request.uri()); + ServerEndpoint endpoint = ServerEndpoint.forPath(uri.getPath()); + ctx.write(controller(endpoint, () -> handle(request, uri, endpoint))); + } + } + + private static Object handle(HttpRequest request, URI uri, ServerEndpoint endpoint) { + ByteBuf content; + FullHttpResponse response; + switch (endpoint) { + case SUCCESS: + case ERROR: + content = Unpooled.copiedBuffer(endpoint.getBody(), CharsetUtil.UTF_8); + response = + new DefaultFullHttpResponse( + HTTP_1_1, HttpResponseStatus.valueOf(endpoint.getStatus()), content); + break; + case INDEXED_CHILD: + content = Unpooled.EMPTY_BUFFER; + endpoint.collectSpanAttributes( + name -> + new QueryStringDecoder(uri) + .parameters().get(name).stream().findFirst().orElse("")); + response = + new DefaultFullHttpResponse( + HTTP_1_1, HttpResponseStatus.valueOf(endpoint.getStatus()), content); + break; + case QUERY_PARAM: + content = Unpooled.copiedBuffer(uri.getQuery(), CharsetUtil.UTF_8); + response = + new DefaultFullHttpResponse( + HTTP_1_1, HttpResponseStatus.valueOf(endpoint.getStatus()), content); + break; + case REDIRECT: + content = Unpooled.EMPTY_BUFFER; + response = + new DefaultFullHttpResponse( + HTTP_1_1, HttpResponseStatus.valueOf(endpoint.getStatus()), content); + response.headers().set(HttpHeaderNames.LOCATION, endpoint.getBody()); + break; + case CAPTURE_HEADERS: + content = Unpooled.copiedBuffer(endpoint.getBody(), CharsetUtil.UTF_8); + response = + new DefaultFullHttpResponse( + HTTP_1_1, HttpResponseStatus.valueOf(endpoint.getStatus()), content); + response.headers().set("X-Test-Response", request.headers().get("X-Test-Request")); + break; + case EXCEPTION: + throw new IllegalStateException(endpoint.getBody()); + default: + content = Unpooled.copiedBuffer(NOT_FOUND.getBody(), CharsetUtil.UTF_8); + response = + new DefaultFullHttpResponse( + HTTP_1_1, HttpResponseStatus.valueOf(NOT_FOUND.getStatus()), content); + break; + } + response.headers().set(CONTENT_TYPE, "text/plain"); + response.headers().set(CONTENT_LENGTH, content.readableBytes()); + return response; + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { + ByteBuf content = Unpooled.copiedBuffer(cause.getMessage(), CharsetUtil.UTF_8); + FullHttpResponse response = + new DefaultFullHttpResponse(HTTP_1_1, INTERNAL_SERVER_ERROR, content); + response.headers().set(CONTENT_TYPE, "text/plain"); + response.headers().set(CONTENT_LENGTH, content.readableBytes()); + ctx.write(response); + } + + @Override + public void channelReadComplete(ChannelHandlerContext ctx) { + ctx.flush(); + } + } +} diff --git a/instrumentation/netty/netty-4.1/javaagent/src/test/groovy/ClientHandler.java b/instrumentation/netty/netty-4.1/testing/src/main/java/io/opentelemetry/instrumentation/netty/v4_1/ClientHandler.java similarity index 93% rename from instrumentation/netty/netty-4.1/javaagent/src/test/groovy/ClientHandler.java rename to instrumentation/netty/netty-4.1/testing/src/main/java/io/opentelemetry/instrumentation/netty/v4_1/ClientHandler.java index 299d90a6d3b4..0448d0f307ec 100644 --- a/instrumentation/netty/netty-4.1/javaagent/src/test/groovy/ClientHandler.java +++ b/instrumentation/netty/netty-4.1/testing/src/main/java/io/opentelemetry/instrumentation/netty/v4_1/ClientHandler.java @@ -3,6 +3,8 @@ * SPDX-License-Identifier: Apache-2.0 */ +package io.opentelemetry.instrumentation.netty.v4_1; + import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; import io.netty.handler.codec.http.FullHttpResponse; @@ -33,13 +35,13 @@ public void channelRead0(ChannelHandlerContext ctx, HttpObject msg) { if (msg instanceof FullHttpResponse) { ctx.pipeline().remove(this); FullHttpResponse response = (FullHttpResponse) msg; - responseCode.complete(response.getStatus().code()); + responseCode.complete(response.status().code()); } else if (msg instanceof HttpResponse) { // Headers before body have been received, store them to use when finishing the span. ctx.channel().attr(HTTP_RESPONSE).set((HttpResponse) msg); } else if (msg instanceof LastHttpContent) { ctx.pipeline().remove(this); - responseCode.complete(ctx.channel().attr(HTTP_RESPONSE).get().getStatus().code()); + responseCode.complete(ctx.channel().attr(HTTP_RESPONSE).get().status().code()); } } diff --git a/instrumentation/netty/netty-4.1/testing/src/main/java/io/opentelemetry/instrumentation/netty/v4_1/Netty41ClientExtension.java b/instrumentation/netty/netty-4.1/testing/src/main/java/io/opentelemetry/instrumentation/netty/v4_1/Netty41ClientExtension.java new file mode 100644 index 000000000000..43f9fa98d758 --- /dev/null +++ b/instrumentation/netty/netty-4.1/testing/src/main/java/io/opentelemetry/instrumentation/netty/v4_1/Netty41ClientExtension.java @@ -0,0 +1,110 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.netty.v4_1; + +import static io.opentelemetry.instrumentation.testing.junit.http.AbstractHttpClientTest.CONNECTION_TIMEOUT; +import static io.opentelemetry.instrumentation.testing.junit.http.AbstractHttpClientTest.READ_TIMEOUT; + +import io.netty.bootstrap.Bootstrap; +import io.netty.channel.Channel; +import io.netty.channel.ChannelInitializer; +import io.netty.channel.ChannelOption; +import io.netty.channel.ChannelPipeline; +import io.netty.channel.EventLoopGroup; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.SocketChannel; +import io.netty.channel.socket.nio.NioSocketChannel; +import io.netty.handler.codec.http.HttpClientCodec; +import io.netty.handler.ssl.SslContext; +import io.netty.handler.ssl.SslContextBuilder; +import io.netty.handler.timeout.ReadTimeoutHandler; +import java.net.URI; +import java.util.concurrent.TimeUnit; +import java.util.function.Consumer; +import java.util.function.Supplier; +import org.junit.jupiter.api.extension.AfterAllCallback; +import org.junit.jupiter.api.extension.BeforeAllCallback; +import org.junit.jupiter.api.extension.ExtensionContext; + +public class Netty41ClientExtension implements BeforeAllCallback, AfterAllCallback { + + private final Consumer channelPipelineConfigurer; + private final Supplier eventLoopGroupSupplier; + private final Class channelClass; + + private EventLoopGroup eventLoopGroup; + private Bootstrap httpBootstrap; + private Bootstrap httpsBootstrap; + private Bootstrap readTimeoutBootstrap; + + public Netty41ClientExtension(Consumer channelPipelineConfigurer) { + this(channelPipelineConfigurer, NioEventLoopGroup::new, NioSocketChannel.class); + } + + public Netty41ClientExtension( + Consumer channelPipelineConfigurer, + Supplier eventLoopGroupSupplier, + Class channelClass) { + this.channelPipelineConfigurer = channelPipelineConfigurer; + this.eventLoopGroupSupplier = eventLoopGroupSupplier; + this.channelClass = channelClass; + } + + @Override + public void beforeAll(ExtensionContext context) { + eventLoopGroup = eventLoopGroupSupplier.get(); + httpBootstrap = buildBootstrap(eventLoopGroup, false, false); + httpsBootstrap = buildBootstrap(eventLoopGroup, true, false); + readTimeoutBootstrap = buildBootstrap(eventLoopGroup, false, true); + } + + public Bootstrap buildBootstrap(boolean https, boolean readTimeout) { + return buildBootstrap(eventLoopGroupSupplier.get(), https, readTimeout); + } + + private Bootstrap buildBootstrap( + EventLoopGroup eventLoopGroup, boolean https, boolean readTimeout) { + Bootstrap bootstrap = new Bootstrap(); + bootstrap + .group(eventLoopGroup) + .channel(channelClass) + .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, (int) CONNECTION_TIMEOUT.toMillis()) + .handler( + new ChannelInitializer() { + @Override + protected void initChannel(SocketChannel socketChannel) throws Exception { + ChannelPipeline pipeline = socketChannel.pipeline(); + if (https) { + SslContext sslContext = SslContextBuilder.forClient().build(); + pipeline.addLast(sslContext.newHandler(socketChannel.alloc())); + } + if (readTimeout) { + pipeline.addLast( + new ReadTimeoutHandler(READ_TIMEOUT.toMillis(), TimeUnit.MILLISECONDS)); + } + pipeline.addLast(new HttpClientCodec()); + channelPipelineConfigurer.accept(pipeline); + } + }); + return bootstrap; + } + + @Override + public void afterAll(ExtensionContext context) throws InterruptedException { + if (eventLoopGroup != null) { + eventLoopGroup.shutdownGracefully().await(10, TimeUnit.SECONDS); + } + } + + public Bootstrap getBootstrap(URI uri) { + if ("https".equals(uri.getScheme())) { + return httpsBootstrap; + } else if ("/read-timeout".equals(uri.getPath())) { + return readTimeoutBootstrap; + } + return httpBootstrap; + } +} diff --git a/instrumentation/netty/netty-4.1/javaagent/src/test/groovy/SingleNettyConnection.java b/instrumentation/netty/netty-4.1/testing/src/main/java/io/opentelemetry/instrumentation/netty/v4_1/SingleNettyConnection.java similarity index 70% rename from instrumentation/netty/netty-4.1/javaagent/src/test/groovy/SingleNettyConnection.java rename to instrumentation/netty/netty-4.1/testing/src/main/java/io/opentelemetry/instrumentation/netty/v4_1/SingleNettyConnection.java index ed6d2d3c6935..d21dccde3a65 100644 --- a/instrumentation/netty/netty-4.1/javaagent/src/test/groovy/SingleNettyConnection.java +++ b/instrumentation/netty/netty-4.1/testing/src/main/java/io/opentelemetry/instrumentation/netty/v4_1/SingleNettyConnection.java @@ -3,19 +3,13 @@ * SPDX-License-Identifier: Apache-2.0 */ +package io.opentelemetry.instrumentation.netty.v4_1; + import io.netty.bootstrap.Bootstrap; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; -import io.netty.channel.ChannelInitializer; -import io.netty.channel.ChannelOption; -import io.netty.channel.ChannelPipeline; -import io.netty.channel.EventLoopGroup; -import io.netty.channel.nio.NioEventLoopGroup; -import io.netty.channel.socket.SocketChannel; -import io.netty.channel.socket.nio.NioSocketChannel; import io.netty.handler.codec.http.DefaultFullHttpRequest; -import io.netty.handler.codec.http.HttpClientCodec; import io.netty.handler.codec.http.HttpHeaderNames; import io.netty.handler.codec.http.HttpMethod; import io.netty.handler.codec.http.HttpRequest; @@ -26,6 +20,7 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; +import java.util.function.Consumer; /* Netty does not actually support proper http pipelining and has no way to correlate incoming response @@ -37,25 +32,14 @@ class is synchronised. Yes, it seems kinda pointless, but at least we test that public class SingleNettyConnection implements SingleConnection { private final String host; private final int port; + private final Consumer channelConsumer; private final Channel channel; - public SingleNettyConnection(String host, int port) { + public SingleNettyConnection( + Bootstrap bootstrap, String host, int port, Consumer channelConsumer) { this.host = host; this.port = port; - EventLoopGroup group = new NioEventLoopGroup(); - Bootstrap bootstrap = new Bootstrap(); - bootstrap - .group(group) - .channel(NioSocketChannel.class) - .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 10000) - .handler( - new ChannelInitializer() { - @Override - protected void initChannel(SocketChannel socketChannel) { - ChannelPipeline pipeline = socketChannel.pipeline(); - pipeline.addLast(new HttpClientCodec()); - } - }); + this.channelConsumer = channelConsumer; ChannelFuture channelFuture = bootstrap.connect(host, port); channelFuture.awaitUninterruptibly(); @@ -70,6 +54,7 @@ protected void initChannel(SocketChannel socketChannel) { public synchronized int doRequest(String path, Map headers) throws ExecutionException, InterruptedException, TimeoutException { CompletableFuture result = new CompletableFuture<>(); + channelConsumer.accept(channel); channel.pipeline().addLast(new ClientHandler(result)); diff --git a/instrumentation/netty/netty-common/javaagent/build.gradle.kts b/instrumentation/netty/netty-common/library/build.gradle.kts similarity index 79% rename from instrumentation/netty/netty-common/javaagent/build.gradle.kts rename to instrumentation/netty/netty-common/library/build.gradle.kts index afe601decbdc..8f6b7c537697 100644 --- a/instrumentation/netty/netty-common/javaagent/build.gradle.kts +++ b/instrumentation/netty/netty-common/library/build.gradle.kts @@ -1,5 +1,5 @@ plugins { - id("otel.javaagent-instrumentation") + id("otel.library-instrumentation") } dependencies { diff --git a/instrumentation/netty/netty-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/common/HttpClientSpanKeyAttributesExtractor.java b/instrumentation/netty/netty-common/library/src/main/java/io/opentelemetry/instrumentation/netty/common/internal/HttpClientSpanKeyAttributesExtractor.java similarity index 87% rename from instrumentation/netty/netty-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/common/HttpClientSpanKeyAttributesExtractor.java rename to instrumentation/netty/netty-common/library/src/main/java/io/opentelemetry/instrumentation/netty/common/internal/HttpClientSpanKeyAttributesExtractor.java index 24fdb4c07729..e1025af94ca8 100644 --- a/instrumentation/netty/netty-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/common/HttpClientSpanKeyAttributesExtractor.java +++ b/instrumentation/netty/netty-common/library/src/main/java/io/opentelemetry/instrumentation/netty/common/internal/HttpClientSpanKeyAttributesExtractor.java @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.javaagent.instrumentation.netty.common; +package io.opentelemetry.instrumentation.netty.common.internal; import io.opentelemetry.api.common.AttributesBuilder; import io.opentelemetry.context.Context; @@ -16,6 +16,9 @@ /** * Attributes extractor that pretends it's a {@link HttpClientAttributesExtractor} so that error * only CONNECT spans can be suppressed by higher level HTTP clients based on netty. + * + *

This class is internal and is hence not for public use. Its APIs are unstable and can change + * at any time. */ public enum HttpClientSpanKeyAttributesExtractor implements AttributesExtractor, SpanKeyProvider { diff --git a/instrumentation/netty/netty-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/common/NettyConnectionRequest.java b/instrumentation/netty/netty-common/library/src/main/java/io/opentelemetry/instrumentation/netty/common/internal/NettyConnectionRequest.java similarity index 78% rename from instrumentation/netty/netty-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/common/NettyConnectionRequest.java rename to instrumentation/netty/netty-common/library/src/main/java/io/opentelemetry/instrumentation/netty/common/internal/NettyConnectionRequest.java index 046f4741f66c..8fda9a667bcd 100644 --- a/instrumentation/netty/netty-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/common/NettyConnectionRequest.java +++ b/instrumentation/netty/netty-common/library/src/main/java/io/opentelemetry/instrumentation/netty/common/internal/NettyConnectionRequest.java @@ -3,12 +3,16 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.javaagent.instrumentation.netty.common; +package io.opentelemetry.instrumentation.netty.common.internal; import com.google.auto.value.AutoValue; import java.net.SocketAddress; import javax.annotation.Nullable; +/** + * This class is internal and is hence not for public use. Its APIs are unstable and can change at + * any time. + */ @AutoValue public abstract class NettyConnectionRequest { diff --git a/instrumentation/netty/netty-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/common/NettyErrorHolder.java b/instrumentation/netty/netty-common/library/src/main/java/io/opentelemetry/instrumentation/netty/common/internal/NettyErrorHolder.java similarity index 87% rename from instrumentation/netty/netty-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/common/NettyErrorHolder.java rename to instrumentation/netty/netty-common/library/src/main/java/io/opentelemetry/instrumentation/netty/common/internal/NettyErrorHolder.java index 9965f2447cda..ec02b9e99170 100644 --- a/instrumentation/netty/netty-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/common/NettyErrorHolder.java +++ b/instrumentation/netty/netty-common/library/src/main/java/io/opentelemetry/instrumentation/netty/common/internal/NettyErrorHolder.java @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.javaagent.instrumentation.netty.common; +package io.opentelemetry.instrumentation.netty.common.internal; import static io.opentelemetry.context.ContextKey.named; @@ -12,6 +12,10 @@ import io.opentelemetry.context.ImplicitContextKeyed; import javax.annotation.Nullable; +/** + * This class is internal and is hence not for public use. Its APIs are unstable and can change at + * any time. + */ public final class NettyErrorHolder implements ImplicitContextKeyed { private static final ContextKey KEY = named("opentelemetry-netty-error"); diff --git a/instrumentation/netty/netty-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/common/Timer.java b/instrumentation/netty/netty-common/library/src/main/java/io/opentelemetry/instrumentation/netty/common/internal/Timer.java similarity index 85% rename from instrumentation/netty/netty-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/common/Timer.java rename to instrumentation/netty/netty-common/library/src/main/java/io/opentelemetry/instrumentation/netty/common/internal/Timer.java index c570bbfc0482..0646a7d4b833 100644 --- a/instrumentation/netty/netty-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/common/Timer.java +++ b/instrumentation/netty/netty-common/library/src/main/java/io/opentelemetry/instrumentation/netty/common/internal/Timer.java @@ -3,13 +3,17 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.javaagent.instrumentation.netty.common; +package io.opentelemetry.instrumentation.netty.common.internal; import io.opentelemetry.context.Context; import io.opentelemetry.context.ContextKey; import io.opentelemetry.context.ImplicitContextKeyed; import java.time.Instant; +/** + * This class is internal and is hence not for public use. Its APIs are unstable and can change at + * any time. + */ public final class Timer implements ImplicitContextKeyed { private static final ContextKey KEY = ContextKey.named("opentelemetry-timer-key"); diff --git a/instrumentation/okhttp/okhttp-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/okhttp/v2_2/OkHttp2HttpAttributesGetter.java b/instrumentation/okhttp/okhttp-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/okhttp/v2_2/OkHttp2HttpAttributesGetter.java index 9822bb2db0a7..a4468c4791e3 100644 --- a/instrumentation/okhttp/okhttp-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/okhttp/v2_2/OkHttp2HttpAttributesGetter.java +++ b/instrumentation/okhttp/okhttp-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/okhttp/v2_2/OkHttp2HttpAttributesGetter.java @@ -30,19 +30,7 @@ public List requestHeader(Request request, String name) { } @Override - @Nullable - public Long requestContentLength(Request request, @Nullable Response response) { - return null; - } - - @Override - @Nullable - public Long requestContentLengthUncompressed(Request request, @Nullable Response response) { - return null; - } - - @Override - public Integer statusCode(Request request, Response response) { + public Integer statusCode(Request request, Response response, @Nullable Throwable error) { return response.code(); } @@ -65,17 +53,6 @@ public String flavor(Request request, @Nullable Response response) { return null; } - @Override - public Long responseContentLength(Request request, Response response) { - return response.body().contentLength(); - } - - @Override - @Nullable - public Long responseContentLengthUncompressed(Request request, Response response) { - return null; - } - @Override public List responseHeader(Request request, Response response, String name) { return response.headers(name); diff --git a/instrumentation/okhttp/okhttp-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/okhttp/v2_2/OkHttp2NetAttributesGetter.java b/instrumentation/okhttp/okhttp-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/okhttp/v2_2/OkHttp2NetAttributesGetter.java index ce2dd1e51522..851693512ed4 100644 --- a/instrumentation/okhttp/okhttp-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/okhttp/v2_2/OkHttp2NetAttributesGetter.java +++ b/instrumentation/okhttp/okhttp-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/okhttp/v2_2/OkHttp2NetAttributesGetter.java @@ -21,18 +21,12 @@ public String transport(Request request, @Nullable Response response) { @Override @Nullable - public String peerName(Request request, @Nullable Response response) { + public String peerName(Request request) { return request.url().getHost(); } @Override - public Integer peerPort(Request request, @Nullable Response response) { + public Integer peerPort(Request request) { return request.url().getPort(); } - - @Override - @Nullable - public String peerIp(Request request, @Nullable Response response) { - return null; - } } diff --git a/instrumentation/okhttp/okhttp-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/okhttp/v2_2/OkHttp2Singletons.java b/instrumentation/okhttp/okhttp-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/okhttp/v2_2/OkHttp2Singletons.java index a951f63f4a45..56f8f79c0ef7 100644 --- a/instrumentation/okhttp/okhttp-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/okhttp/v2_2/OkHttp2Singletons.java +++ b/instrumentation/okhttp/okhttp-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/okhttp/v2_2/OkHttp2Singletons.java @@ -13,12 +13,13 @@ import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.api.OpenTelemetry; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; -import io.opentelemetry.instrumentation.api.instrumenter.PeerServiceAttributesExtractor; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpClientAttributesExtractor; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpClientMetrics; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanNameExtractor; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanStatusExtractor; import io.opentelemetry.instrumentation.api.instrumenter.net.NetClientAttributesExtractor; +import io.opentelemetry.instrumentation.api.instrumenter.net.PeerServiceAttributesExtractor; +import io.opentelemetry.javaagent.bootstrap.internal.CommonConfig; public final class OkHttp2Singletons { private static final String INSTRUMENTATION_NAME = "io.opentelemetry.okhttp-2.2"; @@ -38,12 +39,17 @@ public final class OkHttp2Singletons { INSTRUMENTATION_NAME, HttpSpanNameExtractor.create(httpAttributesGetter)) .setSpanStatusExtractor(HttpSpanStatusExtractor.create(httpAttributesGetter)) - .addAttributesExtractor(HttpClientAttributesExtractor.create(httpAttributesGetter)) + .addAttributesExtractor( + HttpClientAttributesExtractor.builder(httpAttributesGetter) + .setCapturedRequestHeaders(CommonConfig.get().getClientRequestHeaders()) + .setCapturedResponseHeaders(CommonConfig.get().getClientResponseHeaders()) + .build()) .addAttributesExtractor(NetClientAttributesExtractor.create(netClientAttributesGetter)) .addAttributesExtractor( - PeerServiceAttributesExtractor.create(netClientAttributesGetter)) + PeerServiceAttributesExtractor.create( + netClientAttributesGetter, CommonConfig.get().getPeerServiceMapping())) .addOperationMetrics(HttpClientMetrics.get()) - .newInstrumenter(alwaysClient()); + .buildInstrumenter(alwaysClient()); TRACING_INTERCEPTOR = new TracingInterceptor(INSTRUMENTER, openTelemetry.getPropagators()); } diff --git a/instrumentation/okhttp/okhttp-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/okhttp/v2_2/OkHttpIgnoredTypesConfigurer.java b/instrumentation/okhttp/okhttp-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/okhttp/v2_2/OkHttpIgnoredTypesConfigurer.java index e497746f9a9f..8823c36cf44b 100644 --- a/instrumentation/okhttp/okhttp-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/okhttp/v2_2/OkHttpIgnoredTypesConfigurer.java +++ b/instrumentation/okhttp/okhttp-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/okhttp/v2_2/OkHttpIgnoredTypesConfigurer.java @@ -14,7 +14,7 @@ public class OkHttpIgnoredTypesConfigurer implements IgnoredTypesConfigurer { @Override - public void configure(ConfigProperties config, IgnoredTypesBuilder builder) { + public void configure(IgnoredTypesBuilder builder, ConfigProperties config) { // OkHttp connection pool lazily initializes a long running task to detect expired // connections and should not itself be instrumented. builder.ignoreTaskClass("com.squareup.okhttp.ConnectionPool$"); diff --git a/instrumentation/okhttp/okhttp-2.2/javaagent/src/test/groovy/HeadersUtil.groovy b/instrumentation/okhttp/okhttp-2.2/javaagent/src/test/groovy/HeadersUtil.groovy deleted file mode 100644 index 79c87450152b..000000000000 --- a/instrumentation/okhttp/okhttp-2.2/javaagent/src/test/groovy/HeadersUtil.groovy +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -class HeadersUtil { - static headersToArray(Map headers) { - String[] headersArr = new String[headers.size() * 2] - headers.eachWithIndex { k, v, i -> - headersArr[i] = k - headersArr[i + 1] = v - } - - headersArr - } -} diff --git a/instrumentation/okhttp/okhttp-2.2/javaagent/src/test/groovy/OkHttp2Test.groovy b/instrumentation/okhttp/okhttp-2.2/javaagent/src/test/groovy/OkHttp2Test.groovy deleted file mode 100644 index 14609633bad0..000000000000 --- a/instrumentation/okhttp/okhttp-2.2/javaagent/src/test/groovy/OkHttp2Test.groovy +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -import com.squareup.okhttp.Callback -import com.squareup.okhttp.MediaType -import com.squareup.okhttp.OkHttpClient -import com.squareup.okhttp.Request -import com.squareup.okhttp.RequestBody -import com.squareup.okhttp.Response -import com.squareup.okhttp.internal.http.HttpMethod -import io.opentelemetry.api.common.AttributeKey -import io.opentelemetry.instrumentation.test.AgentTestTrait -import io.opentelemetry.instrumentation.test.base.HttpClientTest -import io.opentelemetry.instrumentation.testing.junit.http.AbstractHttpClientTest -import io.opentelemetry.semconv.trace.attributes.SemanticAttributes -import spock.lang.Shared - -import java.util.concurrent.TimeUnit - -class OkHttp2Test extends HttpClientTest implements AgentTestTrait { - @Shared - def client = new OkHttpClient() - @Shared - def clientWithReadTimeout = new OkHttpClient() - - def setupSpec() { - client.setConnectTimeout(CONNECT_TIMEOUT_MS, TimeUnit.MILLISECONDS) - clientWithReadTimeout.setConnectTimeout(CONNECT_TIMEOUT_MS, TimeUnit.MILLISECONDS) - clientWithReadTimeout.setReadTimeout(READ_TIMEOUT_MS, TimeUnit.MILLISECONDS) - } - - @Override - Request buildRequest(String method, URI uri, Map headers) { - def body = HttpMethod.requiresRequestBody(method) ? RequestBody.create(MediaType.parse("text/plain"), "") : null - def request = new Request.Builder() - .url(uri.toURL()) - .method(method, body) - headers.forEach({ key, value -> request.header(key, value) }) - return request.build() - } - - @Override - int sendRequest(Request request, String method, URI uri, Map headers) { - return getClient(uri).newCall(request).execute().code() - } - - @Override - void sendRequestWithCallback(Request request, String method, URI uri, Map headers, AbstractHttpClientTest.RequestResult requestResult) { - getClient(uri).newCall(request).enqueue(new Callback() { - @Override - void onFailure(Request req, IOException e) { - requestResult.complete(e) - } - - @Override - void onResponse(Response response) throws IOException { - requestResult.complete(response.code()) - } - }) - } - - OkHttpClient getClient(URI uri) { - if (uri.toString().contains("/read-timeout")) { - return clientWithReadTimeout - } - return client - } - - @Override - Set> httpAttributes(URI uri) { - Set> extra = [ - SemanticAttributes.HTTP_HOST, - SemanticAttributes.HTTP_SCHEME - ] - def attributes = super.httpAttributes(uri) + extra - - // flavor is extracted from the response, and those URLs cause exceptions (= null response) - switch (uri.toString()) { - case "http://localhost:61/": - case "https://192.0.2.1/": - case resolveAddress("/read-timeout").toString(): - attributes.remove(SemanticAttributes.HTTP_FLAVOR) - } - - attributes - } - - @Override - boolean testRedirects() { - false - } - - @Override - boolean testReadTimeout() { - true - } -} diff --git a/instrumentation/okhttp/okhttp-2.2/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/okhttp/v2_2/OkHttp2Test.java b/instrumentation/okhttp/okhttp-2.2/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/okhttp/v2_2/OkHttp2Test.java new file mode 100644 index 000000000000..8b732419aff0 --- /dev/null +++ b/instrumentation/okhttp/okhttp-2.2/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/okhttp/v2_2/OkHttp2Test.java @@ -0,0 +1,114 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.okhttp.v2_2; + +import com.squareup.okhttp.Callback; +import com.squareup.okhttp.MediaType; +import com.squareup.okhttp.OkHttpClient; +import com.squareup.okhttp.Request; +import com.squareup.okhttp.RequestBody; +import com.squareup.okhttp.Response; +import com.squareup.okhttp.internal.http.HttpMethod; +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.http.AbstractHttpClientTest; +import io.opentelemetry.instrumentation.testing.junit.http.HttpClientInstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.http.HttpClientTestOptions; +import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; +import java.io.IOException; +import java.net.URI; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.TimeUnit; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.extension.RegisterExtension; + +public class OkHttp2Test extends AbstractHttpClientTest { + + @RegisterExtension + static final InstrumentationExtension testing = HttpClientInstrumentationExtension.forAgent(); + + private static final OkHttpClient client = new OkHttpClient(); + private static final OkHttpClient clientWithReadTimeout = new OkHttpClient(); + + @BeforeAll + void setupSpec() { + client.setConnectTimeout(CONNECTION_TIMEOUT.toMillis(), TimeUnit.MILLISECONDS); + clientWithReadTimeout.setConnectTimeout(CONNECTION_TIMEOUT.toMillis(), TimeUnit.MILLISECONDS); + clientWithReadTimeout.setReadTimeout(READ_TIMEOUT.toMillis(), TimeUnit.MILLISECONDS); + } + + @Override + public Request buildRequest(String method, URI uri, Map headers) + throws Exception { + RequestBody body = + HttpMethod.requiresRequestBody(method) + ? RequestBody.create(MediaType.parse("text/plain"), "") + : null; + Request.Builder request = new Request.Builder().url(uri.toURL()).method(method, body); + headers.forEach(request::header); + return request.build(); + } + + @Override + public int sendRequest(Request request, String method, URI uri, Map headers) + throws Exception { + return getClient(uri).newCall(request).execute().code(); + } + + @Override + public void sendRequestWithCallback( + Request request, + String method, + URI uri, + Map headers, + AbstractHttpClientTest.RequestResult requestResult) { + getClient(uri) + .newCall(request) + .enqueue( + new Callback() { + @Override + public void onFailure(Request req, IOException e) { + requestResult.complete(e); + } + + @Override + public void onResponse(Response response) { + requestResult.complete(response.code()); + } + }); + } + + private static OkHttpClient getClient(URI uri) { + if (uri.toString().contains("/read-timeout")) { + return clientWithReadTimeout; + } + return client; + } + + @Override + protected void configure(HttpClientTestOptions options) { + options.disableTestCircularRedirects(); + options.enableTestReadTimeout(); + + options.setHttpAttributes( + uri -> { + Set> attributes = + new HashSet<>(HttpClientTestOptions.DEFAULT_HTTP_ATTRIBUTES); + + // flavor is extracted from the response, and those URLs cause exceptions (= null + // response) + if ("http://localhost:61/".equals(uri.toString()) + || "https://192.0.2.1/".equals(uri.toString()) + || resolveAddress("/read-timeout").toString().equals(uri.toString())) { + attributes.remove(SemanticAttributes.HTTP_FLAVOR); + } + + return attributes; + }); + } +} diff --git a/instrumentation/okhttp/okhttp-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/okhttp/v3_0/OkHttp3IgnoredTypesConfigurer.java b/instrumentation/okhttp/okhttp-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/okhttp/v3_0/OkHttp3IgnoredTypesConfigurer.java index e3ab733ad5ee..6198a2e84523 100644 --- a/instrumentation/okhttp/okhttp-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/okhttp/v3_0/OkHttp3IgnoredTypesConfigurer.java +++ b/instrumentation/okhttp/okhttp-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/okhttp/v3_0/OkHttp3IgnoredTypesConfigurer.java @@ -14,7 +14,7 @@ public class OkHttp3IgnoredTypesConfigurer implements IgnoredTypesConfigurer { @Override - public void configure(ConfigProperties config, IgnoredTypesBuilder builder) { + public void configure(IgnoredTypesBuilder builder, ConfigProperties config) { // OkHttp task runner is a lazily-initialized shared pool of continuously running threads // similar to an event loop. The submitted tasks themselves should already be // instrumented to allow async propagation. diff --git a/instrumentation/okhttp/okhttp-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/okhttp/v3_0/OkHttp3Singletons.java b/instrumentation/okhttp/okhttp-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/okhttp/v3_0/OkHttp3Singletons.java index 77158b065170..155f581fc2eb 100644 --- a/instrumentation/okhttp/okhttp-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/okhttp/v3_0/OkHttp3Singletons.java +++ b/instrumentation/okhttp/okhttp-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/okhttp/v3_0/OkHttp3Singletons.java @@ -6,9 +6,10 @@ package io.opentelemetry.javaagent.instrumentation.okhttp.v3_0; import io.opentelemetry.api.GlobalOpenTelemetry; -import io.opentelemetry.instrumentation.api.instrumenter.PeerServiceAttributesExtractor; +import io.opentelemetry.instrumentation.api.instrumenter.net.PeerServiceAttributesExtractor; import io.opentelemetry.instrumentation.okhttp.v3_0.OkHttpTelemetry; import io.opentelemetry.instrumentation.okhttp.v3_0.internal.OkHttpNetAttributesGetter; +import io.opentelemetry.javaagent.bootstrap.internal.CommonConfig; import okhttp3.Interceptor; /** Holder of singleton interceptors for adding to instrumented clients. */ @@ -18,7 +19,10 @@ public final class OkHttp3Singletons { public static final Interceptor TRACING_INTERCEPTOR = OkHttpTelemetry.builder(GlobalOpenTelemetry.get()) .addAttributesExtractor( - PeerServiceAttributesExtractor.create(new OkHttpNetAttributesGetter())) + PeerServiceAttributesExtractor.create( + new OkHttpNetAttributesGetter(), CommonConfig.get().getPeerServiceMapping())) + .setCapturedRequestHeaders(CommonConfig.get().getClientRequestHeaders()) + .setCapturedResponseHeaders(CommonConfig.get().getClientResponseHeaders()) .build() .newInterceptor(); diff --git a/instrumentation/okhttp/okhttp-3.0/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/okhttp/v3_0/OkHttp3Test.groovy b/instrumentation/okhttp/okhttp-3.0/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/okhttp/v3_0/OkHttp3Test.groovy deleted file mode 100644 index 6dba02b6f6d0..000000000000 --- a/instrumentation/okhttp/okhttp-3.0/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/okhttp/v3_0/OkHttp3Test.groovy +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.javaagent.instrumentation.okhttp.v3_0 - -import io.opentelemetry.instrumentation.okhttp.v3_0.AbstractOkHttp3Test -import io.opentelemetry.instrumentation.test.AgentTestTrait -import okhttp3.Call -import okhttp3.OkHttpClient - -import java.util.concurrent.TimeUnit - -class OkHttp3Test extends AbstractOkHttp3Test implements AgentTestTrait { - - @Override - Call.Factory createCallFactory(OkHttpClient.Builder clientBuilder) { - return clientBuilder.build() - } - - def "reused builder has one interceptor"() { - def builder = new OkHttpClient.Builder() - .connectTimeout(CONNECT_TIMEOUT_MS, TimeUnit.MILLISECONDS) - .retryOnConnectionFailure(false) - when: - def newClient = builder.build().newBuilder().build() - - then: - newClient.interceptors().size() == 1 - } - - def "builder created from client has one interceptor"() { - when: - def newClient = ((OkHttpClient) client).newBuilder().build() - - then: - newClient.interceptors().size() == 1 - } - -} diff --git a/instrumentation/okhttp/okhttp-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/okhttp/v3_0/OkHttp3Test.java b/instrumentation/okhttp/okhttp-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/okhttp/v3_0/OkHttp3Test.java new file mode 100644 index 000000000000..631f97703305 --- /dev/null +++ b/instrumentation/okhttp/okhttp-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/okhttp/v3_0/OkHttp3Test.java @@ -0,0 +1,48 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.okhttp.v3_0; + +import static org.assertj.core.api.Assertions.assertThat; + +import io.opentelemetry.instrumentation.okhttp.v3_0.AbstractOkHttp3Test; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.http.HttpClientInstrumentationExtension; +import java.util.concurrent.TimeUnit; +import okhttp3.Call; +import okhttp3.OkHttpClient; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +public class OkHttp3Test extends AbstractOkHttp3Test { + + @RegisterExtension + static final InstrumentationExtension testing = HttpClientInstrumentationExtension.forAgent(); + + @Override + public Call.Factory createCallFactory(OkHttpClient.Builder clientBuilder) { + return clientBuilder.build(); + } + + @Test + @SuppressWarnings("PreferJavaTimeOverload") + void reusedBuilderHasOneInterceptor() { + OkHttpClient.Builder builder = + new OkHttpClient.Builder() + .connectTimeout(CONNECTION_TIMEOUT.toMillis(), TimeUnit.MILLISECONDS) + .retryOnConnectionFailure(false); + + OkHttpClient newClient = builder.build().newBuilder().build(); + + assertThat(newClient.interceptors()).hasSize(1); + } + + @Test + void builderCreatedFromClientHasOneInterceptor() { + OkHttpClient newClient = ((OkHttpClient) client).newBuilder().build(); + + assertThat(newClient.interceptors()).hasSize(1); + } +} diff --git a/instrumentation/okhttp/okhttp-3.0/library/README.md b/instrumentation/okhttp/okhttp-3.0/library/README.md index 1ba0d28f79d2..f797974f3228 100644 --- a/instrumentation/okhttp/okhttp-3.0/library/README.md +++ b/instrumentation/okhttp/okhttp-3.0/library/README.md @@ -1,4 +1,4 @@ -# Manual Instrumentation for OkHttp3 version 3.0.0+ +# Library Instrumentation for OkHttp version 3.0 and higher Provides OpenTelemetry instrumentation for [okhttp3](https://square.github.io/okhttp/). @@ -6,13 +6,12 @@ Provides OpenTelemetry instrumentation for [okhttp3](https://square.github.io/ok ### Add these dependencies to your project: -Replace `OPENTELEMETRY_VERSION` with the latest stable -[release](https://mvnrepository.com/artifact/io.opentelemetry). `Minimum version: 1.5.0` +Replace `OPENTELEMETRY_VERSION` with the [latest +release](https://search.maven.org/search?q=g:io.opentelemetry.instrumentation%20AND%20a:opentelemetry-okhttp-3.0). For Maven, add to your `pom.xml` dependencies: ```xml - io.opentelemetry.instrumentation diff --git a/instrumentation/okhttp/okhttp-3.0/library/src/main/java/io/opentelemetry/instrumentation/okhttp/v3_0/OkHttpAttributesGetter.java b/instrumentation/okhttp/okhttp-3.0/library/src/main/java/io/opentelemetry/instrumentation/okhttp/v3_0/OkHttpAttributesGetter.java index 23ced7f7c07a..3c9fff91da13 100644 --- a/instrumentation/okhttp/okhttp-3.0/library/src/main/java/io/opentelemetry/instrumentation/okhttp/v3_0/OkHttpAttributesGetter.java +++ b/instrumentation/okhttp/okhttp-3.0/library/src/main/java/io/opentelemetry/instrumentation/okhttp/v3_0/OkHttpAttributesGetter.java @@ -30,18 +30,6 @@ public List requestHeader(Request request, String name) { return request.headers(name); } - @Override - @Nullable - public Long requestContentLength(Request request, @Nullable Response response) { - return null; - } - - @Override - @Nullable - public Long requestContentLengthUncompressed(Request request, @Nullable Response response) { - return null; - } - @Override @SuppressWarnings("UnnecessaryDefaultInEnumSwitch") @Nullable @@ -65,22 +53,10 @@ public String flavor(Request request, @Nullable Response response) { } @Override - public Integer statusCode(Request request, Response response) { + public Integer statusCode(Request request, Response response, @Nullable Throwable error) { return response.code(); } - @Override - @Nullable - public Long responseContentLength(Request request, Response response) { - return null; - } - - @Override - @Nullable - public Long responseContentLengthUncompressed(Request request, Response response) { - return null; - } - @Override public List responseHeader(Request request, Response response, String name) { return response.headers(name); diff --git a/instrumentation/okhttp/okhttp-3.0/library/src/main/java/io/opentelemetry/instrumentation/okhttp/v3_0/OkHttpTelemetry.java b/instrumentation/okhttp/okhttp-3.0/library/src/main/java/io/opentelemetry/instrumentation/okhttp/v3_0/OkHttpTelemetry.java index 7b9e99b3ba73..4d38f891fa02 100644 --- a/instrumentation/okhttp/okhttp-3.0/library/src/main/java/io/opentelemetry/instrumentation/okhttp/v3_0/OkHttpTelemetry.java +++ b/instrumentation/okhttp/okhttp-3.0/library/src/main/java/io/opentelemetry/instrumentation/okhttp/v3_0/OkHttpTelemetry.java @@ -66,7 +66,10 @@ public Interceptor newInterceptor() { * @return a {@link Call.Factory} for creating new {@link Call} instances. */ public Call.Factory newCallFactory(OkHttpClient baseClient) { - OkHttpClient tracingClient = baseClient.newBuilder().addInterceptor(newInterceptor()).build(); + OkHttpClient.Builder builder = baseClient.newBuilder(); + // add our interceptor before other interceptors + builder.interceptors().add(0, newInterceptor()); + OkHttpClient tracingClient = builder.build(); return new TracingCallFactory(tracingClient); } } diff --git a/instrumentation/okhttp/okhttp-3.0/library/src/main/java/io/opentelemetry/instrumentation/okhttp/v3_0/OkHttpTelemetryBuilder.java b/instrumentation/okhttp/okhttp-3.0/library/src/main/java/io/opentelemetry/instrumentation/okhttp/v3_0/OkHttpTelemetryBuilder.java index 73a856ae9c7d..a678c9436076 100644 --- a/instrumentation/okhttp/okhttp-3.0/library/src/main/java/io/opentelemetry/instrumentation/okhttp/v3_0/OkHttpTelemetryBuilder.java +++ b/instrumentation/okhttp/okhttp-3.0/library/src/main/java/io/opentelemetry/instrumentation/okhttp/v3_0/OkHttpTelemetryBuilder.java @@ -7,6 +7,7 @@ import static io.opentelemetry.instrumentation.api.instrumenter.SpanKindExtractor.alwaysClient; +import com.google.errorprone.annotations.CanIgnoreReturnValue; import io.opentelemetry.api.OpenTelemetry; import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; @@ -41,6 +42,7 @@ public final class OkHttpTelemetryBuilder { * Adds an additional {@link AttributesExtractor} to invoke to set attributes to instrumented * items. */ + @CanIgnoreReturnValue public OkHttpTelemetryBuilder addAttributesExtractor( AttributesExtractor attributesExtractor) { additionalExtractors.add(attributesExtractor); @@ -52,6 +54,7 @@ public OkHttpTelemetryBuilder addAttributesExtractor( * * @param requestHeaders A list of HTTP header names. */ + @CanIgnoreReturnValue public OkHttpTelemetryBuilder setCapturedRequestHeaders(List requestHeaders) { httpAttributesExtractorBuilder.setCapturedRequestHeaders(requestHeaders); return this; @@ -62,6 +65,7 @@ public OkHttpTelemetryBuilder setCapturedRequestHeaders(List requestHead * * @param responseHeaders A list of HTTP header names. */ + @CanIgnoreReturnValue public OkHttpTelemetryBuilder setCapturedResponseHeaders(List responseHeaders) { httpAttributesExtractorBuilder.setCapturedResponseHeaders(responseHeaders); return this; @@ -84,7 +88,7 @@ public OkHttpTelemetry build() { .addAttributesExtractor(NetClientAttributesExtractor.create(attributesGetter)) .addAttributesExtractors(additionalExtractors) .addOperationMetrics(HttpClientMetrics.get()) - .newInstrumenter(alwaysClient()); + .buildInstrumenter(alwaysClient()); return new OkHttpTelemetry(instrumenter, openTelemetry.getPropagators()); } } diff --git a/instrumentation/okhttp/okhttp-3.0/library/src/main/java/io/opentelemetry/instrumentation/okhttp/v3_0/internal/OkHttpNetAttributesGetter.java b/instrumentation/okhttp/okhttp-3.0/library/src/main/java/io/opentelemetry/instrumentation/okhttp/v3_0/internal/OkHttpNetAttributesGetter.java index 28904beaac2d..49cb058b3ad3 100644 --- a/instrumentation/okhttp/okhttp-3.0/library/src/main/java/io/opentelemetry/instrumentation/okhttp/v3_0/internal/OkHttpNetAttributesGetter.java +++ b/instrumentation/okhttp/okhttp-3.0/library/src/main/java/io/opentelemetry/instrumentation/okhttp/v3_0/internal/OkHttpNetAttributesGetter.java @@ -25,18 +25,12 @@ public String transport(Request request, @Nullable Response response) { @Override @Nullable - public String peerName(Request request, @Nullable Response response) { + public String peerName(Request request) { return request.url().host(); } @Override - public Integer peerPort(Request request, @Nullable Response response) { + public Integer peerPort(Request request) { return request.url().port(); } - - @Override - @Nullable - public String peerIp(Request request, @Nullable Response response) { - return null; - } } diff --git a/instrumentation/okhttp/okhttp-3.0/library/src/test/groovy/io/opentelemetry/instrumentation/okhttp/v3_0/OkHttp3Test.groovy b/instrumentation/okhttp/okhttp-3.0/library/src/test/groovy/io/opentelemetry/instrumentation/okhttp/v3_0/OkHttp3Test.groovy deleted file mode 100644 index 8b1ea121200c..000000000000 --- a/instrumentation/okhttp/okhttp-3.0/library/src/test/groovy/io/opentelemetry/instrumentation/okhttp/v3_0/OkHttp3Test.groovy +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.instrumentation.okhttp.v3_0 - - -import io.opentelemetry.instrumentation.test.LibraryTestTrait -import okhttp3.Call -import okhttp3.OkHttpClient - -class OkHttp3Test extends AbstractOkHttp3Test implements LibraryTestTrait { - - @Override - Call.Factory createCallFactory(OkHttpClient.Builder clientBuilder) { - return OkHttpTelemetry.create(getOpenTelemetry()).newCallFactory(clientBuilder.build()) - } -} diff --git a/instrumentation/okhttp/okhttp-3.0/library/src/test/java/io/opentelemetry/instrumentation/okhttp/v3_0/OkHttp3Test.java b/instrumentation/okhttp/okhttp-3.0/library/src/test/java/io/opentelemetry/instrumentation/okhttp/v3_0/OkHttp3Test.java new file mode 100644 index 000000000000..4a7db5574401 --- /dev/null +++ b/instrumentation/okhttp/okhttp-3.0/library/src/test/java/io/opentelemetry/instrumentation/okhttp/v3_0/OkHttp3Test.java @@ -0,0 +1,23 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.okhttp.v3_0; + +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.http.HttpClientInstrumentationExtension; +import okhttp3.Call; +import okhttp3.OkHttpClient; +import org.junit.jupiter.api.extension.RegisterExtension; + +public class OkHttp3Test extends AbstractOkHttp3Test { + + @RegisterExtension + static final InstrumentationExtension testing = HttpClientInstrumentationExtension.forLibrary(); + + @Override + public Call.Factory createCallFactory(OkHttpClient.Builder clientBuilder) { + return OkHttpTelemetry.create(testing.getOpenTelemetry()).newCallFactory(clientBuilder.build()); + } +} diff --git a/instrumentation/okhttp/okhttp-3.0/testing/src/main/groovy/io/opentelemetry/instrumentation/okhttp/v3_0/AbstractOkHttp3Test.groovy b/instrumentation/okhttp/okhttp-3.0/testing/src/main/groovy/io/opentelemetry/instrumentation/okhttp/v3_0/AbstractOkHttp3Test.groovy deleted file mode 100644 index 05219fdf586c..000000000000 --- a/instrumentation/okhttp/okhttp-3.0/testing/src/main/groovy/io/opentelemetry/instrumentation/okhttp/v3_0/AbstractOkHttp3Test.groovy +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.instrumentation.okhttp.v3_0 - -import io.opentelemetry.api.common.AttributeKey -import io.opentelemetry.instrumentation.test.base.HttpClientTest -import io.opentelemetry.instrumentation.testing.junit.http.AbstractHttpClientTest -import io.opentelemetry.semconv.trace.attributes.SemanticAttributes -import okhttp3.Call -import okhttp3.Callback -import okhttp3.Headers -import okhttp3.MediaType -import okhttp3.OkHttpClient -import okhttp3.Protocol -import okhttp3.Request -import okhttp3.RequestBody -import okhttp3.Response -import okhttp3.internal.http.HttpMethod -import spock.lang.Shared - -import java.util.concurrent.TimeUnit - -abstract class AbstractOkHttp3Test extends HttpClientTest { - - abstract Call.Factory createCallFactory(OkHttpClient.Builder clientBuilder) - - @Shared - Call.Factory client = createCallFactory(getClientBuilder(false)) - @Shared - Call.Factory clientWithReadTimeout = createCallFactory(getClientBuilder(true)) - - OkHttpClient.Builder getClientBuilder(boolean withReadTimeout) { - OkHttpClient.Builder builder = new OkHttpClient.Builder() - .connectTimeout(CONNECT_TIMEOUT_MS, TimeUnit.MILLISECONDS) - .protocols(Arrays.asList(Protocol.HTTP_1_1)) - .retryOnConnectionFailure(false) - if (withReadTimeout) { - builder.readTimeout(READ_TIMEOUT_MS, TimeUnit.MILLISECONDS) - } - return builder - } - - @Override - Request buildRequest(String method, URI uri, Map headers) { - def body = HttpMethod.requiresRequestBody(method) ? RequestBody.create(MediaType.parse("text/plain"), "") : null - return new Request.Builder() - .url(uri.toURL()) - .method(method, body) - .headers(Headers.of(headers)).build() - } - - @Override - int sendRequest(Request request, String method, URI uri, Map headers) { - def response = getClient(uri).newCall(request).execute() - response.body().withCloseable { - return response.code() - } - } - - @Override - void sendRequestWithCallback(Request request, String method, URI uri, Map headers, AbstractHttpClientTest.RequestResult requestResult) { - getClient(uri).newCall(request).enqueue(new Callback() { - @Override - void onFailure(Call call, IOException e) { - requestResult.complete(e) - } - - @Override - void onResponse(Call call, Response response) throws IOException { - response.body().withCloseable { - requestResult.complete(response.code()) - } - } - }) - } - - Call.Factory getClient(URI uri) { - if (uri.toString().contains("/read-timeout")) { - return clientWithReadTimeout - } - return client - } - - @Override - boolean testCircularRedirects() { - false - } - - @Override - boolean testReadTimeout() { - true - } - - @Override - Set> httpAttributes(URI uri) { - Set> extra = [ - SemanticAttributes.HTTP_HOST, - SemanticAttributes.HTTP_SCHEME - ] - def attributes = super.httpAttributes(uri) + extra - - // flavor is extracted from the response, and those URLs cause exceptions (= null response) - switch (uri.toString()) { - case "http://localhost:61/": - case "https://192.0.2.1/": - case resolveAddress("/read-timeout").toString(): - attributes.remove(SemanticAttributes.HTTP_FLAVOR) - } - - attributes - } -} diff --git a/instrumentation/okhttp/okhttp-3.0/testing/src/main/java/io/opentelemetry/instrumentation/okhttp/v3_0/AbstractOkHttp3Test.java b/instrumentation/okhttp/okhttp-3.0/testing/src/main/java/io/opentelemetry/instrumentation/okhttp/v3_0/AbstractOkHttp3Test.java new file mode 100644 index 000000000000..3f81a93fe017 --- /dev/null +++ b/instrumentation/okhttp/okhttp-3.0/testing/src/main/java/io/opentelemetry/instrumentation/okhttp/v3_0/AbstractOkHttp3Test.java @@ -0,0 +1,190 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.okhttp.v3_0; + +import static java.util.Collections.singletonList; +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.http.AbstractHttpClientTest; +import io.opentelemetry.instrumentation.testing.junit.http.HttpClientTestOptions; +import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; +import java.io.IOException; +import java.net.URI; +import java.util.Collections; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.TimeUnit; +import okhttp3.Call; +import okhttp3.Callback; +import okhttp3.Headers; +import okhttp3.Interceptor; +import okhttp3.MediaType; +import okhttp3.OkHttpClient; +import okhttp3.Protocol; +import okhttp3.Request; +import okhttp3.RequestBody; +import okhttp3.Response; +import okhttp3.ResponseBody; +import okhttp3.internal.http.HttpMethod; +import org.junit.jupiter.api.Test; + +public abstract class AbstractOkHttp3Test extends AbstractHttpClientTest { + + protected abstract Call.Factory createCallFactory(OkHttpClient.Builder clientBuilder); + + protected final Call.Factory client = createCallFactory(getClientBuilder(false)); + private final Call.Factory clientWithReadTimeout = createCallFactory(getClientBuilder(true)); + + OkHttpClient.Builder getClientBuilder(boolean withReadTimeout) { + OkHttpClient.Builder builder = + new OkHttpClient.Builder() + .connectTimeout(CONNECTION_TIMEOUT.toMillis(), TimeUnit.MILLISECONDS) + .protocols(singletonList(Protocol.HTTP_1_1)) + .retryOnConnectionFailure(false); + if (withReadTimeout) { + builder.readTimeout(READ_TIMEOUT.toMillis(), TimeUnit.MILLISECONDS); + } + return builder; + } + + @Override + protected Request buildRequest(String method, URI uri, Map headers) + throws Exception { + RequestBody body = + HttpMethod.requiresRequestBody(method) + ? RequestBody.create(MediaType.parse("text/plain"), "") + : null; + return new Request.Builder() + .url(uri.toURL()) + .method(method, body) + .headers(Headers.of(headers)) + .build(); + } + + @Override + protected int sendRequest(Request request, String method, URI uri, Map headers) + throws Exception { + Response response = getClient(uri).newCall(request).execute(); + try (ResponseBody ignored = response.body()) { + return response.code(); + } + } + + @Override + protected void sendRequestWithCallback( + Request request, + String method, + URI uri, + Map headers, + AbstractHttpClientTest.RequestResult requestResult) { + getClient(uri) + .newCall(request) + .enqueue( + new Callback() { + @Override + public void onFailure(Call call, IOException e) { + requestResult.complete(e); + } + + @Override + public void onResponse(Call call, Response response) { + try (ResponseBody ignored = response.body()) { + requestResult.complete(response.code()); + } + } + }); + } + + private Call.Factory getClient(URI uri) { + if (uri.toString().contains("/read-timeout")) { + return clientWithReadTimeout; + } + return client; + } + + @Override + protected void configure(HttpClientTestOptions options) { + options.disableTestCircularRedirects(); + options.enableTestReadTimeout(); + + options.setHttpAttributes( + uri -> { + Set> attributes = + new HashSet<>(HttpClientTestOptions.DEFAULT_HTTP_ATTRIBUTES); + + // flavor is extracted from the response, and those URLs cause exceptions (= null + // response) + if ("http://localhost:61/".equals(uri.toString()) + || "https://192.0.2.1/".equals(uri.toString()) + || resolveAddress("/read-timeout").toString().equals(uri.toString())) { + attributes.remove(SemanticAttributes.HTTP_FLAVOR); + } + + return attributes; + }); + } + + private static class TestInterceptor implements Interceptor { + + @Override + public Response intercept(Chain chain) throws IOException { + // make copy of the request + Request request = chain.request().newBuilder().build(); + + return chain.proceed(request); + } + } + + @Test + void requestWithCustomInterceptor() throws Throwable { + String method = "GET"; + URI uri = resolveAddress("/success"); + + RequestResult result = new RequestResult(() -> testing.runWithSpan("child", () -> {})); + OkHttpClient.Builder builder = getClientBuilder(false); + builder.addInterceptor(new TestInterceptor()); + Call.Factory testClient = createCallFactory(builder); + + testing.runWithSpan( + "parent", + () -> { + Request request = buildRequest(method, uri, Collections.emptyMap()); + testClient + .newCall(request) + .enqueue( + new Callback() { + @Override + public void onFailure(Call call, IOException e) { + result.complete(e); + } + + @Override + public void onResponse(Call call, Response response) { + try (ResponseBody ignored = response.body()) { + result.complete(response.code()); + } + } + }); + }); + + assertThat(result.get()).isEqualTo(200); + + testing.waitAndAssertTraces( + trace -> { + trace.hasSpansSatisfyingExactly( + span -> span.hasName("parent").hasKind(SpanKind.INTERNAL).hasNoParent(), + span -> span.hasName("HTTP GET").hasKind(SpanKind.CLIENT).hasParent(trace.getSpan(0)), + span -> + span.hasName("test-http-server") + .hasKind(SpanKind.SERVER) + .hasParent(trace.getSpan(1)), + span -> span.hasName("child").hasKind(SpanKind.INTERNAL).hasParent(trace.getSpan(0))); + }); + } +} diff --git a/instrumentation/opentelemetry-api/opentelemetry-api-1.0/javaagent/build.gradle.kts b/instrumentation/opentelemetry-api/opentelemetry-api-1.0/javaagent/build.gradle.kts index 5dc44437324c..c686848ad94c 100644 --- a/instrumentation/opentelemetry-api/opentelemetry-api-1.0/javaagent/build.gradle.kts +++ b/instrumentation/opentelemetry-api/opentelemetry-api-1.0/javaagent/build.gradle.kts @@ -40,6 +40,5 @@ dependencies { testImplementation(project(":instrumentation-api-semconv")) // @WithSpan annotation is used to generate spans in ContextBridgeTest - testImplementation("io.opentelemetry:opentelemetry-extension-annotations") - testInstrumentation(project(":instrumentation:opentelemetry-extension-annotations-1.0:javaagent")) + testImplementation(project(":instrumentation-annotations")) } diff --git a/instrumentation/opentelemetry-api/opentelemetry-api-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/trace/ApplicationSpan.java b/instrumentation/opentelemetry-api/opentelemetry-api-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/trace/ApplicationSpan.java index efff38b9de7e..5897c6dc2537 100644 --- a/instrumentation/opentelemetry-api/opentelemetry-api-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/trace/ApplicationSpan.java +++ b/instrumentation/opentelemetry-api/opentelemetry-api-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/trace/ApplicationSpan.java @@ -15,6 +15,7 @@ import application.io.opentelemetry.api.trace.SpanKind; import application.io.opentelemetry.api.trace.StatusCode; import application.io.opentelemetry.context.Context; +import com.google.errorprone.annotations.CanIgnoreReturnValue; import io.opentelemetry.javaagent.instrumentation.opentelemetryapi.context.AgentContextStorage; import java.util.concurrent.TimeUnit; import javax.annotation.Nullable; @@ -32,30 +33,35 @@ io.opentelemetry.api.trace.Span getAgentSpan() { } @Override + @CanIgnoreReturnValue public Span setAttribute(String key, String value) { agentSpan.setAttribute(key, value); return this; } @Override + @CanIgnoreReturnValue public Span setAttribute(String key, long value) { agentSpan.setAttribute(key, value); return this; } @Override + @CanIgnoreReturnValue public Span setAttribute(String key, double value) { agentSpan.setAttribute(key, value); return this; } @Override + @CanIgnoreReturnValue public Span setAttribute(String key, boolean value) { agentSpan.setAttribute(key, value); return this; } @Override + @CanIgnoreReturnValue public Span setAttribute(AttributeKey applicationKey, T value) { @SuppressWarnings("unchecked") io.opentelemetry.api.common.AttributeKey agentKey = Bridging.toAgent(applicationKey); @@ -66,24 +72,28 @@ public Span setAttribute(AttributeKey applicationKey, T value) { } @Override + @CanIgnoreReturnValue public Span addEvent(String name) { agentSpan.addEvent(name); return this; } @Override + @CanIgnoreReturnValue public Span addEvent(String name, long timestamp, TimeUnit unit) { agentSpan.addEvent(name, timestamp, unit); return this; } @Override + @CanIgnoreReturnValue public Span addEvent(String name, Attributes applicationAttributes) { agentSpan.addEvent(name, Bridging.toAgent(applicationAttributes)); return this; } @Override + @CanIgnoreReturnValue public Span addEvent( String name, Attributes applicationAttributes, long timestamp, TimeUnit unit) { agentSpan.addEvent(name, Bridging.toAgent(applicationAttributes), timestamp, unit); @@ -91,30 +101,35 @@ public Span addEvent( } @Override + @CanIgnoreReturnValue public Span setStatus(StatusCode status) { agentSpan.setStatus(Bridging.toAgent(status)); return this; } @Override + @CanIgnoreReturnValue public Span setStatus(StatusCode status, String description) { agentSpan.setStatus(Bridging.toAgent(status), description); return this; } @Override + @CanIgnoreReturnValue public Span recordException(Throwable throwable) { agentSpan.recordException(throwable); return this; } @Override + @CanIgnoreReturnValue public Span recordException(Throwable throwable, Attributes attributes) { agentSpan.recordException(throwable, Bridging.toAgent(attributes)); return this; } @Override + @CanIgnoreReturnValue public Span updateName(String name) { agentSpan.updateName(name); return this; @@ -171,24 +186,28 @@ static class Builder implements SpanBuilder { } @Override + @CanIgnoreReturnValue public SpanBuilder setParent(Context applicationContext) { agentBuilder.setParent(AgentContextStorage.getAgentContext(applicationContext)); return this; } @Override + @CanIgnoreReturnValue public SpanBuilder setNoParent() { agentBuilder.setNoParent(); return this; } @Override + @CanIgnoreReturnValue public SpanBuilder addLink(SpanContext applicationSpanContext) { agentBuilder.addLink(Bridging.toAgent(applicationSpanContext)); return this; } @Override + @CanIgnoreReturnValue public SpanBuilder addLink( SpanContext applicationSpanContext, Attributes applicationAttributes) { agentBuilder.addLink(Bridging.toAgent(applicationSpanContext)); @@ -196,30 +215,35 @@ public SpanBuilder addLink( } @Override + @CanIgnoreReturnValue public SpanBuilder setAttribute(String key, String value) { agentBuilder.setAttribute(key, value); return this; } @Override + @CanIgnoreReturnValue public SpanBuilder setAttribute(String key, long value) { agentBuilder.setAttribute(key, value); return this; } @Override + @CanIgnoreReturnValue public SpanBuilder setAttribute(String key, double value) { agentBuilder.setAttribute(key, value); return this; } @Override + @CanIgnoreReturnValue public SpanBuilder setAttribute(String key, boolean value) { agentBuilder.setAttribute(key, value); return this; } @Override + @CanIgnoreReturnValue public SpanBuilder setAttribute(AttributeKey applicationKey, T value) { @SuppressWarnings("unchecked") io.opentelemetry.api.common.AttributeKey agentKey = Bridging.toAgent(applicationKey); @@ -230,6 +254,7 @@ public SpanBuilder setAttribute(AttributeKey applicationKey, T value) { } @Override + @CanIgnoreReturnValue public SpanBuilder setSpanKind(SpanKind applicationSpanKind) { io.opentelemetry.api.trace.SpanKind agentSpanKind = toAgentOrNull(applicationSpanKind); if (agentSpanKind != null) { @@ -239,6 +264,7 @@ public SpanBuilder setSpanKind(SpanKind applicationSpanKind) { } @Override + @CanIgnoreReturnValue public SpanBuilder setStartTimestamp(long startTimestamp, TimeUnit unit) { agentBuilder.setStartTimestamp(startTimestamp, unit); return this; diff --git a/instrumentation/opentelemetry-api/opentelemetry-api-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/trace/Bridging.java b/instrumentation/opentelemetry-api/opentelemetry-api-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/trace/Bridging.java index 25d6e7116519..b4373ccc9313 100644 --- a/instrumentation/opentelemetry-api/opentelemetry-api-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/trace/Bridging.java +++ b/instrumentation/opentelemetry-api/opentelemetry-api-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/trace/Bridging.java @@ -160,4 +160,6 @@ private static io.opentelemetry.api.trace.TraceState toAgent(TraceState applicat applicationTraceState.forEach(agentTraceState::put); return agentTraceState.build(); } + + private Bridging() {} } diff --git a/instrumentation/opentelemetry-api/opentelemetry-api-1.0/javaagent/src/test/groovy/ContextBridgeTest.groovy b/instrumentation/opentelemetry-api/opentelemetry-api-1.0/javaagent/src/test/groovy/ContextBridgeTest.groovy index 50fa58f252ff..b4194399fb75 100644 --- a/instrumentation/opentelemetry-api/opentelemetry-api-1.0/javaagent/src/test/groovy/ContextBridgeTest.groovy +++ b/instrumentation/opentelemetry-api/opentelemetry-api-1.0/javaagent/src/test/groovy/ContextBridgeTest.groovy @@ -8,8 +8,9 @@ import io.opentelemetry.api.baggage.Baggage import io.opentelemetry.api.trace.Span import io.opentelemetry.context.Context import io.opentelemetry.context.ContextKey -import io.opentelemetry.extension.annotations.WithSpan +import io.opentelemetry.instrumentation.annotations.WithSpan import io.opentelemetry.instrumentation.test.AgentInstrumentationSpecification +import io.opentelemetry.semconv.trace.attributes.SemanticAttributes import java.util.concurrent.CountDownLatch import java.util.concurrent.Executors @@ -34,8 +35,8 @@ class ContextBridgeTest extends AgentInstrumentationSpecification { } def "application propagates agent's context"() { - when: - new Runnable() { + given: + def runnable = new Runnable() { @WithSpan("test") @Override void run() { @@ -49,7 +50,10 @@ class ContextBridgeTest extends AgentInstrumentationSpecification { } } } - }.run() + } + + when: + runnable.run() then: assertTraces(1) { @@ -58,6 +62,8 @@ class ContextBridgeTest extends AgentInstrumentationSpecification { name "test" hasNoParent() attributes { + "$SemanticAttributes.CODE_NAMESPACE" runnable.class.name + "$SemanticAttributes.CODE_FUNCTION" "run" "cat" "yes" } } @@ -92,8 +98,8 @@ class ContextBridgeTest extends AgentInstrumentationSpecification { } def "application propagates agent's span"() { - when: - new Runnable() { + given: + def runnable = new Runnable() { @WithSpan("test") @Override void run() { @@ -107,7 +113,10 @@ class ContextBridgeTest extends AgentInstrumentationSpecification { } } } - }.run() + } + + when: + runnable.run() then: assertTraces(1) { @@ -116,6 +125,8 @@ class ContextBridgeTest extends AgentInstrumentationSpecification { name "test" hasNoParent() attributes { + "$SemanticAttributes.CODE_NAMESPACE" runnable.class.name + "$SemanticAttributes.CODE_FUNCTION" "run" "cat" "yes" } } diff --git a/instrumentation/opentelemetry-api/opentelemetry-api-1.10/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_10/OpenTelemetryInstrumentation.java b/instrumentation/opentelemetry-api/opentelemetry-api-1.10/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_10/OpenTelemetryInstrumentation.java index 6c0d9218d44f..091fd9a43b49 100644 --- a/instrumentation/opentelemetry-api/opentelemetry-api-1.10/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_10/OpenTelemetryInstrumentation.java +++ b/instrumentation/opentelemetry-api/opentelemetry-api-1.10/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_10/OpenTelemetryInstrumentation.java @@ -27,7 +27,7 @@ public void transform(TypeTransformer transformer) { none(), OpenTelemetryInstrumentation.class.getName() + "$InitAdvice"); } - @SuppressWarnings({"unused", "ReturnValueIgnored"}) + @SuppressWarnings({"ReturnValueIgnored", "unused"}) public static class InitAdvice { @Advice.OnMethodEnter public static void init() { diff --git a/instrumentation/opentelemetry-api/opentelemetry-api-1.10/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_10/metrics/ApplicationDoubleCounterBuilder.java b/instrumentation/opentelemetry-api/opentelemetry-api-1.10/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_10/metrics/ApplicationDoubleCounterBuilder.java index 91ba87acd6c4..01a50ed0014c 100644 --- a/instrumentation/opentelemetry-api/opentelemetry-api-1.10/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_10/metrics/ApplicationDoubleCounterBuilder.java +++ b/instrumentation/opentelemetry-api/opentelemetry-api-1.10/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_10/metrics/ApplicationDoubleCounterBuilder.java @@ -9,6 +9,7 @@ import application.io.opentelemetry.api.metrics.DoubleCounterBuilder; import application.io.opentelemetry.api.metrics.ObservableDoubleCounter; import application.io.opentelemetry.api.metrics.ObservableDoubleMeasurement; +import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.util.function.Consumer; final class ApplicationDoubleCounterBuilder implements DoubleCounterBuilder { @@ -20,12 +21,14 @@ final class ApplicationDoubleCounterBuilder implements DoubleCounterBuilder { } @Override + @CanIgnoreReturnValue public DoubleCounterBuilder setDescription(String description) { agentBuilder.setDescription(description); return this; } @Override + @CanIgnoreReturnValue public DoubleCounterBuilder setUnit(String unit) { agentBuilder.setUnit(unit); return this; diff --git a/instrumentation/opentelemetry-api/opentelemetry-api-1.10/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_10/metrics/ApplicationDoubleGaugeBuilder.java b/instrumentation/opentelemetry-api/opentelemetry-api-1.10/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_10/metrics/ApplicationDoubleGaugeBuilder.java index ad5d11f3d99a..b105abcc56e5 100644 --- a/instrumentation/opentelemetry-api/opentelemetry-api-1.10/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_10/metrics/ApplicationDoubleGaugeBuilder.java +++ b/instrumentation/opentelemetry-api/opentelemetry-api-1.10/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_10/metrics/ApplicationDoubleGaugeBuilder.java @@ -9,6 +9,7 @@ import application.io.opentelemetry.api.metrics.LongGaugeBuilder; import application.io.opentelemetry.api.metrics.ObservableDoubleGauge; import application.io.opentelemetry.api.metrics.ObservableDoubleMeasurement; +import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.util.function.Consumer; final class ApplicationDoubleGaugeBuilder implements DoubleGaugeBuilder { @@ -20,12 +21,14 @@ final class ApplicationDoubleGaugeBuilder implements DoubleGaugeBuilder { } @Override + @CanIgnoreReturnValue public DoubleGaugeBuilder setDescription(String description) { agentBuilder.setDescription(description); return this; } @Override + @CanIgnoreReturnValue public DoubleGaugeBuilder setUnit(String unit) { agentBuilder.setUnit(unit); return this; diff --git a/instrumentation/opentelemetry-api/opentelemetry-api-1.10/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_10/metrics/ApplicationDoubleHistogramBuilder.java b/instrumentation/opentelemetry-api/opentelemetry-api-1.10/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_10/metrics/ApplicationDoubleHistogramBuilder.java index 2465b03c5034..41a228acfc1b 100644 --- a/instrumentation/opentelemetry-api/opentelemetry-api-1.10/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_10/metrics/ApplicationDoubleHistogramBuilder.java +++ b/instrumentation/opentelemetry-api/opentelemetry-api-1.10/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_10/metrics/ApplicationDoubleHistogramBuilder.java @@ -8,6 +8,7 @@ import application.io.opentelemetry.api.metrics.DoubleHistogram; import application.io.opentelemetry.api.metrics.DoubleHistogramBuilder; import application.io.opentelemetry.api.metrics.LongHistogramBuilder; +import com.google.errorprone.annotations.CanIgnoreReturnValue; final class ApplicationDoubleHistogramBuilder implements DoubleHistogramBuilder { @@ -19,12 +20,14 @@ final class ApplicationDoubleHistogramBuilder implements DoubleHistogramBuilder } @Override + @CanIgnoreReturnValue public DoubleHistogramBuilder setDescription(String description) { agentBuilder.setDescription(description); return this; } @Override + @CanIgnoreReturnValue public DoubleHistogramBuilder setUnit(String unit) { agentBuilder.setUnit(unit); return this; diff --git a/instrumentation/opentelemetry-api/opentelemetry-api-1.10/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_10/metrics/ApplicationDoubleUpDownCounterBuilder.java b/instrumentation/opentelemetry-api/opentelemetry-api-1.10/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_10/metrics/ApplicationDoubleUpDownCounterBuilder.java index 20bccbb82db0..e3692f0b1b81 100644 --- a/instrumentation/opentelemetry-api/opentelemetry-api-1.10/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_10/metrics/ApplicationDoubleUpDownCounterBuilder.java +++ b/instrumentation/opentelemetry-api/opentelemetry-api-1.10/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_10/metrics/ApplicationDoubleUpDownCounterBuilder.java @@ -9,6 +9,7 @@ import application.io.opentelemetry.api.metrics.DoubleUpDownCounterBuilder; import application.io.opentelemetry.api.metrics.ObservableDoubleMeasurement; import application.io.opentelemetry.api.metrics.ObservableDoubleUpDownCounter; +import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.util.function.Consumer; final class ApplicationDoubleUpDownCounterBuilder implements DoubleUpDownCounterBuilder { @@ -21,12 +22,14 @@ final class ApplicationDoubleUpDownCounterBuilder implements DoubleUpDownCounter } @Override + @CanIgnoreReturnValue public DoubleUpDownCounterBuilder setDescription(String description) { agentBuilder.setDescription(description); return this; } @Override + @CanIgnoreReturnValue public DoubleUpDownCounterBuilder setUnit(String unit) { agentBuilder.setUnit(unit); return this; diff --git a/instrumentation/opentelemetry-api/opentelemetry-api-1.10/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_10/metrics/ApplicationLongCounterBuilder.java b/instrumentation/opentelemetry-api/opentelemetry-api-1.10/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_10/metrics/ApplicationLongCounterBuilder.java index 7ef1924d117f..9556f984dc7e 100644 --- a/instrumentation/opentelemetry-api/opentelemetry-api-1.10/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_10/metrics/ApplicationLongCounterBuilder.java +++ b/instrumentation/opentelemetry-api/opentelemetry-api-1.10/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_10/metrics/ApplicationLongCounterBuilder.java @@ -10,6 +10,7 @@ import application.io.opentelemetry.api.metrics.LongCounterBuilder; import application.io.opentelemetry.api.metrics.ObservableLongCounter; import application.io.opentelemetry.api.metrics.ObservableLongMeasurement; +import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.util.function.Consumer; final class ApplicationLongCounterBuilder implements LongCounterBuilder { @@ -21,12 +22,14 @@ final class ApplicationLongCounterBuilder implements LongCounterBuilder { } @Override + @CanIgnoreReturnValue public LongCounterBuilder setDescription(String description) { agentBuilder.setDescription(description); return this; } @Override + @CanIgnoreReturnValue public LongCounterBuilder setUnit(String unit) { agentBuilder.setUnit(unit); return this; diff --git a/instrumentation/opentelemetry-api/opentelemetry-api-1.10/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_10/metrics/ApplicationLongGaugeBuilder.java b/instrumentation/opentelemetry-api/opentelemetry-api-1.10/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_10/metrics/ApplicationLongGaugeBuilder.java index b9fc963498c8..f26c8ffb6576 100644 --- a/instrumentation/opentelemetry-api/opentelemetry-api-1.10/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_10/metrics/ApplicationLongGaugeBuilder.java +++ b/instrumentation/opentelemetry-api/opentelemetry-api-1.10/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_10/metrics/ApplicationLongGaugeBuilder.java @@ -8,6 +8,7 @@ import application.io.opentelemetry.api.metrics.LongGaugeBuilder; import application.io.opentelemetry.api.metrics.ObservableLongGauge; import application.io.opentelemetry.api.metrics.ObservableLongMeasurement; +import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.util.function.Consumer; final class ApplicationLongGaugeBuilder implements LongGaugeBuilder { @@ -19,12 +20,14 @@ final class ApplicationLongGaugeBuilder implements LongGaugeBuilder { } @Override + @CanIgnoreReturnValue public LongGaugeBuilder setDescription(String description) { agentBuilder.setDescription(description); return this; } @Override + @CanIgnoreReturnValue public LongGaugeBuilder setUnit(String unit) { agentBuilder.setUnit(unit); return this; diff --git a/instrumentation/opentelemetry-api/opentelemetry-api-1.10/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_10/metrics/ApplicationLongHistogramBuilder.java b/instrumentation/opentelemetry-api/opentelemetry-api-1.10/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_10/metrics/ApplicationLongHistogramBuilder.java index fcaf49121093..3ddd92ef53b6 100644 --- a/instrumentation/opentelemetry-api/opentelemetry-api-1.10/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_10/metrics/ApplicationLongHistogramBuilder.java +++ b/instrumentation/opentelemetry-api/opentelemetry-api-1.10/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_10/metrics/ApplicationLongHistogramBuilder.java @@ -7,6 +7,7 @@ import application.io.opentelemetry.api.metrics.LongHistogram; import application.io.opentelemetry.api.metrics.LongHistogramBuilder; +import com.google.errorprone.annotations.CanIgnoreReturnValue; final class ApplicationLongHistogramBuilder implements LongHistogramBuilder { @@ -17,12 +18,14 @@ final class ApplicationLongHistogramBuilder implements LongHistogramBuilder { } @Override + @CanIgnoreReturnValue public LongHistogramBuilder setDescription(String description) { agentBuilder.setDescription(description); return this; } @Override + @CanIgnoreReturnValue public LongHistogramBuilder setUnit(String unit) { agentBuilder.setUnit(unit); return this; diff --git a/instrumentation/opentelemetry-api/opentelemetry-api-1.10/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_10/metrics/ApplicationLongUpDownCounterBuilder.java b/instrumentation/opentelemetry-api/opentelemetry-api-1.10/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_10/metrics/ApplicationLongUpDownCounterBuilder.java index 5580d1e4d8e5..69416e72ee98 100644 --- a/instrumentation/opentelemetry-api/opentelemetry-api-1.10/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_10/metrics/ApplicationLongUpDownCounterBuilder.java +++ b/instrumentation/opentelemetry-api/opentelemetry-api-1.10/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_10/metrics/ApplicationLongUpDownCounterBuilder.java @@ -10,6 +10,7 @@ import application.io.opentelemetry.api.metrics.LongUpDownCounterBuilder; import application.io.opentelemetry.api.metrics.ObservableLongMeasurement; import application.io.opentelemetry.api.metrics.ObservableLongUpDownCounter; +import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.util.function.Consumer; final class ApplicationLongUpDownCounterBuilder implements LongUpDownCounterBuilder { @@ -22,12 +23,14 @@ final class ApplicationLongUpDownCounterBuilder implements LongUpDownCounterBuil } @Override + @CanIgnoreReturnValue public LongUpDownCounterBuilder setDescription(String description) { agentBuilder.setDescription(description); return this; } @Override + @CanIgnoreReturnValue public LongUpDownCounterBuilder setUnit(String unit) { agentBuilder.setUnit(unit); return this; diff --git a/instrumentation/opentelemetry-api/opentelemetry-api-1.10/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_10/metrics/ApplicationMeterBuilder.java b/instrumentation/opentelemetry-api/opentelemetry-api-1.10/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_10/metrics/ApplicationMeterBuilder.java index 1f6c4db98297..b9e9aeff12ee 100644 --- a/instrumentation/opentelemetry-api/opentelemetry-api-1.10/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_10/metrics/ApplicationMeterBuilder.java +++ b/instrumentation/opentelemetry-api/opentelemetry-api-1.10/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_10/metrics/ApplicationMeterBuilder.java @@ -7,6 +7,7 @@ import application.io.opentelemetry.api.metrics.Meter; import application.io.opentelemetry.api.metrics.MeterBuilder; +import com.google.errorprone.annotations.CanIgnoreReturnValue; final class ApplicationMeterBuilder implements MeterBuilder { @@ -17,12 +18,14 @@ final class ApplicationMeterBuilder implements MeterBuilder { } @Override + @CanIgnoreReturnValue public MeterBuilder setSchemaUrl(String schemaUrl) { agentBuilder.setSchemaUrl(schemaUrl); return this; } @Override + @CanIgnoreReturnValue public MeterBuilder setInstrumentationVersion(String version) { agentBuilder.setInstrumentationVersion(version); return this; diff --git a/instrumentation/opentelemetry-api/opentelemetry-api-1.10/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_10/metrics/MeterTest.java b/instrumentation/opentelemetry-api/opentelemetry-api-1.10/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_10/metrics/MeterTest.java index dee7f1ca696f..65a798b93bec 100644 --- a/instrumentation/opentelemetry-api/opentelemetry-api-1.10/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_10/metrics/MeterTest.java +++ b/instrumentation/opentelemetry-api/opentelemetry-api-1.10/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_10/metrics/MeterTest.java @@ -69,8 +69,9 @@ void longCounter() { .hasDescription("d") .hasUnit("u") .hasInstrumentationScope( - InstrumentationScopeInfo.create( - instrumentationName, "1.2.3", /* schemaUrl= */ null)) + InstrumentationScopeInfo.builder(instrumentationName) + .setVersion("1.2.3") + .build()) .hasLongSumSatisfying( sum -> sum.isMonotonic() @@ -102,8 +103,9 @@ void observableLongCounter() throws InterruptedException { .hasDescription("d") .hasUnit("u") .hasInstrumentationScope( - InstrumentationScopeInfo.create( - instrumentationName, "1.2.3", /* schemaUrl= */ null)) + InstrumentationScopeInfo.builder(instrumentationName) + .setVersion("1.2.3") + .build()) .hasLongSumSatisfying( sum -> sum.isMonotonic() @@ -142,8 +144,9 @@ void doubleCounter() { .hasDescription("d") .hasUnit("u") .hasInstrumentationScope( - InstrumentationScopeInfo.create( - instrumentationName, "1.2.3", /* schemaUrl= */ null)) + InstrumentationScopeInfo.builder(instrumentationName) + .setVersion("1.2.3") + .build()) .hasDoubleSumSatisfying( sum -> sum.isMonotonic() @@ -176,8 +179,9 @@ void observableDoubleCounter() throws InterruptedException { .hasDescription("d") .hasUnit("u") .hasInstrumentationScope( - InstrumentationScopeInfo.create( - instrumentationName, "1.2.3", /* schemaUrl= */ null)) + InstrumentationScopeInfo.builder(instrumentationName) + .setVersion("1.2.3") + .build()) .hasDoubleSumSatisfying( sum -> sum.isMonotonic() @@ -216,8 +220,9 @@ void longUpDownCounter() { .hasDescription("d") .hasUnit("u") .hasInstrumentationScope( - InstrumentationScopeInfo.create( - instrumentationName, "1.2.3", /* schemaUrl= */ null)) + InstrumentationScopeInfo.builder(instrumentationName) + .setVersion("1.2.3") + .build()) .hasLongSumSatisfying( sum -> sum.isNotMonotonic() @@ -249,8 +254,9 @@ void observableLongUpDownCounter() throws InterruptedException { .hasDescription("d") .hasUnit("u") .hasInstrumentationScope( - InstrumentationScopeInfo.create( - instrumentationName, "1.2.3", /* schemaUrl= */ null)) + InstrumentationScopeInfo.builder(instrumentationName) + .setVersion("1.2.3") + .build()) .hasLongSumSatisfying( sum -> sum.isNotMonotonic() @@ -289,8 +295,9 @@ void doubleUpDownCounter() { .hasDescription("d") .hasUnit("u") .hasInstrumentationScope( - InstrumentationScopeInfo.create( - instrumentationName, "1.2.3", /* schemaUrl= */ null)) + InstrumentationScopeInfo.builder(instrumentationName) + .setVersion("1.2.3") + .build()) .hasDoubleSumSatisfying( sum -> sum.isNotMonotonic() @@ -323,8 +330,9 @@ void observableDoubleUpDownCounter() throws InterruptedException { .hasDescription("d") .hasUnit("u") .hasInstrumentationScope( - InstrumentationScopeInfo.create( - instrumentationName, "1.2.3", /* schemaUrl= */ null)) + InstrumentationScopeInfo.builder(instrumentationName) + .setVersion("1.2.3") + .build()) .hasDoubleSumSatisfying( sum -> sum.isNotMonotonic() @@ -363,8 +371,9 @@ void longHistogram() { .hasDescription("d") .hasUnit("u") .hasInstrumentationScope( - InstrumentationScopeInfo.create( - instrumentationName, "1.2.3", /* schemaUrl= */ null)) + InstrumentationScopeInfo.builder(instrumentationName) + .setVersion("1.2.3") + .build()) .hasHistogramSatisfying( histogram -> histogram.hasPointsSatisfying( @@ -393,8 +402,9 @@ void doubleHistogram() { .hasDescription("d") .hasUnit("u") .hasInstrumentationScope( - InstrumentationScopeInfo.create( - instrumentationName, "1.2.3", /* schemaUrl= */ null)) + InstrumentationScopeInfo.builder(instrumentationName) + .setVersion("1.2.3") + .build()) .hasHistogramSatisfying( histogram -> histogram.hasPointsSatisfying( @@ -426,8 +436,9 @@ void longGauge() throws InterruptedException { .hasDescription("d") .hasUnit("u") .hasInstrumentationScope( - InstrumentationScopeInfo.create( - instrumentationName, "1.2.3", /* schemaUrl= */ null)) + InstrumentationScopeInfo.builder(instrumentationName) + .setVersion("1.2.3") + .build()) .hasLongGaugeSatisfying( gauge -> gauge.hasPointsSatisfying( @@ -467,8 +478,9 @@ void doubleGauge() throws InterruptedException { .hasDescription("d") .hasUnit("u") .hasInstrumentationScope( - InstrumentationScopeInfo.create( - instrumentationName, "1.2.3", /* schemaUrl= */ null)) + InstrumentationScopeInfo.builder(instrumentationName) + .setVersion("1.2.3") + .build()) .hasDoubleGaugeSatisfying( gauge -> gauge.hasPointsSatisfying( diff --git a/instrumentation/opentelemetry-api/opentelemetry-api-1.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_4/OpenTelemetryInstrumentation.java b/instrumentation/opentelemetry-api/opentelemetry-api-1.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_4/OpenTelemetryInstrumentation.java index b3f424f6e176..419a92d0f9ac 100644 --- a/instrumentation/opentelemetry-api/opentelemetry-api-1.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_4/OpenTelemetryInstrumentation.java +++ b/instrumentation/opentelemetry-api/opentelemetry-api-1.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_4/OpenTelemetryInstrumentation.java @@ -28,7 +28,7 @@ public void transform(TypeTransformer transformer) { none(), OpenTelemetryInstrumentation.class.getName() + "$InitAdvice"); } - @SuppressWarnings({"unused", "ReturnValueIgnored"}) + @SuppressWarnings({"ReturnValueIgnored", "unused"}) public static class InitAdvice { @Advice.OnMethodEnter public static void init() { diff --git a/instrumentation/opentelemetry-api/opentelemetry-api-1.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_4/trace/ApplicationTracerBuilder.java b/instrumentation/opentelemetry-api/opentelemetry-api-1.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_4/trace/ApplicationTracerBuilder.java index 0e41be141445..1a9dd388989b 100644 --- a/instrumentation/opentelemetry-api/opentelemetry-api-1.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_4/trace/ApplicationTracerBuilder.java +++ b/instrumentation/opentelemetry-api/opentelemetry-api-1.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_4/trace/ApplicationTracerBuilder.java @@ -7,6 +7,7 @@ import application.io.opentelemetry.api.trace.Tracer; import application.io.opentelemetry.api.trace.TracerBuilder; +import com.google.errorprone.annotations.CanIgnoreReturnValue; import io.opentelemetry.javaagent.instrumentation.opentelemetryapi.trace.ApplicationTracer; class ApplicationTracerBuilder implements TracerBuilder { @@ -18,12 +19,14 @@ public ApplicationTracerBuilder(io.opentelemetry.api.trace.TracerBuilder agentTr } @Override + @CanIgnoreReturnValue public TracerBuilder setSchemaUrl(String schemaUrl) { agentTracerBuilder.setSchemaUrl(schemaUrl); return this; } @Override + @CanIgnoreReturnValue public TracerBuilder setInstrumentationVersion(String version) { agentTracerBuilder.setInstrumentationVersion(version); return this; diff --git a/instrumentation/opentelemetry-api/opentelemetry-api-logs-1.19/javaagent/build.gradle.kts b/instrumentation/opentelemetry-api/opentelemetry-api-logs-1.19/javaagent/build.gradle.kts new file mode 100644 index 000000000000..3ce92db5b146 --- /dev/null +++ b/instrumentation/opentelemetry-api/opentelemetry-api-logs-1.19/javaagent/build.gradle.kts @@ -0,0 +1,29 @@ +plugins { + id("otel.javaagent-instrumentation") +} + +dependencies { + // this instrumentation needs to be able to reference both the OpenTelemetry API + // that is shaded in the bootstrap class loader (for sending telemetry to the agent), + // and the OpenTelemetry API that the user brings (in order to capture that telemetry) + // + // since (all) instrumentation already uses OpenTelemetry API for sending telemetry to the agent, + // this instrumentation uses a "temporarily shaded" OpenTelemetry API to represent the + // OpenTelemetry API that the user brings + // + // then later, after the OpenTelemetry API in the bootstrap class loader is shaded, + // the "temporarily shaded" OpenTelemetry API is unshaded, so that it will apply to the + // OpenTelemetry API that the user brings + // + // so in the code "application.io.opentelemetry.*" refers to the (unshaded) OpenTelemetry API that + // the application brings (as those references will be translated during the build to remove the + // "application." prefix) + // + // and in the code "io.opentelemetry.*" refers to the (shaded) OpenTelemetry API that is used by + // the agent (as those references will later be shaded) + compileOnly("io.opentelemetry:opentelemetry-api-logs") + compileOnly(project(":opentelemetry-api-shaded-for-instrumenting", configuration = "shadow")) + implementation(project(":instrumentation:opentelemetry-api:opentelemetry-api-1.0:javaagent")) + + testImplementation("io.opentelemetry:opentelemetry-sdk-logs") +} diff --git a/instrumentation/opentelemetry-api/opentelemetry-api-logs-1.19/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/logs/OpenTelemetryApiLogsInstrumentationModule.java b/instrumentation/opentelemetry-api/opentelemetry-api-logs-1.19/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/logs/OpenTelemetryApiLogsInstrumentationModule.java new file mode 100644 index 000000000000..d7553b977ea2 --- /dev/null +++ b/instrumentation/opentelemetry-api/opentelemetry-api-logs-1.19/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/logs/OpenTelemetryApiLogsInstrumentationModule.java @@ -0,0 +1,24 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.opentelemetryapi.logs; + +import com.google.auto.service.AutoService; +import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule; +import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; +import java.util.Collections; +import java.util.List; + +@AutoService(InstrumentationModule.class) +public class OpenTelemetryApiLogsInstrumentationModule extends InstrumentationModule { + public OpenTelemetryApiLogsInstrumentationModule() { + super("opentelemetry-api-logs"); + } + + @Override + public List typeInstrumentations() { + return Collections.singletonList(new OpenTelemetryLogsInstrumentation()); + } +} diff --git a/instrumentation/opentelemetry-api/opentelemetry-api-logs-1.19/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/logs/OpenTelemetryLogsInstrumentation.java b/instrumentation/opentelemetry-api/opentelemetry-api-logs-1.19/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/logs/OpenTelemetryLogsInstrumentation.java new file mode 100644 index 000000000000..baac74a958ff --- /dev/null +++ b/instrumentation/opentelemetry-api/opentelemetry-api-logs-1.19/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/logs/OpenTelemetryLogsInstrumentation.java @@ -0,0 +1,51 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.opentelemetryapi.logs; + +import static net.bytebuddy.matcher.ElementMatchers.isMethod; +import static net.bytebuddy.matcher.ElementMatchers.isStatic; +import static net.bytebuddy.matcher.ElementMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.takesArguments; + +import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; +import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; +import io.opentelemetry.javaagent.instrumentation.opentelemetryapi.logs.bridge.ApplicationLoggerProvider; +import net.bytebuddy.asm.Advice; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; + +// Our convention for accessing agent package +@SuppressWarnings("UnnecessarilyFullyQualified") +public class OpenTelemetryLogsInstrumentation implements TypeInstrumentation { + + @Override + public ElementMatcher typeMatcher() { + return named("application.io.opentelemetry.api.logs.GlobalLoggerProvider"); + } + + @Override + public void transform(TypeTransformer transformer) { + transformer.applyAdviceToMethod( + isMethod().and(isStatic()).and(named("get")).and(takesArguments(0)), + OpenTelemetryLogsInstrumentation.class.getName() + "$GetGlobalLogsAdvice"); + } + + @SuppressWarnings("unused") + public static class GetGlobalLogsAdvice { + + @Advice.OnMethodEnter(skipOn = Advice.OnDefaultValue.class) + public static Object onEnter() { + return null; + } + + @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) + public static void methodExit( + @Advice.Return(readOnly = false) + application.io.opentelemetry.api.logs.LoggerProvider loggerProvider) { + loggerProvider = ApplicationLoggerProvider.INSTANCE; + } + } +} diff --git a/instrumentation/opentelemetry-api/opentelemetry-api-logs-1.19/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/logs/bridge/ApplicationLogRecordBuilder.java b/instrumentation/opentelemetry-api/opentelemetry-api-logs-1.19/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/logs/bridge/ApplicationLogRecordBuilder.java new file mode 100644 index 000000000000..7d9e0d35cc20 --- /dev/null +++ b/instrumentation/opentelemetry-api/opentelemetry-api-logs-1.19/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/logs/bridge/ApplicationLogRecordBuilder.java @@ -0,0 +1,84 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.opentelemetryapi.logs.bridge; + +import application.io.opentelemetry.api.common.AttributeKey; +import application.io.opentelemetry.api.logs.EventBuilder; +import application.io.opentelemetry.api.logs.LogRecordBuilder; +import application.io.opentelemetry.api.logs.Severity; +import application.io.opentelemetry.context.Context; +import com.google.errorprone.annotations.CanIgnoreReturnValue; +import io.opentelemetry.javaagent.instrumentation.opentelemetryapi.context.AgentContextStorage; +import io.opentelemetry.javaagent.instrumentation.opentelemetryapi.trace.Bridging; +import java.time.Instant; +import java.util.concurrent.TimeUnit; + +class ApplicationLogRecordBuilder implements EventBuilder { + + private final io.opentelemetry.api.logs.LogRecordBuilder agentLogRecordBuilder; + + ApplicationLogRecordBuilder(io.opentelemetry.api.logs.LogRecordBuilder agentLogRecordBuilder) { + this.agentLogRecordBuilder = agentLogRecordBuilder; + } + + @Override + @CanIgnoreReturnValue + public LogRecordBuilder setEpoch(long l, TimeUnit timeUnit) { + agentLogRecordBuilder.setEpoch(l, timeUnit); + return this; + } + + @Override + @CanIgnoreReturnValue + public LogRecordBuilder setEpoch(Instant instant) { + agentLogRecordBuilder.setEpoch(instant); + return this; + } + + @Override + @CanIgnoreReturnValue + public LogRecordBuilder setContext(Context context) { + agentLogRecordBuilder.setContext(AgentContextStorage.getAgentContext(context)); + return this; + } + + @Override + @CanIgnoreReturnValue + public LogRecordBuilder setSeverity(Severity severity) { + agentLogRecordBuilder.setSeverity(LogBridging.toAgent(severity)); + return this; + } + + @Override + @CanIgnoreReturnValue + public LogRecordBuilder setSeverityText(String s) { + agentLogRecordBuilder.setSeverityText(s); + return this; + } + + @Override + @CanIgnoreReturnValue + public LogRecordBuilder setBody(String s) { + agentLogRecordBuilder.setBody(s); + return this; + } + + @Override + @CanIgnoreReturnValue + public LogRecordBuilder setAttribute(AttributeKey attributeKey, T t) { + @SuppressWarnings("unchecked") + io.opentelemetry.api.common.AttributeKey agentKey = Bridging.toAgent(attributeKey); + if (agentKey != null) { + agentLogRecordBuilder.setAttribute(agentKey, t); + } + return this; + } + + @Override + public void emit() { + agentLogRecordBuilder.emit(); + } +} diff --git a/instrumentation/opentelemetry-api/opentelemetry-api-logs-1.19/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/logs/bridge/ApplicationLogger.java b/instrumentation/opentelemetry-api/opentelemetry-api-logs-1.19/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/logs/bridge/ApplicationLogger.java new file mode 100644 index 000000000000..4ac8066b0ed7 --- /dev/null +++ b/instrumentation/opentelemetry-api/opentelemetry-api-logs-1.19/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/logs/bridge/ApplicationLogger.java @@ -0,0 +1,29 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.opentelemetryapi.logs.bridge; + +import application.io.opentelemetry.api.logs.EventBuilder; +import application.io.opentelemetry.api.logs.LogRecordBuilder; +import application.io.opentelemetry.api.logs.Logger; + +class ApplicationLogger implements Logger { + + private final io.opentelemetry.api.logs.Logger agentLogger; + + ApplicationLogger(io.opentelemetry.api.logs.Logger agentLogger) { + this.agentLogger = agentLogger; + } + + @Override + public EventBuilder eventBuilder(String eventName) { + return new ApplicationLogRecordBuilder(agentLogger.eventBuilder(eventName)); + } + + @Override + public LogRecordBuilder logRecordBuilder() { + return new ApplicationLogRecordBuilder(agentLogger.logRecordBuilder()); + } +} diff --git a/instrumentation/opentelemetry-api/opentelemetry-api-logs-1.19/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/logs/bridge/ApplicationLoggerBuilder.java b/instrumentation/opentelemetry-api/opentelemetry-api-logs-1.19/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/logs/bridge/ApplicationLoggerBuilder.java new file mode 100644 index 000000000000..ac37063f70c0 --- /dev/null +++ b/instrumentation/opentelemetry-api/opentelemetry-api-logs-1.19/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/logs/bridge/ApplicationLoggerBuilder.java @@ -0,0 +1,45 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.opentelemetryapi.logs.bridge; + +import application.io.opentelemetry.api.logs.Logger; +import application.io.opentelemetry.api.logs.LoggerBuilder; +import com.google.errorprone.annotations.CanIgnoreReturnValue; + +final class ApplicationLoggerBuilder implements LoggerBuilder { + + private final io.opentelemetry.api.logs.LoggerBuilder agentBuilder; + + ApplicationLoggerBuilder(io.opentelemetry.api.logs.LoggerBuilder agentBuilder) { + this.agentBuilder = agentBuilder; + } + + @Override + @CanIgnoreReturnValue + public LoggerBuilder setEventDomain(String eventDomain) { + agentBuilder.setEventDomain(eventDomain); + return this; + } + + @Override + @CanIgnoreReturnValue + public LoggerBuilder setSchemaUrl(String schemaUrl) { + agentBuilder.setSchemaUrl(schemaUrl); + return this; + } + + @Override + @CanIgnoreReturnValue + public LoggerBuilder setInstrumentationVersion(String version) { + agentBuilder.setInstrumentationVersion(version); + return this; + } + + @Override + public Logger build() { + return new ApplicationLogger(agentBuilder.build()); + } +} diff --git a/instrumentation/opentelemetry-api/opentelemetry-api-logs-1.19/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/logs/bridge/ApplicationLoggerProvider.java b/instrumentation/opentelemetry-api/opentelemetry-api-logs-1.19/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/logs/bridge/ApplicationLoggerProvider.java new file mode 100644 index 000000000000..d5a822c3b7f1 --- /dev/null +++ b/instrumentation/opentelemetry-api/opentelemetry-api-logs-1.19/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/logs/bridge/ApplicationLoggerProvider.java @@ -0,0 +1,27 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.opentelemetryapi.logs.bridge; + +import application.io.opentelemetry.api.logs.LoggerBuilder; +import application.io.opentelemetry.api.logs.LoggerProvider; + +// Our convention for accessing agent packages. +@SuppressWarnings("UnnecessarilyFullyQualified") +public class ApplicationLoggerProvider implements LoggerProvider { + + public static final LoggerProvider INSTANCE = new ApplicationLoggerProvider(); + + private final io.opentelemetry.api.logs.LoggerProvider agentLoggerProvider; + + public ApplicationLoggerProvider() { + this.agentLoggerProvider = io.opentelemetry.api.logs.GlobalLoggerProvider.get(); + } + + @Override + public LoggerBuilder loggerBuilder(String instrumentationName) { + return new ApplicationLoggerBuilder(agentLoggerProvider.loggerBuilder(instrumentationName)); + } +} diff --git a/instrumentation/opentelemetry-api/opentelemetry-api-logs-1.19/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/logs/bridge/LogBridging.java b/instrumentation/opentelemetry-api/opentelemetry-api-logs-1.19/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/logs/bridge/LogBridging.java new file mode 100644 index 000000000000..a6459dddd9d9 --- /dev/null +++ b/instrumentation/opentelemetry-api/opentelemetry-api-logs-1.19/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/logs/bridge/LogBridging.java @@ -0,0 +1,47 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.opentelemetryapi.logs.bridge; + +import application.io.opentelemetry.api.logs.Severity; +import java.util.EnumMap; + +/** + * This class translates between the (unshaded) OpenTelemetry API that the application brings and + * the (shaded) OpenTelemetry API that is used by the agent. + * + *

"application.io.opentelemetry.*" refers to the (unshaded) OpenTelemetry API that the + * application brings (as those references will be translated during the build to remove the + * "application." prefix). + * + *

"io.opentelemetry.*" refers to the (shaded) OpenTelemetry API that is used by the agent (as + * those references will later be shaded). + * + *

Also see comments in this module's gradle file. + */ +// Our convention for accessing agent package +@SuppressWarnings("UnnecessarilyFullyQualified") +public class LogBridging { + + private static final EnumMap severityMap; + + static { + severityMap = new EnumMap<>(Severity.class); + for (Severity severity : Severity.values()) { + try { + severityMap.put(severity, io.opentelemetry.api.logs.Severity.valueOf(severity.name())); + } catch (IllegalArgumentException e) { + // No mapping exists for this severity, ignore + } + } + } + + public static io.opentelemetry.api.logs.Severity toAgent(Severity applicationSeverity) { + return severityMap.getOrDefault( + applicationSeverity, io.opentelemetry.api.logs.Severity.UNDEFINED_SEVERITY_NUMBER); + } + + private LogBridging() {} +} diff --git a/instrumentation/opentelemetry-api/opentelemetry-api-logs-1.19/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/logs/LoggerTest.java b/instrumentation/opentelemetry-api/opentelemetry-api-logs-1.19/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/logs/LoggerTest.java new file mode 100644 index 000000000000..7f70555bb63a --- /dev/null +++ b/instrumentation/opentelemetry-api/opentelemetry-api-logs-1.19/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/logs/LoggerTest.java @@ -0,0 +1,89 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.opentelemetryapi.logs; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.awaitility.Awaitility.await; + +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.logs.GlobalLoggerProvider; +import io.opentelemetry.api.logs.Logger; +import io.opentelemetry.api.logs.Severity; +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.api.trace.SpanContext; +import io.opentelemetry.api.trace.TraceFlags; +import io.opentelemetry.api.trace.TraceState; +import io.opentelemetry.context.Context; +import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension; +import io.opentelemetry.sdk.trace.IdGenerator; +import java.time.Instant; +import java.util.concurrent.TimeUnit; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInfo; +import org.junit.jupiter.api.extension.RegisterExtension; + +class LoggerTest { + + @RegisterExtension + static final AgentInstrumentationExtension testing = AgentInstrumentationExtension.create(); + + private String instrumentationName; + private Logger logger; + + @BeforeEach + void setupLogger(TestInfo test) { + instrumentationName = "test-" + test.getDisplayName(); + logger = + GlobalLoggerProvider.get() + .loggerBuilder(instrumentationName) + .setInstrumentationVersion("1.2.3") + .setSchemaUrl("http://schema.org") + .build(); + } + + @Test + void logRecordBuilder() { + SpanContext spanContext = + SpanContext.create( + IdGenerator.random().generateTraceId(), + IdGenerator.random().generateSpanId(), + TraceFlags.getDefault(), + TraceState.getDefault()); + + logger + .logRecordBuilder() + .setEpoch(1, TimeUnit.SECONDS) + .setEpoch(Instant.now()) + .setContext(Context.current().with(Span.wrap(spanContext))) + .setSeverity(Severity.DEBUG) + .setSeverityText("debug") + .setBody("body") + .setAttribute(AttributeKey.stringKey("key"), "value") + .setAllAttributes(Attributes.builder().put("key", "value").build()) + .emit(); + + await() + .untilAsserted( + () -> + assertThat(testing.logRecords()) + .satisfiesExactly( + logRecordData -> { + assertThat(logRecordData.getInstrumentationScopeInfo().getName()) + .isEqualTo(instrumentationName); + assertThat(logRecordData.getInstrumentationScopeInfo().getVersion()) + .isEqualTo("1.2.3"); + assertThat(logRecordData.getEpochNanos()).isGreaterThan(0); + assertThat(logRecordData.getSpanContext()).isEqualTo(spanContext); + assertThat(logRecordData.getSeverity()).isEqualTo(Severity.DEBUG); + assertThat(logRecordData.getSeverityText()).isEqualTo("debug"); + assertThat(logRecordData.getBody().asString()).isEqualTo("body"); + assertThat(logRecordData.getAttributes()) + .isEqualTo(Attributes.builder().put("key", "value").build()); + })); + } +} diff --git a/instrumentation/opentelemetry-extension-annotations-1.0/javaagent/build.gradle.kts b/instrumentation/opentelemetry-extension-annotations-1.0/javaagent/build.gradle.kts index 7b50ccab82c1..9abbae283ebb 100644 --- a/instrumentation/opentelemetry-extension-annotations-1.0/javaagent/build.gradle.kts +++ b/instrumentation/opentelemetry-extension-annotations-1.0/javaagent/build.gradle.kts @@ -12,8 +12,6 @@ muzzle { } } -val versions: Map by project - dependencies { compileOnly(project(":instrumentation-annotations-support")) diff --git a/instrumentation/opentelemetry-extension-annotations-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/extensionannotations/MethodCodeAttributesGetter.java b/instrumentation/opentelemetry-extension-annotations-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/extensionannotations/MethodCodeAttributesGetter.java new file mode 100644 index 000000000000..894c02c2a4cf --- /dev/null +++ b/instrumentation/opentelemetry-extension-annotations-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/extensionannotations/MethodCodeAttributesGetter.java @@ -0,0 +1,23 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.extensionannotations; + +import io.opentelemetry.instrumentation.api.instrumenter.code.CodeAttributesGetter; +import java.lang.reflect.Method; + +enum MethodCodeAttributesGetter implements CodeAttributesGetter { + INSTANCE; + + @Override + public Class codeClass(Method method) { + return method.getDeclaringClass(); + } + + @Override + public String methodName(Method method) { + return method.getName(); + } +} diff --git a/instrumentation/opentelemetry-extension-annotations-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/extensionannotations/MethodRequestCodeAttributesGetter.java b/instrumentation/opentelemetry-extension-annotations-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/extensionannotations/MethodRequestCodeAttributesGetter.java new file mode 100644 index 000000000000..d52c4fd33b3f --- /dev/null +++ b/instrumentation/opentelemetry-extension-annotations-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/extensionannotations/MethodRequestCodeAttributesGetter.java @@ -0,0 +1,22 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.extensionannotations; + +import io.opentelemetry.instrumentation.api.instrumenter.code.CodeAttributesGetter; + +enum MethodRequestCodeAttributesGetter implements CodeAttributesGetter { + INSTANCE; + + @Override + public Class codeClass(MethodRequest methodRequest) { + return methodRequest.method().getDeclaringClass(); + } + + @Override + public String methodName(MethodRequest methodRequest) { + return methodRequest.method().getName(); + } +} diff --git a/instrumentation/opentelemetry-extension-annotations-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/extensionannotations/WithSpanInstrumentationModule.java b/instrumentation/opentelemetry-extension-annotations-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/extensionannotations/WithSpanInstrumentationModule.java index f7a821ca179e..2d3663c0ca02 100644 --- a/instrumentation/opentelemetry-extension-annotations-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/extensionannotations/WithSpanInstrumentationModule.java +++ b/instrumentation/opentelemetry-extension-annotations-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/extensionannotations/WithSpanInstrumentationModule.java @@ -7,13 +7,15 @@ import static java.util.Collections.singletonList; -import application.io.opentelemetry.extension.annotations.WithSpan; import com.google.auto.service.AutoService; import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import java.util.List; -/** Instrumentation for methods annotated with {@link WithSpan} annotation. */ +/** + * Instrumentation for methods annotated with {@link + * application.io.opentelemetry.extension.annotations.WithSpan} annotation. + */ @AutoService(InstrumentationModule.class) public class WithSpanInstrumentationModule extends InstrumentationModule { diff --git a/instrumentation/opentelemetry-extension-annotations-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/extensionannotations/WithSpanParameterAttributeNamesExtractor.java b/instrumentation/opentelemetry-extension-annotations-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/extensionannotations/WithSpanParameterAttributeNamesExtractor.java index bd0a2a994b4f..b71e51f6600a 100644 --- a/instrumentation/opentelemetry-extension-annotations-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/extensionannotations/WithSpanParameterAttributeNamesExtractor.java +++ b/instrumentation/opentelemetry-extension-annotations-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/extensionannotations/WithSpanParameterAttributeNamesExtractor.java @@ -24,7 +24,7 @@ public enum WithSpanParameterAttributeNamesExtractor implements ParameterAttribu ClassLoader classLoader = WithSpanParameterAttributeNamesExtractor.class.getClassLoader(); spanAttributeAnnotation = AnnotationReflectionHelper.forNameOrNull( - classLoader, "io.opentelemetry.extension.annotations.SpanAttribute"); + classLoader, "application.io.opentelemetry.extension.annotations.SpanAttribute"); if (spanAttributeAnnotation != null) { spanAttributeValueFunction = resolveSpanAttributeValue(spanAttributeAnnotation); } else { diff --git a/instrumentation/opentelemetry-extension-annotations-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/extensionannotations/WithSpanSingletons.java b/instrumentation/opentelemetry-extension-annotations-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/extensionannotations/WithSpanSingletons.java index 4ca6c5317206..836ae98f1344 100644 --- a/instrumentation/opentelemetry-extension-annotations-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/extensionannotations/WithSpanSingletons.java +++ b/instrumentation/opentelemetry-extension-annotations-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/extensionannotations/WithSpanSingletons.java @@ -7,18 +7,20 @@ import static java.util.logging.Level.FINE; -import application.io.opentelemetry.extension.annotations.WithSpan; import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.api.trace.SpanKind; import io.opentelemetry.instrumentation.api.annotation.support.MethodSpanAttributesExtractor; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; -import io.opentelemetry.instrumentation.api.util.SpanNames; +import io.opentelemetry.instrumentation.api.instrumenter.code.CodeAttributesExtractor; +import io.opentelemetry.instrumentation.api.instrumenter.util.SpanNames; import java.lang.reflect.Method; import java.util.logging.Logger; +@SuppressWarnings("deprecation") // instrumenting deprecated class for backwards compatibility public final class WithSpanSingletons { + private static final String INSTRUMENTATION_NAME = - "io.opentelemetry.opentelemetry-annotations-1.0"; + "io.opentelemetry.opentelemetry-extension-annotations-1.0"; private static final Logger logger = Logger.getLogger(WithSpanSingletons.class.getName()); private static final Instrumenter INSTRUMENTER = createInstrumenter(); @@ -36,7 +38,8 @@ public static Instrumenter instrumenterWithAttributes() { private static Instrumenter createInstrumenter() { return Instrumenter.builder( GlobalOpenTelemetry.get(), INSTRUMENTATION_NAME, WithSpanSingletons::spanNameFromMethod) - .newInstrumenter(WithSpanSingletons::spanKindFromMethod); + .addAttributesExtractor(CodeAttributesExtractor.create(MethodCodeAttributesGetter.INSTANCE)) + .buildInstrumenter(WithSpanSingletons::spanKindFromMethod); } private static Instrumenter createInstrumenterWithAttributes() { @@ -44,12 +47,14 @@ private static Instrumenter createInstrumenterWithAttribu GlobalOpenTelemetry.get(), INSTRUMENTATION_NAME, WithSpanSingletons::spanNameFromMethodRequest) + .addAttributesExtractor( + CodeAttributesExtractor.create(MethodRequestCodeAttributesGetter.INSTANCE)) .addAttributesExtractor( MethodSpanAttributesExtractor.newInstance( MethodRequest::method, WithSpanParameterAttributeNamesExtractor.INSTANCE, MethodRequest::args)) - .newInstrumenter(WithSpanSingletons::spanKindFromMethodRequest); + .buildInstrumenter(WithSpanSingletons::spanKindFromMethodRequest); } private static SpanKind spanKindFromMethodRequest(MethodRequest request) { @@ -57,7 +62,9 @@ private static SpanKind spanKindFromMethodRequest(MethodRequest request) { } private static SpanKind spanKindFromMethod(Method method) { - WithSpan annotation = method.getDeclaredAnnotation(WithSpan.class); + application.io.opentelemetry.extension.annotations.WithSpan annotation = + method.getDeclaredAnnotation( + application.io.opentelemetry.extension.annotations.WithSpan.class); if (annotation == null) { return SpanKind.INTERNAL; } @@ -79,11 +86,15 @@ private static String spanNameFromMethodRequest(MethodRequest request) { } private static String spanNameFromMethod(Method method) { - WithSpan annotation = method.getDeclaredAnnotation(WithSpan.class); + application.io.opentelemetry.extension.annotations.WithSpan annotation = + method.getDeclaredAnnotation( + application.io.opentelemetry.extension.annotations.WithSpan.class); String spanName = annotation.value(); if (spanName.isEmpty()) { spanName = SpanNames.fromMethod(method); } return spanName; } + + private WithSpanSingletons() {} } diff --git a/instrumentation/opentelemetry-extension-annotations-1.0/javaagent/src/test/groovy/WithSpanInstrumentationTest.groovy b/instrumentation/opentelemetry-extension-annotations-1.0/javaagent/src/test/groovy/WithSpanInstrumentationTest.groovy index 14fef17c9ad4..fd5fd3264f0e 100644 --- a/instrumentation/opentelemetry-extension-annotations-1.0/javaagent/src/test/groovy/WithSpanInstrumentationTest.groovy +++ b/instrumentation/opentelemetry-extension-annotations-1.0/javaagent/src/test/groovy/WithSpanInstrumentationTest.groovy @@ -3,8 +3,8 @@ * SPDX-License-Identifier: Apache-2.0 */ -import io.opentelemetry.extension.annotations.WithSpan import io.opentelemetry.instrumentation.test.AgentInstrumentationSpecification +import io.opentelemetry.semconv.trace.attributes.SemanticAttributes import io.opentelemetry.test.annotation.TracedWithSpan import net.bytebuddy.ByteBuddy import net.bytebuddy.ClassFileVersion @@ -24,8 +24,10 @@ import static io.opentelemetry.api.trace.SpanKind.SERVER import static io.opentelemetry.api.trace.StatusCode.ERROR /** - * This test verifies that auto instrumentation supports {@link io.opentelemetry.extension.annotations.WithSpan} contrib annotation. + * This test verifies that auto instrumentation supports the + * {@link io.opentelemetry.extension.annotations.WithSpan} annotation. */ +@SuppressWarnings("deprecation") // testing instrumentation of deprecated class class WithSpanInstrumentationTest extends AgentInstrumentationSpecification { def "should derive automatic name"() { @@ -40,6 +42,8 @@ class WithSpanInstrumentationTest extends AgentInstrumentationSpecification { kind INTERNAL hasNoParent() attributes { + "$SemanticAttributes.CODE_NAMESPACE" TracedWithSpan.name + "$SemanticAttributes.CODE_FUNCTION" "otel" } } } @@ -57,6 +61,8 @@ class WithSpanInstrumentationTest extends AgentInstrumentationSpecification { name "manualName" hasNoParent() attributes { + "$SemanticAttributes.CODE_NAMESPACE" TracedWithSpan.name + "$SemanticAttributes.CODE_FUNCTION" "namedOtel" } } } @@ -75,6 +81,8 @@ class WithSpanInstrumentationTest extends AgentInstrumentationSpecification { kind PRODUCER hasNoParent() attributes { + "$SemanticAttributes.CODE_NAMESPACE" TracedWithSpan.name + "$SemanticAttributes.CODE_FUNCTION" "someKind" } } } @@ -93,12 +101,16 @@ class WithSpanInstrumentationTest extends AgentInstrumentationSpecification { kind SERVER hasNoParent() attributes { + "$SemanticAttributes.CODE_NAMESPACE" TracedWithSpan.name + "$SemanticAttributes.CODE_FUNCTION" "server" } } span(1) { name "TracedWithSpan.otel" childOf span(0) attributes { + "$SemanticAttributes.CODE_NAMESPACE" TracedWithSpan.name + "$SemanticAttributes.CODE_FUNCTION" "otel" } } } @@ -127,6 +139,8 @@ class WithSpanInstrumentationTest extends AgentInstrumentationSpecification { kind INTERNAL hasNoParent() attributes { + "$SemanticAttributes.CODE_NAMESPACE" TracedWithSpan.name + "$SemanticAttributes.CODE_FUNCTION" "completionStage" } } } @@ -151,6 +165,8 @@ class WithSpanInstrumentationTest extends AgentInstrumentationSpecification { kind INTERNAL hasNoParent() attributes { + "$SemanticAttributes.CODE_NAMESPACE" TracedWithSpan.name + "$SemanticAttributes.CODE_FUNCTION" "completionStage" } } } @@ -173,6 +189,8 @@ class WithSpanInstrumentationTest extends AgentInstrumentationSpecification { status ERROR errorEvent(IllegalArgumentException, "Boom") attributes { + "$SemanticAttributes.CODE_NAMESPACE" TracedWithSpan.name + "$SemanticAttributes.CODE_FUNCTION" "completionStage" } } } @@ -199,6 +217,8 @@ class WithSpanInstrumentationTest extends AgentInstrumentationSpecification { status ERROR errorEvent(IllegalArgumentException, "Boom") attributes { + "$SemanticAttributes.CODE_NAMESPACE" TracedWithSpan.name + "$SemanticAttributes.CODE_FUNCTION" "completionStage" } } } @@ -217,6 +237,8 @@ class WithSpanInstrumentationTest extends AgentInstrumentationSpecification { kind INTERNAL hasNoParent() attributes { + "$SemanticAttributes.CODE_NAMESPACE" TracedWithSpan.name + "$SemanticAttributes.CODE_FUNCTION" "completionStage" } } } @@ -236,6 +258,8 @@ class WithSpanInstrumentationTest extends AgentInstrumentationSpecification { kind INTERNAL hasNoParent() attributes { + "$SemanticAttributes.CODE_NAMESPACE" TracedWithSpan.name + "$SemanticAttributes.CODE_FUNCTION" "completableFuture" } } } @@ -260,6 +284,8 @@ class WithSpanInstrumentationTest extends AgentInstrumentationSpecification { kind INTERNAL hasNoParent() attributes { + "$SemanticAttributes.CODE_NAMESPACE" TracedWithSpan.name + "$SemanticAttributes.CODE_FUNCTION" "completableFuture" } } } @@ -282,6 +308,8 @@ class WithSpanInstrumentationTest extends AgentInstrumentationSpecification { status ERROR errorEvent(IllegalArgumentException, "Boom") attributes { + "$SemanticAttributes.CODE_NAMESPACE" TracedWithSpan.name + "$SemanticAttributes.CODE_FUNCTION" "completableFuture" } } } @@ -308,6 +336,8 @@ class WithSpanInstrumentationTest extends AgentInstrumentationSpecification { status ERROR errorEvent(IllegalArgumentException, "Boom") attributes { + "$SemanticAttributes.CODE_NAMESPACE" TracedWithSpan.name + "$SemanticAttributes.CODE_FUNCTION" "completableFuture" } } } @@ -326,6 +356,8 @@ class WithSpanInstrumentationTest extends AgentInstrumentationSpecification { kind INTERNAL hasNoParent() attributes { + "$SemanticAttributes.CODE_NAMESPACE" TracedWithSpan.name + "$SemanticAttributes.CODE_FUNCTION" "completableFuture" } } } @@ -353,7 +385,7 @@ class WithSpanInstrumentationTest extends AgentInstrumentationSpecification { } })) .visit(new MemberAttributeExtension.ForMethod() - .annotateMethod(AnnotationDescription.Builder.ofType(WithSpan).build()) + .annotateMethod(AnnotationDescription.Builder.ofType(io.opentelemetry.extension.annotations.WithSpan).build()) .on(ElementMatchers.named("run"))) .make() .load(getClass().getClassLoader()) @@ -370,6 +402,8 @@ class WithSpanInstrumentationTest extends AgentInstrumentationSpecification { kind INTERNAL hasNoParent() attributes { + "$SemanticAttributes.CODE_NAMESPACE" "GeneratedJava6TestClass" + "$SemanticAttributes.CODE_FUNCTION" "run" } } span(1) { @@ -395,6 +429,8 @@ class WithSpanInstrumentationTest extends AgentInstrumentationSpecification { kind INTERNAL hasNoParent() attributes { + "$SemanticAttributes.CODE_NAMESPACE" TracedWithSpan.name + "$SemanticAttributes.CODE_FUNCTION" "withSpanAttributes" "implicitName" "foo" "explicitName" "bar" } diff --git a/instrumentation/opentelemetry-extension-annotations-1.0/javaagent/src/test/java/io/opentelemetry/test/annotation/TracedWithSpan.java b/instrumentation/opentelemetry-extension-annotations-1.0/javaagent/src/test/java/io/opentelemetry/test/annotation/TracedWithSpan.java index 869e40796439..b7bd5eb34f00 100644 --- a/instrumentation/opentelemetry-extension-annotations-1.0/javaagent/src/test/java/io/opentelemetry/test/annotation/TracedWithSpan.java +++ b/instrumentation/opentelemetry-extension-annotations-1.0/javaagent/src/test/java/io/opentelemetry/test/annotation/TracedWithSpan.java @@ -6,54 +6,53 @@ package io.opentelemetry.test.annotation; import io.opentelemetry.api.trace.SpanKind; -import io.opentelemetry.extension.annotations.SpanAttribute; -import io.opentelemetry.extension.annotations.WithSpan; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; +@SuppressWarnings("deprecation") // testing instrumentation of deprecated class public class TracedWithSpan { - @WithSpan + @io.opentelemetry.extension.annotations.WithSpan public String otel() { return "hello!"; } - @WithSpan("manualName") + @io.opentelemetry.extension.annotations.WithSpan("manualName") public String namedOtel() { return "hello!"; } - @WithSpan + @io.opentelemetry.extension.annotations.WithSpan public String ignored() { return "hello!"; } - @WithSpan(kind = SpanKind.PRODUCER) + @io.opentelemetry.extension.annotations.WithSpan(kind = SpanKind.PRODUCER) public String someKind() { return "hello!"; } - @WithSpan(kind = SpanKind.SERVER) + @io.opentelemetry.extension.annotations.WithSpan(kind = SpanKind.SERVER) public String server() { return otel(); } - @WithSpan + @io.opentelemetry.extension.annotations.WithSpan public String withSpanAttributes( - @SpanAttribute String implicitName, - @SpanAttribute("explicitName") String parameter, - @SpanAttribute("nullAttribute") String nullAttribute, + @io.opentelemetry.extension.annotations.SpanAttribute String implicitName, + @io.opentelemetry.extension.annotations.SpanAttribute("explicitName") String parameter, + @io.opentelemetry.extension.annotations.SpanAttribute("nullAttribute") String nullAttribute, String notTraced) { return "hello!"; } - @WithSpan + @io.opentelemetry.extension.annotations.WithSpan public CompletionStage completionStage(CompletableFuture future) { return future; } - @WithSpan + @io.opentelemetry.extension.annotations.WithSpan public CompletableFuture completableFuture(CompletableFuture future) { return future; } diff --git a/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/build.gradle.kts b/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/build.gradle.kts index ba169155f46d..78949295ed70 100644 --- a/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/build.gradle.kts +++ b/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/build.gradle.kts @@ -14,8 +14,6 @@ muzzle { } } -val versions: Map by project - dependencies { compileOnly(project(":instrumentation-annotations-support")) @@ -27,7 +25,7 @@ dependencies { // see the comment in opentelemetry-api-1.0.gradle for more details compileOnly(project(":opentelemetry-instrumentation-annotations-shaded-for-instrumenting", configuration = "shadow")) - testImplementation("io.opentelemetry:opentelemetry-extension-annotations") + testImplementation(project(":instrumentation-annotations")) testImplementation(project(":instrumentation-annotations-support")) testImplementation("net.bytebuddy:byte-buddy") } diff --git a/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/MethodCodeAttributesGetter.java b/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/MethodCodeAttributesGetter.java new file mode 100644 index 000000000000..9cf2be6ee753 --- /dev/null +++ b/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/MethodCodeAttributesGetter.java @@ -0,0 +1,23 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.instrumentationannotations; + +import io.opentelemetry.instrumentation.api.instrumenter.code.CodeAttributesGetter; +import java.lang.reflect.Method; + +enum MethodCodeAttributesGetter implements CodeAttributesGetter { + INSTANCE; + + @Override + public Class codeClass(Method method) { + return method.getDeclaringClass(); + } + + @Override + public String methodName(Method method) { + return method.getName(); + } +} diff --git a/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/MethodRequestCodeAttributesGetter.java b/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/MethodRequestCodeAttributesGetter.java new file mode 100644 index 000000000000..55f6136e4dbc --- /dev/null +++ b/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/MethodRequestCodeAttributesGetter.java @@ -0,0 +1,22 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.instrumentationannotations; + +import io.opentelemetry.instrumentation.api.instrumenter.code.CodeAttributesGetter; + +enum MethodRequestCodeAttributesGetter implements CodeAttributesGetter { + INSTANCE; + + @Override + public Class codeClass(MethodRequest methodRequest) { + return methodRequest.method().getDeclaringClass(); + } + + @Override + public String methodName(MethodRequest methodRequest) { + return methodRequest.method().getName(); + } +} diff --git a/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/WithSpanParameterAttributeNamesExtractor.java b/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/WithSpanParameterAttributeNamesExtractor.java index 2811057d2f4d..d11021453623 100644 --- a/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/WithSpanParameterAttributeNamesExtractor.java +++ b/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/WithSpanParameterAttributeNamesExtractor.java @@ -24,7 +24,7 @@ public enum WithSpanParameterAttributeNamesExtractor implements ParameterAttribu ClassLoader classLoader = WithSpanParameterAttributeNamesExtractor.class.getClassLoader(); spanAttributeAnnotation = AnnotationReflectionHelper.forNameOrNull( - classLoader, "io.opentelemetry.instrumentation.annotations.SpanAttribute"); + classLoader, "application.io.opentelemetry.instrumentation.annotations.SpanAttribute"); if (spanAttributeAnnotation != null) { spanAttributeValueFunction = resolveSpanAttributeValue(spanAttributeAnnotation); } else { diff --git a/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/WithSpanSingletons.java b/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/WithSpanSingletons.java index e78e30d316c9..c0fd5bd7f63b 100644 --- a/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/WithSpanSingletons.java +++ b/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/WithSpanSingletons.java @@ -12,13 +12,15 @@ import io.opentelemetry.api.trace.SpanKind; import io.opentelemetry.instrumentation.api.annotation.support.MethodSpanAttributesExtractor; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; -import io.opentelemetry.instrumentation.api.util.SpanNames; +import io.opentelemetry.instrumentation.api.instrumenter.code.CodeAttributesExtractor; +import io.opentelemetry.instrumentation.api.instrumenter.util.SpanNames; import java.lang.reflect.Method; import java.util.logging.Logger; public final class WithSpanSingletons { + private static final String INSTRUMENTATION_NAME = - "io.opentelemetry.opentelemetry-annotations-1.0"; + "io.opentelemetry.opentelemetry-instrumentation-annotations-1.16"; private static final Logger logger = Logger.getLogger(WithSpanSingletons.class.getName()); private static final Instrumenter INSTRUMENTER = createInstrumenter(); @@ -36,7 +38,8 @@ public static Instrumenter instrumenterWithAttributes() { private static Instrumenter createInstrumenter() { return Instrumenter.builder( GlobalOpenTelemetry.get(), INSTRUMENTATION_NAME, WithSpanSingletons::spanNameFromMethod) - .newInstrumenter(WithSpanSingletons::spanKindFromMethod); + .addAttributesExtractor(CodeAttributesExtractor.create(MethodCodeAttributesGetter.INSTANCE)) + .buildInstrumenter(WithSpanSingletons::spanKindFromMethod); } private static Instrumenter createInstrumenterWithAttributes() { @@ -44,12 +47,14 @@ private static Instrumenter createInstrumenterWithAttribu GlobalOpenTelemetry.get(), INSTRUMENTATION_NAME, WithSpanSingletons::spanNameFromMethodRequest) + .addAttributesExtractor( + CodeAttributesExtractor.create(MethodRequestCodeAttributesGetter.INSTANCE)) .addAttributesExtractor( MethodSpanAttributesExtractor.newInstance( MethodRequest::method, WithSpanParameterAttributeNamesExtractor.INSTANCE, MethodRequest::args)) - .newInstrumenter(WithSpanSingletons::spanKindFromMethodRequest); + .buildInstrumenter(WithSpanSingletons::spanKindFromMethodRequest); } private static SpanKind spanKindFromMethodRequest(MethodRequest request) { @@ -86,4 +91,6 @@ private static String spanNameFromMethod(Method method) { } return spanName; } + + private WithSpanSingletons() {} } diff --git a/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/test/groovy/WithSpanInstrumentationTest.groovy b/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/test/groovy/WithSpanInstrumentationTest.groovy index 14fef17c9ad4..8fb31a938889 100644 --- a/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/test/groovy/WithSpanInstrumentationTest.groovy +++ b/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/test/groovy/WithSpanInstrumentationTest.groovy @@ -3,8 +3,9 @@ * SPDX-License-Identifier: Apache-2.0 */ -import io.opentelemetry.extension.annotations.WithSpan +import io.opentelemetry.instrumentation.annotations.WithSpan import io.opentelemetry.instrumentation.test.AgentInstrumentationSpecification +import io.opentelemetry.semconv.trace.attributes.SemanticAttributes import io.opentelemetry.test.annotation.TracedWithSpan import net.bytebuddy.ByteBuddy import net.bytebuddy.ClassFileVersion @@ -24,7 +25,8 @@ import static io.opentelemetry.api.trace.SpanKind.SERVER import static io.opentelemetry.api.trace.StatusCode.ERROR /** - * This test verifies that auto instrumentation supports {@link io.opentelemetry.extension.annotations.WithSpan} contrib annotation. + * This test verifies that auto instrumentation supports the + * {@link io.opentelemetry.instrumentation.annotations.WithSpan} annotation. */ class WithSpanInstrumentationTest extends AgentInstrumentationSpecification { @@ -40,6 +42,8 @@ class WithSpanInstrumentationTest extends AgentInstrumentationSpecification { kind INTERNAL hasNoParent() attributes { + "$SemanticAttributes.CODE_NAMESPACE" TracedWithSpan.name + "$SemanticAttributes.CODE_FUNCTION" "otel" } } } @@ -57,6 +61,8 @@ class WithSpanInstrumentationTest extends AgentInstrumentationSpecification { name "manualName" hasNoParent() attributes { + "$SemanticAttributes.CODE_NAMESPACE" TracedWithSpan.name + "$SemanticAttributes.CODE_FUNCTION" "namedOtel" } } } @@ -75,6 +81,8 @@ class WithSpanInstrumentationTest extends AgentInstrumentationSpecification { kind PRODUCER hasNoParent() attributes { + "$SemanticAttributes.CODE_NAMESPACE" TracedWithSpan.name + "$SemanticAttributes.CODE_FUNCTION" "someKind" } } } @@ -93,12 +101,16 @@ class WithSpanInstrumentationTest extends AgentInstrumentationSpecification { kind SERVER hasNoParent() attributes { + "$SemanticAttributes.CODE_NAMESPACE" TracedWithSpan.name + "$SemanticAttributes.CODE_FUNCTION" "server" } } span(1) { name "TracedWithSpan.otel" childOf span(0) attributes { + "$SemanticAttributes.CODE_NAMESPACE" TracedWithSpan.name + "$SemanticAttributes.CODE_FUNCTION" "otel" } } } @@ -127,6 +139,8 @@ class WithSpanInstrumentationTest extends AgentInstrumentationSpecification { kind INTERNAL hasNoParent() attributes { + "$SemanticAttributes.CODE_NAMESPACE" TracedWithSpan.name + "$SemanticAttributes.CODE_FUNCTION" "completionStage" } } } @@ -151,6 +165,8 @@ class WithSpanInstrumentationTest extends AgentInstrumentationSpecification { kind INTERNAL hasNoParent() attributes { + "$SemanticAttributes.CODE_NAMESPACE" TracedWithSpan.name + "$SemanticAttributes.CODE_FUNCTION" "completionStage" } } } @@ -173,6 +189,8 @@ class WithSpanInstrumentationTest extends AgentInstrumentationSpecification { status ERROR errorEvent(IllegalArgumentException, "Boom") attributes { + "$SemanticAttributes.CODE_NAMESPACE" TracedWithSpan.name + "$SemanticAttributes.CODE_FUNCTION" "completionStage" } } } @@ -199,6 +217,8 @@ class WithSpanInstrumentationTest extends AgentInstrumentationSpecification { status ERROR errorEvent(IllegalArgumentException, "Boom") attributes { + "$SemanticAttributes.CODE_NAMESPACE" TracedWithSpan.name + "$SemanticAttributes.CODE_FUNCTION" "completionStage" } } } @@ -217,6 +237,8 @@ class WithSpanInstrumentationTest extends AgentInstrumentationSpecification { kind INTERNAL hasNoParent() attributes { + "$SemanticAttributes.CODE_NAMESPACE" TracedWithSpan.name + "$SemanticAttributes.CODE_FUNCTION" "completionStage" } } } @@ -236,6 +258,8 @@ class WithSpanInstrumentationTest extends AgentInstrumentationSpecification { kind INTERNAL hasNoParent() attributes { + "$SemanticAttributes.CODE_NAMESPACE" TracedWithSpan.name + "$SemanticAttributes.CODE_FUNCTION" "completableFuture" } } } @@ -260,6 +284,8 @@ class WithSpanInstrumentationTest extends AgentInstrumentationSpecification { kind INTERNAL hasNoParent() attributes { + "$SemanticAttributes.CODE_NAMESPACE" TracedWithSpan.name + "$SemanticAttributes.CODE_FUNCTION" "completableFuture" } } } @@ -282,6 +308,8 @@ class WithSpanInstrumentationTest extends AgentInstrumentationSpecification { status ERROR errorEvent(IllegalArgumentException, "Boom") attributes { + "$SemanticAttributes.CODE_NAMESPACE" TracedWithSpan.name + "$SemanticAttributes.CODE_FUNCTION" "completableFuture" } } } @@ -308,6 +336,8 @@ class WithSpanInstrumentationTest extends AgentInstrumentationSpecification { status ERROR errorEvent(IllegalArgumentException, "Boom") attributes { + "$SemanticAttributes.CODE_NAMESPACE" TracedWithSpan.name + "$SemanticAttributes.CODE_FUNCTION" "completableFuture" } } } @@ -326,6 +356,8 @@ class WithSpanInstrumentationTest extends AgentInstrumentationSpecification { kind INTERNAL hasNoParent() attributes { + "$SemanticAttributes.CODE_NAMESPACE" TracedWithSpan.name + "$SemanticAttributes.CODE_FUNCTION" "completableFuture" } } } @@ -370,6 +402,8 @@ class WithSpanInstrumentationTest extends AgentInstrumentationSpecification { kind INTERNAL hasNoParent() attributes { + "$SemanticAttributes.CODE_NAMESPACE" "GeneratedJava6TestClass" + "$SemanticAttributes.CODE_FUNCTION" "run" } } span(1) { @@ -395,6 +429,8 @@ class WithSpanInstrumentationTest extends AgentInstrumentationSpecification { kind INTERNAL hasNoParent() attributes { + "$SemanticAttributes.CODE_NAMESPACE" TracedWithSpan.name + "$SemanticAttributes.CODE_FUNCTION" "withSpanAttributes" "implicitName" "foo" "explicitName" "bar" } diff --git a/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/test/java/io/opentelemetry/test/annotation/TracedWithSpan.java b/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/test/java/io/opentelemetry/test/annotation/TracedWithSpan.java index 869e40796439..3fab82c2bcea 100644 --- a/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/test/java/io/opentelemetry/test/annotation/TracedWithSpan.java +++ b/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/test/java/io/opentelemetry/test/annotation/TracedWithSpan.java @@ -6,8 +6,8 @@ package io.opentelemetry.test.annotation; import io.opentelemetry.api.trace.SpanKind; -import io.opentelemetry.extension.annotations.SpanAttribute; -import io.opentelemetry.extension.annotations.WithSpan; +import io.opentelemetry.instrumentation.annotations.SpanAttribute; +import io.opentelemetry.instrumentation.annotations.WithSpan; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; diff --git a/instrumentation/opentelemetry-instrumentation-api/testing/src/main/java/io/opentelemetry/javaagent/instrumentation/testing/AgentSpanTesting.java b/instrumentation/opentelemetry-instrumentation-api/testing/src/main/java/io/opentelemetry/javaagent/instrumentation/testing/AgentSpanTesting.java index 8b0216d1f207..081fa3384643 100644 --- a/instrumentation/opentelemetry-instrumentation-api/testing/src/main/java/io/opentelemetry/javaagent/instrumentation/testing/AgentSpanTesting.java +++ b/instrumentation/opentelemetry-instrumentation-api/testing/src/main/java/io/opentelemetry/javaagent/instrumentation/testing/AgentSpanTesting.java @@ -7,7 +7,7 @@ import io.opentelemetry.instrumentation.api.internal.SpanKey; -public class AgentSpanTesting { +public final class AgentSpanTesting { /** * Runs the provided {@code runnable} inside the scope of an SERVER span with name {@code @@ -24,4 +24,6 @@ public static void runWithHttpServerSpan(String spanName, Runnable runnable) { public static void runWithAllSpanKeys(String spanName, Runnable runnable) { runnable.run(); } + + private AgentSpanTesting() {} } diff --git a/instrumentation/opentelemetry-instrumentation-api/testing/src/main/java/io/opentelemetry/javaagent/instrumentation/testing/AgentSpanTestingInstrumenter.java b/instrumentation/opentelemetry-instrumentation-api/testing/src/main/java/io/opentelemetry/javaagent/instrumentation/testing/AgentSpanTestingInstrumenter.java index de40027e3963..009578fa42db 100644 --- a/instrumentation/opentelemetry-instrumentation-api/testing/src/main/java/io/opentelemetry/javaagent/instrumentation/testing/AgentSpanTestingInstrumenter.java +++ b/instrumentation/opentelemetry-instrumentation-api/testing/src/main/java/io/opentelemetry/javaagent/instrumentation/testing/AgentSpanTestingInstrumenter.java @@ -25,16 +25,17 @@ public final class AgentSpanTestingInstrumenter { Instrumenter.builder(GlobalOpenTelemetry.get(), "test", request -> request) .addContextCustomizer( (context, request, startAttributes) -> context.with(REQUEST_CONTEXT_KEY, request)) - .newInstrumenter(SpanKindExtractor.alwaysInternal()); + .buildInstrumenter(SpanKindExtractor.alwaysInternal()); private static final Instrumenter HTTP_SERVER_INSTRUMENTER = Instrumenter.builder(GlobalOpenTelemetry.get(), "test", request -> request) .addAttributesExtractor( - HttpServerAttributesExtractor.create(MockHttpServerAttributesGetter.INSTANCE)) + HttpServerAttributesExtractor.create( + MockHttpServerAttributesGetter.INSTANCE, MockNetServerAttributesGetter.INSTANCE)) .addContextCustomizer(HttpRouteHolder.get()) .addContextCustomizer( (context, request, startAttributes) -> context.with(REQUEST_CONTEXT_KEY, request)) - .newInstrumenter(SpanKindExtractor.alwaysServer()); + .buildInstrumenter(SpanKindExtractor.alwaysServer()); public static Context startHttpServerSpan(String name) { Context context = HTTP_SERVER_INSTRUMENTER.start(Context.current(), name); diff --git a/instrumentation/opentelemetry-instrumentation-api/testing/src/main/java/io/opentelemetry/javaagent/instrumentation/testing/MockHttpServerAttributesGetter.java b/instrumentation/opentelemetry-instrumentation-api/testing/src/main/java/io/opentelemetry/javaagent/instrumentation/testing/MockHttpServerAttributesGetter.java index 6e8165e64f0c..a5fef3c4552f 100644 --- a/instrumentation/opentelemetry-instrumentation-api/testing/src/main/java/io/opentelemetry/javaagent/instrumentation/testing/MockHttpServerAttributesGetter.java +++ b/instrumentation/opentelemetry-instrumentation-api/testing/src/main/java/io/opentelemetry/javaagent/instrumentation/testing/MockHttpServerAttributesGetter.java @@ -29,31 +29,7 @@ public List requestHeader(String s, String name) { @Nullable @Override - public Long requestContentLength(String s, @Nullable Void unused) { - return null; - } - - @Nullable - @Override - public Long requestContentLengthUncompressed(String s, @Nullable Void unused) { - return null; - } - - @Nullable - @Override - public Integer statusCode(String s, Void unused) { - return null; - } - - @Nullable - @Override - public Long responseContentLength(String s, Void unused) { - return null; - } - - @Nullable - @Override - public Long responseContentLengthUncompressed(String s, Void unused) { + public Integer statusCode(String s, Void unused, @Nullable Throwable error) { return null; } @@ -85,10 +61,4 @@ public String route(String s) { public String scheme(String s) { return null; } - - @Nullable - @Override - public String serverName(String s) { - return null; - } } diff --git a/instrumentation/opentelemetry-instrumentation-api/testing/src/main/java/io/opentelemetry/javaagent/instrumentation/testing/MockNetServerAttributesGetter.java b/instrumentation/opentelemetry-instrumentation-api/testing/src/main/java/io/opentelemetry/javaagent/instrumentation/testing/MockNetServerAttributesGetter.java new file mode 100644 index 000000000000..e49a3a5001a9 --- /dev/null +++ b/instrumentation/opentelemetry-instrumentation-api/testing/src/main/java/io/opentelemetry/javaagent/instrumentation/testing/MockNetServerAttributesGetter.java @@ -0,0 +1,32 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.testing; + +import io.opentelemetry.instrumentation.api.instrumenter.net.NetServerAttributesGetter; +import javax.annotation.Nullable; + +// only needed so that HttpServerAttributesExtractor can be added to the HTTP server instrumenter +enum MockNetServerAttributesGetter implements NetServerAttributesGetter { + INSTANCE; + + @Nullable + @Override + public String transport(String s) { + return null; + } + + @Nullable + @Override + public String hostName(String s) { + return null; + } + + @Nullable + @Override + public Integer hostPort(String s) { + return null; + } +} diff --git a/instrumentation/oracle-ucp-11.2/library/README.md b/instrumentation/oracle-ucp-11.2/library/README.md index 47fee08c9aa8..f16f2309cc22 100644 --- a/instrumentation/oracle-ucp-11.2/library/README.md +++ b/instrumentation/oracle-ucp-11.2/library/README.md @@ -1,4 +1,4 @@ -# Manual Instrumentation for Oracle UCP +# Library Instrumentation for Oracle UCP version 11.2 and higher Provides OpenTelemetry instrumentation for [Oracle UCP](https://docs.oracle.com/database/121/JJUCP/). @@ -6,13 +6,12 @@ Provides OpenTelemetry instrumentation for [Oracle UCP](https://docs.oracle.com/ ### Add these dependencies to your project: -Replace `OPENTELEMETRY_VERSION` with the latest stable -[release](https://mvnrepository.com/artifact/io.opentelemetry). `Minimum version: 1.15.0` +Replace `OPENTELEMETRY_VERSION` with the [latest +release](https://search.maven.org/search?q=g:io.opentelemetry.instrumentation%20AND%20a:opentelemetry-oracle-ucp-11.2). For Maven, add to your `pom.xml` dependencies: ```xml - io.opentelemetry.instrumentation diff --git a/instrumentation/oshi/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/oshi/OshiMetricsInstaller.java b/instrumentation/oshi/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/oshi/OshiMetricsInstaller.java index cbe641ce5391..0496c39e8122 100644 --- a/instrumentation/oshi/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/oshi/OshiMetricsInstaller.java +++ b/instrumentation/oshi/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/oshi/OshiMetricsInstaller.java @@ -10,7 +10,6 @@ import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk; import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import java.lang.reflect.Method; -import java.util.Locale; /** * An {@link AgentListener} that enables oshi metrics during agent startup if oshi is present on the @@ -23,9 +22,8 @@ public class OshiMetricsInstaller implements AgentListener { public void afterAgent(AutoConfiguredOpenTelemetrySdk autoConfiguredSdk) { ConfigProperties config = autoConfiguredSdk.getConfig(); - boolean defaultEnabled = - config.getBoolean(normalize("otel.instrumentation.common.default-enabled"), true); - if (!config.getBoolean(normalize("otel.instrumentation.oshi.enabled"), defaultEnabled)) { + boolean defaultEnabled = config.getBoolean("otel.instrumentation.common.default-enabled", true); + if (!config.getBoolean("otel.instrumentation.oshi.enabled", defaultEnabled)) { return; } @@ -50,9 +48,4 @@ private static Method getCurrentPlatformMethod(Class oshiSystemInfoClass) return oshiSystemInfoClass.getMethod("getCurrentPlatform"); } } - - // TODO: remove after https://github.com/open-telemetry/opentelemetry-java/issues/4562 is fixed - private static String normalize(String key) { - return key.toLowerCase(Locale.ROOT).replace('-', '.'); - } } diff --git a/instrumentation/oshi/library/src/main/java/io/opentelemetry/instrumentation/oshi/ProcessMetrics.java b/instrumentation/oshi/library/src/main/java/io/opentelemetry/instrumentation/oshi/ProcessMetrics.java index 3c9deba9fda6..be364b3ac18f 100644 --- a/instrumentation/oshi/library/src/main/java/io/opentelemetry/instrumentation/oshi/ProcessMetrics.java +++ b/instrumentation/oshi/library/src/main/java/io/opentelemetry/instrumentation/oshi/ProcessMetrics.java @@ -5,7 +5,6 @@ package io.opentelemetry.instrumentation.oshi; -import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.api.OpenTelemetry; import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.api.common.Attributes; @@ -20,16 +19,7 @@ public class ProcessMetrics { private ProcessMetrics() {} - /** - * Register observers for java runtime metrics. - * - * @deprecated use {@link #registerObservers(OpenTelemetry openTelemetry)} - */ - @Deprecated - public static void registerObservers() { - registerObservers(GlobalOpenTelemetry.get()); - } - + /** Register observers for java runtime metrics. */ public static void registerObservers(OpenTelemetry openTelemetry) { Meter meter = openTelemetry.getMeterProvider().get("io.opentelemetry.oshi"); SystemInfo systemInfo = new SystemInfo(); diff --git a/instrumentation/oshi/library/src/main/java/io/opentelemetry/instrumentation/oshi/SystemMetrics.java b/instrumentation/oshi/library/src/main/java/io/opentelemetry/instrumentation/oshi/SystemMetrics.java index 9ef7a696940e..ccdcf17c62a2 100644 --- a/instrumentation/oshi/library/src/main/java/io/opentelemetry/instrumentation/oshi/SystemMetrics.java +++ b/instrumentation/oshi/library/src/main/java/io/opentelemetry/instrumentation/oshi/SystemMetrics.java @@ -5,7 +5,6 @@ package io.opentelemetry.instrumentation.oshi; -import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.api.OpenTelemetry; import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.api.common.Attributes; @@ -28,16 +27,7 @@ public class SystemMetrics { private SystemMetrics() {} - /** - * Register observers for system metrics. - * - * @deprecated use {@link #registerObservers(OpenTelemetry openTelemetry)} - */ - @Deprecated - public static void registerObservers() { - registerObservers(GlobalOpenTelemetry.get()); - } - + /** Register observers for system metrics. */ public static void registerObservers(OpenTelemetry openTelemetry) { Meter meter = openTelemetry.getMeterProvider().get("io.opentelemetry.oshi"); SystemInfo systemInfo = new SystemInfo(); @@ -86,7 +76,7 @@ public static void registerObservers(OpenTelemetry openTelemetry) { meter .counterBuilder("system.network.packets") .setDescription("System network packets") - .setUnit("packets") + .setUnit("{packets}") .buildWithCallback( r -> { for (NetworkIF networkIf : hal.getNetworkIFs()) { @@ -102,7 +92,7 @@ public static void registerObservers(OpenTelemetry openTelemetry) { meter .counterBuilder("system.network.errors") .setDescription("System network errors") - .setUnit("errors") + .setUnit("{errors}") .buildWithCallback( r -> { for (NetworkIF networkIf : hal.getNetworkIFs()) { @@ -133,7 +123,7 @@ public static void registerObservers(OpenTelemetry openTelemetry) { meter .counterBuilder("system.disk.operations") .setDescription("System disk operations") - .setUnit("operations") + .setUnit("{operations}") .buildWithCallback( r -> { for (HWDiskStore diskStore : hal.getDiskStores()) { diff --git a/instrumentation/oshi/testing/src/main/java/io/opentelemetry/instrumentation/oshi/AbstractSystemMetricsTest.java b/instrumentation/oshi/testing/src/main/java/io/opentelemetry/instrumentation/oshi/AbstractSystemMetricsTest.java index 2fd86a83eec1..d62ee8e6cfe0 100644 --- a/instrumentation/oshi/testing/src/main/java/io/opentelemetry/instrumentation/oshi/AbstractSystemMetricsTest.java +++ b/instrumentation/oshi/testing/src/main/java/io/opentelemetry/instrumentation/oshi/AbstractSystemMetricsTest.java @@ -66,7 +66,7 @@ public void test() { metrics -> metrics.anySatisfy( metric -> - assertThat(metric).hasUnit("packets").hasLongSumSatisfying(sum -> {}))); + assertThat(metric).hasUnit("{packets}").hasLongSumSatisfying(sum -> {}))); testing() .waitAndAssertMetrics( "io.opentelemetry.oshi", @@ -74,7 +74,7 @@ public void test() { metrics -> metrics.anySatisfy( metric -> - assertThat(metric).hasUnit("errors").hasLongSumSatisfying(sum -> {}))); + assertThat(metric).hasUnit("{errors}").hasLongSumSatisfying(sum -> {}))); testing() .waitAndAssertMetrics( "io.opentelemetry.oshi", @@ -89,6 +89,8 @@ public void test() { metrics -> metrics.anySatisfy( metric -> - assertThat(metric).hasUnit("operations").hasLongSumSatisfying(sum -> {}))); + assertThat(metric) + .hasUnit("{operations}") + .hasLongSumSatisfying(sum -> {}))); } } diff --git a/instrumentation/play/play-mvc/play-mvc-2.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/play/v2_4/Play24Singletons.java b/instrumentation/play/play-mvc/play-mvc-2.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/play/v2_4/Play24Singletons.java index cd44112d410c..43e221afc7f8 100644 --- a/instrumentation/play/play-mvc/play-mvc-2.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/play/v2_4/Play24Singletons.java +++ b/instrumentation/play/play-mvc/play-mvc-2.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/play/v2_4/Play24Singletons.java @@ -19,7 +19,7 @@ public final class Play24Singletons { private static final Instrumenter INSTRUMENTER = Instrumenter.builder( GlobalOpenTelemetry.get(), "io.opentelemetry.play-mvc-2.4", s -> SPAN_NAME) - .newInstrumenter(); + .buildInstrumenter(); public static Instrumenter instrumenter() { return INSTRUMENTER; diff --git a/instrumentation/play/play-mvc/play-mvc-2.6/javaagent/build.gradle.kts b/instrumentation/play/play-mvc/play-mvc-2.6/javaagent/build.gradle.kts index 4d8192650621..196020f36198 100644 --- a/instrumentation/play/play-mvc/play-mvc-2.6/javaagent/build.gradle.kts +++ b/instrumentation/play/play-mvc/play-mvc-2.6/javaagent/build.gradle.kts @@ -47,7 +47,7 @@ dependencies { testInstrumentation(project(":instrumentation:netty:netty-4.0:javaagent")) testInstrumentation(project(":instrumentation:netty:netty-4.1:javaagent")) - testInstrumentation(project(":instrumentation:akka:akka-actor-2.5:javaagent")) + testInstrumentation(project(":instrumentation:akka:akka-actor-2.3:javaagent")) testInstrumentation(project(":instrumentation:akka:akka-http-10.0:javaagent")) testImplementation("com.typesafe.play:play-java_$scalaVersion:$playVersion") diff --git a/instrumentation/play/play-mvc/play-mvc-2.6/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/play/v2_6/Play26Singletons.java b/instrumentation/play/play-mvc/play-mvc-2.6/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/play/v2_6/Play26Singletons.java index f6931dd9c89d..fc1f694a6962 100644 --- a/instrumentation/play/play-mvc/play-mvc-2.6/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/play/v2_6/Play26Singletons.java +++ b/instrumentation/play/play-mvc/play-mvc-2.6/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/play/v2_6/Play26Singletons.java @@ -25,7 +25,7 @@ public final class Play26Singletons { private static final Instrumenter INSTRUMENTER = Instrumenter.builder( GlobalOpenTelemetry.get(), "io.opentelemetry.play-mvc-2.6", s -> SPAN_NAME) - .newInstrumenter(); + .buildInstrumenter(); @Nullable private static final Method typedKeyGetUnderlying; diff --git a/instrumentation/play/play-mvc/play-mvc-2.6/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/play/v2_6/ResponseFutureWrapper.java b/instrumentation/play/play-mvc/play-mvc-2.6/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/play/v2_6/ResponseFutureWrapper.java index f8d0b2542d9c..3192fff2b8b0 100644 --- a/instrumentation/play/play-mvc/play-mvc-2.6/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/play/v2_6/ResponseFutureWrapper.java +++ b/instrumentation/play/play-mvc/play-mvc-2.6/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/play/v2_6/ResponseFutureWrapper.java @@ -13,7 +13,7 @@ import scala.concurrent.Future; import scala.runtime.AbstractFunction1; -public class ResponseFutureWrapper { +public final class ResponseFutureWrapper { public static Future wrap( Future future, Context context, ExecutionContext executionContext) { @@ -35,4 +35,6 @@ public Throwable apply(Throwable throwable) { }, executionContext); } + + private ResponseFutureWrapper() {} } diff --git a/instrumentation/play/play-ws/play-ws-1.0/javaagent/build.gradle.kts b/instrumentation/play/play-ws/play-ws-1.0/javaagent/build.gradle.kts index ab208093a618..f3861210cba2 100644 --- a/instrumentation/play/play-ws/play-ws-1.0/javaagent/build.gradle.kts +++ b/instrumentation/play/play-ws/play-ws-1.0/javaagent/build.gradle.kts @@ -35,7 +35,7 @@ dependencies { testInstrumentation(project(":instrumentation:netty:netty-4.0:javaagent")) testInstrumentation(project(":instrumentation:netty:netty-4.1:javaagent")) testInstrumentation(project(":instrumentation:akka:akka-http-10.0:javaagent")) - testInstrumentation(project(":instrumentation:akka:akka-actor-2.5:javaagent")) + testInstrumentation(project(":instrumentation:akka:akka-actor-2.3:javaagent")) latestDepTestLibrary("com.typesafe.play:play-ahc-ws-standalone_$scalaVersion:1.+") } diff --git a/instrumentation/play/play-ws/play-ws-2.0/javaagent/build.gradle.kts b/instrumentation/play/play-ws/play-ws-2.0/javaagent/build.gradle.kts index aa16a3218548..6ead5f1cf68b 100644 --- a/instrumentation/play/play-ws/play-ws-2.0/javaagent/build.gradle.kts +++ b/instrumentation/play/play-ws/play-ws-2.0/javaagent/build.gradle.kts @@ -41,7 +41,7 @@ dependencies { testInstrumentation(project(":instrumentation:netty:netty-4.0:javaagent")) testInstrumentation(project(":instrumentation:netty:netty-4.1:javaagent")) testInstrumentation(project(":instrumentation:akka:akka-http-10.0:javaagent")) - testInstrumentation(project(":instrumentation:akka:akka-actor-2.5:javaagent")) + testInstrumentation(project(":instrumentation:akka:akka-actor-2.3:javaagent")) latestDepTestLibrary("com.typesafe.play:play-ahc-ws-standalone_$scalaVersion:2.0.+") } diff --git a/instrumentation/play/play-ws/play-ws-2.1/javaagent/build.gradle.kts b/instrumentation/play/play-ws/play-ws-2.1/javaagent/build.gradle.kts index 8cfb3d02b29b..92ded4cea19f 100644 --- a/instrumentation/play/play-ws/play-ws-2.1/javaagent/build.gradle.kts +++ b/instrumentation/play/play-ws/play-ws-2.1/javaagent/build.gradle.kts @@ -40,7 +40,7 @@ dependencies { testInstrumentation(project(":instrumentation:netty:netty-4.0:javaagent")) testInstrumentation(project(":instrumentation:netty:netty-4.1:javaagent")) testInstrumentation(project(":instrumentation:akka:akka-http-10.0:javaagent")) - testInstrumentation(project(":instrumentation:akka:akka-actor-2.5:javaagent")) + testInstrumentation(project(":instrumentation:akka:akka-actor-2.3:javaagent")) latestDepTestLibrary("com.typesafe.play:play-ahc-ws-standalone_2.13:+") } diff --git a/instrumentation/play/play-ws/play-ws-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/playws/PlayWsClientHttpAttributesGetter.java b/instrumentation/play/play-ws/play-ws-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/playws/PlayWsClientHttpAttributesGetter.java index 64c4bfac3f81..f6cf8a064286 100644 --- a/instrumentation/play/play-ws/play-ws-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/playws/PlayWsClientHttpAttributesGetter.java +++ b/instrumentation/play/play-ws/play-ws-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/playws/PlayWsClientHttpAttributesGetter.java @@ -31,19 +31,7 @@ public List requestHeader(Request request, String name) { } @Override - @Nullable - public Long requestContentLength(Request request, @Nullable Response response) { - return null; - } - - @Override - @Nullable - public Long requestContentLengthUncompressed(Request request, @Nullable Response response) { - return null; - } - - @Override - public Integer statusCode(Request request, Response response) { + public Integer statusCode(Request request, Response response, @Nullable Throwable error) { return response.getStatusCode(); } @@ -52,18 +40,6 @@ public String flavor(Request request, @Nullable Response response) { return SemanticAttributes.HttpFlavorValues.HTTP_1_1; } - @Override - @Nullable - public Long responseContentLength(Request request, Response response) { - return null; - } - - @Override - @Nullable - public Long responseContentLengthUncompressed(Request request, Response response) { - return null; - } - @Override public List responseHeader(Request request, Response response, String name) { return response.getHeaders().getAll(name); diff --git a/instrumentation/play/play-ws/play-ws-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/playws/PlayWsClientInstrumenterFactory.java b/instrumentation/play/play-ws/play-ws-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/playws/PlayWsClientInstrumenterFactory.java index b0b14dff8a98..fd2b3f18d81c 100644 --- a/instrumentation/play/play-ws/play-ws-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/playws/PlayWsClientInstrumenterFactory.java +++ b/instrumentation/play/play-ws/play-ws-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/playws/PlayWsClientInstrumenterFactory.java @@ -7,12 +7,13 @@ import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; -import io.opentelemetry.instrumentation.api.instrumenter.PeerServiceAttributesExtractor; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpClientAttributesExtractor; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpClientMetrics; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanNameExtractor; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanStatusExtractor; import io.opentelemetry.instrumentation.api.instrumenter.net.NetClientAttributesExtractor; +import io.opentelemetry.instrumentation.api.instrumenter.net.PeerServiceAttributesExtractor; +import io.opentelemetry.javaagent.bootstrap.internal.CommonConfig; import play.shaded.ahc.org.asynchttpclient.Request; import play.shaded.ahc.org.asynchttpclient.Response; @@ -27,11 +28,17 @@ public static Instrumenter createInstrumenter(String instrume instrumentationName, HttpSpanNameExtractor.create(httpAttributesGetter)) .setSpanStatusExtractor(HttpSpanStatusExtractor.create(httpAttributesGetter)) - .addAttributesExtractor(HttpClientAttributesExtractor.create(httpAttributesGetter)) + .addAttributesExtractor( + HttpClientAttributesExtractor.builder(httpAttributesGetter) + .setCapturedRequestHeaders(CommonConfig.get().getClientRequestHeaders()) + .setCapturedResponseHeaders(CommonConfig.get().getClientResponseHeaders()) + .build()) .addAttributesExtractor(NetClientAttributesExtractor.create(netAttributesGetter)) - .addAttributesExtractor(PeerServiceAttributesExtractor.create(netAttributesGetter)) + .addAttributesExtractor( + PeerServiceAttributesExtractor.create( + netAttributesGetter, CommonConfig.get().getPeerServiceMapping())) .addOperationMetrics(HttpClientMetrics.get()) - .newClientInstrumenter(HttpHeaderSetter.INSTANCE); + .buildClientInstrumenter(HttpHeaderSetter.INSTANCE); } private PlayWsClientInstrumenterFactory() {} diff --git a/instrumentation/play/play-ws/play-ws-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/playws/PlayWsClientNetAttributesGetter.java b/instrumentation/play/play-ws/play-ws-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/playws/PlayWsClientNetAttributesGetter.java index 96df0b34088f..59b17fa0b97a 100644 --- a/instrumentation/play/play-ws/play-ws-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/playws/PlayWsClientNetAttributesGetter.java +++ b/instrumentation/play/play-ws/play-ws-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/playws/PlayWsClientNetAttributesGetter.java @@ -20,9 +20,20 @@ public String transport(Request request, @Nullable Response response) { return SemanticAttributes.NetTransportValues.IP_TCP; } + @Nullable + @Override + public String peerName(Request request) { + return request.getUri().getHost(); + } + + @Override + public Integer peerPort(Request request) { + return request.getUri().getPort(); + } + @Override @Nullable - public InetSocketAddress getAddress(Request request, @Nullable Response response) { + protected InetSocketAddress getPeerSocketAddress(Request request, @Nullable Response response) { if (response != null && response.getRemoteAddress() instanceof InetSocketAddress) { return (InetSocketAddress) response.getRemoteAddress(); } diff --git a/instrumentation/pubsub-1.0/javaagent/build.gradle.kts b/instrumentation/pubsub-1.0/javaagent/build.gradle.kts new file mode 100644 index 000000000000..10f3c27dcb27 --- /dev/null +++ b/instrumentation/pubsub-1.0/javaagent/build.gradle.kts @@ -0,0 +1,11 @@ +plugins { + id("otel.javaagent-instrumentation") +} + +muzzle { +} + +dependencies { + + library("com.google.cloud:google-cloud-pubsub:1.120.21") +} diff --git a/instrumentation/pubsub-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/pubsub/PubSubAttributesMapGetter.java b/instrumentation/pubsub-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/pubsub/PubSubAttributesMapGetter.java new file mode 100644 index 000000000000..fac42dfbc377 --- /dev/null +++ b/instrumentation/pubsub-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/pubsub/PubSubAttributesMapGetter.java @@ -0,0 +1,34 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.pubsub; + +import com.google.pubsub.v1.PubsubMessage; +import io.opentelemetry.context.propagation.TextMapGetter; +import java.util.Map; +import javax.annotation.Nullable; + +public enum PubSubAttributesMapGetter implements TextMapGetter { + INSTANCE; + + @Override + public Iterable keys(PubsubMessage carrier) { + return null; + } + + @Nullable + @Override + public String get(@Nullable PubsubMessage carrier, String key) { + if (carrier == null) { + return null; + } + Map headers = carrier.getAttributesMap(); + if (headers == null) { + return null; + } + Object obj = headers.get(key); + return obj == null ? null : obj.toString(); + } +} diff --git a/instrumentation/pubsub-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/pubsub/PubSubAttributesMapSetter.java b/instrumentation/pubsub-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/pubsub/PubSubAttributesMapSetter.java new file mode 100644 index 000000000000..2eba52f1ce58 --- /dev/null +++ b/instrumentation/pubsub-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/pubsub/PubSubAttributesMapSetter.java @@ -0,0 +1,25 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.pubsub; + +import com.google.pubsub.v1.PubsubMessage; +import io.opentelemetry.context.propagation.TextMapSetter; +import java.util.Map; +import java.util.Optional; + +public enum PubSubAttributesMapSetter implements TextMapSetter { + INSTANCE; + + @Override + public void set(PubsubMessage carrier, String key, String value) { + Object carrierAsMap = PubsubSingletons.extractPubsubMessageAttributes(carrier); + if (!(carrierAsMap instanceof Optional)) { + @SuppressWarnings("unchecked") + Map newAttributes = (Map) carrierAsMap; + newAttributes.put(key, value); + } + } +} diff --git a/instrumentation/pubsub-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/pubsub/PubsubIgnoredTypesConfigurer.java b/instrumentation/pubsub-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/pubsub/PubsubIgnoredTypesConfigurer.java new file mode 100644 index 000000000000..4bae27b7ad35 --- /dev/null +++ b/instrumentation/pubsub-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/pubsub/PubsubIgnoredTypesConfigurer.java @@ -0,0 +1,20 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.pubsub; + +import com.google.auto.service.AutoService; +import io.opentelemetry.javaagent.extension.ignore.IgnoredTypesBuilder; +import io.opentelemetry.javaagent.extension.ignore.IgnoredTypesConfigurer; +import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; + +@AutoService(IgnoredTypesConfigurer.class) +public class PubsubIgnoredTypesConfigurer implements IgnoredTypesConfigurer { + + @Override + public void configure(IgnoredTypesBuilder builder, ConfigProperties config) { + builder.allowClass("com.google.cloud.pubsub.v1.Publisher"); + } +} diff --git a/instrumentation/pubsub-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/pubsub/PubsubInstrumentationModule.java b/instrumentation/pubsub-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/pubsub/PubsubInstrumentationModule.java new file mode 100644 index 000000000000..69b5afbffaa0 --- /dev/null +++ b/instrumentation/pubsub-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/pubsub/PubsubInstrumentationModule.java @@ -0,0 +1,30 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.pubsub; + +import com.google.auto.service.AutoService; +import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule; +import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; +import java.util.Arrays; +import java.util.List; + +@AutoService(InstrumentationModule.class) +public final class PubsubInstrumentationModule extends InstrumentationModule { + public PubsubInstrumentationModule() { + super(PubsubSingletons.instrumentationName, "pubsub-1.0"); + } + + @Override + public boolean isHelperClass(String className) { + return className.startsWith("com.opentelemetry.javaagent.instrumentation.pubsub"); + } + + @Override + public List typeInstrumentations() { + return Arrays.asList( + new PubsubPublisherInstrumentation(), new PubsubSubscriberInstrumentation()); + } +} diff --git a/instrumentation/pubsub-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/pubsub/PubsubPublisherInstrumentation.java b/instrumentation/pubsub-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/pubsub/PubsubPublisherInstrumentation.java new file mode 100644 index 000000000000..532252897ff8 --- /dev/null +++ b/instrumentation/pubsub-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/pubsub/PubsubPublisherInstrumentation.java @@ -0,0 +1,45 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.pubsub; + +import static io.opentelemetry.javaagent.instrumentation.pubsub.PubsubSingletons.startAndInjectSpan; +import static net.bytebuddy.matcher.ElementMatchers.isPublic; +import static net.bytebuddy.matcher.ElementMatchers.named; + +import com.google.cloud.pubsub.v1.Publisher; +import com.google.pubsub.v1.PubsubMessage; +import io.opentelemetry.context.Context; +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; + +public class PubsubPublisherInstrumentation implements TypeInstrumentation { + @Override + public ElementMatcher typeMatcher() { + return named("com.google.cloud.pubsub.v1.Publisher"); + } + + @Override + public void transform(TypeTransformer typeTransformer) { + typeTransformer.applyAdviceToMethod( + isPublic().and(named("publish")), + this.getClass().getName() + "$PubsubPublisherAddAttributesAdvice"); + } + + @SuppressWarnings("unused") + public static class PubsubPublisherAddAttributesAdvice { + @Advice.OnMethodEnter(suppress = Throwable.class) + public static void onEnterHandle( + @Advice.This Publisher publisher, + @Advice.Argument(value = 0, readOnly = false) PubsubMessage pubsubMessage) { + Context parentContext = Java8BytecodeBridge.currentContext(); + startAndInjectSpan(parentContext, pubsubMessage, publisher); + } + } +} diff --git a/instrumentation/pubsub-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/pubsub/PubsubSingletons.java b/instrumentation/pubsub-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/pubsub/PubsubSingletons.java new file mode 100644 index 000000000000..1e27e0ab8ec0 --- /dev/null +++ b/instrumentation/pubsub-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/pubsub/PubsubSingletons.java @@ -0,0 +1,168 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.pubsub; + +import com.google.cloud.pubsub.v1.AckReplyConsumer; +import com.google.cloud.pubsub.v1.Publisher; +import com.google.pubsub.v1.PubsubMessage; +import io.opentelemetry.api.GlobalOpenTelemetry; +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.context.Context; +import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; +import io.opentelemetry.instrumentation.api.instrumenter.SpanNameExtractor; +import io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge; +import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; +import java.lang.reflect.Field; +import java.nio.charset.StandardCharsets; +import java.util.Optional; + +public class PubsubSingletons { + + private PubsubSingletons() {} + + private static final String MESSAGE_PAYLOAD_ATTRIBUTE = "messaging.payload"; + private static final String ATTRIBUTES_FIELD_NAME = "attributes_"; + + private static final String ACK_PROCESSOR_FIELD_NAME = "ackProcessor"; + + private static final String THIS_MESSAGE_DISPATCHER_FIELD_NAME = "this$0"; + + private static final String SUBSCRIPTION_PATH_FIELD_NAME = "subscription"; + private static final String MAP_DATA_FIELD_NAME = "mapData"; + private static final String DELEGATE_DATA_FIELD_NAME = "delegate"; + + public static final String instrumentationName = "io.opentelemetry.pubsub-1.0"; + + public static final String publisherSpanName = "pubsub.publish"; + public static final String subscriberSpanName = "pubsub.subscribe"; + private static final Instrumenter publisherInstrumenter; + private static final Instrumenter subscriberInstrumenter; + + static { + publisherInstrumenter = createPublisherInstrumenter(); + subscriberInstrumenter = createSubscriberInstrumenter(); + } + + public static Instrumenter publisherInstrumenter() { + return publisherInstrumenter; + } + + private static Instrumenter createPublisherInstrumenter() { + SpanNameExtractor publisherSpanNameExtractor = o -> publisherSpanName; + + return Instrumenter.builder( + GlobalOpenTelemetry.get(), instrumentationName, publisherSpanNameExtractor) + .buildProducerInstrumenter(PubSubAttributesMapSetter.INSTANCE); + } + + public static Instrumenter createSubscriberInstrumenter() { + + SpanNameExtractor subscriberSpanNameExtractor = o -> subscriberSpanName; + + return Instrumenter.builder( + GlobalOpenTelemetry.get(), instrumentationName, subscriberSpanNameExtractor) + .buildConsumerInstrumenter(PubSubAttributesMapGetter.INSTANCE); + } + + public static void startAndInjectSpan( + Context parentContext, PubsubMessage pubsubMessage, Publisher publisher) { + if (!publisherInstrumenter().shouldStart(parentContext, pubsubMessage)) { + return; + } + + Context context = publisherInstrumenter().start(parentContext, pubsubMessage); + Span span = Java8BytecodeBridge.spanFromContext(context); + span.setAttribute( + MESSAGE_PAYLOAD_ATTRIBUTE, + new String(pubsubMessage.getData().toByteArray(), StandardCharsets.UTF_8)); + span.setAttribute(SemanticAttributes.MESSAGING_SYSTEM, "pubsub"); + span.setAttribute(SemanticAttributes.MESSAGING_DESTINATION_KIND, "topic"); + span.setAttribute(SemanticAttributes.MESSAGING_DESTINATION, publisher.getTopicNameString()); + + GlobalOpenTelemetry.get() + .getPropagators() + .getTextMapPropagator() + .inject(context, pubsubMessage, PubSubAttributesMapSetter.INSTANCE); + publisherInstrumenter().end(context, pubsubMessage, null, null); + } + + public static void buildAndFinishSpan( + Context context, PubsubMessage pubsubMessage, AckReplyConsumer consumer) { + Context linkedContext = + GlobalOpenTelemetry.get() + .getPropagators() + .getTextMapPropagator() + .extract(context, pubsubMessage, PubSubAttributesMapGetter.INSTANCE); + Context newContext = context.with(Span.fromContext(linkedContext)); + + if (!subscriberInstrumenter.shouldStart(newContext, pubsubMessage)) { + return; + } + Context current = subscriberInstrumenter.start(newContext, pubsubMessage); + Span span = Java8BytecodeBridge.spanFromContext(current); + span.setAttribute( + MESSAGE_PAYLOAD_ATTRIBUTE, + new String(pubsubMessage.getData().toByteArray(), StandardCharsets.UTF_8)); + span.setAttribute(SemanticAttributes.MESSAGING_SYSTEM, "pubsub"); + span.setAttribute(SemanticAttributes.MESSAGING_DESTINATION_KIND, "topic"); + + String subscriptionPath = (String) extractSubscriptionPath(consumer); + span.setAttribute(SemanticAttributes.MESSAGING_DESTINATION, subscriptionPath); + + subscriberInstrumenter.end(current, pubsubMessage, null, null); + } + + public static Object extractPubsubMessageAttributes(PubsubMessage pubsubMessage) { + Object attributesObject = extractAttributeFromObject(pubsubMessage, ATTRIBUTES_FIELD_NAME); + if (attributesObject != null) { + Object mapDataObject = extractAttributeFromObject(attributesObject, MAP_DATA_FIELD_NAME); + if (mapDataObject != null) { + Object delegate = extractAttributeFromObject(mapDataObject, DELEGATE_DATA_FIELD_NAME); + if (delegate != null) { + return delegate; + } + } + } + + return Optional.empty(); + } + + public static Object extractSubscriptionPath(AckReplyConsumer consumer) { + Object thisMessageDispatcher = + extractAttributeFromObject(consumer, THIS_MESSAGE_DISPATCHER_FIELD_NAME); + if (thisMessageDispatcher != null) { + Object ackProcessor = + extractAttributeFromObject(thisMessageDispatcher, ACK_PROCESSOR_FIELD_NAME); + if (ackProcessor != null) { + Object subscriptionPath = + extractAttributeFromObject(ackProcessor, SUBSCRIPTION_PATH_FIELD_NAME); + if (subscriptionPath != null) { + return subscriptionPath; + } + } + } + + return Optional.empty(); + } + + private static Object extractAttributeFromObject(Object object, String fieldName) { + try { + Class cls = object.getClass(); + Field field = cls.getDeclaredField(fieldName); + field.setAccessible(true); + return field.get(object); + } catch (Exception e) { + System.out.println( + "Got Exception while trying to extract attribute for object: " + + object.getClass().getName() + + " for attribute: " + + fieldName + + " exception: " + + e); + return null; + } + } +} diff --git a/instrumentation/pubsub-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/pubsub/PubsubSubscriberInstrumentation.java b/instrumentation/pubsub-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/pubsub/PubsubSubscriberInstrumentation.java new file mode 100644 index 000000000000..90a5fa252adb --- /dev/null +++ b/instrumentation/pubsub-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/pubsub/PubsubSubscriberInstrumentation.java @@ -0,0 +1,50 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.pubsub; + +import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed; +import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.implementsInterface; +import static net.bytebuddy.matcher.ElementMatchers.isPublic; +import static net.bytebuddy.matcher.ElementMatchers.named; + +import com.google.cloud.pubsub.v1.AckReplyConsumer; +import com.google.pubsub.v1.PubsubMessage; +import io.opentelemetry.context.Context; +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; + +public class PubsubSubscriberInstrumentation implements TypeInstrumentation { + + @Override + public ElementMatcher classLoaderOptimization() { + return hasClassesNamed("com.google.cloud.pubsub.v1.MessageReceiver"); + } + + @Override + public ElementMatcher typeMatcher() { + return implementsInterface(named("com.google.cloud.pubsub.v1.MessageReceiver")); + } + + @Override + public void transform(TypeTransformer typeTransformer) { + typeTransformer.applyAdviceToMethod( + isPublic().and(named("receiveMessage")), + this.getClass().getName() + "$PubsubSubscriberAddAttributesAdvice"); + } + + @SuppressWarnings("unused") + public static class PubsubSubscriberAddAttributesAdvice { + @Advice.OnMethodEnter(suppress = Throwable.class) + public static void onEnterHandle( + @Advice.Argument(value = 0, readOnly = false) PubsubMessage pubsubMessage, + @Advice.Argument(value = 1, readOnly = false) AckReplyConsumer consumer) { + PubsubSingletons.buildAndFinishSpan(Context.current(), pubsubMessage, consumer); + } + } +} diff --git a/instrumentation/pubsub-2.0/javaagent/build.gradle.kts b/instrumentation/pubsub-2.0/javaagent/build.gradle.kts new file mode 100644 index 000000000000..3ae88d8b9179 --- /dev/null +++ b/instrumentation/pubsub-2.0/javaagent/build.gradle.kts @@ -0,0 +1,11 @@ +plugins { + id("otel.javaagent-instrumentation") +} + +muzzle { +} + +dependencies { + + library("com.google.cloud:google-cloud-pubsub:1.101.0") +} diff --git a/instrumentation/pubsub-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/pubsub/PubSubAttributesMapGetter.java b/instrumentation/pubsub-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/pubsub/PubSubAttributesMapGetter.java new file mode 100644 index 000000000000..fac42dfbc377 --- /dev/null +++ b/instrumentation/pubsub-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/pubsub/PubSubAttributesMapGetter.java @@ -0,0 +1,34 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.pubsub; + +import com.google.pubsub.v1.PubsubMessage; +import io.opentelemetry.context.propagation.TextMapGetter; +import java.util.Map; +import javax.annotation.Nullable; + +public enum PubSubAttributesMapGetter implements TextMapGetter { + INSTANCE; + + @Override + public Iterable keys(PubsubMessage carrier) { + return null; + } + + @Nullable + @Override + public String get(@Nullable PubsubMessage carrier, String key) { + if (carrier == null) { + return null; + } + Map headers = carrier.getAttributesMap(); + if (headers == null) { + return null; + } + Object obj = headers.get(key); + return obj == null ? null : obj.toString(); + } +} diff --git a/instrumentation/pubsub-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/pubsub/PubSubAttributesMapSetter.java b/instrumentation/pubsub-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/pubsub/PubSubAttributesMapSetter.java new file mode 100644 index 000000000000..026b781210d8 --- /dev/null +++ b/instrumentation/pubsub-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/pubsub/PubSubAttributesMapSetter.java @@ -0,0 +1,36 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.pubsub; + +import com.google.pubsub.v1.PubsubMessage; +import io.opentelemetry.context.propagation.TextMapSetter; +import java.lang.reflect.Field; +import java.util.Map; + +public enum PubSubAttributesMapSetter implements TextMapSetter { + INSTANCE; + + @Override + public void set(PubsubMessage carrier, String key, String value) { + try { + Class cls = carrier.getClass(); + Field attributes = cls.getDeclaredField("attributes_"); + attributes.setAccessible(true); + Class attributesClass = attributes.get(carrier).getClass(); + Field mapData = attributesClass.getDeclaredField("mapData"); + mapData.setAccessible(true); + Class mapDataObj = mapData.get(attributes.get(carrier)).getClass(); + + Field delegateField = mapDataObj.getDeclaredField("delegate"); + delegateField.setAccessible(true); + Object delegate = delegateField.get(mapData.get(attributes.get(carrier))); + Map newAttributes = (Map) delegate; + newAttributes.put(key, value); + } catch (Exception e) { + System.out.println("Got Exception while instrumenting pubsubMessage: " + e); + } + } +} diff --git a/instrumentation/pubsub-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/pubsub/PubsubIgnoredTypesConfigurer.java b/instrumentation/pubsub-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/pubsub/PubsubIgnoredTypesConfigurer.java new file mode 100644 index 000000000000..8a9cd1f27d29 --- /dev/null +++ b/instrumentation/pubsub-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/pubsub/PubsubIgnoredTypesConfigurer.java @@ -0,0 +1,20 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.pubsub; + +import com.google.auto.service.AutoService; +import io.opentelemetry.javaagent.extension.ignore.IgnoredTypesBuilder; +import io.opentelemetry.javaagent.extension.ignore.IgnoredTypesConfigurer; +import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; + +@AutoService(IgnoredTypesConfigurer.class) +public class PubsubIgnoredTypesConfigurer implements IgnoredTypesConfigurer { + + @Override + public void configure(ConfigProperties config, IgnoredTypesBuilder builder) { + builder.allowClass("com.google.cloud.pubsub.v1.Publisher"); + } +} diff --git a/instrumentation/pubsub-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/pubsub/PubsubInstrumentationModule.java b/instrumentation/pubsub-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/pubsub/PubsubInstrumentationModule.java new file mode 100644 index 000000000000..7142ab500351 --- /dev/null +++ b/instrumentation/pubsub-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/pubsub/PubsubInstrumentationModule.java @@ -0,0 +1,30 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.pubsub; + +import com.google.auto.service.AutoService; +import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule; +import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; +import java.util.Arrays; +import java.util.List; + +@AutoService(InstrumentationModule.class) +public final class PubsubInstrumentationModule extends InstrumentationModule { + public PubsubInstrumentationModule() { + super(PubsubSingletons.instrumentationName, "pubsub-1.101.0"); + } + + @Override + public boolean isHelperClass(String className) { + return className.startsWith("com.opentelemetry.javaagent.instrumentation.pubsub"); + } + + @Override + public List typeInstrumentations() { + return Arrays.asList( + new PubsubPublisherInstrumentation(), new PubsubSubscriberInstrumentation()); + } +} diff --git a/instrumentation/pubsub-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/pubsub/PubsubPublisherInstrumentation.java b/instrumentation/pubsub-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/pubsub/PubsubPublisherInstrumentation.java new file mode 100644 index 000000000000..c8f6a4c3cd7b --- /dev/null +++ b/instrumentation/pubsub-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/pubsub/PubsubPublisherInstrumentation.java @@ -0,0 +1,43 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.pubsub; + +import static io.opentelemetry.javaagent.instrumentation.pubsub.PubsubSingletons.startAndInjectSpan; +import static net.bytebuddy.matcher.ElementMatchers.isPublic; +import static net.bytebuddy.matcher.ElementMatchers.named; + +import com.google.pubsub.v1.PubsubMessage; +import io.opentelemetry.context.Context; +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; + +public class PubsubPublisherInstrumentation implements TypeInstrumentation { + @Override + public ElementMatcher typeMatcher() { + return named("com.google.cloud.pubsub.v1.Publisher"); + } + + @Override + public void transform(TypeTransformer typeTransformer) { + typeTransformer.applyAdviceToMethod( + isPublic().and(named("publish")), + this.getClass().getName() + "$PubsubPublisherAddAttributesAdvice"); + } + + @SuppressWarnings("unused") + public static class PubsubPublisherAddAttributesAdvice { + @Advice.OnMethodEnter(suppress = Throwable.class) + public static void onEnterHandle( + @Advice.Argument(value = 0, readOnly = false) PubsubMessage pubsubMessage) { + Context parentContext = Java8BytecodeBridge.currentContext(); + startAndInjectSpan(parentContext, pubsubMessage); + } + } +} diff --git a/instrumentation/pubsub-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/pubsub/PubsubSingletons.java b/instrumentation/pubsub-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/pubsub/PubsubSingletons.java new file mode 100644 index 000000000000..1bae16a00998 --- /dev/null +++ b/instrumentation/pubsub-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/pubsub/PubsubSingletons.java @@ -0,0 +1,101 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.pubsub; + +import com.google.pubsub.v1.PubsubMessage; +import io.opentelemetry.api.GlobalOpenTelemetry; +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.context.Context; +import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; +import io.opentelemetry.instrumentation.api.instrumenter.SpanKindExtractor; +import io.opentelemetry.instrumentation.api.instrumenter.SpanNameExtractor; +import io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge; +import java.util.HashMap; +import java.util.Map; + +public class PubsubSingletons { + + private PubsubSingletons() {} + + public static final String instrumentationName = "io.opentelemetry.pubsub-1.101.0"; + + public static final String publisherSpanName = "pubsub.publish"; + public static final String subscriberSpanName = "pubsub.subscribe"; + private static final Instrumenter publisherInstrumenter; + private static final Instrumenter subscriberInstrumenter; + + static { + publisherInstrumenter = createPublisherInstrumenter(); + subscriberInstrumenter = createSubscriberInstrumenter(); + } + + public static Instrumenter publisherInstrumenter() { + return publisherInstrumenter; + } + + private static Instrumenter createPublisherInstrumenter() { + SpanNameExtractor publisherSpanNameExtractor = + new SpanNameExtractor() { + @Override + public String extract(Object o) { + return publisherSpanName; + } + }; + + return Instrumenter.builder( + GlobalOpenTelemetry.get(), instrumentationName, publisherSpanNameExtractor) + .newInstrumenter(SpanKindExtractor.alwaysProducer()); + } + + public static Instrumenter createSubscriberInstrumenter() { + + SpanNameExtractor subscriberSpanNameExtractor = + new SpanNameExtractor() { + @Override + public String extract(Object o) { + return subscriberSpanName; + } + }; + + return Instrumenter.builder( + GlobalOpenTelemetry.get(), instrumentationName, subscriberSpanNameExtractor) + .newInstrumenter(SpanKindExtractor.alwaysConsumer()); + } + + public static void startAndInjectSpan(Context parentContext, PubsubMessage pubsubMessage) { + if (!publisherInstrumenter().shouldStart(parentContext, pubsubMessage)) { + return; + } + + Map newAttrMap = new HashMap<>(); + newAttrMap.putAll(pubsubMessage.getAttributesMap()); + + Context context = publisherInstrumenter().start(parentContext, pubsubMessage); + Span span = Java8BytecodeBridge.spanFromContext(context); + span.setAttribute("messaging.payload", new String(pubsubMessage.getData().toByteArray())); + GlobalOpenTelemetry.get() + .getPropagators() + .getTextMapPropagator() + .inject(context, pubsubMessage, PubSubAttributesMapSetter.INSTANCE); + publisherInstrumenter().end(context, pubsubMessage, null, null); + } + + public static void buildAndFinishSpan(Context context, PubsubMessage pubsubMessage) { + + Context linkedContext = + GlobalOpenTelemetry.get() + .getPropagators() + .getTextMapPropagator() + .extract(context, pubsubMessage, PubSubAttributesMapGetter.INSTANCE); + Context newContext = context.with(Span.fromContext(linkedContext)); + + if (!subscriberInstrumenter.shouldStart(newContext, pubsubMessage)) { + return; + } + Context current = subscriberInstrumenter.start(newContext, pubsubMessage); + subscriberInstrumenter.end(current, pubsubMessage, null, null); + } +} diff --git a/instrumentation/pubsub-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/pubsub/PubsubSubscriberInstrumentation.java b/instrumentation/pubsub-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/pubsub/PubsubSubscriberInstrumentation.java new file mode 100644 index 000000000000..133cac4566d9 --- /dev/null +++ b/instrumentation/pubsub-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/pubsub/PubsubSubscriberInstrumentation.java @@ -0,0 +1,49 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.pubsub; + +import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed; +import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.implementsInterface; +import static net.bytebuddy.matcher.ElementMatchers.isPublic; +import static net.bytebuddy.matcher.ElementMatchers.named; + +import com.google.pubsub.v1.PubsubMessage; +import io.opentelemetry.context.Context; +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; + +public class PubsubSubscriberInstrumentation implements TypeInstrumentation { + + @Override + public ElementMatcher classLoaderOptimization() { + return hasClassesNamed("com.google.cloud.pubsub.v1.MessageReceiver"); + } + + @Override + public ElementMatcher typeMatcher() { + return implementsInterface(named("com.google.cloud.pubsub.v1.MessageReceiver")); + } + + @Override + public void transform(TypeTransformer typeTransformer) { + typeTransformer.applyAdviceToMethod( + isPublic().and(named("receiveMessage")), + this.getClass().getName() + "$PubsubSubscriberAddAttributesAdvice"); + } + + @SuppressWarnings("unused") + public static class PubsubSubscriberAddAttributesAdvice { + @Advice.OnMethodEnter(suppress = Throwable.class) + public static void onEnterHandle( + @Advice.Argument(value = 0, readOnly = false) PubsubMessage pubsubMessage) { + System.out.println("got here !"); + PubsubSingletons.buildAndFinishSpan(Context.current(), pubsubMessage); + } + } +} diff --git a/instrumentation/quartz-2.0/javaagent/build.gradle.kts b/instrumentation/quartz-2.0/javaagent/build.gradle.kts index 31f78f992836..a67a4b420265 100644 --- a/instrumentation/quartz-2.0/javaagent/build.gradle.kts +++ b/instrumentation/quartz-2.0/javaagent/build.gradle.kts @@ -18,3 +18,8 @@ dependencies { testImplementation(project(":instrumentation:quartz-2.0:testing")) } + +tasks.withType().configureEach { + // TODO run tests both with and without experimental span attributes + jvmArgs("-Dotel.instrumentation.quartz.experimental-span-attributes=true") +} diff --git a/instrumentation/quartz-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/quartz/v2_0/QuartzIgnoredTypesConfigurer.java b/instrumentation/quartz-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/quartz/v2_0/QuartzIgnoredTypesConfigurer.java index 033a41755607..9eb02928b99c 100644 --- a/instrumentation/quartz-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/quartz/v2_0/QuartzIgnoredTypesConfigurer.java +++ b/instrumentation/quartz-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/quartz/v2_0/QuartzIgnoredTypesConfigurer.java @@ -14,7 +14,7 @@ public class QuartzIgnoredTypesConfigurer implements IgnoredTypesConfigurer { @Override - public void configure(ConfigProperties config, IgnoredTypesBuilder builder) { + public void configure(IgnoredTypesBuilder builder, ConfigProperties config) { // Quartz executes jobs themselves in a synchronous way, there's no reason to propagate context // between its scheduler threads. builder.ignoreTaskClass("org.quartz"); diff --git a/instrumentation/quartz-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/quartz/v2_0/QuartzSingletons.java b/instrumentation/quartz-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/quartz/v2_0/QuartzSingletons.java index edeb75312a55..f467c4d40acf 100644 --- a/instrumentation/quartz-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/quartz/v2_0/QuartzSingletons.java +++ b/instrumentation/quartz-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/quartz/v2_0/QuartzSingletons.java @@ -7,10 +7,16 @@ import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.instrumentation.quartz.v2_0.QuartzTelemetry; +import io.opentelemetry.javaagent.bootstrap.internal.InstrumentationConfig; public final class QuartzSingletons { - public static final QuartzTelemetry TELEMETRY = QuartzTelemetry.create(GlobalOpenTelemetry.get()); + public static final QuartzTelemetry TELEMETRY = + QuartzTelemetry.builder(GlobalOpenTelemetry.get()) + .setCaptureExperimentalSpanAttributes( + InstrumentationConfig.get() + .getBoolean("otel.instrumentation.quartz.experimental-span-attributes", false)) + .build(); private QuartzSingletons() {} } diff --git a/instrumentation/quartz-2.0/library/src/main/java/io/opentelemetry/instrumentation/quartz/v2_0/QuartzTelemetryBuilder.java b/instrumentation/quartz-2.0/library/src/main/java/io/opentelemetry/instrumentation/quartz/v2_0/QuartzTelemetryBuilder.java index aa471f968737..7ad1cfef9659 100644 --- a/instrumentation/quartz-2.0/library/src/main/java/io/opentelemetry/instrumentation/quartz/v2_0/QuartzTelemetryBuilder.java +++ b/instrumentation/quartz-2.0/library/src/main/java/io/opentelemetry/instrumentation/quartz/v2_0/QuartzTelemetryBuilder.java @@ -5,7 +5,9 @@ package io.opentelemetry.instrumentation.quartz.v2_0; +import com.google.errorprone.annotations.CanIgnoreReturnValue; import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; import io.opentelemetry.instrumentation.api.instrumenter.InstrumenterBuilder; @@ -24,6 +26,8 @@ public final class QuartzTelemetryBuilder { private final List> additionalExtractors = new ArrayList<>(); + private boolean captureExperimentalSpanAttributes; + QuartzTelemetryBuilder(OpenTelemetry openTelemetry) { this.openTelemetry = openTelemetry; } @@ -32,12 +36,25 @@ public final class QuartzTelemetryBuilder { * Adds an additional {@link AttributesExtractor} to invoke to set attributes to instrumented * items. The {@link AttributesExtractor} will be executed after all default extractors. */ + @CanIgnoreReturnValue public QuartzTelemetryBuilder addAttributeExtractor( AttributesExtractor attributesExtractor) { additionalExtractors.add(attributesExtractor); return this; } + /** + * Sets whether experimental attributes should be set to spans. These attributes may be changed or + * removed in the future, so only enable this if you know you do not require attributes filled by + * this instrumentation to be stable across versions + */ + @CanIgnoreReturnValue + public QuartzTelemetryBuilder setCaptureExperimentalSpanAttributes( + boolean captureExperimentalSpanAttributes) { + this.captureExperimentalSpanAttributes = captureExperimentalSpanAttributes; + return this; + } + /** * Returns a new {@link QuartzTelemetry} with the settings of this {@link QuartzTelemetryBuilder}. */ @@ -45,11 +62,15 @@ public QuartzTelemetry build() { InstrumenterBuilder instrumenter = Instrumenter.builder(openTelemetry, INSTRUMENTATION_NAME, new QuartzSpanNameExtractor()); + if (captureExperimentalSpanAttributes) { + instrumenter.addAttributesExtractor( + AttributesExtractor.constant(AttributeKey.stringKey("job.system"), "quartz")); + } instrumenter.setErrorCauseExtractor(new QuartzErrorCauseExtractor()); instrumenter.addAttributesExtractor( CodeAttributesExtractor.create(new QuartzCodeAttributesGetter())); instrumenter.addAttributesExtractors(additionalExtractors); - return new QuartzTelemetry(new TracingJobListener(instrumenter.newInstrumenter())); + return new QuartzTelemetry(new TracingJobListener(instrumenter.buildInstrumenter())); } } diff --git a/instrumentation/quartz-2.0/library/src/test/java/io/opentelemetry/instrumentation/quartz/v2_0/QuartzTest.java b/instrumentation/quartz-2.0/library/src/test/java/io/opentelemetry/instrumentation/quartz/v2_0/QuartzTest.java index ac89a24c96cb..78f172cc46c9 100644 --- a/instrumentation/quartz-2.0/library/src/test/java/io/opentelemetry/instrumentation/quartz/v2_0/QuartzTest.java +++ b/instrumentation/quartz-2.0/library/src/test/java/io/opentelemetry/instrumentation/quartz/v2_0/QuartzTest.java @@ -16,7 +16,11 @@ class QuartzTest extends AbstractQuartzTest { @Override protected void configureScheduler(Scheduler scheduler) { - QuartzTelemetry.create(testing.getOpenTelemetry()).configure(scheduler); + QuartzTelemetry.builder(testing.getOpenTelemetry()) + // TODO run tests both with and without experimental span attributes + .setCaptureExperimentalSpanAttributes(true) + .build() + .configure(scheduler); } @Override diff --git a/instrumentation/quartz-2.0/testing/src/main/java/io/opentelemetry/instrumentation/quartz/v2_0/AbstractQuartzTest.java b/instrumentation/quartz-2.0/testing/src/main/java/io/opentelemetry/instrumentation/quartz/v2_0/AbstractQuartzTest.java index d0829f7cfd7e..18a184449ad0 100644 --- a/instrumentation/quartz-2.0/testing/src/main/java/io/opentelemetry/instrumentation/quartz/v2_0/AbstractQuartzTest.java +++ b/instrumentation/quartz-2.0/testing/src/main/java/io/opentelemetry/instrumentation/quartz/v2_0/AbstractQuartzTest.java @@ -5,11 +5,12 @@ package io.opentelemetry.instrumentation.quartz.v2_0; -import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; import static org.quartz.JobBuilder.newJob; import static org.quartz.TriggerBuilder.newTrigger; import io.opentelemetry.api.GlobalOpenTelemetry; +import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.api.trace.SpanKind; import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; import io.opentelemetry.sdk.trace.data.StatusData; @@ -67,14 +68,12 @@ void successfulJob() throws Exception { .hasKind(SpanKind.INTERNAL) .hasNoParent() .hasStatus(StatusData.unset()) - .hasAttributesSatisfying( - attrs -> - assertThat(attrs) - .containsEntry( - SemanticAttributes.CODE_NAMESPACE, - SuccessfulJob.class.getName()) - .containsEntry( - SemanticAttributes.CODE_FUNCTION, "execute")), + .hasAttributesSatisfyingExactly( + equalTo(AttributeKey.stringKey("job.system"), "quartz"), + equalTo( + SemanticAttributes.CODE_NAMESPACE, + SuccessfulJob.class.getName()), + equalTo(SemanticAttributes.CODE_FUNCTION, "execute")), span -> span.hasName("child") .hasKind(SpanKind.INTERNAL) @@ -99,14 +98,11 @@ void failingJob() throws Exception { .hasNoParent() .hasStatus(StatusData.error()) .hasException(new IllegalStateException("Bad job")) - .hasAttributesSatisfying( - attrs -> - assertThat(attrs) - .containsEntry( - SemanticAttributes.CODE_NAMESPACE, - FailingJob.class.getName()) - .containsEntry( - SemanticAttributes.CODE_FUNCTION, "execute")))); + .hasAttributesSatisfyingExactly( + equalTo(AttributeKey.stringKey("job.system"), "quartz"), + equalTo( + SemanticAttributes.CODE_NAMESPACE, FailingJob.class.getName()), + equalTo(SemanticAttributes.CODE_FUNCTION, "execute")))); } private static Scheduler createScheduler(String name) throws Exception { @@ -121,7 +117,7 @@ private static Scheduler createScheduler(String name) throws Exception { public static class SuccessfulJob implements Job { @Override public void execute(JobExecutionContext context) { - GlobalOpenTelemetry.getTracer("jobtracer").spanBuilder("child").startSpan().end(); + GlobalOpenTelemetry.getTracer("test").spanBuilder("child").startSpan().end(); // ensure that JobExecutionContext is serializable try { new ObjectOutputStream(new ByteArrayOutputStream()).writeObject(context); diff --git a/instrumentation/rabbitmq-2.7/javaagent/build.gradle.kts b/instrumentation/rabbitmq-2.7/javaagent/build.gradle.kts index b21b83824dea..a20f7dc2f59d 100644 --- a/instrumentation/rabbitmq-2.7/javaagent/build.gradle.kts +++ b/instrumentation/rabbitmq-2.7/javaagent/build.gradle.kts @@ -32,5 +32,7 @@ dependencies { tasks.withType().configureEach { // TODO run tests both with and without experimental span attributes jvmArgs("-Dotel.instrumentation.rabbitmq.experimental-span-attributes=true") + jvmArgs("-Dotel.instrumentation.messaging.experimental.receive-telemetry.enabled=true") + usesService(gradle.sharedServices.registrations["testcontainersBuildService"].getService()) } diff --git a/instrumentation/rabbitmq-2.7/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rabbitmq/ChannelAndMethod.java b/instrumentation/rabbitmq-2.7/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rabbitmq/ChannelAndMethod.java index bdc7e40b3065..495c4662d25e 100644 --- a/instrumentation/rabbitmq-2.7/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rabbitmq/ChannelAndMethod.java +++ b/instrumentation/rabbitmq-2.7/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rabbitmq/ChannelAndMethod.java @@ -7,6 +7,7 @@ import com.google.auto.value.AutoValue; import com.rabbitmq.client.Channel; +import java.util.Map; @AutoValue public abstract class ChannelAndMethod { @@ -18,4 +19,14 @@ public static ChannelAndMethod create(Channel channel, String method) { abstract Channel getChannel(); abstract String getMethod(); + + private Map headers; + + public Map getHeaders() { + return headers; + } + + public void setHeaders(Map headers) { + this.headers = headers; + } } diff --git a/instrumentation/rabbitmq-2.7/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rabbitmq/RabbitChannelAndMethodHolder.java b/instrumentation/rabbitmq-2.7/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rabbitmq/RabbitChannelAndMethodHolder.java new file mode 100644 index 000000000000..b779cd56bb89 --- /dev/null +++ b/instrumentation/rabbitmq-2.7/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rabbitmq/RabbitChannelAndMethodHolder.java @@ -0,0 +1,18 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.rabbitmq; + +public class RabbitChannelAndMethodHolder { + private ChannelAndMethod channelAndMethod; + + public ChannelAndMethod getChannelAndMethod() { + return channelAndMethod; + } + + public void setChannelAndMethod(ChannelAndMethod channelAndMethod) { + this.channelAndMethod = channelAndMethod; + } +} diff --git a/instrumentation/rabbitmq-2.7/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rabbitmq/RabbitChannelAttributesGetter.java b/instrumentation/rabbitmq-2.7/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rabbitmq/RabbitChannelAttributesGetter.java index f71595d5a4b7..e69747ff8a1d 100644 --- a/instrumentation/rabbitmq-2.7/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rabbitmq/RabbitChannelAttributesGetter.java +++ b/instrumentation/rabbitmq-2.7/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rabbitmq/RabbitChannelAttributesGetter.java @@ -7,6 +7,8 @@ import io.opentelemetry.instrumentation.api.instrumenter.messaging.MessagingAttributesGetter; import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; +import java.util.Collections; +import java.util.List; import javax.annotation.Nullable; enum RabbitChannelAttributesGetter implements MessagingAttributesGetter { @@ -74,4 +76,21 @@ public Long messagePayloadCompressedSize(ChannelAndMethod channelAndMethod) { public String messageId(ChannelAndMethod channelAndMethod, @Nullable Void unused) { return null; } + + @Override + public List header(ChannelAndMethod channelAndMethod, String name) { + if (channelAndMethod.getHeaders() != null) { + Object value = channelAndMethod.getHeaders().get(name); + if (value != null) { + return Collections.singletonList(value.toString()); + } + } + return Collections.emptyList(); + } + + @Nullable + @Override + public String messagePayload(ChannelAndMethod channelAndMethod) { + return null; + } } diff --git a/instrumentation/rabbitmq-2.7/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rabbitmq/RabbitChannelInstrumentation.java b/instrumentation/rabbitmq-2.7/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rabbitmq/RabbitChannelInstrumentation.java index 61fd01492292..9678d6117639 100644 --- a/instrumentation/rabbitmq-2.7/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rabbitmq/RabbitChannelInstrumentation.java +++ b/instrumentation/rabbitmq-2.7/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rabbitmq/RabbitChannelInstrumentation.java @@ -38,6 +38,7 @@ import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; import java.io.IOException; +import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.Map; import net.bytebuddy.asm.Advice; @@ -113,6 +114,7 @@ public static void onEnter( context = channelInstrumenter().start(parentContext, request); CURRENT_RABBIT_CONTEXT.set(context); + helper().setChannelAndMethod(context, request); scope = context.makeCurrent(); } @@ -151,13 +153,14 @@ public static void setSpanNameAddHeaders( if (body != null) { span.setAttribute( SemanticAttributes.MESSAGING_MESSAGE_PAYLOAD_SIZE_BYTES, (long) body.length); + span.setAttribute("messaging.payload", new String(body, StandardCharsets.UTF_8)); } // This is the internal behavior when props are null. We're just doing it earlier now. if (props == null) { props = MessageProperties.MINIMAL_BASIC; } - helper().onProps(span, props); + helper().onProps(context, span, props); // We need to copy the BasicProperties and provide a header map we can modify Map headers = props.getHeaders(); diff --git a/instrumentation/rabbitmq-2.7/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rabbitmq/RabbitChannelNetAttributesGetter.java b/instrumentation/rabbitmq-2.7/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rabbitmq/RabbitChannelNetAttributesGetter.java index e8b95cb61ffb..d567567f9907 100644 --- a/instrumentation/rabbitmq-2.7/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rabbitmq/RabbitChannelNetAttributesGetter.java +++ b/instrumentation/rabbitmq-2.7/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rabbitmq/RabbitChannelNetAttributesGetter.java @@ -6,6 +6,7 @@ package io.opentelemetry.javaagent.instrumentation.rabbitmq; import io.opentelemetry.instrumentation.api.instrumenter.net.NetClientAttributesGetter; +import java.net.Inet6Address; import javax.annotation.Nullable; public class RabbitChannelNetAttributesGetter @@ -19,20 +20,33 @@ public String transport(ChannelAndMethod channelAndMethod, @Nullable Void unused @Nullable @Override - public String peerName(ChannelAndMethod channelAndMethod, @Nullable Void unused) { - // not using InetAddress.getHostName() since that can trigger reverse name lookup + public String peerName(ChannelAndMethod channelAndMethod) { return null; } @Nullable @Override - public Integer peerPort(ChannelAndMethod channelAndMethod, @Nullable Void unused) { - return channelAndMethod.getChannel().getConnection().getPort(); + public Integer peerPort(ChannelAndMethod channelAndMethod) { + return null; } @Nullable @Override - public String peerIp(ChannelAndMethod channelAndMethod, @Nullable Void unused) { + public String sockPeerAddr(ChannelAndMethod channelAndMethod, @Nullable Void unused) { return channelAndMethod.getChannel().getConnection().getAddress().getHostAddress(); } + + @Override + public Integer sockPeerPort(ChannelAndMethod channelAndMethod, @Nullable Void unused) { + return channelAndMethod.getChannel().getConnection().getPort(); + } + + @Nullable + @Override + public String sockFamily(ChannelAndMethod channelAndMethod, @Nullable Void unused) { + if (channelAndMethod.getChannel().getConnection().getAddress() instanceof Inet6Address) { + return "inet6"; + } + return null; + } } diff --git a/instrumentation/rabbitmq-2.7/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rabbitmq/RabbitCommandInstrumentation.java b/instrumentation/rabbitmq-2.7/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rabbitmq/RabbitCommandInstrumentation.java index 2aad00f05756..f9312f278797 100644 --- a/instrumentation/rabbitmq-2.7/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rabbitmq/RabbitCommandInstrumentation.java +++ b/instrumentation/rabbitmq-2.7/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rabbitmq/RabbitCommandInstrumentation.java @@ -39,8 +39,11 @@ public void transform(TypeTransformer transformer) { RabbitCommandInstrumentation.class.getName() + "$CommandConstructorAdvice"); } - public static class SpanHolder { + public static final class SpanHolder { + public static final ThreadLocal CURRENT_RABBIT_CONTEXT = new ThreadLocal<>(); + + private SpanHolder() {} } @SuppressWarnings("unused") diff --git a/instrumentation/rabbitmq-2.7/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rabbitmq/RabbitDeliveryAttributesGetter.java b/instrumentation/rabbitmq-2.7/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rabbitmq/RabbitDeliveryAttributesGetter.java index d9d57e1c339c..702337fa5ced 100644 --- a/instrumentation/rabbitmq-2.7/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rabbitmq/RabbitDeliveryAttributesGetter.java +++ b/instrumentation/rabbitmq-2.7/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rabbitmq/RabbitDeliveryAttributesGetter.java @@ -7,6 +7,9 @@ import io.opentelemetry.instrumentation.api.instrumenter.messaging.MessagingAttributesGetter; import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; +import java.nio.charset.StandardCharsets; +import java.util.Collections; +import java.util.List; import javax.annotation.Nullable; enum RabbitDeliveryAttributesGetter implements MessagingAttributesGetter { @@ -85,4 +88,24 @@ public Long messagePayloadCompressedSize(DeliveryRequest request) { public String messageId(DeliveryRequest request, @Nullable Void unused) { return null; } + + @Nullable + @Override + public String messagePayload(DeliveryRequest request) { + byte[] body = request.getBody(); + if (body != null) { + return new String(body, StandardCharsets.UTF_8); + } + + return null; + } + + @Override + public List header(DeliveryRequest request, String name) { + Object value = request.getProperties().getHeaders().get(name); + if (value != null) { + return Collections.singletonList(value.toString()); + } + return Collections.emptyList(); + } } diff --git a/instrumentation/rabbitmq-2.7/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rabbitmq/RabbitInstrumenterHelper.java b/instrumentation/rabbitmq-2.7/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rabbitmq/RabbitInstrumenterHelper.java index 8417bcd2928e..1569e7be4500 100644 --- a/instrumentation/rabbitmq-2.7/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rabbitmq/RabbitInstrumenterHelper.java +++ b/instrumentation/rabbitmq-2.7/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rabbitmq/RabbitInstrumenterHelper.java @@ -5,6 +5,8 @@ package io.opentelemetry.javaagent.instrumentation.rabbitmq; +import static io.opentelemetry.javaagent.instrumentation.rabbitmq.RabbitSingletons.CHANNEL_AND_METHOD_CONTEXT_KEY; + import com.rabbitmq.client.AMQP; import com.rabbitmq.client.Command; import io.opentelemetry.api.GlobalOpenTelemetry; @@ -41,13 +43,18 @@ public void onPublish(Span span, String exchange, String routingKey) { } } - public void onProps(Span span, AMQP.BasicProperties props) { + public void onProps(Context context, Span span, AMQP.BasicProperties props) { if (CAPTURE_EXPERIMENTAL_SPAN_ATTRIBUTES) { Integer deliveryMode = props.getDeliveryMode(); if (deliveryMode != null) { span.setAttribute("rabbitmq.delivery_mode", deliveryMode); } } + RabbitChannelAndMethodHolder channelContext = context.get(CHANNEL_AND_METHOD_CONTEXT_KEY); + ChannelAndMethod channelAndMethod = channelContext.getChannelAndMethod(); + if (channelAndMethod != null) { + channelAndMethod.setHeaders(props.getHeaders()); + } } private static String normalizeExchangeName(String exchange) { @@ -68,4 +75,11 @@ public static void onCommand(Span span, Command command) { public void inject(Context context, Map headers, MapSetter setter) { GlobalOpenTelemetry.getPropagators().getTextMapPropagator().inject(context, headers, setter); } + + public void setChannelAndMethod(Context context, ChannelAndMethod channelAndMethod) { + RabbitChannelAndMethodHolder holder = context.get(CHANNEL_AND_METHOD_CONTEXT_KEY); + if (holder != null) { + holder.setChannelAndMethod(channelAndMethod); + } + } } diff --git a/instrumentation/rabbitmq-2.7/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rabbitmq/RabbitReceiveAttributesGetter.java b/instrumentation/rabbitmq-2.7/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rabbitmq/RabbitReceiveAttributesGetter.java index a46618de0216..62558528b8f8 100644 --- a/instrumentation/rabbitmq-2.7/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rabbitmq/RabbitReceiveAttributesGetter.java +++ b/instrumentation/rabbitmq-2.7/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rabbitmq/RabbitReceiveAttributesGetter.java @@ -8,6 +8,9 @@ import com.rabbitmq.client.GetResponse; import io.opentelemetry.instrumentation.api.instrumenter.messaging.MessagingAttributesGetter; import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; +import java.nio.charset.StandardCharsets; +import java.util.Collections; +import java.util.List; import javax.annotation.Nullable; enum RabbitReceiveAttributesGetter @@ -84,4 +87,32 @@ public Long messagePayloadCompressedSize(ReceiveRequest request) { public String messageId(ReceiveRequest request, @Nullable GetResponse response) { return null; } + + @Nullable + @Override + public String messagePayload(ReceiveRequest receiveRequest) { + GetResponse response = receiveRequest.getResponse(); + if (response == null) { + return null; + } + + byte[] body = receiveRequest.getResponse().getBody(); + if (body != null) { + return new String(body, StandardCharsets.UTF_8); + } + + return null; + } + + @Override + public List header(ReceiveRequest request, String name) { + GetResponse response = request.getResponse(); + if (response != null) { + Object value = request.getResponse().getProps().getHeaders().get(name); + if (value != null) { + return Collections.singletonList(value.toString()); + } + } + return Collections.emptyList(); + } } diff --git a/instrumentation/rabbitmq-2.7/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rabbitmq/RabbitReceiveNetAttributesGetter.java b/instrumentation/rabbitmq-2.7/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rabbitmq/RabbitReceiveNetAttributesGetter.java index d51e752b8a29..729bb4049c6a 100644 --- a/instrumentation/rabbitmq-2.7/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rabbitmq/RabbitReceiveNetAttributesGetter.java +++ b/instrumentation/rabbitmq-2.7/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rabbitmq/RabbitReceiveNetAttributesGetter.java @@ -7,6 +7,7 @@ import com.rabbitmq.client.GetResponse; import io.opentelemetry.instrumentation.api.instrumenter.net.NetClientAttributesGetter; +import java.net.Inet6Address; import javax.annotation.Nullable; public class RabbitReceiveNetAttributesGetter @@ -20,20 +21,34 @@ public String transport(ReceiveRequest request, @Nullable GetResponse response) @Nullable @Override - public String peerName(ReceiveRequest request, @Nullable GetResponse response) { - // not using InetAddress.getHostName() since that can trigger reverse name lookup + public String peerName(ReceiveRequest request) { return null; } @Nullable @Override - public Integer peerPort(ReceiveRequest request, @Nullable GetResponse response) { - return request.getConnection().getPort(); + public Integer peerPort(ReceiveRequest request) { + return null; } @Nullable @Override - public String peerIp(ReceiveRequest request, @Nullable GetResponse response) { + public String sockPeerAddr(ReceiveRequest request, @Nullable GetResponse response) { return request.getConnection().getAddress().getHostAddress(); } + + @Nullable + @Override + public Integer sockPeerPort(ReceiveRequest request, @Nullable GetResponse response) { + return request.getConnection().getPort(); + } + + @Nullable + @Override + public String sockFamily(ReceiveRequest request, @Nullable GetResponse response) { + if (request.getConnection().getAddress() instanceof Inet6Address) { + return "inet6"; + } + return null; + } } diff --git a/instrumentation/rabbitmq-2.7/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rabbitmq/RabbitSingletons.java b/instrumentation/rabbitmq-2.7/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rabbitmq/RabbitSingletons.java index a189d641c718..43a0a0469613 100644 --- a/instrumentation/rabbitmq-2.7/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rabbitmq/RabbitSingletons.java +++ b/instrumentation/rabbitmq-2.7/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rabbitmq/RabbitSingletons.java @@ -10,30 +10,34 @@ import com.rabbitmq.client.GetResponse; import io.opentelemetry.api.GlobalOpenTelemetry; +import io.opentelemetry.context.ContextKey; import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; import io.opentelemetry.instrumentation.api.instrumenter.SpanKindExtractor; import io.opentelemetry.instrumentation.api.instrumenter.messaging.MessageOperation; import io.opentelemetry.instrumentation.api.instrumenter.messaging.MessagingAttributesExtractor; +import io.opentelemetry.instrumentation.api.instrumenter.messaging.MessagingAttributesGetter; import io.opentelemetry.instrumentation.api.instrumenter.net.NetClientAttributesExtractor; +import io.opentelemetry.instrumentation.api.internal.PropagatorBasedSpanLinksExtractor; +import io.opentelemetry.javaagent.bootstrap.internal.ExperimentalConfig; import io.opentelemetry.javaagent.bootstrap.internal.InstrumentationConfig; import java.util.ArrayList; import java.util.List; -public class RabbitSingletons { +public final class RabbitSingletons { + private static final boolean CAPTURE_EXPERIMENTAL_SPAN_ATTRIBUTES = InstrumentationConfig.get() .getBoolean("otel.instrumentation.rabbitmq.experimental-span-attributes", false); private static final String instrumentationName = "io.opentelemetry.rabbitmq-2.7"; - private static final Instrumenter channelInstrumenter; - private static final Instrumenter receiveInstrumenter; - private static final Instrumenter deliverInstrumenter; - - static { - channelInstrumenter = createChannelInstrumenter(); - receiveInstrumenter = createReceiveInstrumenter(); - deliverInstrumenter = createDeliverInstrumenter(); - } + private static final Instrumenter channelInstrumenter = + createChannelInstrumenter(); + private static final Instrumenter receiveInstrumenter = + createReceiveInstrumenter(); + private static final Instrumenter deliverInstrumenter = + createDeliverInstrumenter(); + static final ContextKey CHANNEL_AND_METHOD_CONTEXT_KEY = + ContextKey.named("opentelemetry-rabbitmq-channel-and-method-context-key"); public static Instrumenter channelInstrumenter() { return channelInstrumenter; @@ -51,11 +55,14 @@ private static Instrumenter createChannelInstrumenter() return Instrumenter.builder( GlobalOpenTelemetry.get(), instrumentationName, ChannelAndMethod::getMethod) .addAttributesExtractor( - MessagingAttributesExtractor.create( + buildMessagingAttributesExtractor( RabbitChannelAttributesGetter.INSTANCE, MessageOperation.SEND)) .addAttributesExtractor( NetClientAttributesExtractor.create(new RabbitChannelNetAttributesGetter())) - .newInstrumenter( + .addContextCustomizer( + (context, request, startAttributes) -> + context.with(CHANNEL_AND_METHOD_CONTEXT_KEY, new RabbitChannelAndMethodHolder())) + .buildInstrumenter( channelAndMethod -> channelAndMethod.getMethod().equals("Channel.basicPublish") ? PRODUCER : CLIENT); } @@ -63,7 +70,7 @@ private static Instrumenter createChannelInstrumenter() private static Instrumenter createReceiveInstrumenter() { List> extractors = new ArrayList<>(); extractors.add( - MessagingAttributesExtractor.create( + buildMessagingAttributesExtractor( RabbitReceiveAttributesGetter.INSTANCE, MessageOperation.RECEIVE)); extractors.add(NetClientAttributesExtractor.create(new RabbitReceiveNetAttributesGetter())); if (CAPTURE_EXPERIMENTAL_SPAN_ATTRIBUTES) { @@ -73,13 +80,18 @@ private static Instrumenter createReceiveInstrument return Instrumenter.builder( GlobalOpenTelemetry.get(), instrumentationName, ReceiveRequest::spanName) .addAttributesExtractors(extractors) - .newInstrumenter(SpanKindExtractor.alwaysClient()); + .setEnabled(ExperimentalConfig.get().messagingReceiveInstrumentationEnabled()) + .addSpanLinksExtractor( + new PropagatorBasedSpanLinksExtractor<>( + GlobalOpenTelemetry.getPropagators().getTextMapPropagator(), + ReceiveRequestTextMapGetter.INSTANCE)) + .buildInstrumenter(SpanKindExtractor.alwaysConsumer()); } private static Instrumenter createDeliverInstrumenter() { List> extractors = new ArrayList<>(); extractors.add( - MessagingAttributesExtractor.create( + buildMessagingAttributesExtractor( RabbitDeliveryAttributesGetter.INSTANCE, MessageOperation.PROCESS)); extractors.add(new RabbitDeliveryExtraAttributesExtractor()); if (CAPTURE_EXPERIMENTAL_SPAN_ATTRIBUTES) { @@ -89,6 +101,15 @@ private static Instrumenter createDeliverInstrumenter() { return Instrumenter.builder( GlobalOpenTelemetry.get(), instrumentationName, DeliveryRequest::spanName) .addAttributesExtractors(extractors) - .newConsumerInstrumenter(DeliveryRequestGetter.INSTANCE); + .buildConsumerInstrumenter(DeliveryRequestGetter.INSTANCE); } + + private static MessagingAttributesExtractor buildMessagingAttributesExtractor( + MessagingAttributesGetter getter, MessageOperation operation) { + return MessagingAttributesExtractor.builder(getter, operation) + .setCapturedHeaders(ExperimentalConfig.get().getMessagingHeaders()) + .build(); + } + + private RabbitSingletons() {} } diff --git a/instrumentation/rabbitmq-2.7/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rabbitmq/ReceiveRequestTextMapGetter.java b/instrumentation/rabbitmq-2.7/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rabbitmq/ReceiveRequestTextMapGetter.java new file mode 100644 index 000000000000..1e34a85fc17a --- /dev/null +++ b/instrumentation/rabbitmq-2.7/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rabbitmq/ReceiveRequestTextMapGetter.java @@ -0,0 +1,40 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.rabbitmq; + +import com.rabbitmq.client.AMQP; +import com.rabbitmq.client.GetResponse; +import io.opentelemetry.context.propagation.TextMapGetter; +import java.util.Collections; +import java.util.Map; +import java.util.Optional; +import javax.annotation.Nullable; + +enum ReceiveRequestTextMapGetter implements TextMapGetter { + INSTANCE; + + @Override + public Iterable keys(ReceiveRequest carrier) { + return Optional.of(carrier) + .map(ReceiveRequest::getResponse) + .map(GetResponse::getProps) + .map(AMQP.BasicProperties::getHeaders) + .map(Map::keySet) + .orElse(Collections.emptySet()); + } + + @Nullable + @Override + public String get(@Nullable ReceiveRequest carrier, String key) { + return Optional.ofNullable(carrier) + .map(ReceiveRequest::getResponse) + .map(GetResponse::getProps) + .map(AMQP.BasicProperties::getHeaders) + .map(headers -> headers.get(key)) + .map(Object::toString) + .orElse(null); + } +} diff --git a/instrumentation/rabbitmq-2.7/javaagent/src/test/groovy/RabbitMqTest.groovy b/instrumentation/rabbitmq-2.7/javaagent/src/test/groovy/RabbitMqTest.groovy index 595129218bd7..295b588485f9 100644 --- a/instrumentation/rabbitmq-2.7/javaagent/src/test/groovy/RabbitMqTest.groovy +++ b/instrumentation/rabbitmq-2.7/javaagent/src/test/groovy/RabbitMqTest.groovy @@ -23,7 +23,9 @@ import org.springframework.amqp.rabbit.connection.CachingConnectionFactory import org.springframework.amqp.rabbit.core.RabbitAdmin import org.springframework.amqp.rabbit.core.RabbitTemplate -import static com.google.common.net.InetAddresses.isInetAddress +import java.util.concurrent.CountDownLatch +import java.util.concurrent.TimeUnit + import static io.opentelemetry.api.trace.SpanKind.CLIENT import static io.opentelemetry.api.trace.SpanKind.CONSUMER import static io.opentelemetry.api.trace.SpanKind.PRODUCER @@ -52,11 +54,14 @@ class RabbitMqTest extends AgentInstrumentationSpecification implements WithRabb def "test rabbit publish/get"() { setup: - GetResponse response = runWithSpan("parent") { + String queueName = runWithSpan("producer parent") { channel.exchangeDeclare(exchangeName, "direct", false) String queueName = channel.queueDeclare().getQueue() channel.queueBind(queueName, exchangeName, routingKey) channel.basicPublish(exchangeName, routingKey, null, "Hello, world!".getBytes()) + return queueName + } + GetResponse response = runWithSpan("consumer parent") { return channel.basicGet(queueName, true) } @@ -64,18 +69,28 @@ class RabbitMqTest extends AgentInstrumentationSpecification implements WithRabb new String(response.getBody()) == "Hello, world!" and: - assertTraces(1) { - trace(0, 6) { + assertTraces(2) { + SpanData producerSpan + + trace(0, 5) { span(0) { - name "parent" - attributes { - } + name "producer parent" + hasNoParent() } rabbitSpan(it, 1, null, null, null, "exchange.declare", span(0)) rabbitSpan(it, 2, null, null, null, "queue.declare", span(0)) rabbitSpan(it, 3, null, null, null, "queue.bind", span(0)) rabbitSpan(it, 4, exchangeName, routingKey, "send", "$exchangeName", span(0)) - rabbitSpan(it, 5, exchangeName, routingKey, "receive", "", span(0)) + + producerSpan = span(4) + } + + trace(1, 2) { + span(0) { + name "consumer parent" + hasNoParent() + } + rabbitSpan(it, 1, exchangeName, routingKey, "receive", "", span(0), producerSpan) } } @@ -86,24 +101,39 @@ class RabbitMqTest extends AgentInstrumentationSpecification implements WithRabb def "test rabbit publish/get default exchange"() { setup: - String queueName = channel.queueDeclare().getQueue() - channel.basicPublish("", queueName, null, "Hello, world!".getBytes()) - GetResponse response = channel.basicGet(queueName, true) + String queueName = runWithSpan("producer parent") { + String queueName = channel.queueDeclare().getQueue() + channel.basicPublish("", queueName, null, "Hello, world!".getBytes()) + return queueName + } + GetResponse response = runWithSpan("consumer parent") { + return channel.basicGet(queueName, true) + } expect: new String(response.getBody()) == "Hello, world!" and: - assertTraces(3) { - traces.subList(1, 3).sort(orderByRootSpanKind(PRODUCER, CLIENT)) - trace(0, 1) { - rabbitSpan(it, 0, null, null, null, "queue.declare") - } - trace(1, 1) { - rabbitSpan(it, 0, "", null, "send", "") + assertTraces(2) { + SpanData producerSpan + + trace(0, 3) { + span(0) { + name "producer parent" + hasNoParent() + } + rabbitSpan(it, 1, null, null, null, "queue.declare", span(0)) + rabbitSpan(it, 2, "", null, "send", "", span(0)) + + producerSpan = span(2) } - trace(2, 1) { - rabbitSpan(it, 0, "", null, "receive", "", null) + + trace(1, 2) { + span(0) { + name "consumer parent" + hasNoParent() + } + rabbitSpan(it, 1, "", null, "receive", "", span(0), producerSpan) } } } @@ -141,16 +171,16 @@ class RabbitMqTest extends AgentInstrumentationSpecification implements WithRabb expect: assertTraces(4 + messageCount) { trace(0, 1) { - rabbitSpan(it, null, null, null, "exchange.declare") + rabbitSpan(it, 0, null, null, null, "exchange.declare") } trace(1, 1) { - rabbitSpan(it, null, null, null, "queue.declare") + rabbitSpan(it, 0, null, null, null, "queue.declare") } trace(2, 1) { - rabbitSpan(it, null, null, null, "queue.bind") + rabbitSpan(it, 0, null, null, null, "queue.bind") } trace(3, 1) { - rabbitSpan(it, null, null, null, "basic.consume") + rabbitSpan(it, 0, null, null, null, "basic.consume") } (1..messageCount).each { trace(3 + it, 2) { @@ -196,16 +226,16 @@ class RabbitMqTest extends AgentInstrumentationSpecification implements WithRabb expect: assertTraces(5) { trace(0, 1) { - rabbitSpan(it, null, null, null, "exchange.declare") + rabbitSpan(it, 0, null, null, null, "exchange.declare") } trace(1, 1) { - rabbitSpan(it, null, null, null, "queue.declare") + rabbitSpan(it, 0, null, null, null, "queue.declare") } trace(2, 1) { - rabbitSpan(it, null, null, null, "queue.bind") + rabbitSpan(it, 0, null, null, null, "queue.bind") } trace(3, 1) { - rabbitSpan(it, null, null, null, "basic.consume") + rabbitSpan(it, 0, null, null, null, "basic.consume") } trace(4, 2) { rabbitSpan(it, 0, exchangeName, null, "send", "$exchangeName") @@ -228,7 +258,7 @@ class RabbitMqTest extends AgentInstrumentationSpecification implements WithRabb assertTraces(1) { trace(0, 1) { - rabbitSpan(it, null, null, operation, command, null, null, error, errorMsg) + rabbitSpan(it, 0, null, null, operation, command, null, null, error, errorMsg) } } @@ -249,45 +279,83 @@ class RabbitMqTest extends AgentInstrumentationSpecification implements WithRabb setup: def connectionFactory = new CachingConnectionFactory(connectionFactory) AmqpAdmin admin = new RabbitAdmin(connectionFactory) - def queue = new Queue("some-routing-queue", false, true, true, null) - admin.declareQueue(queue) AmqpTemplate template = new RabbitTemplate(connectionFactory) - template.convertAndSend(queue.name, "foo") - String message = (String) template.receiveAndConvert(queue.name) + + def queue = new Queue("some-routing-queue", false, true, true, null) + runWithSpan("producer parent") { + admin.declareQueue(queue) + template.convertAndSend(queue.name, "foo") + } + String message = runWithSpan("consumer parent") { + return template.receiveAndConvert(queue.name) as String + } expect: message == "foo" + and: + assertTraces(2) { + SpanData producerSpan + + trace(0, 3) { + span(0) { + name "producer parent" + hasNoParent() + } + rabbitSpan(it, 1, null, null, null, "queue.declare", span(0)) + rabbitSpan(it, 2, "", "some-routing-queue", "send", "", span(0)) + + producerSpan = span(2) + } + + trace(1, 2) { + span(0) { + name "consumer parent" + hasNoParent() + } + rabbitSpan(it, 1, "", "some-routing-queue", "receive", queue.name, span(0), producerSpan) + } + } + } + + def "capture message header as span attributes"() { + setup: + String queueName = channel.queueDeclare().getQueue() + def properties = new AMQP.BasicProperties.Builder().headers(["test-message-header": "test"]).build() + channel.basicPublish("", queueName, properties, "Hello, world!".getBytes()) + + def latch = new CountDownLatch(1) + def deliveries = [] + + Consumer callback = new DefaultConsumer(channel) { + @Override + void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties props, byte[] body) throws IOException { + deliveries << new String(body) + latch.countDown() + } + } + + channel.basicConsume(queueName, callback) + latch.await(10, TimeUnit.SECONDS) + expect: + deliveries[0] == "Hello, world!" + and: assertTraces(3) { traces.subList(1, 3).sort(orderByRootSpanKind(PRODUCER, CLIENT)) trace(0, 1) { - rabbitSpan(it, null, null, null, "queue.declare") + rabbitSpan(it, 0, null, null, null, "queue.declare") } - trace(1, 1) { - rabbitSpan(it, 0, "", "some-routing-queue", "send", "") + trace(1, 2) { + rabbitSpan(it, 0, "", null, "send", "", null, null, null, null, false, true) + rabbitSpan(it, 1, "", null, "process", "", span(0), null, null, null, false, true) } trace(2, 1) { - rabbitSpan(it, 0, "", "some-routing-queue", "receive", queue.name, null) + rabbitSpan(it, 0, null, null, null, "basic.consume") } } } - def rabbitSpan( - TraceAssert trace, - String exchange, - String routingKey, - String operation, - String resource, - Object parentSpan = null, - Object linkSpan = null, - Throwable exception = null, - String errorMsg = null, - Boolean expectTimestamp = false - ) { - rabbitSpan(trace, 0, exchange, routingKey, operation, resource, parentSpan, linkSpan, exception, errorMsg, expectTimestamp) - } - def rabbitSpan( TraceAssert trace, int index, @@ -295,11 +363,12 @@ class RabbitMqTest extends AgentInstrumentationSpecification implements WithRabb String routingKey, String operation, String resource, - Object parentSpan = null, - Object linkSpan = null, + SpanData parentSpan = null, + SpanData linkSpan = null, Throwable exception = null, String errorMsg = null, - Boolean expectTimestamp = false + boolean expectTimestamp = false, + boolean testHeaders = false ) { def spanName = resource @@ -307,31 +376,35 @@ class RabbitMqTest extends AgentInstrumentationSpecification implements WithRabb spanName = spanName + " " + operation } + def rabbitCommand = trace.span(index).attributes.get(AttributeKey.stringKey("rabbitmq.command")) + + def spanKind + switch (rabbitCommand) { + case "basic.publish": + spanKind = PRODUCER + break + case "basic.get": // fallthrough + case "basic.deliver": + spanKind = CONSUMER + break + default: + spanKind = CLIENT + } + trace.span(index) { name spanName + kind spanKind - switch (trace.span(index).attributes.get(AttributeKey.stringKey("rabbitmq.command"))) { - case "basic.publish": - kind PRODUCER - break - case "basic.get": - kind CLIENT - break - case "basic.deliver": - kind CONSUMER - break - default: - kind CLIENT - } - - if (parentSpan) { - childOf((SpanData) parentSpan) - } else { + if (parentSpan == null) { hasNoParent() + } else { + childOf(parentSpan) } - if (linkSpan) { - hasLink((SpanData) linkSpan) + if (linkSpan == null) { + hasNoLinks() + } else { + hasLink(linkSpan) } if (exception) { @@ -340,9 +413,12 @@ class RabbitMqTest extends AgentInstrumentationSpecification implements WithRabb } attributes { - "$SemanticAttributes.NET_PEER_NAME" { it == null || it instanceof String } - "$SemanticAttributes.NET_PEER_IP" { it == null || isInetAddress(it as String) } - "$SemanticAttributes.NET_PEER_PORT" { it == null || it instanceof Long } + // listener does not have access to net attributes + if (rabbitCommand != "basic.deliver") { + "$SemanticAttributes.NET_SOCK_PEER_ADDR" { it == "127.0.0.1" || it == "0:0:0:0:0:0:0:1" || it == null } + "$SemanticAttributes.NET_SOCK_PEER_PORT" Long + "$SemanticAttributes.NET_SOCK_FAMILY" { it == SemanticAttributes.NetSockFamilyValues.INET6 || it == null } + } "$SemanticAttributes.MESSAGING_SYSTEM" "rabbitmq" "$SemanticAttributes.MESSAGING_DESTINATION" exchange @@ -355,8 +431,11 @@ class RabbitMqTest extends AgentInstrumentationSpecification implements WithRabb if (expectTimestamp) { "rabbitmq.record.queue_time_ms" { it instanceof Long && it >= 0 } } + if (testHeaders) { + "messaging.header.test_message_header" { it == ["test"] } + } - switch (trace.span(index).attributes.get(AttributeKey.stringKey("rabbitmq.command"))) { + switch (rabbitCommand) { case "basic.publish": "rabbitmq.command" "basic.publish" "$SemanticAttributes.MESSAGING_RABBITMQ_ROUTING_KEY" { @@ -364,16 +443,19 @@ class RabbitMqTest extends AgentInstrumentationSpecification implements WithRabb } "rabbitmq.delivery_mode" { it == null || it == 2 } "$SemanticAttributes.MESSAGING_MESSAGE_PAYLOAD_SIZE_BYTES" Long + "messaging.payload" String break case "basic.get": "rabbitmq.command" "basic.get" //TODO why this queue name is not a destination for semantic convention "rabbitmq.queue" { it == "some-queue" || it == "some-routing-queue" || it.startsWith("amq.gen-") } "$SemanticAttributes.MESSAGING_MESSAGE_PAYLOAD_SIZE_BYTES" { it == null || it instanceof Long } + "messaging.payload" { it == null || it instanceof String } break case "basic.deliver": "rabbitmq.command" "basic.deliver" "$SemanticAttributes.MESSAGING_MESSAGE_PAYLOAD_SIZE_BYTES" Long + "messaging.payload" String break default: "rabbitmq.command" { it == null || it == resource } diff --git a/instrumentation/rabbitmq-2.7/javaagent/src/test/groovy/ReactorRabbitMqTest.groovy b/instrumentation/rabbitmq-2.7/javaagent/src/test/groovy/ReactorRabbitMqTest.groovy index 2c16bff49dd8..d6e1fb147b10 100644 --- a/instrumentation/rabbitmq-2.7/javaagent/src/test/groovy/ReactorRabbitMqTest.groovy +++ b/instrumentation/rabbitmq-2.7/javaagent/src/test/groovy/ReactorRabbitMqTest.groovy @@ -10,8 +10,6 @@ import reactor.rabbitmq.ExchangeSpecification import reactor.rabbitmq.RabbitFlux import reactor.rabbitmq.SenderOptions -import static com.google.common.net.InetAddresses.isInetAddress - class ReactorRabbitMqTest extends AgentInstrumentationSpecification implements WithRabbitMqTrait { def setupSpec() { @@ -39,9 +37,9 @@ class ReactorRabbitMqTest extends AgentInstrumentationSpecification implements W name 'exchange.declare' kind SpanKind.CLIENT attributes { - "$SemanticAttributes.NET_PEER_NAME" { it == null || it instanceof String } - "$SemanticAttributes.NET_PEER_IP" { isInetAddress(it as String) } - "$SemanticAttributes.NET_PEER_PORT" { it == null || it instanceof Long } + "$SemanticAttributes.NET_SOCK_PEER_ADDR" { it == "127.0.0.1" || it == "0:0:0:0:0:0:0:1" || it == null } + "$SemanticAttributes.NET_SOCK_PEER_PORT" Long + "$SemanticAttributes.NET_SOCK_FAMILY" { it == SemanticAttributes.NetSockFamilyValues.INET6 || it == null } "$SemanticAttributes.MESSAGING_SYSTEM" "rabbitmq" "$SemanticAttributes.MESSAGING_DESTINATION_KIND" "queue" "rabbitmq.command" "exchange.declare" diff --git a/instrumentation/ratpack/ratpack-1.4/javaagent/build.gradle.kts b/instrumentation/ratpack/ratpack-1.4/javaagent/build.gradle.kts index b390c1648822..3c0407a2d0ed 100644 --- a/instrumentation/ratpack/ratpack-1.4/javaagent/build.gradle.kts +++ b/instrumentation/ratpack/ratpack-1.4/javaagent/build.gradle.kts @@ -14,6 +14,7 @@ dependencies { library("io.ratpack:ratpack-core:1.4.0") implementation(project(":instrumentation:netty:netty-4.1:javaagent")) + implementation(project(":instrumentation:netty:netty-4.1:library")) testImplementation(project(":instrumentation:ratpack:ratpack-1.4:testing")) diff --git a/instrumentation/ratpack/ratpack-1.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/ratpack/RatpackIgnoredTypesConfigurer.java b/instrumentation/ratpack/ratpack-1.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/ratpack/RatpackIgnoredTypesConfigurer.java index efbb24db1b71..165c21f1d6d9 100644 --- a/instrumentation/ratpack/ratpack-1.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/ratpack/RatpackIgnoredTypesConfigurer.java +++ b/instrumentation/ratpack/ratpack-1.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/ratpack/RatpackIgnoredTypesConfigurer.java @@ -14,7 +14,7 @@ public class RatpackIgnoredTypesConfigurer implements IgnoredTypesConfigurer { @Override - public void configure(ConfigProperties config, IgnoredTypesBuilder builder) { + public void configure(IgnoredTypesBuilder builder, ConfigProperties config) { // Context is passed through Netty channels in Ratpack as executor instrumentation is // not suitable. As the context that would be propagated via executor would be // incorrect, skip the propagation. Not checking for concrete class names as this covers diff --git a/instrumentation/ratpack/ratpack-1.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/ratpack/RatpackSingletons.java b/instrumentation/ratpack/ratpack-1.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/ratpack/RatpackSingletons.java index e037c3152b20..4e386bf6ef29 100644 --- a/instrumentation/ratpack/ratpack-1.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/ratpack/RatpackSingletons.java +++ b/instrumentation/ratpack/ratpack-1.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/ratpack/RatpackSingletons.java @@ -19,7 +19,7 @@ public final class RatpackSingletons { private static final Instrumenter INSTRUMENTER = Instrumenter.builder( GlobalOpenTelemetry.get(), "io.opentelemetry.ratpack-1.4", s -> s) - .newInstrumenter(); + .buildInstrumenter(); public static Instrumenter instrumenter() { return INSTRUMENTER; @@ -50,7 +50,7 @@ public static String updateServerSpanName( public static void onError(io.opentelemetry.context.Context context, Throwable error) { Span span = Span.fromContext(context); span.setStatus(StatusCode.ERROR); - span.recordException(ErrorCauseExtractor.jdk().extract(error)); + span.recordException(ErrorCauseExtractor.getDefault().extract(error)); } private RatpackSingletons() {} diff --git a/instrumentation/ratpack/ratpack-1.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/ratpack/TracingHandler.java b/instrumentation/ratpack/ratpack-1.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/ratpack/TracingHandler.java index 027348378ad3..7195272f9055 100644 --- a/instrumentation/ratpack/ratpack-1.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/ratpack/TracingHandler.java +++ b/instrumentation/ratpack/ratpack-1.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/ratpack/TracingHandler.java @@ -11,8 +11,8 @@ import io.netty.util.Attribute; import io.opentelemetry.context.Scope; +import io.opentelemetry.instrumentation.netty.v4_1.internal.AttributeKeys; import io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge; -import io.opentelemetry.javaagent.instrumentation.netty.v4_1.AttributeKeys; import ratpack.handling.Context; import ratpack.handling.Handler; diff --git a/instrumentation/ratpack/ratpack-1.4/javaagent/src/test/groovy/server/RatpackRoutesTest.groovy b/instrumentation/ratpack/ratpack-1.4/javaagent/src/test/groovy/server/RatpackRoutesTest.groovy index d2846e0d0f40..e52bd4f93c1e 100644 --- a/instrumentation/ratpack/ratpack-1.4/javaagent/src/test/groovy/server/RatpackRoutesTest.groovy +++ b/instrumentation/ratpack/ratpack-1.4/javaagent/src/test/groovy/server/RatpackRoutesTest.groovy @@ -5,7 +5,6 @@ package server - import io.opentelemetry.instrumentation.ratpack.server.AbstractRatpackRoutesTest import io.opentelemetry.instrumentation.test.AgentTestTrait import ratpack.server.RatpackServerSpec diff --git a/instrumentation/ratpack/ratpack-1.4/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/ratpack/RatpackForkedHttpClientTest.java b/instrumentation/ratpack/ratpack-1.4/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/ratpack/RatpackForkedHttpClientTest.java index ed453607a20d..84dee00acc4e 100644 --- a/instrumentation/ratpack/ratpack-1.4/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/ratpack/RatpackForkedHttpClientTest.java +++ b/instrumentation/ratpack/ratpack-1.4/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/ratpack/RatpackForkedHttpClientTest.java @@ -5,13 +5,27 @@ package io.opentelemetry.javaagent.instrumentation.ratpack; +import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.instrumentation.ratpack.client.AbstractRatpackForkedHttpClientTest; import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; import io.opentelemetry.instrumentation.testing.junit.http.HttpClientInstrumentationExtension; +import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; +import java.net.URI; +import java.util.HashSet; +import java.util.Set; import org.junit.jupiter.api.extension.RegisterExtension; class RatpackForkedHttpClientTest extends AbstractRatpackForkedHttpClientTest { @RegisterExtension static final InstrumentationExtension testing = HttpClientInstrumentationExtension.forAgent(); + + @Override + protected Set> computeHttpAttributes(URI uri) { + Set> attributes = new HashSet<>(super.computeHttpAttributes(uri)); + // underlying netty instrumentation does not provide these + attributes.remove(SemanticAttributes.NET_PEER_NAME); + attributes.remove(SemanticAttributes.NET_PEER_PORT); + return attributes; + } } diff --git a/instrumentation/ratpack/ratpack-1.4/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/ratpack/RatpackHttpClientTest.java b/instrumentation/ratpack/ratpack-1.4/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/ratpack/RatpackHttpClientTest.java index 13b941a21879..19ec05c26707 100644 --- a/instrumentation/ratpack/ratpack-1.4/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/ratpack/RatpackHttpClientTest.java +++ b/instrumentation/ratpack/ratpack-1.4/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/ratpack/RatpackHttpClientTest.java @@ -5,13 +5,27 @@ package io.opentelemetry.javaagent.instrumentation.ratpack; +import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.instrumentation.ratpack.client.AbstractRatpackHttpClientTest; import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; import io.opentelemetry.instrumentation.testing.junit.http.HttpClientInstrumentationExtension; +import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; +import java.net.URI; +import java.util.HashSet; +import java.util.Set; import org.junit.jupiter.api.extension.RegisterExtension; class RatpackHttpClientTest extends AbstractRatpackHttpClientTest { @RegisterExtension static final InstrumentationExtension testing = HttpClientInstrumentationExtension.forAgent(); + + @Override + protected Set> computeHttpAttributes(URI uri) { + Set> attributes = new HashSet<>(super.computeHttpAttributes(uri)); + // underlying netty instrumentation does not provide these + attributes.remove(SemanticAttributes.NET_PEER_NAME); + attributes.remove(SemanticAttributes.NET_PEER_PORT); + return attributes; + } } diff --git a/instrumentation/ratpack/ratpack-1.4/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/ratpack/RatpackPooledHttpClientTest.java b/instrumentation/ratpack/ratpack-1.4/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/ratpack/RatpackPooledHttpClientTest.java index 1b5863d5b0b1..4ebc83c89b26 100644 --- a/instrumentation/ratpack/ratpack-1.4/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/ratpack/RatpackPooledHttpClientTest.java +++ b/instrumentation/ratpack/ratpack-1.4/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/ratpack/RatpackPooledHttpClientTest.java @@ -5,13 +5,27 @@ package io.opentelemetry.javaagent.instrumentation.ratpack; +import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.instrumentation.ratpack.client.AbstractRatpackPooledHttpClientTest; import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; import io.opentelemetry.instrumentation.testing.junit.http.HttpClientInstrumentationExtension; +import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; +import java.net.URI; +import java.util.HashSet; +import java.util.Set; import org.junit.jupiter.api.extension.RegisterExtension; class RatpackPooledHttpClientTest extends AbstractRatpackPooledHttpClientTest { @RegisterExtension static final InstrumentationExtension testing = HttpClientInstrumentationExtension.forAgent(); + + @Override + protected Set> computeHttpAttributes(URI uri) { + Set> attributes = new HashSet<>(super.computeHttpAttributes(uri)); + // underlying netty instrumentation does not provide these + attributes.remove(SemanticAttributes.NET_PEER_NAME); + attributes.remove(SemanticAttributes.NET_PEER_PORT); + return attributes; + } } diff --git a/instrumentation/ratpack/ratpack-1.4/testing/src/main/groovy/io/opentelemetry/instrumentation/ratpack/server/AbstractRatpackRoutesTest.groovy b/instrumentation/ratpack/ratpack-1.4/testing/src/main/groovy/io/opentelemetry/instrumentation/ratpack/server/AbstractRatpackRoutesTest.groovy index b459bcb5d2a7..32dd53cfeaaa 100644 --- a/instrumentation/ratpack/ratpack-1.4/testing/src/main/groovy/io/opentelemetry/instrumentation/ratpack/server/AbstractRatpackRoutesTest.groovy +++ b/instrumentation/ratpack/ratpack-1.4/testing/src/main/groovy/io/opentelemetry/instrumentation/ratpack/server/AbstractRatpackRoutesTest.groovy @@ -96,19 +96,20 @@ abstract class AbstractRatpackRoutesTest extends InstrumentationSpecification { hasNoParent() attributes { "$SemanticAttributes.NET_TRANSPORT" IP_TCP - // net.peer.name resolves to "127.0.0.1" on windows which is same as net.peer.ip so then not captured - "$SemanticAttributes.NET_PEER_NAME" { it == null || it == "localhost" } - "$SemanticAttributes.NET_PEER_IP" { it == null || it == "127.0.0.1" } - "$SemanticAttributes.NET_PEER_PORT" Long - + "$SemanticAttributes.NET_HOST_NAME" { it == "localhost" || it == null } + "$SemanticAttributes.NET_HOST_PORT" { it == app.bindPort || it == null } + "$SemanticAttributes.NET_SOCK_PEER_ADDR" { it == "127.0.0.1" || it == null } + "$SemanticAttributes.NET_SOCK_PEER_PORT" { it instanceof Long || it == null } + "$SemanticAttributes.NET_SOCK_HOST_ADDR" { it == "127.0.0.1" || it == null } + "$SemanticAttributes.NET_SOCK_HOST_PORT" { it instanceof Long || it == null } "$SemanticAttributes.HTTP_METHOD" "GET" "$SemanticAttributes.HTTP_STATUS_CODE" 200 "$SemanticAttributes.HTTP_FLAVOR" "1.1" "$SemanticAttributes.HTTP_USER_AGENT" String "$SemanticAttributes.HTTP_SCHEME" "http" - "$SemanticAttributes.HTTP_HOST" "localhost:${app.bindPort}" "$SemanticAttributes.HTTP_TARGET" "/$path" "$SemanticAttributes.HTTP_ROUTE" "/$route" + "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" Long } } if (hasHandlerSpan()) { diff --git a/instrumentation/ratpack/ratpack-1.4/testing/src/main/java/io/opentelemetry/instrumentation/ratpack/client/AbstractRatpackHttpClientTest.java b/instrumentation/ratpack/ratpack-1.4/testing/src/main/java/io/opentelemetry/instrumentation/ratpack/client/AbstractRatpackHttpClientTest.java index 010bcb1ece39..53d0df256874 100644 --- a/instrumentation/ratpack/ratpack-1.4/testing/src/main/java/io/opentelemetry/instrumentation/ratpack/client/AbstractRatpackHttpClientTest.java +++ b/instrumentation/ratpack/ratpack-1.4/testing/src/main/java/io/opentelemetry/instrumentation/ratpack/client/AbstractRatpackHttpClientTest.java @@ -7,12 +7,14 @@ import io.netty.channel.ConnectTimeoutException; import io.netty.handler.timeout.ReadTimeoutException; +import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.instrumentation.testing.junit.http.AbstractHttpClientTest; import io.opentelemetry.instrumentation.testing.junit.http.HttpClientTestOptions; import java.net.URI; import java.time.Duration; import java.util.Collections; import java.util.Map; +import java.util.Set; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.condition.OS; @@ -139,16 +141,7 @@ protected void configure(HttpClientTestOptions options) { return exception; }); - options.setHttpAttributes( - uri -> { - switch (uri.toString()) { - case "http://localhost:61/": // unopened port - case "https://192.0.2.1/": // non routable address - return Collections.emptySet(); - default: - return HttpClientTestOptions.DEFAULT_HTTP_ATTRIBUTES; - } - }); + options.setHttpAttributes(this::computeHttpAttributes); options.disableTestRedirects(); @@ -157,4 +150,14 @@ protected void configure(HttpClientTestOptions options) { options.enableTestReadTimeout(); } + + protected Set> computeHttpAttributes(URI uri) { + switch (uri.toString()) { + case "http://localhost:61/": // unopened port + case "https://192.0.2.1/": // non routable address + return Collections.emptySet(); + default: + return HttpClientTestOptions.DEFAULT_HTTP_ATTRIBUTES; + } + } } diff --git a/instrumentation/ratpack/ratpack-1.7/library/src/main/java/io/opentelemetry/instrumentation/ratpack/RatpackHttpAttributesGetter.java b/instrumentation/ratpack/ratpack-1.7/library/src/main/java/io/opentelemetry/instrumentation/ratpack/RatpackHttpAttributesGetter.java index 232f1e02dce6..4016907d2ca9 100644 --- a/instrumentation/ratpack/ratpack-1.7/library/src/main/java/io/opentelemetry/instrumentation/ratpack/RatpackHttpAttributesGetter.java +++ b/instrumentation/ratpack/ratpack-1.7/library/src/main/java/io/opentelemetry/instrumentation/ratpack/RatpackHttpAttributesGetter.java @@ -54,18 +54,6 @@ public List requestHeader(Request request, String name) { return request.getHeaders().getAll(name); } - @Override - @Nullable - public Long requestContentLength(Request request, @Nullable Response response) { - return null; - } - - @Override - @Nullable - public Long requestContentLengthUncompressed(Request request, @Nullable Response response) { - return null; - } - @Override @Nullable public String flavor(Request request) { @@ -83,28 +71,10 @@ public String flavor(Request request) { } @Override - @Nullable - public String serverName(Request request) { - return null; - } - - @Override - public Integer statusCode(Request request, Response response) { + public Integer statusCode(Request request, Response response, @Nullable Throwable error) { return response.getStatus().getCode(); } - @Override - @Nullable - public Long responseContentLength(Request request, Response response) { - return null; - } - - @Override - @Nullable - public Long responseContentLengthUncompressed(Request request, Response response) { - return null; - } - @Override public List responseHeader(Request request, Response response, String name) { return response.getHeaders().getAll(name); diff --git a/instrumentation/ratpack/ratpack-1.7/library/src/main/java/io/opentelemetry/instrumentation/ratpack/RatpackHttpClientAttributesGetter.java b/instrumentation/ratpack/ratpack-1.7/library/src/main/java/io/opentelemetry/instrumentation/ratpack/RatpackHttpClientAttributesGetter.java index 57694d5b6c8b..d06c96d95786 100644 --- a/instrumentation/ratpack/ratpack-1.7/library/src/main/java/io/opentelemetry/instrumentation/ratpack/RatpackHttpClientAttributesGetter.java +++ b/instrumentation/ratpack/ratpack-1.7/library/src/main/java/io/opentelemetry/instrumentation/ratpack/RatpackHttpClientAttributesGetter.java @@ -38,37 +38,12 @@ public List requestHeader(RequestSpec requestSpec, String name) { return requestSpec.getHeaders().getAll(name); } - @Nullable - @Override - public Long requestContentLength(RequestSpec requestSpec, @Nullable HttpResponse httpResponse) { - return null; - } - - @Nullable - @Override - public Long requestContentLengthUncompressed( - RequestSpec requestSpec, @Nullable HttpResponse httpResponse) { - return null; - } - @Override - public Integer statusCode(RequestSpec requestSpec, HttpResponse httpResponse) { + public Integer statusCode( + RequestSpec requestSpec, HttpResponse httpResponse, @Nullable Throwable error) { return httpResponse.getStatusCode(); } - @Nullable - @Override - public Long responseContentLength(RequestSpec requestSpec, HttpResponse httpResponse) { - return null; - } - - @Nullable - @Override - public Long responseContentLengthUncompressed( - RequestSpec requestSpec, HttpResponse httpResponse) { - return null; - } - @Override public List responseHeader( RequestSpec requestSpec, HttpResponse httpResponse, String name) { diff --git a/instrumentation/ratpack/ratpack-1.7/library/src/main/java/io/opentelemetry/instrumentation/ratpack/RatpackTelemetryBuilder.java b/instrumentation/ratpack/ratpack-1.7/library/src/main/java/io/opentelemetry/instrumentation/ratpack/RatpackTelemetryBuilder.java index 8850b4f61b0e..fcd1855ad27b 100644 --- a/instrumentation/ratpack/ratpack-1.7/library/src/main/java/io/opentelemetry/instrumentation/ratpack/RatpackTelemetryBuilder.java +++ b/instrumentation/ratpack/ratpack-1.7/library/src/main/java/io/opentelemetry/instrumentation/ratpack/RatpackTelemetryBuilder.java @@ -5,6 +5,7 @@ package io.opentelemetry.instrumentation.ratpack; +import com.google.errorprone.annotations.CanIgnoreReturnValue; import io.opentelemetry.api.OpenTelemetry; import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; @@ -16,7 +17,6 @@ import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanNameExtractor; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanStatusExtractor; import io.opentelemetry.instrumentation.api.instrumenter.net.NetClientAttributesExtractor; -import io.opentelemetry.instrumentation.api.instrumenter.net.NetServerAttributesExtractor; import io.opentelemetry.instrumentation.ratpack.internal.RatpackHttpNetAttributesGetter; import io.opentelemetry.instrumentation.ratpack.internal.RatpackNetAttributesGetter; import java.util.ArrayList; @@ -40,7 +40,8 @@ public final class RatpackTelemetryBuilder { HttpClientAttributesExtractor.builder(RatpackHttpClientAttributesGetter.INSTANCE); private final HttpServerAttributesExtractorBuilder httpServerAttributesExtractorBuilder = - HttpServerAttributesExtractor.builder(RatpackHttpAttributesGetter.INSTANCE); + HttpServerAttributesExtractor.builder( + RatpackHttpAttributesGetter.INSTANCE, new RatpackNetAttributesGetter()); private final List> additionalHttpClientExtractors = new ArrayList<>(); @@ -53,12 +54,14 @@ public final class RatpackTelemetryBuilder { * Adds an additional {@link AttributesExtractor} to invoke to set attributes to instrumented * items. The {@link AttributesExtractor} will be executed after all default extractors. */ + @CanIgnoreReturnValue public RatpackTelemetryBuilder addAttributeExtractor( AttributesExtractor attributesExtractor) { additionalExtractors.add(attributesExtractor); return this; } + @CanIgnoreReturnValue public RatpackTelemetryBuilder addClientAttributeExtractor( AttributesExtractor attributesExtractor) { additionalHttpClientExtractors.add(attributesExtractor); @@ -70,6 +73,7 @@ public RatpackTelemetryBuilder addClientAttributeExtractor( * * @param requestHeaders A list of HTTP header names. */ + @CanIgnoreReturnValue public RatpackTelemetryBuilder setCapturedServerRequestHeaders(List requestHeaders) { httpServerAttributesExtractorBuilder.setCapturedRequestHeaders(requestHeaders); return this; @@ -80,6 +84,7 @@ public RatpackTelemetryBuilder setCapturedServerRequestHeaders(List requ * * @param responseHeaders A list of HTTP header names. */ + @CanIgnoreReturnValue public RatpackTelemetryBuilder setCapturedServerResponseHeaders(List responseHeaders) { httpServerAttributesExtractorBuilder.setCapturedResponseHeaders(responseHeaders); return this; @@ -90,6 +95,7 @@ public RatpackTelemetryBuilder setCapturedServerResponseHeaders(List res * * @param requestHeaders A list of HTTP header names. */ + @CanIgnoreReturnValue public RatpackTelemetryBuilder setCapturedClientRequestHeaders(List requestHeaders) { httpClientAttributesExtractorBuilder.setCapturedRequestHeaders(requestHeaders); return this; @@ -100,6 +106,7 @@ public RatpackTelemetryBuilder setCapturedClientRequestHeaders(List requ * * @param responseHeaders A list of HTTP header names. */ + @CanIgnoreReturnValue public RatpackTelemetryBuilder setCapturedClientResponseHeaders(List responseHeaders) { httpClientAttributesExtractorBuilder.setCapturedResponseHeaders(responseHeaders); return this; @@ -107,18 +114,16 @@ public RatpackTelemetryBuilder setCapturedClientResponseHeaders(List res /** Returns a new {@link RatpackTelemetry} with the configuration of this builder. */ public RatpackTelemetry build() { - RatpackNetAttributesGetter netAttributes = new RatpackNetAttributesGetter(); RatpackHttpAttributesGetter httpAttributes = RatpackHttpAttributesGetter.INSTANCE; Instrumenter instrumenter = Instrumenter.builder( openTelemetry, INSTRUMENTATION_NAME, HttpSpanNameExtractor.create(httpAttributes)) .setSpanStatusExtractor(HttpSpanStatusExtractor.create(httpAttributes)) - .addAttributesExtractor(NetServerAttributesExtractor.create(netAttributes)) .addAttributesExtractor(httpServerAttributesExtractorBuilder.build()) .addAttributesExtractors(additionalExtractors) .addOperationMetrics(HttpServerMetrics.get()) - .newServerInstrumenter(RatpackGetter.INSTANCE); + .buildServerInstrumenter(RatpackGetter.INSTANCE); return new RatpackTelemetry(instrumenter, httpClientInstrumenter()); } @@ -134,6 +139,6 @@ private Instrumenter httpClientInstrumenter() { .addAttributesExtractor(httpClientAttributesExtractorBuilder.build()) .addAttributesExtractors(additionalHttpClientExtractors) .addOperationMetrics(HttpServerMetrics.get()) - .newClientInstrumenter(RequestHeaderSetter.INSTANCE); + .buildClientInstrumenter(RequestHeaderSetter.INSTANCE); } } diff --git a/instrumentation/ratpack/ratpack-1.7/library/src/main/java/io/opentelemetry/instrumentation/ratpack/internal/RatpackHttpNetAttributesGetter.java b/instrumentation/ratpack/ratpack-1.7/library/src/main/java/io/opentelemetry/instrumentation/ratpack/internal/RatpackHttpNetAttributesGetter.java index 27584507426f..b10f3041d4ac 100644 --- a/instrumentation/ratpack/ratpack-1.7/library/src/main/java/io/opentelemetry/instrumentation/ratpack/internal/RatpackHttpNetAttributesGetter.java +++ b/instrumentation/ratpack/ratpack-1.7/library/src/main/java/io/opentelemetry/instrumentation/ratpack/internal/RatpackHttpNetAttributesGetter.java @@ -17,6 +17,7 @@ */ public final class RatpackHttpNetAttributesGetter implements NetClientAttributesGetter { + @Override public String transport(RequestSpec request, @Nullable HttpResponse response) { return SemanticAttributes.NetTransportValues.IP_TCP; @@ -24,18 +25,12 @@ public String transport(RequestSpec request, @Nullable HttpResponse response) { @Override @Nullable - public String peerName(RequestSpec request, @Nullable HttpResponse response) { + public String peerName(RequestSpec request) { return request.getUri().getHost(); } @Override - public Integer peerPort(RequestSpec request, @Nullable HttpResponse response) { + public Integer peerPort(RequestSpec request) { return request.getUri().getPort(); } - - @Override - @Nullable - public String peerIp(RequestSpec request, @Nullable HttpResponse response) { - return null; - } } diff --git a/instrumentation/ratpack/ratpack-1.7/library/src/main/java/io/opentelemetry/instrumentation/ratpack/internal/RatpackNetAttributesGetter.java b/instrumentation/ratpack/ratpack-1.7/library/src/main/java/io/opentelemetry/instrumentation/ratpack/internal/RatpackNetAttributesGetter.java index e11457f58457..d52e1ee15e66 100644 --- a/instrumentation/ratpack/ratpack-1.7/library/src/main/java/io/opentelemetry/instrumentation/ratpack/internal/RatpackNetAttributesGetter.java +++ b/instrumentation/ratpack/ratpack-1.7/library/src/main/java/io/opentelemetry/instrumentation/ratpack/internal/RatpackNetAttributesGetter.java @@ -8,7 +8,9 @@ import io.opentelemetry.instrumentation.api.instrumenter.net.NetServerAttributesGetter; import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; import javax.annotation.Nullable; +import ratpack.handling.Context; import ratpack.http.Request; +import ratpack.server.PublicAddress; /** * This class is internal and is hence not for public use. Its APIs are unstable and can change at @@ -20,14 +22,30 @@ public String transport(Request request) { return SemanticAttributes.NetTransportValues.IP_TCP; } + @Nullable @Override - public Integer peerPort(Request request) { - return request.getRemoteAddress().getPort(); + public String hostName(Request request) { + PublicAddress publicAddress = getPublicAddress(request); + return publicAddress == null ? null : publicAddress.get().getHost(); } - @Override @Nullable - public String peerIp(Request request) { - return null; + @Override + public Integer hostPort(Request request) { + PublicAddress publicAddress = getPublicAddress(request); + return publicAddress == null ? null : publicAddress.get().getPort(); + } + + private static PublicAddress getPublicAddress(Request request) { + Context ratpackContext = request.get(Context.class); + if (ratpackContext == null) { + return null; + } + return ratpackContext.get(PublicAddress.class); + } + + @Override + public Integer sockPeerPort(Request request) { + return request.getRemoteAddress().getPort(); } } diff --git a/instrumentation/ratpack/ratpack-1.7/library/src/test/groovy/io/opentelemetry/instrumentation/ratpack/server/RatpackRoutesTest.groovy b/instrumentation/ratpack/ratpack-1.7/library/src/test/groovy/io/opentelemetry/instrumentation/ratpack/server/RatpackRoutesTest.groovy index 0498f3bd2171..e714669f29ea 100644 --- a/instrumentation/ratpack/ratpack-1.7/library/src/test/groovy/io/opentelemetry/instrumentation/ratpack/server/RatpackRoutesTest.groovy +++ b/instrumentation/ratpack/ratpack-1.7/library/src/test/groovy/io/opentelemetry/instrumentation/ratpack/server/RatpackRoutesTest.groovy @@ -5,7 +5,6 @@ package io.opentelemetry.instrumentation.ratpack.server - import io.opentelemetry.instrumentation.ratpack.RatpackTelemetry import io.opentelemetry.instrumentation.test.LibraryTestTrait import ratpack.server.RatpackServerSpec diff --git a/instrumentation/reactor/reactor-3.1/javaagent/build.gradle.kts b/instrumentation/reactor/reactor-3.1/javaagent/build.gradle.kts index b373235368b0..cf39781e5a0e 100644 --- a/instrumentation/reactor/reactor-3.1/javaagent/build.gradle.kts +++ b/instrumentation/reactor/reactor-3.1/javaagent/build.gradle.kts @@ -28,13 +28,13 @@ dependencies { compileOnly(project(":instrumentation-annotations-support")) compileOnly(project(":opentelemetry-api-shaded-for-instrumenting", configuration = "shadow")) + testInstrumentation(project(":instrumentation:opentelemetry-extension-annotations-1.0:javaagent")) + testLibrary("io.projectreactor:reactor-test:3.1.0.RELEASE") testImplementation(project(":instrumentation-annotations-support-testing")) testImplementation(project(":instrumentation:reactor:reactor-3.1:testing")) + testImplementation(project(":instrumentation-annotations")) testImplementation("io.opentelemetry:opentelemetry-extension-annotations") - - // Looks like later versions on reactor need this dependency for some reason even though it is marked as optional. - latestDepTestLibrary("io.micrometer:micrometer-core:1.+") } testing { @@ -42,7 +42,7 @@ testing { val testInitialization by registering(JvmTestSuite::class) { dependencies { implementation(project(":instrumentation:reactor:reactor-3.1:library")) - implementation("io.opentelemetry:opentelemetry-extension-annotations") + implementation(project(":instrumentation-annotations")) implementation("io.projectreactor:reactor-test:3.1.0.RELEASE") } } diff --git a/instrumentation/reactor/reactor-3.1/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/reactor/BaseFluxWithSpanTest.java b/instrumentation/reactor/reactor-3.1/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/reactor/BaseFluxWithSpanTest.java index c9b42fcc834a..29efae0ca014 100644 --- a/instrumentation/reactor/reactor-3.1/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/reactor/BaseFluxWithSpanTest.java +++ b/instrumentation/reactor/reactor-3.1/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/reactor/BaseFluxWithSpanTest.java @@ -5,6 +5,10 @@ package io.opentelemetry.javaagent.instrumentation.reactor; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; +import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.CODE_FUNCTION; +import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.CODE_NAMESPACE; + import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.trace.SpanKind; import io.opentelemetry.javaagent.instrumentation.otelannotations.AbstractWithSpanTest; @@ -58,7 +62,8 @@ void nested() { return Flux.just("Value"); }); - Flux result = newTracedWithSpan().flux(flux); + TracedWithSpan traced = newTracedWithSpan(); + Flux result = traced.flux(flux); StepVerifier.create(result).expectNext("Value").verifyComplete(); @@ -70,7 +75,9 @@ void nested() { span.hasName("TracedWithSpan.flux") .hasKind(SpanKind.INTERNAL) .hasNoParent() - .hasAttributes(Attributes.empty()), + .hasAttributesSatisfyingExactly( + equalTo(CODE_NAMESPACE, traced.getClass().getName()), + equalTo(CODE_FUNCTION, "flux")), span -> span.hasName("inner-manual") .hasKind(SpanKind.INTERNAL) @@ -80,18 +87,19 @@ void nested() { @Test void nestedFromCurrent() { + TracedWithSpan traced = newTracedWithSpan(); + testing() .runWithSpan( "parent", () -> { Flux result = - newTracedWithSpan() - .flux( - Flux.defer( - () -> { - testing().runWithSpan("inner-manual", () -> {}); - return Flux.just("Value"); - })); + traced.flux( + Flux.defer( + () -> { + testing().runWithSpan("inner-manual", () -> {}); + return Flux.just("Value"); + })); StepVerifier.create(result).expectNext("Value").verifyComplete(); }); @@ -109,7 +117,9 @@ void nestedFromCurrent() { span.hasName("TracedWithSpan.flux") .hasKind(SpanKind.INTERNAL) .hasParent(trace.getSpan(0)) - .hasAttributes(Attributes.empty()), + .hasAttributesSatisfyingExactly( + equalTo(CODE_NAMESPACE, traced.getClass().getName()), + equalTo(CODE_FUNCTION, "flux")), span -> span.hasName("inner-manual") .hasKind(SpanKind.INTERNAL) diff --git a/instrumentation/reactor/reactor-3.1/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/reactor/BaseMonoWithSpanTest.java b/instrumentation/reactor/reactor-3.1/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/reactor/BaseMonoWithSpanTest.java index dc23d9841ca9..10d4bec55b57 100644 --- a/instrumentation/reactor/reactor-3.1/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/reactor/BaseMonoWithSpanTest.java +++ b/instrumentation/reactor/reactor-3.1/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/reactor/BaseMonoWithSpanTest.java @@ -5,6 +5,10 @@ package io.opentelemetry.javaagent.instrumentation.reactor; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; +import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.CODE_FUNCTION; +import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.CODE_NAMESPACE; + import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.trace.SpanKind; import io.opentelemetry.javaagent.instrumentation.otelannotations.AbstractWithSpanTest; @@ -58,7 +62,8 @@ void nested() { return Mono.just("Value"); }); - Mono result = newTracedWithSpan().outer(mono); + TracedWithSpan traced = newTracedWithSpan(); + Mono result = traced.outer(mono); StepVerifier.create(result).expectNext("Value").verifyComplete(); @@ -70,12 +75,16 @@ void nested() { span.hasName("TracedWithSpan.outer") .hasKind(SpanKind.INTERNAL) .hasNoParent() - .hasAttributes(Attributes.empty()), + .hasAttributesSatisfyingExactly( + equalTo(CODE_NAMESPACE, traced.getClass().getName()), + equalTo(CODE_FUNCTION, "outer")), span -> span.hasName("TracedWithSpan.mono") .hasKind(SpanKind.INTERNAL) .hasParent(trace.getSpan(0)) - .hasAttributes(Attributes.empty()), + .hasAttributesSatisfyingExactly( + equalTo(CODE_NAMESPACE, traced.getClass().getName()), + equalTo(CODE_FUNCTION, "mono")), span -> span.hasName("inner-manual") .hasKind(SpanKind.INTERNAL) @@ -85,18 +94,19 @@ void nested() { @Test void nestedFromCurrent() { + TracedWithSpan traced = newTracedWithSpan(); + testing() .runWithSpan( "parent", () -> { Mono result = - newTracedWithSpan() - .mono( - Mono.defer( - () -> { - testing().runWithSpan("inner-manual", () -> {}); - return Mono.just("Value"); - })); + traced.mono( + Mono.defer( + () -> { + testing().runWithSpan("inner-manual", () -> {}); + return Mono.just("Value"); + })); StepVerifier.create(result).expectNext("Value").verifyComplete(); }); @@ -114,7 +124,9 @@ void nestedFromCurrent() { span.hasName("TracedWithSpan.mono") .hasKind(SpanKind.INTERNAL) .hasParent(trace.getSpan(0)) - .hasAttributes(Attributes.empty()), + .hasAttributesSatisfyingExactly( + equalTo(CODE_NAMESPACE, traced.getClass().getName()), + equalTo(CODE_FUNCTION, "mono")), span -> span.hasName("inner-manual") .hasKind(SpanKind.INTERNAL) diff --git a/instrumentation/reactor/reactor-3.1/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/reactor/ExtensionAnnotationsFluxWithSpanTest.java b/instrumentation/reactor/reactor-3.1/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/reactor/ExtensionAnnotationsFluxWithSpanTest.java index 6407a597ce22..e8d991ac70df 100644 --- a/instrumentation/reactor/reactor-3.1/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/reactor/ExtensionAnnotationsFluxWithSpanTest.java +++ b/instrumentation/reactor/reactor-3.1/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/reactor/ExtensionAnnotationsFluxWithSpanTest.java @@ -5,11 +5,11 @@ package io.opentelemetry.javaagent.instrumentation.reactor; -import io.opentelemetry.extension.annotations.WithSpan; import io.opentelemetry.javaagent.instrumentation.otelannotations.AbstractTraced; import reactor.core.publisher.Flux; import reactor.core.publisher.UnicastProcessor; +@SuppressWarnings("deprecation") // testing instrumentation of deprecated class class ExtensionAnnotationsFluxWithSpanTest extends BaseFluxWithSpanTest { @Override @@ -25,19 +25,19 @@ TracedWithSpan newTracedWithSpan() { static class Traced extends AbstractTraced, Flux> { @Override - @WithSpan + @io.opentelemetry.extension.annotations.WithSpan protected Flux completable() { return UnicastProcessor.create(); } @Override - @WithSpan + @io.opentelemetry.extension.annotations.WithSpan protected Flux alreadySucceeded() { return Flux.just(SUCCESS_VALUE); } @Override - @WithSpan + @io.opentelemetry.extension.annotations.WithSpan protected Flux alreadyFailed() { return Flux.error(FAILURE); } diff --git a/instrumentation/reactor/reactor-3.1/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/reactor/ExtensionAnnotationsMonoWithSpanTest.java b/instrumentation/reactor/reactor-3.1/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/reactor/ExtensionAnnotationsMonoWithSpanTest.java index bc7c20d781c6..ac2aadccd042 100644 --- a/instrumentation/reactor/reactor-3.1/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/reactor/ExtensionAnnotationsMonoWithSpanTest.java +++ b/instrumentation/reactor/reactor-3.1/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/reactor/ExtensionAnnotationsMonoWithSpanTest.java @@ -5,11 +5,11 @@ package io.opentelemetry.javaagent.instrumentation.reactor; -import io.opentelemetry.extension.annotations.WithSpan; import io.opentelemetry.javaagent.instrumentation.otelannotations.AbstractTraced; import reactor.core.publisher.Mono; import reactor.core.publisher.UnicastProcessor; +@SuppressWarnings("deprecation") // testing instrumentation of deprecated class class ExtensionAnnotationsMonoWithSpanTest extends BaseMonoWithSpanTest { @Override @@ -25,20 +25,20 @@ TracedWithSpan newTracedWithSpan() { static class Traced extends AbstractTraced, Mono> { @Override - @WithSpan + @io.opentelemetry.extension.annotations.WithSpan protected Mono completable() { UnicastProcessor source = UnicastProcessor.create(); return source.singleOrEmpty(); } @Override - @WithSpan + @io.opentelemetry.extension.annotations.WithSpan protected Mono alreadySucceeded() { return Mono.just(SUCCESS_VALUE); } @Override - @WithSpan + @io.opentelemetry.extension.annotations.WithSpan protected Mono alreadyFailed() { return Mono.error(FAILURE); } diff --git a/instrumentation/reactor/reactor-3.1/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/reactor/ExtensionAnnotationsTracedWithSpan.java b/instrumentation/reactor/reactor-3.1/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/reactor/ExtensionAnnotationsTracedWithSpan.java index a596e9537b13..8dc156c7b6a3 100644 --- a/instrumentation/reactor/reactor-3.1/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/reactor/ExtensionAnnotationsTracedWithSpan.java +++ b/instrumentation/reactor/reactor-3.1/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/reactor/ExtensionAnnotationsTracedWithSpan.java @@ -5,26 +5,26 @@ package io.opentelemetry.javaagent.instrumentation.reactor; -import io.opentelemetry.extension.annotations.WithSpan; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; +@SuppressWarnings("deprecation") // testing instrumentation of deprecated class public class ExtensionAnnotationsTracedWithSpan implements TracedWithSpan { @Override - @WithSpan("TracedWithSpan.mono") + @io.opentelemetry.extension.annotations.WithSpan("TracedWithSpan.mono") public Mono mono(Mono mono) { return mono; } @Override - @WithSpan("TracedWithSpan.outer") + @io.opentelemetry.extension.annotations.WithSpan("TracedWithSpan.outer") public Mono outer(Mono inner) { return mono(inner); } @Override - @WithSpan("TracedWithSpan.flux") + @io.opentelemetry.extension.annotations.WithSpan("TracedWithSpan.flux") public Flux flux(Flux flux) { return flux; } diff --git a/instrumentation/reactor/reactor-3.1/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/reactor/InstrumentationAnnotationsFluxWithSpanTest.java b/instrumentation/reactor/reactor-3.1/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/reactor/InstrumentationAnnotationsFluxWithSpanTest.java new file mode 100644 index 000000000000..9d376faaef0c --- /dev/null +++ b/instrumentation/reactor/reactor-3.1/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/reactor/InstrumentationAnnotationsFluxWithSpanTest.java @@ -0,0 +1,45 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.reactor; + +import io.opentelemetry.instrumentation.annotations.WithSpan; +import io.opentelemetry.javaagent.instrumentation.otelannotations.AbstractTraced; +import reactor.core.publisher.Flux; +import reactor.core.publisher.UnicastProcessor; + +class InstrumentationAnnotationsFluxWithSpanTest extends BaseFluxWithSpanTest { + + @Override + protected AbstractTraced, Flux> newTraced() { + return new Traced(); + } + + @Override + TracedWithSpan newTracedWithSpan() { + return new ExtensionAnnotationsTracedWithSpan(); + } + + static class Traced extends AbstractTraced, Flux> { + + @Override + @WithSpan + protected Flux completable() { + return UnicastProcessor.create(); + } + + @Override + @WithSpan + protected Flux alreadySucceeded() { + return Flux.just(SUCCESS_VALUE); + } + + @Override + @WithSpan + protected Flux alreadyFailed() { + return Flux.error(FAILURE); + } + } +} diff --git a/instrumentation/reactor/reactor-3.1/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/reactor/InstrumentationAnnotationsMonoWithSpanTest.java b/instrumentation/reactor/reactor-3.1/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/reactor/InstrumentationAnnotationsMonoWithSpanTest.java new file mode 100644 index 000000000000..30aebee6969c --- /dev/null +++ b/instrumentation/reactor/reactor-3.1/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/reactor/InstrumentationAnnotationsMonoWithSpanTest.java @@ -0,0 +1,46 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.reactor; + +import io.opentelemetry.instrumentation.annotations.WithSpan; +import io.opentelemetry.javaagent.instrumentation.otelannotations.AbstractTraced; +import reactor.core.publisher.Mono; +import reactor.core.publisher.UnicastProcessor; + +class InstrumentationAnnotationsMonoWithSpanTest extends BaseMonoWithSpanTest { + + @Override + protected AbstractTraced, Mono> newTraced() { + return new Traced(); + } + + @Override + TracedWithSpan newTracedWithSpan() { + return new ExtensionAnnotationsTracedWithSpan(); + } + + static class Traced extends AbstractTraced, Mono> { + + @Override + @WithSpan + protected Mono completable() { + UnicastProcessor source = UnicastProcessor.create(); + return source.singleOrEmpty(); + } + + @Override + @WithSpan + protected Mono alreadySucceeded() { + return Mono.just(SUCCESS_VALUE); + } + + @Override + @WithSpan + protected Mono alreadyFailed() { + return Mono.error(FAILURE); + } + } +} diff --git a/instrumentation/reactor/reactor-3.1/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/reactor/InstrumentationAnnotationsTracedWithSpan.java b/instrumentation/reactor/reactor-3.1/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/reactor/InstrumentationAnnotationsTracedWithSpan.java new file mode 100644 index 000000000000..9a295c7d0d96 --- /dev/null +++ b/instrumentation/reactor/reactor-3.1/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/reactor/InstrumentationAnnotationsTracedWithSpan.java @@ -0,0 +1,31 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.reactor; + +import io.opentelemetry.instrumentation.annotations.WithSpan; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +public class InstrumentationAnnotationsTracedWithSpan implements TracedWithSpan { + + @Override + @WithSpan("TracedWithSpan.mono") + public Mono mono(Mono mono) { + return mono; + } + + @Override + @WithSpan("TracedWithSpan.outer") + public Mono outer(Mono inner) { + return mono(inner); + } + + @Override + @WithSpan("TracedWithSpan.flux") + public Flux flux(Flux flux) { + return flux; + } +} diff --git a/instrumentation/reactor/reactor-3.1/javaagent/src/testInitialization/java/io/opentelemetry/javaagent/instrumentation/reactor/InitializationTest.java b/instrumentation/reactor/reactor-3.1/javaagent/src/testInitialization/java/io/opentelemetry/javaagent/instrumentation/reactor/InitializationTest.java index f254de54aa2b..0435e75271d6 100644 --- a/instrumentation/reactor/reactor-3.1/javaagent/src/testInitialization/java/io/opentelemetry/javaagent/instrumentation/reactor/InitializationTest.java +++ b/instrumentation/reactor/reactor-3.1/javaagent/src/testInitialization/java/io/opentelemetry/javaagent/instrumentation/reactor/InitializationTest.java @@ -7,7 +7,7 @@ import static org.assertj.core.api.Assertions.assertThat; -import io.opentelemetry.extension.annotations.WithSpan; +import io.opentelemetry.instrumentation.annotations.WithSpan; import java.util.stream.Collectors; import org.junit.jupiter.api.Test; import reactor.core.Scannable; diff --git a/instrumentation/reactor/reactor-3.1/library/build.gradle.kts b/instrumentation/reactor/reactor-3.1/library/build.gradle.kts index 2b4bdf7fa322..ef973abaa3f9 100644 --- a/instrumentation/reactor/reactor-3.1/library/build.gradle.kts +++ b/instrumentation/reactor/reactor-3.1/library/build.gradle.kts @@ -8,7 +8,4 @@ dependencies { testLibrary("io.projectreactor:reactor-test:3.1.0.RELEASE") testImplementation(project(":instrumentation:reactor:reactor-3.1:testing")) - - // Looks like later versions on reactor need this dependency for some reason even though it is marked as optional. - latestDepTestLibrary("io.micrometer:micrometer-core:1.+") } diff --git a/instrumentation/reactor/reactor-3.1/library/src/main/java/io/opentelemetry/instrumentation/reactor/ContextPropagationOperatorBuilder.java b/instrumentation/reactor/reactor-3.1/library/src/main/java/io/opentelemetry/instrumentation/reactor/ContextPropagationOperatorBuilder.java index ebe4f255fab7..04df31f4a0ab 100644 --- a/instrumentation/reactor/reactor-3.1/library/src/main/java/io/opentelemetry/instrumentation/reactor/ContextPropagationOperatorBuilder.java +++ b/instrumentation/reactor/reactor-3.1/library/src/main/java/io/opentelemetry/instrumentation/reactor/ContextPropagationOperatorBuilder.java @@ -5,11 +5,14 @@ package io.opentelemetry.instrumentation.reactor; +import com.google.errorprone.annotations.CanIgnoreReturnValue; + public final class ContextPropagationOperatorBuilder { private boolean captureExperimentalSpanAttributes; ContextPropagationOperatorBuilder() {} + @CanIgnoreReturnValue public ContextPropagationOperatorBuilder setCaptureExperimentalSpanAttributes( boolean captureExperimentalSpanAttributes) { this.captureExperimentalSpanAttributes = captureExperimentalSpanAttributes; diff --git a/instrumentation/reactor/reactor-3.1/library/src/main/java/io/opentelemetry/instrumentation/reactor/ReactorAsyncOperationEndStrategyBuilder.java b/instrumentation/reactor/reactor-3.1/library/src/main/java/io/opentelemetry/instrumentation/reactor/ReactorAsyncOperationEndStrategyBuilder.java index 2fc91e0372e3..dbf53da4ad79 100644 --- a/instrumentation/reactor/reactor-3.1/library/src/main/java/io/opentelemetry/instrumentation/reactor/ReactorAsyncOperationEndStrategyBuilder.java +++ b/instrumentation/reactor/reactor-3.1/library/src/main/java/io/opentelemetry/instrumentation/reactor/ReactorAsyncOperationEndStrategyBuilder.java @@ -5,11 +5,14 @@ package io.opentelemetry.instrumentation.reactor; +import com.google.errorprone.annotations.CanIgnoreReturnValue; + public final class ReactorAsyncOperationEndStrategyBuilder { private boolean captureExperimentalSpanAttributes; ReactorAsyncOperationEndStrategyBuilder() {} + @CanIgnoreReturnValue public ReactorAsyncOperationEndStrategyBuilder setCaptureExperimentalSpanAttributes( boolean captureExperimentalSpanAttributes) { this.captureExperimentalSpanAttributes = captureExperimentalSpanAttributes; diff --git a/instrumentation/reactor/reactor-3.1/library/src/main/java/io/opentelemetry/instrumentation/reactor/TracingSubscriber.java b/instrumentation/reactor/reactor-3.1/library/src/main/java/io/opentelemetry/instrumentation/reactor/TracingSubscriber.java index fae0af530fa4..355068bf1d8e 100644 --- a/instrumentation/reactor/reactor-3.1/library/src/main/java/io/opentelemetry/instrumentation/reactor/TracingSubscriber.java +++ b/instrumentation/reactor/reactor-3.1/library/src/main/java/io/opentelemetry/instrumentation/reactor/TracingSubscriber.java @@ -20,6 +20,7 @@ package io.opentelemetry.instrumentation.reactor; +import io.opentelemetry.api.trace.Span; import io.opentelemetry.context.Scope; import org.reactivestreams.Subscriber; import org.reactivestreams.Subscription; @@ -34,6 +35,7 @@ public class TracingSubscriber implements CoreSubscriber { private final io.opentelemetry.context.Context traceContext; private final Subscriber subscriber; private final Context context; + private final boolean hasContextToPropagate; public TracingSubscriber(Subscriber subscriber, Context ctx) { this(subscriber, ctx, io.opentelemetry.context.Context.current()); @@ -46,6 +48,8 @@ public TracingSubscriber( this.subscriber = subscriber; this.context = ctx; this.traceContext = ContextPropagationOperator.getOpenTelemetryContext(ctx, contextToPropagate); + this.hasContextToPropagate = + traceContext == null ? false : Span.fromContext(traceContext).getSpanContext().isValid(); } @Override @@ -74,7 +78,7 @@ public Context currentContext() { } private void withActiveSpan(Runnable runnable) { - if (traceContext != null) { + if (hasContextToPropagate) { try (Scope ignored = traceContext.makeCurrent()) { runnable.run(); } diff --git a/instrumentation/reactor/reactor-3.1/library/src/test/java/io/opentelemetry/instrumentation/reactor/ReactorCoreTest.java b/instrumentation/reactor/reactor-3.1/library/src/test/java/io/opentelemetry/instrumentation/reactor/ReactorCoreTest.java index aefefb572356..eff9c205b0a1 100644 --- a/instrumentation/reactor/reactor-3.1/library/src/test/java/io/opentelemetry/instrumentation/reactor/ReactorCoreTest.java +++ b/instrumentation/reactor/reactor-3.1/library/src/test/java/io/opentelemetry/instrumentation/reactor/ReactorCoreTest.java @@ -7,13 +7,16 @@ import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.attributeEntry; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.fail; import io.opentelemetry.api.trace.Span; import io.opentelemetry.api.trace.StatusCode; import io.opentelemetry.api.trace.Tracer; import io.opentelemetry.context.Context; +import io.opentelemetry.context.Scope; import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension; +import java.time.Duration; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; @@ -22,6 +25,7 @@ import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import reactor.core.publisher.UnicastProcessor; +import reactor.test.StepVerifier; class ReactorCoreTest extends AbstractReactorCoreTest { @@ -229,6 +233,135 @@ void fluxParentsAccessible() { .isPresent(); } + @Test + void doesNotOverrideInnerCurrentSpans() { + Flux publish = + Flux.create( + sink -> { + for (int i = 0; i < 2; i++) { + Span s = tracer.spanBuilder("inner").startSpan(); + try (Scope scope = s.makeCurrent()) { + sink.next(i); + } finally { + s.end(); + } + } + }); + + // as a result we'll have + // 1. publish subscriber that creates inner spans + // 2. tracing subscriber without current context - subscription was done outside any scope + // 3. inner subscriber that will add onNext attribute to inner spans + // I.e. tracing subscriber context (root) at subscription time will be different from inner in + // onNext + publish + .take(2) + .subscribe( + n -> { + assertThat(Span.current().getSpanContext().isValid()).isTrue(); + Span.current().setAttribute("onNext", true); + }, + error -> fail(error.getMessage())); + + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> + span.hasName("inner") + .hasNoParent() + .hasAttributes(attributeEntry("onNext", true))), + trace -> + trace.hasSpansSatisfyingExactly( + span -> + span.hasName("inner") + .hasNoParent() + .hasAttributes(attributeEntry("onNext", true)))); + } + + @Test + void doesNotOverrideInnerCurrentSpansAsync() { + Flux publish = + Flux.create( + sink -> { + Span s = tracer.spanBuilder("inner").startSpan(); + try (Scope scope = s.makeCurrent()) { + sink.next(s); + } finally { + s.end(); + } + }); + + publish + .take(1) + .delayElements(Duration.ofMillis(1)) + .doOnNext( + span -> { + assertThat(Span.current().getSpanContext().isValid()).isTrue(); + assertThat(Span.current()).isSameAs(span); + }) + .subscribe( + span -> assertThat(Span.current()).isSameAs(span), error -> fail(error.getMessage())); + + testing.waitAndAssertTraces( + trace -> trace.hasSpansSatisfyingExactly(span -> span.hasName("inner").hasNoParent())); + } + + @Test + void doesNotOverrideInnerCurrentSpansWithThereIsOuterCurrent() { + Flux publish = + Flux.create( + sink -> { + for (int i = 0; i < 2; i++) { + Span s = tracer.spanBuilder("inner").startSpan(); + try (Scope scope = s.makeCurrent()) { + sink.next(i); + } finally { + s.end(); + } + } + }); + + // as a result we'll have + // 1. publish subscriber that creates inner spans + // 2. tracing subscriber with outer context - it was active at subscription time + // 3. inner subscriber that will add onNext attribute + // I.e. tracing subscriber context at subscription time will be different from inner in onNext + Span outer = tracer.spanBuilder("outer").startSpan(); + try (Scope scope = outer.makeCurrent()) { + StepVerifier.create( + publish + .take(2) + .doOnNext( + n -> { + assertThat(Span.current().getSpanContext().isValid()).isTrue(); + Span.current().setAttribute("onNext", true); + }) + .subscriberContext( + // subscribers that know that their subscription can happen + // ahead of time and in the 'wrong' context, has to clean up 'wrong' context + context -> + ContextPropagationOperator.storeOpenTelemetryContext( + context, Context.root()))) + .expectNextCount(2) + .verifyComplete(); + + outer.end(); + } + + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> span.hasName("outer").hasNoParent(), + span -> + span.hasName("inner") + .hasParent(trace.getSpan(0)) + .hasAttributes(attributeEntry("onNext", true)), + span -> + span.hasName("inner") + .hasParent(trace.getSpan(0)) + .hasAttributes(attributeEntry("onNext", true)))); + } + private Mono monoSpan(Mono mono, String spanName) { return ContextPropagationOperator.ScalarPropagatingMono.create(mono) .doOnEach( 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 7b6aca282d9c..a15491f4680b 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 @@ -21,6 +21,7 @@ muzzle { dependencies { implementation(project(":instrumentation:netty:netty-4.1:javaagent")) + implementation(project(":instrumentation:netty:netty-4.1:library")) library("io.projectreactor.netty:reactor-netty:0.9.0.RELEASE") testInstrumentation(project(":instrumentation:reactor:reactor-netty:reactor-netty-1.0:javaagent")) diff --git a/instrumentation/reactor/reactor-netty/reactor-netty-0.9/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/reactornetty/v0_9/DecoratorFunctions.java b/instrumentation/reactor/reactor-netty/reactor-netty-0.9/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/reactornetty/v0_9/DecoratorFunctions.java index 984e167e4cc3..25f340ea6ca0 100644 --- a/instrumentation/reactor/reactor-netty/reactor-netty-0.9/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/reactornetty/v0_9/DecoratorFunctions.java +++ b/instrumentation/reactor/reactor-netty/reactor-netty-0.9/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/reactornetty/v0_9/DecoratorFunctions.java @@ -8,7 +8,7 @@ import io.netty.channel.Channel; import io.opentelemetry.context.Context; import io.opentelemetry.context.Scope; -import io.opentelemetry.javaagent.instrumentation.netty.v4_1.AttributeKeys; +import io.opentelemetry.instrumentation.netty.v4_1.internal.AttributeKeys; import java.util.function.BiConsumer; import javax.annotation.Nullable; import reactor.netty.Connection; diff --git a/instrumentation/reactor/reactor-netty/reactor-netty-0.9/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/reactornetty/v0_9/OnRequest.java b/instrumentation/reactor/reactor-netty/reactor-netty-0.9/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/reactornetty/v0_9/OnRequest.java index 032eba29ba66..f9007fd027d5 100644 --- a/instrumentation/reactor/reactor-netty/reactor-netty-0.9/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/reactornetty/v0_9/OnRequest.java +++ b/instrumentation/reactor/reactor-netty/reactor-netty-0.9/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/reactornetty/v0_9/OnRequest.java @@ -6,7 +6,7 @@ package io.opentelemetry.javaagent.instrumentation.reactornetty.v0_9; import io.opentelemetry.context.Context; -import io.opentelemetry.javaagent.instrumentation.netty.v4_1.AttributeKeys; +import io.opentelemetry.instrumentation.netty.v4_1.NettyClientTelemetry; import java.util.function.BiConsumer; import reactor.netty.Connection; import reactor.netty.http.client.HttpClientRequest; @@ -15,6 +15,6 @@ public class OnRequest implements BiConsumer { @Override public void accept(HttpClientRequest r, Connection c) { Context context = r.currentContext().get(MapConnect.CONTEXT_ATTRIBUTE); - c.channel().attr(AttributeKeys.WRITE_CONTEXT).set(context); + NettyClientTelemetry.setChannelContext(c.channel(), context); } } diff --git a/instrumentation/reactor/reactor-netty/reactor-netty-0.9/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/reactornetty/v0_9/AbstractReactorNettyHttpClientTest.groovy b/instrumentation/reactor/reactor-netty/reactor-netty-0.9/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/reactornetty/v0_9/AbstractReactorNettyHttpClientTest.groovy index bd2b38e5c902..42e99ae013f1 100644 --- a/instrumentation/reactor/reactor-netty/reactor-netty-0.9/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/reactornetty/v0_9/AbstractReactorNettyHttpClientTest.groovy +++ b/instrumentation/reactor/reactor-netty/reactor-netty-0.9/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/reactornetty/v0_9/AbstractReactorNettyHttpClientTest.groovy @@ -13,6 +13,7 @@ import io.opentelemetry.instrumentation.test.AgentTestTrait import io.opentelemetry.instrumentation.test.base.HttpClientTest import io.opentelemetry.instrumentation.testing.junit.http.AbstractHttpClientTest import io.opentelemetry.sdk.trace.data.SpanData +import io.opentelemetry.semconv.trace.attributes.SemanticAttributes import reactor.netty.http.client.HttpClient import java.util.concurrent.CountDownLatch @@ -100,7 +101,10 @@ abstract class AbstractReactorNettyHttpClientTest extends HttpClientTest callbackClass) { - return !callbackClass.getName().startsWith("io.opentelemetry.javaagent"); + return callbackClass != OnMessageDecorator.class + && callbackClass != OnMessageErrorDecorator.class; } public static final class OnMessageDecorator diff --git a/instrumentation/reactor/reactor-netty/reactor-netty-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/reactornetty/v1_0/HttpClientInstrumentation.java b/instrumentation/reactor/reactor-netty/reactor-netty-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/reactornetty/v1_0/HttpClientInstrumentation.java index d3f9fd5ff517..9c1a49585ae8 100644 --- a/instrumentation/reactor/reactor-netty/reactor-netty-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/reactornetty/v1_0/HttpClientInstrumentation.java +++ b/instrumentation/reactor/reactor-netty/reactor-netty-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/reactornetty/v1_0/HttpClientInstrumentation.java @@ -85,7 +85,9 @@ public static void onEnter( BiConsumer callback) { if (DecoratorFunctions.shouldDecorate(callback.getClass())) { - callback = new DecoratorFunctions.OnMessageDecorator<>(callback, PropagatedContext.PARENT); + // perform the callback with the client span active (instead of the parent) since this + // callback occurs after the connection is made + callback = new DecoratorFunctions.OnMessageDecorator<>(callback, PropagatedContext.CLIENT); } } } diff --git a/instrumentation/reactor/reactor-netty/reactor-netty-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/reactornetty/v1_0/HttpResponseReceiverInstrumenter.java b/instrumentation/reactor/reactor-netty/reactor-netty-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/reactornetty/v1_0/HttpResponseReceiverInstrumenter.java index bf83a43230a3..fda2e86f27fe 100644 --- a/instrumentation/reactor/reactor-netty/reactor-netty-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/reactornetty/v1_0/HttpResponseReceiverInstrumenter.java +++ b/instrumentation/reactor/reactor-netty/reactor-netty-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/reactornetty/v1_0/HttpResponseReceiverInstrumenter.java @@ -11,8 +11,9 @@ import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.context.Context; +import io.opentelemetry.instrumentation.netty.v4_1.NettyClientTelemetry; import io.opentelemetry.instrumentation.reactor.ContextPropagationOperator; -import io.opentelemetry.javaagent.instrumentation.netty.v4_1.AttributeKeys; +import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; import java.util.function.BiConsumer; import java.util.function.Function; import javax.annotation.Nullable; @@ -58,8 +59,20 @@ public static HttpClient.ResponseReceiver instrument(HttpClient.ResponseRecei } static final class ContextHolder { + + private static final AtomicReferenceFieldUpdater contextUpdater = + AtomicReferenceFieldUpdater.newUpdater(ContextHolder.class, Context.class, "context"); + volatile Context parentContext; volatile Context context; + + void setContext(Context context) { + contextUpdater.set(this, context); + } + + Context getAndRemoveContext() { + return contextUpdater.getAndSet(this, null); + } } static final class StartOperation @@ -76,23 +89,33 @@ static final class StartOperation @Override public Mono apply(Mono mono) { return Mono.defer( - () -> { - Context parentContext = Context.current(); - contextHolder.parentContext = parentContext; - if (!instrumenter().shouldStart(parentContext, config)) { - // make context accessible via the reactor ContextView - the doOn* callbacks - // instrumentation uses this to set the proper context for callbacks - return mono.contextWrite(ctx -> ctx.put(CLIENT_PARENT_CONTEXT_KEY, parentContext)); - } - - Context context = instrumenter().start(parentContext, config); - contextHolder.context = context; - return ContextPropagationOperator.runWithContext(mono, context) - // make contexts accessible via the reactor ContextView - the doOn* callbacks - // instrumentation uses the parent context to set the proper context for callbacks - .contextWrite(ctx -> ctx.put(CLIENT_PARENT_CONTEXT_KEY, parentContext)) - .contextWrite(ctx -> ctx.put(CLIENT_CONTEXT_KEY, context)); - }); + () -> { + Context parentContext = Context.current(); + contextHolder.parentContext = parentContext; + if (!instrumenter().shouldStart(parentContext, config)) { + // make context accessible via the reactor ContextView - the doOn* callbacks + // instrumentation uses this to set the proper context for callbacks + return mono.contextWrite( + ctx -> ctx.put(CLIENT_PARENT_CONTEXT_KEY, parentContext)); + } + + Context context = instrumenter().start(parentContext, config); + contextHolder.setContext(context); + return ContextPropagationOperator.runWithContext(mono, context) + // make contexts accessible via the reactor ContextView - the doOn* callbacks + // instrumentation uses the parent context to set the proper context for + // callbacks + .contextWrite(ctx -> ctx.put(CLIENT_PARENT_CONTEXT_KEY, parentContext)) + .contextWrite(ctx -> ctx.put(CLIENT_CONTEXT_KEY, context)); + }) + .doOnCancel( + () -> { + Context context = contextHolder.getAndRemoveContext(); + if (context == null) { + return; + } + instrumenter().end(context, config, null, null); + }); } } @@ -117,7 +140,7 @@ public void accept(HttpClientRequest httpClientRequest, Connection connection) { // if this span was suppressed and context is null, propagate parentContext - this will allow // netty spans to be suppressed too Context nettyParentContext = context == null ? contextHolder.parentContext : context; - connection.channel().attr(AttributeKeys.WRITE_CONTEXT).set(nettyParentContext); + NettyClientTelemetry.setChannelContext(connection.channel(), nettyParentContext); } } @@ -134,7 +157,7 @@ static final class EndOperationWithRequestError @Override public void accept(HttpClientRequest httpClientRequest, Throwable error) { - Context context = contextHolder.context; + Context context = contextHolder.getAndRemoveContext(); if (context == null) { return; } @@ -155,7 +178,7 @@ static final class EndOperationWithResponseError @Override public void accept(HttpClientResponse response, Throwable error) { - Context context = contextHolder.context; + Context context = contextHolder.getAndRemoveContext(); if (context == null) { return; } @@ -175,7 +198,7 @@ static final class EndOperationWithSuccess implements BiConsumer requestHeader(HttpClientConfig request, String name) { return request.headers().getAll(name); } - @Nullable - @Override - public Long requestContentLength( - HttpClientConfig request, @Nullable HttpClientResponse response) { - return null; - } - - @Nullable - @Override - public Long requestContentLengthUncompressed( - HttpClientConfig request, @Nullable HttpClientResponse response) { - return null; - } - @Override - public Integer statusCode(HttpClientConfig request, HttpClientResponse response) { + public Integer statusCode( + HttpClientConfig request, HttpClientResponse response, @Nullable Throwable error) { return response.status().code(); } - @Nullable - @Override - public Long responseContentLength(HttpClientConfig request, HttpClientResponse response) { - return null; - } - - @Nullable - @Override - public Long responseContentLengthUncompressed( - HttpClientConfig request, HttpClientResponse response) { - return null; - } - @Override public List responseHeader( HttpClientConfig request, HttpClientResponse response, String name) { diff --git a/instrumentation/reactor/reactor-netty/reactor-netty-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/reactornetty/v1_0/ReactorNettyNetClientAttributesGetter.java b/instrumentation/reactor/reactor-netty/reactor-netty-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/reactornetty/v1_0/ReactorNettyNetClientAttributesGetter.java index 067cc10feaa3..3ea477c3e5bd 100644 --- a/instrumentation/reactor/reactor-netty/reactor-netty-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/reactornetty/v1_0/ReactorNettyNetClientAttributesGetter.java +++ b/instrumentation/reactor/reactor-netty/reactor-netty-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/reactornetty/v1_0/ReactorNettyNetClientAttributesGetter.java @@ -24,7 +24,19 @@ public String transport(HttpClientConfig request, @Nullable HttpClientResponse r @Nullable @Override - public InetSocketAddress getAddress( + public String peerName(HttpClientConfig request) { + return getHost(request); + } + + @Nullable + @Override + public Integer peerPort(HttpClientConfig request) { + return getPort(request); + } + + @Nullable + @Override + protected InetSocketAddress getPeerSocketAddress( HttpClientConfig request, @Nullable HttpClientResponse response) { // we're making use of the fact that HttpClientOperations is both a Connection and an @@ -38,4 +50,28 @@ public InetSocketAddress getAddress( } return null; } + + @Nullable + private static String getHost(HttpClientConfig request) { + String baseUrl = request.baseUrl(); + String uri = request.uri(); + + if (baseUrl != null && uri.startsWith("/")) { + return UrlParser.getHost(baseUrl); + } else { + return UrlParser.getHost(uri); + } + } + + @Nullable + private static Integer getPort(HttpClientConfig request) { + String baseUrl = request.baseUrl(); + String uri = request.uri(); + + if (baseUrl != null && uri.startsWith("/")) { + return UrlParser.getPort(baseUrl); + } else { + return UrlParser.getPort(uri); + } + } } diff --git a/instrumentation/reactor/reactor-netty/reactor-netty-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/reactornetty/v1_0/ReactorNettySingletons.java b/instrumentation/reactor/reactor-netty/reactor-netty-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/reactornetty/v1_0/ReactorNettySingletons.java index 5054823dd347..37e6890d6448 100644 --- a/instrumentation/reactor/reactor-netty/reactor-netty-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/reactornetty/v1_0/ReactorNettySingletons.java +++ b/instrumentation/reactor/reactor-netty/reactor-netty-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/reactornetty/v1_0/ReactorNettySingletons.java @@ -7,17 +7,18 @@ import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; -import io.opentelemetry.instrumentation.api.instrumenter.PeerServiceAttributesExtractor; import io.opentelemetry.instrumentation.api.instrumenter.SpanKindExtractor; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpClientAttributesExtractor; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpClientMetrics; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanNameExtractor; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanStatusExtractor; import io.opentelemetry.instrumentation.api.instrumenter.net.NetClientAttributesExtractor; -import io.opentelemetry.javaagent.bootstrap.internal.DeprecatedConfigPropertyWarning; +import io.opentelemetry.instrumentation.api.instrumenter.net.PeerServiceAttributesExtractor; +import io.opentelemetry.instrumentation.netty.v4.common.internal.client.NettyClientInstrumenterFactory; +import io.opentelemetry.instrumentation.netty.v4.common.internal.client.NettyConnectionInstrumenter; +import io.opentelemetry.javaagent.bootstrap.internal.CommonConfig; +import io.opentelemetry.javaagent.bootstrap.internal.DeprecatedConfigProperties; import io.opentelemetry.javaagent.bootstrap.internal.InstrumentationConfig; -import io.opentelemetry.javaagent.instrumentation.netty.v4.common.client.NettyClientInstrumenterFactory; -import io.opentelemetry.javaagent.instrumentation.netty.v4.common.client.NettyConnectionInstrumenter; import reactor.netty.http.client.HttpClientConfig; import reactor.netty.http.client.HttpClientResponse; @@ -29,16 +30,12 @@ public final class ReactorNettySingletons { static { InstrumentationConfig config = InstrumentationConfig.get(); - DeprecatedConfigPropertyWarning.warnIfUsed( - config, - "otel.instrumentation.reactor-netty.always-create-connect-span", - "otel.instrumentation.reactor-netty.connection-telemetry.enabled"); - boolean alwaysCreateConnectSpan = - config.getBoolean("otel.instrumentation.reactor-netty.always-create-connect-span", false); connectionTelemetryEnabled = - config.getBoolean( + DeprecatedConfigProperties.getBoolean( + config, + "otel.instrumentation.reactor-netty.always-create-connect-span", "otel.instrumentation.reactor-netty.connection-telemetry.enabled", - alwaysCreateConnectSpan); + false); } private static final Instrumenter INSTRUMENTER; @@ -56,15 +53,26 @@ public final class ReactorNettySingletons { INSTRUMENTATION_NAME, HttpSpanNameExtractor.create(httpAttributesGetter)) .setSpanStatusExtractor(HttpSpanStatusExtractor.create(httpAttributesGetter)) - .addAttributesExtractor(HttpClientAttributesExtractor.create(httpAttributesGetter)) + .addAttributesExtractor( + HttpClientAttributesExtractor.builder(httpAttributesGetter) + .setCapturedRequestHeaders(CommonConfig.get().getClientRequestHeaders()) + .setCapturedResponseHeaders(CommonConfig.get().getClientResponseHeaders()) + .build()) .addAttributesExtractor(NetClientAttributesExtractor.create(netAttributesGetter)) - .addAttributesExtractor(PeerServiceAttributesExtractor.create(netAttributesGetter)) + .addAttributesExtractor( + PeerServiceAttributesExtractor.create( + netAttributesGetter, CommonConfig.get().getPeerServiceMapping())) .addOperationMetrics(HttpClientMetrics.get()) // headers are injected in ResponseReceiverInstrumenter - .newInstrumenter(SpanKindExtractor.alwaysClient()); + .buildInstrumenter(SpanKindExtractor.alwaysClient()); NettyClientInstrumenterFactory instrumenterFactory = - new NettyClientInstrumenterFactory(INSTRUMENTATION_NAME, connectionTelemetryEnabled, false); + new NettyClientInstrumenterFactory( + GlobalOpenTelemetry.get(), + INSTRUMENTATION_NAME, + connectionTelemetryEnabled, + false, + CommonConfig.get().getPeerServiceMapping()); CONNECTION_INSTRUMENTER = instrumenterFactory.createConnectionInstrumenter(); } diff --git a/instrumentation/reactor/reactor-netty/reactor-netty-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/reactornetty/v1_0/TransportConnectorInstrumentation.java b/instrumentation/reactor/reactor-netty/reactor-netty-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/reactornetty/v1_0/TransportConnectorInstrumentation.java index ecfffed5d574..312bd9f23de9 100644 --- a/instrumentation/reactor/reactor-netty/reactor-netty-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/reactornetty/v1_0/TransportConnectorInstrumentation.java +++ b/instrumentation/reactor/reactor-netty/reactor-netty-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/reactornetty/v1_0/TransportConnectorInstrumentation.java @@ -16,10 +16,10 @@ import io.opentelemetry.context.Context; import io.opentelemetry.context.Scope; import io.opentelemetry.instrumentation.api.util.VirtualField; +import io.opentelemetry.instrumentation.netty.common.internal.NettyConnectionRequest; import io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; -import io.opentelemetry.javaagent.instrumentation.netty.common.NettyConnectionRequest; import io.opentelemetry.javaagent.instrumentation.netty.v4_1.InstrumentedAddressResolverGroup; import java.net.SocketAddress; import java.util.List; diff --git a/instrumentation/reactor/reactor-netty/reactor-netty-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/reactornetty/v1_0/UrlParser.java b/instrumentation/reactor/reactor-netty/reactor-netty-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/reactornetty/v1_0/UrlParser.java new file mode 100644 index 000000000000..cbae4cc80207 --- /dev/null +++ b/instrumentation/reactor/reactor-netty/reactor-netty-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/reactornetty/v1_0/UrlParser.java @@ -0,0 +1,102 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.reactornetty.v1_0; + +import javax.annotation.Nullable; + +class UrlParser { + + @Nullable + static String getHost(String url) { + + int schemeEndIndex = url.indexOf(':'); + if (schemeEndIndex == -1) { + // not a valid url + return null; + } + + int len = url.length(); + if (len <= schemeEndIndex + 2 + || url.charAt(schemeEndIndex + 1) != '/' + || url.charAt(schemeEndIndex + 2) != '/') { + // has no authority component + return null; + } + + // look for the end of the host: + // ':' ==> start of port, or + // '/', '?', '#' ==> start of path + int index; + for (index = schemeEndIndex + 3; index < len; index++) { + char c = url.charAt(index); + if (c == ':' || c == '/' || c == '?' || c == '#') { + break; + } + } + String host = url.substring(schemeEndIndex + 3, index); + return host.isEmpty() ? null : host; + } + + @Nullable + static Integer getPort(String url) { + + int schemeEndIndex = url.indexOf(':'); + if (schemeEndIndex == -1) { + // not a valid url + return null; + } + + int len = url.length(); + if (len <= schemeEndIndex + 2 + || url.charAt(schemeEndIndex + 1) != '/' + || url.charAt(schemeEndIndex + 2) != '/') { + // has no authority component + return null; + } + + // look for the end of the host: + // ':' ==> start of port, or + // '/', '?', '#' ==> start of path + int index; + int portIndex = -1; + for (index = schemeEndIndex + 3; index < len; index++) { + char c = url.charAt(index); + if (c == ':') { + portIndex = index + 1; + break; + } + if (c == '/' || c == '?' || c == '#') { + break; + } + } + + if (portIndex == -1) { + return null; + } + + // look for the end of the port: + // '/', '?', '#' ==> start of path + for (index = portIndex; index < len; index++) { + char c = url.charAt(index); + if (c == '/' || c == '?' || c == '#') { + break; + } + } + String port = url.substring(portIndex, index); + return port.isEmpty() ? null : safeParse(port); + } + + @Nullable + private static Integer safeParse(String port) { + try { + return Integer.valueOf(port); + } catch (NumberFormatException e) { + return null; + } + } + + private UrlParser() {} +} diff --git a/instrumentation/reactor/reactor-netty/reactor-netty-1.0/javaagent/src/test/groovy/ReactorNettyWithSpanTest.groovy b/instrumentation/reactor/reactor-netty/reactor-netty-1.0/javaagent/src/test/groovy/ReactorNettyWithSpanTest.groovy deleted file mode 100644 index d9ebc777cd54..000000000000 --- a/instrumentation/reactor/reactor-netty/reactor-netty-1.0/javaagent/src/test/groovy/ReactorNettyWithSpanTest.groovy +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -import io.opentelemetry.instrumentation.test.AgentTestTrait -import io.opentelemetry.instrumentation.test.InstrumentationSpecification -import io.opentelemetry.instrumentation.testing.junit.http.HttpClientTestServer -import io.opentelemetry.test.reactor.netty.TracedWithSpan -import reactor.core.publisher.Mono -import reactor.netty.http.client.HttpClient -import reactor.test.StepVerifier -import spock.lang.Shared - -import java.time.Duration - -import static io.opentelemetry.api.trace.SpanKind.CLIENT -import static io.opentelemetry.api.trace.SpanKind.INTERNAL -import static io.opentelemetry.api.trace.SpanKind.SERVER - -class ReactorNettyWithSpanTest extends InstrumentationSpecification implements AgentTestTrait { - - @Shared - private HttpClientTestServer server - - def setupSpec() { - server = new HttpClientTestServer(openTelemetry) - server.start() - } - - def cleanupSpec() { - server.stop() - } - - def "test successful nested under WithSpan"() { - when: - def httpClient = HttpClient.create() - - def httpRequest = Mono.defer({ -> - httpClient.get().uri("http://localhost:${server.httpPort()}/success") - .responseSingle({ resp, content -> - // Make sure to consume content since that's when we close the span. - content.map { resp } - }) - .map({ r -> r.status().code() }) - }) - - def getResponse = new TracedWithSpan().mono( - // https://github.com/open-telemetry/opentelemetry-java-instrumentation/issues/4348 - // our HTTP server is synchronous, i.e. it returns Mono.just with response - // which is not supported by TracingSubscriber - it does not instrument scalar calls - // so we delay here to fake async http request and let Reactor context instrumentation work - Mono.delay(Duration.ofMillis(1)).then(httpRequest)) - - then: - StepVerifier.create(getResponse) - .expectNext(200) - .expectComplete() - .verify() - - assertTraces(1) { - trace(0, 3) { - span(0) { - name "TracedWithSpan.mono" - kind INTERNAL - hasNoParent() - } - span(1) { - name "HTTP GET" - kind CLIENT - childOf(span(0)) - } - span(2) { - name "test-http-server" - kind SERVER - childOf(span(1)) - } - } - } - } -} diff --git a/instrumentation/reactor/reactor-netty/reactor-netty-1.0/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/reactornetty/v1_0/AbstractReactorNettyHttpClientTest.groovy b/instrumentation/reactor/reactor-netty/reactor-netty-1.0/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/reactornetty/v1_0/AbstractReactorNettyHttpClientTest.groovy deleted file mode 100644 index 93e4d8f9f9b3..000000000000 --- a/instrumentation/reactor/reactor-netty/reactor-netty-1.0/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/reactornetty/v1_0/AbstractReactorNettyHttpClientTest.groovy +++ /dev/null @@ -1,278 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.javaagent.instrumentation.reactornetty.v1_0 - -import io.netty.resolver.AddressResolver -import io.netty.resolver.AddressResolverGroup -import io.netty.resolver.InetNameResolver -import io.netty.util.concurrent.EventExecutor -import io.netty.util.concurrent.Promise -import io.opentelemetry.api.common.AttributeKey -import io.opentelemetry.api.trace.Span -import io.opentelemetry.api.trace.SpanKind -import io.opentelemetry.api.trace.StatusCode -import io.opentelemetry.instrumentation.test.AgentTestTrait -import io.opentelemetry.instrumentation.test.base.HttpClientTest -import io.opentelemetry.instrumentation.testing.junit.http.AbstractHttpClientTest -import io.opentelemetry.sdk.trace.data.SpanData -import io.opentelemetry.semconv.trace.attributes.SemanticAttributes -import java.time.Duration -import reactor.netty.http.client.HttpClient - -import java.util.concurrent.CountDownLatch -import java.util.concurrent.atomic.AtomicReference - -import static io.opentelemetry.instrumentation.test.utils.PortUtils.UNUSABLE_PORT - -abstract class AbstractReactorNettyHttpClientTest extends HttpClientTest implements AgentTestTrait { - - @Override - boolean testRedirects() { - false - } - - @Override - boolean testReadTimeout() { - true - } - - @Override - String userAgent() { - return "ReactorNetty" - } - - @Override - HttpClient.ResponseReceiver buildRequest(String method, URI uri, Map headers) { - def client = createHttpClient() - .followRedirect(true) - .headers({ h -> headers.each { k, v -> h.add(k, v) } }) - .baseUrl(resolveAddress("").toString()) - if (uri.toString().contains("/read-timeout")) { - client = client.responseTimeout(Duration.ofMillis(READ_TIMEOUT_MS)) - } - return client."${method.toLowerCase()}"() - .uri(uri.toString()) - } - - @Override - int sendRequest(HttpClient.ResponseReceiver request, String method, URI uri, Map headers) { - return request.responseSingle { resp, content -> - // Make sure to consume content since that's when we close the span. - content.map { - resp - } - }.block().status().code() - } - - @Override - void sendRequestWithCallback(HttpClient.ResponseReceiver request, String method, URI uri, Map headers, AbstractHttpClientTest.RequestResult requestResult) { - request.responseSingle { resp, content -> - // Make sure to consume content since that's when we close the span. - content.map { resp } - }.subscribe({ - requestResult.complete(it.status().code()) - }, { throwable -> - requestResult.complete(throwable) - }) - } - - @Override - Throwable clientSpanError(URI uri, Throwable exception) { - if (exception.class.getName().endsWith("ReactiveException")) { - switch (uri.toString()) { - case "http://localhost:61/": // unopened port - case "https://192.0.2.1/": // non routable address - exception = exception.getCause() - } - } - return exception - } - - @Override - Set> httpAttributes(URI uri) { - switch (uri.toString()) { - case "http://localhost:61/": // unopened port - case "https://192.0.2.1/": // non routable address - return [] - } - def attributes = super.httpAttributes(uri) - if (uri.toString().contains("/read-timeout")) { - attributes.remove(SemanticAttributes.NET_PEER_NAME) - attributes.remove(SemanticAttributes.NET_PEER_PORT) - attributes.remove(SemanticAttributes.HTTP_FLAVOR) - } - attributes - } - - abstract HttpClient createHttpClient() - - AddressResolverGroup getAddressResolverGroup() { - return CustomNameResolverGroup.INSTANCE - } - - def "should expose context to http client callbacks"() { - given: - def onRequestSpan = new AtomicReference() - def afterRequestSpan = new AtomicReference() - def onResponseSpan = new AtomicReference() - def afterResponseSpan = new AtomicReference() - def latch = new CountDownLatch(1) - - def httpClient = createHttpClient() - .doOnRequest({ rq, con -> onRequestSpan.set(Span.current()) }) - .doAfterRequest({ rq, con -> afterRequestSpan.set(Span.current()) }) - .doOnResponse({ rs, con -> onResponseSpan.set(Span.current()) }) - .doAfterResponseSuccess({ rs, con -> - afterResponseSpan.set(Span.current()) - latch.countDown() - }) - - when: - runWithSpan("parent") { - httpClient.baseUrl(resolveAddress("").toString()) - .get() - .uri("/success") - .responseSingle { resp, content -> - // Make sure to consume content since that's when we close the span. - content.map { resp } - } - .block() - } - latch.await() - - then: - assertTraces(1) { - trace(0, 3) { - def parentSpan = span(0) - def nettyClientSpan = span(1) - - span(0) { - name "parent" - kind SpanKind.INTERNAL - hasNoParent() - } - clientSpan(it, 1, parentSpan, "GET", resolveAddress("/success")) - serverSpan(it, 2, nettyClientSpan) - - assertSameSpan(parentSpan, onRequestSpan) - assertSameSpan(nettyClientSpan, afterRequestSpan) - assertSameSpan(nettyClientSpan, onResponseSpan) - assertSameSpan(parentSpan, afterResponseSpan) - } - } - } - - def "should expose context to http request error callback"() { - given: - def onRequestErrorSpan = new AtomicReference() - - def httpClient = createHttpClient() - .doOnRequestError({ rq, err -> onRequestErrorSpan.set(Span.current()) }) - - when: - runWithSpan("parent") { - httpClient.get() - .uri("http://localhost:$UNUSABLE_PORT/") - .response() - .block() - } - - then: - def ex = thrown(Exception) - - assertTraces(1) { - trace(0, 2) { - def parentSpan = span(0) - - span(0) { - name "parent" - kind SpanKind.INTERNAL - hasNoParent() - status StatusCode.ERROR - errorEvent(ex.class, ex.message) - } - span(1) { - def actualException = ex.cause - kind SpanKind.CLIENT - childOf parentSpan - status StatusCode.ERROR - errorEvent(actualException.class, actualException.message) - } - - assertSameSpan(parentSpan, onRequestErrorSpan) - } - } - } - - def "should not leak connections"() { - given: - def uniqueChannelHashes = new HashSet<>() - def httpClient = createHttpClient() - .doOnConnect({ uniqueChannelHashes.add(it.channelHash()) }) - def uri = "http://localhost:${server.httpPort()}/success" - - def count = 100 - - when: - (1..count).forEach({ - runWithSpan("parent") { - def status = httpClient.get().uri(uri) - .responseSingle { resp, content -> - // Make sure to consume content since that's when we close the span. - content.map { resp.status().code() } - }.block() - assert status == 200 - } - }) - - then: - traces.size() == count - uniqueChannelHashes.size() == 1 - } - - static void assertSameSpan(SpanData expected, AtomicReference actual) { - def expectedSpanContext = expected.spanContext - def actualSpanContext = actual.get().spanContext - assert expectedSpanContext.traceId == actualSpanContext.traceId - assert expectedSpanContext.spanId == actualSpanContext.spanId - } - - // custom address resolver that returns at most one address for each host - // adapted from io.netty.resolver.DefaultAddressResolverGroup - static class CustomNameResolverGroup extends AddressResolverGroup { - public static final CustomNameResolverGroup INSTANCE = new CustomNameResolverGroup() - - private CustomNameResolverGroup() { - } - - protected AddressResolver newResolver(EventExecutor executor) throws Exception { - return (new CustomNameResolver(executor)).asAddressResolver() - } - } - - static class CustomNameResolver extends InetNameResolver { - CustomNameResolver(EventExecutor executor) { - super(executor) - } - - protected void doResolve(String inetHost, Promise promise) throws Exception { - try { - promise.setSuccess(InetAddress.getByName(inetHost)) - } catch (UnknownHostException exception) { - promise.setFailure(exception) - } - } - - protected void doResolveAll(String inetHost, Promise> promise) throws Exception { - try { - // default implementation calls InetAddress.getAllByName - promise.setSuccess(Collections.singletonList(InetAddress.getByName(inetHost))) - } catch (UnknownHostException exception) { - promise.setFailure(exception) - } - } - } -} diff --git a/instrumentation/reactor/reactor-netty/reactor-netty-1.0/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/reactornetty/v1_0/ReactorNettyClientSslTest.groovy b/instrumentation/reactor/reactor-netty/reactor-netty-1.0/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/reactornetty/v1_0/ReactorNettyClientSslTest.groovy deleted file mode 100644 index c982447dbbd0..000000000000 --- a/instrumentation/reactor/reactor-netty/reactor-netty-1.0/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/reactornetty/v1_0/ReactorNettyClientSslTest.groovy +++ /dev/null @@ -1,207 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.javaagent.instrumentation.reactornetty.v1_0 - -import io.netty.handler.ssl.SslContextBuilder -import io.opentelemetry.instrumentation.test.AgentInstrumentationSpecification -import io.opentelemetry.instrumentation.testing.junit.http.HttpClientTestServer -import io.opentelemetry.semconv.trace.attributes.SemanticAttributes -import reactor.netty.http.client.HttpClient -import reactor.netty.tcp.SslProvider -import spock.lang.Shared - -import javax.net.ssl.SSLHandshakeException - -import static io.opentelemetry.api.trace.SpanKind.CLIENT -import static io.opentelemetry.api.trace.SpanKind.INTERNAL -import static io.opentelemetry.api.trace.SpanKind.SERVER -import static io.opentelemetry.api.trace.StatusCode.ERROR -import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.HttpFlavorValues.HTTP_1_1 -import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.NetTransportValues.IP_TCP - -class ReactorNettyClientSslTest extends AgentInstrumentationSpecification { - - @Shared - private HttpClientTestServer server - - def setupSpec() { - server = new HttpClientTestServer(openTelemetry) - server.start() - } - - def cleanupSpec() { - server.stop() - } - - def "should fail SSL handshake"() { - given: - def httpClient = createHttpClient(["SSLv3"]) - def uri = "https://localhost:${server.httpsPort()}/success" - - when: - def responseMono = httpClient.get().uri(uri) - .responseSingle { resp, content -> - // Make sure to consume content since that's when we close the span. - content.map { resp } - } - - runWithSpan("parent") { - responseMono.block() - } - - then: - Throwable thrownException = thrown() - - assertTraces(1) { - trace(0, 5) { - def list = Arrays.asList("RESOLVE", "CONNECT", "SSL handshake") - spans.subList(2, 5).sort(Comparator.comparing { item -> list.indexOf(item.name) }) - span(0) { - name "parent" - status ERROR - errorEvent(thrownException.class, thrownException.message) - } - span(1) { - name "HTTP GET" - kind CLIENT - childOf span(0) - status ERROR - // netty swallows the exception, it doesn't make any sense to hard-code the message - errorEventWithAnyMessage(SSLHandshakeException) - attributes { - "$SemanticAttributes.HTTP_METHOD" "GET" - "$SemanticAttributes.HTTP_URL" uri - } - } - span(2) { - name "RESOLVE" - kind INTERNAL - childOf span(1) - attributes { - "$SemanticAttributes.NET_TRANSPORT" IP_TCP - "$SemanticAttributes.NET_PEER_NAME" "localhost" - "$SemanticAttributes.NET_PEER_PORT" server.httpsPort() - } - } - span(3) { - name "CONNECT" - kind INTERNAL - childOf span(1) - attributes { - "$SemanticAttributes.NET_TRANSPORT" IP_TCP - "$SemanticAttributes.NET_PEER_NAME" "localhost" - "$SemanticAttributes.NET_PEER_PORT" server.httpsPort() - "$SemanticAttributes.NET_PEER_IP" "127.0.0.1" - } - } - span(4) { - name "SSL handshake" - kind INTERNAL - childOf span(1) - status ERROR - // netty swallows the exception, it doesn't make any sense to hard-code the message - errorEventWithAnyMessage(SSLHandshakeException) - attributes { - "$SemanticAttributes.NET_TRANSPORT" IP_TCP - "$SemanticAttributes.NET_PEER_NAME" "localhost" - "$SemanticAttributes.NET_PEER_PORT" server.httpsPort() - "$SemanticAttributes.NET_PEER_IP" "127.0.0.1" - } - } - } - } - } - - def "should successfully establish SSL handshake"() { - given: - def httpClient = createHttpClient() - def uri = "https://localhost:${server.httpsPort()}/success" - - when: - def responseMono = httpClient.get().uri(uri) - .responseSingle { resp, content -> - // Make sure to consume content since that's when we close the span. - content.map { resp } - } - - runWithSpan("parent") { - responseMono.block() - } - - then: - assertTraces(1) { - trace(0, 6) { - def list = Arrays.asList("RESOLVE", "CONNECT", "SSL handshake") - spans.subList(2, 5).sort(Comparator.comparing { item -> list.indexOf(item.name) }) - span(0) { - name "parent" - } - span(1) { - name "HTTP GET" - kind CLIENT - childOf span(0) - attributes { - "$SemanticAttributes.HTTP_METHOD" "GET" - "$SemanticAttributes.HTTP_URL" uri - "$SemanticAttributes.HTTP_FLAVOR" HTTP_1_1 - "$SemanticAttributes.HTTP_STATUS_CODE" 200 - "$SemanticAttributes.NET_PEER_NAME" "localhost" - "$SemanticAttributes.NET_PEER_PORT" server.httpsPort() - "$SemanticAttributes.NET_PEER_IP" "127.0.0.1" - } - } - span(2) { - name "RESOLVE" - kind INTERNAL - childOf span(1) - attributes { - "$SemanticAttributes.NET_TRANSPORT" IP_TCP - "$SemanticAttributes.NET_PEER_NAME" "localhost" - "$SemanticAttributes.NET_PEER_PORT" server.httpsPort() - } - } - span(3) { - name "CONNECT" - kind INTERNAL - childOf span(1) - attributes { - "$SemanticAttributes.NET_TRANSPORT" IP_TCP - "$SemanticAttributes.NET_PEER_NAME" "localhost" - "$SemanticAttributes.NET_PEER_PORT" server.httpsPort() - "$SemanticAttributes.NET_PEER_IP" "127.0.0.1" - } - } - span(4) { - name "SSL handshake" - kind INTERNAL - childOf span(1) - attributes { - "$SemanticAttributes.NET_TRANSPORT" IP_TCP - "$SemanticAttributes.NET_PEER_NAME" "localhost" - "$SemanticAttributes.NET_PEER_PORT" server.httpsPort() - "$SemanticAttributes.NET_PEER_IP" "127.0.0.1" - } - } - span(5) { - name "test-http-server" - kind SERVER - childOf span(1) - } - } - } - } - - private static HttpClient createHttpClient(List enabledProtocols = null) { - def sslContext = SslContextBuilder.forClient() - if (enabledProtocols != null) { - sslContext = sslContext.protocols(enabledProtocols) - } - def sslProvider = SslProvider.builder() - .sslContext(sslContext.build()) - .build() - HttpClient.create().secure(sslProvider) - } -} diff --git a/instrumentation/reactor/reactor-netty/reactor-netty-1.0/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/reactornetty/v1_0/ReactorNettyConnectionSpanTest.groovy b/instrumentation/reactor/reactor-netty/reactor-netty-1.0/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/reactornetty/v1_0/ReactorNettyConnectionSpanTest.groovy deleted file mode 100644 index 859e4dbecded..000000000000 --- a/instrumentation/reactor/reactor-netty/reactor-netty-1.0/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/reactornetty/v1_0/ReactorNettyConnectionSpanTest.groovy +++ /dev/null @@ -1,178 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.javaagent.instrumentation.reactornetty.v1_0 - -import io.opentelemetry.instrumentation.test.AgentTestTrait -import io.opentelemetry.instrumentation.test.InstrumentationSpecification -import io.opentelemetry.instrumentation.test.utils.PortUtils -import io.opentelemetry.instrumentation.testing.junit.http.HttpClientTestServer -import io.opentelemetry.semconv.trace.attributes.SemanticAttributes -import reactor.netty.http.client.HttpClient -import spock.lang.Shared - -import static io.opentelemetry.api.trace.SpanKind.CLIENT -import static io.opentelemetry.api.trace.SpanKind.INTERNAL -import static io.opentelemetry.api.trace.SpanKind.SERVER -import static io.opentelemetry.api.trace.StatusCode.ERROR -import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.HttpFlavorValues.HTTP_1_1 -import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.NetTransportValues.IP_TCP - -class ReactorNettyConnectionSpanTest extends InstrumentationSpecification implements AgentTestTrait { - - @Shared - private HttpClientTestServer server - - def setupSpec() { - server = new HttpClientTestServer(openTelemetry) - server.start() - } - - def cleanupSpec() { - server.stop() - } - - def "test successful request"() { - given: - def httpClient = HttpClient.create() - def uri = "http://localhost:${server.httpPort()}/success" - - when: - def responseCode = - runWithSpan("parent") { - httpClient.get().uri(uri) - .responseSingle { resp, content -> - // Make sure to consume content since that's when we close the span. - content.map { resp } - } - .block() - .status().code() - } - - then: - responseCode == 200 - assertTraces(1) { - trace(0, 5) { - def list = Arrays.asList("RESOLVE", "CONNECT") - spans.subList(2, 4).sort(Comparator.comparing { item -> list.indexOf(item.name) }) - span(0) { - name "parent" - kind INTERNAL - hasNoParent() - } - span(1) { - name "HTTP GET" - kind CLIENT - childOf span(0) - attributes { - "$SemanticAttributes.HTTP_METHOD" "GET" - "$SemanticAttributes.HTTP_URL" uri - "$SemanticAttributes.HTTP_FLAVOR" HTTP_1_1 - "$SemanticAttributes.HTTP_STATUS_CODE" 200 - "$SemanticAttributes.NET_PEER_NAME" "localhost" - "$SemanticAttributes.NET_PEER_PORT" server.httpPort() - "$SemanticAttributes.NET_PEER_IP" "127.0.0.1" - } - } - span(2) { - name "RESOLVE" - kind INTERNAL - childOf span(1) - attributes { - "$SemanticAttributes.NET_TRANSPORT" IP_TCP - "$SemanticAttributes.NET_PEER_NAME" "localhost" - "$SemanticAttributes.NET_PEER_PORT" server.httpPort() - } - } - span(3) { - name "CONNECT" - kind INTERNAL - childOf span(1) - attributes { - "$SemanticAttributes.NET_TRANSPORT" IP_TCP - "$SemanticAttributes.NET_PEER_NAME" "localhost" - "$SemanticAttributes.NET_PEER_PORT" server.httpPort() - "$SemanticAttributes.NET_PEER_IP" "127.0.0.1" - } - } - span(4) { - name "test-http-server" - kind SERVER - childOf span(1) - } - } - } - } - - def "test failing request"() { - given: - def httpClient = HttpClient.create() - def uri = "http://localhost:${PortUtils.UNUSABLE_PORT}" - - when: - runWithSpan("parent") { - httpClient.get().uri(uri) - .responseSingle { resp, content -> - // Make sure to consume content since that's when we close the span. - content.map { resp } - } - .block() - .status().code() - } - - then: - def thrownException = thrown(Exception) - def connectException = thrownException.getCause() - - and: - assertTraces(1) { - trace(0, 4) { - def list = Arrays.asList("RESOLVE", "CONNECT") - spans.subList(2, 4).sort(Comparator.comparing { item -> list.indexOf(item.name) }) - span(0) { - name "parent" - kind INTERNAL - hasNoParent() - status ERROR - errorEvent(thrownException.class, thrownException.message) - } - span(1) { - name "HTTP GET" - kind CLIENT - childOf span(0) - status ERROR - errorEvent(connectException.class, connectException.message) - attributes { - "$SemanticAttributes.HTTP_METHOD" "GET" - "$SemanticAttributes.HTTP_URL" uri - } - } - span(2) { - name "RESOLVE" - kind INTERNAL - childOf span(1) - attributes { - "$SemanticAttributes.NET_TRANSPORT" IP_TCP - "$SemanticAttributes.NET_PEER_NAME" "localhost" - "$SemanticAttributes.NET_PEER_PORT" PortUtils.UNUSABLE_PORT - } - } - span(3) { - name "CONNECT" - kind INTERNAL - childOf span(1) - status ERROR - errorEvent(connectException.class, connectException.message) - attributes { - "$SemanticAttributes.NET_TRANSPORT" IP_TCP - "$SemanticAttributes.NET_PEER_NAME" "localhost" - "$SemanticAttributes.NET_PEER_PORT" PortUtils.UNUSABLE_PORT - "$SemanticAttributes.NET_PEER_IP" "127.0.0.1" - } - } - } - } - } -} diff --git a/instrumentation/reactor/reactor-netty/reactor-netty-1.0/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/reactornetty/v1_0/ReactorNettyHttpClientTest.groovy b/instrumentation/reactor/reactor-netty/reactor-netty-1.0/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/reactornetty/v1_0/ReactorNettyHttpClientTest.groovy deleted file mode 100644 index c844c2878336..000000000000 --- a/instrumentation/reactor/reactor-netty/reactor-netty-1.0/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/reactornetty/v1_0/ReactorNettyHttpClientTest.groovy +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.javaagent.instrumentation.reactornetty.v1_0 - -import io.netty.channel.ChannelOption -import io.opentelemetry.instrumentation.testing.junit.http.SingleConnection -import io.opentelemetry.testing.internal.armeria.common.HttpHeaderNames -import reactor.netty.http.client.HttpClient - -import java.util.concurrent.ExecutionException -import java.util.concurrent.TimeoutException - -class ReactorNettyHttpClientTest extends AbstractReactorNettyHttpClientTest { - - HttpClient createHttpClient() { - return HttpClient.create() - .tcpConfiguration({ tcpClient -> - tcpClient.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, CONNECT_TIMEOUT_MS) - }) - .resolver(getAddressResolverGroup()) - .headers({ headers -> headers.set(HttpHeaderNames.USER_AGENT, userAgent()) }) - } - - @Override - SingleConnection createSingleConnection(String host, int port) { - def httpClient = HttpClient - .newConnection() - .host(host) - .port(port) - .headers({ headers -> headers.set(HttpHeaderNames.USER_AGENT, userAgent()) }) - - return new SingleConnection() { - - @Override - int doRequest(String path, Map headers) throws ExecutionException, InterruptedException, TimeoutException { - return httpClient - .headers({ h -> headers.each { k, v -> h.add(k, v) } }) - .get() - .uri(path) - .responseSingle { resp, content -> - // Make sure to consume content since that's when we close the span. - content.map { resp } - } - .block() - .status().code() - } - } - } -} diff --git a/instrumentation/reactor/reactor-netty/reactor-netty-1.0/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/reactornetty/v1_0/ReactorNettyHttpClientUsingFromTest.groovy b/instrumentation/reactor/reactor-netty/reactor-netty-1.0/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/reactornetty/v1_0/ReactorNettyHttpClientUsingFromTest.groovy deleted file mode 100644 index db1a9879be4a..000000000000 --- a/instrumentation/reactor/reactor-netty/reactor-netty-1.0/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/reactornetty/v1_0/ReactorNettyHttpClientUsingFromTest.groovy +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.javaagent.instrumentation.reactornetty.v1_0 - -import io.netty.channel.ChannelOption -import io.opentelemetry.testing.internal.armeria.common.HttpHeaderNames -import reactor.netty.http.client.HttpClient -import reactor.netty.tcp.TcpClient - -class ReactorNettyHttpClientUsingFromTest extends AbstractReactorNettyHttpClientTest { - - HttpClient createHttpClient() { - return HttpClient.from(TcpClient.create()) - .tcpConfiguration({ tcpClient -> - tcpClient.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, CONNECT_TIMEOUT_MS) - }) - .resolver(getAddressResolverGroup()) - .headers({ headers -> headers.set(HttpHeaderNames.USER_AGENT, userAgent()) }) - } -} diff --git a/instrumentation/reactor/reactor-netty/reactor-netty-1.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/reactornetty/v1_0/AbstractReactorNettyHttpClientTest.java b/instrumentation/reactor/reactor-netty/reactor-netty-1.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/reactornetty/v1_0/AbstractReactorNettyHttpClientTest.java new file mode 100644 index 000000000000..d91e9181e715 --- /dev/null +++ b/instrumentation/reactor/reactor-netty/reactor-netty-1.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/reactornetty/v1_0/AbstractReactorNettyHttpClientTest.java @@ -0,0 +1,321 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.reactornetty.v1_0; + +import static io.opentelemetry.api.trace.SpanKind.CLIENT; +import static io.opentelemetry.api.trace.SpanKind.INTERNAL; +import static io.opentelemetry.api.trace.SpanKind.SERVER; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; +import static java.util.Collections.emptySet; +import static org.assertj.core.api.Assertions.catchThrowable; + +import io.netty.handler.codec.http.HttpMethod; +import io.netty.resolver.AddressResolverGroup; +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.api.trace.SpanContext; +import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.instrumentation.test.utils.PortUtils; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.http.AbstractHttpClientTest; +import io.opentelemetry.instrumentation.testing.junit.http.HttpClientInstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.http.HttpClientTestOptions; +import io.opentelemetry.sdk.trace.data.SpanData; +import io.opentelemetry.sdk.trace.data.StatusData; +import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; +import java.net.InetSocketAddress; +import java.net.URI; +import java.time.Duration; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; +import java.util.stream.IntStream; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; +import reactor.netty.http.client.HttpClient; + +abstract class AbstractReactorNettyHttpClientTest + extends AbstractHttpClientTest> { + + static final String USER_AGENT = "ReactorNetty"; + + @RegisterExtension + static final InstrumentationExtension testing = HttpClientInstrumentationExtension.forAgent(); + + protected abstract HttpClient createHttpClient(); + + protected AddressResolverGroup getAddressResolverGroup() { + return CustomNameResolverGroup.INSTANCE; + } + + @Override + public HttpClient.ResponseReceiver buildRequest( + String method, URI uri, Map headers) { + HttpClient client = + createHttpClient() + .followRedirect(true) + .headers(h -> headers.forEach(h::add)) + .baseUrl(resolveAddress("").toString()); + if (uri.toString().contains("/read-timeout")) { + client = client.responseTimeout(READ_TIMEOUT); + } + return client.request(HttpMethod.valueOf(method)).uri(uri.toString()); + } + + @Override + public int sendRequest( + HttpClient.ResponseReceiver request, String method, URI uri, Map headers) { + return request + .responseSingle( + (resp, content) -> { + // Make sure to consume content since that's when we close the span. + return content.map(unused -> resp); + }) + .block() + .status() + .code(); + } + + @Override + public void sendRequestWithCallback( + HttpClient.ResponseReceiver request, + String method, + URI uri, + Map headers, + AbstractHttpClientTest.RequestResult requestResult) { + request + .responseSingle( + (resp, content) -> { + // Make sure to consume content since that's when we close the span. + return content.map(unused -> resp); + }) + .subscribe( + response -> requestResult.complete(response.status().code()), requestResult::complete); + } + + @Override + protected void configure(HttpClientTestOptions options) { + options.disableTestRedirects(); + options.enableTestReadTimeout(); + options.setUserAgent(USER_AGENT); + options.enableTestCallbackWithImplicitParent(); + + options.setClientSpanErrorMapper( + (uri, exception) -> { + if (exception.getClass().getName().endsWith("ReactiveException")) { + // unopened port or non routable address + if ("http://localhost:61/".equals(uri.toString()) + || "https://192.0.2.1/".equals(uri.toString())) { + exception = exception.getCause(); + } + } + return exception; + }); + + options.setHttpAttributes(this::getHttpAttributes); + } + + protected Set> getHttpAttributes(URI uri) { + // unopened port or non routable address + if ("http://localhost:61/".equals(uri.toString()) + || "https://192.0.2.1/".equals(uri.toString())) { + return emptySet(); + } + + Set> attributes = new HashSet<>(HttpClientTestOptions.DEFAULT_HTTP_ATTRIBUTES); + if (uri.toString().contains("/read-timeout")) { + attributes.remove(SemanticAttributes.NET_PEER_NAME); + attributes.remove(SemanticAttributes.NET_PEER_PORT); + attributes.remove(SemanticAttributes.HTTP_FLAVOR); + } + return attributes; + } + + @Test + void shouldExposeContextToHttpClientCallbacks() throws InterruptedException { + AtomicReference onRequestSpan = new AtomicReference<>(); + AtomicReference afterRequestSpan = new AtomicReference<>(); + AtomicReference onResponseSpan = new AtomicReference<>(); + AtomicReference afterResponseSpan = new AtomicReference<>(); + CountDownLatch latch = new CountDownLatch(1); + + HttpClient httpClient = + createHttpClient() + .doOnRequest((rq, con) -> onRequestSpan.set(Span.current())) + .doAfterRequest((rq, con) -> afterRequestSpan.set(Span.current())) + .doOnResponse((rs, con) -> onResponseSpan.set(Span.current())) + .doAfterResponseSuccess( + (rs, con) -> { + afterResponseSpan.set(Span.current()); + latch.countDown(); + }); + + testing.runWithSpan( + "parent", + () -> + httpClient + .baseUrl(resolveAddress("").toString()) + .get() + .uri("/success") + .responseSingle( + (resp, content) -> { + // Make sure to consume content since that's when we close the span. + return content.map(unused -> resp); + }) + .block()); + + latch.await(10, TimeUnit.SECONDS); + + testing.waitAndAssertTraces( + trace -> { + SpanData parentSpan = trace.getSpan(0); + SpanData nettyClientSpan = trace.getSpan(1); + + trace.hasSpansSatisfyingExactly( + span -> span.hasName("parent").hasKind(INTERNAL).hasNoParent(), + span -> span.hasName("HTTP GET").hasKind(CLIENT).hasParent(parentSpan), + span -> span.hasName("test-http-server").hasKind(SERVER).hasParent(nettyClientSpan)); + + assertSameSpan(nettyClientSpan, onRequestSpan); + assertSameSpan(nettyClientSpan, afterRequestSpan); + assertSameSpan(nettyClientSpan, onResponseSpan); + assertSameSpan(parentSpan, afterResponseSpan); + }); + } + + @Test + void shouldExposeContextToHttpRequestErrorCallback() { + AtomicReference onRequestErrorSpan = new AtomicReference<>(); + + HttpClient httpClient = + createHttpClient().doOnRequestError((rq, err) -> onRequestErrorSpan.set(Span.current())); + + Throwable thrown = + catchThrowable( + () -> + testing.runWithSpan( + "parent", + () -> + httpClient + .get() + .uri("http://localhost:" + PortUtils.UNUSABLE_PORT + "/") + .response() + .block())); + + testing.waitAndAssertTraces( + trace -> { + SpanData parentSpan = trace.getSpan(0); + + trace.hasSpansSatisfyingExactly( + span -> + span.hasName("parent") + .hasKind(INTERNAL) + .hasNoParent() + .hasStatus(StatusData.error()) + .hasException(thrown), + span -> + span.hasKind(CLIENT) + .hasParent(parentSpan) + .hasStatus(StatusData.error()) + .hasException(thrown.getCause())); + + assertSameSpan(parentSpan, onRequestErrorSpan); + }); + } + + @Test + void shouldNotLeakConnections() { + HashSet uniqueChannelHashes = new HashSet<>(); + HttpClient httpClient = + createHttpClient().doOnConnect(config -> uniqueChannelHashes.add(config.channelHash())); + + int count = 100; + IntStream.range(0, count) + .forEach( + i -> + testing.runWithSpan( + "parent", + () -> { + int status = + httpClient + .get() + .uri(resolveAddress("/success")) + .responseSingle( + (resp, content) -> { + // Make sure to consume content since that's when we close the + // span. + return content.map(unused -> resp); + }) + .block() + .status() + .code(); + assertThat(status).isEqualTo(200); + })); + + testing.waitForTraces(count); + assertThat(uniqueChannelHashes).hasSize(1); + } + + @Test + void shouldEndSpanOnMonoTimeout() { + HttpClient httpClient = createHttpClient(); + + URI uri = resolveAddress("/read-timeout"); + Throwable thrown = + catchThrowable( + () -> + testing.runWithSpan( + "parent", + () -> + httpClient + .get() + .uri(uri) + .responseSingle( + (resp, content) -> { + // Make sure to consume content since that's when we close the + // span. + return content.map(unused -> resp); + }) + // apply Mono timeout that is way shorter than HTTP request timeout + .timeout(Duration.ofSeconds(1)) + .block())); + + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> + span.hasName("parent") + .hasKind(SpanKind.INTERNAL) + .hasNoParent() + .hasStatus(StatusData.error()) + .hasException(thrown), + span -> + span.hasName("HTTP GET") + .hasKind(CLIENT) + .hasParent(trace.getSpan(0)) + .hasAttributesSatisfyingExactly( + equalTo(SemanticAttributes.HTTP_METHOD, "GET"), + equalTo(SemanticAttributes.HTTP_URL, uri.toString()), + equalTo(SemanticAttributes.HTTP_USER_AGENT, USER_AGENT), + equalTo(SemanticAttributes.NET_PEER_NAME, "localhost"), + equalTo(SemanticAttributes.NET_PEER_PORT, uri.getPort())), + span -> + span.hasName("test-http-server") + .hasKind(SpanKind.SERVER) + .hasParent(trace.getSpan(1)))); + } + + private static void assertSameSpan(SpanData expected, AtomicReference actual) { + SpanContext expectedSpanContext = expected.getSpanContext(); + SpanContext actualSpanContext = actual.get().getSpanContext(); + assertThat(actualSpanContext.getTraceId()).isEqualTo(expectedSpanContext.getTraceId()); + assertThat(actualSpanContext.getSpanId()).isEqualTo(expectedSpanContext.getSpanId()); + } +} diff --git a/instrumentation/reactor/reactor-netty/reactor-netty-1.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/reactornetty/v1_0/CustomNameResolverGroup.java b/instrumentation/reactor/reactor-netty/reactor-netty-1.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/reactornetty/v1_0/CustomNameResolverGroup.java new file mode 100644 index 000000000000..de5ddee31fce --- /dev/null +++ b/instrumentation/reactor/reactor-netty/reactor-netty-1.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/reactornetty/v1_0/CustomNameResolverGroup.java @@ -0,0 +1,56 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.reactornetty.v1_0; + +import static java.util.Collections.singletonList; + +import io.netty.resolver.AddressResolver; +import io.netty.resolver.AddressResolverGroup; +import io.netty.resolver.InetNameResolver; +import io.netty.util.concurrent.EventExecutor; +import io.netty.util.concurrent.Promise; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.UnknownHostException; +import java.util.List; + +public class CustomNameResolverGroup extends AddressResolverGroup { + + public static final CustomNameResolverGroup INSTANCE = new CustomNameResolverGroup(); + + private CustomNameResolverGroup() {} + + @Override + protected AddressResolver newResolver(EventExecutor executor) { + return new CustomNameResolver(executor).asAddressResolver(); + } + + private static class CustomNameResolver extends InetNameResolver { + + private CustomNameResolver(EventExecutor executor) { + super(executor); + } + + @Override + protected void doResolve(String inetHost, Promise promise) { + try { + promise.setSuccess(InetAddress.getByName(inetHost)); + } catch (UnknownHostException exception) { + promise.setFailure(exception); + } + } + + @Override + protected void doResolveAll(String inetHost, Promise> promise) { + try { + // default implementation calls InetAddress.getAllByName + promise.setSuccess(singletonList(InetAddress.getByName(inetHost))); + } catch (UnknownHostException exception) { + promise.setFailure(exception); + } + } + } +} diff --git a/instrumentation/reactor/reactor-netty/reactor-netty-1.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/reactornetty/v1_0/ReactorNettyClientSslTest.java b/instrumentation/reactor/reactor-netty/reactor-netty-1.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/reactornetty/v1_0/ReactorNettyClientSslTest.java new file mode 100644 index 000000000000..288f842a033d --- /dev/null +++ b/instrumentation/reactor/reactor-netty/reactor-netty-1.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/reactornetty/v1_0/ReactorNettyClientSslTest.java @@ -0,0 +1,229 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.reactornetty.v1_0; + +import static io.opentelemetry.api.trace.SpanKind.CLIENT; +import static io.opentelemetry.api.trace.SpanKind.INTERNAL; +import static io.opentelemetry.api.trace.SpanKind.SERVER; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies; +import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.HttpFlavorValues.HTTP_1_1; +import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.NetTransportValues.IP_TCP; +import static org.assertj.core.api.Assertions.catchThrowable; + +import io.netty.handler.ssl.SslContextBuilder; +import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.http.HttpClientTestServer; +import io.opentelemetry.sdk.trace.data.EventData; +import io.opentelemetry.sdk.trace.data.StatusData; +import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; +import java.util.List; +import javax.annotation.Nullable; +import javax.net.ssl.SSLException; +import javax.net.ssl.SSLHandshakeException; +import org.assertj.core.api.AbstractLongAssert; +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 reactor.core.publisher.Mono; +import reactor.netty.http.client.HttpClient; +import reactor.netty.http.client.HttpClientResponse; +import reactor.netty.tcp.SslProvider; + +class ReactorNettyClientSslTest { + + @RegisterExtension + static final InstrumentationExtension testing = AgentInstrumentationExtension.create(); + + static HttpClientTestServer server; + + @BeforeAll + static void setUp() { + server = new HttpClientTestServer(testing.getOpenTelemetry()); + server.start(); + } + + @AfterAll + static void tearDown() { + server.stop(); + } + + @Test + void shouldFailSslHandshake() throws SSLException { + HttpClient httpClient = createHttpClient("SSLv3"); + String uri = "https://localhost:" + server.httpsPort() + "/success"; + + Mono responseMono = + httpClient + .get() + .uri(uri) + .responseSingle( + (resp, content) -> { + // Make sure to consume content since that's when we close the span. + return content.map(unused -> resp); + }); + + Throwable thrown = + catchThrowable(() -> testing.runWithSpan("parent", () -> responseMono.block())); + + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactlyInAnyOrder( + span -> + span.hasName("parent") + .hasKind(INTERNAL) + .hasNoParent() + .hasStatus(StatusData.error()) + .hasException(thrown), + span -> + span.hasName("HTTP GET") + .hasKind(CLIENT) + .hasParent(trace.getSpan(0)) + .hasStatus(StatusData.error()) + // netty swallows the exception, it doesn't make any sense to hard-code the + // message + .hasEventsSatisfying(ReactorNettyClientSslTest::isSslHandshakeException) + .hasAttributesSatisfyingExactly( + equalTo(SemanticAttributes.HTTP_METHOD, "GET"), + equalTo(SemanticAttributes.HTTP_URL, uri), + equalTo(SemanticAttributes.NET_PEER_NAME, "localhost"), + equalTo(SemanticAttributes.NET_PEER_PORT, server.httpsPort())), + span -> + span.hasName("RESOLVE") + .hasKind(INTERNAL) + .hasParent(trace.getSpan(1)) + .hasAttributesSatisfyingExactly( + equalTo(SemanticAttributes.NET_TRANSPORT, IP_TCP), + equalTo(SemanticAttributes.NET_PEER_NAME, "localhost"), + equalTo(SemanticAttributes.NET_PEER_PORT, server.httpsPort())), + span -> + span.hasName("CONNECT") + .hasKind(INTERNAL) + .hasParent(trace.getSpan(1)) + .hasAttributesSatisfyingExactly( + equalTo(SemanticAttributes.NET_TRANSPORT, IP_TCP), + equalTo(SemanticAttributes.NET_PEER_NAME, "localhost"), + equalTo(SemanticAttributes.NET_PEER_PORT, server.httpsPort()), + equalTo(SemanticAttributes.NET_SOCK_PEER_ADDR, "127.0.0.1")), + span -> + span.hasName("SSL handshake") + .hasKind(INTERNAL) + .hasParent(trace.getSpan(1)) + .hasStatus(StatusData.error()) + // netty swallows the exception, it doesn't make any sense to hard-code the + // message + .hasEventsSatisfying(ReactorNettyClientSslTest::isSslHandshakeException) + .hasAttributesSatisfyingExactly( + equalTo(SemanticAttributes.NET_TRANSPORT, IP_TCP), + equalTo(SemanticAttributes.NET_SOCK_PEER_ADDR, "127.0.0.1"), + equalTo(SemanticAttributes.NET_SOCK_PEER_NAME, "localhost"), + equalTo(SemanticAttributes.NET_SOCK_PEER_PORT, server.httpsPort())))); + } + + @Test + void shouldSuccessfullyEstablishSslHandshake() throws SSLException { + HttpClient httpClient = createHttpClient(); + String uri = "https://localhost:" + server.httpsPort() + "/success"; + + Mono responseMono = + httpClient + .get() + .uri(uri) + .responseSingle( + (resp, content) -> { + // Make sure to consume content since that's when we close the span. + return content.map(unused -> resp); + }); + + testing.runWithSpan("parent", () -> responseMono.block()); + + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactlyInAnyOrder( + span -> span.hasName("parent").hasKind(INTERNAL).hasNoParent(), + span -> + span.hasName("HTTP GET") + .hasKind(CLIENT) + .hasParent(trace.getSpan(0)) + .hasAttributesSatisfyingExactly( + equalTo(SemanticAttributes.HTTP_METHOD, "GET"), + equalTo(SemanticAttributes.HTTP_URL, uri), + equalTo(SemanticAttributes.HTTP_FLAVOR, HTTP_1_1), + equalTo(SemanticAttributes.HTTP_STATUS_CODE, 200), + satisfies( + SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH, + AbstractLongAssert::isNotNegative), + equalTo(SemanticAttributes.NET_PEER_NAME, "localhost"), + equalTo(SemanticAttributes.NET_PEER_PORT, server.httpsPort()), + equalTo(SemanticAttributes.NET_SOCK_PEER_ADDR, "127.0.0.1")), + span -> + span.hasName("RESOLVE") + .hasKind(INTERNAL) + .hasParent(trace.getSpan(1)) + .hasAttributesSatisfyingExactly( + equalTo(SemanticAttributes.NET_TRANSPORT, IP_TCP), + equalTo(SemanticAttributes.NET_PEER_NAME, "localhost"), + equalTo(SemanticAttributes.NET_PEER_PORT, server.httpsPort())), + span -> + span.hasName("CONNECT") + .hasKind(INTERNAL) + .hasParent(trace.getSpan(1)) + .hasAttributesSatisfyingExactly( + equalTo(SemanticAttributes.NET_TRANSPORT, IP_TCP), + equalTo(SemanticAttributes.NET_PEER_NAME, "localhost"), + equalTo(SemanticAttributes.NET_PEER_PORT, server.httpsPort()), + equalTo(SemanticAttributes.NET_SOCK_PEER_ADDR, "127.0.0.1")), + span -> + span.hasName("SSL handshake") + .hasKind(INTERNAL) + .hasParent(trace.getSpan(1)) + .hasAttributesSatisfyingExactly( + equalTo(SemanticAttributes.NET_TRANSPORT, IP_TCP), + equalTo(SemanticAttributes.NET_SOCK_PEER_ADDR, "127.0.0.1"), + equalTo(SemanticAttributes.NET_SOCK_PEER_NAME, "localhost"), + equalTo(SemanticAttributes.NET_SOCK_PEER_PORT, server.httpsPort())), + span -> + span.hasName("test-http-server").hasKind(SERVER).hasParent(trace.getSpan(1)))); + } + + private static HttpClient createHttpClient() throws SSLException { + return ReactorNettyClientSslTest.createHttpClient(null); + } + + private static HttpClient createHttpClient(@Nullable String enabledProtocol) throws SSLException { + SslContextBuilder sslContext = SslContextBuilder.forClient(); + if (enabledProtocol != null) { + sslContext = sslContext.protocols(enabledProtocol); + } + + SslProvider sslProvider = SslProvider.builder().sslContext(sslContext.build()).build(); + return HttpClient.create().secure(sslProvider); + } + + private static void isSslHandshakeException(List events) { + assertThat(events) + .filteredOn(event -> event.getName().equals(SemanticAttributes.EXCEPTION_EVENT_NAME)) + .satisfiesExactly( + event -> + assertThat(event) + .hasAttributesSatisfying( + attributes -> + assertThat(attributes) + .hasSize(3) + .containsEntry( + SemanticAttributes.EXCEPTION_TYPE, + SSLHandshakeException.class.getCanonicalName()) + .hasEntrySatisfying( + SemanticAttributes.EXCEPTION_MESSAGE, + s -> assertThat(s).isNotEmpty()) + .hasEntrySatisfying( + SemanticAttributes.EXCEPTION_STACKTRACE, + s -> assertThat(s).isNotEmpty()))); + } +} diff --git a/instrumentation/reactor/reactor-netty/reactor-netty-1.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/reactornetty/v1_0/ReactorNettyConnectionSpanTest.java b/instrumentation/reactor/reactor-netty/reactor-netty-1.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/reactornetty/v1_0/ReactorNettyConnectionSpanTest.java new file mode 100644 index 000000000000..5d9245e5fb75 --- /dev/null +++ b/instrumentation/reactor/reactor-netty/reactor-netty-1.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/reactornetty/v1_0/ReactorNettyConnectionSpanTest.java @@ -0,0 +1,180 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.reactornetty.v1_0; + +import static io.opentelemetry.api.trace.SpanKind.CLIENT; +import static io.opentelemetry.api.trace.SpanKind.INTERNAL; +import static io.opentelemetry.api.trace.SpanKind.SERVER; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies; +import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.HttpFlavorValues.HTTP_1_1; +import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.NetTransportValues.IP_TCP; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.catchThrowable; + +import io.opentelemetry.instrumentation.test.utils.PortUtils; +import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.http.HttpClientTestServer; +import io.opentelemetry.sdk.trace.data.StatusData; +import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; +import org.assertj.core.api.AbstractLongAssert; +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 reactor.netty.http.client.HttpClient; + +class ReactorNettyConnectionSpanTest { + + @RegisterExtension + static final InstrumentationExtension testing = AgentInstrumentationExtension.create(); + + static HttpClientTestServer server; + + @BeforeAll + static void setUp() { + server = new HttpClientTestServer(testing.getOpenTelemetry()); + server.start(); + } + + @AfterAll + static void tearDown() { + server.stop(); + } + + @Test + void testSuccessfulRequest() { + HttpClient httpClient = HttpClient.create(); + String uri = "http://localhost:" + server.httpPort() + "/success"; + + int responseCode = + testing.runWithSpan( + "parent", + () -> + httpClient + .get() + .uri(uri) + .responseSingle( + (resp, content) -> { + // Make sure to consume content since that's when we close the span. + return content.map(unused -> resp); + }) + .block() + .status() + .code()); + + assertThat(responseCode).isEqualTo(200); + + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactlyInAnyOrder( + span -> span.hasName("parent").hasKind(INTERNAL).hasNoParent(), + span -> + span.hasName("HTTP GET") + .hasKind(CLIENT) + .hasParent(trace.getSpan(0)) + .hasAttributesSatisfyingExactly( + equalTo(SemanticAttributes.HTTP_METHOD, "GET"), + equalTo(SemanticAttributes.HTTP_URL, uri), + equalTo(SemanticAttributes.HTTP_FLAVOR, HTTP_1_1), + equalTo(SemanticAttributes.HTTP_STATUS_CODE, 200), + satisfies( + SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH, + AbstractLongAssert::isNotNegative), + equalTo(SemanticAttributes.NET_PEER_NAME, "localhost"), + equalTo(SemanticAttributes.NET_PEER_PORT, server.httpPort()), + equalTo(SemanticAttributes.NET_SOCK_PEER_ADDR, "127.0.0.1")), + span -> + span.hasName("RESOLVE") + .hasKind(INTERNAL) + .hasParent(trace.getSpan(1)) + .hasAttributesSatisfyingExactly( + equalTo(SemanticAttributes.NET_TRANSPORT, IP_TCP), + equalTo(SemanticAttributes.NET_PEER_NAME, "localhost"), + equalTo(SemanticAttributes.NET_PEER_PORT, server.httpPort())), + span -> + span.hasName("CONNECT") + .hasKind(INTERNAL) + .hasParent(trace.getSpan(1)) + .hasAttributesSatisfyingExactly( + equalTo(SemanticAttributes.NET_TRANSPORT, IP_TCP), + equalTo(SemanticAttributes.NET_PEER_NAME, "localhost"), + equalTo(SemanticAttributes.NET_PEER_PORT, server.httpPort()), + equalTo(SemanticAttributes.NET_SOCK_PEER_ADDR, "127.0.0.1")), + span -> + span.hasName("test-http-server").hasKind(SERVER).hasParent(trace.getSpan(1)))); + } + + @Test + void testFailingRequest() { + HttpClient httpClient = HttpClient.create(); + String uri = "http://localhost:" + PortUtils.UNUSABLE_PORT; + + Throwable thrown = + catchThrowable( + () -> + testing.runWithSpan( + "parent", + () -> + httpClient + .get() + .uri(uri) + .responseSingle( + (resp, content) -> { + // Make sure to consume content since that's when we close the + // span. + return content.map(unused -> resp); + }) + .block() + .status() + .code())); + + Throwable connectException = thrown.getCause(); + + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactlyInAnyOrder( + span -> + span.hasName("parent") + .hasKind(INTERNAL) + .hasNoParent() + .hasStatus(StatusData.error()) + .hasException(thrown), + span -> + span.hasName("HTTP GET") + .hasKind(CLIENT) + .hasParent(trace.getSpan(0)) + .hasStatus(StatusData.error()) + .hasException(connectException) + .hasAttributesSatisfyingExactly( + equalTo(SemanticAttributes.HTTP_METHOD, "GET"), + equalTo(SemanticAttributes.HTTP_URL, uri), + equalTo(SemanticAttributes.NET_PEER_NAME, "localhost"), + equalTo(SemanticAttributes.NET_PEER_PORT, PortUtils.UNUSABLE_PORT)), + span -> + span.hasName("RESOLVE") + .hasKind(INTERNAL) + .hasParent(trace.getSpan(1)) + .hasAttributesSatisfyingExactly( + equalTo(SemanticAttributes.NET_TRANSPORT, IP_TCP), + equalTo(SemanticAttributes.NET_PEER_NAME, "localhost"), + equalTo(SemanticAttributes.NET_PEER_PORT, PortUtils.UNUSABLE_PORT)), + span -> + span.hasName("CONNECT") + .hasKind(INTERNAL) + .hasParent(trace.getSpan(1)) + .hasStatus(StatusData.error()) + .hasException(connectException) + .hasAttributesSatisfyingExactly( + equalTo(SemanticAttributes.NET_TRANSPORT, IP_TCP), + equalTo(SemanticAttributes.NET_PEER_NAME, "localhost"), + equalTo(SemanticAttributes.NET_PEER_PORT, PortUtils.UNUSABLE_PORT), + satisfies( + SemanticAttributes.NET_SOCK_PEER_ADDR, + val -> val.isIn(null, "127.0.0.1"))))); + } +} diff --git a/instrumentation/reactor/reactor-netty/reactor-netty-1.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/reactornetty/v1_0/ReactorNettyHttpClientTest.java b/instrumentation/reactor/reactor-netty/reactor-netty-1.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/reactornetty/v1_0/ReactorNettyHttpClientTest.java new file mode 100644 index 000000000000..02b368fd23e7 --- /dev/null +++ b/instrumentation/reactor/reactor-netty/reactor-netty-1.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/reactornetty/v1_0/ReactorNettyHttpClientTest.java @@ -0,0 +1,70 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.reactornetty.v1_0; + +import io.netty.channel.ChannelOption; +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.instrumentation.testing.junit.http.HttpClientTestOptions; +import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; +import io.opentelemetry.testing.internal.armeria.common.HttpHeaderNames; +import java.net.URI; +import java.util.HashSet; +import java.util.Set; +import reactor.netty.http.client.HttpClient; + +class ReactorNettyHttpClientTest extends AbstractReactorNettyHttpClientTest { + + @Override + protected HttpClient createHttpClient() { + int connectionTimeoutMillis = (int) CONNECTION_TIMEOUT.toMillis(); + return HttpClient.create() + .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, connectionTimeoutMillis) + .resolver(getAddressResolverGroup()) + .headers(headers -> headers.set(HttpHeaderNames.USER_AGENT, USER_AGENT)); + } + + @Override + protected void configure(HttpClientTestOptions options) { + super.configure(options); + + options.setSingleConnectionFactory( + (host, port) -> { + HttpClient httpClient = + HttpClient.newConnection() + .host(host) + .port(port) + .headers(headers -> headers.set(HttpHeaderNames.USER_AGENT, USER_AGENT)); + + return (path, headers) -> + httpClient + .headers(h -> headers.forEach(h::add)) + .get() + .uri(path) + .responseSingle( + (resp, content) -> { + // Make sure to consume content since that's when we close the span. + return content.map(unused -> resp); + }) + .block() + .status() + .code(); + }); + } + + @Override + protected Set> getHttpAttributes(URI uri) { + if (uri.toString().contains("/success")) { + // the single connection test does not report net.peer.* attributes; it only reports the + // net.peer.sock.* attributes + Set> attributes = + new HashSet<>(HttpClientTestOptions.DEFAULT_HTTP_ATTRIBUTES); + attributes.remove(SemanticAttributes.NET_PEER_NAME); + attributes.remove(SemanticAttributes.NET_PEER_PORT); + return attributes; + } + return super.getHttpAttributes(uri); + } +} diff --git a/instrumentation/reactor/reactor-netty/reactor-netty-1.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/reactornetty/v1_0/ReactorNettyHttpClientUsingFromTest.java b/instrumentation/reactor/reactor-netty/reactor-netty-1.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/reactornetty/v1_0/ReactorNettyHttpClientUsingFromTest.java new file mode 100644 index 000000000000..446b658ad152 --- /dev/null +++ b/instrumentation/reactor/reactor-netty/reactor-netty-1.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/reactornetty/v1_0/ReactorNettyHttpClientUsingFromTest.java @@ -0,0 +1,24 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.reactornetty.v1_0; + +import io.netty.channel.ChannelOption; +import io.opentelemetry.testing.internal.armeria.common.HttpHeaderNames; +import reactor.netty.http.client.HttpClient; +import reactor.netty.tcp.TcpClient; + +class ReactorNettyHttpClientUsingFromTest extends AbstractReactorNettyHttpClientTest { + + @SuppressWarnings("deprecation") // from(TcpClient) is deprecated, but we want to test it anyway + @Override + protected HttpClient createHttpClient() { + int connectionTimeoutMillis = (int) CONNECTION_TIMEOUT.toMillis(); + return HttpClient.from(TcpClient.create()) + .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, connectionTimeoutMillis) + .resolver(getAddressResolverGroup()) + .headers(headers -> headers.set(HttpHeaderNames.USER_AGENT, USER_AGENT)); + } +} diff --git a/instrumentation/reactor/reactor-netty/reactor-netty-1.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/reactornetty/v1_0/ReactorNettyWithSpanTest.java b/instrumentation/reactor/reactor-netty/reactor-netty-1.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/reactornetty/v1_0/ReactorNettyWithSpanTest.java new file mode 100644 index 000000000000..3f1d60570192 --- /dev/null +++ b/instrumentation/reactor/reactor-netty/reactor-netty-1.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/reactornetty/v1_0/ReactorNettyWithSpanTest.java @@ -0,0 +1,79 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.reactornetty.v1_0; + +import static io.opentelemetry.api.trace.SpanKind.CLIENT; +import static io.opentelemetry.api.trace.SpanKind.INTERNAL; +import static io.opentelemetry.api.trace.SpanKind.SERVER; + +import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.http.HttpClientTestServer; +import java.time.Duration; +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 reactor.core.publisher.Mono; +import reactor.netty.http.client.HttpClient; +import reactor.test.StepVerifier; + +class ReactorNettyWithSpanTest { + + @RegisterExtension + static final InstrumentationExtension testing = AgentInstrumentationExtension.create(); + + static HttpClientTestServer server; + + @BeforeAll + static void setUp() { + server = new HttpClientTestServer(testing.getOpenTelemetry()); + server.start(); + } + + @AfterAll + static void tearDown() { + server.stop(); + } + + @Test + public void testSuccessfulNestedUnderWithSpan() { + HttpClient httpClient = HttpClient.create(); + + Mono httpRequest = + Mono.defer( + () -> + httpClient + .get() + .uri("http://localhost:" + server.httpPort() + "/success") + .responseSingle( + (resp, content) -> { + // Make sure to consume content since that's when we close the span. + return content.map(unused -> resp); + }) + .map(r -> r.status().code())); + + Mono getResponse = + new TracedWithSpan() + .mono( + // https://github.com/open-telemetry/opentelemetry-java-instrumentation/issues/4348 + // our HTTP server is synchronous, i.e. it returns Mono.just with response + // which is not supported by TracingSubscriber - it does not instrument scalar calls + // so we delay here to fake async http request and let Reactor context + // instrumentation work + Mono.delay(Duration.ofMillis(1)).then(httpRequest)); + + StepVerifier.create(getResponse).expectNext(200).expectComplete().verify(); + + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> span.hasName("TracedWithSpan.mono").hasKind(INTERNAL).hasNoParent(), + span -> span.hasName("HTTP GET").hasKind(CLIENT).hasParent(trace.getSpan(0)), + span -> + span.hasName("test-http-server").hasKind(SERVER).hasParent(trace.getSpan(1)))); + } +} diff --git a/instrumentation/reactor/reactor-netty/reactor-netty-1.0/javaagent/src/test/java/io/opentelemetry/test/reactor/netty/TracedWithSpan.java b/instrumentation/reactor/reactor-netty/reactor-netty-1.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/reactornetty/v1_0/TracedWithSpan.java similarity index 63% rename from instrumentation/reactor/reactor-netty/reactor-netty-1.0/javaagent/src/test/java/io/opentelemetry/test/reactor/netty/TracedWithSpan.java rename to instrumentation/reactor/reactor-netty/reactor-netty-1.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/reactornetty/v1_0/TracedWithSpan.java index 7a208056e28d..2bd4c9607ea7 100644 --- a/instrumentation/reactor/reactor-netty/reactor-netty-1.0/javaagent/src/test/java/io/opentelemetry/test/reactor/netty/TracedWithSpan.java +++ b/instrumentation/reactor/reactor-netty/reactor-netty-1.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/reactornetty/v1_0/TracedWithSpan.java @@ -3,9 +3,9 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.test.reactor.netty; +package io.opentelemetry.javaagent.instrumentation.reactornetty.v1_0; -import io.opentelemetry.extension.annotations.WithSpan; +import io.opentelemetry.instrumentation.annotations.WithSpan; import reactor.core.publisher.Mono; public class TracedWithSpan { diff --git a/instrumentation/rediscala-1.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rediscala/RediscalaAttributesGetter.java b/instrumentation/rediscala-1.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rediscala/RediscalaAttributesGetter.java index 5dd7ce2e0b23..1ab67dbba85b 100644 --- a/instrumentation/rediscala-1.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rediscala/RediscalaAttributesGetter.java +++ b/instrumentation/rediscala-1.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rediscala/RediscalaAttributesGetter.java @@ -6,7 +6,6 @@ package io.opentelemetry.javaagent.instrumentation.rediscala; import io.opentelemetry.instrumentation.api.instrumenter.db.DbClientAttributesGetter; -import io.opentelemetry.instrumentation.api.util.ClassNames; import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; import java.util.Locale; import javax.annotation.Nullable; @@ -45,6 +44,6 @@ public String statement(RedisCommand redisCommand) { @Override public String operation(RedisCommand redisCommand) { - return ClassNames.simpleName(redisCommand.getClass()).toUpperCase(Locale.ROOT); + return redisCommand.getClass().getSimpleName().toUpperCase(Locale.ROOT); } } diff --git a/instrumentation/rediscala-1.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rediscala/RediscalaSingletons.java b/instrumentation/rediscala-1.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rediscala/RediscalaSingletons.java index ca0365de176b..fa92b17a2633 100644 --- a/instrumentation/rediscala-1.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rediscala/RediscalaSingletons.java +++ b/instrumentation/rediscala-1.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rediscala/RediscalaSingletons.java @@ -27,7 +27,7 @@ public final class RediscalaSingletons { INSTRUMENTATION_NAME, DbClientSpanNameExtractor.create(dbAttributesGetter)) .addAttributesExtractor(DbClientAttributesExtractor.create(dbAttributesGetter)) - .newInstrumenter(SpanKindExtractor.alwaysClient()); + .buildInstrumenter(SpanKindExtractor.alwaysClient()); } public static Instrumenter, Void> instrumenter() { diff --git a/instrumentation/redisson/redisson-3.0/javaagent/build.gradle.kts b/instrumentation/redisson/redisson-3.0/javaagent/build.gradle.kts index 6ef7cc2b65cc..bc3bdb749e46 100644 --- a/instrumentation/redisson/redisson-3.0/javaagent/build.gradle.kts +++ b/instrumentation/redisson/redisson-3.0/javaagent/build.gradle.kts @@ -22,10 +22,10 @@ dependencies { testImplementation(project(":instrumentation:redisson:redisson-common:testing")) - latestDepTestLibrary("org.redisson:redisson:3.16.+") + latestDepTestLibrary("org.redisson:redisson:3.16.+") // see redisson-3.17 module } tasks.test { systemProperty("testLatestDeps", findProperty("testLatestDeps") as Boolean) - usesService(gradle.sharedServices.registrations["testcontainersBuildService"].getService()) + usesService(gradle.sharedServices.registrations["testcontainersBuildService"].service) } diff --git a/instrumentation/redisson/redisson-3.17/javaagent/build.gradle.kts b/instrumentation/redisson/redisson-3.17/javaagent/build.gradle.kts index f3f50e716781..34db4c2c325b 100644 --- a/instrumentation/redisson/redisson-3.17/javaagent/build.gradle.kts +++ b/instrumentation/redisson/redisson-3.17/javaagent/build.gradle.kts @@ -27,4 +27,5 @@ dependencies { tasks.test { systemProperty("testLatestDeps", findProperty("testLatestDeps") as Boolean) usesService(gradle.sharedServices.registrations["testcontainersBuildService"].getService()) + jvmArgs("-Dotel.instrumentation.common.db-statement-sanitizer.enabled=true") } diff --git a/instrumentation/redisson/redisson-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/redisson/RedissonInstrumenterFactory.java b/instrumentation/redisson/redisson-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/redisson/RedissonInstrumenterFactory.java index 0d105692a04f..745d55e72659 100644 --- a/instrumentation/redisson/redisson-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/redisson/RedissonInstrumenterFactory.java +++ b/instrumentation/redisson/redisson-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/redisson/RedissonInstrumenterFactory.java @@ -24,7 +24,7 @@ public static Instrumenter createInstrumenter(String inst DbClientSpanNameExtractor.create(dbAttributesGetter)) .addAttributesExtractor(DbClientAttributesExtractor.create(dbAttributesGetter)) .addAttributesExtractor(NetClientAttributesExtractor.create(netAttributesGetter)) - .newInstrumenter(SpanKindExtractor.alwaysClient()); + .buildInstrumenter(SpanKindExtractor.alwaysClient()); } private RedissonInstrumenterFactory() {} diff --git a/instrumentation/redisson/redisson-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/redisson/RedissonNetAttributesGetter.java b/instrumentation/redisson/redisson-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/redisson/RedissonNetAttributesGetter.java index 0700622183e0..d06d159eac06 100644 --- a/instrumentation/redisson/redisson-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/redisson/RedissonNetAttributesGetter.java +++ b/instrumentation/redisson/redisson-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/redisson/RedissonNetAttributesGetter.java @@ -12,14 +12,26 @@ final class RedissonNetAttributesGetter extends InetSocketAddressNetClientAttributesGetter { + @Nullable @Override - public InetSocketAddress getAddress(RedissonRequest request, @Nullable Void unused) { - return request.getAddress(); + public String transport(RedissonRequest request, @Nullable Void unused) { + return null; } @Nullable @Override - public String transport(RedissonRequest request, @Nullable Void unused) { + public String peerName(RedissonRequest redissonRequest) { + return null; + } + + @Nullable + @Override + public Integer peerPort(RedissonRequest redissonRequest) { return null; } + + @Override + protected InetSocketAddress getPeerSocketAddress(RedissonRequest request, @Nullable Void unused) { + return request.getAddress(); + } } diff --git a/instrumentation/redisson/redisson-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/redisson/RedissonRequest.java b/instrumentation/redisson/redisson-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/redisson/RedissonRequest.java index 828fde96db08..4a3d1e651846 100644 --- a/instrumentation/redisson/redisson-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/redisson/RedissonRequest.java +++ b/instrumentation/redisson/redisson-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/redisson/RedissonRequest.java @@ -11,6 +11,7 @@ import com.google.auto.value.AutoValue; import io.netty.buffer.ByteBuf; import io.opentelemetry.instrumentation.api.db.RedisCommandSanitizer; +import io.opentelemetry.javaagent.bootstrap.internal.CommonConfig; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; @@ -27,6 +28,9 @@ @AutoValue public abstract class RedissonRequest { + private static final RedisCommandSanitizer sanitizer = + RedisCommandSanitizer.create(CommonConfig.get().isStatementSanitizationEnabled()); + public static RedissonRequest create(InetSocketAddress address, Object command) { return new AutoValue_RedissonRequest(address, command); } @@ -98,7 +102,7 @@ private static String normalizeSingleCommand(CommandData command) { args.add(param); } } - return RedisCommandSanitizer.sanitize(command.getCommand().getName(), args); + return sanitizer.sanitize(command.getCommand().getName(), args); } @Nullable diff --git a/instrumentation/redisson/redisson-common/testing/build.gradle.kts b/instrumentation/redisson/redisson-common/testing/build.gradle.kts index e730646c7981..7931c38e109b 100644 --- a/instrumentation/redisson/redisson-common/testing/build.gradle.kts +++ b/instrumentation/redisson/redisson-common/testing/build.gradle.kts @@ -12,3 +12,9 @@ dependencies { compileOnly("org.redisson:redisson:3.0.0") } + +tasks { + test { + jvmArgs("-Dotel.instrumentation.common.db-statement-sanitizer.enabled=true") + } +} diff --git a/instrumentation/redisson/redisson-common/testing/src/main/groovy/AbstractRedissonAsyncClientTest.groovy b/instrumentation/redisson/redisson-common/testing/src/main/groovy/AbstractRedissonAsyncClientTest.groovy index 9849b7db21b0..b205aaf4df53 100644 --- a/instrumentation/redisson/redisson-common/testing/src/main/groovy/AbstractRedissonAsyncClientTest.groovy +++ b/instrumentation/redisson/redisson-common/testing/src/main/groovy/AbstractRedissonAsyncClientTest.groovy @@ -6,9 +6,6 @@ import io.opentelemetry.api.trace.Span import io.opentelemetry.instrumentation.test.AgentInstrumentationSpecification import io.opentelemetry.semconv.trace.attributes.SemanticAttributes -import java.util.concurrent.Callable -import java.util.concurrent.CompletionStage -import java.util.concurrent.TimeUnit import org.redisson.Redisson import org.redisson.api.RBucket import org.redisson.api.RFuture @@ -20,6 +17,10 @@ import org.redisson.config.SingleServerConfig import org.testcontainers.containers.GenericContainer import spock.lang.Shared +import java.util.concurrent.Callable +import java.util.concurrent.CompletionStage +import java.util.concurrent.TimeUnit + import static io.opentelemetry.api.trace.SpanKind.CLIENT import static io.opentelemetry.api.trace.SpanKind.INTERNAL @@ -77,10 +78,10 @@ abstract class AbstractRedissonAsyncClientTest extends AgentInstrumentationSpeci name "SET" kind CLIENT attributes { + "$SemanticAttributes.NET_SOCK_PEER_ADDR" "127.0.0.1" + "$SemanticAttributes.NET_SOCK_PEER_NAME" "localhost" + "$SemanticAttributes.NET_SOCK_PEER_PORT" port "$SemanticAttributes.DB_SYSTEM" "redis" - "$SemanticAttributes.NET_PEER_IP" "127.0.0.1" - "$SemanticAttributes.NET_PEER_NAME" "localhost" - "$SemanticAttributes.NET_PEER_PORT" port "$SemanticAttributes.DB_STATEMENT" "SET foo ?" "$SemanticAttributes.DB_OPERATION" "SET" } @@ -117,10 +118,10 @@ abstract class AbstractRedissonAsyncClientTest extends AgentInstrumentationSpeci kind CLIENT childOf(span(0)) attributes { + "$SemanticAttributes.NET_SOCK_PEER_ADDR" "127.0.0.1" + "$SemanticAttributes.NET_SOCK_PEER_NAME" "localhost" + "$SemanticAttributes.NET_SOCK_PEER_PORT" port "$SemanticAttributes.DB_SYSTEM" "redis" - "$SemanticAttributes.NET_PEER_IP" "127.0.0.1" - "$SemanticAttributes.NET_PEER_NAME" "localhost" - "$SemanticAttributes.NET_PEER_PORT" port "$SemanticAttributes.DB_STATEMENT" "SADD set1 ?" "$SemanticAttributes.DB_OPERATION" "SADD" } diff --git a/instrumentation/redisson/redisson-common/testing/src/main/groovy/AbstractRedissonClientTest.groovy b/instrumentation/redisson/redisson-common/testing/src/main/groovy/AbstractRedissonClientTest.groovy index 98722e2f6f05..537d9ff32155 100644 --- a/instrumentation/redisson/redisson-common/testing/src/main/groovy/AbstractRedissonClientTest.groovy +++ b/instrumentation/redisson/redisson-common/testing/src/main/groovy/AbstractRedissonClientTest.groovy @@ -77,10 +77,10 @@ abstract class AbstractRedissonClientTest extends AgentInstrumentationSpecificat name "SET" kind CLIENT attributes { + "$SemanticAttributes.NET_SOCK_PEER_ADDR" "127.0.0.1" + "$SemanticAttributes.NET_SOCK_PEER_NAME" "localhost" + "$SemanticAttributes.NET_SOCK_PEER_PORT" port "$SemanticAttributes.DB_SYSTEM" "redis" - "$SemanticAttributes.NET_PEER_IP" "127.0.0.1" - "$SemanticAttributes.NET_PEER_NAME" "localhost" - "$SemanticAttributes.NET_PEER_PORT" port "$SemanticAttributes.DB_STATEMENT" "SET foo ?" "$SemanticAttributes.DB_OPERATION" "SET" } @@ -91,10 +91,10 @@ abstract class AbstractRedissonClientTest extends AgentInstrumentationSpecificat name "GET" kind CLIENT attributes { + "$SemanticAttributes.NET_SOCK_PEER_ADDR" "127.0.0.1" + "$SemanticAttributes.NET_SOCK_PEER_NAME" "localhost" + "$SemanticAttributes.NET_SOCK_PEER_PORT" port "$SemanticAttributes.DB_SYSTEM" "redis" - "$SemanticAttributes.NET_PEER_IP" "127.0.0.1" - "$SemanticAttributes.NET_PEER_NAME" "localhost" - "$SemanticAttributes.NET_PEER_PORT" port "$SemanticAttributes.DB_STATEMENT" "GET foo" "$SemanticAttributes.DB_OPERATION" "GET" } @@ -117,10 +117,10 @@ abstract class AbstractRedissonClientTest extends AgentInstrumentationSpecificat name "DB Query" kind CLIENT attributes { + "$SemanticAttributes.NET_SOCK_PEER_ADDR" "127.0.0.1" + "$SemanticAttributes.NET_SOCK_PEER_NAME" "localhost" + "$SemanticAttributes.NET_SOCK_PEER_PORT" port "$SemanticAttributes.DB_SYSTEM" "redis" - "$SemanticAttributes.NET_PEER_IP" "127.0.0.1" - "$SemanticAttributes.NET_PEER_NAME" "localhost" - "$SemanticAttributes.NET_PEER_PORT" port "$SemanticAttributes.DB_STATEMENT" "SET batch1 ?;SET batch2 ?" "$SemanticAttributes.DB_OPERATION" null } @@ -141,10 +141,10 @@ abstract class AbstractRedissonClientTest extends AgentInstrumentationSpecificat name "RPUSH" kind CLIENT attributes { + "$SemanticAttributes.NET_SOCK_PEER_ADDR" "127.0.0.1" + "$SemanticAttributes.NET_SOCK_PEER_NAME" "localhost" + "$SemanticAttributes.NET_SOCK_PEER_PORT" port "$SemanticAttributes.DB_SYSTEM" "redis" - "$SemanticAttributes.NET_PEER_IP" "127.0.0.1" - "$SemanticAttributes.NET_PEER_NAME" "localhost" - "$SemanticAttributes.NET_PEER_PORT" port "$SemanticAttributes.DB_STATEMENT" "RPUSH list1 ?" "$SemanticAttributes.DB_OPERATION" "RPUSH" } @@ -168,10 +168,10 @@ abstract class AbstractRedissonClientTest extends AgentInstrumentationSpecificat name "EVAL" kind CLIENT attributes { + "$SemanticAttributes.NET_SOCK_PEER_ADDR" "127.0.0.1" + "$SemanticAttributes.NET_SOCK_PEER_NAME" "localhost" + "$SemanticAttributes.NET_SOCK_PEER_PORT" port "$SemanticAttributes.DB_SYSTEM" "redis" - "$SemanticAttributes.NET_PEER_IP" "127.0.0.1" - "$SemanticAttributes.NET_PEER_NAME" "localhost" - "$SemanticAttributes.NET_PEER_PORT" port "$SemanticAttributes.DB_STATEMENT" "EVAL $script 1 map1 ? ?" "$SemanticAttributes.DB_OPERATION" "EVAL" } @@ -182,10 +182,10 @@ abstract class AbstractRedissonClientTest extends AgentInstrumentationSpecificat name "HGET" kind CLIENT attributes { + "$SemanticAttributes.NET_SOCK_PEER_ADDR" "127.0.0.1" + "$SemanticAttributes.NET_SOCK_PEER_NAME" "localhost" + "$SemanticAttributes.NET_SOCK_PEER_PORT" port "$SemanticAttributes.DB_SYSTEM" "redis" - "$SemanticAttributes.NET_PEER_IP" "127.0.0.1" - "$SemanticAttributes.NET_PEER_NAME" "localhost" - "$SemanticAttributes.NET_PEER_PORT" port "$SemanticAttributes.DB_STATEMENT" "HGET map1 key1" "$SemanticAttributes.DB_OPERATION" "HGET" } @@ -206,10 +206,10 @@ abstract class AbstractRedissonClientTest extends AgentInstrumentationSpecificat name "SADD" kind CLIENT attributes { + "$SemanticAttributes.NET_SOCK_PEER_ADDR" "127.0.0.1" + "$SemanticAttributes.NET_SOCK_PEER_NAME" "localhost" + "$SemanticAttributes.NET_SOCK_PEER_PORT" port "$SemanticAttributes.DB_SYSTEM" "redis" - "$SemanticAttributes.NET_PEER_IP" "127.0.0.1" - "$SemanticAttributes.NET_PEER_NAME" "localhost" - "$SemanticAttributes.NET_PEER_PORT" port "$SemanticAttributes.DB_STATEMENT" "SADD set1 ?" "$SemanticAttributes.DB_OPERATION" "SADD" } @@ -234,10 +234,10 @@ abstract class AbstractRedissonClientTest extends AgentInstrumentationSpecificat name "ZADD" kind CLIENT attributes { + "$SemanticAttributes.NET_SOCK_PEER_ADDR" "127.0.0.1" + "$SemanticAttributes.NET_SOCK_PEER_NAME" "localhost" + "$SemanticAttributes.NET_SOCK_PEER_PORT" port "$SemanticAttributes.DB_SYSTEM" "redis" - "$SemanticAttributes.NET_PEER_IP" "127.0.0.1" - "$SemanticAttributes.NET_PEER_NAME" "localhost" - "$SemanticAttributes.NET_PEER_PORT" port "$SemanticAttributes.DB_STATEMENT" "ZADD sort_set1 ? ? ? ? ? ?" "$SemanticAttributes.DB_OPERATION" "ZADD" } @@ -258,10 +258,10 @@ abstract class AbstractRedissonClientTest extends AgentInstrumentationSpecificat name "INCR" kind CLIENT attributes { + "$SemanticAttributes.NET_SOCK_PEER_ADDR" "127.0.0.1" + "$SemanticAttributes.NET_SOCK_PEER_NAME" "localhost" + "$SemanticAttributes.NET_SOCK_PEER_PORT" port "$SemanticAttributes.DB_SYSTEM" "redis" - "$SemanticAttributes.NET_PEER_IP" "127.0.0.1" - "$SemanticAttributes.NET_PEER_NAME" "localhost" - "$SemanticAttributes.NET_PEER_PORT" port "$SemanticAttributes.DB_STATEMENT" "INCR AtomicLong" "$SemanticAttributes.DB_OPERATION" "INCR" } @@ -287,10 +287,10 @@ abstract class AbstractRedissonClientTest extends AgentInstrumentationSpecificat name "EVAL" kind CLIENT attributes { + "$SemanticAttributes.NET_SOCK_PEER_ADDR" "127.0.0.1" + "$SemanticAttributes.NET_SOCK_PEER_NAME" "localhost" + "$SemanticAttributes.NET_SOCK_PEER_PORT" port "$SemanticAttributes.DB_SYSTEM" "redis" - "$SemanticAttributes.NET_PEER_IP" "127.0.0.1" - "$SemanticAttributes.NET_PEER_NAME" "localhost" - "$SemanticAttributes.NET_PEER_PORT" port "$SemanticAttributes.DB_STATEMENT" { lockScriptPattern.matcher(it).matches() } "$SemanticAttributes.DB_OPERATION" "EVAL" } @@ -303,10 +303,10 @@ abstract class AbstractRedissonClientTest extends AgentInstrumentationSpecificat name "EVAL" kind CLIENT attributes { + "$SemanticAttributes.NET_SOCK_PEER_ADDR" "127.0.0.1" + "$SemanticAttributes.NET_SOCK_PEER_NAME" "localhost" + "$SemanticAttributes.NET_SOCK_PEER_PORT" port "$SemanticAttributes.DB_SYSTEM" "redis" - "$SemanticAttributes.NET_PEER_IP" "127.0.0.1" - "$SemanticAttributes.NET_PEER_NAME" "localhost" - "$SemanticAttributes.NET_PEER_PORT" port "$SemanticAttributes.DB_STATEMENT" { lockScriptPattern.matcher(it).matches() } "$SemanticAttributes.DB_OPERATION" "EVAL" } diff --git a/instrumentation/resources/library/README.md b/instrumentation/resources/library/README.md new file mode 100644 index 000000000000..d72b3f09d9b4 --- /dev/null +++ b/instrumentation/resources/library/README.md @@ -0,0 +1,62 @@ +# OpenTelemetry Resource Providers + +This package includes some standard `ResourceProvider`s for filling in attributes related to +common environments. Currently, the resources provide the following semantic conventions: + +## Populated attributes + +### Container + +Provider: `io.opentelemetry.instrumentation.resources.ContainerResource` + +Specification: https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/resource/semantic_conventions/container.md + +Implemented attributes: +- `container.id` + +### Host + +Provider: `io.opentelemetry.instrumentation.resources.HostResource` + +Specification: https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/resource/semantic_conventions/host.md + +Implemented attributes: +- `host.name` +- `host.arch` + +### Operating System + +Provider: `io.opentelemetry.instrumentation.resources.OsResource` + +Specification: https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/resource/semantic_conventions/os.md + +Implemented attributes: +- `os.type` +- `os.description` + +### Process + +Implementation: `io.opentelemetry.instrumentation.resources.ProcessResource` + +Specification: https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/resource/semantic_conventions/process.md#process + +Implemented attributes: +- `process.pid` +- `process.executable.path` (note, we assume the `java` binary is located in the `bin` subfolder of `JAVA_HOME`) +- `process.command_line` (note this includes all system properties and arguments when running) + +### Java Runtime + +Implementation: `io.opentelemetry.instrumentation.resources.ProcessRuntimeResource` + +Specification: https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/resource/semantic_conventions/process.md#process-runtimes + +Implemented attributes: +- `process.runtime.name` +- `process.runtime.version` +- `process.runtime.description` + +## Platforms + +This package currently does not run on Android. It has been verified on OpenJDK and should work on +other server JVM distributions but if you find any issues please let us know. diff --git a/instrumentation/resources/library/build.gradle.kts b/instrumentation/resources/library/build.gradle.kts new file mode 100644 index 000000000000..dc32c3091937 --- /dev/null +++ b/instrumentation/resources/library/build.gradle.kts @@ -0,0 +1,62 @@ +plugins { + id("otel.library-instrumentation") +} + +val mrJarVersions = listOf(9, 11) + +dependencies { + implementation("io.opentelemetry:opentelemetry-sdk-common") + implementation("io.opentelemetry:opentelemetry-semconv") + implementation("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure-spi") + + annotationProcessor("com.google.auto.service:auto-service") + compileOnly("com.google.auto.service:auto-service-annotations") + testCompileOnly("com.google.auto.service:auto-service-annotations") + + testImplementation("org.junit.jupiter:junit-jupiter-api") +} + +for (version in mrJarVersions) { + sourceSets { + create("java$version") { + java { + setSrcDirs(listOf("src/main/java$version")) + } + } + } + + tasks { + named("compileJava${version}Java") { + sourceCompatibility = "$version" + targetCompatibility = "$version" + options.release.set(version) + } + } + + configurations { + named("java${version}Implementation") { + extendsFrom(configurations["implementation"]) + } + named("java${version}CompileOnly") { + extendsFrom(configurations["compileOnly"]) + } + } + + dependencies { + // Common to reference classes in main sourceset from Java 9 one (e.g., to return a common interface) + add("java${version}Implementation", files(sourceSets.main.get().output.classesDirs)) + } +} + +tasks { + withType(Jar::class) { + for (version in mrJarVersions) { + into("META-INF/versions/$version") { + from(sourceSets["java$version"].output) + } + } + manifest.attributes( + "Multi-Release" to "true" + ) + } +} diff --git a/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/ContainerResource.java b/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/ContainerResource.java new file mode 100644 index 000000000000..f225084ae517 --- /dev/null +++ b/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/ContainerResource.java @@ -0,0 +1,110 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.resources; + +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.internal.OtelEncodingUtils; +import io.opentelemetry.sdk.resources.Resource; +import io.opentelemetry.semconv.resource.attributes.ResourceAttributes; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Optional; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.stream.Stream; + +/** Factory for {@link Resource} retrieving Container ID information. */ +public final class ContainerResource { + + private static final Logger logger = Logger.getLogger(ContainerResource.class.getName()); + private static final String UNIQUE_HOST_NAME_FILE_NAME = "/proc/self/cgroup"; + private static final Resource INSTANCE = buildSingleton(UNIQUE_HOST_NAME_FILE_NAME); + + private static Resource buildSingleton(String uniqueHostNameFileName) { + // can't initialize this statically without running afoul of animalSniffer on paths + return buildResource(Paths.get(uniqueHostNameFileName)); + } + + // package private for testing + static Resource buildResource(Path path) { + return extractContainerId(path) + .map( + containerId -> + Resource.create(Attributes.of(ResourceAttributes.CONTAINER_ID, containerId))) + .orElseGet(Resource::empty); + } + + /** Returns resource with container information. */ + public static Resource get() { + return INSTANCE; + } + + /** + * Each line of cgroup file looks like "14:name=systemd:/docker/.../... A hex string is expected + * inside the last section separated by '/' Each segment of the '/' can contain metadata separated + * by either '.' (at beginning) or '-' (at end) + * + * @return containerId + */ + private static Optional extractContainerId(Path cgroupFilePath) { + if (!Files.exists(cgroupFilePath) || !Files.isReadable(cgroupFilePath)) { + return Optional.empty(); + } + try (Stream lines = Files.lines(cgroupFilePath)) { + return lines + .filter(line -> !line.isEmpty()) + .map(ContainerResource::getIdFromLine) + .filter(Optional::isPresent) + .findFirst() + .orElse(Optional.empty()); + } catch (Exception e) { + logger.log(Level.WARNING, "Unable to read file", e); + } + return Optional.empty(); + } + + private static Optional getIdFromLine(String line) { + // This cgroup output line should have the container id in it + int lastSlashIdx = line.lastIndexOf('/'); + if (lastSlashIdx < 0) { + return Optional.empty(); + } + + String containerId; + + String lastSection = line.substring(lastSlashIdx + 1); + int colonIdx = lastSection.lastIndexOf(':'); + + if (colonIdx != -1) { + // since containerd v1.5.0+, containerId is divided by the last colon when the cgroupDriver is + // systemd: + // https://github.com/containerd/containerd/blob/release/1.5/pkg/cri/server/helpers_linux.go#L64 + containerId = lastSection.substring(colonIdx + 1); + } else { + int startIdx = lastSection.lastIndexOf('-'); + int endIdx = lastSection.lastIndexOf('.'); + + startIdx = startIdx == -1 ? 0 : startIdx + 1; + if (endIdx == -1) { + endIdx = lastSection.length(); + } + if (startIdx > endIdx) { + return Optional.empty(); + } + + containerId = lastSection.substring(startIdx, endIdx); + } + + if (OtelEncodingUtils.isValidBase16String(containerId) && !containerId.isEmpty()) { + return Optional.of(containerId); + } else { + return Optional.empty(); + } + } + + private ContainerResource() {} +} diff --git a/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/ContainerResourceProvider.java b/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/ContainerResourceProvider.java new file mode 100644 index 000000000000..b637e0123332 --- /dev/null +++ b/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/ContainerResourceProvider.java @@ -0,0 +1,20 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.resources; + +import com.google.auto.service.AutoService; +import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; +import io.opentelemetry.sdk.autoconfigure.spi.ResourceProvider; +import io.opentelemetry.sdk.resources.Resource; + +/** {@link ResourceProvider} for automatically configuring {@link ResourceProvider}. */ +@AutoService(ResourceProvider.class) +public class ContainerResourceProvider implements ResourceProvider { + @Override + public Resource createResource(ConfigProperties config) { + return ContainerResource.get(); + } +} diff --git a/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/HostResource.java b/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/HostResource.java new file mode 100644 index 000000000000..e322927cd940 --- /dev/null +++ b/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/HostResource.java @@ -0,0 +1,47 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.resources; + +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.common.AttributesBuilder; +import io.opentelemetry.sdk.resources.Resource; +import io.opentelemetry.semconv.resource.attributes.ResourceAttributes; +import java.net.InetAddress; +import java.net.UnknownHostException; + +/** Factory for a {@link Resource} which provides information about the host info. */ +public final class HostResource { + + private static final Resource INSTANCE = buildResource(); + + /** Returns a {@link Resource} which provides information about host. */ + public static Resource get() { + return INSTANCE; + } + + // Visible for testing + static Resource buildResource() { + AttributesBuilder attributes = Attributes.builder(); + try { + attributes.put(ResourceAttributes.HOST_NAME, InetAddress.getLocalHost().getHostName()); + } catch (UnknownHostException e) { + // Ignore + } + String hostArch = null; + try { + hostArch = System.getProperty("os.arch"); + } catch (SecurityException t) { + // Ignore + } + if (hostArch != null) { + attributes.put(ResourceAttributes.HOST_ARCH, hostArch); + } + + return Resource.create(attributes.build(), ResourceAttributes.SCHEMA_URL); + } + + private HostResource() {} +} diff --git a/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/HostResourceProvider.java b/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/HostResourceProvider.java new file mode 100644 index 000000000000..4b4dfb7465b8 --- /dev/null +++ b/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/HostResourceProvider.java @@ -0,0 +1,20 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.resources; + +import com.google.auto.service.AutoService; +import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; +import io.opentelemetry.sdk.autoconfigure.spi.ResourceProvider; +import io.opentelemetry.sdk.resources.Resource; + +/** {@link ResourceProvider} for automatically configuring {@link HostResource}. */ +@AutoService(ResourceProvider.class) +public final class HostResourceProvider implements ResourceProvider { + @Override + public Resource createResource(ConfigProperties config) { + return HostResource.get(); + } +} 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 new file mode 100644 index 000000000000..f3c5ce17bc7a --- /dev/null +++ b/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/JarServiceNameDetector.java @@ -0,0 +1,124 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +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.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.resource.attributes.ResourceAttributes; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Map; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.function.Supplier; +import java.util.logging.Logger; +import javax.annotation.Nullable; + +/** + * A {@link ResourceProvider} that will attempt to detect the application name from the jar name. + */ +@AutoService(ResourceProvider.class) +public final class JarServiceNameDetector implements ConditionalResourceProvider { + + private static final Logger logger = Logger.getLogger(JarServiceNameDetector.class.getName()); + + private final Supplier getProcessHandleArguments; + private final Function getSystemProperty; + private final Predicate fileExists; + + @SuppressWarnings("unused") // SPI + public JarServiceNameDetector() { + this(ProcessArguments::getProcessArguments, System::getProperty, Files::isRegularFile); + } + + // visible for tests + JarServiceNameDetector( + Supplier getProcessHandleArguments, + Function getSystemProperty, + Predicate fileExists) { + this.getProcessHandleArguments = getProcessHandleArguments; + this.getSystemProperty = getSystemProperty; + this.fileExists = fileExists; + } + + @Override + public Resource createResource(ConfigProperties config) { + Path jarPath = getJarPathFromProcessHandle(); + if (jarPath == null) { + jarPath = getJarPathFromSunCommandLine(); + } + if (jarPath == null) { + return Resource.empty(); + } + String serviceName = getServiceName(jarPath); + logger.log(FINE, "Auto-detected service name from the jar file name: {0}", serviceName); + return Resource.create(Attributes.of(ResourceAttributes.SERVICE_NAME, serviceName)); + } + + @Override + public boolean shouldApply(ConfigProperties config, Resource existing) { + String serviceName = config.getString("otel.service.name"); + Map resourceAttributes = config.getMap("otel.resource.attributes"); + return serviceName == null + && !resourceAttributes.containsKey(ResourceAttributes.SERVICE_NAME.getKey()) + && "unknown_service:java".equals(existing.getAttribute(ResourceAttributes.SERVICE_NAME)); + } + + @Nullable + private Path getJarPathFromProcessHandle() { + String[] javaArgs = getProcessHandleArguments.get(); + for (int i = 0; i < javaArgs.length; ++i) { + if ("-jar".equals(javaArgs[i]) && (i < javaArgs.length - 1)) { + return Paths.get(javaArgs[i + 1]); + } + } + return null; + } + + @Nullable + private Path getJarPathFromSunCommandLine() { + // the jar file is the first argument in the command line string + String programArguments = getSystemProperty.apply("sun.java.command"); + if (programArguments == null) { + return null; + } + + // Take the path until the first space. If the path doesn't exist extend it up to the next + // space. Repeat until a path that exists is found or input runs out. + int next = 0; + while (true) { + int nextSpace = programArguments.indexOf(' ', next); + if (nextSpace == -1) { + Path candidate = Paths.get(programArguments); + return fileExists.test(candidate) ? candidate : null; + } + Path candidate = Paths.get(programArguments.substring(0, nextSpace)); + next = nextSpace + 1; + if (fileExists.test(candidate)) { + return candidate; + } + } + } + + 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 + return 1000; + } +} 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 new file mode 100644 index 000000000000..4b223d77c15b --- /dev/null +++ b/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/OsResource.java @@ -0,0 +1,91 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.resources; + +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.common.AttributesBuilder; +import io.opentelemetry.sdk.resources.Resource; +import io.opentelemetry.semconv.resource.attributes.ResourceAttributes; +import javax.annotation.Nullable; + +/** Factory of a {@link Resource} which provides information about the current operating system. */ +public final class OsResource { + + private static final Resource INSTANCE = buildResource(); + + /** + * Returns a factory for a {@link Resource} which provides information about the current operating + * system. + */ + public static Resource get() { + return INSTANCE; + } + + // Visible for testing + static Resource buildResource() { + + String os; + try { + os = System.getProperty("os.name"); + } catch (SecurityException t) { + // Security manager enabled, can't provide much os information. + return Resource.empty(); + } + + if (os == null) { + return Resource.empty(); + } + + AttributesBuilder attributes = Attributes.builder(); + + String osName = getOs(os); + if (osName != null) { + attributes.put(ResourceAttributes.OS_TYPE, osName); + } + + String version = null; + try { + version = System.getProperty("os.version"); + } catch (SecurityException e) { + // Ignore + } + String osDescription = version != null ? os + ' ' + version : os; + attributes.put(ResourceAttributes.OS_DESCRIPTION, osDescription); + + return Resource.create(attributes.build(), ResourceAttributes.SCHEMA_URL); + } + + @Nullable + private static String getOs(String os) { + os = os.toLowerCase(); + if (os.startsWith("windows")) { + return ResourceAttributes.OsTypeValues.WINDOWS; + } else if (os.startsWith("linux")) { + return ResourceAttributes.OsTypeValues.LINUX; + } else if (os.startsWith("mac")) { + return ResourceAttributes.OsTypeValues.DARWIN; + } else if (os.startsWith("freebsd")) { + return ResourceAttributes.OsTypeValues.FREEBSD; + } else if (os.startsWith("netbsd")) { + return ResourceAttributes.OsTypeValues.NETBSD; + } else if (os.startsWith("openbsd")) { + return ResourceAttributes.OsTypeValues.OPENBSD; + } else if (os.startsWith("dragonflybsd")) { + return ResourceAttributes.OsTypeValues.DRAGONFLYBSD; + } else if (os.startsWith("hp-ux")) { + return ResourceAttributes.OsTypeValues.HPUX; + } else if (os.startsWith("aix")) { + return ResourceAttributes.OsTypeValues.AIX; + } else if (os.startsWith("solaris")) { + return ResourceAttributes.OsTypeValues.SOLARIS; + } else if (os.startsWith("z/os")) { + return ResourceAttributes.OsTypeValues.Z_OS; + } + return null; + } + + private OsResource() {} +} diff --git a/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/OsResourceProvider.java b/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/OsResourceProvider.java new file mode 100644 index 000000000000..7dd0b4eb9680 --- /dev/null +++ b/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/OsResourceProvider.java @@ -0,0 +1,20 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.resources; + +import com.google.auto.service.AutoService; +import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; +import io.opentelemetry.sdk.autoconfigure.spi.ResourceProvider; +import io.opentelemetry.sdk.resources.Resource; + +/** {@link ResourceProvider} for automatically configuring {@link OsResource}. */ +@AutoService(ResourceProvider.class) +public final class OsResourceProvider implements ResourceProvider { + @Override + public Resource createResource(ConfigProperties config) { + return OsResource.get(); + } +} 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 new file mode 100644 index 000000000000..c835f876965c --- /dev/null +++ b/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/ProcessArguments.java @@ -0,0 +1,15 @@ +/* + * 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/ProcessPid.java b/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/ProcessPid.java new file mode 100644 index 000000000000..7277a9ead7a1 --- /dev/null +++ b/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/ProcessPid.java @@ -0,0 +1,29 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.resources; + +import java.lang.management.ManagementFactory; + +final class ProcessPid { + + private ProcessPid() {} + + static long getPid() { + // While this is not strictly defined, almost all commonly used JVMs format this as + // pid@hostname. + String runtimeName = ManagementFactory.getRuntimeMXBean().getName(); + int atIndex = runtimeName.indexOf('@'); + if (atIndex >= 0) { + String pidString = runtimeName.substring(0, atIndex); + try { + return Long.parseLong(pidString); + } catch (NumberFormatException ignored) { + // Ignore parse failure. + } + } + return -1; + } +} 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 new file mode 100644 index 000000000000..9ff1848182c0 --- /dev/null +++ b/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/ProcessResource.java @@ -0,0 +1,83 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.resources; + +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.common.AttributesBuilder; +import io.opentelemetry.sdk.resources.Resource; +import io.opentelemetry.semconv.resource.attributes.ResourceAttributes; +import java.io.File; +import java.lang.management.ManagementFactory; +import java.lang.management.RuntimeMXBean; + +/** Factory of a {@link Resource} which provides information about the current running process. */ +public final class ProcessResource { + + private static final Resource INSTANCE = buildResource(); + + /** + * Returns a factory for a {@link Resource} which provides information about the current running + * process. + */ + public static Resource get() { + return INSTANCE; + } + + // Visible for testing + static Resource buildResource() { + try { + return doBuildResource(); + } catch (LinkageError t) { + // Will only happen on Android, where these attributes generally don't make much sense + // anyways. + return Resource.empty(); + } + } + + private static Resource doBuildResource() { + AttributesBuilder attributes = Attributes.builder(); + + RuntimeMXBean runtime = ManagementFactory.getRuntimeMXBean(); + + long pid = ProcessPid.getPid(); + + if (pid >= 0) { + attributes.put(ResourceAttributes.PROCESS_PID, pid); + } + + String javaHome = null; + String osName = null; + try { + javaHome = System.getProperty("java.home"); + osName = System.getProperty("os.name"); + } catch (SecurityException e) { + // Ignore + } + if (javaHome != null) { + StringBuilder executablePath = new StringBuilder(javaHome); + executablePath + .append(File.separatorChar) + .append("bin") + .append(File.separatorChar) + .append("java"); + if (osName != null && osName.toLowerCase().startsWith("windows")) { + executablePath.append(".exe"); + } + + attributes.put(ResourceAttributes.PROCESS_EXECUTABLE_PATH, executablePath.toString()); + + StringBuilder commandLine = new StringBuilder(executablePath); + for (String arg : runtime.getInputArguments()) { + commandLine.append(' ').append(arg); + } + attributes.put(ResourceAttributes.PROCESS_COMMAND_LINE, commandLine.toString()); + } + + return Resource.create(attributes.build(), ResourceAttributes.SCHEMA_URL); + } + + private ProcessResource() {} +} diff --git a/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/ProcessResourceProvider.java b/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/ProcessResourceProvider.java new file mode 100644 index 000000000000..fda5ef4325ba --- /dev/null +++ b/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/ProcessResourceProvider.java @@ -0,0 +1,20 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.resources; + +import com.google.auto.service.AutoService; +import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; +import io.opentelemetry.sdk.autoconfigure.spi.ResourceProvider; +import io.opentelemetry.sdk.resources.Resource; + +/** {@link ResourceProvider} for automatically configuring {@link ProcessResource}. */ +@AutoService(ResourceProvider.class) +public final class ProcessResourceProvider implements ResourceProvider { + @Override + public Resource createResource(ConfigProperties config) { + return ProcessResource.get(); + } +} diff --git a/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/ProcessRuntimeResource.java b/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/ProcessRuntimeResource.java new file mode 100644 index 000000000000..1c1222c7ede7 --- /dev/null +++ b/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/ProcessRuntimeResource.java @@ -0,0 +1,53 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.resources; + +import static io.opentelemetry.semconv.resource.attributes.ResourceAttributes.PROCESS_RUNTIME_DESCRIPTION; +import static io.opentelemetry.semconv.resource.attributes.ResourceAttributes.PROCESS_RUNTIME_NAME; +import static io.opentelemetry.semconv.resource.attributes.ResourceAttributes.PROCESS_RUNTIME_VERSION; + +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.sdk.resources.Resource; +import io.opentelemetry.semconv.resource.attributes.ResourceAttributes; + +/** Factory of a {@link Resource} which provides information about the Java runtime. */ +public final class ProcessRuntimeResource { + + private static final Resource INSTANCE = buildResource(); + + /** Returns a factory for a {@link Resource} which provides information about the Java runtime. */ + public static Resource get() { + return INSTANCE; + } + + // Visible for testing + static Resource buildResource() { + try { + String name = System.getProperty("java.runtime.name"); + String version = System.getProperty("java.runtime.version"); + String description = + System.getProperty("java.vm.vendor") + + " " + + System.getProperty("java.vm.name") + + " " + + System.getProperty("java.vm.version"); + + return Resource.create( + Attributes.of( + PROCESS_RUNTIME_NAME, + name, + PROCESS_RUNTIME_VERSION, + version, + PROCESS_RUNTIME_DESCRIPTION, + description), + ResourceAttributes.SCHEMA_URL); + } catch (SecurityException ignored) { + return Resource.empty(); + } + } + + private ProcessRuntimeResource() {} +} diff --git a/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/ProcessRuntimeResourceProvider.java b/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/ProcessRuntimeResourceProvider.java new file mode 100644 index 000000000000..1e370fe1a469 --- /dev/null +++ b/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/ProcessRuntimeResourceProvider.java @@ -0,0 +1,20 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.resources; + +import com.google.auto.service.AutoService; +import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; +import io.opentelemetry.sdk.autoconfigure.spi.ResourceProvider; +import io.opentelemetry.sdk.resources.Resource; + +/** {@link ResourceProvider} for automatically configuring {@link ProcessRuntimeResource}. */ +@AutoService(ResourceProvider.class) +public final class ProcessRuntimeResourceProvider implements ResourceProvider { + @Override + public Resource createResource(ConfigProperties config) { + return ProcessRuntimeResource.get(); + } +} diff --git a/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/package-info.java b/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/package-info.java new file mode 100644 index 000000000000..0db0bc965cb2 --- /dev/null +++ b/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/package-info.java @@ -0,0 +1,13 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * {@link io.opentelemetry.sdk.autoconfigure.spi.ResourceProvider} implementations for common + * resource information. + */ +@ParametersAreNonnullByDefault +package io.opentelemetry.instrumentation.resources; + +import javax.annotation.ParametersAreNonnullByDefault; diff --git a/instrumentation/resources/library/src/main/java11/io/opentelemetry/instrumentation/resources/ProcessPid.java b/instrumentation/resources/library/src/main/java11/io/opentelemetry/instrumentation/resources/ProcessPid.java new file mode 100644 index 000000000000..9c03842da002 --- /dev/null +++ b/instrumentation/resources/library/src/main/java11/io/opentelemetry/instrumentation/resources/ProcessPid.java @@ -0,0 +1,17 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.resources; + +import java.lang.management.ManagementFactory; + +final class ProcessPid { + + private ProcessPid() {} + + static long getPid() { + return ManagementFactory.getRuntimeMXBean().getPid(); + } +} 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 new file mode 100644 index 000000000000..b32f6c361524 --- /dev/null +++ b/instrumentation/resources/library/src/main/java9/io/opentelemetry/instrumentation/resources/ProcessArguments.java @@ -0,0 +1,15 @@ +/* + * 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/test/java/io/opentelemetry/instrumentation/resources/ContainerResourceTest.java b/instrumentation/resources/library/src/test/java/io/opentelemetry/instrumentation/resources/ContainerResourceTest.java new file mode 100644 index 000000000000..82408b102dc4 --- /dev/null +++ b/instrumentation/resources/library/src/test/java/io/opentelemetry/instrumentation/resources/ContainerResourceTest.java @@ -0,0 +1,95 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.resources; + +import static io.opentelemetry.instrumentation.resources.ContainerResource.buildResource; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.params.provider.Arguments.arguments; + +import io.opentelemetry.sdk.resources.Resource; +import io.opentelemetry.semconv.resource.attributes.ResourceAttributes; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.stream.Stream; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; + +class ContainerResourceTest { + + @ParameterizedTest + @ValueSource( + strings = { + // invalid containerId (non-hex) + "13:name=systemd:/podruntime/docker/kubepods/ac679f8a8319c8cf7d38e1adf263bc08d23zzzz", + // unrecognized format (last "-" is after last ".") + "13:name=systemd:/podruntime/docker/kubepods/ac679f8.a8319c8cf7d38e1adf263bc08-d23zzzz" + }) + void buildResource_returnsEmptyResource_whenContainerIdIsInvalid( + String line, @TempDir Path tempFolder) throws IOException { + Path cgroup = createCgroup(tempFolder.resolve("cgroup"), line); + assertThat(buildResource(cgroup)).isEqualTo(Resource.empty()); + } + + @Test + void buildResource_returnsEmptyResource_whenFileDoesNotExist(@TempDir Path tempFolder) { + Path cgroup = tempFolder.resolve("DoesNotExist"); + assertThat(buildResource(cgroup)).isEqualTo(Resource.empty()); + } + + @ParameterizedTest + @MethodSource("validLines") + void buildResource_extractsContainerIdFromValidLines( + String line, String expectedContainerId, @TempDir Path tempFolder) throws IOException { + Path cgroup = createCgroup(tempFolder.resolve("cgroup"), line); + assertThat(getContainerId(buildResource(cgroup))).isEqualTo(expectedContainerId); + } + + static Stream validLines() { + return Stream.of( + // with suffix + arguments( + "13:name=systemd:/podruntime/docker/kubepods/ac679f8a8319c8cf7d38e1adf263bc08d23.aaaa", + "ac679f8a8319c8cf7d38e1adf263bc08d23"), + // with prefix and suffix + arguments( + "13:name=systemd:/podruntime/docker/kubepods/crio-dc679f8a8319c8cf7d38e1adf263bc08d23.stuff", + "dc679f8a8319c8cf7d38e1adf263bc08d23"), + // just container id + arguments( + "13:name=systemd:/pod/d86d75589bf6cc254f3e2cc29debdf85dde404998aa128997a819ff991827356", + "d86d75589bf6cc254f3e2cc29debdf85dde404998aa128997a819ff991827356"), + // with prefix + arguments( + "//\n" + + "1:name=systemd:/podruntime/docker/kubepods/docker-dc579f8a8319c8cf7d38e1adf263bc08d23" + + "2:name=systemd:/podruntime/docker/kubepods/docker-dc579f8a8319c8cf7d38e1adf263bc08d23" + + "3:name=systemd:/podruntime/docker/kubepods/docker-dc579f8a8319c8cf7d38e1adf263bc08d23", + "dc579f8a8319c8cf7d38e1adf263bc08d23"), + // with two dashes in prefix + arguments( + "11:perf_event:/kubepods.slice/kubepods-burstable.slice/kubepods-burstable-pod4415fd05_2c0f_4533_909b_f2180dca8d7c.slice/cri-containerd-713a77a26fe2a38ebebd5709604a048c3d380db1eb16aa43aca0b2499e54733c.scope", + "713a77a26fe2a38ebebd5709604a048c3d380db1eb16aa43aca0b2499e54733c"), + // with colon, env: k8s v1.24.0, the cgroupDriver by systemd(default), and container is + // cri-containerd v1.6.8 + arguments( + "11:devices:/system.slice/containerd.service/kubepods-pod87a18a64_b74a_454a_b10b_a4a36059d0a3.slice:cri-containerd:05c48c82caff3be3d7f1e896981dd410e81487538936914f32b624d168de9db0", + "05c48c82caff3be3d7f1e896981dd410e81487538936914f32b624d168de9db0")); + } + + private static String getContainerId(Resource resource) { + return resource.getAttribute(ResourceAttributes.CONTAINER_ID); + } + + private static Path createCgroup(Path path, String line) throws IOException { + return Files.write(path, line.getBytes(StandardCharsets.UTF_8)); + } +} diff --git a/instrumentation/resources/library/src/test/java/io/opentelemetry/instrumentation/resources/HostResourceTest.java b/instrumentation/resources/library/src/test/java/io/opentelemetry/instrumentation/resources/HostResourceTest.java new file mode 100644 index 000000000000..d85c10d62708 --- /dev/null +++ b/instrumentation/resources/library/src/test/java/io/opentelemetry/instrumentation/resources/HostResourceTest.java @@ -0,0 +1,46 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.resources; + +import static org.assertj.core.api.Assertions.assertThat; + +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.sdk.resources.Resource; +import io.opentelemetry.semconv.resource.attributes.ResourceAttributes; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; +import org.junit.jupiter.api.condition.EnabledOnJre; +import org.junit.jupiter.api.condition.JRE; +import org.junit.jupiter.api.extension.ExtendWith; + +class HostResourceTest { + @Test + void shouldCreateRuntimeAttributes() { + // when + Resource resource = HostResource.buildResource(); + Attributes attributes = resource.getAttributes(); + + // then + assertThat(resource.getSchemaUrl()).isEqualTo(ResourceAttributes.SCHEMA_URL); + assertThat(attributes.get(ResourceAttributes.HOST_NAME)).isNotBlank(); + assertThat(attributes.get(ResourceAttributes.HOST_ARCH)).isNotBlank(); + } + + @Nested + @TestInstance(TestInstance.Lifecycle.PER_CLASS) + @ExtendWith(SecurityManagerExtension.class) + @EnabledOnJre( + value = {JRE.JAVA_8, JRE.JAVA_11, JRE.JAVA_16}, + disabledReason = "Java 17 deprecates security manager for removal") + static class SecurityManagerEnabled { + @Test + void empty() { + Attributes attributes = HostResource.buildResource().getAttributes(); + assertThat(attributes.asMap()).containsOnlyKeys(ResourceAttributes.HOST_NAME); + } + } +} 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/JarServiceNameDetectorTest.java new file mode 100644 index 000000000000..826f8c2b7b05 --- /dev/null +++ b/instrumentation/resources/library/src/test/java/io/opentelemetry/instrumentation/resources/JarServiceNameDetectorTest.java @@ -0,0 +1,118 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.resources; + +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; +import static org.junit.jupiter.params.provider.Arguments.arguments; + +import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; +import io.opentelemetry.sdk.resources.Resource; +import io.opentelemetry.semconv.resource.attributes.ResourceAttributes; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.stream.Stream; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.ArgumentsProvider; +import org.junit.jupiter.params.provider.ArgumentsSource; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +class JarServiceNameDetectorTest { + + @Mock ConfigProperties config; + + @Test + void createResource_empty() { + JarServiceNameDetector serviceNameProvider = + new JarServiceNameDetector( + () -> new String[0], prop -> null, JarServiceNameDetectorTest::failPath); + + Resource resource = serviceNameProvider.createResource(config); + + assertThat(resource.getAttributes()).isEmpty(); + } + + @Test + void createResource_noJarFileInArgs() { + String[] args = new String[] {"-Dtest=42", "-Xmx666m", "-jar"}; + JarServiceNameDetector serviceNameProvider = + new JarServiceNameDetector(() -> args, prop -> null, JarServiceNameDetectorTest::failPath); + + Resource resource = serviceNameProvider.createResource(config); + + assertThat(resource.getAttributes()).isEmpty(); + } + + @Test + void createResource_processHandleJar() { + String path = Paths.get("path", "to", "app", "my-service.jar").toString(); + String[] args = new String[] {"-Dtest=42", "-Xmx666m", "-jar", path, "abc", "def"}; + JarServiceNameDetector serviceNameProvider = + new JarServiceNameDetector(() -> args, prop -> null, JarServiceNameDetectorTest::failPath); + + Resource resource = serviceNameProvider.createResource(config); + + assertThat(resource.getAttributes()) + .hasSize(1) + .containsEntry(ResourceAttributes.SERVICE_NAME, "my-service"); + } + + @Test + void createResource_processHandleJarWithoutExtension() { + String path = Paths.get("path", "to", "app", "my-service.jar").toString(); + String[] args = new String[] {"-Dtest=42", "-Xmx666m", "-jar", path}; + JarServiceNameDetector serviceNameProvider = + new JarServiceNameDetector(() -> args, prop -> null, JarServiceNameDetectorTest::failPath); + + Resource resource = serviceNameProvider.createResource(config); + + assertThat(resource.getAttributes()) + .hasSize(1) + .containsEntry(ResourceAttributes.SERVICE_NAME, "my-service"); + } + + @ParameterizedTest + @ArgumentsSource(SunCommandLineProvider.class) + void createResource_sunCommandLine(String commandLine, Path jarPath) { + Function getProperty = + key -> "sun.java.command".equals(key) ? commandLine : null; + Predicate fileExists = jarPath::equals; + + JarServiceNameDetector serviceNameProvider = + new JarServiceNameDetector(() -> new String[0], getProperty, fileExists); + + Resource resource = serviceNameProvider.createResource(config); + + assertThat(resource.getAttributes()) + .hasSize(1) + .containsEntry(ResourceAttributes.SERVICE_NAME, "my-service"); + } + + static final class SunCommandLineProvider implements ArgumentsProvider { + + @Override + public Stream provideArguments(ExtensionContext context) { + Path path = Paths.get("path", "to", "my-service.jar"); + Path pathWithSpaces = Paths.get("path to app", "with spaces", "my-service.jar"); + Path pathWithoutExtension = Paths.get("path to app", "with spaces", "my-service"); + return Stream.of( + arguments(path.toString(), path), + arguments(pathWithSpaces + " 1 2 3", pathWithSpaces), + arguments(pathWithoutExtension + " 1 2 3", pathWithoutExtension)); + } + } + + private static boolean failPath(Path file) { + throw new AssertionError("Unexpected call to Files.isRegularFile()"); + } +} 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 new file mode 100644 index 000000000000..988514c47105 --- /dev/null +++ b/instrumentation/resources/library/src/test/java/io/opentelemetry/instrumentation/resources/OsResourceTest.java @@ -0,0 +1,153 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.resources; + +import static org.assertj.core.api.Assertions.assertThat; + +import io.opentelemetry.sdk.resources.Resource; +import io.opentelemetry.semconv.resource.attributes.ResourceAttributes; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; +import org.junit.jupiter.api.condition.EnabledOnJre; +import org.junit.jupiter.api.condition.JRE; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junitpioneer.jupiter.SetSystemProperty; + +class OsResourceTest { + + @Test + @SetSystemProperty(key = "os.name", value = "Linux 4.11") + void linux() { + Resource resource = OsResource.buildResource(); + assertThat(resource.getSchemaUrl()).isEqualTo(ResourceAttributes.SCHEMA_URL); + assertThat(resource.getAttribute(ResourceAttributes.OS_TYPE)) + .isEqualTo(ResourceAttributes.OsTypeValues.LINUX); + assertThat(resource.getAttribute(ResourceAttributes.OS_DESCRIPTION)).isNotEmpty(); + } + + @Test + @SetSystemProperty(key = "os.name", value = "MacOS X 11") + void macos() { + Resource resource = OsResource.buildResource(); + assertThat(resource.getSchemaUrl()).isEqualTo(ResourceAttributes.SCHEMA_URL); + assertThat(resource.getAttribute(ResourceAttributes.OS_TYPE)) + .isEqualTo(ResourceAttributes.OsTypeValues.DARWIN); + assertThat(resource.getAttribute(ResourceAttributes.OS_DESCRIPTION)).isNotEmpty(); + } + + @Test + @SetSystemProperty(key = "os.name", value = "Windows 10") + void windows() { + Resource resource = OsResource.buildResource(); + assertThat(resource.getSchemaUrl()).isEqualTo(ResourceAttributes.SCHEMA_URL); + assertThat(resource.getAttribute(ResourceAttributes.OS_TYPE)) + .isEqualTo(ResourceAttributes.OsTypeValues.WINDOWS); + assertThat(resource.getAttribute(ResourceAttributes.OS_DESCRIPTION)).isNotEmpty(); + } + + @Test + @SetSystemProperty(key = "os.name", value = "FreeBSD 10") + void freebsd() { + Resource resource = OsResource.buildResource(); + assertThat(resource.getSchemaUrl()).isEqualTo(ResourceAttributes.SCHEMA_URL); + assertThat(resource.getAttribute(ResourceAttributes.OS_TYPE)) + .isEqualTo(ResourceAttributes.OsTypeValues.FREEBSD); + assertThat(resource.getAttribute(ResourceAttributes.OS_DESCRIPTION)).isNotEmpty(); + } + + @Test + @SetSystemProperty(key = "os.name", value = "NetBSD 10") + void netbsd() { + Resource resource = OsResource.buildResource(); + assertThat(resource.getSchemaUrl()).isEqualTo(ResourceAttributes.SCHEMA_URL); + assertThat(resource.getAttribute(ResourceAttributes.OS_TYPE)) + .isEqualTo(ResourceAttributes.OsTypeValues.NETBSD); + assertThat(resource.getAttribute(ResourceAttributes.OS_DESCRIPTION)).isNotEmpty(); + } + + @Test + @SetSystemProperty(key = "os.name", value = "OpenBSD 10") + void openbsd() { + Resource resource = OsResource.buildResource(); + assertThat(resource.getSchemaUrl()).isEqualTo(ResourceAttributes.SCHEMA_URL); + assertThat(resource.getAttribute(ResourceAttributes.OS_TYPE)) + .isEqualTo(ResourceAttributes.OsTypeValues.OPENBSD); + assertThat(resource.getAttribute(ResourceAttributes.OS_DESCRIPTION)).isNotEmpty(); + } + + @Test + @SetSystemProperty(key = "os.name", value = "DragonFlyBSD 10") + void dragonflybsd() { + Resource resource = OsResource.buildResource(); + assertThat(resource.getSchemaUrl()).isEqualTo(ResourceAttributes.SCHEMA_URL); + assertThat(resource.getAttribute(ResourceAttributes.OS_TYPE)) + .isEqualTo(ResourceAttributes.OsTypeValues.DRAGONFLYBSD); + assertThat(resource.getAttribute(ResourceAttributes.OS_DESCRIPTION)).isNotEmpty(); + } + + @Test + @SetSystemProperty(key = "os.name", value = "HP-UX 10") + void hpux() { + Resource resource = OsResource.buildResource(); + assertThat(resource.getSchemaUrl()).isEqualTo(ResourceAttributes.SCHEMA_URL); + assertThat(resource.getAttribute(ResourceAttributes.OS_TYPE)) + .isEqualTo(ResourceAttributes.OsTypeValues.HPUX); + assertThat(resource.getAttribute(ResourceAttributes.OS_DESCRIPTION)).isNotEmpty(); + } + + @Test + @SetSystemProperty(key = "os.name", value = "AIX 10") + void aix() { + Resource resource = OsResource.buildResource(); + assertThat(resource.getSchemaUrl()).isEqualTo(ResourceAttributes.SCHEMA_URL); + assertThat(resource.getAttribute(ResourceAttributes.OS_TYPE)) + .isEqualTo(ResourceAttributes.OsTypeValues.AIX); + assertThat(resource.getAttribute(ResourceAttributes.OS_DESCRIPTION)).isNotEmpty(); + } + + @Test + @SetSystemProperty(key = "os.name", value = "Solaris 10") + void solaris() { + Resource resource = OsResource.buildResource(); + assertThat(resource.getSchemaUrl()).isEqualTo(ResourceAttributes.SCHEMA_URL); + assertThat(resource.getAttribute(ResourceAttributes.OS_TYPE)) + .isEqualTo(ResourceAttributes.OsTypeValues.SOLARIS); + assertThat(resource.getAttribute(ResourceAttributes.OS_DESCRIPTION)).isNotEmpty(); + } + + @Test + @SetSystemProperty(key = "os.name", value = "Z/OS 10") + void zos() { + Resource resource = OsResource.buildResource(); + assertThat(resource.getSchemaUrl()).isEqualTo(ResourceAttributes.SCHEMA_URL); + assertThat(resource.getAttribute(ResourceAttributes.OS_TYPE)) + .isEqualTo(ResourceAttributes.OsTypeValues.Z_OS); + assertThat(resource.getAttribute(ResourceAttributes.OS_DESCRIPTION)).isNotEmpty(); + } + + @Test + @SetSystemProperty(key = "os.name", value = "RagOS 10") + void unknown() { + Resource resource = OsResource.buildResource(); + assertThat(resource.getSchemaUrl()).isEqualTo(ResourceAttributes.SCHEMA_URL); + assertThat(resource.getAttribute(ResourceAttributes.OS_TYPE)).isNull(); + assertThat(resource.getAttribute(ResourceAttributes.OS_DESCRIPTION)).isNotEmpty(); + } + + @Nested + @TestInstance(TestInstance.Lifecycle.PER_CLASS) + @ExtendWith(SecurityManagerExtension.class) + @EnabledOnJre( + value = {JRE.JAVA_8, JRE.JAVA_11, JRE.JAVA_16}, + disabledReason = "Java 17 deprecates security manager for removal") + static class SecurityManagerEnabled { + @Test + void empty() { + assertThat(OsResource.buildResource()).isEqualTo(Resource.empty()); + } + } +} diff --git a/instrumentation/resources/library/src/test/java/io/opentelemetry/instrumentation/resources/ProcessResourceTest.java b/instrumentation/resources/library/src/test/java/io/opentelemetry/instrumentation/resources/ProcessResourceTest.java new file mode 100644 index 000000000000..9fde20d97a72 --- /dev/null +++ b/instrumentation/resources/library/src/test/java/io/opentelemetry/instrumentation/resources/ProcessResourceTest.java @@ -0,0 +1,63 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.resources; + +import static org.assertj.core.api.Assertions.assertThat; + +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.sdk.resources.Resource; +import io.opentelemetry.semconv.resource.attributes.ResourceAttributes; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; +import org.junit.jupiter.api.condition.EnabledOnJre; +import org.junit.jupiter.api.condition.JRE; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junitpioneer.jupiter.SetSystemProperty; + +class ProcessResourceTest { + + @Test + @SetSystemProperty(key = "os.name", value = "Linux 4.12") + void notWindows() { + Resource resource = ProcessResource.buildResource(); + assertThat(resource.getSchemaUrl()).isEqualTo(ResourceAttributes.SCHEMA_URL); + Attributes attributes = resource.getAttributes(); + + assertThat(attributes.get(ResourceAttributes.PROCESS_PID)).isGreaterThan(1); + assertThat(attributes.get(ResourceAttributes.PROCESS_EXECUTABLE_PATH)).matches(".*[/\\\\]java"); + assertThat(attributes.get(ResourceAttributes.PROCESS_COMMAND_LINE)) + .contains(attributes.get(ResourceAttributes.PROCESS_EXECUTABLE_PATH)); + } + + @Test + @SetSystemProperty(key = "os.name", value = "Windows 10") + void windows() { + Resource resource = ProcessResource.buildResource(); + assertThat(resource.getSchemaUrl()).isEqualTo(ResourceAttributes.SCHEMA_URL); + Attributes attributes = resource.getAttributes(); + + assertThat(attributes.get(ResourceAttributes.PROCESS_PID)).isGreaterThan(1); + assertThat(attributes.get(ResourceAttributes.PROCESS_EXECUTABLE_PATH)) + .matches(".*[/\\\\]java\\.exe"); + assertThat(attributes.get(ResourceAttributes.PROCESS_COMMAND_LINE)) + .contains(attributes.get(ResourceAttributes.PROCESS_EXECUTABLE_PATH)); + } + + @Nested + @TestInstance(TestInstance.Lifecycle.PER_CLASS) + @ExtendWith(SecurityManagerExtension.class) + @EnabledOnJre( + value = {JRE.JAVA_8, JRE.JAVA_11, JRE.JAVA_16}, + disabledReason = "Java 17 deprecates security manager for removal") + static class SecurityManagerEnabled { + @Test + void empty() { + Attributes attributes = ProcessResource.buildResource().getAttributes(); + assertThat(attributes.asMap()).containsOnlyKeys(ResourceAttributes.PROCESS_PID); + } + } +} diff --git a/instrumentation/resources/library/src/test/java/io/opentelemetry/instrumentation/resources/ProcessRuntimeResourceTest.java b/instrumentation/resources/library/src/test/java/io/opentelemetry/instrumentation/resources/ProcessRuntimeResourceTest.java new file mode 100644 index 000000000000..2893d40a1df7 --- /dev/null +++ b/instrumentation/resources/library/src/test/java/io/opentelemetry/instrumentation/resources/ProcessRuntimeResourceTest.java @@ -0,0 +1,46 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.resources; + +import static org.assertj.core.api.Assertions.assertThat; + +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.sdk.resources.Resource; +import io.opentelemetry.semconv.resource.attributes.ResourceAttributes; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; +import org.junit.jupiter.api.condition.EnabledOnJre; +import org.junit.jupiter.api.condition.JRE; +import org.junit.jupiter.api.extension.ExtendWith; + +class ProcessRuntimeResourceTest { + @Test + void shouldCreateRuntimeAttributes() { + // when + Resource resource = ProcessRuntimeResource.buildResource(); + Attributes attributes = resource.getAttributes(); + + // then + assertThat(resource.getSchemaUrl()).isEqualTo(ResourceAttributes.SCHEMA_URL); + assertThat(attributes.get(ResourceAttributes.PROCESS_RUNTIME_NAME)).isNotBlank(); + assertThat(attributes.get(ResourceAttributes.PROCESS_RUNTIME_VERSION)).isNotBlank(); + assertThat(attributes.get(ResourceAttributes.PROCESS_RUNTIME_DESCRIPTION)).isNotBlank(); + } + + @Nested + @TestInstance(TestInstance.Lifecycle.PER_CLASS) + @ExtendWith(SecurityManagerExtension.class) + @EnabledOnJre( + value = {JRE.JAVA_8, JRE.JAVA_11, JRE.JAVA_16}, + disabledReason = "Java 17 deprecates security manager for removal") + static class SecurityManagerEnabled { + @Test + void empty() { + assertThat(ProcessRuntimeResource.buildResource()).isEqualTo(Resource.empty()); + } + } +} diff --git a/instrumentation/resources/library/src/test/java/io/opentelemetry/instrumentation/resources/SecurityManagerExtension.java b/instrumentation/resources/library/src/test/java/io/opentelemetry/instrumentation/resources/SecurityManagerExtension.java new file mode 100644 index 000000000000..c9c32458b449 --- /dev/null +++ b/instrumentation/resources/library/src/test/java/io/opentelemetry/instrumentation/resources/SecurityManagerExtension.java @@ -0,0 +1,60 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.resources; + +import java.security.Permission; +import java.util.HashSet; +import java.util.PropertyPermission; +import java.util.Set; +import org.junit.jupiter.api.extension.AfterEachCallback; +import org.junit.jupiter.api.extension.BeforeEachCallback; +import org.junit.jupiter.api.extension.ExtensionContext; + +final class SecurityManagerExtension implements BeforeEachCallback, AfterEachCallback { + + private static final ExtensionContext.Namespace NAMESPACE = + ExtensionContext.Namespace.create(SecurityManagerExtension.class); + + @Override + public void beforeEach(ExtensionContext context) { + context.getStore(NAMESPACE).put(SecurityManager.class, System.getSecurityManager()); + System.setSecurityManager(BlockPropertiesAccess.INSTANCE); + } + + @Override + public void afterEach(ExtensionContext context) { + System.setSecurityManager( + (SecurityManager) context.getStore(NAMESPACE).get(SecurityManager.class)); + } + + private static class BlockPropertiesAccess extends SecurityManager { + + private static final BlockPropertiesAccess INSTANCE = new BlockPropertiesAccess(); + + private static final Set BLOCKED_PROPERTIES = new HashSet<>(); + + static { + BLOCKED_PROPERTIES.add("java.home"); + BLOCKED_PROPERTIES.add("java.runtime.home"); + BLOCKED_PROPERTIES.add("java.runtime.version"); + BLOCKED_PROPERTIES.add("java.vm.name"); + BLOCKED_PROPERTIES.add("java.vm.vendor"); + BLOCKED_PROPERTIES.add("java.vm.version"); + BLOCKED_PROPERTIES.add("os.arch"); + BLOCKED_PROPERTIES.add("os.name"); + BLOCKED_PROPERTIES.add("os.version"); + } + + @Override + public void checkPermission(Permission perm) { + if (perm instanceof PropertyPermission) { + if (BLOCKED_PROPERTIES.contains(perm.getName())) { + throw new SecurityException("Property access not allowed."); + } + } + } + } +} diff --git a/instrumentation/restlet/restlet-1.0/library/src/main/java/io/opentelemetry/instrumentation/restlet/v1_0/RestletNetAttributesGetter.java b/instrumentation/restlet/restlet-1.0/library/src/main/java/io/opentelemetry/instrumentation/restlet/v1_0/RestletNetAttributesGetter.java deleted file mode 100644 index e3ff56cdb7a3..000000000000 --- a/instrumentation/restlet/restlet-1.0/library/src/main/java/io/opentelemetry/instrumentation/restlet/v1_0/RestletNetAttributesGetter.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.instrumentation.restlet.v1_0; - -import io.opentelemetry.instrumentation.api.instrumenter.net.NetServerAttributesGetter; -import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; -import javax.annotation.Nullable; -import org.restlet.data.Request; - -final class RestletNetAttributesGetter implements NetServerAttributesGetter { - @Override - public String transport(Request request) { - return SemanticAttributes.NetTransportValues.IP_TCP; - } - - @Override - public Integer peerPort(Request request) { - return request.getClientInfo().getPort(); - } - - @Override - @Nullable - public String peerIp(Request request) { - return request.getClientInfo().getAddress(); - } -} diff --git a/instrumentation/restlet/restlet-1.0/javaagent/build.gradle.kts b/instrumentation/restlet/restlet-1.1/javaagent/build.gradle.kts similarity index 71% rename from instrumentation/restlet/restlet-1.0/javaagent/build.gradle.kts rename to instrumentation/restlet/restlet-1.1/javaagent/build.gradle.kts index 8c303bd13db0..47437565ccaa 100644 --- a/instrumentation/restlet/restlet-1.0/javaagent/build.gradle.kts +++ b/instrumentation/restlet/restlet-1.1/javaagent/build.gradle.kts @@ -6,7 +6,8 @@ muzzle { pass { group.set("org.restlet") module.set("org.restlet") - versions.set("[1.0.0, 1.2-M1)") + versions.set("[1.1.0, 1.2-M1)") + extraDependency("com.noelios.restlet:com.noelios.restlet") assertInverse.set(true) } } @@ -18,18 +19,15 @@ repositories { } dependencies { - api(project(":instrumentation:restlet:restlet-1.0:library")) bootstrap(project(":instrumentation:servlet:servlet-common:bootstrap")) + implementation(project(":instrumentation:restlet:restlet-1.1:library")) + library("org.restlet:org.restlet:1.1.5") library("com.noelios.restlet:com.noelios.restlet:1.1.5") - implementation(project(":instrumentation:restlet:restlet-1.0:library")) - testImplementation(project(":instrumentation:restlet:restlet-1.0:testing")) + testImplementation(project(":instrumentation:restlet:restlet-1.1:testing")) testInstrumentation(project(":instrumentation:jetty:jetty-8.0:javaagent")) testInstrumentation(project(":instrumentation:servlet:servlet-3.0:javaagent")) testInstrumentation(project(":instrumentation:servlet:servlet-javax-common:javaagent")) - - latestDepTestLibrary("org.restlet:org.restlet:1.1.+") - latestDepTestLibrary("com.noelios.restlet:com.noelios.restlet:1.1.+") } diff --git a/instrumentation/restlet/restlet-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/restlet/v1_0/RestletInstrumentationModule.java b/instrumentation/restlet/restlet-1.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/restlet/v1_1/RestletInstrumentationModule.java similarity index 86% rename from instrumentation/restlet/restlet-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/restlet/v1_0/RestletInstrumentationModule.java rename to instrumentation/restlet/restlet-1.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/restlet/v1_1/RestletInstrumentationModule.java index af81c0990294..a346d76d93d1 100644 --- a/instrumentation/restlet/restlet-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/restlet/v1_0/RestletInstrumentationModule.java +++ b/instrumentation/restlet/restlet-1.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/restlet/v1_1/RestletInstrumentationModule.java @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.javaagent.instrumentation.restlet.v1_0; +package io.opentelemetry.javaagent.instrumentation.restlet.v1_1; import com.google.auto.service.AutoService; import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule; @@ -15,7 +15,7 @@ public class RestletInstrumentationModule extends InstrumentationModule { public RestletInstrumentationModule() { - super("restlet", "restlet-1.0"); + super("restlet", "restlet-1.1"); } @Override diff --git a/instrumentation/restlet/restlet-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/restlet/v1_0/RestletSingletons.java b/instrumentation/restlet/restlet-1.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/restlet/v1_1/RestletSingletons.java similarity index 62% rename from instrumentation/restlet/restlet-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/restlet/v1_0/RestletSingletons.java rename to instrumentation/restlet/restlet-1.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/restlet/v1_1/RestletSingletons.java index e254e2dfa2d2..76ae94863e9d 100644 --- a/instrumentation/restlet/restlet-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/restlet/v1_0/RestletSingletons.java +++ b/instrumentation/restlet/restlet-1.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/restlet/v1_1/RestletSingletons.java @@ -3,12 +3,13 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.javaagent.instrumentation.restlet.v1_0; +package io.opentelemetry.javaagent.instrumentation.restlet.v1_1; import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpRouteGetter; -import io.opentelemetry.instrumentation.restlet.v1_0.RestletTelemetry; +import io.opentelemetry.instrumentation.restlet.v1_1.RestletTelemetry; +import io.opentelemetry.javaagent.bootstrap.internal.CommonConfig; import io.opentelemetry.javaagent.bootstrap.servlet.ServletContextPath; import org.restlet.data.Request; import org.restlet.data.Response; @@ -16,7 +17,11 @@ public final class RestletSingletons { private static final Instrumenter INSTRUMENTER = - RestletTelemetry.create(GlobalOpenTelemetry.get()).getServerInstrumenter(); + RestletTelemetry.builder(GlobalOpenTelemetry.get()) + .setCapturedRequestHeaders(CommonConfig.get().getServerRequestHeaders()) + .setCapturedResponseHeaders(CommonConfig.get().getServerResponseHeaders()) + .build() + .getServerInstrumenter(); public static Instrumenter instrumenter() { return INSTRUMENTER; diff --git a/instrumentation/restlet/restlet-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/restlet/v1_0/RouteInstrumentation.java b/instrumentation/restlet/restlet-1.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/restlet/v1_1/RouteInstrumentation.java similarity index 94% rename from instrumentation/restlet/restlet-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/restlet/v1_0/RouteInstrumentation.java rename to instrumentation/restlet/restlet-1.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/restlet/v1_1/RouteInstrumentation.java index 42373d9e9d99..ebd1c7f55fe1 100644 --- a/instrumentation/restlet/restlet-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/restlet/v1_0/RouteInstrumentation.java +++ b/instrumentation/restlet/restlet-1.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/restlet/v1_1/RouteInstrumentation.java @@ -3,11 +3,11 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.javaagent.instrumentation.restlet.v1_0; +package io.opentelemetry.javaagent.instrumentation.restlet.v1_1; import static io.opentelemetry.instrumentation.api.instrumenter.http.HttpRouteSource.CONTROLLER; import static io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge.currentContext; -import static io.opentelemetry.javaagent.instrumentation.restlet.v1_0.RestletSingletons.serverSpanName; +import static io.opentelemetry.javaagent.instrumentation.restlet.v1_1.RestletSingletons.serverSpanName; import static net.bytebuddy.matcher.ElementMatchers.isMethod; import static net.bytebuddy.matcher.ElementMatchers.named; import static net.bytebuddy.matcher.ElementMatchers.takesArgument; diff --git a/instrumentation/restlet/restlet-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/restlet/v1_0/ServerInstrumentation.java b/instrumentation/restlet/restlet-1.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/restlet/v1_1/ServerInstrumentation.java similarity index 95% rename from instrumentation/restlet/restlet-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/restlet/v1_0/ServerInstrumentation.java rename to instrumentation/restlet/restlet-1.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/restlet/v1_1/ServerInstrumentation.java index 14d710eef59b..a9f8d78a9764 100644 --- a/instrumentation/restlet/restlet-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/restlet/v1_0/ServerInstrumentation.java +++ b/instrumentation/restlet/restlet-1.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/restlet/v1_1/ServerInstrumentation.java @@ -3,12 +3,12 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.javaagent.instrumentation.restlet.v1_0; +package io.opentelemetry.javaagent.instrumentation.restlet.v1_1; import static io.opentelemetry.instrumentation.api.instrumenter.http.HttpRouteSource.CONTROLLER; import static io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge.currentContext; -import static io.opentelemetry.javaagent.instrumentation.restlet.v1_0.RestletSingletons.instrumenter; -import static io.opentelemetry.javaagent.instrumentation.restlet.v1_0.RestletSingletons.serverSpanName; +import static io.opentelemetry.javaagent.instrumentation.restlet.v1_1.RestletSingletons.instrumenter; +import static io.opentelemetry.javaagent.instrumentation.restlet.v1_1.RestletSingletons.serverSpanName; import static net.bytebuddy.matcher.ElementMatchers.isMethod; import static net.bytebuddy.matcher.ElementMatchers.named; import static net.bytebuddy.matcher.ElementMatchers.takesArgument; diff --git a/instrumentation/restlet/restlet-1.0/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/restlet/v1_0/RestletServerTest.groovy b/instrumentation/restlet/restlet-1.1/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/restlet/v1_1/RestletServerTest.groovy similarity index 66% rename from instrumentation/restlet/restlet-1.0/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/restlet/v1_0/RestletServerTest.groovy rename to instrumentation/restlet/restlet-1.1/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/restlet/v1_1/RestletServerTest.groovy index 580196a0ba92..40eb940ce048 100644 --- a/instrumentation/restlet/restlet-1.0/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/restlet/v1_0/RestletServerTest.groovy +++ b/instrumentation/restlet/restlet-1.1/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/restlet/v1_1/RestletServerTest.groovy @@ -3,9 +3,9 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.javaagent.instrumentation.restlet.v1_0 +package io.opentelemetry.javaagent.instrumentation.restlet.v1_1 -import io.opentelemetry.instrumentation.restlet.v1_0.AbstractRestletServerTest +import io.opentelemetry.instrumentation.restlet.v1_1.AbstractRestletServerTest import io.opentelemetry.instrumentation.test.AgentTestTrait class RestletServerTest extends AbstractRestletServerTest implements AgentTestTrait { diff --git a/instrumentation/restlet/restlet-1.0/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/restlet/v1_0/ServletServerTest.groovy b/instrumentation/restlet/restlet-1.1/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/restlet/v1_1/ServletServerTest.groovy similarity index 66% rename from instrumentation/restlet/restlet-1.0/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/restlet/v1_0/ServletServerTest.groovy rename to instrumentation/restlet/restlet-1.1/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/restlet/v1_1/ServletServerTest.groovy index d00f94b8bd57..0a5adfce2c96 100644 --- a/instrumentation/restlet/restlet-1.0/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/restlet/v1_0/ServletServerTest.groovy +++ b/instrumentation/restlet/restlet-1.1/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/restlet/v1_1/ServletServerTest.groovy @@ -3,10 +3,10 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.javaagent.instrumentation.restlet.v1_0 +package io.opentelemetry.javaagent.instrumentation.restlet.v1_1 -import io.opentelemetry.instrumentation.restlet.v1_0.AbstractServletServerTest +import io.opentelemetry.instrumentation.restlet.v1_1.AbstractServletServerTest import io.opentelemetry.instrumentation.test.AgentTestTrait class ServletServerTest extends AbstractServletServerTest implements AgentTestTrait { diff --git a/instrumentation/restlet/restlet-1.0/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/restlet/v1_0/spring/SpringBeanRouterTest.groovy b/instrumentation/restlet/restlet-1.1/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/restlet/v1_1/spring/SpringBeanRouterTest.groovy similarity index 73% rename from instrumentation/restlet/restlet-1.0/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/restlet/v1_0/spring/SpringBeanRouterTest.groovy rename to instrumentation/restlet/restlet-1.1/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/restlet/v1_1/spring/SpringBeanRouterTest.groovy index ccc3bb8aee7f..fd191be328b2 100644 --- a/instrumentation/restlet/restlet-1.0/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/restlet/v1_0/spring/SpringBeanRouterTest.groovy +++ b/instrumentation/restlet/restlet-1.1/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/restlet/v1_1/spring/SpringBeanRouterTest.groovy @@ -3,9 +3,9 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.javaagent.instrumentation.restlet.v1_0.spring +package io.opentelemetry.javaagent.instrumentation.restlet.v1_1.spring -import io.opentelemetry.instrumentation.restlet.v1_0.spring.AbstractSpringServerTest +import io.opentelemetry.instrumentation.restlet.v1_1.spring.AbstractSpringServerTest import io.opentelemetry.instrumentation.test.AgentTestTrait class SpringBeanRouterTest extends AbstractSpringServerTest implements AgentTestTrait { diff --git a/instrumentation/restlet/restlet-1.0/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/restlet/v1_0/spring/SpringRouterTest.groovy b/instrumentation/restlet/restlet-1.1/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/restlet/v1_1/spring/SpringRouterTest.groovy similarity index 73% rename from instrumentation/restlet/restlet-1.0/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/restlet/v1_0/spring/SpringRouterTest.groovy rename to instrumentation/restlet/restlet-1.1/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/restlet/v1_1/spring/SpringRouterTest.groovy index bb824d14c00e..69e79b35d56a 100644 --- a/instrumentation/restlet/restlet-1.0/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/restlet/v1_0/spring/SpringRouterTest.groovy +++ b/instrumentation/restlet/restlet-1.1/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/restlet/v1_1/spring/SpringRouterTest.groovy @@ -3,9 +3,9 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.javaagent.instrumentation.restlet.v1_0.spring +package io.opentelemetry.javaagent.instrumentation.restlet.v1_1.spring -import io.opentelemetry.instrumentation.restlet.v1_0.spring.AbstractSpringServerTest +import io.opentelemetry.instrumentation.restlet.v1_1.spring.AbstractSpringServerTest import io.opentelemetry.instrumentation.test.AgentTestTrait class SpringRouterTest extends AbstractSpringServerTest implements AgentTestTrait { diff --git a/instrumentation/restlet/restlet-1.0/library/build.gradle.kts b/instrumentation/restlet/restlet-1.1/library/build.gradle.kts similarity index 69% rename from instrumentation/restlet/restlet-1.0/library/build.gradle.kts rename to instrumentation/restlet/restlet-1.1/library/build.gradle.kts index 9a223681b67b..7a6ba61f4994 100644 --- a/instrumentation/restlet/restlet-1.0/library/build.gradle.kts +++ b/instrumentation/restlet/restlet-1.1/library/build.gradle.kts @@ -13,8 +13,5 @@ dependencies { library("org.restlet:org.restlet:1.1.5") library("com.noelios.restlet:com.noelios.restlet:1.1.5") - testImplementation(project(":instrumentation:restlet:restlet-1.0:testing")) - - latestDepTestLibrary("org.restlet:org.restlet:1.1.+") - latestDepTestLibrary("com.noelios.restlet:com.noelios.restlet:1.1.+") + testImplementation(project(":instrumentation:restlet:restlet-1.1:testing")) } diff --git a/instrumentation/restlet/restlet-1.0/library/src/main/java/io/opentelemetry/instrumentation/restlet/v1_0/RestletHeadersGetter.java b/instrumentation/restlet/restlet-1.1/library/src/main/java/io/opentelemetry/instrumentation/restlet/v1_1/RestletHeadersGetter.java similarity index 93% rename from instrumentation/restlet/restlet-1.0/library/src/main/java/io/opentelemetry/instrumentation/restlet/v1_0/RestletHeadersGetter.java rename to instrumentation/restlet/restlet-1.1/library/src/main/java/io/opentelemetry/instrumentation/restlet/v1_1/RestletHeadersGetter.java index 511ccd02ef5b..13d06f8ce1b9 100644 --- a/instrumentation/restlet/restlet-1.0/library/src/main/java/io/opentelemetry/instrumentation/restlet/v1_0/RestletHeadersGetter.java +++ b/instrumentation/restlet/restlet-1.1/library/src/main/java/io/opentelemetry/instrumentation/restlet/v1_1/RestletHeadersGetter.java @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.instrumentation.restlet.v1_0; +package io.opentelemetry.instrumentation.restlet.v1_1; import io.opentelemetry.context.propagation.TextMapGetter; import java.util.Locale; diff --git a/instrumentation/restlet/restlet-1.0/library/src/main/java/io/opentelemetry/instrumentation/restlet/v1_0/RestletHttpAttributesGetter.java b/instrumentation/restlet/restlet-1.1/library/src/main/java/io/opentelemetry/instrumentation/restlet/v1_1/RestletHttpAttributesGetter.java similarity index 77% rename from instrumentation/restlet/restlet-1.0/library/src/main/java/io/opentelemetry/instrumentation/restlet/v1_0/RestletHttpAttributesGetter.java rename to instrumentation/restlet/restlet-1.1/library/src/main/java/io/opentelemetry/instrumentation/restlet/v1_1/RestletHttpAttributesGetter.java index 0cd5a6aef514..6116061a7644 100644 --- a/instrumentation/restlet/restlet-1.0/library/src/main/java/io/opentelemetry/instrumentation/restlet/v1_0/RestletHttpAttributesGetter.java +++ b/instrumentation/restlet/restlet-1.1/library/src/main/java/io/opentelemetry/instrumentation/restlet/v1_1/RestletHttpAttributesGetter.java @@ -3,9 +3,9 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.instrumentation.restlet.v1_0; +package io.opentelemetry.instrumentation.restlet.v1_1; -import static io.opentelemetry.instrumentation.restlet.v1_0.RestletHeadersGetter.getHeaders; +import static io.opentelemetry.instrumentation.restlet.v1_1.RestletHeadersGetter.getHeaders; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpServerAttributesGetter; import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; @@ -57,18 +57,6 @@ public List requestHeader(Request request, String name) { return parametersToList(headers.subList(name, /* ignoreCase = */ true)); } - @Override - @Nullable - public Long requestContentLength(Request request, @Nullable Response response) { - return null; - } - - @Override - @Nullable - public Long requestContentLengthUncompressed(Request request, @Nullable Response response) { - return null; - } - @Override @Nullable public String flavor(Request request) { @@ -87,28 +75,10 @@ public String flavor(Request request) { } @Override - @Nullable - public String serverName(Request request) { - return null; - } - - @Override - public Integer statusCode(Request request, Response response) { + public Integer statusCode(Request request, Response response, @Nullable Throwable error) { return response.getStatus().getCode(); } - @Override - @Nullable - public Long responseContentLength(Request request, Response response) { - return null; - } - - @Override - @Nullable - public Long responseContentLengthUncompressed(Request request, Response response) { - return null; - } - @Override public List responseHeader(Request request, Response response, String name) { Form headers = getHeaders(response); diff --git a/instrumentation/restlet/restlet-1.1/library/src/main/java/io/opentelemetry/instrumentation/restlet/v1_1/RestletNetAttributesGetter.java b/instrumentation/restlet/restlet-1.1/library/src/main/java/io/opentelemetry/instrumentation/restlet/v1_1/RestletNetAttributesGetter.java new file mode 100644 index 000000000000..45d773fdb9fc --- /dev/null +++ b/instrumentation/restlet/restlet-1.1/library/src/main/java/io/opentelemetry/instrumentation/restlet/v1_1/RestletNetAttributesGetter.java @@ -0,0 +1,61 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.restlet.v1_1; + +import com.noelios.restlet.http.HttpCall; +import com.noelios.restlet.http.HttpRequest; +import io.opentelemetry.instrumentation.api.instrumenter.net.NetServerAttributesGetter; +import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; +import javax.annotation.Nullable; +import org.restlet.data.Request; + +final class RestletNetAttributesGetter implements NetServerAttributesGetter { + + @Override + public String transport(Request request) { + return SemanticAttributes.NetTransportValues.IP_TCP; + } + + @Nullable + @Override + public String hostName(Request request) { + HttpCall call = httpCall(request); + return call == null ? null : call.getHostDomain(); + } + + @Nullable + @Override + public Integer hostPort(Request request) { + HttpCall call = httpCall(request); + return call == null ? null : call.getServerPort(); + } + + @Override + @Nullable + public String sockPeerAddr(Request request) { + return request.getClientInfo().getAddress(); + } + + @Override + public Integer sockPeerPort(Request request) { + return request.getClientInfo().getPort(); + } + + @Nullable + @Override + public String sockHostAddr(Request request) { + HttpCall call = httpCall(request); + return call == null ? null : call.getServerAddress(); + } + + @Nullable + private static HttpCall httpCall(Request request) { + if (request instanceof HttpRequest) { + return ((HttpRequest) request).getHttpCall(); + } + return null; + } +} diff --git a/instrumentation/restlet/restlet-1.0/library/src/main/java/io/opentelemetry/instrumentation/restlet/v1_0/RestletTelemetry.java b/instrumentation/restlet/restlet-1.1/library/src/main/java/io/opentelemetry/instrumentation/restlet/v1_1/RestletTelemetry.java similarity index 96% rename from instrumentation/restlet/restlet-1.0/library/src/main/java/io/opentelemetry/instrumentation/restlet/v1_0/RestletTelemetry.java rename to instrumentation/restlet/restlet-1.1/library/src/main/java/io/opentelemetry/instrumentation/restlet/v1_1/RestletTelemetry.java index 7dbafd1eb1bb..ba29b9b6d64d 100644 --- a/instrumentation/restlet/restlet-1.0/library/src/main/java/io/opentelemetry/instrumentation/restlet/v1_0/RestletTelemetry.java +++ b/instrumentation/restlet/restlet-1.1/library/src/main/java/io/opentelemetry/instrumentation/restlet/v1_1/RestletTelemetry.java @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.instrumentation.restlet.v1_0; +package io.opentelemetry.instrumentation.restlet.v1_1; import io.opentelemetry.api.OpenTelemetry; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; diff --git a/instrumentation/restlet/restlet-1.0/library/src/main/java/io/opentelemetry/instrumentation/restlet/v1_0/RestletTelemetryBuilder.java b/instrumentation/restlet/restlet-1.1/library/src/main/java/io/opentelemetry/instrumentation/restlet/v1_1/RestletTelemetryBuilder.java similarity index 86% rename from instrumentation/restlet/restlet-1.0/library/src/main/java/io/opentelemetry/instrumentation/restlet/v1_0/RestletTelemetryBuilder.java rename to instrumentation/restlet/restlet-1.1/library/src/main/java/io/opentelemetry/instrumentation/restlet/v1_1/RestletTelemetryBuilder.java index 856db04f9768..3305851a7a56 100644 --- a/instrumentation/restlet/restlet-1.0/library/src/main/java/io/opentelemetry/instrumentation/restlet/v1_0/RestletTelemetryBuilder.java +++ b/instrumentation/restlet/restlet-1.1/library/src/main/java/io/opentelemetry/instrumentation/restlet/v1_1/RestletTelemetryBuilder.java @@ -3,8 +3,9 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.instrumentation.restlet.v1_0; +package io.opentelemetry.instrumentation.restlet.v1_1; +import com.google.errorprone.annotations.CanIgnoreReturnValue; import io.opentelemetry.api.OpenTelemetry; import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; @@ -13,7 +14,6 @@ import io.opentelemetry.instrumentation.api.instrumenter.http.HttpServerMetrics; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanNameExtractor; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanStatusExtractor; -import io.opentelemetry.instrumentation.api.instrumenter.net.NetServerAttributesExtractor; import java.util.ArrayList; import java.util.List; import org.restlet.data.Request; @@ -22,14 +22,15 @@ /** A builder of {@link RestletTelemetry}. */ public final class RestletTelemetryBuilder { - private static final String INSTRUMENTATION_NAME = "io.opentelemetry.restlet-1.0"; + private static final String INSTRUMENTATION_NAME = "io.opentelemetry.restlet-1.1"; private final OpenTelemetry openTelemetry; private final List> additionalExtractors = new ArrayList<>(); private final HttpServerAttributesExtractorBuilder httpAttributesExtractorBuilder = - HttpServerAttributesExtractor.builder(RestletHttpAttributesGetter.INSTANCE); + HttpServerAttributesExtractor.builder( + RestletHttpAttributesGetter.INSTANCE, new RestletNetAttributesGetter()); RestletTelemetryBuilder(OpenTelemetry openTelemetry) { this.openTelemetry = openTelemetry; @@ -39,6 +40,7 @@ public final class RestletTelemetryBuilder { * Adds an additional {@link AttributesExtractor} to invoke to set attributes to instrumented * items. */ + @CanIgnoreReturnValue public RestletTelemetryBuilder addAttributesExtractor( AttributesExtractor attributesExtractor) { additionalExtractors.add(attributesExtractor); @@ -50,6 +52,7 @@ public RestletTelemetryBuilder addAttributesExtractor( * * @param requestHeaders A list of HTTP header names. */ + @CanIgnoreReturnValue public RestletTelemetryBuilder setCapturedRequestHeaders(List requestHeaders) { httpAttributesExtractorBuilder.setCapturedRequestHeaders(requestHeaders); return this; @@ -60,6 +63,7 @@ public RestletTelemetryBuilder setCapturedRequestHeaders(List requestHea * * @param responseHeaders A list of HTTP header names. */ + @CanIgnoreReturnValue public RestletTelemetryBuilder setCapturedResponseHeaders(List responseHeaders) { httpAttributesExtractorBuilder.setCapturedResponseHeaders(responseHeaders); return this; @@ -71,7 +75,6 @@ public RestletTelemetryBuilder setCapturedResponseHeaders(List responseH */ public RestletTelemetry build() { RestletHttpAttributesGetter httpAttributesGetter = RestletHttpAttributesGetter.INSTANCE; - RestletNetAttributesGetter netAttributesGetter = new RestletNetAttributesGetter(); Instrumenter instrumenter = Instrumenter.builder( @@ -80,10 +83,9 @@ public RestletTelemetry build() { HttpSpanNameExtractor.create(httpAttributesGetter)) .setSpanStatusExtractor(HttpSpanStatusExtractor.create(httpAttributesGetter)) .addAttributesExtractor(httpAttributesExtractorBuilder.build()) - .addAttributesExtractor(NetServerAttributesExtractor.create(netAttributesGetter)) .addAttributesExtractors(additionalExtractors) .addOperationMetrics(HttpServerMetrics.get()) - .newServerInstrumenter(RestletHeadersGetter.INSTANCE); + .buildServerInstrumenter(RestletHeadersGetter.INSTANCE); return new RestletTelemetry(instrumenter); } diff --git a/instrumentation/restlet/restlet-1.0/library/src/main/java/io/opentelemetry/instrumentation/restlet/v1_0/TracingFilter.java b/instrumentation/restlet/restlet-1.1/library/src/main/java/io/opentelemetry/instrumentation/restlet/v1_1/TracingFilter.java similarity index 96% rename from instrumentation/restlet/restlet-1.0/library/src/main/java/io/opentelemetry/instrumentation/restlet/v1_0/TracingFilter.java rename to instrumentation/restlet/restlet-1.1/library/src/main/java/io/opentelemetry/instrumentation/restlet/v1_1/TracingFilter.java index 585410e46c6a..1b13e5b206aa 100644 --- a/instrumentation/restlet/restlet-1.0/library/src/main/java/io/opentelemetry/instrumentation/restlet/v1_0/TracingFilter.java +++ b/instrumentation/restlet/restlet-1.1/library/src/main/java/io/opentelemetry/instrumentation/restlet/v1_1/TracingFilter.java @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.instrumentation.restlet.v1_0; +package io.opentelemetry.instrumentation.restlet.v1_1; import static io.opentelemetry.instrumentation.api.instrumenter.http.HttpRouteSource.CONTROLLER; diff --git a/instrumentation/restlet/restlet-1.0/library/src/test/groovy/io/opententelemetry/instrumentation/restlet/v1_0/RestletServerTest.groovy b/instrumentation/restlet/restlet-1.1/library/src/test/groovy/io/opententelemetry/instrumentation/restlet/v1_1/RestletServerTest.groovy similarity index 83% rename from instrumentation/restlet/restlet-1.0/library/src/test/groovy/io/opententelemetry/instrumentation/restlet/v1_0/RestletServerTest.groovy rename to instrumentation/restlet/restlet-1.1/library/src/test/groovy/io/opententelemetry/instrumentation/restlet/v1_1/RestletServerTest.groovy index 2e919ebed2f4..7a2eb0b27a1a 100644 --- a/instrumentation/restlet/restlet-1.0/library/src/test/groovy/io/opententelemetry/instrumentation/restlet/v1_0/RestletServerTest.groovy +++ b/instrumentation/restlet/restlet-1.1/library/src/test/groovy/io/opententelemetry/instrumentation/restlet/v1_1/RestletServerTest.groovy @@ -3,11 +3,11 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opententelemetry.instrumentation.restlet.v1_0 +package io.opententelemetry.instrumentation.restlet.v1_1 import com.noelios.restlet.StatusFilter -import io.opentelemetry.instrumentation.restlet.v1_0.AbstractRestletServerTest -import io.opentelemetry.instrumentation.restlet.v1_0.RestletTelemetry +import io.opentelemetry.instrumentation.restlet.v1_1.AbstractRestletServerTest +import io.opentelemetry.instrumentation.restlet.v1_1.RestletTelemetry import io.opentelemetry.instrumentation.test.LibraryTestTrait import io.opentelemetry.instrumentation.testing.junit.http.AbstractHttpServerTest import org.restlet.Restlet diff --git a/instrumentation/restlet/restlet-1.0/library/src/test/groovy/io/opententelemetry/instrumentation/restlet/v1_0/spring/AbstractSpringServerLibraryTest.groovy b/instrumentation/restlet/restlet-1.1/library/src/test/groovy/io/opententelemetry/instrumentation/restlet/v1_1/spring/AbstractSpringServerLibraryTest.groovy similarity index 88% rename from instrumentation/restlet/restlet-1.0/library/src/test/groovy/io/opententelemetry/instrumentation/restlet/v1_0/spring/AbstractSpringServerLibraryTest.groovy rename to instrumentation/restlet/restlet-1.1/library/src/test/groovy/io/opententelemetry/instrumentation/restlet/v1_1/spring/AbstractSpringServerLibraryTest.groovy index 681973629c6e..0ae770fb3b0a 100644 --- a/instrumentation/restlet/restlet-1.0/library/src/test/groovy/io/opententelemetry/instrumentation/restlet/v1_0/spring/AbstractSpringServerLibraryTest.groovy +++ b/instrumentation/restlet/restlet-1.1/library/src/test/groovy/io/opententelemetry/instrumentation/restlet/v1_1/spring/AbstractSpringServerLibraryTest.groovy @@ -3,11 +3,11 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opententelemetry.instrumentation.restlet.v1_0.spring +package io.opententelemetry.instrumentation.restlet.v1_1.spring import com.noelios.restlet.StatusFilter -import io.opentelemetry.instrumentation.restlet.v1_0.RestletTelemetry -import io.opentelemetry.instrumentation.restlet.v1_0.spring.AbstractSpringServerTest +import io.opentelemetry.instrumentation.restlet.v1_1.RestletTelemetry +import io.opentelemetry.instrumentation.restlet.v1_1.spring.AbstractSpringServerTest import io.opentelemetry.instrumentation.test.LibraryTestTrait import io.opentelemetry.instrumentation.testing.junit.http.AbstractHttpServerTest import org.restlet.Restlet diff --git a/instrumentation/restlet/restlet-1.0/library/src/test/groovy/io/opententelemetry/instrumentation/restlet/v1_0/spring/SpringBeanRouterTest.groovy b/instrumentation/restlet/restlet-1.1/library/src/test/groovy/io/opententelemetry/instrumentation/restlet/v1_1/spring/SpringBeanRouterTest.groovy similarity index 79% rename from instrumentation/restlet/restlet-1.0/library/src/test/groovy/io/opententelemetry/instrumentation/restlet/v1_0/spring/SpringBeanRouterTest.groovy rename to instrumentation/restlet/restlet-1.1/library/src/test/groovy/io/opententelemetry/instrumentation/restlet/v1_1/spring/SpringBeanRouterTest.groovy index d0397e3b9c34..86eebd6ba125 100644 --- a/instrumentation/restlet/restlet-1.0/library/src/test/groovy/io/opententelemetry/instrumentation/restlet/v1_0/spring/SpringBeanRouterTest.groovy +++ b/instrumentation/restlet/restlet-1.1/library/src/test/groovy/io/opententelemetry/instrumentation/restlet/v1_1/spring/SpringBeanRouterTest.groovy @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opententelemetry.instrumentation.restlet.v1_0.spring +package io.opententelemetry.instrumentation.restlet.v1_1.spring class SpringBeanRouterTest extends AbstractSpringServerLibraryTest { @Override diff --git a/instrumentation/restlet/restlet-1.0/library/src/test/groovy/io/opententelemetry/instrumentation/restlet/v1_0/spring/SpringRouterTest.groovy b/instrumentation/restlet/restlet-1.1/library/src/test/groovy/io/opententelemetry/instrumentation/restlet/v1_1/spring/SpringRouterTest.groovy similarity index 78% rename from instrumentation/restlet/restlet-1.0/library/src/test/groovy/io/opententelemetry/instrumentation/restlet/v1_0/spring/SpringRouterTest.groovy rename to instrumentation/restlet/restlet-1.1/library/src/test/groovy/io/opententelemetry/instrumentation/restlet/v1_1/spring/SpringRouterTest.groovy index d3891b696ff3..1e124b351cfd 100644 --- a/instrumentation/restlet/restlet-1.0/library/src/test/groovy/io/opententelemetry/instrumentation/restlet/v1_0/spring/SpringRouterTest.groovy +++ b/instrumentation/restlet/restlet-1.1/library/src/test/groovy/io/opententelemetry/instrumentation/restlet/v1_1/spring/SpringRouterTest.groovy @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opententelemetry.instrumentation.restlet.v1_0.spring +package io.opententelemetry.instrumentation.restlet.v1_1.spring class SpringRouterTest extends AbstractSpringServerLibraryTest { diff --git a/instrumentation/restlet/restlet-1.0/testing/build.gradle.kts b/instrumentation/restlet/restlet-1.1/testing/build.gradle.kts similarity index 100% rename from instrumentation/restlet/restlet-1.0/testing/build.gradle.kts rename to instrumentation/restlet/restlet-1.1/testing/build.gradle.kts diff --git a/instrumentation/restlet/restlet-1.0/testing/src/main/groovy/io/opentelemetry/instrumentation/restlet/v1_0/AbstractRestletServerTest.groovy b/instrumentation/restlet/restlet-1.1/testing/src/main/groovy/io/opentelemetry/instrumentation/restlet/v1_1/AbstractRestletServerTest.groovy similarity index 99% rename from instrumentation/restlet/restlet-1.0/testing/src/main/groovy/io/opentelemetry/instrumentation/restlet/v1_0/AbstractRestletServerTest.groovy rename to instrumentation/restlet/restlet-1.1/testing/src/main/groovy/io/opentelemetry/instrumentation/restlet/v1_1/AbstractRestletServerTest.groovy index a33b4a2c219c..380413a08f98 100644 --- a/instrumentation/restlet/restlet-1.0/testing/src/main/groovy/io/opentelemetry/instrumentation/restlet/v1_0/AbstractRestletServerTest.groovy +++ b/instrumentation/restlet/restlet-1.1/testing/src/main/groovy/io/opentelemetry/instrumentation/restlet/v1_1/AbstractRestletServerTest.groovy @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.instrumentation.restlet.v1_0 +package io.opentelemetry.instrumentation.restlet.v1_1 import io.opentelemetry.instrumentation.test.base.HttpServerTest diff --git a/instrumentation/restlet/restlet-1.0/testing/src/main/groovy/io/opentelemetry/instrumentation/restlet/v1_0/AbstractServletServerTest.groovy b/instrumentation/restlet/restlet-1.1/testing/src/main/groovy/io/opentelemetry/instrumentation/restlet/v1_1/AbstractServletServerTest.groovy similarity index 98% rename from instrumentation/restlet/restlet-1.0/testing/src/main/groovy/io/opentelemetry/instrumentation/restlet/v1_0/AbstractServletServerTest.groovy rename to instrumentation/restlet/restlet-1.1/testing/src/main/groovy/io/opentelemetry/instrumentation/restlet/v1_1/AbstractServletServerTest.groovy index fb48543db412..3c764ae8e065 100644 --- a/instrumentation/restlet/restlet-1.0/testing/src/main/groovy/io/opentelemetry/instrumentation/restlet/v1_0/AbstractServletServerTest.groovy +++ b/instrumentation/restlet/restlet-1.1/testing/src/main/groovy/io/opentelemetry/instrumentation/restlet/v1_1/AbstractServletServerTest.groovy @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.instrumentation.restlet.v1_0 +package io.opentelemetry.instrumentation.restlet.v1_1 import io.opentelemetry.instrumentation.test.base.HttpServerTest diff --git a/instrumentation/restlet/restlet-1.0/testing/src/main/groovy/io/opentelemetry/instrumentation/restlet/v1_0/RestletAppTestBase.groovy b/instrumentation/restlet/restlet-1.1/testing/src/main/groovy/io/opentelemetry/instrumentation/restlet/v1_1/RestletAppTestBase.groovy similarity index 98% rename from instrumentation/restlet/restlet-1.0/testing/src/main/groovy/io/opentelemetry/instrumentation/restlet/v1_0/RestletAppTestBase.groovy rename to instrumentation/restlet/restlet-1.1/testing/src/main/groovy/io/opentelemetry/instrumentation/restlet/v1_1/RestletAppTestBase.groovy index 472296fb4f26..299fc09f75da 100644 --- a/instrumentation/restlet/restlet-1.0/testing/src/main/groovy/io/opentelemetry/instrumentation/restlet/v1_0/RestletAppTestBase.groovy +++ b/instrumentation/restlet/restlet-1.1/testing/src/main/groovy/io/opentelemetry/instrumentation/restlet/v1_1/RestletAppTestBase.groovy @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.instrumentation.restlet.v1_0 +package io.opentelemetry.instrumentation.restlet.v1_1 import org.restlet.Context import org.restlet.data.Form diff --git a/instrumentation/restlet/restlet-1.0/testing/src/main/groovy/io/opentelemetry/instrumentation/restlet/v1_0/spring/AbstractSpringServerTest.groovy b/instrumentation/restlet/restlet-1.1/testing/src/main/groovy/io/opentelemetry/instrumentation/restlet/v1_1/spring/AbstractSpringServerTest.groovy similarity index 86% rename from instrumentation/restlet/restlet-1.0/testing/src/main/groovy/io/opentelemetry/instrumentation/restlet/v1_0/spring/AbstractSpringServerTest.groovy rename to instrumentation/restlet/restlet-1.1/testing/src/main/groovy/io/opentelemetry/instrumentation/restlet/v1_1/spring/AbstractSpringServerTest.groovy index a31f8d94931f..0700158f93be 100644 --- a/instrumentation/restlet/restlet-1.0/testing/src/main/groovy/io/opentelemetry/instrumentation/restlet/v1_0/spring/AbstractSpringServerTest.groovy +++ b/instrumentation/restlet/restlet-1.1/testing/src/main/groovy/io/opentelemetry/instrumentation/restlet/v1_1/spring/AbstractSpringServerTest.groovy @@ -3,9 +3,9 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.instrumentation.restlet.v1_0.spring +package io.opentelemetry.instrumentation.restlet.v1_1.spring -import io.opentelemetry.instrumentation.restlet.v1_0.AbstractRestletServerTest +import io.opentelemetry.instrumentation.restlet.v1_1.AbstractRestletServerTest import org.restlet.Component import org.restlet.Router import org.restlet.Server diff --git a/instrumentation/restlet/restlet-1.0/testing/src/main/resources/servlet-ext-app/WEB-INF/web.xml b/instrumentation/restlet/restlet-1.1/testing/src/main/resources/servlet-ext-app/WEB-INF/web.xml similarity index 93% rename from instrumentation/restlet/restlet-1.0/testing/src/main/resources/servlet-ext-app/WEB-INF/web.xml rename to instrumentation/restlet/restlet-1.1/testing/src/main/resources/servlet-ext-app/WEB-INF/web.xml index e0c24070292e..2bdf2e658526 100644 --- a/instrumentation/restlet/restlet-1.0/testing/src/main/resources/servlet-ext-app/WEB-INF/web.xml +++ b/instrumentation/restlet/restlet-1.1/testing/src/main/resources/servlet-ext-app/WEB-INF/web.xml @@ -14,7 +14,7 @@ com.noelios.restlet.ext.servlet.ServerServlet org.restlet.application - io.opentelemetry.instrumentation.restlet.v1_0.AbstractServletServerTest$TestApp + io.opentelemetry.instrumentation.restlet.v1_1.AbstractServletServerTest$TestApp diff --git a/instrumentation/restlet/restlet-1.0/testing/src/main/resources/springBeanRouterConf.xml b/instrumentation/restlet/restlet-1.1/testing/src/main/resources/springBeanRouterConf.xml similarity index 71% rename from instrumentation/restlet/restlet-1.0/testing/src/main/resources/springBeanRouterConf.xml rename to instrumentation/restlet/restlet-1.1/testing/src/main/resources/springBeanRouterConf.xml index 579c386f166b..4680bc583ef2 100644 --- a/instrumentation/restlet/restlet-1.0/testing/src/main/resources/springBeanRouterConf.xml +++ b/instrumentation/restlet/restlet-1.1/testing/src/main/resources/springBeanRouterConf.xml @@ -10,20 +10,20 @@ + class="io.opentelemetry.instrumentation.restlet.v1_1.RestletAppTestBase$SuccessResource"/> + class="io.opentelemetry.instrumentation.restlet.v1_1.RestletAppTestBase$ErrorResource"/> + class="io.opentelemetry.instrumentation.restlet.v1_1.RestletAppTestBase$ExceptionResource"/> + class="io.opentelemetry.instrumentation.restlet.v1_1.RestletAppTestBase$QueryParamResource"/> + class="io.opentelemetry.instrumentation.restlet.v1_1.RestletAppTestBase$PathParamResource"/> + class="io.opentelemetry.instrumentation.restlet.v1_1.RestletAppTestBase$RedirectResource"/> + class="io.opentelemetry.instrumentation.restlet.v1_1.RestletAppTestBase$CaptureHeadersResource"/> + class="io.opentelemetry.instrumentation.restlet.v1_1.RestletAppTestBase$IndexedChildResource"/> \ No newline at end of file diff --git a/instrumentation/restlet/restlet-1.0/testing/src/main/resources/springRouterConf.xml b/instrumentation/restlet/restlet-1.1/testing/src/main/resources/springRouterConf.xml similarity index 69% rename from instrumentation/restlet/restlet-1.0/testing/src/main/resources/springRouterConf.xml rename to instrumentation/restlet/restlet-1.1/testing/src/main/resources/springRouterConf.xml index 6807703cb0dd..b80777a7a42f 100644 --- a/instrumentation/restlet/restlet-1.0/testing/src/main/resources/springRouterConf.xml +++ b/instrumentation/restlet/restlet-1.1/testing/src/main/resources/springRouterConf.xml @@ -12,21 +12,21 @@ + value="io.opentelemetry.instrumentation.restlet.v1_1.RestletAppTestBase$SuccessResource"/> + value="io.opentelemetry.instrumentation.restlet.v1_1.RestletAppTestBase$ErrorResource"/> + value="io.opentelemetry.instrumentation.restlet.v1_1.RestletAppTestBase$ExceptionResource"/> + value="io.opentelemetry.instrumentation.restlet.v1_1.RestletAppTestBase$QueryParamResource"/> + value="io.opentelemetry.instrumentation.restlet.v1_1.RestletAppTestBase$PathParamResource"/> + value="io.opentelemetry.instrumentation.restlet.v1_1.RestletAppTestBase$RedirectResource"/> + value="io.opentelemetry.instrumentation.restlet.v1_1.RestletAppTestBase$CaptureHeadersResource"/> + value="io.opentelemetry.instrumentation.restlet.v1_1.RestletAppTestBase$IndexedChildResource"/> diff --git a/instrumentation/restlet/restlet-2.0/javaagent/build.gradle.kts b/instrumentation/restlet/restlet-2.0/javaagent/build.gradle.kts index 410d3b8a4042..c88a2e6cad51 100644 --- a/instrumentation/restlet/restlet-2.0/javaagent/build.gradle.kts +++ b/instrumentation/restlet/restlet-2.0/javaagent/build.gradle.kts @@ -4,8 +4,8 @@ plugins { muzzle { pass { - group.set("org.restlet") - module.set("org.restlet.jse") + group.set("org.restlet.jse") + module.set("org.restlet") versions.set("[2.0.0,)") assertInverse.set(true) } diff --git a/instrumentation/restlet/restlet-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/restlet/v2_0/RestletSingletons.java b/instrumentation/restlet/restlet-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/restlet/v2_0/RestletSingletons.java index 43ee5de33dc3..1526fd2a5b2a 100644 --- a/instrumentation/restlet/restlet-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/restlet/v2_0/RestletSingletons.java +++ b/instrumentation/restlet/restlet-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/restlet/v2_0/RestletSingletons.java @@ -8,15 +8,27 @@ import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpRouteGetter; +import io.opentelemetry.instrumentation.api.instrumenter.http.HttpServerAttributesExtractor; +import io.opentelemetry.instrumentation.restlet.v2_0.internal.RestletHttpAttributesGetter; import io.opentelemetry.instrumentation.restlet.v2_0.internal.RestletInstrumenterFactory; +import io.opentelemetry.instrumentation.restlet.v2_0.internal.RestletNetAttributesGetter; +import io.opentelemetry.javaagent.bootstrap.internal.CommonConfig; import io.opentelemetry.javaagent.bootstrap.servlet.ServletContextPath; +import java.util.Collections; import org.restlet.Request; import org.restlet.Response; public final class RestletSingletons { private static final Instrumenter INSTRUMENTER = - RestletInstrumenterFactory.newServerInstrumenter(GlobalOpenTelemetry.get()); + RestletInstrumenterFactory.newServerInstrumenter( + GlobalOpenTelemetry.get(), + HttpServerAttributesExtractor.builder( + RestletHttpAttributesGetter.INSTANCE, new RestletNetAttributesGetter()) + .setCapturedRequestHeaders(CommonConfig.get().getServerRequestHeaders()) + .setCapturedResponseHeaders(CommonConfig.get().getServerResponseHeaders()) + .build(), + Collections.emptyList()); public static Instrumenter instrumenter() { return INSTRUMENTER; diff --git a/instrumentation/restlet/restlet-2.0/library/src/main/java/io/opentelemetry/instrumentation/restlet/v2_0/RestletTelemetryBuilder.java b/instrumentation/restlet/restlet-2.0/library/src/main/java/io/opentelemetry/instrumentation/restlet/v2_0/RestletTelemetryBuilder.java index a6bc1638b3f0..86236231a2ac 100644 --- a/instrumentation/restlet/restlet-2.0/library/src/main/java/io/opentelemetry/instrumentation/restlet/v2_0/RestletTelemetryBuilder.java +++ b/instrumentation/restlet/restlet-2.0/library/src/main/java/io/opentelemetry/instrumentation/restlet/v2_0/RestletTelemetryBuilder.java @@ -5,6 +5,7 @@ package io.opentelemetry.instrumentation.restlet.v2_0; +import com.google.errorprone.annotations.CanIgnoreReturnValue; import io.opentelemetry.api.OpenTelemetry; import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; @@ -12,6 +13,7 @@ import io.opentelemetry.instrumentation.api.instrumenter.http.HttpServerAttributesExtractorBuilder; import io.opentelemetry.instrumentation.restlet.v2_0.internal.RestletHttpAttributesGetter; import io.opentelemetry.instrumentation.restlet.v2_0.internal.RestletInstrumenterFactory; +import io.opentelemetry.instrumentation.restlet.v2_0.internal.RestletNetAttributesGetter; import java.util.ArrayList; import java.util.List; import org.restlet.Request; @@ -25,7 +27,8 @@ public final class RestletTelemetryBuilder { new ArrayList<>(); private final HttpServerAttributesExtractorBuilder httpAttributesExtractorBuilder = - HttpServerAttributesExtractor.builder(RestletHttpAttributesGetter.INSTANCE); + HttpServerAttributesExtractor.builder( + RestletHttpAttributesGetter.INSTANCE, new RestletNetAttributesGetter()); RestletTelemetryBuilder(OpenTelemetry openTelemetry) { this.openTelemetry = openTelemetry; @@ -35,6 +38,7 @@ public final class RestletTelemetryBuilder { * Adds an additional {@link AttributesExtractor} to invoke to set attributes to instrumented * items. */ + @CanIgnoreReturnValue public RestletTelemetryBuilder addAttributesExtractor( AttributesExtractor attributesExtractor) { additionalExtractors.add(attributesExtractor); @@ -46,6 +50,7 @@ public RestletTelemetryBuilder addAttributesExtractor( * * @param requestHeaders A list of HTTP header names. */ + @CanIgnoreReturnValue public RestletTelemetryBuilder setCapturedRequestHeaders(List requestHeaders) { httpAttributesExtractorBuilder.setCapturedRequestHeaders(requestHeaders); return this; @@ -56,6 +61,7 @@ public RestletTelemetryBuilder setCapturedRequestHeaders(List requestHea * * @param responseHeaders A list of HTTP header names. */ + @CanIgnoreReturnValue public RestletTelemetryBuilder setCapturedResponseHeaders(List responseHeaders) { httpAttributesExtractorBuilder.setCapturedResponseHeaders(responseHeaders); return this; diff --git a/instrumentation/restlet/restlet-2.0/library/src/main/java/io/opentelemetry/instrumentation/restlet/v2_0/internal/RestletHeadersGetter.java b/instrumentation/restlet/restlet-2.0/library/src/main/java/io/opentelemetry/instrumentation/restlet/v2_0/internal/RestletHeadersGetter.java index 8000838585c3..e4f91c64d185 100644 --- a/instrumentation/restlet/restlet-2.0/library/src/main/java/io/opentelemetry/instrumentation/restlet/v2_0/internal/RestletHeadersGetter.java +++ b/instrumentation/restlet/restlet-2.0/library/src/main/java/io/opentelemetry/instrumentation/restlet/v2_0/internal/RestletHeadersGetter.java @@ -5,25 +5,67 @@ package io.opentelemetry.instrumentation.restlet.v2_0.internal; +import static java.util.Collections.emptySet; + import io.opentelemetry.context.propagation.TextMapGetter; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import java.util.Map; +import java.util.concurrent.ConcurrentMap; +import javax.annotation.Nullable; import org.restlet.Message; import org.restlet.Request; import org.restlet.util.Series; final class RestletHeadersGetter implements TextMapGetter { + private static final MethodHandle GET_ATTRIBUTES; + + static { + MethodHandle getAttributes = null; + + MethodHandles.Lookup lookup = MethodHandles.lookup(); + try { + getAttributes = + lookup.findVirtual(Message.class, "getAttributes", MethodType.methodType(Map.class)); + } catch (NoSuchMethodException | IllegalAccessException e) { + // changed the return type to ConcurrentMap in version 2.1 + try { + getAttributes = + lookup.findVirtual( + Message.class, "getAttributes", MethodType.methodType(ConcurrentMap.class)); + } catch (NoSuchMethodException | IllegalAccessException ex) { + // ignored + } + } + + GET_ATTRIBUTES = getAttributes; + } + @Override public Iterable keys(Request carrier) { - return getHeaders(carrier).getNames(); + Series headers = getHeaders(carrier); + return headers == null ? emptySet() : headers.getNames(); } @Override public String get(Request carrier, String key) { Series headers = getHeaders(carrier); - return headers.getFirstValue(key, /* ignoreCase = */ true); + return headers == null ? null : headers.getFirstValue(key, /* ignoreCase = */ true); } + @SuppressWarnings("unchecked") + @Nullable static Series getHeaders(Message carrier) { - return (Series) carrier.getAttributes().get("org.restlet.http.headers"); + if (GET_ATTRIBUTES == null) { + return null; + } + try { + Map attributes = (Map) GET_ATTRIBUTES.invoke(carrier); + return (Series) attributes.get("org.restlet.http.headers"); + } catch (Throwable e) { + return null; + } } } diff --git a/instrumentation/restlet/restlet-2.0/library/src/main/java/io/opentelemetry/instrumentation/restlet/v2_0/internal/RestletHttpAttributesGetter.java b/instrumentation/restlet/restlet-2.0/library/src/main/java/io/opentelemetry/instrumentation/restlet/v2_0/internal/RestletHttpAttributesGetter.java index 2ce344a48eb7..69fc25c81f58 100644 --- a/instrumentation/restlet/restlet-2.0/library/src/main/java/io/opentelemetry/instrumentation/restlet/v2_0/internal/RestletHttpAttributesGetter.java +++ b/instrumentation/restlet/restlet-2.0/library/src/main/java/io/opentelemetry/instrumentation/restlet/v2_0/internal/RestletHttpAttributesGetter.java @@ -59,18 +59,6 @@ public List requestHeader(Request request, String name) { return Arrays.asList(headers.getValuesArray(name, true)); } - @Override - @Nullable - public Long requestContentLength(Request request, @Nullable Response response) { - return null; - } - - @Override - @Nullable - public Long requestContentLengthUncompressed(Request request, @Nullable Response response) { - return null; - } - @Override @Nullable public String flavor(Request request) { @@ -88,28 +76,10 @@ public String flavor(Request request) { } @Override - @Nullable - public String serverName(Request request) { - return null; - } - - @Override - public Integer statusCode(Request request, Response response) { + public Integer statusCode(Request request, Response response, @Nullable Throwable error) { return response.getStatus().getCode(); } - @Override - @Nullable - public Long responseContentLength(Request request, Response response) { - return null; - } - - @Override - @Nullable - public Long responseContentLengthUncompressed(Request request, Response response) { - return null; - } - @Override public List responseHeader(Request request, Response response, String name) { Series headers = getHeaders(response); diff --git a/instrumentation/restlet/restlet-2.0/library/src/main/java/io/opentelemetry/instrumentation/restlet/v2_0/internal/RestletInstrumenterFactory.java b/instrumentation/restlet/restlet-2.0/library/src/main/java/io/opentelemetry/instrumentation/restlet/v2_0/internal/RestletInstrumenterFactory.java index a7fe031039fd..4c811963f9e9 100644 --- a/instrumentation/restlet/restlet-2.0/library/src/main/java/io/opentelemetry/instrumentation/restlet/v2_0/internal/RestletInstrumenterFactory.java +++ b/instrumentation/restlet/restlet-2.0/library/src/main/java/io/opentelemetry/instrumentation/restlet/v2_0/internal/RestletInstrumenterFactory.java @@ -12,8 +12,6 @@ import io.opentelemetry.instrumentation.api.instrumenter.http.HttpServerMetrics; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanNameExtractor; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanStatusExtractor; -import io.opentelemetry.instrumentation.api.instrumenter.net.NetServerAttributesExtractor; -import java.util.Collections; import java.util.List; import org.restlet.Request; import org.restlet.Response; @@ -22,32 +20,25 @@ * This class is internal and is hence not for public use. Its APIs are unstable and can change at * any time. */ -public class RestletInstrumenterFactory { +public final class RestletInstrumenterFactory { private static final String INSTRUMENTATION_NAME = "io.opentelemetry.restlet-2.0"; - public static Instrumenter newServerInstrumenter(OpenTelemetry openTelemetry) { - return newServerInstrumenter( - openTelemetry, - HttpServerAttributesExtractor.create(RestletHttpAttributesGetter.INSTANCE), - Collections.emptyList()); - } - public static Instrumenter newServerInstrumenter( OpenTelemetry openTelemetry, HttpServerAttributesExtractor httpServerAttributesExtractor, List> additionalExtractors) { RestletHttpAttributesGetter httpAttributesGetter = RestletHttpAttributesGetter.INSTANCE; - RestletNetAttributesGetter netAttributesGetter = new RestletNetAttributesGetter(); return Instrumenter.builder( openTelemetry, INSTRUMENTATION_NAME, HttpSpanNameExtractor.create(httpAttributesGetter)) .setSpanStatusExtractor(HttpSpanStatusExtractor.create(httpAttributesGetter)) .addAttributesExtractor(httpServerAttributesExtractor) - .addAttributesExtractor(NetServerAttributesExtractor.create(netAttributesGetter)) .addAttributesExtractors(additionalExtractors) .addOperationMetrics(HttpServerMetrics.get()) - .newServerInstrumenter(new RestletHeadersGetter()); + .buildServerInstrumenter(new RestletHeadersGetter()); } + + private RestletInstrumenterFactory() {} } diff --git a/instrumentation/restlet/restlet-2.0/library/src/main/java/io/opentelemetry/instrumentation/restlet/v2_0/internal/RestletNetAttributesGetter.java b/instrumentation/restlet/restlet-2.0/library/src/main/java/io/opentelemetry/instrumentation/restlet/v2_0/internal/RestletNetAttributesGetter.java index a2918dc9558e..d956ea6018b9 100644 --- a/instrumentation/restlet/restlet-2.0/library/src/main/java/io/opentelemetry/instrumentation/restlet/v2_0/internal/RestletNetAttributesGetter.java +++ b/instrumentation/restlet/restlet-2.0/library/src/main/java/io/opentelemetry/instrumentation/restlet/v2_0/internal/RestletNetAttributesGetter.java @@ -5,25 +5,158 @@ package io.opentelemetry.instrumentation.restlet.v2_0.internal; +import static java.lang.invoke.MethodType.methodType; + import io.opentelemetry.instrumentation.api.instrumenter.net.NetServerAttributesGetter; import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; import javax.annotation.Nullable; import org.restlet.Request; -final class RestletNetAttributesGetter implements NetServerAttributesGetter { +/** + * This class is internal and is hence not for public use. Its APIs are unstable and can change at + * any time. + */ +public final class RestletNetAttributesGetter implements NetServerAttributesGetter { + + private static final Class HTTP_REQUEST_CLASS; + private static final MethodHandle GET_HTTP_CALL; + private static final MethodHandle GET_HOST_DOMAIN; + private static final MethodHandle GET_SERVER_PORT; + private static final MethodHandle GET_SERVER_ADDRESS; + + static { + Class httpRequestClass = null; + Class serverCallClass = null; + MethodHandle getHttpCall = null; + MethodHandle getHostDomain = null; + MethodHandle getServerPort = null; + MethodHandle getServerAddress = null; + + try { + httpRequestClass = Class.forName("org.restlet.engine.http.HttpRequest"); + } catch (ClassNotFoundException e) { + // moved to another package in version 2.4 + try { + httpRequestClass = Class.forName("org.restlet.engine.adapter.HttpRequest"); + } catch (ClassNotFoundException ex) { + // ignored + } + } + + try { + serverCallClass = Class.forName("org.restlet.engine.http.ServerCall"); + } catch (ClassNotFoundException e) { + // moved to another package in version 2.4 + try { + serverCallClass = Class.forName("org.restlet.engine.adapter.ServerCall"); + } catch (ClassNotFoundException ex) { + // ignored + } + } + + if (httpRequestClass != null && serverCallClass != null) { + try { + MethodHandles.Lookup lookup = MethodHandles.publicLookup(); + getHttpCall = + lookup.findVirtual(httpRequestClass, "getHttpCall", methodType(serverCallClass)); + getHostDomain = + lookup.findVirtual(serverCallClass, "getHostDomain", methodType(String.class)); + getServerPort = lookup.findVirtual(serverCallClass, "getServerPort", methodType(int.class)); + getServerAddress = + lookup.findVirtual(serverCallClass, "getServerAddress", methodType(String.class)); + } catch (NoSuchMethodException | IllegalAccessException e) { + // ignored + } + } + + HTTP_REQUEST_CLASS = httpRequestClass; + GET_HTTP_CALL = getHttpCall; + GET_HOST_DOMAIN = getHostDomain; + GET_SERVER_PORT = getServerPort; + GET_SERVER_ADDRESS = getServerAddress; + } + @Override public String transport(Request request) { return SemanticAttributes.NetTransportValues.IP_TCP; } + @Nullable @Override - public Integer peerPort(Request request) { - return request.getClientInfo().getPort(); + public String hostName(Request request) { + if (GET_HOST_DOMAIN == null) { + return null; + } + Object call = serverCall(request); + if (call == null) { + return null; + } + try { + return (String) GET_HOST_DOMAIN.invoke(call); + } catch (Throwable e) { + return null; + } + } + + @Nullable + @Override + public Integer hostPort(Request request) { + if (GET_SERVER_PORT == null) { + return null; + } + Object call = serverCall(request); + if (call == null) { + return null; + } + try { + return (int) GET_SERVER_PORT.invoke(call); + } catch (Throwable e) { + return null; + } } @Override @Nullable - public String peerIp(Request request) { + public String sockPeerAddr(Request request) { return request.getClientInfo().getAddress(); } + + @Override + public Integer sockPeerPort(Request request) { + return request.getClientInfo().getPort(); + } + + @Nullable + @Override + public String sockHostAddr(Request request) { + if (GET_SERVER_ADDRESS == null) { + return null; + } + Object call = serverCall(request); + if (call == null) { + return null; + } + try { + return (String) GET_SERVER_ADDRESS.invoke(call); + } catch (Throwable e) { + return null; + } + } + + @Nullable + private static Object serverCall(Request request) { + if (GET_HTTP_CALL == null || HTTP_REQUEST_CLASS == null) { + return null; + } + if (HTTP_REQUEST_CLASS.isInstance(request)) { + try { + return GET_HTTP_CALL.invoke(request); + } catch (Throwable e) { + return null; + } + } + return null; + } } diff --git a/instrumentation/rmi/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rmi/client/RmiClientSingletons.java b/instrumentation/rmi/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rmi/client/RmiClientSingletons.java index 8a5985fe3486..678f17611b3b 100644 --- a/instrumentation/rmi/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rmi/client/RmiClientSingletons.java +++ b/instrumentation/rmi/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rmi/client/RmiClientSingletons.java @@ -25,7 +25,7 @@ public final class RmiClientSingletons { "io.opentelemetry.rmi", RpcSpanNameExtractor.create(rpcAttributesGetter)) .addAttributesExtractor(RpcClientAttributesExtractor.create(rpcAttributesGetter)) - .newInstrumenter(SpanKindExtractor.alwaysClient()); + .buildInstrumenter(SpanKindExtractor.alwaysClient()); } public static Instrumenter instrumenter() { diff --git a/instrumentation/rmi/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rmi/client/UnicastRefInstrumentation.java b/instrumentation/rmi/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rmi/client/UnicastRefInstrumentation.java index bea23ca889b7..1d32fa89efb1 100644 --- a/instrumentation/rmi/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rmi/client/UnicastRefInstrumentation.java +++ b/instrumentation/rmi/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rmi/client/UnicastRefInstrumentation.java @@ -5,7 +5,6 @@ package io.opentelemetry.javaagent.instrumentation.rmi.client; -import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.extendsClass; import static io.opentelemetry.javaagent.instrumentation.rmi.client.RmiClientSingletons.instrumenter; import static net.bytebuddy.matcher.ElementMatchers.isMethod; import static net.bytebuddy.matcher.ElementMatchers.named; @@ -22,9 +21,10 @@ import net.bytebuddy.matcher.ElementMatcher; public class UnicastRefInstrumentation implements TypeInstrumentation { + @Override public ElementMatcher typeMatcher() { - return extendsClass(named("sun.rmi.server.UnicastRef")); + return named("sun.rmi.server.UnicastRef"); } @Override diff --git a/instrumentation/rmi/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rmi/context/client/RmiClientContextInstrumentation.java b/instrumentation/rmi/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rmi/context/client/RmiClientContextInstrumentation.java index e2dc02ad477f..bacfd1e70d5a 100644 --- a/instrumentation/rmi/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rmi/context/client/RmiClientContextInstrumentation.java +++ b/instrumentation/rmi/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rmi/context/client/RmiClientContextInstrumentation.java @@ -5,7 +5,6 @@ package io.opentelemetry.javaagent.instrumentation.rmi.context.client; -import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.extendsClass; import static io.opentelemetry.javaagent.instrumentation.rmi.context.ContextPropagator.PROPAGATOR; import static net.bytebuddy.matcher.ElementMatchers.isConstructor; import static net.bytebuddy.matcher.ElementMatchers.named; @@ -54,7 +53,7 @@ public class RmiClientContextInstrumentation implements TypeInstrumentation { @Override public ElementMatcher typeMatcher() { - return extendsClass(named("sun.rmi.transport.StreamRemoteCall")); + return named("sun.rmi.transport.StreamRemoteCall"); } @Override @@ -67,15 +66,15 @@ public void transform(TypeTransformer transformer) { // expose sun.rmi.transport.StreamRemoteCall to helper classes transformer.applyTransformer( - (builder, typeDescription, classLoader, module) -> { + (builder, typeDescription, classLoader, javaModule, protectionDomain) -> { if (JavaModule.isSupported() && classLoader == null && "sun.rmi.transport.StreamRemoteCall".equals(typeDescription.getName()) - && module != null) { + && javaModule != null) { Instrumentation instrumentation = InstrumentationHolder.getInstrumentation(); ClassInjector.UsingInstrumentation.redefineModule( instrumentation, - module, + javaModule, Collections.emptySet(), Collections.emptyMap(), Collections.singletonMap( diff --git a/instrumentation/rmi/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rmi/context/jpms/ExposeRmiModuleInstrumentation.java b/instrumentation/rmi/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rmi/context/jpms/ExposeRmiModuleInstrumentation.java index 0afbec2ca3a1..e82c4786f286 100644 --- a/instrumentation/rmi/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rmi/context/jpms/ExposeRmiModuleInstrumentation.java +++ b/instrumentation/rmi/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rmi/context/jpms/ExposeRmiModuleInstrumentation.java @@ -55,15 +55,15 @@ public boolean matches(ClassLoader target) { @Override public void transform(TypeTransformer transformer) { transformer.applyTransformer( - (builder, typeDescription, classLoader, module) -> { - if (module != null && module.isNamed()) { + (builder, typeDescription, classLoader, javaModule, protectionDomain) -> { + if (javaModule != null && javaModule.isNamed()) { // using Java8BytecodeBridge because it's in the unnamed module in the bootstrap // loader, and that's where the rmi instrumentation helper classes will end up JavaModule helperModule = JavaModule.ofType(Java8BytecodeBridge.class); // expose sun.rmi.server package to unnamed module ClassInjector.UsingInstrumentation.redefineModule( InstrumentationHolder.getInstrumentation(), - module, + javaModule, Collections.emptySet(), Collections.singletonMap("sun.rmi.server", Collections.singleton(helperModule)), Collections.emptyMap(), @@ -72,7 +72,9 @@ public void transform(TypeTransformer transformer) { instrumented.set(true); logger.log( - FINE, "Exposed package \"sun.rmi.server\" in module {0} to unnamed module", module); + FINE, + "Exposed package \"sun.rmi.server\" in module {0} to unnamed module", + javaModule); } return builder; }); diff --git a/instrumentation/rmi/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rmi/context/server/RmiServerContextInstrumentation.java b/instrumentation/rmi/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rmi/context/server/RmiServerContextInstrumentation.java index eaf8e91fd22e..f56048f5770a 100644 --- a/instrumentation/rmi/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rmi/context/server/RmiServerContextInstrumentation.java +++ b/instrumentation/rmi/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rmi/context/server/RmiServerContextInstrumentation.java @@ -5,7 +5,6 @@ package io.opentelemetry.javaagent.instrumentation.rmi.context.server; -import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.extendsClass; import static io.opentelemetry.javaagent.instrumentation.rmi.context.ContextPropagator.CONTEXT_CALL_ID; import static net.bytebuddy.matcher.ElementMatchers.isMethod; import static net.bytebuddy.matcher.ElementMatchers.isStatic; @@ -23,7 +22,7 @@ public class RmiServerContextInstrumentation implements TypeInstrumentation { @Override public ElementMatcher typeMatcher() { - return extendsClass(named("sun.rmi.transport.ObjectTable")); + return named("sun.rmi.transport.ObjectTable"); } @Override diff --git a/instrumentation/rmi/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rmi/server/RemoteServerInstrumentation.java b/instrumentation/rmi/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rmi/server/RemoteServerInstrumentation.java index cd4f74178041..cddb58191a8b 100644 --- a/instrumentation/rmi/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rmi/server/RemoteServerInstrumentation.java +++ b/instrumentation/rmi/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rmi/server/RemoteServerInstrumentation.java @@ -17,7 +17,7 @@ import io.opentelemetry.context.Context; import io.opentelemetry.context.Scope; -import io.opentelemetry.instrumentation.api.util.ClassAndMethod; +import io.opentelemetry.instrumentation.api.instrumenter.util.ClassAndMethod; import io.opentelemetry.javaagent.bootstrap.CallDepth; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; diff --git a/instrumentation/rmi/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rmi/server/RmiServerAttributesGetter.java b/instrumentation/rmi/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rmi/server/RmiServerAttributesGetter.java index 3aea7bb481b6..500de061c99a 100644 --- a/instrumentation/rmi/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rmi/server/RmiServerAttributesGetter.java +++ b/instrumentation/rmi/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rmi/server/RmiServerAttributesGetter.java @@ -6,7 +6,7 @@ package io.opentelemetry.javaagent.instrumentation.rmi.server; import io.opentelemetry.instrumentation.api.instrumenter.rpc.RpcAttributesGetter; -import io.opentelemetry.instrumentation.api.util.ClassAndMethod; +import io.opentelemetry.instrumentation.api.instrumenter.util.ClassAndMethod; enum RmiServerAttributesGetter implements RpcAttributesGetter { INSTANCE; diff --git a/instrumentation/rmi/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rmi/server/RmiServerSingletons.java b/instrumentation/rmi/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rmi/server/RmiServerSingletons.java index 7d5944cda812..ac2194c69867 100644 --- a/instrumentation/rmi/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rmi/server/RmiServerSingletons.java +++ b/instrumentation/rmi/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rmi/server/RmiServerSingletons.java @@ -10,7 +10,7 @@ import io.opentelemetry.instrumentation.api.instrumenter.SpanKindExtractor; import io.opentelemetry.instrumentation.api.instrumenter.rpc.RpcServerAttributesExtractor; import io.opentelemetry.instrumentation.api.instrumenter.rpc.RpcSpanNameExtractor; -import io.opentelemetry.instrumentation.api.util.ClassAndMethod; +import io.opentelemetry.instrumentation.api.instrumenter.util.ClassAndMethod; public final class RmiServerSingletons { @@ -25,7 +25,7 @@ public final class RmiServerSingletons { "io.opentelemetry.rmi", RpcSpanNameExtractor.create(rpcAttributesGetter)) .addAttributesExtractor(RpcServerAttributesExtractor.create(rpcAttributesGetter)) - .newInstrumenter(SpanKindExtractor.alwaysServer()); + .buildInstrumenter(SpanKindExtractor.alwaysServer()); } public static Instrumenter instrumenter() { diff --git a/instrumentation/rocketmq-client-4.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rocketmq/RocketMqClientHooks.java b/instrumentation/rocketmq-client-4.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rocketmq/RocketMqClientHooks.java deleted file mode 100644 index 9c91ee7db32c..000000000000 --- a/instrumentation/rocketmq-client-4.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rocketmq/RocketMqClientHooks.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.javaagent.instrumentation.rocketmq; - -import io.opentelemetry.api.GlobalOpenTelemetry; -import io.opentelemetry.instrumentation.rocketmq.RocketMqTelemetry; -import io.opentelemetry.javaagent.bootstrap.internal.InstrumentationConfig; -import org.apache.rocketmq.client.hook.ConsumeMessageHook; -import org.apache.rocketmq.client.hook.SendMessageHook; - -public final class RocketMqClientHooks { - private static final RocketMqTelemetry TELEMETRY = - RocketMqTelemetry.builder(GlobalOpenTelemetry.get()) - .setPropagationEnabled( - InstrumentationConfig.get() - .getBoolean("otel.instrumentation.rocketmq-client.propagation", true)) - .setCaptureExperimentalSpanAttributes( - InstrumentationConfig.get() - .getBoolean( - "otel.instrumentation.rocketmq-client.experimental-span-attributes", false)) - .build(); - - public static final ConsumeMessageHook CONSUME_MESSAGE_HOOK = - TELEMETRY.newTracingConsumeMessageHook(); - - public static final SendMessageHook SEND_MESSAGE_HOOK = TELEMETRY.newTracingSendMessageHook(); -} diff --git a/instrumentation/rocketmq-client-4.8/README.md b/instrumentation/rocketmq/rocketmq-client/rocketmq-client-4.8/README.md similarity index 52% rename from instrumentation/rocketmq-client-4.8/README.md rename to instrumentation/rocketmq/rocketmq-client/rocketmq-client-4.8/README.md index 4ea97b1779fa..c200fdef1c86 100644 --- a/instrumentation/rocketmq-client-4.8/README.md +++ b/instrumentation/rocketmq/rocketmq-client/rocketmq-client-4.8/README.md @@ -1,6 +1,5 @@ -# Settings for the Apache RocketMQ client instrumentation +# Settings for the Apache RocketMQ remoting-based client instrumentation | System property | Type | Default | Description | |---|---|---|---| | `otel.instrumentation.rocketmq-client.experimental-span-attributes` | Boolean | `false` | Enable the capture of experimental span attributes. | -| `otel.instrumentation.rocketmq-client.propagation` | Boolean | `true` | Enables remote context propagation via RocketMQ message headers. | \ No newline at end of file diff --git a/instrumentation/rocketmq-client-4.8/javaagent/build.gradle.kts b/instrumentation/rocketmq/rocketmq-client/rocketmq-client-4.8/javaagent/build.gradle.kts similarity index 62% rename from instrumentation/rocketmq-client-4.8/javaagent/build.gradle.kts rename to instrumentation/rocketmq/rocketmq-client/rocketmq-client-4.8/javaagent/build.gradle.kts index 7ebeca1c3e98..929b2de66edb 100644 --- a/instrumentation/rocketmq-client-4.8/javaagent/build.gradle.kts +++ b/instrumentation/rocketmq/rocketmq-client/rocketmq-client-4.8/javaagent/build.gradle.kts @@ -14,13 +14,15 @@ muzzle { dependencies { library("org.apache.rocketmq:rocketmq-client:4.8.0") - implementation(project(":instrumentation:rocketmq-client-4.8:library")) + implementation(project(":instrumentation:rocketmq:rocketmq-client:rocketmq-client-4.8:library")) - testImplementation(project(":instrumentation:rocketmq-client-4.8:testing")) + testImplementation(project(":instrumentation:rocketmq:rocketmq-client:rocketmq-client-4.8:testing")) testLibrary("org.apache.rocketmq:rocketmq-test:4.8.0") } tasks.withType().configureEach { jvmArgs("-Dotel.instrumentation.rocketmq-client.experimental-span-attributes=true") + + systemProperty("testLatestDeps", findProperty("testLatestDeps") as Boolean) } diff --git a/instrumentation/rocketmq/rocketmq-client/rocketmq-client-4.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rocketmqclient/v4_8/RocketMqClientHooks.java b/instrumentation/rocketmq/rocketmq-client/rocketmq-client-4.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rocketmqclient/v4_8/RocketMqClientHooks.java new file mode 100644 index 000000000000..097c9ff64e61 --- /dev/null +++ b/instrumentation/rocketmq/rocketmq-client/rocketmq-client-4.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rocketmqclient/v4_8/RocketMqClientHooks.java @@ -0,0 +1,51 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.rocketmqclient.v4_8; + +import io.opentelemetry.api.GlobalOpenTelemetry; +import io.opentelemetry.instrumentation.rocketmqclient.v4_8.RocketMqTelemetry; +import io.opentelemetry.javaagent.bootstrap.internal.ExperimentalConfig; +import io.opentelemetry.javaagent.bootstrap.internal.InstrumentationConfig; +import java.util.logging.Logger; +import org.apache.rocketmq.client.hook.ConsumeMessageHook; +import org.apache.rocketmq.client.hook.SendMessageHook; + +public final class RocketMqClientHooks { + + private static final Logger logger = Logger.getLogger(RocketMqClientHooks.class.getName()); + + @SuppressWarnings("deprecation") // call to deprecated method will be removed in the future + private static final RocketMqTelemetry TELEMETRY = + RocketMqTelemetry.builder(GlobalOpenTelemetry.get()) + .setCapturedHeaders(ExperimentalConfig.get().getMessagingHeaders()) + .setPropagationEnabled( + InstrumentationConfig.get() + .getBoolean("otel.instrumentation.rocketmq-client.propagation", true)) + .setCaptureExperimentalSpanAttributes( + InstrumentationConfig.get() + .getBoolean( + "otel.instrumentation.rocketmq-client.experimental-span-attributes", false)) + .build(); + + static { + if (InstrumentationConfig.get().getString("otel.instrumentation.rocketmq-client.propagation") + != null) { + logger.warning( + "The \"otel.instrumentation.rocketmq-client.propagation\" configuration property has" + + " been deprecated and will be removed in a future version." + + " If you have a need for this configuration property, please open an issue in the" + + " opentelemetry-java-instrumentation repository:" + + " https://github.com/open-telemetry/opentelemetry-java-instrumentation/issues"); + } + } + + public static final ConsumeMessageHook CONSUME_MESSAGE_HOOK = + TELEMETRY.newTracingConsumeMessageHook(); + + public static final SendMessageHook SEND_MESSAGE_HOOK = TELEMETRY.newTracingSendMessageHook(); + + private RocketMqClientHooks() {} +} diff --git a/instrumentation/rocketmq-client-4.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rocketmq/RocketMqConsumerInstrumentation.java b/instrumentation/rocketmq/rocketmq-client/rocketmq-client-4.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rocketmqclient/v4_8/RocketMqConsumerInstrumentation.java similarity index 87% rename from instrumentation/rocketmq-client-4.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rocketmq/RocketMqConsumerInstrumentation.java rename to instrumentation/rocketmq/rocketmq-client/rocketmq-client-4.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rocketmqclient/v4_8/RocketMqConsumerInstrumentation.java index 904f0ae16dfc..9c0b1a7430c0 100644 --- a/instrumentation/rocketmq-client-4.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rocketmq/RocketMqConsumerInstrumentation.java +++ b/instrumentation/rocketmq/rocketmq-client/rocketmq-client-4.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rocketmqclient/v4_8/RocketMqConsumerInstrumentation.java @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.javaagent.instrumentation.rocketmq; +package io.opentelemetry.javaagent.instrumentation.rocketmqclient.v4_8; import static net.bytebuddy.matcher.ElementMatchers.isMethod; import static net.bytebuddy.matcher.ElementMatchers.named; @@ -28,10 +28,11 @@ public ElementMatcher typeMatcher() { public void transform(TypeTransformer transformer) { transformer.applyAdviceToMethod( isMethod().and(named("start")).and(takesArguments(0)), - RocketMqConsumerInstrumentation.class.getName() + "$AdviceStart"); + RocketMqConsumerInstrumentation.class.getName() + "$StartAdvice"); } - public static class AdviceStart { + @SuppressWarnings("unused") + public static class StartAdvice { @Advice.OnMethodEnter(suppress = Throwable.class) public static void onEnter( @Advice.FieldValue( diff --git a/instrumentation/rocketmq-client-4.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rocketmq/RocketMqInstrumentationModule.java b/instrumentation/rocketmq/rocketmq-client/rocketmq-client-4.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rocketmqclient/v4_8/RocketMqInstrumentationModule.java similarity index 91% rename from instrumentation/rocketmq-client-4.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rocketmq/RocketMqInstrumentationModule.java rename to instrumentation/rocketmq/rocketmq-client/rocketmq-client-4.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rocketmqclient/v4_8/RocketMqInstrumentationModule.java index 40ba9a0c3311..d36914f099da 100644 --- a/instrumentation/rocketmq-client-4.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rocketmq/RocketMqInstrumentationModule.java +++ b/instrumentation/rocketmq/rocketmq-client/rocketmq-client-4.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rocketmqclient/v4_8/RocketMqInstrumentationModule.java @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.javaagent.instrumentation.rocketmq; +package io.opentelemetry.javaagent.instrumentation.rocketmqclient.v4_8; import static java.util.Arrays.asList; diff --git a/instrumentation/rocketmq-client-4.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rocketmq/RocketMqProducerInstrumentation.java b/instrumentation/rocketmq/rocketmq-client/rocketmq-client-4.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rocketmqclient/v4_8/RocketMqProducerInstrumentation.java similarity index 87% rename from instrumentation/rocketmq-client-4.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rocketmq/RocketMqProducerInstrumentation.java rename to instrumentation/rocketmq/rocketmq-client/rocketmq-client-4.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rocketmqclient/v4_8/RocketMqProducerInstrumentation.java index dc9b836e772e..2ee95c6a2b79 100644 --- a/instrumentation/rocketmq-client-4.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rocketmq/RocketMqProducerInstrumentation.java +++ b/instrumentation/rocketmq/rocketmq-client/rocketmq-client-4.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rocketmqclient/v4_8/RocketMqProducerInstrumentation.java @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.javaagent.instrumentation.rocketmq; +package io.opentelemetry.javaagent.instrumentation.rocketmqclient.v4_8; import static net.bytebuddy.matcher.ElementMatchers.isMethod; import static net.bytebuddy.matcher.ElementMatchers.named; @@ -28,10 +28,11 @@ public ElementMatcher typeMatcher() { public void transform(TypeTransformer transformer) { transformer.applyAdviceToMethod( isMethod().and(named("start")).and(takesArguments(0)), - RocketMqProducerInstrumentation.class.getName() + "$AdviceStart"); + RocketMqProducerInstrumentation.class.getName() + "$StartAdvice"); } - public static class AdviceStart { + @SuppressWarnings("unused") + public static class StartAdvice { @Advice.OnMethodEnter(suppress = Throwable.class) public static void onEnter( @Advice.FieldValue(value = "defaultMQProducerImpl", declaringType = DefaultMQProducer.class) diff --git a/instrumentation/rocketmq-client-4.8/javaagent/src/test/groovy/io/opentelemetry/instrumentation/rocketmq/RocketMqClientTest.groovy b/instrumentation/rocketmq/rocketmq-client/rocketmq-client-4.8/javaagent/src/test/groovy/io/opentelemetry/instrumentation/rocketmqclient/v4_8/RocketMqClientTest.groovy similarity index 89% rename from instrumentation/rocketmq-client-4.8/javaagent/src/test/groovy/io/opentelemetry/instrumentation/rocketmq/RocketMqClientTest.groovy rename to instrumentation/rocketmq/rocketmq-client/rocketmq-client-4.8/javaagent/src/test/groovy/io/opentelemetry/instrumentation/rocketmqclient/v4_8/RocketMqClientTest.groovy index 61167aaf4a3a..d2c2b7d558bd 100644 --- a/instrumentation/rocketmq-client-4.8/javaagent/src/test/groovy/io/opentelemetry/instrumentation/rocketmq/RocketMqClientTest.groovy +++ b/instrumentation/rocketmq/rocketmq-client/rocketmq-client-4.8/javaagent/src/test/groovy/io/opentelemetry/instrumentation/rocketmqclient/v4_8/RocketMqClientTest.groovy @@ -3,7 +3,8 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.instrumentation.rocketmq +package io.opentelemetry.instrumentation.rocketmqclient.v4_8 + import io.opentelemetry.instrumentation.test.AgentTestTrait import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer 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 new file mode 100644 index 000000000000..9f8fb41c3426 --- /dev/null +++ b/instrumentation/rocketmq/rocketmq-client/rocketmq-client-4.8/library/README.md @@ -0,0 +1,43 @@ +# Library Instrumentation for Apache RocketMQ remoting-based client 4.0.0+ + +Provides OpenTelemetry instrumentation for [Apache RocketMQ](https://rocketmq.apache.org/) remoting-based client. + +## Quickstart + +### 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). + +For Maven, add the following to your `pom.xml` dependencies: + +```xml + + + io.opentelemetry.instrumentation + opentelemetry-rocketmq-client-4.8 + OPENTELEMETRY_VERSION + + +``` + +For Gradle, add the following to your dependencies: + +```groovy +implementation("io.opentelemetry.instrumentation:opentelemetry-rocketmq-client-4.8:OPENTELEMETRY_VERSION") +``` + +### Usage + +The instrumentation library provides the implementation of `SendMessageHook` and `ConsumeMessageHook` to provide OpenTelemetry-based spans and context propagation. + +```java +RocketMqTelemetry rocketMqTelemetry; + +void configure(OpenTelemetry openTelemetry, DefaultMQProducerImpl producer, DefaultMQPushConsumerImpl pushConsumer) { + rocketMqTelemetry = RocketMqTelemetry.create(openTelemetry); + // For producer. + producer.registerSendMessageHook(rocketMqTelemetry.newTracingSendMessageHook()); + // For push consumer. + pushConsumer.registerConsumeMessageHook(rocketMqTelemetry.newTracingConsumeMessageHook()); +} +``` diff --git a/instrumentation/rocketmq-client-4.8/library/build.gradle.kts b/instrumentation/rocketmq/rocketmq-client/rocketmq-client-4.8/library/build.gradle.kts similarity index 57% rename from instrumentation/rocketmq-client-4.8/library/build.gradle.kts rename to instrumentation/rocketmq/rocketmq-client/rocketmq-client-4.8/library/build.gradle.kts index 35e337f3814b..4098d5afdcdf 100644 --- a/instrumentation/rocketmq-client-4.8/library/build.gradle.kts +++ b/instrumentation/rocketmq/rocketmq-client/rocketmq-client-4.8/library/build.gradle.kts @@ -10,5 +10,9 @@ dependencies { testLibrary("org.apache.rocketmq:rocketmq-test:4.8.0") - testImplementation(project(":instrumentation:rocketmq-client-4.8:testing")) + testImplementation(project(":instrumentation:rocketmq:rocketmq-client:rocketmq-client-4.8:testing")) +} + +tasks.withType().configureEach { + systemProperty("testLatestDeps", findProperty("testLatestDeps") as Boolean) } diff --git a/instrumentation/rocketmq-client-4.8/library/src/main/java/io/opentelemetry/instrumentation/rocketmq/ContextAndScope.java b/instrumentation/rocketmq/rocketmq-client/rocketmq-client-4.8/library/src/main/java/io/opentelemetry/instrumentation/rocketmqclient/v4_8/ContextAndScope.java similarity index 88% rename from instrumentation/rocketmq-client-4.8/library/src/main/java/io/opentelemetry/instrumentation/rocketmq/ContextAndScope.java rename to instrumentation/rocketmq/rocketmq-client/rocketmq-client-4.8/library/src/main/java/io/opentelemetry/instrumentation/rocketmqclient/v4_8/ContextAndScope.java index 1acebf32f2e1..1adade581392 100644 --- a/instrumentation/rocketmq-client-4.8/library/src/main/java/io/opentelemetry/instrumentation/rocketmq/ContextAndScope.java +++ b/instrumentation/rocketmq/rocketmq-client/rocketmq-client-4.8/library/src/main/java/io/opentelemetry/instrumentation/rocketmqclient/v4_8/ContextAndScope.java @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.instrumentation.rocketmq; +package io.opentelemetry.instrumentation.rocketmqclient.v4_8; import com.google.auto.value.AutoValue; import io.opentelemetry.context.Context; diff --git a/instrumentation/rocketmq-client-4.8/library/src/main/java/io/opentelemetry/instrumentation/rocketmq/MapSetter.java b/instrumentation/rocketmq/rocketmq-client/rocketmq-client-4.8/library/src/main/java/io/opentelemetry/instrumentation/rocketmqclient/v4_8/MapSetter.java similarity index 88% rename from instrumentation/rocketmq-client-4.8/library/src/main/java/io/opentelemetry/instrumentation/rocketmq/MapSetter.java rename to instrumentation/rocketmq/rocketmq-client/rocketmq-client-4.8/library/src/main/java/io/opentelemetry/instrumentation/rocketmqclient/v4_8/MapSetter.java index 5b481d4591ec..c36bb8658745 100644 --- a/instrumentation/rocketmq-client-4.8/library/src/main/java/io/opentelemetry/instrumentation/rocketmq/MapSetter.java +++ b/instrumentation/rocketmq/rocketmq-client/rocketmq-client-4.8/library/src/main/java/io/opentelemetry/instrumentation/rocketmqclient/v4_8/MapSetter.java @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.instrumentation.rocketmq; +package io.opentelemetry.instrumentation.rocketmqclient.v4_8; import io.opentelemetry.context.propagation.TextMapSetter; import org.apache.rocketmq.client.hook.SendMessageContext; diff --git a/instrumentation/rocketmq-client-4.8/library/src/main/java/io/opentelemetry/instrumentation/rocketmq/RocketMqConsumerAttributeGetter.java b/instrumentation/rocketmq/rocketmq-client/rocketmq-client-4.8/library/src/main/java/io/opentelemetry/instrumentation/rocketmqclient/v4_8/RocketMqConsumerAttributeGetter.java similarity index 74% rename from instrumentation/rocketmq-client-4.8/library/src/main/java/io/opentelemetry/instrumentation/rocketmq/RocketMqConsumerAttributeGetter.java rename to instrumentation/rocketmq/rocketmq-client/rocketmq-client-4.8/library/src/main/java/io/opentelemetry/instrumentation/rocketmqclient/v4_8/RocketMqConsumerAttributeGetter.java index 07c4d14bdc18..22a2118f5836 100644 --- a/instrumentation/rocketmq-client-4.8/library/src/main/java/io/opentelemetry/instrumentation/rocketmq/RocketMqConsumerAttributeGetter.java +++ b/instrumentation/rocketmq/rocketmq-client/rocketmq-client-4.8/library/src/main/java/io/opentelemetry/instrumentation/rocketmqclient/v4_8/RocketMqConsumerAttributeGetter.java @@ -3,10 +3,13 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.instrumentation.rocketmq; +package io.opentelemetry.instrumentation.rocketmqclient.v4_8; import io.opentelemetry.instrumentation.api.instrumenter.messaging.MessagingAttributesGetter; import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; +import java.nio.charset.StandardCharsets; +import java.util.Collections; +import java.util.List; import javax.annotation.Nullable; import org.apache.rocketmq.common.message.MessageExt; @@ -75,4 +78,19 @@ public Long messagePayloadCompressedSize(MessageExt request) { public String messageId(MessageExt request, @Nullable Void unused) { return request.getMsgId(); } + + @Override + public List header(MessageExt request, String name) { + String value = request.getProperties().get(name); + if (value != null) { + return Collections.singletonList(value); + } + return Collections.emptyList(); + } + + @Nullable + @Override + public String messagePayload(MessageExt messageExt) { + return new String(messageExt.getBody(), StandardCharsets.UTF_8); + } } diff --git a/instrumentation/rocketmq-client-4.8/library/src/main/java/io/opentelemetry/instrumentation/rocketmq/RocketMqConsumerExperimentalAttributeExtractor.java b/instrumentation/rocketmq/rocketmq-client/rocketmq-client-4.8/library/src/main/java/io/opentelemetry/instrumentation/rocketmqclient/v4_8/RocketMqConsumerExperimentalAttributeExtractor.java similarity index 88% rename from instrumentation/rocketmq-client-4.8/library/src/main/java/io/opentelemetry/instrumentation/rocketmq/RocketMqConsumerExperimentalAttributeExtractor.java rename to instrumentation/rocketmq/rocketmq-client/rocketmq-client-4.8/library/src/main/java/io/opentelemetry/instrumentation/rocketmqclient/v4_8/RocketMqConsumerExperimentalAttributeExtractor.java index b4e29dd18e64..adcfe424e6b4 100644 --- a/instrumentation/rocketmq-client-4.8/library/src/main/java/io/opentelemetry/instrumentation/rocketmq/RocketMqConsumerExperimentalAttributeExtractor.java +++ b/instrumentation/rocketmq/rocketmq-client/rocketmq-client-4.8/library/src/main/java/io/opentelemetry/instrumentation/rocketmqclient/v4_8/RocketMqConsumerExperimentalAttributeExtractor.java @@ -3,12 +3,13 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.instrumentation.rocketmq; +package io.opentelemetry.instrumentation.rocketmqclient.v4_8; import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.api.common.AttributesBuilder; import io.opentelemetry.context.Context; import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor; +import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; import java.net.SocketAddress; import javax.annotation.Nullable; import org.apache.rocketmq.common.message.MessageExt; @@ -17,8 +18,6 @@ enum RocketMqConsumerExperimentalAttributeExtractor implements AttributesExtractor { INSTANCE; - private static final AttributeKey MESSAGING_ROCKETMQ_TAGS = - AttributeKey.stringKey("messaging.rocketmq.tags"); private static final AttributeKey MESSAGING_ROCKETMQ_QUEUE_ID = AttributeKey.longKey("messaging.rocketmq.queue_id"); private static final AttributeKey MESSAGING_ROCKETMQ_QUEUE_OFFSET = @@ -30,7 +29,7 @@ enum RocketMqConsumerExperimentalAttributeExtractor public void onStart(AttributesBuilder attributes, Context parentContext, MessageExt msg) { String tags = msg.getTags(); if (tags != null) { - attributes.put(MESSAGING_ROCKETMQ_TAGS, tags); + attributes.put(SemanticAttributes.MESSAGING_ROCKETMQ_MESSAGE_TAG, tags); } attributes.put(MESSAGING_ROCKETMQ_QUEUE_ID, msg.getQueueId()); attributes.put(MESSAGING_ROCKETMQ_QUEUE_OFFSET, msg.getQueueOffset()); diff --git a/instrumentation/rocketmq-client-4.8/library/src/main/java/io/opentelemetry/instrumentation/rocketmq/RocketMqConsumerInstrumenter.java b/instrumentation/rocketmq/rocketmq-client/rocketmq-client-4.8/library/src/main/java/io/opentelemetry/instrumentation/rocketmqclient/v4_8/RocketMqConsumerInstrumenter.java similarity index 97% rename from instrumentation/rocketmq-client-4.8/library/src/main/java/io/opentelemetry/instrumentation/rocketmq/RocketMqConsumerInstrumenter.java rename to instrumentation/rocketmq/rocketmq-client/rocketmq-client-4.8/library/src/main/java/io/opentelemetry/instrumentation/rocketmqclient/v4_8/RocketMqConsumerInstrumenter.java index 6d5bb13d1f2b..218cc5aa53e7 100644 --- a/instrumentation/rocketmq-client-4.8/library/src/main/java/io/opentelemetry/instrumentation/rocketmq/RocketMqConsumerInstrumenter.java +++ b/instrumentation/rocketmq/rocketmq-client/rocketmq-client-4.8/library/src/main/java/io/opentelemetry/instrumentation/rocketmqclient/v4_8/RocketMqConsumerInstrumenter.java @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.instrumentation.rocketmq; +package io.opentelemetry.instrumentation.rocketmqclient.v4_8; import io.opentelemetry.context.Context; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; diff --git a/instrumentation/rocketmq-client-4.8/library/src/main/java/io/opentelemetry/instrumentation/rocketmq/RocketMqInstrumenterFactory.java b/instrumentation/rocketmq/rocketmq-client/rocketmq-client-4.8/library/src/main/java/io/opentelemetry/instrumentation/rocketmqclient/v4_8/RocketMqInstrumenterFactory.java similarity index 67% rename from instrumentation/rocketmq-client-4.8/library/src/main/java/io/opentelemetry/instrumentation/rocketmq/RocketMqInstrumenterFactory.java rename to instrumentation/rocketmq/rocketmq-client/rocketmq-client-4.8/library/src/main/java/io/opentelemetry/instrumentation/rocketmqclient/v4_8/RocketMqInstrumenterFactory.java index 5fe25521da4f..3b8cfe76a6c4 100644 --- a/instrumentation/rocketmq-client-4.8/library/src/main/java/io/opentelemetry/instrumentation/rocketmq/RocketMqInstrumenterFactory.java +++ b/instrumentation/rocketmq/rocketmq-client/rocketmq-client-4.8/library/src/main/java/io/opentelemetry/instrumentation/rocketmqclient/v4_8/RocketMqInstrumenterFactory.java @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.instrumentation.rocketmq; +package io.opentelemetry.instrumentation.rocketmqclient.v4_8; import static io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor.constant; import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.MESSAGING_OPERATION; @@ -16,7 +16,10 @@ import io.opentelemetry.instrumentation.api.instrumenter.SpanLinksExtractor; import io.opentelemetry.instrumentation.api.instrumenter.messaging.MessageOperation; import io.opentelemetry.instrumentation.api.instrumenter.messaging.MessagingAttributesExtractor; +import io.opentelemetry.instrumentation.api.instrumenter.messaging.MessagingAttributesGetter; import io.opentelemetry.instrumentation.api.instrumenter.messaging.MessagingSpanNameExtractor; +import io.opentelemetry.instrumentation.api.internal.PropagatorBasedSpanLinksExtractor; +import java.util.List; import org.apache.rocketmq.client.hook.SendMessageContext; import org.apache.rocketmq.common.message.MessageExt; @@ -26,6 +29,7 @@ class RocketMqInstrumenterFactory { static Instrumenter createProducerInstrumenter( OpenTelemetry openTelemetry, + List capturedHeaders, boolean captureExperimentalSpanAttributes, boolean propagationEnabled) { @@ -37,21 +41,23 @@ static Instrumenter createProducerInstrumenter( openTelemetry, INSTRUMENTATION_NAME, MessagingSpanNameExtractor.create(getter, operation)) - .addAttributesExtractor(MessagingAttributesExtractor.create(getter, operation)); + .addAttributesExtractor( + buildMessagingAttributesExtractor(getter, operation, capturedHeaders)); if (captureExperimentalSpanAttributes) { instrumenterBuilder.addAttributesExtractor( RocketMqProducerExperimentalAttributeExtractor.INSTANCE); } if (propagationEnabled) { - return instrumenterBuilder.newProducerInstrumenter(MapSetter.INSTANCE); + return instrumenterBuilder.buildProducerInstrumenter(MapSetter.INSTANCE); } else { - return instrumenterBuilder.newInstrumenter(SpanKindExtractor.alwaysProducer()); + return instrumenterBuilder.buildInstrumenter(SpanKindExtractor.alwaysProducer()); } } static RocketMqConsumerInstrumenter createConsumerInstrumenter( OpenTelemetry openTelemetry, + List capturedHeaders, boolean captureExperimentalSpanAttributes, boolean propagationEnabled) { @@ -63,14 +69,23 @@ static RocketMqConsumerInstrumenter createConsumerInstrumenter( return new RocketMqConsumerInstrumenter( createProcessInstrumenter( - openTelemetry, captureExperimentalSpanAttributes, propagationEnabled, false), + openTelemetry, + capturedHeaders, + captureExperimentalSpanAttributes, + propagationEnabled, + false), createProcessInstrumenter( - openTelemetry, captureExperimentalSpanAttributes, propagationEnabled, true), - batchReceiveInstrumenterBuilder.newInstrumenter(SpanKindExtractor.alwaysConsumer())); + openTelemetry, + capturedHeaders, + captureExperimentalSpanAttributes, + propagationEnabled, + true), + batchReceiveInstrumenterBuilder.buildInstrumenter(SpanKindExtractor.alwaysConsumer())); } private static Instrumenter createProcessInstrumenter( OpenTelemetry openTelemetry, + List capturedHeaders, boolean captureExperimentalSpanAttributes, boolean propagationEnabled, boolean batch) { @@ -84,30 +99,42 @@ private static Instrumenter createProcessInstrumenter( INSTRUMENTATION_NAME, MessagingSpanNameExtractor.create(getter, operation)); - builder.addAttributesExtractor(MessagingAttributesExtractor.create(getter, operation)); + builder.addAttributesExtractor( + buildMessagingAttributesExtractor(getter, operation, capturedHeaders)); if (captureExperimentalSpanAttributes) { builder.addAttributesExtractor(RocketMqConsumerExperimentalAttributeExtractor.INSTANCE); } if (!propagationEnabled) { - return builder.newInstrumenter(SpanKindExtractor.alwaysConsumer()); + return builder.buildInstrumenter(SpanKindExtractor.alwaysConsumer()); } if (batch) { SpanLinksExtractor spanLinksExtractor = - SpanLinksExtractor.extractFromRequest( + new PropagatorBasedSpanLinksExtractor<>( openTelemetry.getPropagators().getTextMapPropagator(), TextMapExtractAdapter.INSTANCE); return builder .addSpanLinksExtractor(spanLinksExtractor) - .newInstrumenter(SpanKindExtractor.alwaysConsumer()); + .buildInstrumenter(SpanKindExtractor.alwaysConsumer()); } else { - return builder.newConsumerInstrumenter(TextMapExtractAdapter.INSTANCE); + return builder.buildConsumerInstrumenter(TextMapExtractAdapter.INSTANCE); } } + private static MessagingAttributesExtractor buildMessagingAttributesExtractor( + MessagingAttributesGetter getter, + MessageOperation operation, + List capturedHeaders) { + return MessagingAttributesExtractor.builder(getter, operation) + .setCapturedHeaders(capturedHeaders) + .build(); + } + private static String spanNameOnReceive(Void unused) { return "multiple_sources receive"; } + + private RocketMqInstrumenterFactory() {} } diff --git a/instrumentation/rocketmq-client-4.8/library/src/main/java/io/opentelemetry/instrumentation/rocketmq/RocketMqProducerAttributeGetter.java b/instrumentation/rocketmq/rocketmq-client/rocketmq-client-4.8/library/src/main/java/io/opentelemetry/instrumentation/rocketmqclient/v4_8/RocketMqProducerAttributeGetter.java similarity index 72% rename from instrumentation/rocketmq-client-4.8/library/src/main/java/io/opentelemetry/instrumentation/rocketmq/RocketMqProducerAttributeGetter.java rename to instrumentation/rocketmq/rocketmq-client/rocketmq-client-4.8/library/src/main/java/io/opentelemetry/instrumentation/rocketmqclient/v4_8/RocketMqProducerAttributeGetter.java index 3b0cf0c2d6b8..bfe7326844bb 100644 --- a/instrumentation/rocketmq-client-4.8/library/src/main/java/io/opentelemetry/instrumentation/rocketmq/RocketMqProducerAttributeGetter.java +++ b/instrumentation/rocketmq/rocketmq-client/rocketmq-client-4.8/library/src/main/java/io/opentelemetry/instrumentation/rocketmqclient/v4_8/RocketMqProducerAttributeGetter.java @@ -3,10 +3,13 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.instrumentation.rocketmq; +package io.opentelemetry.instrumentation.rocketmqclient.v4_8; import io.opentelemetry.instrumentation.api.instrumenter.messaging.MessagingAttributesGetter; import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; +import java.nio.charset.StandardCharsets; +import java.util.Collections; +import java.util.List; import javax.annotation.Nullable; import org.apache.rocketmq.client.hook.SendMessageContext; import org.apache.rocketmq.client.producer.SendResult; @@ -80,4 +83,29 @@ public String messageId(SendMessageContext request, @Nullable Void unused) { SendResult sendResult = request.getSendResult(); return sendResult == null ? null : sendResult.getMsgId(); } + + @Override + public List header(SendMessageContext request, String name) { + String value = request.getMessage().getProperties().get(name); + if (value != null) { + return Collections.singletonList(value); + } + return Collections.emptyList(); + } + + @Nullable + @Override + public String messagePayload(SendMessageContext sendMessageContext) { + Message message = sendMessageContext.getMessage(); + if (message == null) { + return null; + } + + byte[] body = message.getBody(); + if (body == null) { + return null; + } + + return new String(body, StandardCharsets.UTF_8); + } } diff --git a/instrumentation/rocketmq-client-4.8/library/src/main/java/io/opentelemetry/instrumentation/rocketmq/RocketMqProducerExperimentalAttributeExtractor.java b/instrumentation/rocketmq/rocketmq-client/rocketmq-client-4.8/library/src/main/java/io/opentelemetry/instrumentation/rocketmqclient/v4_8/RocketMqProducerExperimentalAttributeExtractor.java similarity index 87% rename from instrumentation/rocketmq-client-4.8/library/src/main/java/io/opentelemetry/instrumentation/rocketmq/RocketMqProducerExperimentalAttributeExtractor.java rename to instrumentation/rocketmq/rocketmq-client/rocketmq-client-4.8/library/src/main/java/io/opentelemetry/instrumentation/rocketmqclient/v4_8/RocketMqProducerExperimentalAttributeExtractor.java index 95e90132318d..b0a55ae4a87e 100644 --- a/instrumentation/rocketmq-client-4.8/library/src/main/java/io/opentelemetry/instrumentation/rocketmq/RocketMqProducerExperimentalAttributeExtractor.java +++ b/instrumentation/rocketmq/rocketmq-client/rocketmq-client-4.8/library/src/main/java/io/opentelemetry/instrumentation/rocketmqclient/v4_8/RocketMqProducerExperimentalAttributeExtractor.java @@ -3,12 +3,13 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.instrumentation.rocketmq; +package io.opentelemetry.instrumentation.rocketmqclient.v4_8; import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.api.common.AttributesBuilder; import io.opentelemetry.context.Context; import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor; +import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; import javax.annotation.Nullable; import org.apache.rocketmq.client.hook.SendMessageContext; @@ -16,8 +17,6 @@ enum RocketMqProducerExperimentalAttributeExtractor implements AttributesExtractor { INSTANCE; - private static final AttributeKey MESSAGING_ROCKETMQ_TAGS = - AttributeKey.stringKey("messaging.rocketmq.tags"); private static final AttributeKey MESSAGING_ROCKETMQ_BROKER_ADDRESS = AttributeKey.stringKey("messaging.rocketmq.broker_address"); private static final AttributeKey MESSAGING_ROCKETMQ_SEND_RESULT = @@ -29,7 +28,7 @@ public void onStart( if (request.getMessage() != null) { String tags = request.getMessage().getTags(); if (tags != null) { - attributes.put(MESSAGING_ROCKETMQ_TAGS, tags); + attributes.put(SemanticAttributes.MESSAGING_ROCKETMQ_MESSAGE_TAG, tags); } } String brokerAddr = request.getBrokerAddr(); diff --git a/instrumentation/rocketmq-client-4.8/library/src/main/java/io/opentelemetry/instrumentation/rocketmq/RocketMqTelemetry.java b/instrumentation/rocketmq/rocketmq-client/rocketmq-client-4.8/library/src/main/java/io/opentelemetry/instrumentation/rocketmqclient/v4_8/RocketMqTelemetry.java similarity index 87% rename from instrumentation/rocketmq-client-4.8/library/src/main/java/io/opentelemetry/instrumentation/rocketmq/RocketMqTelemetry.java rename to instrumentation/rocketmq/rocketmq-client/rocketmq-client-4.8/library/src/main/java/io/opentelemetry/instrumentation/rocketmqclient/v4_8/RocketMqTelemetry.java index 7e474db50ce9..750054563d03 100644 --- a/instrumentation/rocketmq-client-4.8/library/src/main/java/io/opentelemetry/instrumentation/rocketmq/RocketMqTelemetry.java +++ b/instrumentation/rocketmq/rocketmq-client/rocketmq-client-4.8/library/src/main/java/io/opentelemetry/instrumentation/rocketmqclient/v4_8/RocketMqTelemetry.java @@ -3,10 +3,11 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.instrumentation.rocketmq; +package io.opentelemetry.instrumentation.rocketmqclient.v4_8; import io.opentelemetry.api.OpenTelemetry; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; +import java.util.List; import org.apache.rocketmq.client.hook.ConsumeMessageHook; import org.apache.rocketmq.client.hook.SendMessageContext; import org.apache.rocketmq.client.hook.SendMessageHook; @@ -31,14 +32,15 @@ public static RocketMqTelemetryBuilder builder(OpenTelemetry openTelemetry) { RocketMqTelemetry( OpenTelemetry openTelemetry, + List capturedHeaders, boolean captureExperimentalSpanAttributes, boolean propagationEnabled) { rocketMqConsumerInstrumenter = RocketMqInstrumenterFactory.createConsumerInstrumenter( - openTelemetry, captureExperimentalSpanAttributes, propagationEnabled); + openTelemetry, capturedHeaders, captureExperimentalSpanAttributes, propagationEnabled); rocketMqProducerInstrumenter = RocketMqInstrumenterFactory.createProducerInstrumenter( - openTelemetry, captureExperimentalSpanAttributes, propagationEnabled); + openTelemetry, capturedHeaders, captureExperimentalSpanAttributes, propagationEnabled); } /** diff --git a/instrumentation/rocketmq-client-4.8/library/src/main/java/io/opentelemetry/instrumentation/rocketmq/RocketMqTelemetryBuilder.java b/instrumentation/rocketmq/rocketmq-client/rocketmq-client-4.8/library/src/main/java/io/opentelemetry/instrumentation/rocketmqclient/v4_8/RocketMqTelemetryBuilder.java similarity index 59% rename from instrumentation/rocketmq-client-4.8/library/src/main/java/io/opentelemetry/instrumentation/rocketmq/RocketMqTelemetryBuilder.java rename to instrumentation/rocketmq/rocketmq-client/rocketmq-client-4.8/library/src/main/java/io/opentelemetry/instrumentation/rocketmqclient/v4_8/RocketMqTelemetryBuilder.java index 6056e728691f..264b564f5f0a 100644 --- a/instrumentation/rocketmq-client-4.8/library/src/main/java/io/opentelemetry/instrumentation/rocketmq/RocketMqTelemetryBuilder.java +++ b/instrumentation/rocketmq/rocketmq-client/rocketmq-client-4.8/library/src/main/java/io/opentelemetry/instrumentation/rocketmqclient/v4_8/RocketMqTelemetryBuilder.java @@ -3,15 +3,20 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.instrumentation.rocketmq; +package io.opentelemetry.instrumentation.rocketmqclient.v4_8; +import static java.util.Collections.emptyList; + +import com.google.errorprone.annotations.CanIgnoreReturnValue; import io.opentelemetry.api.OpenTelemetry; +import java.util.List; /** A builder of {@link RocketMqTelemetry}. */ public final class RocketMqTelemetryBuilder { private final OpenTelemetry openTelemetry; + private List capturedHeaders = emptyList(); private boolean captureExperimentalSpanAttributes; private boolean propagationEnabled = true; @@ -24,6 +29,7 @@ public final class RocketMqTelemetryBuilder { * removed in the future, so only enable this if you know you do not require attributes filled by * this instrumentation to be stable across versions */ + @CanIgnoreReturnValue public RocketMqTelemetryBuilder setCaptureExperimentalSpanAttributes( boolean captureExperimentalSpanAttributes) { this.captureExperimentalSpanAttributes = captureExperimentalSpanAttributes; @@ -33,18 +39,35 @@ public RocketMqTelemetryBuilder setCaptureExperimentalSpanAttributes( /** * Sets whether the trace context should be written from producers / read from consumers for * propagating through messaging. + * + * @deprecated if you have a need for this configuration option please open an issue in the opentelemetry-java-instrumentation + * repository. */ + @Deprecated + @CanIgnoreReturnValue public RocketMqTelemetryBuilder setPropagationEnabled(boolean propagationEnabled) { this.propagationEnabled = propagationEnabled; return this; } + /** + * Configures the messaging headers that will be captured as span attributes. + * + * @param capturedHeaders A list of messaging header names. + */ + @CanIgnoreReturnValue + public RocketMqTelemetryBuilder setCapturedHeaders(List capturedHeaders) { + this.capturedHeaders = capturedHeaders; + return this; + } + /** * Returns a new {@link RocketMqTelemetry} with the settings of this {@link * RocketMqTelemetryBuilder}. */ public RocketMqTelemetry build() { return new RocketMqTelemetry( - openTelemetry, captureExperimentalSpanAttributes, propagationEnabled); + openTelemetry, capturedHeaders, captureExperimentalSpanAttributes, propagationEnabled); } } diff --git a/instrumentation/rocketmq-client-4.8/library/src/main/java/io/opentelemetry/instrumentation/rocketmq/TextMapExtractAdapter.java b/instrumentation/rocketmq/rocketmq-client/rocketmq-client-4.8/library/src/main/java/io/opentelemetry/instrumentation/rocketmqclient/v4_8/TextMapExtractAdapter.java similarity index 90% rename from instrumentation/rocketmq-client-4.8/library/src/main/java/io/opentelemetry/instrumentation/rocketmq/TextMapExtractAdapter.java rename to instrumentation/rocketmq/rocketmq-client/rocketmq-client-4.8/library/src/main/java/io/opentelemetry/instrumentation/rocketmqclient/v4_8/TextMapExtractAdapter.java index 3c9ce2d856b2..c813d9853408 100644 --- a/instrumentation/rocketmq-client-4.8/library/src/main/java/io/opentelemetry/instrumentation/rocketmq/TextMapExtractAdapter.java +++ b/instrumentation/rocketmq/rocketmq-client/rocketmq-client-4.8/library/src/main/java/io/opentelemetry/instrumentation/rocketmqclient/v4_8/TextMapExtractAdapter.java @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.instrumentation.rocketmq; +package io.opentelemetry.instrumentation.rocketmqclient.v4_8; import io.opentelemetry.context.propagation.TextMapGetter; import javax.annotation.Nullable; diff --git a/instrumentation/rocketmq-client-4.8/library/src/main/java/io/opentelemetry/instrumentation/rocketmq/TracingConsumeMessageHookImpl.java b/instrumentation/rocketmq/rocketmq-client/rocketmq-client-4.8/library/src/main/java/io/opentelemetry/instrumentation/rocketmqclient/v4_8/TracingConsumeMessageHookImpl.java similarity index 67% rename from instrumentation/rocketmq-client-4.8/library/src/main/java/io/opentelemetry/instrumentation/rocketmq/TracingConsumeMessageHookImpl.java rename to instrumentation/rocketmq/rocketmq-client/rocketmq-client-4.8/library/src/main/java/io/opentelemetry/instrumentation/rocketmqclient/v4_8/TracingConsumeMessageHookImpl.java index dc05a22980d5..be6e328bdc37 100644 --- a/instrumentation/rocketmq-client-4.8/library/src/main/java/io/opentelemetry/instrumentation/rocketmq/TracingConsumeMessageHookImpl.java +++ b/instrumentation/rocketmq/rocketmq-client/rocketmq-client-4.8/library/src/main/java/io/opentelemetry/instrumentation/rocketmqclient/v4_8/TracingConsumeMessageHookImpl.java @@ -3,14 +3,18 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.instrumentation.rocketmq; +package io.opentelemetry.instrumentation.rocketmqclient.v4_8; import io.opentelemetry.context.Context; +import io.opentelemetry.instrumentation.api.util.VirtualField; import org.apache.rocketmq.client.hook.ConsumeMessageContext; import org.apache.rocketmq.client.hook.ConsumeMessageHook; final class TracingConsumeMessageHookImpl implements ConsumeMessageHook { + private static final VirtualField contextAndScopeField = + VirtualField.find(ConsumeMessageContext.class, ContextAndScope.class); + private final RocketMqConsumerInstrumenter instrumenter; TracingConsumeMessageHookImpl(RocketMqConsumerInstrumenter instrumenter) { @@ -30,12 +34,13 @@ public void consumeMessageBefore(ConsumeMessageContext context) { Context parentContext = Context.current(); Context newContext = instrumenter.start(parentContext, context.getMsgList()); - // it's safe to store the scope in the rocketMq trace context, both before() and after() methods - // are always called from the same thread; see: + // it's safe to store the scope in the rocketMq message context, both before() and after() + // methods are always called from the same thread; see: // - ConsumeMessageConcurrentlyService$ConsumeRequest#run() // - ConsumeMessageOrderlyService$ConsumeRequest#run() if (newContext != parentContext) { - context.setMqTraceContext(ContextAndScope.create(newContext, newContext.makeCurrent())); + contextAndScopeField.set( + context, ContextAndScope.create(newContext, newContext.makeCurrent())); } } @@ -44,8 +49,8 @@ public void consumeMessageAfter(ConsumeMessageContext context) { if (context == null || context.getMsgList() == null || context.getMsgList().isEmpty()) { return; } - if (context.getMqTraceContext() instanceof ContextAndScope) { - ContextAndScope contextAndScope = (ContextAndScope) context.getMqTraceContext(); + ContextAndScope contextAndScope = contextAndScopeField.get(context); + if (contextAndScope != null) { contextAndScope.close(); instrumenter.end(contextAndScope.getContext(), context.getMsgList()); } diff --git a/instrumentation/rocketmq-client-4.8/library/src/main/java/io/opentelemetry/instrumentation/rocketmq/TracingSendMessageHookImpl.java b/instrumentation/rocketmq/rocketmq-client/rocketmq-client-4.8/library/src/main/java/io/opentelemetry/instrumentation/rocketmqclient/v4_8/TracingSendMessageHookImpl.java similarity index 74% rename from instrumentation/rocketmq-client-4.8/library/src/main/java/io/opentelemetry/instrumentation/rocketmq/TracingSendMessageHookImpl.java rename to instrumentation/rocketmq/rocketmq-client/rocketmq-client-4.8/library/src/main/java/io/opentelemetry/instrumentation/rocketmqclient/v4_8/TracingSendMessageHookImpl.java index 9d027d7c3dac..3dafc3e0e3c6 100644 --- a/instrumentation/rocketmq-client-4.8/library/src/main/java/io/opentelemetry/instrumentation/rocketmq/TracingSendMessageHookImpl.java +++ b/instrumentation/rocketmq/rocketmq-client/rocketmq-client-4.8/library/src/main/java/io/opentelemetry/instrumentation/rocketmqclient/v4_8/TracingSendMessageHookImpl.java @@ -3,15 +3,19 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.instrumentation.rocketmq; +package io.opentelemetry.instrumentation.rocketmqclient.v4_8; import io.opentelemetry.context.Context; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; +import io.opentelemetry.instrumentation.api.util.VirtualField; import org.apache.rocketmq.client.hook.SendMessageContext; import org.apache.rocketmq.client.hook.SendMessageHook; final class TracingSendMessageHookImpl implements SendMessageHook { + private static final VirtualField contextField = + VirtualField.find(SendMessageContext.class, Context.class); + private final Instrumenter instrumenter; TracingSendMessageHookImpl(Instrumenter instrumenter) { @@ -32,7 +36,7 @@ public void sendMessageBefore(SendMessageContext context) { if (!instrumenter.shouldStart(parentContext, context)) { return; } - context.setMqTraceContext(instrumenter.start(parentContext, context)); + contextField.set(context, instrumenter.start(parentContext, context)); } @Override @@ -40,9 +44,9 @@ public void sendMessageAfter(SendMessageContext context) { if (context == null) { return; } - if (context.getMqTraceContext() instanceof Context + Context otelContext = contextField.get(context); + if (otelContext != null && (context.getSendResult() != null || context.getException() != null)) { - Context otelContext = (Context) context.getMqTraceContext(); instrumenter.end(otelContext, context, null, context.getException()); } } diff --git a/instrumentation/rocketmq-client-4.8/library/src/test/groovy/io/opentelemetry/instrumentation/rocketmq/RocketMqClientTest.groovy b/instrumentation/rocketmq/rocketmq-client/rocketmq-client-4.8/library/src/test/groovy/io/opentelemetry/instrumentation/rocketmqclient/v4_8/RocketMqClientTest.groovy similarity index 79% rename from instrumentation/rocketmq-client-4.8/library/src/test/groovy/io/opentelemetry/instrumentation/rocketmq/RocketMqClientTest.groovy rename to instrumentation/rocketmq/rocketmq-client/rocketmq-client-4.8/library/src/test/groovy/io/opentelemetry/instrumentation/rocketmqclient/v4_8/RocketMqClientTest.groovy index 2a4e774a34c7..50e9f3f781b6 100644 --- a/instrumentation/rocketmq-client-4.8/library/src/test/groovy/io/opentelemetry/instrumentation/rocketmq/RocketMqClientTest.groovy +++ b/instrumentation/rocketmq/rocketmq-client/rocketmq-client-4.8/library/src/test/groovy/io/opentelemetry/instrumentation/rocketmqclient/v4_8/RocketMqClientTest.groovy @@ -3,18 +3,21 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.instrumentation.rocketmq +package io.opentelemetry.instrumentation.rocketmqclient.v4_8 import io.opentelemetry.instrumentation.test.LibraryTestTrait import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer import org.apache.rocketmq.client.producer.DefaultMQProducer +import static java.util.Collections.singletonList + class RocketMqClientTest extends AbstractRocketMqClientTest implements LibraryTestTrait { @Override void configureMQProducer(DefaultMQProducer producer) { producer.getDefaultMQProducerImpl().registerSendMessageHook(RocketMqTelemetry.builder(openTelemetry) + .setCapturedHeaders(singletonList("test-message-header")) .setCaptureExperimentalSpanAttributes(true) .build().newTracingSendMessageHook()) } @@ -22,6 +25,7 @@ class RocketMqClientTest extends AbstractRocketMqClientTest implements LibraryTe @Override void configureMQPushConsumer(DefaultMQPushConsumer consumer) { consumer.getDefaultMQPushConsumerImpl().registerConsumeMessageHook(RocketMqTelemetry.builder(openTelemetry) + .setCapturedHeaders(singletonList("test-message-header")) .setCaptureExperimentalSpanAttributes(true) .build().newTracingConsumeMessageHook()) } diff --git a/instrumentation/rocketmq-client-4.8/testing/build.gradle.kts b/instrumentation/rocketmq/rocketmq-client/rocketmq-client-4.8/testing/build.gradle.kts similarity index 100% rename from instrumentation/rocketmq-client-4.8/testing/build.gradle.kts rename to instrumentation/rocketmq/rocketmq-client/rocketmq-client-4.8/testing/build.gradle.kts diff --git a/instrumentation/rocketmq-client-4.8/testing/src/main/groovy/io/opentelemetry/instrumentation/rocketmq/AbstractRocketMqClientTest.groovy b/instrumentation/rocketmq/rocketmq-client/rocketmq-client-4.8/testing/src/main/groovy/io/opentelemetry/instrumentation/rocketmqclient/v4_8/AbstractRocketMqClientTest.groovy similarity index 73% rename from instrumentation/rocketmq-client-4.8/testing/src/main/groovy/io/opentelemetry/instrumentation/rocketmq/AbstractRocketMqClientTest.groovy rename to instrumentation/rocketmq/rocketmq-client/rocketmq-client-4.8/testing/src/main/groovy/io/opentelemetry/instrumentation/rocketmqclient/v4_8/AbstractRocketMqClientTest.groovy index 09708ac91f23..4bbc3d63c46e 100644 --- a/instrumentation/rocketmq-client-4.8/testing/src/main/groovy/io/opentelemetry/instrumentation/rocketmq/AbstractRocketMqClientTest.groovy +++ b/instrumentation/rocketmq/rocketmq-client/rocketmq-client-4.8/testing/src/main/groovy/io/opentelemetry/instrumentation/rocketmqclient/v4_8/AbstractRocketMqClientTest.groovy @@ -3,11 +3,12 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.instrumentation.rocketmq +package io.opentelemetry.instrumentation.rocketmqclient.v4_8 -import base.BaseConf +import io.opentelemetry.instrumentation.rocketmqclient.v4_8.base.BaseConf import io.opentelemetry.instrumentation.test.InstrumentationSpecification import io.opentelemetry.semconv.trace.attributes.SemanticAttributes + import java.util.concurrent.CompletableFuture import java.util.concurrent.TimeUnit import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer @@ -61,6 +62,11 @@ abstract class AbstractRocketMqClientTest extends InstrumentationSpecification { configureMQProducer(producer) consumer = BaseConf.getConsumer(BaseConf.nsAddr, sharedTopic, "*", tracingMessageListener) configureMQPushConsumer(consumer) + + // for RocketMQ 5.x wait a bit to ensure that consumer is properly started up + if (Boolean.getBoolean("testLatestDeps")) { + Thread.sleep(30_000) + } } def cleanupSpec() { @@ -102,9 +108,10 @@ abstract class AbstractRocketMqClientTest extends InstrumentationSpecification { "$SemanticAttributes.MESSAGING_DESTINATION" sharedTopic "$SemanticAttributes.MESSAGING_DESTINATION_KIND" "topic" "$SemanticAttributes.MESSAGING_MESSAGE_ID" String - "messaging.rocketmq.tags" "TagA" + "$SemanticAttributes.MESSAGING_ROCKETMQ_MESSAGE_TAG" "TagA" "messaging.rocketmq.broker_address" String "messaging.rocketmq.send_result" "SEND_OK" + "messaging.payload" String } } span(1) { @@ -118,10 +125,11 @@ abstract class AbstractRocketMqClientTest extends InstrumentationSpecification { "$SemanticAttributes.MESSAGING_OPERATION" "process" "$SemanticAttributes.MESSAGING_MESSAGE_PAYLOAD_SIZE_BYTES" Long "$SemanticAttributes.MESSAGING_MESSAGE_ID" String - "messaging.rocketmq.tags" "TagA" + "$SemanticAttributes.MESSAGING_ROCKETMQ_MESSAGE_TAG" "TagA" "messaging.rocketmq.broker_address" String "messaging.rocketmq.queue_id" Long "messaging.rocketmq.queue_offset" Long + "messaging.payload" String } } span(2) { @@ -158,9 +166,10 @@ abstract class AbstractRocketMqClientTest extends InstrumentationSpecification { "$SemanticAttributes.MESSAGING_DESTINATION" sharedTopic "$SemanticAttributes.MESSAGING_DESTINATION_KIND" "topic" "$SemanticAttributes.MESSAGING_MESSAGE_ID" String - "messaging.rocketmq.tags" "TagA" + "$SemanticAttributes.MESSAGING_ROCKETMQ_MESSAGE_TAG" "TagA" "messaging.rocketmq.broker_address" String "messaging.rocketmq.send_result" "SEND_OK" + "messaging.payload" String } } span(2) { @@ -174,10 +183,11 @@ abstract class AbstractRocketMqClientTest extends InstrumentationSpecification { "$SemanticAttributes.MESSAGING_OPERATION" "process" "$SemanticAttributes.MESSAGING_MESSAGE_PAYLOAD_SIZE_BYTES" Long "$SemanticAttributes.MESSAGING_MESSAGE_ID" String - "messaging.rocketmq.tags" "TagA" + "$SemanticAttributes.MESSAGING_ROCKETMQ_MESSAGE_TAG" "TagA" "messaging.rocketmq.broker_address" String "messaging.rocketmq.queue_id" Long "messaging.rocketmq.queue_offset" Long + "messaging.payload" String } } span(3) { @@ -238,6 +248,7 @@ abstract class AbstractRocketMqClientTest extends InstrumentationSpecification { "$SemanticAttributes.MESSAGING_MESSAGE_ID" String "messaging.rocketmq.broker_address" String "messaging.rocketmq.send_result" "SEND_OK" + "messaging.payload" String } } } @@ -261,10 +272,11 @@ abstract class AbstractRocketMqClientTest extends InstrumentationSpecification { "$SemanticAttributes.MESSAGING_OPERATION" "process" "$SemanticAttributes.MESSAGING_MESSAGE_PAYLOAD_SIZE_BYTES" Long "$SemanticAttributes.MESSAGING_MESSAGE_ID" String - "messaging.rocketmq.tags" "TagA" + "$SemanticAttributes.MESSAGING_ROCKETMQ_MESSAGE_TAG" "TagA" "messaging.rocketmq.broker_address" String "messaging.rocketmq.queue_id" Long "messaging.rocketmq.queue_offset" Long + "messaging.payload" String } childOf span(0) hasLink producerSpan @@ -279,10 +291,11 @@ abstract class AbstractRocketMqClientTest extends InstrumentationSpecification { "$SemanticAttributes.MESSAGING_OPERATION" "process" "$SemanticAttributes.MESSAGING_MESSAGE_PAYLOAD_SIZE_BYTES" Long "$SemanticAttributes.MESSAGING_MESSAGE_ID" String - "messaging.rocketmq.tags" "TagB" + "$SemanticAttributes.MESSAGING_ROCKETMQ_MESSAGE_TAG" "TagB" "messaging.rocketmq.broker_address" String "messaging.rocketmq.queue_id" Long "messaging.rocketmq.queue_offset" Long + "messaging.payload" String } childOf span(0) hasLink producerSpan @@ -295,4 +308,66 @@ abstract class AbstractRocketMqClientTest extends InstrumentationSpecification { } } } + + def "capture message header as span attributes"() { + when: + runWithSpan("parent") { + def msg = new Message(sharedTopic, "TagA", ("Hello RocketMQ").getBytes(RemotingHelper.DEFAULT_CHARSET)) + msg.putUserProperty("test-message-header", "test") + SendResult sendResult = producer.send(msg) + assert sendResult.sendStatus == SendStatus.SEND_OK + } + // waiting longer than assertTraces below does on its own because of CI flakiness + tracingMessageListener.waitForMessages() + + then: + assertTraces(1) { + trace(0, 4) { + span(0) { + name "parent" + kind INTERNAL + } + span(1) { + name sharedTopic + " send" + kind PRODUCER + childOf span(0) + attributes { + "$SemanticAttributes.MESSAGING_SYSTEM" "rocketmq" + "$SemanticAttributes.MESSAGING_DESTINATION" sharedTopic + "$SemanticAttributes.MESSAGING_DESTINATION_KIND" "topic" + "$SemanticAttributes.MESSAGING_MESSAGE_ID" String + "$SemanticAttributes.MESSAGING_ROCKETMQ_MESSAGE_TAG" "TagA" + "messaging.rocketmq.broker_address" String + "messaging.rocketmq.send_result" "SEND_OK" + "messaging.header.test_message_header" { it == ["test"] } + "messaging.payload" "Hello RocketMQ" + } + } + span(2) { + name sharedTopic + " process" + kind CONSUMER + childOf span(1) + attributes { + "$SemanticAttributes.MESSAGING_SYSTEM" "rocketmq" + "$SemanticAttributes.MESSAGING_DESTINATION" sharedTopic + "$SemanticAttributes.MESSAGING_DESTINATION_KIND" "topic" + "$SemanticAttributes.MESSAGING_OPERATION" "process" + "$SemanticAttributes.MESSAGING_MESSAGE_PAYLOAD_SIZE_BYTES" Long + "$SemanticAttributes.MESSAGING_MESSAGE_ID" String + "$SemanticAttributes.MESSAGING_ROCKETMQ_MESSAGE_TAG" "TagA" + "messaging.rocketmq.broker_address" String + "messaging.rocketmq.queue_id" Long + "messaging.rocketmq.queue_offset" Long + "messaging.header.test_message_header" { it == ["test"] } + "messaging.payload" "Hello RocketMQ" + } + } + span(3) { + name "messageListener" + kind INTERNAL + childOf span(2) + } + } + } + } } diff --git a/instrumentation/rocketmq-client-4.8/testing/src/main/groovy/io/opentelemetry/instrumentation/rocketmq/TracingMessageListener.groovy b/instrumentation/rocketmq/rocketmq-client/rocketmq-client-4.8/testing/src/main/groovy/io/opentelemetry/instrumentation/rocketmqclient/v4_8/TracingMessageListener.groovy similarity index 95% rename from instrumentation/rocketmq-client-4.8/testing/src/main/groovy/io/opentelemetry/instrumentation/rocketmq/TracingMessageListener.groovy rename to instrumentation/rocketmq/rocketmq-client/rocketmq-client-4.8/testing/src/main/groovy/io/opentelemetry/instrumentation/rocketmqclient/v4_8/TracingMessageListener.groovy index 21a67c509cc4..b2fa0781bed5 100644 --- a/instrumentation/rocketmq-client-4.8/testing/src/main/groovy/io/opentelemetry/instrumentation/rocketmq/TracingMessageListener.groovy +++ b/instrumentation/rocketmq/rocketmq-client/rocketmq-client-4.8/testing/src/main/groovy/io/opentelemetry/instrumentation/rocketmqclient/v4_8/TracingMessageListener.groovy @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.instrumentation.rocketmq +package io.opentelemetry.instrumentation.rocketmqclient.v4_8 import java.util.concurrent.TimeUnit import org.apache.rocketmq.client.consumer.listener.ConsumeOrderlyContext diff --git a/instrumentation/rocketmq-client-4.8/testing/src/main/java/base/BaseConf.java b/instrumentation/rocketmq/rocketmq-client/rocketmq-client-4.8/testing/src/main/java/io/opentelemetry/instrumentation/rocketmqclient/v4_8/base/BaseConf.java similarity index 97% rename from instrumentation/rocketmq-client-4.8/testing/src/main/java/base/BaseConf.java rename to instrumentation/rocketmq/rocketmq-client/rocketmq-client-4.8/testing/src/main/java/io/opentelemetry/instrumentation/rocketmqclient/v4_8/base/BaseConf.java index 0230ab698609..5774ebad177c 100644 --- a/instrumentation/rocketmq-client-4.8/testing/src/main/java/base/BaseConf.java +++ b/instrumentation/rocketmq/rocketmq-client/rocketmq-client-4.8/testing/src/main/java/io/opentelemetry/instrumentation/rocketmqclient/v4_8/base/BaseConf.java @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package base; +package io.opentelemetry.instrumentation.rocketmqclient.v4_8.base; import java.util.UUID; import org.apache.rocketmq.broker.BrokerController; diff --git a/instrumentation/rocketmq-client-4.8/testing/src/main/java/base/IntegrationTestBase.java b/instrumentation/rocketmq/rocketmq-client/rocketmq-client-4.8/testing/src/main/java/io/opentelemetry/instrumentation/rocketmqclient/v4_8/base/IntegrationTestBase.java similarity index 80% rename from instrumentation/rocketmq-client-4.8/testing/src/main/java/base/IntegrationTestBase.java rename to instrumentation/rocketmq/rocketmq-client/rocketmq-client-4.8/testing/src/main/java/io/opentelemetry/instrumentation/rocketmqclient/v4_8/base/IntegrationTestBase.java index 53107a397912..0cde2926b4cd 100644 --- a/instrumentation/rocketmq-client-4.8/testing/src/main/java/base/IntegrationTestBase.java +++ b/instrumentation/rocketmq/rocketmq-client/rocketmq-client-4.8/testing/src/main/java/io/opentelemetry/instrumentation/rocketmqclient/v4_8/base/IntegrationTestBase.java @@ -3,16 +3,21 @@ * SPDX-License-Identifier: Apache-2.0 */ -package base; +package io.opentelemetry.instrumentation.rocketmqclient.v4_8.base; + +import static java.util.Collections.emptyMap; import io.opentelemetry.instrumentation.test.utils.PortUtils; import java.io.File; import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; import java.util.List; +import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; import org.apache.rocketmq.broker.BrokerController; import org.apache.rocketmq.common.BrokerConfig; @@ -23,7 +28,6 @@ import org.apache.rocketmq.remoting.netty.NettyClientConfig; import org.apache.rocketmq.remoting.netty.NettyServerConfig; import org.apache.rocketmq.store.config.MessageStoreConfig; -import org.apache.rocketmq.test.util.MQAdmin; import org.junit.Assert; public final class IntegrationTestBase { @@ -128,7 +132,31 @@ public static BrokerController createAndStartBroker( } public static void initTopic(String topic, String nsAddr, String clusterName) { - MQAdmin.createTopic(nsAddr, clusterName, topic, 20); + try { + // RocketMQ 4.x + Class mqAdmin = Class.forName("org.apache.rocketmq.test.util.MQAdmin"); + Method createTopic = + mqAdmin.getMethod("createTopic", String.class, String.class, String.class, int.class); + createTopic.invoke(null, nsAddr, clusterName, topic, 20); + } catch (ClassNotFoundException + | InvocationTargetException + | NoSuchMethodException + | IllegalAccessException e) { + + // RocketMQ 5.x + try { + Class mqAdmin = Class.forName("org.apache.rocketmq.test.util.MQAdminTestUtils"); + Method createTopic = + mqAdmin.getMethod( + "createTopic", String.class, String.class, String.class, int.class, Map.class); + createTopic.invoke(null, nsAddr, clusterName, topic, 20, emptyMap()); + } catch (ClassNotFoundException + | InvocationTargetException + | NoSuchMethodException + | IllegalAccessException ex) { + throw new LinkageError("Could not initialize topic", ex); + } + } } private IntegrationTestBase() {} 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 new file mode 100644 index 000000000000..4efaaaad3845 --- /dev/null +++ b/instrumentation/rocketmq/rocketmq-client/rocketmq-client-5.0/javaagent/build.gradle.kts @@ -0,0 +1,18 @@ +plugins { + id("otel.javaagent-instrumentation") +} + +muzzle { + pass { + group.set("org.apache.rocketmq") + module.set("rocketmq-client-java") + versions.set("[5.0.0,)") + assertInverse.set(true) + } +} + +dependencies { + library("org.apache.rocketmq:rocketmq-client-java:5.0.0") + + testImplementation(project(":instrumentation:rocketmq:rocketmq-client:rocketmq-client-5.0:testing")) +} diff --git a/instrumentation/rocketmq/rocketmq-client/rocketmq-client-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rocketmqclient/v5_0/FutureConverter.java b/instrumentation/rocketmq/rocketmq-client/rocketmq-client-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rocketmqclient/v5_0/FutureConverter.java new file mode 100644 index 000000000000..bf0dce0b6e92 --- /dev/null +++ b/instrumentation/rocketmq/rocketmq-client/rocketmq-client-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rocketmqclient/v5_0/FutureConverter.java @@ -0,0 +1,51 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.rocketmqclient.v5_0; + +import java.util.ArrayList; +import java.util.List; +import org.apache.rocketmq.shaded.com.google.common.util.concurrent.FutureCallback; +import org.apache.rocketmq.shaded.com.google.common.util.concurrent.Futures; +import org.apache.rocketmq.shaded.com.google.common.util.concurrent.MoreExecutors; +import org.apache.rocketmq.shaded.com.google.common.util.concurrent.SettableFuture; + +/** Future converter, which covert future of list into list of future. */ +public class FutureConverter { + private FutureConverter() {} + + public static List> convert(SettableFuture> future, int num) { + List> futures = new ArrayList<>(num); + for (int i = 0; i < num; i++) { + SettableFuture f = SettableFuture.create(); + futures.add(f); + } + ListFutureCallback futureCallback = new ListFutureCallback<>(futures); + Futures.addCallback(future, futureCallback, MoreExecutors.directExecutor()); + return futures; + } + + public static class ListFutureCallback implements FutureCallback> { + private final List> futures; + + public ListFutureCallback(List> futures) { + this.futures = futures; + } + + @Override + public void onSuccess(List result) { + for (int i = 0; i < result.size(); i++) { + futures.get(i).set(result.get(i)); + } + } + + @Override + public void onFailure(Throwable t) { + for (SettableFuture future : futures) { + future.setException(t); + } + } + } +} diff --git a/instrumentation/rocketmq/rocketmq-client/rocketmq-client-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rocketmqclient/v5_0/MapSetter.java b/instrumentation/rocketmq/rocketmq-client/rocketmq-client-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rocketmqclient/v5_0/MapSetter.java new file mode 100644 index 000000000000..1eeaefd08558 --- /dev/null +++ b/instrumentation/rocketmq/rocketmq-client/rocketmq-client-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rocketmqclient/v5_0/MapSetter.java @@ -0,0 +1,29 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.rocketmqclient.v5_0; + +import io.opentelemetry.context.propagation.TextMapSetter; +import java.util.HashMap; +import java.util.Map; +import javax.annotation.Nullable; +import org.apache.rocketmq.client.java.message.PublishingMessageImpl; + +enum MapSetter implements TextMapSetter { + INSTANCE; + + @Override + public void set(@Nullable PublishingMessageImpl message, String key, String value) { + if (message == null) { + return; + } + Map extraProperties = VirtualFieldStore.getExtraPropertiesByMessage(message); + if (extraProperties == null) { + extraProperties = new HashMap<>(); + VirtualFieldStore.setExtraPropertiesByMessage(message, extraProperties); + } + extraProperties.put(key, value); + } +} diff --git a/instrumentation/rocketmq/rocketmq-client/rocketmq-client-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rocketmqclient/v5_0/RocketMqInstrumentationModule.java b/instrumentation/rocketmq/rocketmq-client/rocketmq-client-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rocketmqclient/v5_0/RocketMqInstrumentationModule.java new file mode 100644 index 000000000000..9c04ced7cad6 --- /dev/null +++ b/instrumentation/rocketmq/rocketmq-client/rocketmq-client-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rocketmqclient/v5_0/RocketMqInstrumentationModule.java @@ -0,0 +1,26 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.rocketmqclient.v5_0; + +import static java.util.Arrays.asList; + +import com.google.auto.service.AutoService; +import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule; +import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; +import java.util.List; + +@AutoService(InstrumentationModule.class) +public final class RocketMqInstrumentationModule extends InstrumentationModule { + public RocketMqInstrumentationModule() { + super("rocketmq-client", "rocketmq-client-5.0"); + } + + @Override + public List typeInstrumentations() { + return asList( + new RocketMqPublishingMessageImplInstrumentation(), new RocketMqProducerInstrumentation()); + } +} diff --git a/instrumentation/rocketmq/rocketmq-client/rocketmq-client-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rocketmqclient/v5_0/RocketMqInstrumenterFactory.java b/instrumentation/rocketmq/rocketmq-client/rocketmq-client-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rocketmqclient/v5_0/RocketMqInstrumenterFactory.java new file mode 100644 index 000000000000..5d6fd7efdb7e --- /dev/null +++ b/instrumentation/rocketmq/rocketmq-client/rocketmq-client-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rocketmqclient/v5_0/RocketMqInstrumenterFactory.java @@ -0,0 +1,60 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.rocketmqclient.v5_0; + +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.api.trace.StatusCode; +import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor; +import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; +import io.opentelemetry.instrumentation.api.instrumenter.InstrumenterBuilder; +import io.opentelemetry.instrumentation.api.instrumenter.messaging.MessageOperation; +import io.opentelemetry.instrumentation.api.instrumenter.messaging.MessagingAttributesExtractor; +import io.opentelemetry.instrumentation.api.instrumenter.messaging.MessagingAttributesGetter; +import io.opentelemetry.instrumentation.api.instrumenter.messaging.MessagingSpanNameExtractor; +import java.util.List; +import org.apache.rocketmq.client.java.impl.producer.SendReceiptImpl; +import org.apache.rocketmq.client.java.message.PublishingMessageImpl; + +final class RocketMqInstrumenterFactory { + private static final String INSTRUMENTATION_NAME = "io.opentelemetry.rocketmq-client-5.0"; + + private RocketMqInstrumenterFactory() {} + + public static Instrumenter createProducerInstrumenter( + OpenTelemetry openTelemetry, List capturedHeaders) { + + RocketMqProducerAttributeGetter getter = RocketMqProducerAttributeGetter.INSTANCE; + MessageOperation operation = MessageOperation.SEND; + + AttributesExtractor attributesExtractor = + buildMessagingAttributesExtractor(getter, operation, capturedHeaders); + + InstrumenterBuilder instrumenterBuilder = + Instrumenter.builder( + openTelemetry, + INSTRUMENTATION_NAME, + MessagingSpanNameExtractor.create(getter, operation)) + .addAttributesExtractor(attributesExtractor) + .addAttributesExtractor(RocketMqProducerAttributeExtractor.INSTANCE) + .setSpanStatusExtractor( + (spanStatusBuilder, message, sendReceipt, error) -> { + if (null != error) { + spanStatusBuilder.setStatus(StatusCode.ERROR); + } + }); + + return instrumenterBuilder.buildProducerInstrumenter(MapSetter.INSTANCE); + } + + private static MessagingAttributesExtractor buildMessagingAttributesExtractor( + MessagingAttributesGetter getter, + MessageOperation operation, + List capturedHeaders) { + return MessagingAttributesExtractor.builder(getter, operation) + .setCapturedHeaders(capturedHeaders) + .build(); + } +} diff --git a/instrumentation/rocketmq/rocketmq-client/rocketmq-client-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rocketmqclient/v5_0/RocketMqProducerAttributeExtractor.java b/instrumentation/rocketmq/rocketmq-client/rocketmq-client-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rocketmqclient/v5_0/RocketMqProducerAttributeExtractor.java new file mode 100644 index 000000000000..aa322d92d081 --- /dev/null +++ b/instrumentation/rocketmq/rocketmq-client/rocketmq-client-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rocketmqclient/v5_0/RocketMqProducerAttributeExtractor.java @@ -0,0 +1,55 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.rocketmqclient.v5_0; + +import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.MESSAGING_ROCKETMQ_MESSAGE_KEYS; +import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.MESSAGING_ROCKETMQ_MESSAGE_TAG; +import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.MESSAGING_ROCKETMQ_MESSAGE_TYPE; +import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.MessagingRocketmqMessageTypeValues.DELAY; +import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.MessagingRocketmqMessageTypeValues.FIFO; +import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.MessagingRocketmqMessageTypeValues.NORMAL; +import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.MessagingRocketmqMessageTypeValues.TRANSACTION; + +import io.opentelemetry.api.common.AttributesBuilder; +import io.opentelemetry.context.Context; +import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor; +import java.util.ArrayList; +import javax.annotation.Nullable; +import org.apache.rocketmq.client.java.impl.producer.SendReceiptImpl; +import org.apache.rocketmq.client.java.message.PublishingMessageImpl; + +enum RocketMqProducerAttributeExtractor + implements AttributesExtractor { + INSTANCE; + + @Override + public void onStart( + AttributesBuilder attributes, Context parentContext, PublishingMessageImpl message) { + message.getTag().ifPresent(s -> attributes.put(MESSAGING_ROCKETMQ_MESSAGE_TAG, s)); + attributes.put(MESSAGING_ROCKETMQ_MESSAGE_KEYS, new ArrayList<>(message.getKeys())); + switch (message.getMessageType()) { + case FIFO: + attributes.put(MESSAGING_ROCKETMQ_MESSAGE_TYPE, FIFO); + break; + case DELAY: + attributes.put(MESSAGING_ROCKETMQ_MESSAGE_TYPE, DELAY); + break; + case TRANSACTION: + attributes.put(MESSAGING_ROCKETMQ_MESSAGE_TYPE, TRANSACTION); + break; + default: + attributes.put(MESSAGING_ROCKETMQ_MESSAGE_TYPE, NORMAL); + } + } + + @Override + public void onEnd( + AttributesBuilder attributes, + Context context, + PublishingMessageImpl message, + @Nullable SendReceiptImpl sendReceipt, + @Nullable Throwable error) {} +} diff --git a/instrumentation/rocketmq/rocketmq-client/rocketmq-client-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rocketmqclient/v5_0/RocketMqProducerAttributeGetter.java b/instrumentation/rocketmq/rocketmq-client/rocketmq-client-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rocketmqclient/v5_0/RocketMqProducerAttributeGetter.java new file mode 100644 index 000000000000..13fc3252da92 --- /dev/null +++ b/instrumentation/rocketmq/rocketmq-client/rocketmq-client-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rocketmqclient/v5_0/RocketMqProducerAttributeGetter.java @@ -0,0 +1,93 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.rocketmqclient.v5_0; + +import io.opentelemetry.instrumentation.api.instrumenter.messaging.MessagingAttributesGetter; +import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; +import java.util.Collections; +import java.util.List; +import javax.annotation.Nullable; +import org.apache.rocketmq.client.java.impl.producer.SendReceiptImpl; +import org.apache.rocketmq.client.java.message.PublishingMessageImpl; + +enum RocketMqProducerAttributeGetter + implements MessagingAttributesGetter { + INSTANCE; + + @Nullable + @Override + public String system(PublishingMessageImpl message) { + return "rocketmq"; + } + + @Nullable + @Override + public String destinationKind(PublishingMessageImpl message) { + return SemanticAttributes.MessagingDestinationKindValues.TOPIC; + } + + @Nullable + @Override + public String destination(PublishingMessageImpl message) { + return message.getTopic(); + } + + @Override + public boolean temporaryDestination(PublishingMessageImpl message) { + return false; + } + + @Nullable + @Override + public String protocol(PublishingMessageImpl message) { + return null; + } + + @Nullable + @Override + public String protocolVersion(PublishingMessageImpl message) { + return null; + } + + @Nullable + @Override + public String url(PublishingMessageImpl message) { + return null; + } + + @Nullable + @Override + public String conversationId(PublishingMessageImpl message) { + return null; + } + + @Nullable + @Override + public Long messagePayloadSize(PublishingMessageImpl message) { + return (long) message.getBody().remaining(); + } + + @Nullable + @Override + public Long messagePayloadCompressedSize(PublishingMessageImpl message) { + return null; + } + + @Nullable + @Override + public String messageId(PublishingMessageImpl message, @Nullable SendReceiptImpl sendReceipt) { + return message.getMessageId().toString(); + } + + @Override + public List header(PublishingMessageImpl message, String name) { + String value = message.getProperties().get(name); + if (value != null) { + return Collections.singletonList(value); + } + return Collections.emptyList(); + } +} diff --git a/instrumentation/rocketmq/rocketmq-client/rocketmq-client-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rocketmqclient/v5_0/RocketMqProducerInstrumentation.java b/instrumentation/rocketmq/rocketmq-client/rocketmq-client-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rocketmqclient/v5_0/RocketMqProducerInstrumentation.java new file mode 100644 index 000000000000..40602994ce68 --- /dev/null +++ b/instrumentation/rocketmq/rocketmq-client/rocketmq-client-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rocketmqclient/v5_0/RocketMqProducerInstrumentation.java @@ -0,0 +1,119 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.rocketmqclient.v5_0; + +import static net.bytebuddy.matcher.ElementMatchers.isMethod; +import static net.bytebuddy.matcher.ElementMatchers.isPrivate; +import static net.bytebuddy.matcher.ElementMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.takesArgument; +import static net.bytebuddy.matcher.ElementMatchers.takesArguments; + +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.context.Context; +import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; +import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; +import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; +import java.util.List; +import net.bytebuddy.asm.Advice; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; +import org.apache.rocketmq.client.java.impl.producer.SendReceiptImpl; +import org.apache.rocketmq.client.java.message.PublishingMessageImpl; +import org.apache.rocketmq.shaded.com.google.common.util.concurrent.FutureCallback; +import org.apache.rocketmq.shaded.com.google.common.util.concurrent.Futures; +import org.apache.rocketmq.shaded.com.google.common.util.concurrent.MoreExecutors; +import org.apache.rocketmq.shaded.com.google.common.util.concurrent.SettableFuture; + +final class RocketMqProducerInstrumentation implements TypeInstrumentation { + + /** Match the implementation of RocketMQ producer. */ + @Override + public ElementMatcher typeMatcher() { + return named("org.apache.rocketmq.client.java.impl.producer.ProducerImpl"); + } + + @Override + public void transform(TypeTransformer transformer) { + transformer.applyAdviceToMethod( + isMethod() + .and(named("send0")) + .and(isPrivate()) + .and(takesArguments(6)) + .and( + takesArgument( + 0, + named( + "org.apache.rocketmq.shaded.com.google.common.util.concurrent.SettableFuture"))) + .and(takesArgument(1, String.class)) + .and(takesArgument(2, named("org.apache.rocketmq.client.java.message.MessageType"))) + .and(takesArgument(3, List.class)) + .and(takesArgument(4, List.class)) + .and(takesArgument(5, int.class)), + RocketMqProducerInstrumentation.class.getName() + "$SendAdvice"); + } + + @SuppressWarnings("unused") + public static class SendAdvice { + @Advice.OnMethodEnter(suppress = Throwable.class) + public static void onEnter( + @Advice.Argument(0) SettableFuture> future0, + @Advice.Argument(4) List messages) { + Instrumenter instrumenter = + RocketMqSingletons.producerInstrumenter(); + int count = messages.size(); + List> futures = FutureConverter.convert(future0, count); + for (int i = 0; i < count; i++) { + PublishingMessageImpl message = messages.get(i); + + // Try to extract parent context. + Context parentContext = VirtualFieldStore.getContextByMessage(message); + if (parentContext == null) { + parentContext = Context.current(); + } + + Span span = Span.fromContext(parentContext); + if (!span.getSpanContext().isValid()) { + parentContext = Context.current(); + } + + SettableFuture future = futures.get(i); + if (!instrumenter.shouldStart(parentContext, message)) { + return; + } + Context context = instrumenter.start(parentContext, message); + Futures.addCallback( + future, + new SpanFinishingCallback(instrumenter, context, message), + MoreExecutors.directExecutor()); + } + } + } + + public static class SpanFinishingCallback implements FutureCallback { + private final Instrumenter instrumenter; + private final Context context; + private final PublishingMessageImpl message; + + public SpanFinishingCallback( + Instrumenter instrumenter, + Context context, + PublishingMessageImpl message) { + this.instrumenter = instrumenter; + this.context = context; + this.message = message; + } + + @Override + public void onSuccess(SendReceiptImpl sendReceipt) { + instrumenter.end(context, message, sendReceipt, null); + } + + @Override + public void onFailure(Throwable t) { + instrumenter.end(context, message, null, t); + } + } +} diff --git a/instrumentation/rocketmq/rocketmq-client/rocketmq-client-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rocketmqclient/v5_0/RocketMqPublishingMessageImplInstrumentation.java b/instrumentation/rocketmq/rocketmq-client/rocketmq-client-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rocketmqclient/v5_0/RocketMqPublishingMessageImplInstrumentation.java new file mode 100644 index 000000000000..7590366164ed --- /dev/null +++ b/instrumentation/rocketmq/rocketmq-client/rocketmq-client-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rocketmqclient/v5_0/RocketMqPublishingMessageImplInstrumentation.java @@ -0,0 +1,84 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.rocketmqclient.v5_0; + +import static net.bytebuddy.matcher.ElementMatchers.isConstructor; +import static net.bytebuddy.matcher.ElementMatchers.isMethod; +import static net.bytebuddy.matcher.ElementMatchers.isPublic; +import static net.bytebuddy.matcher.ElementMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.namedOneOf; +import static net.bytebuddy.matcher.ElementMatchers.takesArgument; + +import io.opentelemetry.context.Context; +import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; +import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; +import java.util.Map; +import net.bytebuddy.asm.Advice; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; +import org.apache.rocketmq.client.apis.message.Message; +import org.apache.rocketmq.client.apis.producer.Producer; +import org.apache.rocketmq.client.apis.producer.Transaction; +import org.apache.rocketmq.client.java.message.MessageImpl; +import org.apache.rocketmq.client.java.message.PublishingMessageImpl; + +final class RocketMqPublishingMessageImplInstrumentation implements TypeInstrumentation { + + @Override + public ElementMatcher typeMatcher() { + return namedOneOf( + "org.apache.rocketmq.client.java.message.PublishingMessageImpl", + "org.apache.rocketmq.client.java.message.MessageImpl"); + } + + @Override + public void transform(TypeTransformer transformer) { + transformer.applyAdviceToMethod( + isConstructor() + .and(isPublic()) + .and(takesArgument(0, named("org.apache.rocketmq.client.apis.message.Message"))) + .and( + takesArgument( + 1, named("org.apache.rocketmq.client.java.impl.producer.PublishingSettings"))) + .and(takesArgument(2, boolean.class)), + RocketMqPublishingMessageImplInstrumentation.class.getName() + "$ConstructorAdvice"); + transformer.applyAdviceToMethod( + isMethod().and(named("getProperties")).and(isPublic()), + RocketMqPublishingMessageImplInstrumentation.class.getName() + "$GetPropertiesAdvice"); + } + + @SuppressWarnings("unused") + public static class ConstructorAdvice { + /** + * The constructor of {@link PublishingMessageImpl} is always called in the same thread that + * user invoke {@link Producer#send(Message)}/{@link Producer#sendAsync(Message)}/{@link + * Producer#send(Message, Transaction)}. Store the {@link Context} here and fetch it in {@link + * RocketMqProducerInstrumentation}. + */ + @Advice.OnMethodExit(suppress = Throwable.class) + public static void onExit(@Advice.This PublishingMessageImpl message) { + VirtualFieldStore.setContextByMessage(message, Context.current()); + } + } + + @SuppressWarnings("unused") + public static class GetPropertiesAdvice { + /** Update the message properties to propagate context recorded by {@link MapSetter}. */ + @Advice.OnMethodExit(suppress = Throwable.class) + public static void onExit( + @Advice.This MessageImpl messageImpl, + @Advice.Return(readOnly = false) Map properties) { + if (!(messageImpl instanceof PublishingMessageImpl)) { + return; + } + PublishingMessageImpl message = (PublishingMessageImpl) messageImpl; + Map extraProperties = VirtualFieldStore.getExtraPropertiesByMessage(message); + if (extraProperties != null) { + properties.putAll(extraProperties); + } + } + } +} diff --git a/instrumentation/rocketmq/rocketmq-client/rocketmq-client-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rocketmqclient/v5_0/RocketMqSingletons.java b/instrumentation/rocketmq/rocketmq-client/rocketmq-client-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rocketmqclient/v5_0/RocketMqSingletons.java new file mode 100644 index 000000000000..01b6e6c8a7cc --- /dev/null +++ b/instrumentation/rocketmq/rocketmq-client/rocketmq-client-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rocketmqclient/v5_0/RocketMqSingletons.java @@ -0,0 +1,29 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.rocketmqclient.v5_0; + +import io.opentelemetry.api.GlobalOpenTelemetry; +import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; +import io.opentelemetry.javaagent.bootstrap.internal.ExperimentalConfig; +import org.apache.rocketmq.client.java.impl.producer.SendReceiptImpl; +import org.apache.rocketmq.client.java.message.PublishingMessageImpl; + +public final class RocketMqSingletons { + + private static final Instrumenter PRODUCER_INSTRUMENTER; + + static { + PRODUCER_INSTRUMENTER = + RocketMqInstrumenterFactory.createProducerInstrumenter( + GlobalOpenTelemetry.get(), ExperimentalConfig.get().getMessagingHeaders()); + } + + public static Instrumenter producerInstrumenter() { + return PRODUCER_INSTRUMENTER; + } + + private RocketMqSingletons() {} +} diff --git a/instrumentation/rocketmq/rocketmq-client/rocketmq-client-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rocketmqclient/v5_0/VirtualFieldStore.java b/instrumentation/rocketmq/rocketmq-client/rocketmq-client-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rocketmqclient/v5_0/VirtualFieldStore.java new file mode 100644 index 000000000000..cf84acd3fc7e --- /dev/null +++ b/instrumentation/rocketmq/rocketmq-client/rocketmq-client-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rocketmqclient/v5_0/VirtualFieldStore.java @@ -0,0 +1,37 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.rocketmqclient.v5_0; + +import io.opentelemetry.context.Context; +import io.opentelemetry.instrumentation.api.util.VirtualField; +import java.util.Map; +import org.apache.rocketmq.client.java.message.PublishingMessageImpl; + +public class VirtualFieldStore { + private static final VirtualField messageContextField = + VirtualField.find(PublishingMessageImpl.class, Context.class); + private static final VirtualField> + messageExtraPropertiesField = VirtualField.find(PublishingMessageImpl.class, Map.class); + + private VirtualFieldStore() {} + + public static Context getContextByMessage(PublishingMessageImpl message) { + return messageContextField.get(message); + } + + public static void setContextByMessage(PublishingMessageImpl message, Context context) { + messageContextField.set(message, context); + } + + public static Map getExtraPropertiesByMessage(PublishingMessageImpl message) { + return messageExtraPropertiesField.get(message); + } + + public static void setExtraPropertiesByMessage( + PublishingMessageImpl message, Map extraProperties) { + messageExtraPropertiesField.set(message, extraProperties); + } +} diff --git a/instrumentation/rocketmq/rocketmq-client/rocketmq-client-5.0/javaagent/src/test/java/io/opentelemetry/instrumentation/rocketmqclient/v5_0/RocketMqClientTest.java b/instrumentation/rocketmq/rocketmq-client/rocketmq-client-5.0/javaagent/src/test/java/io/opentelemetry/instrumentation/rocketmqclient/v5_0/RocketMqClientTest.java new file mode 100644 index 000000000000..9c68c5ee9fbd --- /dev/null +++ b/instrumentation/rocketmq/rocketmq-client/rocketmq-client-5.0/javaagent/src/test/java/io/opentelemetry/instrumentation/rocketmqclient/v5_0/RocketMqClientTest.java @@ -0,0 +1,20 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.rocketmqclient.v5_0; + +import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import org.junit.jupiter.api.extension.RegisterExtension; + +public class RocketMqClientTest extends AbstractRocketMqClientTest { + @RegisterExtension + static final InstrumentationExtension testing = AgentInstrumentationExtension.create(); + + @Override + protected InstrumentationExtension testing() { + return testing; + } +} diff --git a/instrumentation/rocketmq/rocketmq-client/rocketmq-client-5.0/testing/build.gradle.kts b/instrumentation/rocketmq/rocketmq-client/rocketmq-client-5.0/testing/build.gradle.kts new file mode 100644 index 000000000000..287d950d28de --- /dev/null +++ b/instrumentation/rocketmq/rocketmq-client/rocketmq-client-5.0/testing/build.gradle.kts @@ -0,0 +1,12 @@ +plugins { + id("otel.java-conventions") +} + +dependencies { + api(project(":testing-common")) + + // earlier versions have bugs that may make tests flaky. + implementation("org.apache.rocketmq:rocketmq-client-java:5.0.2") + implementation("org.testcontainers:testcontainers:1.17.5") + implementation("io.opentelemetry:opentelemetry-api") +} 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 new file mode 100644 index 000000000000..0999a8e823e0 --- /dev/null +++ b/instrumentation/rocketmq/rocketmq-client/rocketmq-client-5.0/testing/src/main/java/io/opentelemetry/instrumentation/rocketmqclient/v5_0/AbstractRocketMqClientTest.java @@ -0,0 +1,108 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.rocketmqclient.v5_0; + +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; +import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.MESSAGING_DESTINATION; +import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.MESSAGING_DESTINATION_KIND; +import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.MESSAGING_MESSAGE_ID; +import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.MESSAGING_MESSAGE_PAYLOAD_SIZE_BYTES; +import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.MESSAGING_ROCKETMQ_MESSAGE_KEYS; +import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.MESSAGING_ROCKETMQ_MESSAGE_TAG; +import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.MESSAGING_ROCKETMQ_MESSAGE_TYPE; +import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.MESSAGING_SYSTEM; +import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.MessagingRocketmqMessageTypeValues.NORMAL; + +import io.opentelemetry.instrumentation.test.utils.PortUtils; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.sdk.trace.data.StatusData; +import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import org.apache.rocketmq.client.apis.ClientConfiguration; +import org.apache.rocketmq.client.apis.ClientException; +import org.apache.rocketmq.client.apis.ClientServiceProvider; +import org.apache.rocketmq.client.apis.message.Message; +import org.apache.rocketmq.client.apis.producer.Producer; +import org.apache.rocketmq.client.apis.producer.SendReceipt; +import org.junit.jupiter.api.Test; +import org.testcontainers.containers.FixedHostPortGenericContainer; +import org.testcontainers.containers.GenericContainer; + +public abstract class AbstractRocketMqClientTest { + + protected abstract InstrumentationExtension testing(); + + // TODO(aaron-ai): replace it by the official image. + private static final String IMAGE_NAME = "aaronai/rocketmq-proxy-it:v1.0.0"; + + // We still need this container type to do fixed-port-mapping. + @SuppressWarnings({"deprecation", "rawtypes", "resource"}) + @Test + public void testSendMessage() throws ClientException { + int proxyPort = PortUtils.findOpenPorts(4); + int brokerPort = proxyPort + 1; + int brokerHaPort = proxyPort + 2; + int namesrvPort = proxyPort + 3; + try (GenericContainer container = + new FixedHostPortGenericContainer(IMAGE_NAME) + .withFixedExposedPort(proxyPort, proxyPort) + .withEnv("rocketmq.broker.port", String.valueOf(brokerPort)) + .withEnv("rocketmq.proxy.port", String.valueOf(proxyPort)) + .withEnv("rocketmq.broker.ha.port", String.valueOf(brokerHaPort)) + .withEnv("rocketmq.namesrv.port", String.valueOf(namesrvPort)) + .withExposedPorts(proxyPort)) { + // Start the container. + container.start(); + String endpoints = "127.0.0.1:" + proxyPort; + ClientConfiguration clientConfiguration = + ClientConfiguration.newBuilder().setEndpoints(endpoints).build(); + // Inner topic of the container. + String topic = "normal-topic-0"; + ClientServiceProvider provider = ClientServiceProvider.loadService(); + Producer producer = + provider + .newProducerBuilder() + .setClientConfiguration(clientConfiguration) + .setTopics(topic) + .build(); + + String tag = "tagA"; + String[] keys = new String[] {"yourMessageKey-0", "yourMessageKey-1"}; + byte[] body = "foobar".getBytes(StandardCharsets.UTF_8); + Message message = + provider + .newMessageBuilder() + .setTopic(topic) + .setTag(tag) + .setKeys(keys) + .setBody(body) + .build(); + + SendReceipt sendReceipt = producer.send(message); + testing() + .waitAndAssertTraces( + traceAssert -> + traceAssert.hasSpansSatisfyingExactly( + spanDataAssert -> + spanDataAssert + .hasName(topic + " send") + .hasStatus(StatusData.unset()) + .hasAttributesSatisfyingExactly( + equalTo(MESSAGING_ROCKETMQ_MESSAGE_TAG, tag), + equalTo(MESSAGING_ROCKETMQ_MESSAGE_KEYS, Arrays.asList(keys)), + equalTo(MESSAGING_ROCKETMQ_MESSAGE_TYPE, NORMAL), + equalTo(MESSAGING_MESSAGE_PAYLOAD_SIZE_BYTES, (long) body.length), + equalTo(MESSAGING_SYSTEM, "rocketmq"), + equalTo( + MESSAGING_MESSAGE_ID, sendReceipt.getMessageId().toString()), + equalTo( + MESSAGING_DESTINATION_KIND, + SemanticAttributes.MessagingDestinationKindValues.TOPIC), + equalTo(MESSAGING_DESTINATION, topic)))); + } + } +} diff --git a/instrumentation/runtime-metrics/javaagent/src/main/java/io/opentelemetry/instrumentation/javaagent/runtimemetrics/RuntimeMetricsInstaller.java b/instrumentation/runtime-metrics/javaagent/src/main/java/io/opentelemetry/instrumentation/javaagent/runtimemetrics/RuntimeMetricsInstaller.java index 392bcbce1432..025e2298d04a 100644 --- a/instrumentation/runtime-metrics/javaagent/src/main/java/io/opentelemetry/instrumentation/javaagent/runtimemetrics/RuntimeMetricsInstaller.java +++ b/instrumentation/runtime-metrics/javaagent/src/main/java/io/opentelemetry/instrumentation/javaagent/runtimemetrics/RuntimeMetricsInstaller.java @@ -7,6 +7,7 @@ import com.google.auto.service.AutoService; import io.opentelemetry.api.GlobalOpenTelemetry; +import io.opentelemetry.api.OpenTelemetry; import io.opentelemetry.instrumentation.runtimemetrics.BufferPools; import io.opentelemetry.instrumentation.runtimemetrics.Classes; import io.opentelemetry.instrumentation.runtimemetrics.Cpu; @@ -16,7 +17,6 @@ import io.opentelemetry.javaagent.extension.AgentListener; import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk; import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; -import java.util.Locale; /** An {@link AgentListener} that enables runtime metrics during agent startup. */ @AutoService(AgentListener.class) @@ -26,27 +26,22 @@ public class RuntimeMetricsInstaller implements AgentListener { public void afterAgent(AutoConfiguredOpenTelemetrySdk autoConfiguredSdk) { ConfigProperties config = autoConfiguredSdk.getConfig(); - boolean defaultEnabled = - config.getBoolean(normalize("otel.instrumentation.common.default-enabled"), true); - if (!config.getBoolean( - normalize("otel.instrumentation.runtime-metrics.enabled"), defaultEnabled)) { + boolean defaultEnabled = config.getBoolean("otel.instrumentation.common.default-enabled", true); + if (!config.getBoolean("otel.instrumentation.runtime-metrics.enabled", defaultEnabled)) { return; } - Classes.registerObservers(GlobalOpenTelemetry.get()); - Cpu.registerObservers(GlobalOpenTelemetry.get()); - MemoryPools.registerObservers(GlobalOpenTelemetry.get()); - Threads.registerObservers(GlobalOpenTelemetry.get()); + OpenTelemetry openTelemetry = GlobalOpenTelemetry.get(); + + BufferPools.registerObservers(openTelemetry); + Classes.registerObservers(openTelemetry); + Cpu.registerObservers(openTelemetry); + MemoryPools.registerObservers(openTelemetry); + Threads.registerObservers(openTelemetry); if (config.getBoolean( - normalize("otel.instrumentation.runtime-metrics.experimental-metrics.enabled"), false)) { - GarbageCollector.registerObservers(GlobalOpenTelemetry.get()); - BufferPools.registerObservers(GlobalOpenTelemetry.get()); + "otel.instrumentation.runtime-metrics.experimental-metrics.enabled", false)) { + GarbageCollector.registerObservers(openTelemetry); } } - - // TODO: remove after https://github.com/open-telemetry/opentelemetry-java/issues/4562 is fixed - private static String normalize(String key) { - return key.toLowerCase(Locale.ROOT).replace('-', '.'); - } } diff --git a/instrumentation/runtime-metrics/library/build.gradle.kts b/instrumentation/runtime-metrics/library/build.gradle.kts index 34d3651cdc0a..55de74a17ed4 100644 --- a/instrumentation/runtime-metrics/library/build.gradle.kts +++ b/instrumentation/runtime-metrics/library/build.gradle.kts @@ -3,6 +3,8 @@ plugins { } dependencies { + implementation(project(":instrumentation-api")) + testImplementation("io.opentelemetry:opentelemetry-sdk-metrics") testImplementation(project(":testing-common")) } diff --git a/instrumentation/runtime-metrics/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/BufferPools.java b/instrumentation/runtime-metrics/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/BufferPools.java index 60a7c30a6bcb..b754797dc25c 100644 --- a/instrumentation/runtime-metrics/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/BufferPools.java +++ b/instrumentation/runtime-metrics/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/BufferPools.java @@ -39,10 +39,14 @@ public final class BufferPools { /** Register observers for java runtime buffer pool metrics. */ public static void registerObservers(OpenTelemetry openTelemetry) { - List bufferBeans = ManagementFactory.getPlatformMXBeans(BufferPoolMXBean.class); - Meter meter = openTelemetry.getMeter("io.opentelemetry.runtime-metrics"); + registerObservers(openTelemetry, bufferBeans); + } + + // Visible for testing + static void registerObservers(OpenTelemetry openTelemetry, List bufferBeans) { + Meter meter = RuntimeMetricsUtil.getMeter(openTelemetry); meter .upDownCounterBuilder("process.runtime.jvm.buffer.usage") @@ -59,10 +63,11 @@ public static void registerObservers(OpenTelemetry openTelemetry) { meter .upDownCounterBuilder("process.runtime.jvm.buffer.count") .setDescription("The number of buffers in the pool") - .setUnit("buffers") + .setUnit("{buffers}") .buildWithCallback(callback(bufferBeans, BufferPoolMXBean::getCount)); } + // Visible for testing static Consumer callback( List bufferPools, Function extractor) { List attributeSets = new ArrayList<>(bufferPools.size()); @@ -79,4 +84,6 @@ static Consumer callback( } }; } + + private BufferPools() {} } diff --git a/instrumentation/runtime-metrics/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/Classes.java b/instrumentation/runtime-metrics/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/Classes.java index 334c5b169c55..bf04a8dd0057 100644 --- a/instrumentation/runtime-metrics/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/Classes.java +++ b/instrumentation/runtime-metrics/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/Classes.java @@ -39,7 +39,7 @@ public static void registerObservers(OpenTelemetry openTelemetry) { // Visible for testing void registerObservers(OpenTelemetry openTelemetry, ClassLoadingMXBean classBean) { - Meter meter = openTelemetry.getMeter("io.opentelemetry.runtime-metrics"); + Meter meter = RuntimeMetricsUtil.getMeter(openTelemetry); meter .counterBuilder("process.runtime.jvm.classes.loaded") diff --git a/instrumentation/runtime-metrics/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/Cpu.java b/instrumentation/runtime-metrics/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/Cpu.java index ba2be73d209d..182ae9a44727 100644 --- a/instrumentation/runtime-metrics/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/Cpu.java +++ b/instrumentation/runtime-metrics/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/Cpu.java @@ -80,7 +80,7 @@ void registerObservers( OperatingSystemMXBean osBean, @Nullable Supplier systemCpuUsage, @Nullable Supplier processCpuUsage) { - Meter meter = openTelemetry.getMeter("io.opentelemetry.runtime-metrics"); + Meter meter = RuntimeMetricsUtil.getMeter(openTelemetry); meter .gaugeBuilder("process.runtime.jvm.system.cpu.load_1m") diff --git a/instrumentation/runtime-metrics/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/GarbageCollector.java b/instrumentation/runtime-metrics/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/GarbageCollector.java index d9e84efc7071..5556060b891e 100644 --- a/instrumentation/runtime-metrics/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/GarbageCollector.java +++ b/instrumentation/runtime-metrics/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/GarbageCollector.java @@ -5,7 +5,6 @@ package io.opentelemetry.instrumentation.runtimemetrics; -import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.api.OpenTelemetry; import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.api.common.Attributes; @@ -34,19 +33,10 @@ public final class GarbageCollector { private static final AttributeKey GC_KEY = AttributeKey.stringKey("gc"); - /** - * Register all observers provided by this module. - * - * @deprecated use {@link #registerObservers(OpenTelemetry openTelemetry)} - */ - @Deprecated - public static void registerObservers() { - registerObservers(GlobalOpenTelemetry.get()); - } - + /** Register observers for java runtime garbage collector metrics. */ public static void registerObservers(OpenTelemetry openTelemetry) { List garbageCollectors = ManagementFactory.getGarbageCollectorMXBeans(); - Meter meter = openTelemetry.getMeterProvider().get(GarbageCollector.class.getName()); + Meter meter = RuntimeMetricsUtil.getMeter(openTelemetry); List labelSets = new ArrayList<>(garbageCollectors.size()); for (GarbageCollectorMXBean gc : garbageCollectors) { labelSets.add(Attributes.of(GC_KEY, gc.getName())); @@ -66,7 +56,7 @@ public static void registerObservers(OpenTelemetry openTelemetry) { .counterBuilder("runtime.jvm.gc.count") .setDescription( "The number of collections that have occurred for a given JVM garbage collector.") - .setUnit("collections") + .setUnit("{collections}") .buildWithCallback( resultLongObserver -> { for (int i = 0; i < garbageCollectors.size(); i++) { diff --git a/instrumentation/runtime-metrics/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/MemoryPools.java b/instrumentation/runtime-metrics/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/MemoryPools.java index d47ecec6bca8..44b83c66c469 100644 --- a/instrumentation/runtime-metrics/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/MemoryPools.java +++ b/instrumentation/runtime-metrics/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/MemoryPools.java @@ -5,7 +5,6 @@ package io.opentelemetry.instrumentation.runtimemetrics; -import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.api.OpenTelemetry; import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.api.common.Attributes; @@ -49,20 +48,14 @@ public final class MemoryPools { private static final String HEAP = "heap"; private static final String NON_HEAP = "non_heap"; - /** - * Register observers for java runtime memory metrics. - * - * @deprecated use {@link #registerObservers(OpenTelemetry openTelemetry)} - */ - @Deprecated - public static void registerObservers() { - registerObservers(GlobalOpenTelemetry.get()); - } - /** Register observers for java runtime memory metrics. */ public static void registerObservers(OpenTelemetry openTelemetry) { - List poolBeans = ManagementFactory.getMemoryPoolMXBeans(); - Meter meter = openTelemetry.getMeter("io.opentelemetry.runtime-metrics"); + registerObservers(openTelemetry, ManagementFactory.getMemoryPoolMXBeans()); + } + + // Visible for testing + static void registerObservers(OpenTelemetry openTelemetry, List poolBeans) { + Meter meter = RuntimeMetricsUtil.getMeter(openTelemetry); meter .upDownCounterBuilder("process.runtime.jvm.memory.usage") diff --git a/instrumentation/runtime-metrics/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/RuntimeMetricsUtil.java b/instrumentation/runtime-metrics/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/RuntimeMetricsUtil.java new file mode 100644 index 000000000000..f144dce92846 --- /dev/null +++ b/instrumentation/runtime-metrics/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/RuntimeMetricsUtil.java @@ -0,0 +1,31 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.runtimemetrics; + +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.api.metrics.Meter; +import io.opentelemetry.api.metrics.MeterBuilder; +import io.opentelemetry.instrumentation.api.internal.EmbeddedInstrumentationProperties; +import javax.annotation.Nullable; + +class RuntimeMetricsUtil { + + private static final String INSTRUMENTATION_NAME = "io.opentelemetry.runtime-metrics"; + + @Nullable + private static final String INSTRUMENTATION_VERSION = + EmbeddedInstrumentationProperties.findVersion(INSTRUMENTATION_NAME); + + static Meter getMeter(OpenTelemetry openTelemetry) { + MeterBuilder meterBuilder = openTelemetry.meterBuilder(INSTRUMENTATION_NAME); + if (INSTRUMENTATION_VERSION != null) { + meterBuilder.setInstrumentationVersion(INSTRUMENTATION_VERSION); + } + return meterBuilder.build(); + } + + private RuntimeMetricsUtil() {} +} diff --git a/instrumentation/runtime-metrics/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/Threads.java b/instrumentation/runtime-metrics/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/Threads.java index fbcd4a4e71ec..891e6772f393 100644 --- a/instrumentation/runtime-metrics/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/Threads.java +++ b/instrumentation/runtime-metrics/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/Threads.java @@ -6,6 +6,8 @@ package io.opentelemetry.instrumentation.runtimemetrics; import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.metrics.Meter; import java.lang.management.ManagementFactory; import java.lang.management.ThreadMXBean; @@ -22,7 +24,8 @@ *

Example metrics being exported: * *

- *   process.runtime.jvm.threads.count 4
+ *   process.runtime.jvm.threads.count{daemon=true} 2
+ *   process.runtime.jvm.threads.count{daemon=false} 5
  * 
*/ public final class Threads { @@ -30,6 +33,8 @@ public final class Threads { // Visible for testing static final Threads INSTANCE = new Threads(); + static final AttributeKey DAEMON = AttributeKey.booleanKey("daemon"); + /** Register observers for java runtime class metrics. */ public static void registerObservers(OpenTelemetry openTelemetry) { INSTANCE.registerObservers(openTelemetry, ManagementFactory.getThreadMXBean()); @@ -37,14 +42,21 @@ public static void registerObservers(OpenTelemetry openTelemetry) { // Visible for testing void registerObservers(OpenTelemetry openTelemetry, ThreadMXBean threadBean) { - Meter meter = openTelemetry.getMeter("io.opentelemetry.runtime-metrics"); + Meter meter = RuntimeMetricsUtil.getMeter(openTelemetry); meter .upDownCounterBuilder("process.runtime.jvm.threads.count") .setDescription("Number of executing threads") .setUnit("1") .buildWithCallback( - observableMeasurement -> observableMeasurement.record(threadBean.getThreadCount())); + observableMeasurement -> { + observableMeasurement.record( + threadBean.getDaemonThreadCount(), + Attributes.builder().put(DAEMON, true).build()); + observableMeasurement.record( + threadBean.getThreadCount() - threadBean.getDaemonThreadCount(), + Attributes.builder().put(DAEMON, false).build()); + }); } private Threads() {} diff --git a/instrumentation/runtime-metrics/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/BufferPoolsTest.java b/instrumentation/runtime-metrics/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/BufferPoolsTest.java index 8e62a0250c9d..d0593a224c84 100644 --- a/instrumentation/runtime-metrics/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/BufferPoolsTest.java +++ b/instrumentation/runtime-metrics/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/BufferPoolsTest.java @@ -5,14 +5,19 @@ package io.opentelemetry.instrumentation.runtimemetrics; +import static io.opentelemetry.instrumentation.runtimemetrics.ScopeUtil.EXPECTED_SCOPE; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.metrics.ObservableLongMeasurement; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension; import java.lang.management.BufferPoolMXBean; import java.util.Arrays; import java.util.List; @@ -20,12 +25,17 @@ 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.Spy; import org.mockito.junit.jupiter.MockitoExtension; @ExtendWith(MockitoExtension.class) public class BufferPoolsTest { + + @RegisterExtension + static final InstrumentationExtension testing = LibraryInstrumentationExtension.create(); + @Spy private ObservableLongMeasurement measurement; @Mock private BufferPoolMXBean bufferPoolBean; private List beans; @@ -36,6 +46,74 @@ void setup() { beans = Arrays.asList(bufferPoolBean); } + @Test + void registerObservers() { + when(bufferPoolBean.getMemoryUsed()).thenReturn(10L); + when(bufferPoolBean.getTotalCapacity()).thenReturn(11L); + when(bufferPoolBean.getCount()).thenReturn(12L); + + BufferPools.registerObservers(testing.getOpenTelemetry(), beans); + + testing.waitAndAssertMetrics( + "io.opentelemetry.runtime-metrics", + "process.runtime.jvm.buffer.usage", + metrics -> + metrics.anySatisfy( + metricData -> + assertThat(metricData) + .hasInstrumentationScope(EXPECTED_SCOPE) + .hasDescription( + "Memory that the Java virtual machine is using for this buffer pool") + .hasUnit("By") + .hasLongSumSatisfying( + sum -> + sum.hasPointsSatisfying( + point -> + point + .hasValue(10) + .hasAttribute( + AttributeKey.stringKey("pool"), + "buffer_pool_1"))))); + testing.waitAndAssertMetrics( + "io.opentelemetry.runtime-metrics", + "process.runtime.jvm.buffer.limit", + metrics -> + metrics.anySatisfy( + metricData -> + assertThat(metricData) + .hasInstrumentationScope(EXPECTED_SCOPE) + .hasDescription("Total capacity of the buffers in this pool") + .hasUnit("By") + .hasLongSumSatisfying( + sum -> + sum.hasPointsSatisfying( + point -> + point + .hasValue(11) + .hasAttribute( + AttributeKey.stringKey("pool"), + "buffer_pool_1"))))); + testing.waitAndAssertMetrics( + "io.opentelemetry.runtime-metrics", + "process.runtime.jvm.buffer.count", + metrics -> + metrics.anySatisfy( + metricData -> + assertThat(metricData) + .hasInstrumentationScope(EXPECTED_SCOPE) + .hasDescription("The number of buffers in the pool") + .hasUnit("{buffers}") + .hasLongSumSatisfying( + sum -> + sum.hasPointsSatisfying( + point -> + point + .hasValue(12) + .hasAttribute( + AttributeKey.stringKey("pool"), + "buffer_pool_1"))))); + } + @Test void callback_Records() { when(bufferPoolBean.getMemoryUsed()).thenReturn(1L); diff --git a/instrumentation/runtime-metrics/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/ClassesTest.java b/instrumentation/runtime-metrics/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/ClassesTest.java index 8cdbc2fd93b9..3dd9fb3ba622 100644 --- a/instrumentation/runtime-metrics/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/ClassesTest.java +++ b/instrumentation/runtime-metrics/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/ClassesTest.java @@ -5,6 +5,7 @@ package io.opentelemetry.instrumentation.runtimemetrics; +import static io.opentelemetry.instrumentation.runtimemetrics.ScopeUtil.EXPECTED_SCOPE; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; import static org.mockito.Mockito.when; @@ -41,6 +42,7 @@ void registerObservers() { metrics.anySatisfy( metricData -> assertThat(metricData) + .hasInstrumentationScope(EXPECTED_SCOPE) .hasDescription("Number of classes loaded since JVM start") .hasUnit("1") .hasLongSumSatisfying( @@ -56,6 +58,7 @@ void registerObservers() { metrics.anySatisfy( metricData -> assertThat(metricData) + .hasInstrumentationScope(EXPECTED_SCOPE) .hasDescription("Number of classes unloaded since JVM start") .hasUnit("1") .hasLongSumSatisfying( @@ -71,6 +74,7 @@ void registerObservers() { metrics.anySatisfy( metricData -> assertThat(metricData) + .hasInstrumentationScope(EXPECTED_SCOPE) .hasDescription("Number of classes currently loaded") .hasUnit("1") .hasLongSumSatisfying( diff --git a/instrumentation/runtime-metrics/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/CpuTest.java b/instrumentation/runtime-metrics/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/CpuTest.java index 91ce9c30050e..912172616b4a 100644 --- a/instrumentation/runtime-metrics/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/CpuTest.java +++ b/instrumentation/runtime-metrics/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/CpuTest.java @@ -5,6 +5,7 @@ package io.opentelemetry.instrumentation.runtimemetrics; +import static io.opentelemetry.instrumentation.runtimemetrics.ScopeUtil.EXPECTED_SCOPE; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; import static org.mockito.Mockito.when; @@ -42,6 +43,7 @@ void registerObservers() { metrics.anySatisfy( metricData -> assertThat(metricData) + .hasInstrumentationScope(EXPECTED_SCOPE) .hasDescription("Average CPU load of the whole system for the last minute") .hasUnit("1") .hasDoubleGaugeSatisfying( @@ -53,6 +55,7 @@ void registerObservers() { metrics.anySatisfy( metricData -> assertThat(metricData) + .hasInstrumentationScope(EXPECTED_SCOPE) .hasDescription("Recent cpu utilization for the whole system") .hasUnit("1") .hasDoubleGaugeSatisfying( @@ -64,6 +67,7 @@ void registerObservers() { metrics.anySatisfy( metricData -> assertThat(metricData) + .hasInstrumentationScope(EXPECTED_SCOPE) .hasDescription("Recent cpu utilization for the process") .hasUnit("1") .hasDoubleGaugeSatisfying( diff --git a/instrumentation/runtime-metrics/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/MemoryPoolsTest.java b/instrumentation/runtime-metrics/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/MemoryPoolsTest.java index cac71df834ec..a58bfb6eca56 100644 --- a/instrumentation/runtime-metrics/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/MemoryPoolsTest.java +++ b/instrumentation/runtime-metrics/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/MemoryPoolsTest.java @@ -5,14 +5,19 @@ package io.opentelemetry.instrumentation.runtimemetrics; +import static io.opentelemetry.instrumentation.runtimemetrics.ScopeUtil.EXPECTED_SCOPE; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.metrics.ObservableLongMeasurement; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension; import java.lang.management.MemoryPoolMXBean; import java.lang.management.MemoryType; import java.lang.management.MemoryUsage; @@ -22,6 +27,7 @@ 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.Spy; import org.mockito.junit.jupiter.MockitoExtension; @@ -29,6 +35,9 @@ @ExtendWith(MockitoExtension.class) class MemoryPoolsTest { + @RegisterExtension + static final InstrumentationExtension testing = LibraryInstrumentationExtension.create(); + @Spy private ObservableLongMeasurement measurement; @Mock private MemoryPoolMXBean heapPoolBean; @@ -50,6 +59,125 @@ void setup() { beans = Arrays.asList(heapPoolBean, nonHeapPoolBean); } + @Test + void registerObservers() { + when(heapPoolUsage.getInit()).thenReturn(10L); + when(heapPoolUsage.getUsed()).thenReturn(11L); + when(heapPoolUsage.getCommitted()).thenReturn(12L); + when(heapPoolUsage.getMax()).thenReturn(13L); + when(nonHeapUsage.getInit()).thenReturn(14L); + when(nonHeapUsage.getUsed()).thenReturn(15L); + when(nonHeapUsage.getCommitted()).thenReturn(16L); + when(nonHeapUsage.getMax()).thenReturn(17L); + + MemoryPools.registerObservers(testing.getOpenTelemetry(), beans); + + testing.waitAndAssertMetrics( + "io.opentelemetry.runtime-metrics", + "process.runtime.jvm.memory.init", + metrics -> + metrics.anySatisfy( + metricData -> + assertThat(metricData) + .hasInstrumentationScope(EXPECTED_SCOPE) + .hasDescription("Measure of initial memory requested") + .hasUnit("By") + .hasLongSumSatisfying( + sum -> + sum.hasPointsSatisfying( + point -> + point + .hasValue(10) + .hasAttribute( + AttributeKey.stringKey("pool"), "heap_pool") + .hasAttribute(AttributeKey.stringKey("type"), "heap"), + point -> + point + .hasValue(14) + .hasAttribute( + AttributeKey.stringKey("pool"), "non_heap_pool") + .hasAttribute( + AttributeKey.stringKey("type"), "non_heap"))))); + testing.waitAndAssertMetrics( + "io.opentelemetry.runtime-metrics", + "process.runtime.jvm.memory.usage", + metrics -> + metrics.anySatisfy( + metricData -> + assertThat(metricData) + .hasInstrumentationScope(EXPECTED_SCOPE) + .hasDescription("Measure of memory used") + .hasUnit("By") + .hasLongSumSatisfying( + sum -> + sum.hasPointsSatisfying( + point -> + point + .hasValue(11) + .hasAttribute( + AttributeKey.stringKey("pool"), "heap_pool") + .hasAttribute(AttributeKey.stringKey("type"), "heap"), + point -> + point + .hasValue(15) + .hasAttribute( + AttributeKey.stringKey("pool"), "non_heap_pool") + .hasAttribute( + AttributeKey.stringKey("type"), "non_heap"))))); + testing.waitAndAssertMetrics( + "io.opentelemetry.runtime-metrics", + "process.runtime.jvm.memory.committed", + metrics -> + metrics.anySatisfy( + metricData -> + assertThat(metricData) + .hasInstrumentationScope(EXPECTED_SCOPE) + .hasDescription("Measure of memory committed") + .hasUnit("By") + .hasLongSumSatisfying( + sum -> + sum.hasPointsSatisfying( + point -> + point + .hasValue(12) + .hasAttribute( + AttributeKey.stringKey("pool"), "heap_pool") + .hasAttribute(AttributeKey.stringKey("type"), "heap"), + point -> + point + .hasValue(16) + .hasAttribute( + AttributeKey.stringKey("pool"), "non_heap_pool") + .hasAttribute( + AttributeKey.stringKey("type"), "non_heap"))))); + testing.waitAndAssertMetrics( + "io.opentelemetry.runtime-metrics", + "process.runtime.jvm.memory.limit", + metrics -> + metrics.anySatisfy( + metricData -> + assertThat(metricData) + .hasInstrumentationScope(EXPECTED_SCOPE) + .hasDescription("Measure of max obtainable memory") + .hasUnit("By") + .hasLongSumSatisfying( + sum -> + sum.hasPointsSatisfying( + point -> + point + .hasValue(13) + .hasAttribute( + AttributeKey.stringKey("pool"), "heap_pool") + .hasAttribute(AttributeKey.stringKey("type"), "heap"), + point -> + point + .hasValue(17) + .hasAttribute( + AttributeKey.stringKey("pool"), "non_heap_pool") + .hasAttribute( + AttributeKey.stringKey("type"), "non_heap"))))); + } + @Test void callback_Records() { when(heapPoolUsage.getUsed()).thenReturn(1L); diff --git a/instrumentation/runtime-metrics/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/ScopeUtil.java b/instrumentation/runtime-metrics/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/ScopeUtil.java new file mode 100644 index 000000000000..3f229de822e9 --- /dev/null +++ b/instrumentation/runtime-metrics/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/ScopeUtil.java @@ -0,0 +1,25 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.runtimemetrics; + +import io.opentelemetry.instrumentation.api.internal.EmbeddedInstrumentationProperties; +import io.opentelemetry.sdk.common.InstrumentationScopeInfo; +import java.util.Optional; + +class ScopeUtil { + + static final InstrumentationScopeInfo EXPECTED_SCOPE = + InstrumentationScopeInfo.builder("io.opentelemetry.runtime-metrics") + .setVersion( + Optional.ofNullable( + EmbeddedInstrumentationProperties.findVersion( + "io.opentelemetry.runtime-metrics")) + .orElseThrow( + () -> new IllegalStateException("Unable to find instrumentation version"))) + .build(); + + private ScopeUtil() {} +} diff --git a/instrumentation/runtime-metrics/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/ThreadsTest.java b/instrumentation/runtime-metrics/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/ThreadsTest.java index b1128ce41faa..d9001f4db901 100644 --- a/instrumentation/runtime-metrics/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/ThreadsTest.java +++ b/instrumentation/runtime-metrics/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/ThreadsTest.java @@ -5,10 +5,12 @@ package io.opentelemetry.instrumentation.runtimemetrics; +import static io.opentelemetry.instrumentation.runtimemetrics.ScopeUtil.EXPECTED_SCOPE; +import static io.opentelemetry.instrumentation.runtimemetrics.Threads.DAEMON; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; import static org.mockito.Mockito.when; -import io.opentelemetry.api.common.Attributes; import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension; import java.lang.management.ThreadMXBean; @@ -28,7 +30,8 @@ class ThreadsTest { @Test void registerObservers() { - when(threadBean.getThreadCount()).thenReturn(3); + when(threadBean.getThreadCount()).thenReturn(7); + when(threadBean.getDaemonThreadCount()).thenReturn(2); Threads.INSTANCE.registerObservers(testing.getOpenTelemetry(), threadBean); @@ -39,6 +42,7 @@ void registerObservers() { metrics.anySatisfy( metricData -> assertThat(metricData) + .hasInstrumentationScope(EXPECTED_SCOPE) .hasDescription("Number of executing threads") .hasUnit("1") .hasLongSumSatisfying( @@ -46,6 +50,13 @@ void registerObservers() { sum.isNotMonotonic() .hasPointsSatisfying( point -> - point.hasValue(3).hasAttributes(Attributes.empty()))))); + point + .hasValue(2) + .hasAttributesSatisfying(equalTo(DAEMON, true)), + point -> + point + .hasValue(5) + .hasAttributesSatisfying( + equalTo(DAEMON, false)))))); } } diff --git a/instrumentation/rxjava/rxjava-2.0/javaagent/build.gradle.kts b/instrumentation/rxjava/rxjava-2.0/javaagent/build.gradle.kts index 312eccc241cc..157cc043ded1 100644 --- a/instrumentation/rxjava/rxjava-2.0/javaagent/build.gradle.kts +++ b/instrumentation/rxjava/rxjava-2.0/javaagent/build.gradle.kts @@ -22,6 +22,9 @@ dependencies { implementation(project(":instrumentation:rxjava:rxjava-2.0:library")) + testInstrumentation(project(":instrumentation:opentelemetry-extension-annotations-1.0:javaagent")) + + testImplementation(project(":instrumentation-annotations")) testImplementation("io.opentelemetry:opentelemetry-extension-annotations") testImplementation(project(":instrumentation:rxjava:rxjava-2.0:testing")) } diff --git a/instrumentation/rxjava/rxjava-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rxjava/v2_0/RxJava2IgnoredTypesConfigurer.java b/instrumentation/rxjava/rxjava-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rxjava/v2_0/RxJava2IgnoredTypesConfigurer.java index 45bff5f7df3a..1ff5909a7620 100644 --- a/instrumentation/rxjava/rxjava-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rxjava/v2_0/RxJava2IgnoredTypesConfigurer.java +++ b/instrumentation/rxjava/rxjava-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rxjava/v2_0/RxJava2IgnoredTypesConfigurer.java @@ -14,7 +14,7 @@ public class RxJava2IgnoredTypesConfigurer implements IgnoredTypesConfigurer { @Override - public void configure(ConfigProperties config, IgnoredTypesBuilder builder) { + public void configure(IgnoredTypesBuilder builder, ConfigProperties config) { // ScheduledRunnable is a wrapper around a Runnable and doesn't itself need context. builder.ignoreTaskClass("io.reactivex.internal.schedulers.ScheduledRunnable"); } diff --git a/instrumentation/rxjava/rxjava-2.0/javaagent/src/test/groovy/RxJava2WithSpanInstrumentationTest.groovy b/instrumentation/rxjava/rxjava-2.0/javaagent/src/test/groovy/BaseRxJava2WithSpanTest.groovy similarity index 80% rename from instrumentation/rxjava/rxjava-2.0/javaagent/src/test/groovy/RxJava2WithSpanInstrumentationTest.groovy rename to instrumentation/rxjava/rxjava-2.0/javaagent/src/test/groovy/BaseRxJava2WithSpanTest.groovy index 5fccfdeedcb8..c9dde6a5e501 100644 --- a/instrumentation/rxjava/rxjava-2.0/javaagent/src/test/groovy/RxJava2WithSpanInstrumentationTest.groovy +++ b/instrumentation/rxjava/rxjava-2.0/javaagent/src/test/groovy/BaseRxJava2WithSpanTest.groovy @@ -3,8 +3,9 @@ * SPDX-License-Identifier: Apache-2.0 */ -import io.opentelemetry.instrumentation.rxjava.v2_0.TracedWithSpan +import io.opentelemetry.instrumentation.rxjava.v2_0.AbstractTracedWithSpan import io.opentelemetry.instrumentation.test.AgentInstrumentationSpecification +import io.opentelemetry.semconv.trace.attributes.SemanticAttributes import io.reactivex.Completable import io.reactivex.Flowable import io.reactivex.Maybe @@ -24,13 +25,15 @@ import org.reactivestreams.Subscription import static io.opentelemetry.api.trace.SpanKind.INTERNAL import static io.opentelemetry.api.trace.StatusCode.ERROR -class RxJava2WithSpanInstrumentationTest extends AgentInstrumentationSpecification { +abstract class BaseRxJava2WithSpanTest extends AgentInstrumentationSpecification { + + abstract AbstractTracedWithSpan newTraced() def "should capture span for already completed Completable"() { setup: def observer = new TestObserver() def source = Completable.complete() - new TracedWithSpan() + newTraced() .completable(source) .subscribe(observer) observer.assertComplete() @@ -43,6 +46,8 @@ class RxJava2WithSpanInstrumentationTest extends AgentInstrumentationSpecificati kind INTERNAL hasNoParent() attributes { + "$SemanticAttributes.CODE_NAMESPACE" { it.endsWith(".TracedWithSpan") } + "$SemanticAttributes.CODE_FUNCTION" "completable" } } } @@ -53,7 +58,7 @@ class RxJava2WithSpanInstrumentationTest extends AgentInstrumentationSpecificati setup: def source = CompletableSubject.create() def observer = new TestObserver() - new TracedWithSpan() + newTraced() .completable(source) .subscribe(observer) observer.assertSubscribed() @@ -72,6 +77,8 @@ class RxJava2WithSpanInstrumentationTest extends AgentInstrumentationSpecificati kind INTERNAL hasNoParent() attributes { + "$SemanticAttributes.CODE_NAMESPACE" { it.endsWith(".TracedWithSpan") } + "$SemanticAttributes.CODE_FUNCTION" "completable" } } } @@ -83,7 +90,7 @@ class RxJava2WithSpanInstrumentationTest extends AgentInstrumentationSpecificati def error = new IllegalArgumentException("Boom") def observer = new TestObserver() def source = Completable.error(error) - new TracedWithSpan() + newTraced() .completable(source) .subscribe(observer) observer.assertError(error) @@ -98,6 +105,8 @@ class RxJava2WithSpanInstrumentationTest extends AgentInstrumentationSpecificati status ERROR errorEvent(IllegalArgumentException, "Boom") attributes { + "$SemanticAttributes.CODE_NAMESPACE" { it.endsWith(".TracedWithSpan") } + "$SemanticAttributes.CODE_FUNCTION" "completable" } } } @@ -109,7 +118,7 @@ class RxJava2WithSpanInstrumentationTest extends AgentInstrumentationSpecificati def error = new IllegalArgumentException("Boom") def source = CompletableSubject.create() def observer = new TestObserver() - new TracedWithSpan() + newTraced() .completable(source) .subscribe(observer) observer.assertSubscribed() @@ -130,6 +139,8 @@ class RxJava2WithSpanInstrumentationTest extends AgentInstrumentationSpecificati status ERROR errorEvent(IllegalArgumentException, "Boom") attributes { + "$SemanticAttributes.CODE_NAMESPACE" { it.endsWith(".TracedWithSpan") } + "$SemanticAttributes.CODE_FUNCTION" "completable" } } } @@ -140,7 +151,7 @@ class RxJava2WithSpanInstrumentationTest extends AgentInstrumentationSpecificati setup: def source = CompletableSubject.create() def observer = new TestObserver() - new TracedWithSpan() + newTraced() .completable(source) .subscribe(observer) observer.assertSubscribed() @@ -158,6 +169,8 @@ class RxJava2WithSpanInstrumentationTest extends AgentInstrumentationSpecificati kind INTERNAL hasNoParent() attributes { + "$SemanticAttributes.CODE_NAMESPACE" { it.endsWith(".TracedWithSpan") } + "$SemanticAttributes.CODE_FUNCTION" "completable" "rxjava.canceled" true } } @@ -169,7 +182,7 @@ class RxJava2WithSpanInstrumentationTest extends AgentInstrumentationSpecificati setup: def observer = new TestObserver() def source = Maybe.just("Value") - new TracedWithSpan() + newTraced() .maybe(source) .subscribe(observer) observer.assertValue("Value") @@ -183,6 +196,8 @@ class RxJava2WithSpanInstrumentationTest extends AgentInstrumentationSpecificati kind INTERNAL hasNoParent() attributes { + "$SemanticAttributes.CODE_NAMESPACE" { it.endsWith(".TracedWithSpan") } + "$SemanticAttributes.CODE_FUNCTION" "maybe" } } } @@ -193,7 +208,7 @@ class RxJava2WithSpanInstrumentationTest extends AgentInstrumentationSpecificati setup: def observer = new TestObserver() def source = Maybe. empty() - new TracedWithSpan() + newTraced() .maybe(source) .subscribe(observer) observer.assertComplete() @@ -206,6 +221,8 @@ class RxJava2WithSpanInstrumentationTest extends AgentInstrumentationSpecificati kind INTERNAL hasNoParent() attributes { + "$SemanticAttributes.CODE_NAMESPACE" { it.endsWith(".TracedWithSpan") } + "$SemanticAttributes.CODE_FUNCTION" "maybe" } } } @@ -216,7 +233,7 @@ class RxJava2WithSpanInstrumentationTest extends AgentInstrumentationSpecificati setup: def source = MaybeSubject. create() def observer = new TestObserver() - new TracedWithSpan() + newTraced() .maybe(source) .subscribe(observer) observer.assertSubscribed() @@ -236,6 +253,8 @@ class RxJava2WithSpanInstrumentationTest extends AgentInstrumentationSpecificati kind INTERNAL hasNoParent() attributes { + "$SemanticAttributes.CODE_NAMESPACE" { it.endsWith(".TracedWithSpan") } + "$SemanticAttributes.CODE_FUNCTION" "maybe" } } } @@ -247,7 +266,7 @@ class RxJava2WithSpanInstrumentationTest extends AgentInstrumentationSpecificati def error = new IllegalArgumentException("Boom") def observer = new TestObserver() def source = Maybe. error(error) - new TracedWithSpan() + newTraced() .maybe(source) .subscribe(observer) observer.assertError(error) @@ -262,6 +281,8 @@ class RxJava2WithSpanInstrumentationTest extends AgentInstrumentationSpecificati status ERROR errorEvent(IllegalArgumentException, "Boom") attributes { + "$SemanticAttributes.CODE_NAMESPACE" { it.endsWith(".TracedWithSpan") } + "$SemanticAttributes.CODE_FUNCTION" "maybe" } } } @@ -273,7 +294,7 @@ class RxJava2WithSpanInstrumentationTest extends AgentInstrumentationSpecificati def error = new IllegalArgumentException("Boom") def source = MaybeSubject. create() def observer = new TestObserver() - new TracedWithSpan() + newTraced() .maybe(source) .subscribe(observer) observer.assertSubscribed() @@ -294,6 +315,8 @@ class RxJava2WithSpanInstrumentationTest extends AgentInstrumentationSpecificati status ERROR errorEvent(IllegalArgumentException, "Boom") attributes { + "$SemanticAttributes.CODE_NAMESPACE" { it.endsWith(".TracedWithSpan") } + "$SemanticAttributes.CODE_FUNCTION" "maybe" } } } @@ -304,7 +327,7 @@ class RxJava2WithSpanInstrumentationTest extends AgentInstrumentationSpecificati setup: def source = MaybeSubject. create() def observer = new TestObserver() - new TracedWithSpan() + newTraced() .maybe(source) .subscribe(observer) observer.assertSubscribed() @@ -322,6 +345,8 @@ class RxJava2WithSpanInstrumentationTest extends AgentInstrumentationSpecificati kind INTERNAL hasNoParent() attributes { + "$SemanticAttributes.CODE_NAMESPACE" { it.endsWith(".TracedWithSpan") } + "$SemanticAttributes.CODE_FUNCTION" "maybe" "rxjava.canceled" true } } @@ -333,7 +358,7 @@ class RxJava2WithSpanInstrumentationTest extends AgentInstrumentationSpecificati setup: def observer = new TestObserver() def source = Single.just("Value") - new TracedWithSpan() + newTraced() .single(source) .subscribe(observer) observer.assertValue("Value") @@ -347,6 +372,8 @@ class RxJava2WithSpanInstrumentationTest extends AgentInstrumentationSpecificati kind INTERNAL hasNoParent() attributes { + "$SemanticAttributes.CODE_NAMESPACE" { it.endsWith(".TracedWithSpan") } + "$SemanticAttributes.CODE_FUNCTION" "single" } } } @@ -357,7 +384,7 @@ class RxJava2WithSpanInstrumentationTest extends AgentInstrumentationSpecificati setup: def source = SingleSubject. create() def observer = new TestObserver() - new TracedWithSpan() + newTraced() .single(source) .subscribe(observer) observer.assertSubscribed() @@ -377,6 +404,8 @@ class RxJava2WithSpanInstrumentationTest extends AgentInstrumentationSpecificati kind INTERNAL hasNoParent() attributes { + "$SemanticAttributes.CODE_NAMESPACE" { it.endsWith(".TracedWithSpan") } + "$SemanticAttributes.CODE_FUNCTION" "single" } } } @@ -388,7 +417,7 @@ class RxJava2WithSpanInstrumentationTest extends AgentInstrumentationSpecificati def error = new IllegalArgumentException("Boom") def observer = new TestObserver() def source = Single. error(error) - new TracedWithSpan() + newTraced() .single(source) .subscribe(observer) observer.assertError(error) @@ -403,6 +432,8 @@ class RxJava2WithSpanInstrumentationTest extends AgentInstrumentationSpecificati status ERROR errorEvent(IllegalArgumentException, "Boom") attributes { + "$SemanticAttributes.CODE_NAMESPACE" { it.endsWith(".TracedWithSpan") } + "$SemanticAttributes.CODE_FUNCTION" "single" } } } @@ -414,7 +445,7 @@ class RxJava2WithSpanInstrumentationTest extends AgentInstrumentationSpecificati def error = new IllegalArgumentException("Boom") def source = SingleSubject. create() def observer = new TestObserver() - new TracedWithSpan() + newTraced() .single(source) .subscribe(observer) observer.assertSubscribed() @@ -435,6 +466,8 @@ class RxJava2WithSpanInstrumentationTest extends AgentInstrumentationSpecificati status ERROR errorEvent(IllegalArgumentException, "Boom") attributes { + "$SemanticAttributes.CODE_NAMESPACE" { it.endsWith(".TracedWithSpan") } + "$SemanticAttributes.CODE_FUNCTION" "single" } } } @@ -445,7 +478,7 @@ class RxJava2WithSpanInstrumentationTest extends AgentInstrumentationSpecificati setup: def source = SingleSubject. create() def observer = new TestObserver() - new TracedWithSpan() + newTraced() .single(source) .subscribe(observer) observer.assertSubscribed() @@ -463,6 +496,8 @@ class RxJava2WithSpanInstrumentationTest extends AgentInstrumentationSpecificati kind INTERNAL hasNoParent() attributes { + "$SemanticAttributes.CODE_NAMESPACE" { it.endsWith(".TracedWithSpan") } + "$SemanticAttributes.CODE_FUNCTION" "single" "rxjava.canceled" true } } @@ -474,7 +509,7 @@ class RxJava2WithSpanInstrumentationTest extends AgentInstrumentationSpecificati setup: def observer = new TestObserver() def source = Observable. just("Value") - new TracedWithSpan() + newTraced() .observable(source) .subscribe(observer) observer.assertValue("Value") @@ -488,6 +523,8 @@ class RxJava2WithSpanInstrumentationTest extends AgentInstrumentationSpecificati kind INTERNAL hasNoParent() attributes { + "$SemanticAttributes.CODE_NAMESPACE" { it.endsWith(".TracedWithSpan") } + "$SemanticAttributes.CODE_FUNCTION" "observable" } } } @@ -498,7 +535,7 @@ class RxJava2WithSpanInstrumentationTest extends AgentInstrumentationSpecificati setup: def source = UnicastSubject. create() def observer = new TestObserver() - new TracedWithSpan() + newTraced() .observable(source) .subscribe(observer) observer.assertSubscribed() @@ -523,6 +560,8 @@ class RxJava2WithSpanInstrumentationTest extends AgentInstrumentationSpecificati kind INTERNAL hasNoParent() attributes { + "$SemanticAttributes.CODE_NAMESPACE" { it.endsWith(".TracedWithSpan") } + "$SemanticAttributes.CODE_FUNCTION" "observable" } } } @@ -534,7 +573,7 @@ class RxJava2WithSpanInstrumentationTest extends AgentInstrumentationSpecificati def error = new IllegalArgumentException("Boom") def observer = new TestObserver() def source = Observable. error(error) - new TracedWithSpan() + newTraced() .observable(source) .subscribe(observer) observer.assertError(error) @@ -549,6 +588,8 @@ class RxJava2WithSpanInstrumentationTest extends AgentInstrumentationSpecificati status ERROR errorEvent(IllegalArgumentException, "Boom") attributes { + "$SemanticAttributes.CODE_NAMESPACE" { it.endsWith(".TracedWithSpan") } + "$SemanticAttributes.CODE_FUNCTION" "observable" } } } @@ -560,7 +601,7 @@ class RxJava2WithSpanInstrumentationTest extends AgentInstrumentationSpecificati def error = new IllegalArgumentException("Boom") def source = UnicastSubject. create() def observer = new TestObserver() - new TracedWithSpan() + newTraced() .observable(source) .subscribe(observer) observer.assertSubscribed() @@ -587,6 +628,8 @@ class RxJava2WithSpanInstrumentationTest extends AgentInstrumentationSpecificati status ERROR errorEvent(IllegalArgumentException, "Boom") attributes { + "$SemanticAttributes.CODE_NAMESPACE" { it.endsWith(".TracedWithSpan") } + "$SemanticAttributes.CODE_FUNCTION" "observable" } } } @@ -597,7 +640,7 @@ class RxJava2WithSpanInstrumentationTest extends AgentInstrumentationSpecificati setup: def source = UnicastSubject. create() def observer = new TestObserver() - new TracedWithSpan() + newTraced() .observable(source) .subscribe(observer) observer.assertSubscribed() @@ -621,6 +664,8 @@ class RxJava2WithSpanInstrumentationTest extends AgentInstrumentationSpecificati kind INTERNAL hasNoParent() attributes { + "$SemanticAttributes.CODE_NAMESPACE" { it.endsWith(".TracedWithSpan") } + "$SemanticAttributes.CODE_FUNCTION" "observable" "rxjava.canceled" true } } @@ -632,7 +677,7 @@ class RxJava2WithSpanInstrumentationTest extends AgentInstrumentationSpecificati setup: def observer = new TestSubscriber() def source = Flowable. just("Value") - new TracedWithSpan() + newTraced() .flowable(source) .subscribe(observer) observer.assertValue("Value") @@ -646,6 +691,8 @@ class RxJava2WithSpanInstrumentationTest extends AgentInstrumentationSpecificati kind INTERNAL hasNoParent() attributes { + "$SemanticAttributes.CODE_NAMESPACE" { it.endsWith(".TracedWithSpan") } + "$SemanticAttributes.CODE_FUNCTION" "flowable" } } } @@ -656,7 +703,7 @@ class RxJava2WithSpanInstrumentationTest extends AgentInstrumentationSpecificati setup: def source = UnicastProcessor. create() def observer = new TestSubscriber() - new TracedWithSpan() + newTraced() .flowable(source) .subscribe(observer) observer.assertSubscribed() @@ -681,6 +728,8 @@ class RxJava2WithSpanInstrumentationTest extends AgentInstrumentationSpecificati kind INTERNAL hasNoParent() attributes { + "$SemanticAttributes.CODE_NAMESPACE" { it.endsWith(".TracedWithSpan") } + "$SemanticAttributes.CODE_FUNCTION" "flowable" } } } @@ -692,7 +741,7 @@ class RxJava2WithSpanInstrumentationTest extends AgentInstrumentationSpecificati def error = new IllegalArgumentException("Boom") def observer = new TestSubscriber() def source = Flowable. error(error) - new TracedWithSpan() + newTraced() .flowable(source) .subscribe(observer) observer.assertError(error) @@ -707,6 +756,8 @@ class RxJava2WithSpanInstrumentationTest extends AgentInstrumentationSpecificati status ERROR errorEvent(IllegalArgumentException, "Boom") attributes { + "$SemanticAttributes.CODE_NAMESPACE" { it.endsWith(".TracedWithSpan") } + "$SemanticAttributes.CODE_FUNCTION" "flowable" } } } @@ -718,7 +769,7 @@ class RxJava2WithSpanInstrumentationTest extends AgentInstrumentationSpecificati def error = new IllegalArgumentException("Boom") def source = UnicastProcessor. create() def observer = new TestSubscriber() - new TracedWithSpan() + newTraced() .flowable(source) .subscribe(observer) observer.assertSubscribed() @@ -745,6 +796,8 @@ class RxJava2WithSpanInstrumentationTest extends AgentInstrumentationSpecificati status ERROR errorEvent(IllegalArgumentException, "Boom") attributes { + "$SemanticAttributes.CODE_NAMESPACE" { it.endsWith(".TracedWithSpan") } + "$SemanticAttributes.CODE_FUNCTION" "flowable" } } } @@ -755,7 +808,7 @@ class RxJava2WithSpanInstrumentationTest extends AgentInstrumentationSpecificati setup: def source = UnicastProcessor. create() def observer = new TestSubscriber() - new TracedWithSpan() + newTraced() .flowable(source) .subscribe(observer) observer.assertSubscribed() @@ -779,6 +832,8 @@ class RxJava2WithSpanInstrumentationTest extends AgentInstrumentationSpecificati kind INTERNAL hasNoParent() attributes { + "$SemanticAttributes.CODE_NAMESPACE" { it.endsWith(".TracedWithSpan") } + "$SemanticAttributes.CODE_FUNCTION" "flowable" "rxjava.canceled" true } } @@ -790,7 +845,7 @@ class RxJava2WithSpanInstrumentationTest extends AgentInstrumentationSpecificati setup: def observer = new TestSubscriber() def source = Flowable. just("Value") - new TracedWithSpan() + newTraced() .parallelFlowable(source.parallel()) .sequential() .subscribe(observer) @@ -805,6 +860,8 @@ class RxJava2WithSpanInstrumentationTest extends AgentInstrumentationSpecificati kind INTERNAL hasNoParent() attributes { + "$SemanticAttributes.CODE_NAMESPACE" { it.endsWith(".TracedWithSpan") } + "$SemanticAttributes.CODE_FUNCTION" "parallelFlowable" } } } @@ -815,7 +872,7 @@ class RxJava2WithSpanInstrumentationTest extends AgentInstrumentationSpecificati setup: def source = UnicastProcessor. create() def observer = new TestSubscriber() - new TracedWithSpan() + newTraced() .parallelFlowable(source.parallel()) .sequential() .subscribe(observer) @@ -841,6 +898,8 @@ class RxJava2WithSpanInstrumentationTest extends AgentInstrumentationSpecificati kind INTERNAL hasNoParent() attributes { + "$SemanticAttributes.CODE_NAMESPACE" { it.endsWith(".TracedWithSpan") } + "$SemanticAttributes.CODE_FUNCTION" "parallelFlowable" } } } @@ -852,7 +911,7 @@ class RxJava2WithSpanInstrumentationTest extends AgentInstrumentationSpecificati def error = new IllegalArgumentException("Boom") def observer = new TestSubscriber() def source = Flowable. error(error) - new TracedWithSpan() + newTraced() .parallelFlowable(source.parallel()) .sequential() .subscribe(observer) @@ -868,6 +927,8 @@ class RxJava2WithSpanInstrumentationTest extends AgentInstrumentationSpecificati status ERROR errorEvent(IllegalArgumentException, "Boom") attributes { + "$SemanticAttributes.CODE_NAMESPACE" { it.endsWith(".TracedWithSpan") } + "$SemanticAttributes.CODE_FUNCTION" "parallelFlowable" } } } @@ -879,7 +940,7 @@ class RxJava2WithSpanInstrumentationTest extends AgentInstrumentationSpecificati def error = new IllegalArgumentException("Boom") def source = UnicastProcessor. create() def observer = new TestSubscriber() - new TracedWithSpan() + newTraced() .parallelFlowable(source.parallel()) .sequential() .subscribe(observer) @@ -907,6 +968,8 @@ class RxJava2WithSpanInstrumentationTest extends AgentInstrumentationSpecificati status ERROR errorEvent(IllegalArgumentException, "Boom") attributes { + "$SemanticAttributes.CODE_NAMESPACE" { it.endsWith(".TracedWithSpan") } + "$SemanticAttributes.CODE_FUNCTION" "parallelFlowable" } } } @@ -917,7 +980,7 @@ class RxJava2WithSpanInstrumentationTest extends AgentInstrumentationSpecificati setup: def source = UnicastProcessor. create() def observer = new TestSubscriber() - new TracedWithSpan() + newTraced() .parallelFlowable(source.parallel()) .sequential() .subscribe(observer) @@ -942,6 +1005,8 @@ class RxJava2WithSpanInstrumentationTest extends AgentInstrumentationSpecificati kind INTERNAL hasNoParent() attributes { + "$SemanticAttributes.CODE_NAMESPACE" { it.endsWith(".TracedWithSpan") } + "$SemanticAttributes.CODE_FUNCTION" "parallelFlowable" "rxjava.canceled" true } } @@ -953,7 +1018,7 @@ class RxJava2WithSpanInstrumentationTest extends AgentInstrumentationSpecificati setup: def source = new CustomPublisher() def observer = new TestSubscriber() - new TracedWithSpan() + newTraced() .publisher(source) .subscribe(observer) observer.assertSubscribed() @@ -972,6 +1037,8 @@ class RxJava2WithSpanInstrumentationTest extends AgentInstrumentationSpecificati kind INTERNAL hasNoParent() attributes { + "$SemanticAttributes.CODE_NAMESPACE" { it.endsWith(".TracedWithSpan") } + "$SemanticAttributes.CODE_FUNCTION" "publisher" } } } @@ -983,7 +1050,7 @@ class RxJava2WithSpanInstrumentationTest extends AgentInstrumentationSpecificati def error = new IllegalArgumentException("Boom") def source = new CustomPublisher() def observer = new TestSubscriber() - new TracedWithSpan() + newTraced() .publisher(source) .subscribe(observer) observer.assertSubscribed() @@ -1004,6 +1071,8 @@ class RxJava2WithSpanInstrumentationTest extends AgentInstrumentationSpecificati status ERROR errorEvent(IllegalArgumentException, "Boom") attributes { + "$SemanticAttributes.CODE_NAMESPACE" { it.endsWith(".TracedWithSpan") } + "$SemanticAttributes.CODE_FUNCTION" "publisher" } } } @@ -1014,7 +1083,7 @@ class RxJava2WithSpanInstrumentationTest extends AgentInstrumentationSpecificati setup: def source = new CustomPublisher() def observer = new TestSubscriber() - new TracedWithSpan() + newTraced() .publisher(source) .subscribe(observer) observer.assertSubscribed() @@ -1032,6 +1101,8 @@ class RxJava2WithSpanInstrumentationTest extends AgentInstrumentationSpecificati kind INTERNAL hasNoParent() attributes { + "$SemanticAttributes.CODE_NAMESPACE" { it.endsWith(".TracedWithSpan") } + "$SemanticAttributes.CODE_FUNCTION" "publisher" "rxjava.canceled" true } } diff --git a/instrumentation/rxjava/rxjava-2.0/javaagent/src/test/groovy/RxJava2ExtensionWithSpanTest.groovy b/instrumentation/rxjava/rxjava-2.0/javaagent/src/test/groovy/RxJava2ExtensionWithSpanTest.groovy new file mode 100644 index 000000000000..f2b1424ecc3b --- /dev/null +++ b/instrumentation/rxjava/rxjava-2.0/javaagent/src/test/groovy/RxJava2ExtensionWithSpanTest.groovy @@ -0,0 +1,20 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +import io.opentelemetry.instrumentation.rxjava.v2_0.AbstractTracedWithSpan +import io.opentelemetry.instrumentation.rxjava.v2_0.extensionannotation.TracedWithSpan + +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +class RxJava2ExtensionWithSpanTest extends BaseRxJava2WithSpanTest { + + @Override + AbstractTracedWithSpan newTraced() { + return new TracedWithSpan() + } +} diff --git a/instrumentation/rxjava/rxjava-2.0/javaagent/src/test/groovy/RxJava2InstrumentationWithSpanTest.groovy b/instrumentation/rxjava/rxjava-2.0/javaagent/src/test/groovy/RxJava2InstrumentationWithSpanTest.groovy new file mode 100644 index 000000000000..870514f728ac --- /dev/null +++ b/instrumentation/rxjava/rxjava-2.0/javaagent/src/test/groovy/RxJava2InstrumentationWithSpanTest.groovy @@ -0,0 +1,20 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +import io.opentelemetry.instrumentation.rxjava.v2_0.AbstractTracedWithSpan +import io.opentelemetry.instrumentation.rxjava.v2_0.instrumentationannotation.TracedWithSpan + +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +class RxJava2InstrumentationWithSpanTest extends BaseRxJava2WithSpanTest { + + @Override + AbstractTracedWithSpan newTraced() { + return new TracedWithSpan() + } +} diff --git a/instrumentation/rxjava/rxjava-2.0/javaagent/src/test/java/io/opentelemetry/instrumentation/rxjava/v2_0/AbstractTracedWithSpan.java b/instrumentation/rxjava/rxjava-2.0/javaagent/src/test/java/io/opentelemetry/instrumentation/rxjava/v2_0/AbstractTracedWithSpan.java new file mode 100644 index 000000000000..482d400ebe22 --- /dev/null +++ b/instrumentation/rxjava/rxjava-2.0/javaagent/src/test/java/io/opentelemetry/instrumentation/rxjava/v2_0/AbstractTracedWithSpan.java @@ -0,0 +1,31 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.rxjava.v2_0; + +import io.reactivex.Completable; +import io.reactivex.Flowable; +import io.reactivex.Maybe; +import io.reactivex.Observable; +import io.reactivex.Single; +import io.reactivex.parallel.ParallelFlowable; +import org.reactivestreams.Publisher; + +public abstract class AbstractTracedWithSpan { + + public abstract Completable completable(Completable source); + + public abstract Maybe maybe(Maybe source); + + public abstract Single single(Single source); + + public abstract Observable observable(Observable source); + + public abstract Flowable flowable(Flowable source); + + public abstract ParallelFlowable parallelFlowable(ParallelFlowable source); + + public abstract Publisher publisher(Publisher source); +} diff --git a/instrumentation/rxjava/rxjava-2.0/javaagent/src/test/java/io/opentelemetry/instrumentation/rxjava/v2_0/extensionannotation/TracedWithSpan.java b/instrumentation/rxjava/rxjava-2.0/javaagent/src/test/java/io/opentelemetry/instrumentation/rxjava/v2_0/extensionannotation/TracedWithSpan.java new file mode 100644 index 000000000000..15d6dea4c4a1 --- /dev/null +++ b/instrumentation/rxjava/rxjava-2.0/javaagent/src/test/java/io/opentelemetry/instrumentation/rxjava/v2_0/extensionannotation/TracedWithSpan.java @@ -0,0 +1,61 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.rxjava.v2_0.extensionannotation; + +import io.opentelemetry.instrumentation.rxjava.v2_0.AbstractTracedWithSpan; +import io.reactivex.Completable; +import io.reactivex.Flowable; +import io.reactivex.Maybe; +import io.reactivex.Observable; +import io.reactivex.Single; +import io.reactivex.parallel.ParallelFlowable; +import org.reactivestreams.Publisher; + +@SuppressWarnings("deprecation") // testing instrumentation of deprecated class +public class TracedWithSpan extends AbstractTracedWithSpan { + + @Override + @io.opentelemetry.extension.annotations.WithSpan + public Completable completable(Completable source) { + return source; + } + + @Override + @io.opentelemetry.extension.annotations.WithSpan + public Maybe maybe(Maybe source) { + return source; + } + + @Override + @io.opentelemetry.extension.annotations.WithSpan + public Single single(Single source) { + return source; + } + + @Override + @io.opentelemetry.extension.annotations.WithSpan + public Observable observable(Observable source) { + return source; + } + + @Override + @io.opentelemetry.extension.annotations.WithSpan + public Flowable flowable(Flowable source) { + return source; + } + + @Override + @io.opentelemetry.extension.annotations.WithSpan + public ParallelFlowable parallelFlowable(ParallelFlowable source) { + return source; + } + + @Override + @io.opentelemetry.extension.annotations.WithSpan + public Publisher publisher(Publisher source) { + return source; + } +} diff --git a/instrumentation/rxjava/rxjava-2.0/javaagent/src/test/java/io/opentelemetry/instrumentation/rxjava/v2_0/TracedWithSpan.java b/instrumentation/rxjava/rxjava-2.0/javaagent/src/test/java/io/opentelemetry/instrumentation/rxjava/v2_0/instrumentationannotation/TracedWithSpan.java similarity index 73% rename from instrumentation/rxjava/rxjava-2.0/javaagent/src/test/java/io/opentelemetry/instrumentation/rxjava/v2_0/TracedWithSpan.java rename to instrumentation/rxjava/rxjava-2.0/javaagent/src/test/java/io/opentelemetry/instrumentation/rxjava/v2_0/instrumentationannotation/TracedWithSpan.java index 8e6435223bcb..c749b9d3033d 100644 --- a/instrumentation/rxjava/rxjava-2.0/javaagent/src/test/java/io/opentelemetry/instrumentation/rxjava/v2_0/TracedWithSpan.java +++ b/instrumentation/rxjava/rxjava-2.0/javaagent/src/test/java/io/opentelemetry/instrumentation/rxjava/v2_0/instrumentationannotation/TracedWithSpan.java @@ -3,9 +3,10 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.instrumentation.rxjava.v2_0; +package io.opentelemetry.instrumentation.rxjava.v2_0.instrumentationannotation; -import io.opentelemetry.extension.annotations.WithSpan; +import io.opentelemetry.instrumentation.annotations.WithSpan; +import io.opentelemetry.instrumentation.rxjava.v2_0.AbstractTracedWithSpan; import io.reactivex.Completable; import io.reactivex.Flowable; import io.reactivex.Maybe; @@ -14,38 +15,45 @@ import io.reactivex.parallel.ParallelFlowable; import org.reactivestreams.Publisher; -public class TracedWithSpan { +public class TracedWithSpan extends AbstractTracedWithSpan { + @Override @WithSpan public Completable completable(Completable source) { return source; } + @Override @WithSpan public Maybe maybe(Maybe source) { return source; } + @Override @WithSpan public Single single(Single source) { return source; } + @Override @WithSpan public Observable observable(Observable source) { return source; } + @Override @WithSpan public Flowable flowable(Flowable source) { return source; } + @Override @WithSpan public ParallelFlowable parallelFlowable(ParallelFlowable source) { return source; } + @Override @WithSpan public Publisher publisher(Publisher source) { return source; diff --git a/instrumentation/rxjava/rxjava-2.0/library/src/main/java/io/opentelemetry/instrumentation/rxjava/v2_0/RxJava2AsyncOperationEndStrategyBuilder.java b/instrumentation/rxjava/rxjava-2.0/library/src/main/java/io/opentelemetry/instrumentation/rxjava/v2_0/RxJava2AsyncOperationEndStrategyBuilder.java index eff84d4a8854..a62a9a489954 100644 --- a/instrumentation/rxjava/rxjava-2.0/library/src/main/java/io/opentelemetry/instrumentation/rxjava/v2_0/RxJava2AsyncOperationEndStrategyBuilder.java +++ b/instrumentation/rxjava/rxjava-2.0/library/src/main/java/io/opentelemetry/instrumentation/rxjava/v2_0/RxJava2AsyncOperationEndStrategyBuilder.java @@ -5,12 +5,15 @@ package io.opentelemetry.instrumentation.rxjava.v2_0; +import com.google.errorprone.annotations.CanIgnoreReturnValue; + public final class RxJava2AsyncOperationEndStrategyBuilder { private boolean captureExperimentalSpanAttributes; RxJava2AsyncOperationEndStrategyBuilder() {} + @CanIgnoreReturnValue public RxJava2AsyncOperationEndStrategyBuilder setCaptureExperimentalSpanAttributes( boolean captureExperimentalSpanAttributes) { this.captureExperimentalSpanAttributes = captureExperimentalSpanAttributes; diff --git a/instrumentation/rxjava/rxjava-2.0/library/src/main/java/io/opentelemetry/instrumentation/rxjava/v2_0/TracingAssemblyBuilder.java b/instrumentation/rxjava/rxjava-2.0/library/src/main/java/io/opentelemetry/instrumentation/rxjava/v2_0/TracingAssemblyBuilder.java index b705f277f37b..96b669b1d9db 100644 --- a/instrumentation/rxjava/rxjava-2.0/library/src/main/java/io/opentelemetry/instrumentation/rxjava/v2_0/TracingAssemblyBuilder.java +++ b/instrumentation/rxjava/rxjava-2.0/library/src/main/java/io/opentelemetry/instrumentation/rxjava/v2_0/TracingAssemblyBuilder.java @@ -5,11 +5,14 @@ package io.opentelemetry.instrumentation.rxjava.v2_0; +import com.google.errorprone.annotations.CanIgnoreReturnValue; + public final class TracingAssemblyBuilder { private boolean captureExperimentalSpanAttributes; TracingAssemblyBuilder() {} + @CanIgnoreReturnValue public TracingAssemblyBuilder setCaptureExperimentalSpanAttributes( boolean captureExperimentalSpanAttributes) { this.captureExperimentalSpanAttributes = captureExperimentalSpanAttributes; diff --git a/instrumentation/rxjava/rxjava-3-common/library/src/main/java/io/opentelemetry/instrumentation/rxjava/v3/common/RxJava3AsyncOperationEndStrategyBuilder.java b/instrumentation/rxjava/rxjava-3-common/library/src/main/java/io/opentelemetry/instrumentation/rxjava/v3/common/RxJava3AsyncOperationEndStrategyBuilder.java index 88f36798ad49..60c9e994141f 100644 --- a/instrumentation/rxjava/rxjava-3-common/library/src/main/java/io/opentelemetry/instrumentation/rxjava/v3/common/RxJava3AsyncOperationEndStrategyBuilder.java +++ b/instrumentation/rxjava/rxjava-3-common/library/src/main/java/io/opentelemetry/instrumentation/rxjava/v3/common/RxJava3AsyncOperationEndStrategyBuilder.java @@ -5,12 +5,15 @@ package io.opentelemetry.instrumentation.rxjava.v3.common; +import com.google.errorprone.annotations.CanIgnoreReturnValue; + public final class RxJava3AsyncOperationEndStrategyBuilder { private boolean captureExperimentalSpanAttributes; RxJava3AsyncOperationEndStrategyBuilder() {} + @CanIgnoreReturnValue public RxJava3AsyncOperationEndStrategyBuilder setCaptureExperimentalSpanAttributes( boolean captureExperimentalSpanAttributes) { this.captureExperimentalSpanAttributes = captureExperimentalSpanAttributes; diff --git a/instrumentation/rxjava/rxjava-3-common/testing/build.gradle.kts b/instrumentation/rxjava/rxjava-3-common/testing/build.gradle.kts index 319757606854..02795972f9d6 100644 --- a/instrumentation/rxjava/rxjava-3-common/testing/build.gradle.kts +++ b/instrumentation/rxjava/rxjava-3-common/testing/build.gradle.kts @@ -7,6 +7,7 @@ dependencies { api("io.reactivex.rxjava3:rxjava:3.0.12") + implementation(project(":instrumentation-annotations")) implementation("io.opentelemetry:opentelemetry-extension-annotations") implementation("com.google.guava:guava") implementation("org.apache.groovy:groovy") diff --git a/instrumentation/rxjava/rxjava-3-common/testing/src/main/groovy/io/opentelemetry/instrumentation/rxjava/v3/common/AbstractRxJava3WithSpanInstrumentationTest.groovy b/instrumentation/rxjava/rxjava-3-common/testing/src/main/groovy/io/opentelemetry/instrumentation/rxjava/v3/common/AbstractRxJava3WithSpanTest.groovy similarity index 80% rename from instrumentation/rxjava/rxjava-3-common/testing/src/main/groovy/io/opentelemetry/instrumentation/rxjava/v3/common/AbstractRxJava3WithSpanInstrumentationTest.groovy rename to instrumentation/rxjava/rxjava-3-common/testing/src/main/groovy/io/opentelemetry/instrumentation/rxjava/v3/common/AbstractRxJava3WithSpanTest.groovy index 6bf12cc5eb94..65b41ebbf812 100644 --- a/instrumentation/rxjava/rxjava-3-common/testing/src/main/groovy/io/opentelemetry/instrumentation/rxjava/v3/common/AbstractRxJava3WithSpanInstrumentationTest.groovy +++ b/instrumentation/rxjava/rxjava-3-common/testing/src/main/groovy/io/opentelemetry/instrumentation/rxjava/v3/common/AbstractRxJava3WithSpanTest.groovy @@ -7,6 +7,7 @@ package io.opentelemetry.instrumentation.rxjava.v3.common import io.opentelemetry.instrumentation.test.AgentInstrumentationSpecification +import io.opentelemetry.semconv.trace.attributes.SemanticAttributes import io.reactivex.rxjava3.core.Completable import io.reactivex.rxjava3.core.Flowable import io.reactivex.rxjava3.core.Maybe @@ -26,13 +27,15 @@ import org.reactivestreams.Subscription import static io.opentelemetry.api.trace.SpanKind.INTERNAL import static io.opentelemetry.api.trace.StatusCode.ERROR -class AbstractRxJava3WithSpanInstrumentationTest extends AgentInstrumentationSpecification { +abstract class AbstractRxJava3WithSpanTest extends AgentInstrumentationSpecification { + + abstract AbstractTracedWithSpan newTraced() def "should capture span for already completed Completable"() { setup: def observer = new TestObserver() def source = Completable.complete() - new TracedWithSpan() + newTraced() .completable(source) .subscribe(observer) observer.assertComplete() @@ -45,6 +48,8 @@ class AbstractRxJava3WithSpanInstrumentationTest extends AgentInstrumentationSpe kind INTERNAL hasNoParent() attributes { + "$SemanticAttributes.CODE_NAMESPACE" { it.endsWith(".TracedWithSpan") } + "$SemanticAttributes.CODE_FUNCTION" "completable" } } } @@ -55,7 +60,7 @@ class AbstractRxJava3WithSpanInstrumentationTest extends AgentInstrumentationSpe setup: def source = CompletableSubject.create() def observer = new TestObserver() - new TracedWithSpan() + newTraced() .completable(source) .subscribe(observer) @@ -73,6 +78,8 @@ class AbstractRxJava3WithSpanInstrumentationTest extends AgentInstrumentationSpe kind INTERNAL hasNoParent() attributes { + "$SemanticAttributes.CODE_NAMESPACE" { it.endsWith(".TracedWithSpan") } + "$SemanticAttributes.CODE_FUNCTION" "completable" } } } @@ -84,7 +91,7 @@ class AbstractRxJava3WithSpanInstrumentationTest extends AgentInstrumentationSpe def error = new IllegalArgumentException("Boom") def observer = new TestObserver() def source = Completable.error(error) - new TracedWithSpan() + newTraced() .completable(source) .subscribe(observer) observer.assertError(error) @@ -99,6 +106,8 @@ class AbstractRxJava3WithSpanInstrumentationTest extends AgentInstrumentationSpe status ERROR errorEvent(IllegalArgumentException, "Boom") attributes { + "$SemanticAttributes.CODE_NAMESPACE" { it.endsWith(".TracedWithSpan") } + "$SemanticAttributes.CODE_FUNCTION" "completable" } } } @@ -110,7 +119,7 @@ class AbstractRxJava3WithSpanInstrumentationTest extends AgentInstrumentationSpe def error = new IllegalArgumentException("Boom") def source = CompletableSubject.create() def observer = new TestObserver() - new TracedWithSpan() + newTraced() .completable(source) .subscribe(observer) @@ -130,6 +139,8 @@ class AbstractRxJava3WithSpanInstrumentationTest extends AgentInstrumentationSpe status ERROR errorEvent(IllegalArgumentException, "Boom") attributes { + "$SemanticAttributes.CODE_NAMESPACE" { it.endsWith(".TracedWithSpan") } + "$SemanticAttributes.CODE_FUNCTION" "completable" } } } @@ -140,7 +151,7 @@ class AbstractRxJava3WithSpanInstrumentationTest extends AgentInstrumentationSpe setup: def source = CompletableSubject.create() def observer = new TestObserver() - new TracedWithSpan() + newTraced() .completable(source) .subscribe(observer) @@ -157,6 +168,8 @@ class AbstractRxJava3WithSpanInstrumentationTest extends AgentInstrumentationSpe kind INTERNAL hasNoParent() attributes { + "$SemanticAttributes.CODE_NAMESPACE" { it.endsWith(".TracedWithSpan") } + "$SemanticAttributes.CODE_FUNCTION" "completable" "rxjava.canceled" true } } @@ -168,7 +181,7 @@ class AbstractRxJava3WithSpanInstrumentationTest extends AgentInstrumentationSpe setup: def observer = new TestObserver() def source = Maybe.just("Value") - new TracedWithSpan() + newTraced() .maybe(source) .subscribe(observer) observer.assertValue("Value") @@ -182,6 +195,8 @@ class AbstractRxJava3WithSpanInstrumentationTest extends AgentInstrumentationSpe kind INTERNAL hasNoParent() attributes { + "$SemanticAttributes.CODE_NAMESPACE" { it.endsWith(".TracedWithSpan") } + "$SemanticAttributes.CODE_FUNCTION" "maybe" } } } @@ -192,7 +207,7 @@ class AbstractRxJava3WithSpanInstrumentationTest extends AgentInstrumentationSpe setup: def observer = new TestObserver() def source = Maybe. empty() - new TracedWithSpan() + newTraced() .maybe(source) .subscribe(observer) observer.assertComplete() @@ -205,6 +220,8 @@ class AbstractRxJava3WithSpanInstrumentationTest extends AgentInstrumentationSpe kind INTERNAL hasNoParent() attributes { + "$SemanticAttributes.CODE_NAMESPACE" { it.endsWith(".TracedWithSpan") } + "$SemanticAttributes.CODE_FUNCTION" "maybe" } } } @@ -215,7 +232,7 @@ class AbstractRxJava3WithSpanInstrumentationTest extends AgentInstrumentationSpe setup: def source = MaybeSubject. create() def observer = new TestObserver() - new TracedWithSpan() + newTraced() .maybe(source) .subscribe(observer) @@ -234,6 +251,8 @@ class AbstractRxJava3WithSpanInstrumentationTest extends AgentInstrumentationSpe kind INTERNAL hasNoParent() attributes { + "$SemanticAttributes.CODE_NAMESPACE" { it.endsWith(".TracedWithSpan") } + "$SemanticAttributes.CODE_FUNCTION" "maybe" } } } @@ -245,7 +264,7 @@ class AbstractRxJava3WithSpanInstrumentationTest extends AgentInstrumentationSpe def error = new IllegalArgumentException("Boom") def observer = new TestObserver() def source = Maybe. error(error) - new TracedWithSpan() + newTraced() .maybe(source) .subscribe(observer) observer.assertError(error) @@ -260,6 +279,8 @@ class AbstractRxJava3WithSpanInstrumentationTest extends AgentInstrumentationSpe status ERROR errorEvent(IllegalArgumentException, "Boom") attributes { + "$SemanticAttributes.CODE_NAMESPACE" { it.endsWith(".TracedWithSpan") } + "$SemanticAttributes.CODE_FUNCTION" "maybe" } } } @@ -271,7 +292,7 @@ class AbstractRxJava3WithSpanInstrumentationTest extends AgentInstrumentationSpe def error = new IllegalArgumentException("Boom") def source = MaybeSubject. create() def observer = new TestObserver() - new TracedWithSpan() + newTraced() .maybe(source) .subscribe(observer) @@ -291,6 +312,8 @@ class AbstractRxJava3WithSpanInstrumentationTest extends AgentInstrumentationSpe status ERROR errorEvent(IllegalArgumentException, "Boom") attributes { + "$SemanticAttributes.CODE_NAMESPACE" { it.endsWith(".TracedWithSpan") } + "$SemanticAttributes.CODE_FUNCTION" "maybe" } } } @@ -301,7 +324,7 @@ class AbstractRxJava3WithSpanInstrumentationTest extends AgentInstrumentationSpe setup: def source = MaybeSubject. create() def observer = new TestObserver() - new TracedWithSpan() + newTraced() .maybe(source) .subscribe(observer) @@ -318,6 +341,8 @@ class AbstractRxJava3WithSpanInstrumentationTest extends AgentInstrumentationSpe kind INTERNAL hasNoParent() attributes { + "$SemanticAttributes.CODE_NAMESPACE" { it.endsWith(".TracedWithSpan") } + "$SemanticAttributes.CODE_FUNCTION" "maybe" "rxjava.canceled" true } } @@ -329,7 +354,7 @@ class AbstractRxJava3WithSpanInstrumentationTest extends AgentInstrumentationSpe setup: def observer = new TestObserver() def source = Single.just("Value") - new TracedWithSpan() + newTraced() .single(source) .subscribe(observer) observer.assertValue("Value") @@ -343,6 +368,8 @@ class AbstractRxJava3WithSpanInstrumentationTest extends AgentInstrumentationSpe kind INTERNAL hasNoParent() attributes { + "$SemanticAttributes.CODE_NAMESPACE" { it.endsWith(".TracedWithSpan") } + "$SemanticAttributes.CODE_FUNCTION" "single" } } } @@ -353,7 +380,7 @@ class AbstractRxJava3WithSpanInstrumentationTest extends AgentInstrumentationSpe setup: def source = SingleSubject. create() def observer = new TestObserver() - new TracedWithSpan() + newTraced() .single(source) .subscribe(observer) @@ -372,6 +399,8 @@ class AbstractRxJava3WithSpanInstrumentationTest extends AgentInstrumentationSpe kind INTERNAL hasNoParent() attributes { + "$SemanticAttributes.CODE_NAMESPACE" { it.endsWith(".TracedWithSpan") } + "$SemanticAttributes.CODE_FUNCTION" "single" } } } @@ -383,7 +412,7 @@ class AbstractRxJava3WithSpanInstrumentationTest extends AgentInstrumentationSpe def error = new IllegalArgumentException("Boom") def observer = new TestObserver() def source = Single. error(error) - new TracedWithSpan() + newTraced() .single(source) .subscribe(observer) observer.assertError(error) @@ -398,6 +427,8 @@ class AbstractRxJava3WithSpanInstrumentationTest extends AgentInstrumentationSpe status ERROR errorEvent(IllegalArgumentException, "Boom") attributes { + "$SemanticAttributes.CODE_NAMESPACE" { it.endsWith(".TracedWithSpan") } + "$SemanticAttributes.CODE_FUNCTION" "single" } } } @@ -409,7 +440,7 @@ class AbstractRxJava3WithSpanInstrumentationTest extends AgentInstrumentationSpe def error = new IllegalArgumentException("Boom") def source = SingleSubject. create() def observer = new TestObserver() - new TracedWithSpan() + newTraced() .single(source) .subscribe(observer) @@ -429,6 +460,8 @@ class AbstractRxJava3WithSpanInstrumentationTest extends AgentInstrumentationSpe status ERROR errorEvent(IllegalArgumentException, "Boom") attributes { + "$SemanticAttributes.CODE_NAMESPACE" { it.endsWith(".TracedWithSpan") } + "$SemanticAttributes.CODE_FUNCTION" "single" } } } @@ -439,7 +472,7 @@ class AbstractRxJava3WithSpanInstrumentationTest extends AgentInstrumentationSpe setup: def source = SingleSubject. create() def observer = new TestObserver() - new TracedWithSpan() + newTraced() .single(source) .subscribe(observer) @@ -456,6 +489,8 @@ class AbstractRxJava3WithSpanInstrumentationTest extends AgentInstrumentationSpe kind INTERNAL hasNoParent() attributes { + "$SemanticAttributes.CODE_NAMESPACE" { it.endsWith(".TracedWithSpan") } + "$SemanticAttributes.CODE_FUNCTION" "single" "rxjava.canceled" true } } @@ -467,7 +502,7 @@ class AbstractRxJava3WithSpanInstrumentationTest extends AgentInstrumentationSpe setup: def observer = new TestObserver() def source = Observable. just("Value") - new TracedWithSpan() + newTraced() .observable(source) .subscribe(observer) observer.assertValue("Value") @@ -481,6 +516,8 @@ class AbstractRxJava3WithSpanInstrumentationTest extends AgentInstrumentationSpe kind INTERNAL hasNoParent() attributes { + "$SemanticAttributes.CODE_NAMESPACE" { it.endsWith(".TracedWithSpan") } + "$SemanticAttributes.CODE_FUNCTION" "observable" } } } @@ -491,7 +528,7 @@ class AbstractRxJava3WithSpanInstrumentationTest extends AgentInstrumentationSpe setup: def source = UnicastSubject. create() def observer = new TestObserver() - new TracedWithSpan() + newTraced() .observable(source) .subscribe(observer) @@ -515,6 +552,8 @@ class AbstractRxJava3WithSpanInstrumentationTest extends AgentInstrumentationSpe kind INTERNAL hasNoParent() attributes { + "$SemanticAttributes.CODE_NAMESPACE" { it.endsWith(".TracedWithSpan") } + "$SemanticAttributes.CODE_FUNCTION" "observable" } } } @@ -526,7 +565,7 @@ class AbstractRxJava3WithSpanInstrumentationTest extends AgentInstrumentationSpe def error = new IllegalArgumentException("Boom") def observer = new TestObserver() def source = Observable. error(error) - new TracedWithSpan() + newTraced() .observable(source) .subscribe(observer) observer.assertError(error) @@ -541,6 +580,8 @@ class AbstractRxJava3WithSpanInstrumentationTest extends AgentInstrumentationSpe status ERROR errorEvent(IllegalArgumentException, "Boom") attributes { + "$SemanticAttributes.CODE_NAMESPACE" { it.endsWith(".TracedWithSpan") } + "$SemanticAttributes.CODE_FUNCTION" "observable" } } } @@ -552,7 +593,7 @@ class AbstractRxJava3WithSpanInstrumentationTest extends AgentInstrumentationSpe def error = new IllegalArgumentException("Boom") def source = UnicastSubject. create() def observer = new TestObserver() - new TracedWithSpan() + newTraced() .observable(source) .subscribe(observer) @@ -578,6 +619,8 @@ class AbstractRxJava3WithSpanInstrumentationTest extends AgentInstrumentationSpe status ERROR errorEvent(IllegalArgumentException, "Boom") attributes { + "$SemanticAttributes.CODE_NAMESPACE" { it.endsWith(".TracedWithSpan") } + "$SemanticAttributes.CODE_FUNCTION" "observable" } } } @@ -588,7 +631,7 @@ class AbstractRxJava3WithSpanInstrumentationTest extends AgentInstrumentationSpe setup: def source = UnicastSubject. create() def observer = new TestObserver() - new TracedWithSpan() + newTraced() .observable(source) .subscribe(observer) @@ -611,6 +654,8 @@ class AbstractRxJava3WithSpanInstrumentationTest extends AgentInstrumentationSpe kind INTERNAL hasNoParent() attributes { + "$SemanticAttributes.CODE_NAMESPACE" { it.endsWith(".TracedWithSpan") } + "$SemanticAttributes.CODE_FUNCTION" "observable" "rxjava.canceled" true } } @@ -622,7 +667,7 @@ class AbstractRxJava3WithSpanInstrumentationTest extends AgentInstrumentationSpe setup: def observer = new TestSubscriber() def source = Flowable. just("Value") - new TracedWithSpan() + newTraced() .flowable(source) .subscribe(observer) observer.assertValue("Value") @@ -636,6 +681,8 @@ class AbstractRxJava3WithSpanInstrumentationTest extends AgentInstrumentationSpe kind INTERNAL hasNoParent() attributes { + "$SemanticAttributes.CODE_NAMESPACE" { it.endsWith(".TracedWithSpan") } + "$SemanticAttributes.CODE_FUNCTION" "flowable" } } } @@ -646,7 +693,7 @@ class AbstractRxJava3WithSpanInstrumentationTest extends AgentInstrumentationSpe setup: def source = UnicastProcessor. create() def observer = new TestSubscriber() - new TracedWithSpan() + newTraced() .flowable(source) .subscribe(observer) @@ -670,6 +717,8 @@ class AbstractRxJava3WithSpanInstrumentationTest extends AgentInstrumentationSpe kind INTERNAL hasNoParent() attributes { + "$SemanticAttributes.CODE_NAMESPACE" { it.endsWith(".TracedWithSpan") } + "$SemanticAttributes.CODE_FUNCTION" "flowable" } } } @@ -681,7 +730,7 @@ class AbstractRxJava3WithSpanInstrumentationTest extends AgentInstrumentationSpe def error = new IllegalArgumentException("Boom") def observer = new TestSubscriber() def source = Flowable. error(error) - new TracedWithSpan() + newTraced() .flowable(source) .subscribe(observer) observer.assertError(error) @@ -696,6 +745,8 @@ class AbstractRxJava3WithSpanInstrumentationTest extends AgentInstrumentationSpe status ERROR errorEvent(IllegalArgumentException, "Boom") attributes { + "$SemanticAttributes.CODE_NAMESPACE" { it.endsWith(".TracedWithSpan") } + "$SemanticAttributes.CODE_FUNCTION" "flowable" } } } @@ -707,7 +758,7 @@ class AbstractRxJava3WithSpanInstrumentationTest extends AgentInstrumentationSpe def error = new IllegalArgumentException("Boom") def source = UnicastProcessor. create() def observer = new TestSubscriber() - new TracedWithSpan() + newTraced() .flowable(source) .subscribe(observer) @@ -733,6 +784,8 @@ class AbstractRxJava3WithSpanInstrumentationTest extends AgentInstrumentationSpe status ERROR errorEvent(IllegalArgumentException, "Boom") attributes { + "$SemanticAttributes.CODE_NAMESPACE" { it.endsWith(".TracedWithSpan") } + "$SemanticAttributes.CODE_FUNCTION" "flowable" } } } @@ -743,7 +796,7 @@ class AbstractRxJava3WithSpanInstrumentationTest extends AgentInstrumentationSpe setup: def source = UnicastProcessor. create() def observer = new TestSubscriber() - new TracedWithSpan() + newTraced() .flowable(source) .subscribe(observer) @@ -766,6 +819,8 @@ class AbstractRxJava3WithSpanInstrumentationTest extends AgentInstrumentationSpe kind INTERNAL hasNoParent() attributes { + "$SemanticAttributes.CODE_NAMESPACE" { it.endsWith(".TracedWithSpan") } + "$SemanticAttributes.CODE_FUNCTION" "flowable" "rxjava.canceled" true } } @@ -777,7 +832,7 @@ class AbstractRxJava3WithSpanInstrumentationTest extends AgentInstrumentationSpe setup: def observer = new TestSubscriber() def source = Flowable. just("Value") - new TracedWithSpan() + newTraced() .parallelFlowable(source.parallel()) .sequential() .subscribe(observer) @@ -792,6 +847,8 @@ class AbstractRxJava3WithSpanInstrumentationTest extends AgentInstrumentationSpe kind INTERNAL hasNoParent() attributes { + "$SemanticAttributes.CODE_NAMESPACE" { it.endsWith(".TracedWithSpan") } + "$SemanticAttributes.CODE_FUNCTION" "parallelFlowable" } } } @@ -802,7 +859,7 @@ class AbstractRxJava3WithSpanInstrumentationTest extends AgentInstrumentationSpe setup: def source = UnicastProcessor. create() def observer = new TestSubscriber() - new TracedWithSpan() + newTraced() .parallelFlowable(source.parallel()) .sequential() .subscribe(observer) @@ -827,6 +884,8 @@ class AbstractRxJava3WithSpanInstrumentationTest extends AgentInstrumentationSpe kind INTERNAL hasNoParent() attributes { + "$SemanticAttributes.CODE_NAMESPACE" { it.endsWith(".TracedWithSpan") } + "$SemanticAttributes.CODE_FUNCTION" "parallelFlowable" } } } @@ -838,7 +897,7 @@ class AbstractRxJava3WithSpanInstrumentationTest extends AgentInstrumentationSpe def error = new IllegalArgumentException("Boom") def observer = new TestSubscriber() def source = Flowable. error(error) - new TracedWithSpan() + newTraced() .parallelFlowable(source.parallel()) .sequential() .subscribe(observer) @@ -854,6 +913,8 @@ class AbstractRxJava3WithSpanInstrumentationTest extends AgentInstrumentationSpe status ERROR errorEvent(IllegalArgumentException, "Boom") attributes { + "$SemanticAttributes.CODE_NAMESPACE" { it.endsWith(".TracedWithSpan") } + "$SemanticAttributes.CODE_FUNCTION" "parallelFlowable" } } } @@ -865,7 +926,7 @@ class AbstractRxJava3WithSpanInstrumentationTest extends AgentInstrumentationSpe def error = new IllegalArgumentException("Boom") def source = UnicastProcessor. create() def observer = new TestSubscriber() - new TracedWithSpan() + newTraced() .parallelFlowable(source.parallel()) .sequential() .subscribe(observer) @@ -892,6 +953,8 @@ class AbstractRxJava3WithSpanInstrumentationTest extends AgentInstrumentationSpe status ERROR errorEvent(IllegalArgumentException, "Boom") attributes { + "$SemanticAttributes.CODE_NAMESPACE" { it.endsWith(".TracedWithSpan") } + "$SemanticAttributes.CODE_FUNCTION" "parallelFlowable" } } } @@ -902,7 +965,7 @@ class AbstractRxJava3WithSpanInstrumentationTest extends AgentInstrumentationSpe setup: def source = UnicastProcessor. create() def observer = new TestSubscriber() - new TracedWithSpan() + newTraced() .parallelFlowable(source.parallel()) .sequential() .subscribe(observer) @@ -926,6 +989,8 @@ class AbstractRxJava3WithSpanInstrumentationTest extends AgentInstrumentationSpe kind INTERNAL hasNoParent() attributes { + "$SemanticAttributes.CODE_NAMESPACE" { it.endsWith(".TracedWithSpan") } + "$SemanticAttributes.CODE_FUNCTION" "parallelFlowable" "rxjava.canceled" true } } @@ -937,7 +1002,7 @@ class AbstractRxJava3WithSpanInstrumentationTest extends AgentInstrumentationSpe setup: def source = new CustomPublisher() def observer = new TestSubscriber() - new TracedWithSpan() + newTraced() .publisher(source) .subscribe(observer) @@ -955,6 +1020,8 @@ class AbstractRxJava3WithSpanInstrumentationTest extends AgentInstrumentationSpe kind INTERNAL hasNoParent() attributes { + "$SemanticAttributes.CODE_NAMESPACE" { it.endsWith(".TracedWithSpan") } + "$SemanticAttributes.CODE_FUNCTION" "publisher" } } } @@ -966,7 +1033,7 @@ class AbstractRxJava3WithSpanInstrumentationTest extends AgentInstrumentationSpe def error = new IllegalArgumentException("Boom") def source = new CustomPublisher() def observer = new TestSubscriber() - new TracedWithSpan() + newTraced() .publisher(source) .subscribe(observer) @@ -986,6 +1053,8 @@ class AbstractRxJava3WithSpanInstrumentationTest extends AgentInstrumentationSpe status ERROR errorEvent(IllegalArgumentException, "Boom") attributes { + "$SemanticAttributes.CODE_NAMESPACE" { it.endsWith(".TracedWithSpan") } + "$SemanticAttributes.CODE_FUNCTION" "publisher" } } } @@ -996,7 +1065,7 @@ class AbstractRxJava3WithSpanInstrumentationTest extends AgentInstrumentationSpe setup: def source = new CustomPublisher() def observer = new TestSubscriber() - new TracedWithSpan() + newTraced() .publisher(source) .subscribe(observer) @@ -1013,6 +1082,8 @@ class AbstractRxJava3WithSpanInstrumentationTest extends AgentInstrumentationSpe kind INTERNAL hasNoParent() attributes { + "$SemanticAttributes.CODE_NAMESPACE" { it.endsWith(".TracedWithSpan") } + "$SemanticAttributes.CODE_FUNCTION" "publisher" "rxjava.canceled" true } } diff --git a/instrumentation/rxjava/rxjava-3-common/testing/src/main/java/io/opentelemetry/instrumentation/rxjava/v3/common/AbstractTracedWithSpan.java b/instrumentation/rxjava/rxjava-3-common/testing/src/main/java/io/opentelemetry/instrumentation/rxjava/v3/common/AbstractTracedWithSpan.java new file mode 100644 index 000000000000..7f45316a32fd --- /dev/null +++ b/instrumentation/rxjava/rxjava-3-common/testing/src/main/java/io/opentelemetry/instrumentation/rxjava/v3/common/AbstractTracedWithSpan.java @@ -0,0 +1,31 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.rxjava.v3.common; + +import io.reactivex.rxjava3.core.Completable; +import io.reactivex.rxjava3.core.Flowable; +import io.reactivex.rxjava3.core.Maybe; +import io.reactivex.rxjava3.core.Observable; +import io.reactivex.rxjava3.core.Single; +import io.reactivex.rxjava3.parallel.ParallelFlowable; +import org.reactivestreams.Publisher; + +public abstract class AbstractTracedWithSpan { + + public abstract Completable completable(Completable source); + + public abstract Maybe maybe(Maybe source); + + public abstract Single single(Single source); + + public abstract Observable observable(Observable source); + + public abstract Flowable flowable(Flowable source); + + public abstract ParallelFlowable parallelFlowable(ParallelFlowable source); + + public abstract Publisher publisher(Publisher source); +} diff --git a/instrumentation/rxjava/rxjava-3-common/testing/src/main/java/io/opentelemetry/instrumentation/rxjava/v3/common/extensionannotation/TracedWithSpan.java b/instrumentation/rxjava/rxjava-3-common/testing/src/main/java/io/opentelemetry/instrumentation/rxjava/v3/common/extensionannotation/TracedWithSpan.java new file mode 100644 index 000000000000..94c679776345 --- /dev/null +++ b/instrumentation/rxjava/rxjava-3-common/testing/src/main/java/io/opentelemetry/instrumentation/rxjava/v3/common/extensionannotation/TracedWithSpan.java @@ -0,0 +1,61 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.rxjava.v3.common.extensionannotation; + +import io.opentelemetry.instrumentation.rxjava.v3.common.AbstractTracedWithSpan; +import io.reactivex.rxjava3.core.Completable; +import io.reactivex.rxjava3.core.Flowable; +import io.reactivex.rxjava3.core.Maybe; +import io.reactivex.rxjava3.core.Observable; +import io.reactivex.rxjava3.core.Single; +import io.reactivex.rxjava3.parallel.ParallelFlowable; +import org.reactivestreams.Publisher; + +@SuppressWarnings("deprecation") // testing instrumentation of deprecated class +public class TracedWithSpan extends AbstractTracedWithSpan { + + @Override + @io.opentelemetry.extension.annotations.WithSpan + public Completable completable(Completable source) { + return source; + } + + @Override + @io.opentelemetry.extension.annotations.WithSpan + public Maybe maybe(Maybe source) { + return source; + } + + @Override + @io.opentelemetry.extension.annotations.WithSpan + public Single single(Single source) { + return source; + } + + @Override + @io.opentelemetry.extension.annotations.WithSpan + public Observable observable(Observable source) { + return source; + } + + @Override + @io.opentelemetry.extension.annotations.WithSpan + public Flowable flowable(Flowable source) { + return source; + } + + @Override + @io.opentelemetry.extension.annotations.WithSpan + public ParallelFlowable parallelFlowable(ParallelFlowable source) { + return source; + } + + @Override + @io.opentelemetry.extension.annotations.WithSpan + public Publisher publisher(Publisher source) { + return source; + } +} diff --git a/instrumentation/rxjava/rxjava-3-common/testing/src/main/java/io/opentelemetry/instrumentation/rxjava/v3/common/TracedWithSpan.java b/instrumentation/rxjava/rxjava-3-common/testing/src/main/java/io/opentelemetry/instrumentation/rxjava/v3/common/instrumentationannotation/TracedWithSpan.java similarity index 74% rename from instrumentation/rxjava/rxjava-3-common/testing/src/main/java/io/opentelemetry/instrumentation/rxjava/v3/common/TracedWithSpan.java rename to instrumentation/rxjava/rxjava-3-common/testing/src/main/java/io/opentelemetry/instrumentation/rxjava/v3/common/instrumentationannotation/TracedWithSpan.java index 9ee439ae4189..d8f31c2874da 100644 --- a/instrumentation/rxjava/rxjava-3-common/testing/src/main/java/io/opentelemetry/instrumentation/rxjava/v3/common/TracedWithSpan.java +++ b/instrumentation/rxjava/rxjava-3-common/testing/src/main/java/io/opentelemetry/instrumentation/rxjava/v3/common/instrumentationannotation/TracedWithSpan.java @@ -3,9 +3,10 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.instrumentation.rxjava.v3.common; +package io.opentelemetry.instrumentation.rxjava.v3.common.instrumentationannotation; -import io.opentelemetry.extension.annotations.WithSpan; +import io.opentelemetry.instrumentation.annotations.WithSpan; +import io.opentelemetry.instrumentation.rxjava.v3.common.AbstractTracedWithSpan; import io.reactivex.rxjava3.core.Completable; import io.reactivex.rxjava3.core.Flowable; import io.reactivex.rxjava3.core.Maybe; @@ -14,38 +15,45 @@ import io.reactivex.rxjava3.parallel.ParallelFlowable; import org.reactivestreams.Publisher; -public class TracedWithSpan { +public class TracedWithSpan extends AbstractTracedWithSpan { + @Override @WithSpan public Completable completable(Completable source) { return source; } + @Override @WithSpan public Maybe maybe(Maybe source) { return source; } + @Override @WithSpan public Single single(Single source) { return source; } + @Override @WithSpan public Observable observable(Observable source) { return source; } + @Override @WithSpan public Flowable flowable(Flowable source) { return source; } + @Override @WithSpan public ParallelFlowable parallelFlowable(ParallelFlowable source) { return source; } + @Override @WithSpan public Publisher publisher(Publisher source) { return source; diff --git a/instrumentation/rxjava/rxjava-3.0/javaagent/build.gradle.kts b/instrumentation/rxjava/rxjava-3.0/javaagent/build.gradle.kts index 0e8c7db2468d..fbdc5a2fd9b9 100644 --- a/instrumentation/rxjava/rxjava-3.0/javaagent/build.gradle.kts +++ b/instrumentation/rxjava/rxjava-3.0/javaagent/build.gradle.kts @@ -20,6 +20,7 @@ dependencies { testImplementation("io.opentelemetry:opentelemetry-extension-annotations") testImplementation(project(":instrumentation:rxjava:rxjava-3-common:testing")) + testInstrumentation(project(":instrumentation:opentelemetry-extension-annotations-1.0:javaagent")) testInstrumentation(project(":instrumentation:rxjava:rxjava-3.1.1:javaagent")) latestDepTestLibrary("io.reactivex.rxjava3:rxjava:3.1.0") // see rxjava-3.1.1 module diff --git a/instrumentation/rxjava/rxjava-3.0/javaagent/src/test/groovy/RxJava3ExtensionWithSpanTest.groovy b/instrumentation/rxjava/rxjava-3.0/javaagent/src/test/groovy/RxJava3ExtensionWithSpanTest.groovy new file mode 100644 index 000000000000..e910848831e3 --- /dev/null +++ b/instrumentation/rxjava/rxjava-3.0/javaagent/src/test/groovy/RxJava3ExtensionWithSpanTest.groovy @@ -0,0 +1,16 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +import io.opentelemetry.instrumentation.rxjava.v3.common.AbstractRxJava3WithSpanTest +import io.opentelemetry.instrumentation.rxjava.v3.common.AbstractTracedWithSpan +import io.opentelemetry.instrumentation.rxjava.v3.common.extensionannotation.TracedWithSpan + +class RxJava3ExtensionWithSpanTest extends AbstractRxJava3WithSpanTest { + + @Override + AbstractTracedWithSpan newTraced() { + return new TracedWithSpan() + } +} diff --git a/instrumentation/rxjava/rxjava-3.0/javaagent/src/test/groovy/RxJava3InstrumentationWithSpanTest.groovy b/instrumentation/rxjava/rxjava-3.0/javaagent/src/test/groovy/RxJava3InstrumentationWithSpanTest.groovy new file mode 100644 index 000000000000..68938741d3b9 --- /dev/null +++ b/instrumentation/rxjava/rxjava-3.0/javaagent/src/test/groovy/RxJava3InstrumentationWithSpanTest.groovy @@ -0,0 +1,16 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +import io.opentelemetry.instrumentation.rxjava.v3.common.AbstractRxJava3WithSpanTest +import io.opentelemetry.instrumentation.rxjava.v3.common.AbstractTracedWithSpan +import io.opentelemetry.instrumentation.rxjava.v3.common.instrumentationannotation.TracedWithSpan + +class RxJava3InstrumentationWithSpanTest extends AbstractRxJava3WithSpanTest { + + @Override + AbstractTracedWithSpan newTraced() { + return new TracedWithSpan() + } +} diff --git a/instrumentation/rxjava/rxjava-3.0/javaagent/src/test/groovy/RxJava3WithSpanInstrumentationTest.groovy b/instrumentation/rxjava/rxjava-3.0/javaagent/src/test/groovy/RxJava3WithSpanInstrumentationTest.groovy deleted file mode 100644 index 999c0efca180..000000000000 --- a/instrumentation/rxjava/rxjava-3.0/javaagent/src/test/groovy/RxJava3WithSpanInstrumentationTest.groovy +++ /dev/null @@ -1,9 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -import io.opentelemetry.instrumentation.rxjava.v3.common.AbstractRxJava3WithSpanInstrumentationTest - -class RxJava3WithSpanInstrumentationTest extends AbstractRxJava3WithSpanInstrumentationTest { -} diff --git a/instrumentation/rxjava/rxjava-3.0/library/src/main/java/io/opentelemetry/instrumentation/rxjava/v3_0/TracingAssemblyBuilder.java b/instrumentation/rxjava/rxjava-3.0/library/src/main/java/io/opentelemetry/instrumentation/rxjava/v3_0/TracingAssemblyBuilder.java index 5af5dd4d8f49..a81f3cc87921 100644 --- a/instrumentation/rxjava/rxjava-3.0/library/src/main/java/io/opentelemetry/instrumentation/rxjava/v3_0/TracingAssemblyBuilder.java +++ b/instrumentation/rxjava/rxjava-3.0/library/src/main/java/io/opentelemetry/instrumentation/rxjava/v3_0/TracingAssemblyBuilder.java @@ -5,11 +5,14 @@ package io.opentelemetry.instrumentation.rxjava.v3_0; +import com.google.errorprone.annotations.CanIgnoreReturnValue; + public final class TracingAssemblyBuilder { private boolean captureExperimentalSpanAttributes; TracingAssemblyBuilder() {} + @CanIgnoreReturnValue public TracingAssemblyBuilder setCaptureExperimentalSpanAttributes( boolean captureExperimentalSpanAttributes) { this.captureExperimentalSpanAttributes = captureExperimentalSpanAttributes; diff --git a/instrumentation/rxjava/rxjava-3.1.1/javaagent/build.gradle.kts b/instrumentation/rxjava/rxjava-3.1.1/javaagent/build.gradle.kts index 32f399f9714c..c32b99fed23b 100644 --- a/instrumentation/rxjava/rxjava-3.1.1/javaagent/build.gradle.kts +++ b/instrumentation/rxjava/rxjava-3.1.1/javaagent/build.gradle.kts @@ -19,6 +19,7 @@ dependencies { testImplementation(project(":instrumentation:rxjava:rxjava-3-common:testing")) + testInstrumentation(project(":instrumentation:opentelemetry-extension-annotations-1.0:javaagent")) testInstrumentation(project(":instrumentation:rxjava:rxjava-3.0:javaagent")) } diff --git a/instrumentation/rxjava/rxjava-3.1.1/javaagent/src/test/groovy/RxJava3ExtensionWithSpanTest.groovy b/instrumentation/rxjava/rxjava-3.1.1/javaagent/src/test/groovy/RxJava3ExtensionWithSpanTest.groovy new file mode 100644 index 000000000000..e910848831e3 --- /dev/null +++ b/instrumentation/rxjava/rxjava-3.1.1/javaagent/src/test/groovy/RxJava3ExtensionWithSpanTest.groovy @@ -0,0 +1,16 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +import io.opentelemetry.instrumentation.rxjava.v3.common.AbstractRxJava3WithSpanTest +import io.opentelemetry.instrumentation.rxjava.v3.common.AbstractTracedWithSpan +import io.opentelemetry.instrumentation.rxjava.v3.common.extensionannotation.TracedWithSpan + +class RxJava3ExtensionWithSpanTest extends AbstractRxJava3WithSpanTest { + + @Override + AbstractTracedWithSpan newTraced() { + return new TracedWithSpan() + } +} diff --git a/instrumentation/rxjava/rxjava-3.1.1/javaagent/src/test/groovy/RxJava3InstrumentationWithSpanTest.groovy b/instrumentation/rxjava/rxjava-3.1.1/javaagent/src/test/groovy/RxJava3InstrumentationWithSpanTest.groovy new file mode 100644 index 000000000000..68938741d3b9 --- /dev/null +++ b/instrumentation/rxjava/rxjava-3.1.1/javaagent/src/test/groovy/RxJava3InstrumentationWithSpanTest.groovy @@ -0,0 +1,16 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +import io.opentelemetry.instrumentation.rxjava.v3.common.AbstractRxJava3WithSpanTest +import io.opentelemetry.instrumentation.rxjava.v3.common.AbstractTracedWithSpan +import io.opentelemetry.instrumentation.rxjava.v3.common.instrumentationannotation.TracedWithSpan + +class RxJava3InstrumentationWithSpanTest extends AbstractRxJava3WithSpanTest { + + @Override + AbstractTracedWithSpan newTraced() { + return new TracedWithSpan() + } +} diff --git a/instrumentation/rxjava/rxjava-3.1.1/javaagent/src/test/groovy/RxJava3WithSpanInstrumentationTest.groovy b/instrumentation/rxjava/rxjava-3.1.1/javaagent/src/test/groovy/RxJava3WithSpanInstrumentationTest.groovy deleted file mode 100644 index 999c0efca180..000000000000 --- a/instrumentation/rxjava/rxjava-3.1.1/javaagent/src/test/groovy/RxJava3WithSpanInstrumentationTest.groovy +++ /dev/null @@ -1,9 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -import io.opentelemetry.instrumentation.rxjava.v3.common.AbstractRxJava3WithSpanInstrumentationTest - -class RxJava3WithSpanInstrumentationTest extends AbstractRxJava3WithSpanInstrumentationTest { -} diff --git a/instrumentation/rxjava/rxjava-3.1.1/library/src/main/java/io/opentelemetry/instrumentation/rxjava/v3_1_1/TracingAssemblyBuilder.java b/instrumentation/rxjava/rxjava-3.1.1/library/src/main/java/io/opentelemetry/instrumentation/rxjava/v3_1_1/TracingAssemblyBuilder.java index d4d3c70b6e7a..d810201c47dc 100644 --- a/instrumentation/rxjava/rxjava-3.1.1/library/src/main/java/io/opentelemetry/instrumentation/rxjava/v3_1_1/TracingAssemblyBuilder.java +++ b/instrumentation/rxjava/rxjava-3.1.1/library/src/main/java/io/opentelemetry/instrumentation/rxjava/v3_1_1/TracingAssemblyBuilder.java @@ -5,11 +5,14 @@ package io.opentelemetry.instrumentation.rxjava.v3_1_1; +import com.google.errorprone.annotations.CanIgnoreReturnValue; + public final class TracingAssemblyBuilder { private boolean captureExperimentalSpanAttributes; TracingAssemblyBuilder() {} + @CanIgnoreReturnValue public TracingAssemblyBuilder setCaptureExperimentalSpanAttributes( boolean captureExperimentalSpanAttributes) { this.captureExperimentalSpanAttributes = captureExperimentalSpanAttributes; diff --git a/instrumentation/scala-fork-join-2.8/javaagent/build.gradle.kts b/instrumentation/scala-fork-join-2.8/javaagent/build.gradle.kts index 299ce908b81d..9f2ecc6e31d1 100644 --- a/instrumentation/scala-fork-join-2.8/javaagent/build.gradle.kts +++ b/instrumentation/scala-fork-join-2.8/javaagent/build.gradle.kts @@ -42,3 +42,7 @@ tasks { dependsOn(slickTest) } } + +tasks.withType().configureEach { + jvmArgs("-Dotel.instrumentation.common.db-statement-sanitizer.enabled=true") +} diff --git a/instrumentation/servlet/servlet-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v2_2/Servlet2Accessor.java b/instrumentation/servlet/servlet-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v2_2/Servlet2Accessor.java index c4fd3176f5e5..91e1c278e7f1 100644 --- a/instrumentation/servlet/servlet-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v2_2/Servlet2Accessor.java +++ b/instrumentation/servlet/servlet-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v2_2/Servlet2Accessor.java @@ -22,6 +22,16 @@ public Integer getRequestRemotePort(HttpServletRequest httpServletRequest) { return null; } + @Override + public String getRequestLocalAddr(HttpServletRequest request) { + return null; + } + + @Override + public Integer getRequestLocalPort(HttpServletRequest request) { + return null; + } + @Override public void addRequestAsyncListener( HttpServletRequest httpServletRequest, @@ -35,11 +45,6 @@ public int getResponseStatus(HttpServletResponse httpServletResponse) { throw new UnsupportedOperationException(); } - @Override - public String getResponseHeader(HttpServletResponse httpServletResponse, String name) { - return null; - } - @Override public List getResponseHeaderValues( HttpServletResponse httpServletResponse, String name) { diff --git a/instrumentation/servlet/servlet-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v2_2/Servlet2Helper.java b/instrumentation/servlet/servlet-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v2_2/Servlet2Helper.java index ea87f8363678..a8207c6601a9 100644 --- a/instrumentation/servlet/servlet-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v2_2/Servlet2Helper.java +++ b/instrumentation/servlet/servlet-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v2_2/Servlet2Helper.java @@ -31,7 +31,7 @@ public void end( Throwable throwable) { ServletResponseContext responseContext = - new ServletResponseContext<>(response, throwable); + new ServletResponseContext<>(response); responseContext.setStatus(statusCode); instrumenter.end(context, requestContext, responseContext, throwable); diff --git a/instrumentation/servlet/servlet-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v2_2/Servlet2HttpAttributesGetter.java b/instrumentation/servlet/servlet-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v2_2/Servlet2HttpAttributesGetter.java index 78157933d97b..b7faef8d1aaf 100644 --- a/instrumentation/servlet/servlet-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v2_2/Servlet2HttpAttributesGetter.java +++ b/instrumentation/servlet/servlet-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v2_2/Servlet2HttpAttributesGetter.java @@ -25,10 +25,11 @@ public Servlet2HttpAttributesGetter( @Nullable public Integer statusCode( ServletRequestContext requestContext, - ServletResponseContext responseContext) { + ServletResponseContext responseContext, + @Nullable Throwable error) { HttpServletResponse response = responseContext.response(); - if (!accessor.isResponseCommitted(response) && responseContext.error() != null) { + if (!accessor.isResponseCommitted(response) && error != null) { // if response is not committed and there is a throwable set status to 500 / // INTERNAL_SERVER_ERROR, due to servlet spec // https://javaee.github.io/servlet-spec/downloads/servlet-4.0/servlet-4_0_FINAL.pdf: diff --git a/instrumentation/servlet/servlet-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v2_2/Servlet2ResponseSendAdvice.java b/instrumentation/servlet/servlet-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v2_2/Servlet2ResponseSendAdvice.java index 0e2dc6a37bc7..b857b7d61567 100644 --- a/instrumentation/servlet/servlet-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v2_2/Servlet2ResponseSendAdvice.java +++ b/instrumentation/servlet/servlet-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v2_2/Servlet2ResponseSendAdvice.java @@ -9,7 +9,7 @@ import io.opentelemetry.context.Context; import io.opentelemetry.context.Scope; -import io.opentelemetry.instrumentation.api.util.ClassAndMethod; +import io.opentelemetry.instrumentation.api.instrumenter.util.ClassAndMethod; import io.opentelemetry.javaagent.bootstrap.CallDepth; import io.opentelemetry.javaagent.instrumentation.servlet.common.response.HttpServletResponseAdviceHelper; import javax.servlet.http.HttpServletResponse; diff --git a/instrumentation/servlet/servlet-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v2_2/Servlet2Singletons.java b/instrumentation/servlet/servlet-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v2_2/Servlet2Singletons.java index 533f01ad825e..447b99b4a5f5 100644 --- a/instrumentation/servlet/servlet-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v2_2/Servlet2Singletons.java +++ b/instrumentation/servlet/servlet-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v2_2/Servlet2Singletons.java @@ -7,7 +7,7 @@ import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; import io.opentelemetry.instrumentation.api.instrumenter.SpanNameExtractor; -import io.opentelemetry.instrumentation.api.util.ClassAndMethod; +import io.opentelemetry.instrumentation.api.instrumenter.util.ClassAndMethod; import io.opentelemetry.javaagent.instrumentation.servlet.ServletInstrumenterBuilder; import io.opentelemetry.javaagent.instrumentation.servlet.ServletRequestContext; import io.opentelemetry.javaagent.instrumentation.servlet.ServletResponseContext; diff --git a/instrumentation/servlet/servlet-2.2/javaagent/src/test/groovy/HttpServletResponseTest.groovy b/instrumentation/servlet/servlet-2.2/javaagent/src/test/groovy/HttpServletResponseTest.groovy index d0a3f589910b..cc22e9390a79 100644 --- a/instrumentation/servlet/servlet-2.2/javaagent/src/test/groovy/HttpServletResponseTest.groovy +++ b/instrumentation/servlet/servlet-2.2/javaagent/src/test/groovy/HttpServletResponseTest.groovy @@ -5,6 +5,8 @@ import io.opentelemetry.api.trace.SpanKind import io.opentelemetry.instrumentation.test.AgentInstrumentationSpecification +import io.opentelemetry.semconv.trace.attributes.SemanticAttributes + import javax.servlet.http.HttpServlet import spock.lang.Subject @@ -66,18 +68,24 @@ class HttpServletResponseTest extends AgentInstrumentationSpecification { name "TestResponse.sendError" childOf span(0) attributes { + "$SemanticAttributes.CODE_NAMESPACE" TestResponse.name + "$SemanticAttributes.CODE_FUNCTION" "sendError" } } span(2) { name "TestResponse.sendError" childOf span(0) attributes { + "$SemanticAttributes.CODE_NAMESPACE" TestResponse.name + "$SemanticAttributes.CODE_FUNCTION" "sendError" } } span(3) { name "TestResponse.sendRedirect" childOf span(0) attributes { + "$SemanticAttributes.CODE_NAMESPACE" TestResponse.name + "$SemanticAttributes.CODE_FUNCTION" "sendRedirect" } } } diff --git a/instrumentation/servlet/servlet-2.2/javaagent/src/test/groovy/JettyServlet2Test.groovy b/instrumentation/servlet/servlet-2.2/javaagent/src/test/groovy/JettyServlet2Test.groovy index cf22b521b103..e3f592ca4175 100644 --- a/instrumentation/servlet/servlet-2.2/javaagent/src/test/groovy/JettyServlet2Test.groovy +++ b/instrumentation/servlet/servlet-2.2/javaagent/src/test/groovy/JettyServlet2Test.groovy @@ -10,6 +10,7 @@ import io.opentelemetry.instrumentation.test.base.HttpServerTest import io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint import io.opentelemetry.sdk.trace.data.SpanData import io.opentelemetry.semconv.trace.attributes.SemanticAttributes +import org.eclipse.jetty.server.Response import org.eclipse.jetty.server.Server import org.eclipse.jetty.server.handler.ErrorHandler import org.eclipse.jetty.servlet.ServletContextHandler @@ -111,11 +112,14 @@ class JettyServlet2Test extends HttpServerTest implements AgentTestTrait @Override void responseSpan(TraceAssert trace, int index, Object parent, String method = "GET", ServerEndpoint endpoint = SUCCESS) { + def responseMethod = endpoint == REDIRECT ? "sendRedirect" : "sendError" trace.span(index) { - name endpoint == REDIRECT ? "Response.sendRedirect" : "Response.sendError" + name "Response.$responseMethod" kind INTERNAL childOf((SpanData) parent) attributes { + "$SemanticAttributes.CODE_NAMESPACE" Response.name + "$SemanticAttributes.CODE_FUNCTION" responseMethod } } } diff --git a/instrumentation/servlet/servlet-3.0/javaagent/build.gradle.kts b/instrumentation/servlet/servlet-3.0/javaagent/build.gradle.kts index c4b4b417247e..689d229e20fd 100644 --- a/instrumentation/servlet/servlet-3.0/javaagent/build.gradle.kts +++ b/instrumentation/servlet/servlet-3.0/javaagent/build.gradle.kts @@ -38,4 +38,7 @@ dependencies { tasks.withType().configureEach { jvmArgs("-Dotel.instrumentation.servlet.experimental.capture-request-parameters=test-parameter") + // required on jdk17 + jvmArgs("--add-opens=java.base/java.util=ALL-UNNAMED") + jvmArgs("-XX:+IgnoreUnrecognizedVMOptions") } diff --git a/instrumentation/servlet/servlet-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v3_0/Servlet3Accessor.java b/instrumentation/servlet/servlet-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v3_0/Servlet3Accessor.java index 481739261fde..a5a22189764d 100644 --- a/instrumentation/servlet/servlet-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v3_0/Servlet3Accessor.java +++ b/instrumentation/servlet/servlet-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v3_0/Servlet3Accessor.java @@ -26,6 +26,16 @@ public Integer getRequestRemotePort(HttpServletRequest request) { return request.getRemotePort(); } + @Override + public String getRequestLocalAddr(HttpServletRequest request) { + return request.getLocalAddr(); + } + + @Override + public Integer getRequestLocalPort(HttpServletRequest request) { + return request.getLocalPort(); + } + @Override public void addRequestAsyncListener( HttpServletRequest request, @@ -43,11 +53,6 @@ public int getResponseStatus(HttpServletResponse response) { return response.getStatus(); } - @Override - public String getResponseHeader(HttpServletResponse response, String name) { - return response.getHeader(name); - } - @Override public List getResponseHeaderValues(HttpServletResponse response, String name) { Collection values = response.getHeaders(name); diff --git a/instrumentation/servlet/servlet-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v3_0/Servlet3Advice.java b/instrumentation/servlet/servlet-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v3_0/Servlet3Advice.java index 9da2aa307eda..197b87614713 100644 --- a/instrumentation/servlet/servlet-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v3_0/Servlet3Advice.java +++ b/instrumentation/servlet/servlet-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v3_0/Servlet3Advice.java @@ -39,7 +39,6 @@ public static void onEnter( return; } HttpServletRequest httpServletRequest = (HttpServletRequest) request; - callDepth = CallDepth.forClass(AppServerBridge.getCallDepthKey()); callDepth.getAndIncrement(); @@ -50,6 +49,7 @@ public static void onEnter( requestContext = new ServletRequestContext<>(httpServletRequest, servletOrFilter); if (attachedContext == null && helper().shouldStart(currentContext, requestContext)) { context = helper().start(currentContext, requestContext); + helper().setAsyncListenerResponse(httpServletRequest, (HttpServletResponse) response); contextToUpdate = context; diff --git a/instrumentation/servlet/servlet-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v3_0/Servlet3ResponseSendAdvice.java b/instrumentation/servlet/servlet-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v3_0/Servlet3ResponseSendAdvice.java index e253275db44b..9e9a867b8a4f 100644 --- a/instrumentation/servlet/servlet-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v3_0/Servlet3ResponseSendAdvice.java +++ b/instrumentation/servlet/servlet-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v3_0/Servlet3ResponseSendAdvice.java @@ -9,7 +9,7 @@ import io.opentelemetry.context.Context; import io.opentelemetry.context.Scope; -import io.opentelemetry.instrumentation.api.util.ClassAndMethod; +import io.opentelemetry.instrumentation.api.instrumenter.util.ClassAndMethod; import io.opentelemetry.javaagent.bootstrap.CallDepth; import io.opentelemetry.javaagent.instrumentation.servlet.common.response.HttpServletResponseAdviceHelper; import javax.servlet.http.HttpServletResponse; diff --git a/instrumentation/servlet/servlet-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v3_0/Servlet3Singletons.java b/instrumentation/servlet/servlet-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v3_0/Servlet3Singletons.java index 179be5f74d5a..fe4d8a3ce055 100644 --- a/instrumentation/servlet/servlet-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v3_0/Servlet3Singletons.java +++ b/instrumentation/servlet/servlet-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v3_0/Servlet3Singletons.java @@ -6,7 +6,7 @@ package io.opentelemetry.javaagent.instrumentation.servlet.v3_0; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; -import io.opentelemetry.instrumentation.api.util.ClassAndMethod; +import io.opentelemetry.instrumentation.api.instrumenter.util.ClassAndMethod; import io.opentelemetry.instrumentation.api.util.VirtualField; import io.opentelemetry.javaagent.bootstrap.servlet.MappingResolver; import io.opentelemetry.javaagent.instrumentation.servlet.ServletHelper; diff --git a/instrumentation/servlet/servlet-3.0/javaagent/src/test/groovy/HttpServletResponseTest.groovy b/instrumentation/servlet/servlet-3.0/javaagent/src/test/groovy/HttpServletResponseTest.groovy index 1ee25a12cf87..163937448759 100644 --- a/instrumentation/servlet/servlet-3.0/javaagent/src/test/groovy/HttpServletResponseTest.groovy +++ b/instrumentation/servlet/servlet-3.0/javaagent/src/test/groovy/HttpServletResponseTest.groovy @@ -5,6 +5,8 @@ import io.opentelemetry.api.trace.SpanKind import io.opentelemetry.instrumentation.test.AgentInstrumentationSpecification +import io.opentelemetry.semconv.trace.attributes.SemanticAttributes + import javax.servlet.http.HttpServlet import spock.lang.Subject @@ -66,18 +68,24 @@ class HttpServletResponseTest extends AgentInstrumentationSpecification { name "TestResponse.sendError" childOf span(0) attributes { + "$SemanticAttributes.CODE_NAMESPACE" TestResponse.name + "$SemanticAttributes.CODE_FUNCTION" "sendError" } } span(2) { name "TestResponse.sendError" childOf span(0) attributes { + "$SemanticAttributes.CODE_NAMESPACE" TestResponse.name + "$SemanticAttributes.CODE_FUNCTION" "sendError" } } span(3) { name "TestResponse.sendRedirect" childOf span(0) attributes { + "$SemanticAttributes.CODE_NAMESPACE" TestResponse.name + "$SemanticAttributes.CODE_FUNCTION" "sendRedirect" } } } diff --git a/instrumentation/servlet/servlet-3.0/javaagent/src/test/groovy/TomcatServlet3Test.groovy b/instrumentation/servlet/servlet-3.0/javaagent/src/test/groovy/TomcatServlet3Test.groovy index 095b9677b808..93cfeacd8e6b 100644 --- a/instrumentation/servlet/servlet-3.0/javaagent/src/test/groovy/TomcatServlet3Test.groovy +++ b/instrumentation/servlet/servlet-3.0/javaagent/src/test/groovy/TomcatServlet3Test.groovy @@ -233,7 +233,7 @@ abstract class TomcatServlet3Test extends AbstractServlet3Test class ErrorHandlerValve extends ErrorReportValve { @Override protected void report(Request request, Response response, Throwable t) { - if (response.getStatus() < 400 || response.getContentWritten() > 0 || !response.setErrorReported()) { + if (response.getStatus() < 400 || response.getContentWritten() > 0 || !response.isError()) { return } try { diff --git a/instrumentation/servlet/servlet-3.0/javaagent/src/test/java/RequestDispatcherServlet.java b/instrumentation/servlet/servlet-3.0/javaagent/src/test/java/RequestDispatcherServlet.java index a0914660d0c7..25e2e68d379a 100644 --- a/instrumentation/servlet/servlet-3.0/javaagent/src/test/java/RequestDispatcherServlet.java +++ b/instrumentation/servlet/servlet-3.0/javaagent/src/test/java/RequestDispatcherServlet.java @@ -40,4 +40,6 @@ protected void service(HttpServletRequest req, HttpServletResponse resp) dispatcher.include(req, resp); } } + + private RequestDispatcherServlet() {} } diff --git a/instrumentation/servlet/servlet-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/Servlet5Accessor.java b/instrumentation/servlet/servlet-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/Servlet5Accessor.java index 2109e0fe2404..7fe4de161043 100644 --- a/instrumentation/servlet/servlet-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/Servlet5Accessor.java +++ b/instrumentation/servlet/servlet-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/Servlet5Accessor.java @@ -35,16 +35,6 @@ public String getRequestScheme(HttpServletRequest request) { return request.getScheme(); } - @Override - public String getRequestServerName(HttpServletRequest request) { - return request.getServerName(); - } - - @Override - public int getRequestServerPort(HttpServletRequest request) { - return request.getServerPort(); - } - @Override public String getRequestUri(HttpServletRequest request) { return request.getRequestURI(); @@ -75,11 +65,36 @@ public String getRequestMethod(HttpServletRequest request) { return request.getMethod(); } + @Override + public String getRequestServerName(HttpServletRequest request) { + return request.getServerName(); + } + + @Override + public Integer getRequestServerPort(HttpServletRequest request) { + return request.getServerPort(); + } + @Override public String getRequestRemoteAddr(HttpServletRequest request) { return request.getRemoteAddr(); } + @Override + public Integer getRequestRemotePort(HttpServletRequest request) { + return request.getRemotePort(); + } + + @Override + public String getRequestLocalAddr(HttpServletRequest request) { + return request.getLocalAddr(); + } + + @Override + public Integer getRequestLocalPort(HttpServletRequest request) { + return request.getLocalPort(); + } + @Override public String getRequestHeader(HttpServletRequest request, String name) { return request.getHeader(name); @@ -118,16 +133,6 @@ public Principal getRequestUserPrincipal(HttpServletRequest request) { return request.getUserPrincipal(); } - @Override - public Integer getRequestRemotePort(HttpServletRequest request) { - return request.getRemotePort(); - } - - @Override - public int getRequestContentLength(HttpServletRequest request) { - return request.getContentLength(); - } - @Override public void addRequestAsyncListener( HttpServletRequest request, @@ -145,11 +150,6 @@ public int getResponseStatus(HttpServletResponse response) { return response.getStatus(); } - @Override - public String getResponseHeader(HttpServletResponse response, String name) { - return response.getHeader(name); - } - @Override public List getResponseHeaderValues(HttpServletResponse response, String name) { Collection values = response.getHeaders(name); diff --git a/instrumentation/servlet/servlet-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/Servlet5Singletons.java b/instrumentation/servlet/servlet-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/Servlet5Singletons.java index 9bc44c1d17da..cdda410ae88d 100644 --- a/instrumentation/servlet/servlet-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/Servlet5Singletons.java +++ b/instrumentation/servlet/servlet-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/Servlet5Singletons.java @@ -6,7 +6,7 @@ package io.opentelemetry.javaagent.instrumentation.servlet.v5_0; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; -import io.opentelemetry.instrumentation.api.util.ClassAndMethod; +import io.opentelemetry.instrumentation.api.instrumenter.util.ClassAndMethod; import io.opentelemetry.instrumentation.api.util.VirtualField; import io.opentelemetry.javaagent.bootstrap.servlet.MappingResolver; import io.opentelemetry.javaagent.instrumentation.servlet.ServletHelper; diff --git a/instrumentation/servlet/servlet-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/response/ResponseSendAdvice.java b/instrumentation/servlet/servlet-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/response/ResponseSendAdvice.java index 280b69d98e5f..7e10bd211e6a 100644 --- a/instrumentation/servlet/servlet-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/response/ResponseSendAdvice.java +++ b/instrumentation/servlet/servlet-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/response/ResponseSendAdvice.java @@ -9,7 +9,7 @@ import io.opentelemetry.context.Context; import io.opentelemetry.context.Scope; -import io.opentelemetry.instrumentation.api.util.ClassAndMethod; +import io.opentelemetry.instrumentation.api.instrumenter.util.ClassAndMethod; import io.opentelemetry.javaagent.bootstrap.CallDepth; import io.opentelemetry.javaagent.instrumentation.servlet.common.response.HttpServletResponseAdviceHelper; import jakarta.servlet.http.HttpServletResponse; diff --git a/instrumentation/servlet/servlet-5.0/javaagent/src/test/groovy/TomcatServlet5Test.groovy b/instrumentation/servlet/servlet-5.0/javaagent/src/test/groovy/TomcatServlet5Test.groovy index 7d03ba845156..a3b061aff9ea 100644 --- a/instrumentation/servlet/servlet-5.0/javaagent/src/test/groovy/TomcatServlet5Test.groovy +++ b/instrumentation/servlet/servlet-5.0/javaagent/src/test/groovy/TomcatServlet5Test.groovy @@ -233,7 +233,7 @@ abstract class TomcatServlet5Test extends AbstractServlet5Test class ErrorHandlerValve extends ErrorReportValve { @Override protected void report(Request request, Response response, Throwable t) { - if (response.getStatus() < 400 || response.getContentWritten() > 0 || !response.setErrorReported()) { + if (response.getStatus() < 400 || response.getContentWritten() > 0 || !response.isError()) { return } try { diff --git a/instrumentation/servlet/servlet-5.0/javaagent/src/test/java/RequestDispatcherServlet.java b/instrumentation/servlet/servlet-5.0/javaagent/src/test/java/RequestDispatcherServlet.java index e84e56b761a4..3a917bb21999 100644 --- a/instrumentation/servlet/servlet-5.0/javaagent/src/test/java/RequestDispatcherServlet.java +++ b/instrumentation/servlet/servlet-5.0/javaagent/src/test/java/RequestDispatcherServlet.java @@ -40,4 +40,6 @@ protected void service(HttpServletRequest req, HttpServletResponse resp) dispatcher.include(req, resp); } } + + private RequestDispatcherServlet() {} } diff --git a/instrumentation/servlet/servlet-common/bootstrap/src/main/java/io/opentelemetry/javaagent/bootstrap/servlet/AppServerBridge.java b/instrumentation/servlet/servlet-common/bootstrap/src/main/java/io/opentelemetry/javaagent/bootstrap/servlet/AppServerBridge.java index ba31aa8db1aa..a9b926bbdcba 100644 --- a/instrumentation/servlet/servlet-common/bootstrap/src/main/java/io/opentelemetry/javaagent/bootstrap/servlet/AppServerBridge.java +++ b/instrumentation/servlet/servlet-common/bootstrap/src/main/java/io/opentelemetry/javaagent/bootstrap/servlet/AppServerBridge.java @@ -5,6 +5,7 @@ package io.opentelemetry.javaagent.bootstrap.servlet; +import com.google.errorprone.annotations.CanIgnoreReturnValue; import io.opentelemetry.context.Context; import io.opentelemetry.context.ContextKey; import javax.annotation.Nullable; @@ -98,6 +99,7 @@ public static class Builder { * * @return this builder. */ + @CanIgnoreReturnValue public Builder recordException() { recordException = true; return this; @@ -110,6 +112,7 @@ public Builder recordException() { * * @return this builder. */ + @CanIgnoreReturnValue public Builder captureServletAttributes() { captureServletAttributes = true; return this; diff --git a/instrumentation/servlet/servlet-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/AsyncRequestCompletionListener.java b/instrumentation/servlet/servlet-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/AsyncRequestCompletionListener.java index 0e279620e314..1314ac9fee71 100644 --- a/instrumentation/servlet/servlet-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/AsyncRequestCompletionListener.java +++ b/instrumentation/servlet/servlet-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/AsyncRequestCompletionListener.java @@ -32,8 +32,7 @@ public AsyncRequestCompletionListener( @Override public void onComplete(RESPONSE response) { if (responseHandled.compareAndSet(false, true)) { - ServletResponseContext responseContext = - new ServletResponseContext<>(response, null); + ServletResponseContext responseContext = new ServletResponseContext<>(response); Throwable throwable = servletHelper.getAsyncException(requestContext.request()); instrumenter.end(context, requestContext, responseContext, throwable); } @@ -43,8 +42,7 @@ public void onComplete(RESPONSE response) { public void onTimeout(long timeout) { if (responseHandled.compareAndSet(false, true)) { RESPONSE response = servletHelper.getAsyncListenerResponse(requestContext.request()); - ServletResponseContext responseContext = - new ServletResponseContext<>(response, null); + ServletResponseContext responseContext = new ServletResponseContext<>(response); responseContext.setTimeout(timeout); Throwable throwable = servletHelper.getAsyncException(requestContext.request()); instrumenter.end(context, requestContext, responseContext, throwable); @@ -54,8 +52,7 @@ public void onTimeout(long timeout) { @Override public void onError(Throwable throwable, RESPONSE response) { if (responseHandled.compareAndSet(false, true)) { - ServletResponseContext responseContext = - new ServletResponseContext<>(response, throwable); + ServletResponseContext responseContext = new ServletResponseContext<>(response); instrumenter.end(context, requestContext, responseContext, throwable); } } diff --git a/instrumentation/servlet/servlet-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/ServletAccessor.java b/instrumentation/servlet/servlet-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/ServletAccessor.java index 056b70a1e98a..1b1f49b8e0c9 100644 --- a/instrumentation/servlet/servlet-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/ServletAccessor.java +++ b/instrumentation/servlet/servlet-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/ServletAccessor.java @@ -23,10 +23,6 @@ public interface ServletAccessor { String getRequestScheme(REQUEST request); - String getRequestServerName(REQUEST request); - - int getRequestServerPort(REQUEST request); - String getRequestUri(REQUEST request); String getRequestQueryString(REQUEST request); @@ -39,8 +35,18 @@ public interface ServletAccessor { String getRequestMethod(REQUEST request); + String getRequestServerName(REQUEST request); + + Integer getRequestServerPort(REQUEST request); + String getRequestRemoteAddr(REQUEST request); + Integer getRequestRemotePort(REQUEST request); + + String getRequestLocalAddr(REQUEST request); + + Integer getRequestLocalPort(REQUEST request); + String getRequestHeader(REQUEST request, String name); List getRequestHeaderValues(REQUEST request, String name); @@ -55,17 +61,11 @@ public interface ServletAccessor { Principal getRequestUserPrincipal(REQUEST request); - Integer getRequestRemotePort(REQUEST request); - - int getRequestContentLength(REQUEST request); - void addRequestAsyncListener( REQUEST request, ServletAsyncListener listener, Object response); int getResponseStatus(RESPONSE response); - String getResponseHeader(RESPONSE response, String name); - List getResponseHeaderValues(RESPONSE response, String name); boolean isResponseCommitted(RESPONSE response); diff --git a/instrumentation/servlet/servlet-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/ServletErrorCauseExtractor.java b/instrumentation/servlet/servlet-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/ServletErrorCauseExtractor.java index 9538bbe02650..6b949c32369c 100644 --- a/instrumentation/servlet/servlet-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/ServletErrorCauseExtractor.java +++ b/instrumentation/servlet/servlet-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/ServletErrorCauseExtractor.java @@ -19,6 +19,6 @@ public Throwable extract(Throwable error) { if (accessor.isServletException(error) && error.getCause() != null) { error = error.getCause(); } - return ErrorCauseExtractor.jdk().extract(error); + return ErrorCauseExtractor.getDefault().extract(error); } } diff --git a/instrumentation/servlet/servlet-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/ServletHelper.java b/instrumentation/servlet/servlet-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/ServletHelper.java index e4dc1332998f..55dcec6ece91 100644 --- a/instrumentation/servlet/servlet-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/ServletHelper.java +++ b/instrumentation/servlet/servlet-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/ServletHelper.java @@ -57,8 +57,7 @@ public void end( return; } - ServletResponseContext responseContext = - new ServletResponseContext<>(response, throwable); + ServletResponseContext responseContext = new ServletResponseContext<>(response); if (throwable != null || mustEndOnHandlerMethodExit(request)) { instrumenter.end(context, requestContext, responseContext, throwable); } diff --git a/instrumentation/servlet/servlet-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/ServletHttpAttributesGetter.java b/instrumentation/servlet/servlet-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/ServletHttpAttributesGetter.java index 3cb2925d593f..78a560518812 100644 --- a/instrumentation/servlet/servlet-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/ServletHttpAttributesGetter.java +++ b/instrumentation/servlet/servlet-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/ServletHttpAttributesGetter.java @@ -48,26 +48,6 @@ public List requestHeader(ServletRequestContext requestContext, return accessor.getRequestHeaderValues(requestContext.request(), name); } - @Override - @Nullable - public Long requestContentLength( - ServletRequestContext requestContext, - @Nullable ServletResponseContext responseContext) { - int contentLength = accessor.getRequestContentLength(requestContext.request()); - if (contentLength > -1) { - return (long) contentLength; - } - return null; - } - - @Override - @Nullable - public Long requestContentLengthUncompressed( - ServletRequestContext requestContext, - @Nullable ServletResponseContext responseContext) { - return null; - } - @Override @Nullable public String flavor(ServletRequestContext requestContext) { @@ -85,10 +65,11 @@ public String flavor(ServletRequestContext requestContext) { @Nullable public Integer statusCode( ServletRequestContext requestContext, - ServletResponseContext responseContext) { + ServletResponseContext responseContext, + @Nullable Throwable error) { RESPONSE response = responseContext.response(); - if (!accessor.isResponseCommitted(response) && responseContext.error() != null) { + if (!accessor.isResponseCommitted(response) && error != null) { // if response is not committed and there is a throwable set status to 500 / // INTERNAL_SERVER_ERROR, due to servlet spec // https://javaee.github.io/servlet-spec/downloads/servlet-4.0/servlet-4_0_FINAL.pdf: @@ -99,30 +80,6 @@ public Integer statusCode( return accessor.getResponseStatus(response); } - @Override - @Nullable - public Long responseContentLength( - ServletRequestContext requestContext, - ServletResponseContext responseContext) { - String contentLength = accessor.getResponseHeader(responseContext.response(), "Content-Length"); - if (contentLength != null) { - try { - return Long.valueOf(contentLength); - } catch (NumberFormatException ignored) { - // ignore - } - } - return null; - } - - @Override - @Nullable - public Long responseContentLengthUncompressed( - ServletRequestContext requestContext, - ServletResponseContext responseContext) { - return null; - } - @Override public List responseHeader( ServletRequestContext requestContext, @@ -136,10 +93,4 @@ public List responseHeader( public String route(ServletRequestContext requestContext) { return null; } - - @Override - @Nullable - public String serverName(ServletRequestContext requestContext) { - return null; - } } diff --git a/instrumentation/servlet/servlet-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/ServletInstrumenterBuilder.java b/instrumentation/servlet/servlet-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/ServletInstrumenterBuilder.java index 3bc5507f9815..0da91e124e28 100644 --- a/instrumentation/servlet/servlet-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/ServletInstrumenterBuilder.java +++ b/instrumentation/servlet/servlet-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/ServletInstrumenterBuilder.java @@ -5,6 +5,7 @@ package io.opentelemetry.javaagent.instrumentation.servlet; +import com.google.errorprone.annotations.CanIgnoreReturnValue; import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor; import io.opentelemetry.instrumentation.api.instrumenter.ContextCustomizer; @@ -17,7 +18,7 @@ import io.opentelemetry.instrumentation.api.instrumenter.http.HttpServerMetrics; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanNameExtractor; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanStatusExtractor; -import io.opentelemetry.instrumentation.api.instrumenter.net.NetServerAttributesExtractor; +import io.opentelemetry.javaagent.bootstrap.internal.CommonConfig; import java.util.ArrayList; import java.util.List; @@ -32,6 +33,7 @@ public static ServletInstrumenterBuilder return new ServletInstrumenterBuilder<>(); } + @CanIgnoreReturnValue public ServletInstrumenterBuilder addContextCustomizer( ContextCustomizer> contextCustomizer) { contextCustomizers.add(contextCustomizer); @@ -57,8 +59,11 @@ public Instrumenter, ServletResponseContext, ServletResponseContext(accessor)); + return builder.buildServerInstrumenter(new ServletRequestGetter<>(accessor)); } public Instrumenter, ServletResponseContext> build( diff --git a/instrumentation/servlet/servlet-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/ServletNetAttributesGetter.java b/instrumentation/servlet/servlet-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/ServletNetAttributesGetter.java index 08954e4d3396..7410628c5d01 100644 --- a/instrumentation/servlet/servlet-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/ServletNetAttributesGetter.java +++ b/instrumentation/servlet/servlet-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/ServletNetAttributesGetter.java @@ -11,6 +11,7 @@ public class ServletNetAttributesGetter implements NetServerAttributesGetter> { + private final ServletAccessor accessor; public ServletNetAttributesGetter(ServletAccessor accessor) { @@ -23,15 +24,39 @@ public String transport(ServletRequestContext requestContext) { return SemanticAttributes.NetTransportValues.IP_TCP; } + @Nullable @Override + public String hostName(ServletRequestContext requestContext) { + return accessor.getRequestServerName(requestContext.request()); + } + @Nullable - public Integer peerPort(ServletRequestContext requestContext) { - return accessor.getRequestRemotePort(requestContext.request()); + @Override + public Integer hostPort(ServletRequestContext requestContext) { + return accessor.getRequestServerPort(requestContext.request()); } @Override @Nullable - public String peerIp(ServletRequestContext requestContext) { + public String sockPeerAddr(ServletRequestContext requestContext) { return accessor.getRequestRemoteAddr(requestContext.request()); } + + @Override + @Nullable + public Integer sockPeerPort(ServletRequestContext requestContext) { + return accessor.getRequestRemotePort(requestContext.request()); + } + + @Nullable + @Override + public String sockHostAddr(ServletRequestContext requestContext) { + return accessor.getRequestLocalAddr(requestContext.request()); + } + + @Nullable + @Override + public Integer sockHostPort(ServletRequestContext requestContext) { + return accessor.getRequestLocalPort(requestContext.request()); + } } diff --git a/instrumentation/servlet/servlet-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/ServletResponseContext.java b/instrumentation/servlet/servlet-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/ServletResponseContext.java index d186549b53d1..12a8fade7f93 100644 --- a/instrumentation/servlet/servlet-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/ServletResponseContext.java +++ b/instrumentation/servlet/servlet-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/ServletResponseContext.java @@ -7,24 +7,18 @@ public class ServletResponseContext { private final T response; - private final Throwable error; // used for servlet 2.2 where request status can't be extracted from HttpServletResponse private Integer status; private Long timeout; - public ServletResponseContext(T response, Throwable error) { + public ServletResponseContext(T response) { this.response = response; - this.error = error; } public T response() { return response; } - public Throwable error() { - return error; - } - public void setStatus(int status) { this.status = status; } diff --git a/instrumentation/servlet/servlet-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/common/response/HttpServletResponseAdviceHelper.java b/instrumentation/servlet/servlet-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/common/response/HttpServletResponseAdviceHelper.java index c6ac2778fcde..c58e5d1f46a6 100644 --- a/instrumentation/servlet/servlet-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/common/response/HttpServletResponseAdviceHelper.java +++ b/instrumentation/servlet/servlet-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/common/response/HttpServletResponseAdviceHelper.java @@ -8,7 +8,7 @@ import io.opentelemetry.context.Context; import io.opentelemetry.context.Scope; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; -import io.opentelemetry.instrumentation.api.util.ClassAndMethod; +import io.opentelemetry.instrumentation.api.instrumenter.util.ClassAndMethod; import io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge; public class HttpServletResponseAdviceHelper { @@ -65,4 +65,6 @@ public static void stopSpan( instrumenter.end(context, request, null, throwable); } } + + private HttpServletResponseAdviceHelper() {} } diff --git a/instrumentation/servlet/servlet-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/common/response/ResponseInstrumenterFactory.java b/instrumentation/servlet/servlet-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/common/response/ResponseInstrumenterFactory.java index 07f09bcfce71..4269b7c35ffc 100644 --- a/instrumentation/servlet/servlet-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/common/response/ResponseInstrumenterFactory.java +++ b/instrumentation/servlet/servlet-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/common/response/ResponseInstrumenterFactory.java @@ -7,15 +7,22 @@ import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; -import io.opentelemetry.instrumentation.api.util.ClassAndMethod; -import io.opentelemetry.instrumentation.api.util.SpanNames; +import io.opentelemetry.instrumentation.api.instrumenter.code.CodeAttributesExtractor; +import io.opentelemetry.instrumentation.api.instrumenter.code.CodeAttributesGetter; +import io.opentelemetry.instrumentation.api.instrumenter.code.CodeSpanNameExtractor; +import io.opentelemetry.instrumentation.api.instrumenter.util.ClassAndMethod; public final class ResponseInstrumenterFactory { public static Instrumenter createInstrumenter(String instrumentationName) { + CodeAttributesGetter codeAttributesGetter = + ClassAndMethod.codeAttributesGetter(); return Instrumenter.builder( - GlobalOpenTelemetry.get(), instrumentationName, SpanNames::fromMethod) - .newInstrumenter(); + GlobalOpenTelemetry.get(), + instrumentationName, + CodeSpanNameExtractor.create(codeAttributesGetter)) + .addAttributesExtractor(CodeAttributesExtractor.create(codeAttributesGetter)) + .buildInstrumenter(); } private ResponseInstrumenterFactory() {} diff --git a/instrumentation/servlet/servlet-javax-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/javax/JavaxServletAccessor.java b/instrumentation/servlet/servlet-javax-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/javax/JavaxServletAccessor.java index b37c17f326a8..7a300c16313f 100644 --- a/instrumentation/servlet/servlet-javax-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/javax/JavaxServletAccessor.java +++ b/instrumentation/servlet/servlet-javax-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/javax/JavaxServletAccessor.java @@ -25,16 +25,6 @@ public String getRequestScheme(HttpServletRequest request) { return request.getScheme(); } - @Override - public String getRequestServerName(HttpServletRequest request) { - return request.getServerName(); - } - - @Override - public int getRequestServerPort(HttpServletRequest request) { - return request.getServerPort(); - } - @Override public String getRequestUri(HttpServletRequest request) { return request.getRequestURI(); @@ -65,6 +55,16 @@ public String getRequestMethod(HttpServletRequest request) { return request.getMethod(); } + @Override + public String getRequestServerName(HttpServletRequest request) { + return request.getServerName(); + } + + @Override + public Integer getRequestServerPort(HttpServletRequest request) { + return request.getServerPort(); + } + @Override public String getRequestRemoteAddr(HttpServletRequest request) { return request.getRemoteAddr(); @@ -111,11 +111,6 @@ public Principal getRequestUserPrincipal(HttpServletRequest request) { return request.getUserPrincipal(); } - @Override - public int getRequestContentLength(HttpServletRequest request) { - return request.getContentLength(); - } - @Override public boolean isServletException(Throwable throwable) { return throwable instanceof ServletException; diff --git a/instrumentation/spark-2.3/javaagent/src/test/groovy/SparkJavaBasedTest.groovy b/instrumentation/spark-2.3/javaagent/src/test/groovy/SparkJavaBasedTest.groovy index 50e58c276159..837eb59ec8e6 100644 --- a/instrumentation/spark-2.3/javaagent/src/test/groovy/SparkJavaBasedTest.groovy +++ b/instrumentation/spark-2.3/javaagent/src/test/groovy/SparkJavaBasedTest.groovy @@ -47,17 +47,19 @@ class SparkJavaBasedTest extends AgentInstrumentationSpecification { kind SERVER hasNoParent() attributes { - "$SemanticAttributes.NET_TRANSPORT" IP_TCP - "$SemanticAttributes.NET_PEER_IP" "127.0.0.1" - "$SemanticAttributes.NET_PEER_PORT" Long "$SemanticAttributes.HTTP_SCHEME" "http" - "$SemanticAttributes.HTTP_HOST" "localhost:$port" "$SemanticAttributes.HTTP_TARGET" "/param/asdf1234" "$SemanticAttributes.HTTP_METHOD" "GET" "$SemanticAttributes.HTTP_STATUS_CODE" 200 "$SemanticAttributes.HTTP_FLAVOR" "1.1" "$SemanticAttributes.HTTP_USER_AGENT" String "$SemanticAttributes.HTTP_ROUTE" "/param/:param" + "$SemanticAttributes.NET_TRANSPORT" IP_TCP + "$SemanticAttributes.NET_HOST_NAME" "localhost" + "$SemanticAttributes.NET_HOST_PORT" port + "$SemanticAttributes.NET_SOCK_PEER_ADDR" "127.0.0.1" + "$SemanticAttributes.NET_SOCK_PEER_PORT" Long + "$SemanticAttributes.NET_SOCK_HOST_ADDR" "127.0.0.1" } } } diff --git a/instrumentation/spark-2.3/javaagent/src/test/java/TestSparkJavaApplication.java b/instrumentation/spark-2.3/javaagent/src/test/java/TestSparkJavaApplication.java index da146bde5e73..fe6cd875b00d 100644 --- a/instrumentation/spark-2.3/javaagent/src/test/java/TestSparkJavaApplication.java +++ b/instrumentation/spark-2.3/javaagent/src/test/java/TestSparkJavaApplication.java @@ -21,4 +21,6 @@ public static void initSpark(int port) { Spark.awaitInitialization(); } + + private TestSparkJavaApplication() {} } diff --git a/instrumentation/spring/README.md b/instrumentation/spring/README.md index 990ac03d2161..681114a61a4f 100644 --- a/instrumentation/spring/README.md +++ b/instrumentation/spring/README.md @@ -37,6 +37,7 @@ Replace `OPENTELEMETRY_VERSION` with the latest stable [release](https://search. ### Maven #### OpenTelemetry + ```xml io.opentelemetry @@ -51,6 +52,7 @@ Replace `OPENTELEMETRY_VERSION` with the latest stable [release](https://search. ``` #### LoggingSpanExporter + ```xml io.opentelemetry @@ -60,6 +62,7 @@ Replace `OPENTELEMETRY_VERSION` with the latest stable [release](https://search. ``` #### Jaeger Exporter + ```xml io.opentelemetry @@ -76,17 +79,20 @@ Replace `OPENTELEMETRY_VERSION` with the latest stable [release](https://search. ### Gradle #### OpenTelemetry + ```gradle implementation("io.opentelemetry:opentelemetry-api:OPENTELEMETRY_VERSION") implementation("io.opentelemetry:opentelemetry-sdk:OPENTELEMETRY_VERSION") ``` #### LoggingExporter + ```gradle implementation("io.opentelemetry:opentelemetry-exporter-logging:OPENTELEMETRY_VERSION") ``` #### Jaeger Exporter + ```gradle implementation("io.opentelemetry:opentelemetry-exporters-jaeger:OPENTELEMETRY_VERSION") compile "io.grpc:grpc-netty:1.30.2" @@ -599,7 +605,11 @@ To create a sample trace enter `localhost:8080/message` in a browser. This trace ## Auto Instrumentation using Spring Starters -In this tutorial we will create two SpringBoot applications (MainService and TimeService). We will use [opentelemetry-spring-starter](starters/spring-starter) to enable distributed tracing using OpenTelemetry and export spans using the default LoggingSpanExporter. We will also use the [opentelemetry-zipkin-exporter-starter](starters/zipkin-exporter-starter) to export traces to Zipkin. +In this tutorial we will create two SpringBoot applications (MainService and TimeService). We will +use [opentelemetry-spring-boot-starter](starters/spring-boot-starter) to enable distributed tracing using +OpenTelemetry and export spans using the default LoggingSpanExporter. We will also use +the [opentelemetry-zipkin-spring-boot-starter](starters/zipkin-spring-boot-starter) to export traces to +Zipkin. ### OpenTelemetry Spring Starter Dependencies @@ -609,17 +619,19 @@ Replace `OPENTELEMETRY_VERSION` with the latest stable [release](https://search. - Minimum version: `1.1.0` #### Maven + ```xml io.opentelemetry.instrumentation - opentelemetry-spring-starter + opentelemetry-spring-boot-starter OPENTELEMETRY_VERSION ``` #### Gradle + ```gradle -implementation("io.opentelemetry.instrumentation:opentelemetry-spring-starter:OPENTELEMETRY_VERSION") +implementation("io.opentelemetry.instrumentation:opentelemetry-spring-boot-starter:OPENTELEMETRY_VERSION") ``` ### Create two Spring Projects @@ -711,7 +723,7 @@ import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; import io.opentelemetry.context.Scope; -import io.opentelemetry.extension.annotations.WithSpan; +import io.opentelemetry.instrumentation.annotations.WithSpan; import io.opentelemetry.api.trace.Span; import io.opentelemetry.api.trace.Tracer; @@ -759,11 +771,11 @@ To generate a trace, run MainServiceApplication and TimeServiceApplication, and SpanWrapper{ delegate=RecordEventsReadableSpan{traceId=TraceId{traceId=52d6edec17bbf842cf5032ebce2043f8}, spanId=SpanId{spanId=15b72a8e85c842c5}, parentSpanId=SpanId{spanId=57f0106dd1121b54}, name=HTTP GET, kind=CLIENT, attributes={net.peer.name=AttributeValueString{stringValue=localhost}, -http.status_code=AttributeValueLong{longValue=200}, net.peer.port=AttributeValueLong{longValue=8080}, +http.status_code=AttributeValueLong{longValue=200}, net.sock.peer.port=AttributeValueLong{longValue=8080}, http.url=AttributeValueString{stringValue=http://localhost:8080/time}, http.method=AttributeValueString{stringValue=GET}}, status=Status{canonicalCode=OK, description=null}, totalRecordedEvents=0, totalRecordedLinks=0, startEpochNanos=1598409410457933181, endEpochNanos=1598409410925420912}, resolvedLinks=[], resolvedEvents=[], attributes={net.peer.name=AttributeValueString{stringValue=localhost}, -http.status_code=AttributeValueLong{longValue=200}, net.peer.port=AttributeValueLong{longValue=8080}, +http.status_code=AttributeValueLong{longValue=200}, net.sock.peer.port=AttributeValueLong{longValue=8080}, http.url=AttributeValueString{stringValue=http://localhost:8080/time}, http.method=AttributeValueString{stringValue=GET}}, totalAttributeCount=5, totalRecordedEvents=0, status=Status{canonicalCode=OK, description=null}, name=HTTP GET, endEpochNanos=1598409410925420912, hasEnded=true } @@ -771,15 +783,15 @@ totalRecordedEvents=0, status=Status{canonicalCode=OK, description=null}, name=H SpanWrapper{ delegate=RecordEventsReadableSpan{traceId=TraceId{traceId=52d6edec17bbf842cf5032ebce2043f8}, spanId=SpanId{spanId=57f0106dd1121b54}, parentSpanId=SpanId{spanId=0000000000000000}, name=WebMVCTracingFilter.doFilterInteral, kind=SERVER, attributes={http.status_code=AttributeValueLong{longValue=200}, -sampling.probability=AttributeValueDouble{doubleValue=1.0}, net.peer.port=AttributeValueLong{longValue=57578}, +sampling.probability=AttributeValueDouble{doubleValue=1.0}, net.sock.peer.port=AttributeValueLong{longValue=57578}, http.user_agent=AttributeValueString{stringValue=PostmanRuntime/7.26.2}, http.flavor=AttributeValueString{stringValue=1.1}, -http.url=AttributeValueString{stringValue=/message}, net.peer.ip=AttributeValueString{stringValue=0:0:0:0:0:0:0:1}, +http.url=AttributeValueString{stringValue=/message}, net.sock.peer.addr=AttributeValueString{stringValue=0:0:0:0:0:0:0:1}, http.method=AttributeValueString{stringValue=GET}, http.client_ip=AttributeValueString{stringValue=0:0:0:0:0:0:0:1}}, status=Status{canonicalCode=OK, description=null}, totalRecordedEvents=0, totalRecordedLinks=0, startEpochNanos=1598409410399317331, endEpochNanos=1598409411045782693}, resolvedLinks=[], resolvedEvents=[], attributes={http.status_code=AttributeValueLong{longValue=200}, sampling.probability=AttributeValueDouble{doubleValue=1.0}, -net.peer.port=AttributeValueLong{longValue=57578}, http.user_agent=AttributeValueString{stringValue=PostmanRuntime/7.26.2}, +net.sock.peer.port=AttributeValueLong{longValue=57578}, http.user_agent=AttributeValueString{stringValue=PostmanRuntime/7.26.2}, http.flavor=AttributeValueString{stringValue=1.1}, http.url=AttributeValueString{stringValue=/message}, -net.peer.ip=AttributeValueString{stringValue=0:0:0:0:0:0:0:1}, http.method=AttributeValueString{stringValue=GET}, +net.sock.peer.addr=AttributeValueString{stringValue=0:0:0:0:0:0:0:1}, http.method=AttributeValueString{stringValue=GET}, http.client_ip=AttributeValueString{stringValue=0:0:0:0:0:0:0:1}}, totalAttributeCount=9, totalRecordedEvents=0, status=Status{canonicalCode=OK, description=null}, name=WebMVCTracingFilter.doFilterInteral, endEpochNanos=1598409411045782693, hasEnded=true } @@ -801,15 +813,15 @@ status=Status{canonicalCode=OK, description=null}, name=time, endEpochNanos=1598 SpanWrapper{ delegate=RecordEventsReadableSpan{traceId=TraceId{traceId=52d6edec17bbf842cf5032ebce2043f8}, spanId=SpanId{spanId=b4ae77c523215f9d}, parentSpanId=SpanId{spanId=15b72a8e85c842c5}, name=WebMVCTracingFilter.doFilterInteral, kind=SERVER, -attributes={http.status_code=AttributeValueLong{longValue=200}, net.peer.port=AttributeValueLong{longValue=40174}, +attributes={http.status_code=AttributeValueLong{longValue=200}, net.sock.peer.port=AttributeValueLong{longValue=40174}, http.user_agent=AttributeValueString{stringValue=Java/11.0.8}, http.flavor=AttributeValueString{stringValue=1.1}, -http.url=AttributeValueString{stringValue=/time}, net.peer.ip=AttributeValueString{stringValue=127.0.0.1}, +http.url=AttributeValueString{stringValue=/time}, net.sock.peer.addr=AttributeValueString{stringValue=127.0.0.1}, http.method=AttributeValueString{stringValue=GET}, http.client_ip=AttributeValueString{stringValue=127.0.0.1}}, status=Status{canonicalCode=OK, description=null}, totalRecordedEvents=0, totalRecordedLinks=0, startEpochNanos=1598409410680549805, endEpochNanos=1598409410921631068}, resolvedLinks=[], resolvedEvents=[], attributes={http.status_code=AttributeValueLong{longValue=200}, -net.peer.port=AttributeValueLong{longValue=40174}, http.user_agent=AttributeValueString{stringValue=Java/11.0.8}, +net.sock.peer.port=AttributeValueLong{longValue=40174}, http.user_agent=AttributeValueString{stringValue=Java/11.0.8}, http.flavor=AttributeValueString{stringValue=1.1}, http.url=AttributeValueString{stringValue=/time}, -net.peer.ip=AttributeValueString{stringValue=127.0.0.1}, http.method=AttributeValueString{stringValue=GET}, +net.sock.peer.addr=AttributeValueString{stringValue=127.0.0.1}, http.method=AttributeValueString{stringValue=GET}, http.client_ip=AttributeValueString{stringValue=127.0.0.1}}, totalAttributeCount=8, totalRecordedEvents=0, status=Status{canonicalCode=OK, description=null}, name=WebMVCTracingFilter.doFilterInteral, endEpochNanos=1598409410921631068, hasEnded=true } @@ -822,41 +834,31 @@ status=Status{canonicalCode=OK, description=null}, name=WebMVCTracingFilter.doFi To configure OpenTelemetry tracing with the OTLP, Zipkin, or Jaeger span exporters replace the OpenTelemetry Spring Starter dependency with one of the artifacts listed below: #### Maven -```xml +```xml io.opentelemetry.instrumentation - opentelemetry-zipkin-exporter-starter + opentelemetry-zipkin-spring-boot-starter OPENTELEMETRY_VERSION io.opentelemetry.instrumentation - opentelemetry-jaeger-exporter-starter - OPENTELEMETRY_VERSION - - - - - io.opentelemetry.instrumentation - opentelemetry-otlp-exporter-starter + opentelemetry-jaeger-spring-boot-starter OPENTELEMETRY_VERSION ``` #### Gradle -```gradle +```gradle //opentelemetry starter with zipkin configurations -implementation("io.opentelemetry.instrumentation:opentelemetry-zipkin-exporter-starter:OPENTELEMETRY_VERSION") +implementation("io.opentelemetry.instrumentation:opentelemetry-zipkin-spring-boot-starter:OPENTELEMETRY_VERSION") //opentelemetry starter with jaeger configurations -implementation("io.opentelemetry.instrumentation:opentelemetry-jaeger-exporter-starter:OPENTELEMETRY_VERSION") - -//opentelemetry starter with otlp configurations -implementation("io.opentelemetry.instrumentation:opentelemetry-otlp-exporter-starter:OPENTELEMETRY_VERSION") +implementation("io.opentelemetry.instrumentation:opentelemetry-jaeger-spring-boot-starter:OPENTELEMETRY_VERSION") ``` #### Exporter Configuration Properties @@ -878,7 +880,7 @@ Add the following configurations to overwrite the default exporter values listed ### Sample Trace Zipkin To generate a trace using the zipkin exporter follow the steps below: - 1. Replace `opentelemetry-spring-starter` with `opentelemetry-zipkin-starter` in your pom or gradle build file + 1. Replace `opentelemetry-spring-boot-starter` with `opentelemetry-zipkin-spring-boot-starter` in your pom or gradle build file 2. Use the Zipkin [quick starter](https://zipkin.io/pages/quickstart) to download and run the zipkin executable jar - Ensure the zipkin endpoint matches the default value listed in your application properties 3. Run `MainServiceApplication.java` and `TimeServiceApplication.java` @@ -886,7 +888,7 @@ To generate a trace using the zipkin exporter follow the steps below: 5. Navigate to `http://localhost:9411` to see your trace -Shown below is the sample trace generated by `MainService` and `TimeService` using the opentelemetry-zipkin-exporter-starter. +Shown below is the sample trace generated by `MainService` and `TimeService` using the opentelemetry-zipkin-spring-boot-starter. ```json [ @@ -930,8 +932,8 @@ Shown below is the sample trace generated by `MainService` and `TimeService` usi "http.status_code":"200", "http.url":"/time", "http.user_agent":"Java/11.0.8", - "net.peer.ip":"127.0.0.1", - "net.peer.port":"40174" + "net.sock.peer.addr":"127.0.0.1", + "net.sock.peer.port":"40174" } }, { @@ -951,7 +953,7 @@ Shown below is the sample trace generated by `MainService` and `TimeService` usi "http.status_code":"200", "http.url":"http://localhost:8080/time", "net.peer.name":"localhost", - "net.peer.port":"8080" + "net.sock.peer.port":"8080" } }, { @@ -972,8 +974,9 @@ Shown below is the sample trace generated by `MainService` and `TimeService` usi "http.status_code":"200", "http.url":"/message", "http.user_agent":"PostmanRuntime/7.26.2", - "net.peer.ip":"0:0:0:0:0:0:0:1", - "net.peer.port":"57578", + "net.sock.peer.addr":"0:0:0:0:0:0:0:1", + "net.sock.peer.port":"57578", + "net.sock.family":"inet6" "sampling.probability":"1.0" } } 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 5098d34017e0..8a264755b4a1 100644 --- a/instrumentation/spring/spring-batch-3.0/javaagent/build.gradle.kts +++ b/instrumentation/spring/spring-batch-3.0/javaagent/build.gradle.kts @@ -53,5 +53,13 @@ tasks { withType().configureEach { systemProperty("testLatestDeps", findProperty("testLatestDeps")) jvmArgs("-Dotel.instrumentation.spring-batch.enabled=true") + // TODO run tests both with and without experimental span attributes + jvmArgs("-Dotel.instrumentation.spring-batch.experimental-span-attributes=true") } } + +tasks.withType().configureEach { + // required on jdk17 + jvmArgs("--add-opens=java.base/java.lang=ALL-UNNAMED") + jvmArgs("-XX:+IgnoreUnrecognizedVMOptions") +} diff --git a/instrumentation/spring/spring-batch-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/batch/SpringBatchInstrumentationModule.java b/instrumentation/spring/spring-batch-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/batch/SpringBatchInstrumentationModule.java index 777095025567..ffd848644eea 100644 --- a/instrumentation/spring/spring-batch-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/batch/SpringBatchInstrumentationModule.java +++ b/instrumentation/spring/spring-batch-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/batch/SpringBatchInstrumentationModule.java @@ -19,6 +19,7 @@ import io.opentelemetry.javaagent.instrumentation.spring.batch.job.JobFactoryBeanInstrumentation; import io.opentelemetry.javaagent.instrumentation.spring.batch.job.JobParserJobFactoryBeanInstrumentation; import io.opentelemetry.javaagent.instrumentation.spring.batch.step.StepBuilderHelperInstrumentation; +import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import java.util.Arrays; import java.util.List; import net.bytebuddy.matcher.ElementMatcher; @@ -54,7 +55,7 @@ public List typeInstrumentations() { } @Override - public boolean defaultEnabled() { + public boolean defaultEnabled(ConfigProperties config) { // TODO: replace this with an experimental flag return false; } diff --git a/instrumentation/spring/spring-batch-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/batch/chunk/ChunkSingletons.java b/instrumentation/spring/spring-batch-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/batch/chunk/ChunkSingletons.java index ed4d67cefc08..6a2bf7745c8c 100644 --- a/instrumentation/spring/spring-batch-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/batch/chunk/ChunkSingletons.java +++ b/instrumentation/spring/spring-batch-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/batch/chunk/ChunkSingletons.java @@ -32,7 +32,7 @@ public class ChunkSingletons { instrumenterBuilder.addSpanLinksExtractor(ChunkSingletons::extractSpanLinks); } - INSTRUMENTER = instrumenterBuilder.newInstrumenter(); + INSTRUMENTER = instrumenterBuilder.buildInstrumenter(); } public static Instrumenter chunkInstrumenter() { diff --git a/instrumentation/spring/spring-batch-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/batch/item/ItemSingletons.java b/instrumentation/spring/spring-batch-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/batch/item/ItemSingletons.java index 800c00a2592f..86e436d021f9 100644 --- a/instrumentation/spring/spring-batch-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/batch/item/ItemSingletons.java +++ b/instrumentation/spring/spring-batch-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/batch/item/ItemSingletons.java @@ -22,7 +22,7 @@ public class ItemSingletons { private static final Instrumenter INSTRUMENTER = Instrumenter.builder( GlobalOpenTelemetry.get(), instrumentationName(), str -> str) - .newInstrumenter(); + .buildInstrumenter(); public static Instrumenter itemInstrumenter() { return INSTRUMENTER; diff --git a/instrumentation/spring/spring-batch-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/batch/job/JobSingletons.java b/instrumentation/spring/spring-batch-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/batch/job/JobSingletons.java index fff90877b95e..77c07a0a4dd9 100644 --- a/instrumentation/spring/spring-batch-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/batch/job/JobSingletons.java +++ b/instrumentation/spring/spring-batch-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/batch/job/JobSingletons.java @@ -8,15 +8,31 @@ import static io.opentelemetry.javaagent.instrumentation.spring.batch.SpringBatchInstrumentationConfig.instrumentationName; import io.opentelemetry.api.GlobalOpenTelemetry; +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; +import io.opentelemetry.instrumentation.api.instrumenter.InstrumenterBuilder; +import io.opentelemetry.javaagent.bootstrap.internal.InstrumentationConfig; import org.springframework.batch.core.JobExecution; public class JobSingletons { - private static final Instrumenter INSTRUMENTER = - Instrumenter.builder( - GlobalOpenTelemetry.get(), instrumentationName(), JobSingletons::extractSpanName) - .newInstrumenter(); + private static final boolean CAPTURE_EXPERIMENTAL_SPAN_ATTRIBUTES = + InstrumentationConfig.get() + .getBoolean("otel.instrumentation.spring-batch.experimental-span-attributes", false); + + private static final Instrumenter INSTRUMENTER; + + static { + InstrumenterBuilder instrumenter = + Instrumenter.builder( + GlobalOpenTelemetry.get(), instrumentationName(), JobSingletons::extractSpanName); + if (CAPTURE_EXPERIMENTAL_SPAN_ATTRIBUTES) { + instrumenter.addAttributesExtractor( + AttributesExtractor.constant(AttributeKey.stringKey("job.system"), "spring_batch")); + } + INSTRUMENTER = instrumenter.buildInstrumenter(); + } private static String extractSpanName(JobExecution jobExecution) { return "BatchJob " + jobExecution.getJobInstance().getJobName(); diff --git a/instrumentation/spring/spring-batch-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/batch/step/StepSingletons.java b/instrumentation/spring/spring-batch-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/batch/step/StepSingletons.java index ed9c7cb982ab..a4c052d7dc3e 100644 --- a/instrumentation/spring/spring-batch-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/batch/step/StepSingletons.java +++ b/instrumentation/spring/spring-batch-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/batch/step/StepSingletons.java @@ -16,7 +16,7 @@ public class StepSingletons { private static final Instrumenter INSTRUMENTER = Instrumenter.builder( GlobalOpenTelemetry.get(), instrumentationName(), StepSingletons::spanName) - .newInstrumenter(); + .buildInstrumenter(); public static Instrumenter stepInstrumenter() { return INSTRUMENTER; diff --git a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/SpringBatchTest.groovy b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/SpringBatchTest.groovy index d6148be5448a..d91d38ca6ba9 100644 --- a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/SpringBatchTest.groovy +++ b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/SpringBatchTest.groovy @@ -27,16 +27,21 @@ abstract class SpringBatchTest extends AgentInstrumentationSpecification { span(0) { name "BatchJob taskletJob" kind INTERNAL + attributes { + "job.system" "spring_batch" + } } span(1) { name "BatchJob taskletJob.step" kind INTERNAL childOf span(0) + attributes {} } span(2) { name "BatchJob taskletJob.step.Tasklet" kind INTERNAL childOf span(1) + attributes {} } } } @@ -52,11 +57,15 @@ abstract class SpringBatchTest extends AgentInstrumentationSpecification { span(0) { name "BatchJob taskletJob" kind INTERNAL + attributes { + "job.system" "spring_batch" + } } span(1) { name "BatchJob taskletJob.step" kind INTERNAL childOf span(0) + attributes {} } span(2) { name "BatchJob taskletJob.step.Tasklet" @@ -64,6 +73,7 @@ abstract class SpringBatchTest extends AgentInstrumentationSpecification { childOf span(1) status ERROR errorEvent IllegalStateException, "fail" + attributes {} } } } @@ -79,36 +89,45 @@ abstract class SpringBatchTest extends AgentInstrumentationSpecification { span(0) { name "BatchJob itemsAndTaskletJob" kind INTERNAL + attributes { + "job.system" "spring_batch" + } } span(1) { name "BatchJob itemsAndTaskletJob.itemStep" kind INTERNAL childOf span(0) + attributes {} } span(2) { name "BatchJob itemsAndTaskletJob.itemStep.Chunk" kind INTERNAL childOf span(1) + attributes {} } span(3) { name "BatchJob itemsAndTaskletJob.itemStep.Chunk" kind INTERNAL childOf span(1) + attributes {} } span(4) { name "BatchJob itemsAndTaskletJob.itemStep.Chunk" kind INTERNAL childOf span(1) + attributes {} } span(5) { name "BatchJob itemsAndTaskletJob.taskletStep" kind INTERNAL childOf span(0) + attributes {} } span(6) { name "BatchJob itemsAndTaskletJob.taskletStep.Tasklet" kind INTERNAL childOf span(5) + attributes {} } } } @@ -124,26 +143,33 @@ abstract class SpringBatchTest extends AgentInstrumentationSpecification { span(0) { name "BatchJob flowJob" kind INTERNAL + attributes { + "job.system" "spring_batch" + } } span(1) { name "BatchJob flowJob.flowStep1" kind INTERNAL childOf span(0) + attributes {} } span(2) { name "BatchJob flowJob.flowStep1.Tasklet" kind INTERNAL childOf span(1) + attributes {} } span(3) { name "BatchJob flowJob.flowStep2" kind INTERNAL childOf span(0) + attributes {} } span(4) { name "BatchJob flowJob.flowStep2.Tasklet" kind INTERNAL childOf span(3) + attributes {} } } } @@ -159,26 +185,33 @@ abstract class SpringBatchTest extends AgentInstrumentationSpecification { span(0) { name "BatchJob splitJob" kind INTERNAL + attributes { + "job.system" "spring_batch" + } } span(1) { name ~/BatchJob splitJob\.splitFlowStep[12]/ kind INTERNAL childOf span(0) + attributes {} } span(2) { name ~/BatchJob splitJob\.splitFlowStep[12]\.Tasklet/ kind INTERNAL childOf span(1) + attributes {} } span(3) { name ~/BatchJob splitJob\.splitFlowStep[12]/ kind INTERNAL childOf span(0) + attributes {} } span(4) { name ~/BatchJob splitJob\.splitFlowStep[12]\.Tasklet/ kind INTERNAL childOf span(3) + attributes {} } } } @@ -194,26 +227,33 @@ abstract class SpringBatchTest extends AgentInstrumentationSpecification { span(0) { name "BatchJob decisionJob" kind INTERNAL + attributes { + "job.system" "spring_batch" + } } span(1) { name "BatchJob decisionJob.decisionStepStart" kind INTERNAL childOf span(0) + attributes {} } span(2) { name "BatchJob decisionJob.decisionStepStart.Tasklet" kind INTERNAL childOf span(1) + attributes {} } span(3) { name "BatchJob decisionJob.decisionStepLeft" kind INTERNAL childOf span(0) + attributes {} } span(4) { name "BatchJob decisionJob.decisionStepLeft.Tasklet" kind INTERNAL childOf span(3) + attributes {} } } } @@ -229,42 +269,52 @@ abstract class SpringBatchTest extends AgentInstrumentationSpecification { span(0) { name "BatchJob partitionedJob" kind INTERNAL + attributes { + "job.system" "spring_batch" + } } span(1) { def stepName = hasPartitionManagerStep() ? "partitionManagerStep" : "partitionWorkerStep" name "BatchJob partitionedJob.$stepName" kind INTERNAL childOf span(0) + attributes {} } span(2) { name ~/BatchJob partitionedJob.partitionWorkerStep:partition[01]/ kind INTERNAL childOf span(1) + attributes {} } span(3) { name ~/BatchJob partitionedJob.partitionWorkerStep:partition[01].Chunk/ kind INTERNAL childOf span(2) + attributes {} } span(4) { name ~/BatchJob partitionedJob.partitionWorkerStep:partition[01].Chunk/ kind INTERNAL childOf span(2) + attributes {} } span(5) { name ~/BatchJob partitionedJob.partitionWorkerStep:partition[01]/ kind INTERNAL childOf span(1) + attributes {} } span(6) { name ~/BatchJob partitionedJob.partitionWorkerStep:partition[01].Chunk/ kind INTERNAL childOf span(5) + attributes {} } span(7) { name ~/BatchJob partitionedJob.partitionWorkerStep:partition[01].Chunk/ kind INTERNAL childOf span(5) + attributes {} } } } diff --git a/instrumentation/spring/spring-boot-actuator-autoconfigure-2.0/javaagent/build.gradle.kts b/instrumentation/spring/spring-boot-actuator-autoconfigure-2.0/javaagent/build.gradle.kts index 9962eebf70e0..87188f0f8fb3 100644 --- a/instrumentation/spring/spring-boot-actuator-autoconfigure-2.0/javaagent/build.gradle.kts +++ b/instrumentation/spring/spring-boot-actuator-autoconfigure-2.0/javaagent/build.gradle.kts @@ -18,3 +18,17 @@ dependencies { implementation(project(":instrumentation:micrometer:micrometer-1.5:javaagent")) } + +tasks.withType().configureEach { + // required on jdk17 + jvmArgs("--add-opens=java.base/java.lang=ALL-UNNAMED") + jvmArgs("-XX:+IgnoreUnrecognizedVMOptions") +} + +configurations.testRuntimeClasspath { + resolutionStrategy { + // requires old logback (and therefore also old slf4j) + force("ch.qos.logback:logback-classic:1.2.11") + force("org.slf4j:slf4j-api:1.7.36") + } +} diff --git a/instrumentation/spring/spring-boot-actuator-autoconfigure-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/actuator/SpringBootActuatorIgnoredTypesConfigurer.java b/instrumentation/spring/spring-boot-actuator-autoconfigure-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/actuator/SpringBootActuatorIgnoredTypesConfigurer.java index 3a5b27e0b022..31e19c6d674d 100644 --- a/instrumentation/spring/spring-boot-actuator-autoconfigure-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/actuator/SpringBootActuatorIgnoredTypesConfigurer.java +++ b/instrumentation/spring/spring-boot-actuator-autoconfigure-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/actuator/SpringBootActuatorIgnoredTypesConfigurer.java @@ -14,7 +14,7 @@ public class SpringBootActuatorIgnoredTypesConfigurer implements IgnoredTypesConfigurer { @Override - public void configure(ConfigProperties config, IgnoredTypesBuilder builder) { + public void configure(IgnoredTypesBuilder builder, ConfigProperties config) { builder.allowClass("org.springframework.boot.autoconfigure.AutoConfigurationImportSelector"); } } diff --git a/instrumentation/spring/spring-boot-actuator-autoconfigure-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/actuator/SpringBootActuatorInstrumentationModule.java b/instrumentation/spring/spring-boot-actuator-autoconfigure-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/actuator/SpringBootActuatorInstrumentationModule.java index fdb2f71e4568..1aa98998b3de 100644 --- a/instrumentation/spring/spring-boot-actuator-autoconfigure-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/actuator/SpringBootActuatorInstrumentationModule.java +++ b/instrumentation/spring/spring-boot-actuator-autoconfigure-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/actuator/SpringBootActuatorInstrumentationModule.java @@ -5,6 +5,7 @@ package io.opentelemetry.javaagent.instrumentation.spring.actuator; +import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed; import static java.util.Collections.singletonList; import com.google.auto.service.AutoService; @@ -12,6 +13,7 @@ import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import java.util.List; +import net.bytebuddy.matcher.ElementMatcher; @AutoService(InstrumentationModule.class) public class SpringBootActuatorInstrumentationModule extends InstrumentationModule { @@ -20,6 +22,12 @@ public SpringBootActuatorInstrumentationModule() { super("spring-boot-actuator-autoconfigure", "spring-boot-actuator-autoconfigure-2.0"); } + @Override + public ElementMatcher.Junction classLoaderMatcher() { + // added in micrometer-core 1.5 + return hasClassesNamed("io.micrometer.core.instrument.config.validate.Validated"); + } + @Override public void registerHelperResources(HelperResourceBuilder helperResourceBuilder) { // autoconfigure classes are loaded as resources using ClassPathResource @@ -30,11 +38,6 @@ public void registerHelperResources(HelperResourceBuilder helperResourceBuilder) "io/opentelemetry/javaagent/instrumentation/spring/actuator/OpenTelemetryMeterRegistryAutoConfiguration.class"); } - @Override - public boolean isHelperClass(String className) { - return className.startsWith("io.opentelemetry.micrometer1shim."); - } - @Override public List typeInstrumentations() { return singletonList(new AutoConfigurationImportSelectorInstrumentation()); diff --git a/instrumentation/spring/spring-boot-actuator-autoconfigure-2.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/actuator/ActuatorTest.java b/instrumentation/spring/spring-boot-actuator-autoconfigure-2.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/actuator/ActuatorTest.java index ffcc9bd3e767..8fa46384722a 100644 --- a/instrumentation/spring/spring-boot-actuator-autoconfigure-2.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/actuator/ActuatorTest.java +++ b/instrumentation/spring/spring-boot-actuator-autoconfigure-2.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/actuator/ActuatorTest.java @@ -37,7 +37,7 @@ void shouldInjectOtelMeterRegistry() { testBean.inc(); testing.waitAndAssertMetrics( - "io.opentelemetry.micrometer1shim", + "io.opentelemetry.micrometer-1.5", "test-counter", metrics -> metrics.anySatisfy( diff --git a/instrumentation/spring/spring-boot-autoconfigure/README.md b/instrumentation/spring/spring-boot-autoconfigure/README.md index 5d7d09a9695c..6b29157619fc 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/README.md +++ b/instrumentation/spring/spring-boot-autoconfigure/README.md @@ -1,23 +1,26 @@ # OpenTelemetry Spring Auto-Configuration -Auto-configures OpenTelemetry instrumentation for [spring-web](../spring-web-3.1), [spring-webmvc](../spring-webmvc-3.1), and [spring-webflux](../spring-webflux-5.0). Leverages Spring Aspect Oriented Programming, dependency injection, and bean post-processing to trace spring applications. To include all features listed below use the [opentelemetry-spring-starter](../starters/spring-starter/README.md). +Auto-configures OpenTelemetry instrumentation for [spring-web](../spring-web-3.1/library) +, [spring-webmvc](../spring-webmvc-5.3/library), and [spring-webflux](../spring-webflux-5.0/library) +. Leverages Spring Aspect Oriented Programming, dependency injection, and bean post-processing to +trace spring applications. To include all features listed below use +the [opentelemetry-spring-boot-starter](../starters/spring-boot-starter/README.md). ## Quickstart ### Add these dependencies to your project. Replace `OPENTELEMETRY_VERSION` with the latest stable [release](https://search.maven.org/search?q=g:io.opentelemetry). - - Minimum version: `0.17.0` + - Minimum version: `1.17.0` - -For Maven add to your `pom.xml`: +For Maven, add to your `pom.xml` dependencies: ```xml io.opentelemetry.instrumentation - opentelemetry-spring-boot-autoconfigure + opentelemetry-spring-boot OPENTELEMETRY_VERSION @@ -39,11 +42,11 @@ For Maven add to your `pom.xml`: ``` -For Gradle add to your dependencies: +For Gradle, add to your dependencies: ```groovy //opentelemetry spring auto-configuration -implementation("io.opentelemetry.instrumentation:opentelemetry-spring-boot-autoconfigure:OPENTELEMETRY_VERSION") +implementation("io.opentelemetry.instrumentation:opentelemetry-spring-boot:OPENTELEMETRY_VERSION") //opentelemetry implementation("io.opentelemetry:opentelemetry-api:OPENTELEMETRY_VERSION") //opentelemetry exporter @@ -62,7 +65,7 @@ Replace `SPRING_VERSION` with the version of spring you're using. Replace `SPRING_WEBFLUX_VERSION` with the version of spring-webflux you're using. - Minimum version: `5.0` -For Maven add to your `pom.xml`: +For Maven, add to your `pom.xml` dependencies: ```xml @@ -118,7 +121,7 @@ For Maven add to your `pom.xml`: ``` -For Gradle add to your dependencies: +For Gradle, add to your dependencies: ```groovy //opentelemetry exporter @@ -153,7 +156,12 @@ Provides auto-configuration for the OpenTelemetry RestTemplate trace interceptor #### Spring Web MVC Auto Configuration -This feature auto-configures instrumentation for spring-webmvc controllers by adding a [WebMvcTracingFilter](../spring-webmvc-3.1/library/src/main/java/io/opentelemetry/instrumentation/spring/webmvc/WebMvcTracingFilter.java) bean to the application context. This request filter implements the [OncePerRequestFilter](https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/filter/OncePerRequestFilter.html) interface to capture OpenTelemetry server spans and propagate distribute tracing context if provided in the request. [Spring Web MVC - Server Span](#spring-web-mvc---server-span) show cases a sample span generated by the WebMvcTracingFilter. Check out [opentelemetry-spring-webmvc-3.1](../spring-webmvc-3.1/) to learn more about the OpenTelemetry WebMvcTracingFilter. +This feature autoconfigures instrumentation for Spring WebMVC controllers by adding +a [telemetry producing servlet `Filter`](../spring-webmvc-5.3/library/src/main/java/io/opentelemetry/instrumentation/spring/webmvc/v5_3/WebMvcTelemetryProducingFilter.java) +bean to the application context. This filter decorates the request execution with an OpenTelemetry +server span, propagating the incoming tracing context if received in the HTTP request. Check +out [`opentelemetry-spring-webmvc-5.3` instrumentation library](../spring-webmvc-5.3/library) to +learn more about the OpenTelemetry Spring WebMVC instrumentation. #### Spring WebFlux Auto Configuration @@ -174,8 +182,8 @@ to learn more about aspect weaving in spring. ```java import org.springframework.stereotype.Component; -import io.opentelemetry.extension.annotations.SpanAttribute; -import io.opentelemetry.extension.annotations.WithSpan; +import io.opentelemetry.instrumentation.annotations.SpanAttribute; +import io.opentelemetry.instrumentation.annotations.WithSpan; import io.opentelemetry.api.trace.Span; import io.opentelemetry.api.trace.SpanKind; @@ -230,8 +238,9 @@ The traces below were exported using Zipkin. "http.status_code":"200", "http.url":"/spring-webmvc/sample", "http.user_agent":"PostmanRuntime/7.26.2", - "net.peer.ip":"0:0:0:0:0:0:0:1", - "net.peer.port":"33916", + "net.sock.peer.addr":"0:0:0:0:0:0:0:1", + "net.sock.peer.port":"33916", + "net.sock.family":"inet6" "sampling.probability":"1.0" } } diff --git a/instrumentation/spring/spring-boot-autoconfigure/build.gradle.kts b/instrumentation/spring/spring-boot-autoconfigure/build.gradle.kts index 01541752c7a6..03effd41bd8c 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/build.gradle.kts +++ b/instrumentation/spring/spring-boot-autoconfigure/build.gradle.kts @@ -1,28 +1,28 @@ plugins { - id("otel.java-conventions") - id("otel.publish-conventions") + id("otel.library-instrumentation") } +// Name the Spring Boot modules in accordance with https://docs.spring.io/spring-boot/docs/current/reference/html/features.html#features.developing-auto-configuration.custom-starter +base.archivesName.set("opentelemetry-spring-boot") group = "io.opentelemetry.instrumentation" val versions: Map by project val springBootVersion = versions["org.springframework.boot"] dependencies { - implementation(project(":instrumentation-annotations-support")) - implementation("org.springframework.boot:spring-boot-autoconfigure:$springBootVersion") annotationProcessor("org.springframework.boot:spring-boot-autoconfigure-processor:$springBootVersion") - implementation("javax.validation:validation-api:2.0.1.Final") + implementation("javax.validation:validation-api") + implementation(project(":instrumentation-annotations-support")) + implementation(project(":instrumentation:kafka:kafka-clients:kafka-clients-2.6:library")) + implementation(project(":instrumentation:spring:spring-kafka-2.7:library")) implementation(project(":instrumentation:spring:spring-web-3.1:library")) - implementation(project(":instrumentation:spring:spring-webmvc-3.1:library")) + implementation(project(":instrumentation:spring:spring-webmvc-5.3:library")) implementation(project(":instrumentation:spring:spring-webflux-5.0:library")) - implementation("io.opentelemetry:opentelemetry-micrometer1-shim") { - // just get the instrumentation, without micrometer itself - exclude("io.micrometer", "micrometer-core") - } + implementation(project(":instrumentation:micrometer:micrometer-1.5:library")) + compileOnly("org.springframework.kafka:spring-kafka:2.9.0") compileOnly("org.springframework.boot:spring-boot-starter-actuator:$springBootVersion") compileOnly("org.springframework.boot:spring-boot-starter-aop:$springBootVersion") compileOnly("org.springframework.boot:spring-boot-starter-web:$springBootVersion") @@ -32,12 +32,17 @@ dependencies { compileOnly("io.opentelemetry:opentelemetry-extension-annotations") compileOnly("io.opentelemetry:opentelemetry-extension-trace-propagators") compileOnly("io.opentelemetry:opentelemetry-extension-aws") - compileOnly("io.opentelemetry:opentelemetry-sdk-extension-resources") compileOnly("io.opentelemetry:opentelemetry-exporter-logging") compileOnly("io.opentelemetry:opentelemetry-exporter-jaeger") compileOnly("io.opentelemetry:opentelemetry-exporter-otlp") compileOnly("io.opentelemetry:opentelemetry-exporter-zipkin") + compileOnly(project(":instrumentation-annotations")) + + compileOnly(project(":instrumentation:resources:library")) + annotationProcessor("com.google.auto.service:auto-service") + compileOnly("com.google.auto.service:auto-service-annotations") + testImplementation("org.springframework.kafka:spring-kafka:2.9.0") testImplementation("org.springframework.boot:spring-boot-starter-actuator:$springBootVersion") testImplementation("org.springframework.boot:spring-boot-starter-aop:$springBootVersion") testImplementation("org.springframework.boot:spring-boot-starter-webflux:$springBootVersion") @@ -45,11 +50,12 @@ dependencies { testImplementation("org.springframework.boot:spring-boot-starter-test:$springBootVersion") { exclude("org.junit.vintage", "junit-vintage-engine") } + testImplementation("org.testcontainers:kafka") testImplementation(project(":testing-common")) testImplementation("io.opentelemetry:opentelemetry-sdk") testImplementation("io.opentelemetry:opentelemetry-sdk-testing") - testImplementation("io.opentelemetry:opentelemetry-sdk-extension-resources") + testImplementation(project(":instrumentation:resources:library")) testImplementation("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure-spi") testImplementation("io.opentelemetry:opentelemetry-extension-annotations") testImplementation("io.opentelemetry:opentelemetry-extension-trace-propagators") @@ -58,9 +64,23 @@ dependencies { testImplementation("io.opentelemetry:opentelemetry-exporter-jaeger") testImplementation("io.opentelemetry:opentelemetry-exporter-otlp") testImplementation("io.opentelemetry:opentelemetry-exporter-zipkin") - testImplementation(project(":instrumentation-annotations-support")) + testImplementation(project(":instrumentation-annotations")) } tasks.compileTestJava { options.compilerArgs.add("-parameters") } + +tasks.withType().configureEach { + // required on jdk17 + jvmArgs("--add-opens=java.base/java.lang=ALL-UNNAMED") + jvmArgs("-XX:+IgnoreUnrecognizedVMOptions") + + // disable tests on openj9 18 because they often crash JIT compiler + val testJavaVersion = gradle.startParameter.projectProperties["testJavaVersion"]?.let(JavaVersion::toVersion) + val testOnOpenJ9 = gradle.startParameter.projectProperties["testJavaVM"]?.run { this == "openj9" } + ?: false + if (testOnOpenJ9 && testJavaVersion?.majorVersion == "18") { + enabled = false + } +} diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/OpenTelemetryAutoConfiguration.java b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/OpenTelemetryAutoConfiguration.java index f04dca94133b..23bad1890fd4 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/OpenTelemetryAutoConfiguration.java +++ b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/OpenTelemetryAutoConfiguration.java @@ -44,6 +44,8 @@ @EnableConfigurationProperties({MetricExportProperties.class, SamplerProperties.class}) public class OpenTelemetryAutoConfiguration { + public OpenTelemetryAutoConfiguration() {} + @Configuration @ConditionalOnMissingBean(OpenTelemetry.class) public static class OpenTelemetryBeanConfig { diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/aspects/InstrumentationWithSpanAspect.java b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/aspects/InstrumentationWithSpanAspect.java new file mode 100644 index 000000000000..568f55e22e73 --- /dev/null +++ b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/aspects/InstrumentationWithSpanAspect.java @@ -0,0 +1,32 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.spring.autoconfigure.aspects; + +import io.opentelemetry.api.OpenTelemetry; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.springframework.core.ParameterNameDiscoverer; + +@Aspect +class InstrumentationWithSpanAspect extends WithSpanAspect { + + InstrumentationWithSpanAspect( + OpenTelemetry openTelemetry, ParameterNameDiscoverer parameterNameDiscoverer) { + super( + openTelemetry, + parameterNameDiscoverer, + new JoinPointRequest.InstrumentationAnnotationFactory(), + new WithSpanAspectParameterAttributeNamesExtractor + .InstrumentationAnnotationAttributeNameSupplier()); + } + + @Override + @Around("@annotation(io.opentelemetry.instrumentation.annotations.WithSpan)") + public Object traceMethod(ProceedingJoinPoint pjp) throws Throwable { + return super.traceMethod(pjp); + } +} diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/aspects/JoinPointRequest.java b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/aspects/JoinPointRequest.java index 4cd4bc28a84c..986e8d207d2f 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/aspects/JoinPointRequest.java +++ b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/aspects/JoinPointRequest.java @@ -5,32 +5,89 @@ package io.opentelemetry.instrumentation.spring.autoconfigure.aspects; -import io.opentelemetry.extension.annotations.WithSpan; +import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.instrumentation.annotations.WithSpan; +import io.opentelemetry.instrumentation.api.instrumenter.util.SpanNames; import java.lang.reflect.Method; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.reflect.MethodSignature; final class JoinPointRequest { + private final JoinPoint joinPoint; private final Method method; - private final WithSpan annotation; + private final String spanName; + private final SpanKind spanKind; + + private JoinPointRequest(JoinPoint joinPoint, Method method, String spanName, SpanKind spanKind) { + if (spanName.isEmpty()) { + spanName = SpanNames.fromMethod(method); + } - JoinPointRequest(JoinPoint joinPoint) { this.joinPoint = joinPoint; - MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature(); - this.method = methodSignature.getMethod(); - this.annotation = this.method.getDeclaredAnnotation(WithSpan.class); + this.method = method; + this.spanName = spanName; + this.spanKind = spanKind; } - Method method() { - return method; + String spanName() { + return spanName; } - WithSpan annotation() { - return annotation; + SpanKind spanKind() { + return spanKind; + } + + Method method() { + return method; } Object[] args() { return joinPoint.getArgs(); } + + interface Factory { + + JoinPointRequest create(JoinPoint joinPoint); + } + + static final class InstrumentationAnnotationFactory implements Factory { + + @Override + public JoinPointRequest create(JoinPoint joinPoint) { + MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature(); + Method method = methodSignature.getMethod(); + + // in rare cases, when interface method does not have annotations but the implementation does, + // and the AspectJ factory is configured to proxy interfaces, this class will receive the + // abstract interface method (without annotations) instead of the implementation method (with + // annotations); these defaults prevent NPEs in this scenario + WithSpan annotation = method.getDeclaredAnnotation(WithSpan.class); + String spanName = annotation != null ? annotation.value() : ""; + SpanKind spanKind = annotation != null ? annotation.kind() : SpanKind.INTERNAL; + + return new JoinPointRequest(joinPoint, method, spanName, spanKind); + } + } + + static final class SdkExtensionAnnotationFactory implements Factory { + + @Override + @SuppressWarnings("deprecation") // instrumenting deprecated class for backwards compatibility + public JoinPointRequest create(JoinPoint joinPoint) { + MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature(); + Method method = methodSignature.getMethod(); + + // in rare cases, when interface method does not have annotations but the implementation does, + // and the AspectJ factory is configured to proxy interfaces, this class will receive the + // abstract interface method (without annotations) instead of the implementation method (with + // annotations); these defaults prevent NPEs in this scenario + io.opentelemetry.extension.annotations.WithSpan annotation = + method.getDeclaredAnnotation(io.opentelemetry.extension.annotations.WithSpan.class); + String spanName = annotation != null ? annotation.value() : ""; + SpanKind spanKind = annotation != null ? annotation.kind() : SpanKind.INTERNAL; + + return new JoinPointRequest(joinPoint, method, spanName, spanKind); + } + } } diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/aspects/JointPointCodeAttributesExtractor.java b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/aspects/JointPointCodeAttributesExtractor.java new file mode 100644 index 000000000000..3c83bbd3cd11 --- /dev/null +++ b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/aspects/JointPointCodeAttributesExtractor.java @@ -0,0 +1,22 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.spring.autoconfigure.aspects; + +import io.opentelemetry.instrumentation.api.instrumenter.code.CodeAttributesGetter; + +enum JointPointCodeAttributesExtractor implements CodeAttributesGetter { + INSTANCE; + + @Override + public Class codeClass(JoinPointRequest joinPointRequest) { + return joinPointRequest.method().getDeclaringClass(); + } + + @Override + public String methodName(JoinPointRequest joinPointRequest) { + return joinPointRequest.method().getName(); + } +} diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/aspects/SdkExtensionWithSpanAspect.java b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/aspects/SdkExtensionWithSpanAspect.java new file mode 100644 index 000000000000..5b6db318f564 --- /dev/null +++ b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/aspects/SdkExtensionWithSpanAspect.java @@ -0,0 +1,32 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.spring.autoconfigure.aspects; + +import io.opentelemetry.api.OpenTelemetry; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.springframework.core.ParameterNameDiscoverer; + +@Aspect +class SdkExtensionWithSpanAspect extends WithSpanAspect { + + SdkExtensionWithSpanAspect( + OpenTelemetry openTelemetry, ParameterNameDiscoverer parameterNameDiscoverer) { + super( + openTelemetry, + parameterNameDiscoverer, + new JoinPointRequest.SdkExtensionAnnotationFactory(), + new WithSpanAspectParameterAttributeNamesExtractor + .SdkExtensionAnnotationAttributeNameSupplier()); + } + + @Override + @Around("@annotation(io.opentelemetry.extension.annotations.WithSpan)") + public Object traceMethod(ProceedingJoinPoint pjp) throws Throwable { + return super.traceMethod(pjp); + } +} diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/aspects/TraceAspectAutoConfiguration.java b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/aspects/TraceAspectAutoConfiguration.java index 298c8bf8818d..2f568eebe7fd 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/aspects/TraceAspectAutoConfiguration.java +++ b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/aspects/TraceAspectAutoConfiguration.java @@ -6,7 +6,7 @@ package io.opentelemetry.instrumentation.spring.autoconfigure.aspects; import io.opentelemetry.api.OpenTelemetry; -import io.opentelemetry.extension.annotations.WithSpan; +import io.opentelemetry.instrumentation.annotations.WithSpan; import org.aspectj.lang.annotation.Aspect; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; @@ -21,7 +21,7 @@ @Configuration @EnableConfigurationProperties(TraceAspectProperties.class) @ConditionalOnProperty(prefix = "otel.springboot.aspects", name = "enabled", matchIfMissing = true) -@ConditionalOnClass({Aspect.class, WithSpan.class}) +@ConditionalOnClass(Aspect.class) public class TraceAspectAutoConfiguration { @Bean @@ -31,8 +31,17 @@ public ParameterNameDiscoverer parameterNameDiscoverer() { } @Bean - public WithSpanAspect withSpanAspect( + @ConditionalOnClass(WithSpan.class) + public InstrumentationWithSpanAspect instrumentationWithSpanAspect( OpenTelemetry openTelemetry, ParameterNameDiscoverer parameterNameDiscoverer) { - return new WithSpanAspect(openTelemetry, parameterNameDiscoverer); + return new InstrumentationWithSpanAspect(openTelemetry, parameterNameDiscoverer); + } + + @Bean + @SuppressWarnings("deprecation") // instrumenting deprecated class for backwards compatibility + @ConditionalOnClass(io.opentelemetry.extension.annotations.WithSpan.class) + public SdkExtensionWithSpanAspect sdkExtensionWithSpanAspect( + OpenTelemetry openTelemetry, ParameterNameDiscoverer parameterNameDiscoverer) { + return new SdkExtensionWithSpanAspect(openTelemetry, parameterNameDiscoverer); } } diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/aspects/WithSpanAspect.java b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/aspects/WithSpanAspect.java index f05e7a435094..b81acc6be9d1 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/aspects/WithSpanAspect.java +++ b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/aspects/WithSpanAspect.java @@ -6,69 +6,60 @@ package io.opentelemetry.instrumentation.spring.autoconfigure.aspects; import io.opentelemetry.api.OpenTelemetry; -import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.api.trace.Span; import io.opentelemetry.context.Context; import io.opentelemetry.context.Scope; -import io.opentelemetry.extension.annotations.WithSpan; +import io.opentelemetry.instrumentation.annotations.WithSpan; import io.opentelemetry.instrumentation.api.annotation.support.MethodSpanAttributesExtractor; import io.opentelemetry.instrumentation.api.annotation.support.ParameterAttributeNamesExtractor; import io.opentelemetry.instrumentation.api.annotation.support.async.AsyncOperationEndSupport; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; -import io.opentelemetry.instrumentation.api.util.SpanNames; +import io.opentelemetry.instrumentation.api.instrumenter.code.CodeAttributesExtractor; import org.aspectj.lang.ProceedingJoinPoint; -import org.aspectj.lang.annotation.Around; -import org.aspectj.lang.annotation.Aspect; import org.springframework.core.ParameterNameDiscoverer; /** - * Uses Spring-AOP to wrap methods marked by {@link WithSpan} in a {@link - * io.opentelemetry.api.trace.Span}. + * Uses Spring-AOP to wrap methods marked by {@link WithSpan} (or the deprecated {@link + * io.opentelemetry.extension.annotations.WithSpan}) in a {@link Span}. * *

Ensure methods annotated with {@link WithSpan} are implemented on beans managed by the Spring * container. * - *

Note: This Aspect uses spring-aop to proxy beans. Therefore the {@link WithSpan} annotation + *

Note: This Aspect uses spring-aop to proxy beans. Therefore, the {@link WithSpan} annotation * can not be applied to constructors. */ -@Aspect -public class WithSpanAspect { +abstract class WithSpanAspect { private static final String INSTRUMENTATION_NAME = "io.opentelemetry.spring-boot-autoconfigure"; private final Instrumenter instrumenter; + private final JoinPointRequest.Factory requestFactory; - public WithSpanAspect( - OpenTelemetry openTelemetry, ParameterNameDiscoverer parameterNameDiscoverer) { + WithSpanAspect( + OpenTelemetry openTelemetry, + ParameterNameDiscoverer parameterNameDiscoverer, + JoinPointRequest.Factory requestFactory, + WithSpanAspectParameterAttributeNamesExtractor.SpanAttributeNameSupplier + spanAttributeNameSupplier) { ParameterAttributeNamesExtractor parameterAttributeNamesExtractor = - new WithSpanAspectParameterAttributeNamesExtractor(parameterNameDiscoverer); + new WithSpanAspectParameterAttributeNamesExtractor( + parameterNameDiscoverer, spanAttributeNameSupplier); instrumenter = - Instrumenter.builder(openTelemetry, INSTRUMENTATION_NAME, WithSpanAspect::spanName) + Instrumenter.builder(openTelemetry, INSTRUMENTATION_NAME, JoinPointRequest::spanName) + .addAttributesExtractor( + CodeAttributesExtractor.create(JointPointCodeAttributesExtractor.INSTANCE)) .addAttributesExtractor( MethodSpanAttributesExtractor.newInstance( JoinPointRequest::method, parameterAttributeNamesExtractor, JoinPointRequest::args)) - .newInstrumenter(WithSpanAspect::spanKind); - } - - private static String spanName(JoinPointRequest request) { - WithSpan annotation = request.annotation(); - String spanName = annotation.value(); - if (spanName.isEmpty()) { - return SpanNames.fromMethod(request.method()); - } - return spanName; + .buildInstrumenter(JoinPointRequest::spanKind); + this.requestFactory = requestFactory; } - private static SpanKind spanKind(JoinPointRequest request) { - return request.annotation().kind(); - } - - @Around("@annotation(io.opentelemetry.extension.annotations.WithSpan)") public Object traceMethod(ProceedingJoinPoint pjp) throws Throwable { - - JoinPointRequest request = new JoinPointRequest(pjp); + JoinPointRequest request = requestFactory.create(pjp); Context parentContext = Context.current(); if (!instrumenter.shouldStart(parentContext, request)) { return pjp.proceed(); diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/aspects/WithSpanAspectParameterAttributeNamesExtractor.java b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/aspects/WithSpanAspectParameterAttributeNamesExtractor.java index c6c2d96f0da7..12cc1b99f73d 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/aspects/WithSpanAspectParameterAttributeNamesExtractor.java +++ b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/aspects/WithSpanAspectParameterAttributeNamesExtractor.java @@ -5,7 +5,7 @@ package io.opentelemetry.instrumentation.spring.autoconfigure.aspects; -import io.opentelemetry.extension.annotations.SpanAttribute; +import io.opentelemetry.instrumentation.annotations.SpanAttribute; import io.opentelemetry.instrumentation.api.annotation.support.ParameterAttributeNamesExtractor; import java.lang.reflect.Method; import java.lang.reflect.Parameter; @@ -13,11 +13,15 @@ import org.springframework.core.ParameterNameDiscoverer; class WithSpanAspectParameterAttributeNamesExtractor implements ParameterAttributeNamesExtractor { + private final ParameterNameDiscoverer parameterNameDiscoverer; + private final SpanAttributeNameSupplier spanAttributeNameSupplier; public WithSpanAspectParameterAttributeNamesExtractor( - ParameterNameDiscoverer parameterNameDiscoverer) { + ParameterNameDiscoverer parameterNameDiscoverer, + SpanAttributeNameSupplier spanAttributeNameSupplier) { this.parameterNameDiscoverer = parameterNameDiscoverer; + this.spanAttributeNameSupplier = spanAttributeNameSupplier; } @Override @@ -33,14 +37,13 @@ public String[] extract(Method method, Parameter[] parameters) { } @Nullable - private static String attributeName(Parameter parameter, String[] parameterNames, int index) { - SpanAttribute annotation = parameter.getDeclaredAnnotation(SpanAttribute.class); - if (annotation == null) { + private String attributeName(Parameter parameter, String[] parameterNames, int index) { + String annotationValue = spanAttributeNameSupplier.spanAttributeName(parameter); + if (annotationValue == null) { return null; } - String value = annotation.value(); - if (!value.isEmpty()) { - return value; + if (!annotationValue.isEmpty()) { + return annotationValue; } if (parameterNames != null && index < parameterNames.length) { String parameterName = parameterNames[index]; @@ -53,4 +56,35 @@ private static String attributeName(Parameter parameter, String[] parameterNames } return null; } + + interface SpanAttributeNameSupplier { + + @Nullable + String spanAttributeName(Parameter parameter); + } + + static final class InstrumentationAnnotationAttributeNameSupplier + implements SpanAttributeNameSupplier { + + @Nullable + @Override + public String spanAttributeName(Parameter parameter) { + SpanAttribute annotation = parameter.getDeclaredAnnotation(SpanAttribute.class); + return annotation == null ? null : annotation.value(); + } + } + + static final class SdkExtensionAnnotationAttributeNameSupplier + implements SpanAttributeNameSupplier { + + @Nullable + @Override + @SuppressWarnings("deprecation") // instrumenting deprecated class for backwards compatibility + public String spanAttributeName(Parameter parameter) { + io.opentelemetry.extension.annotations.SpanAttribute annotation = + parameter.getDeclaredAnnotation( + io.opentelemetry.extension.annotations.SpanAttribute.class); + return annotation == null ? null : annotation.value(); + } + } } diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/exporters/logging/LoggingExporterProperties.java b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/exporters/logging/LoggingExporterProperties.java index 63f8b9db2cc3..c0e8402f6b91 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/exporters/logging/LoggingExporterProperties.java +++ b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/exporters/logging/LoggingExporterProperties.java @@ -14,7 +14,7 @@ @ConfigurationProperties(prefix = "otel.exporter.logging") public final class LoggingExporterProperties { - private boolean enabled = true; + private boolean enabled = false; private final SignalProperties traces = new SignalProperties(); private final SignalProperties metrics = new SignalProperties(); @@ -36,7 +36,7 @@ public SignalProperties getMetrics() { public static class SignalProperties { - private boolean enabled = true; + private boolean enabled = false; public boolean isEnabled() { return enabled; diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/exporters/logging/LoggingMetricExporterAutoConfiguration.java b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/exporters/logging/LoggingMetricExporterAutoConfiguration.java index 170f643ae399..860e97905331 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/exporters/logging/LoggingMetricExporterAutoConfiguration.java +++ b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/exporters/logging/LoggingMetricExporterAutoConfiguration.java @@ -9,27 +9,37 @@ import io.opentelemetry.exporter.logging.LoggingSpanExporter; import io.opentelemetry.instrumentation.spring.autoconfigure.OpenTelemetryAutoConfiguration; import org.springframework.boot.autoconfigure.AutoConfigureBefore; +import org.springframework.boot.autoconfigure.condition.AnyNestedCondition; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; /** Configures {@link LoggingSpanExporter} bean for tracing. */ @Configuration @EnableConfigurationProperties(LoggingExporterProperties.class) @AutoConfigureBefore(OpenTelemetryAutoConfiguration.class) -@ConditionalOnProperty( - prefix = "otel.exporter.logging", - name = {"enabled", "metrics.enabled"}, - matchIfMissing = true) +@Conditional(LoggingMetricExporterAutoConfiguration.AnyPropertyEnabled.class) @ConditionalOnClass(LoggingMetricExporter.class) public class LoggingMetricExporterAutoConfiguration { @Bean - @ConditionalOnMissingBean public LoggingMetricExporter otelLoggingMetricExporter() { return LoggingMetricExporter.create(); } + + static final class AnyPropertyEnabled extends AnyNestedCondition { + + AnyPropertyEnabled() { + super(ConfigurationPhase.PARSE_CONFIGURATION); + } + + @ConditionalOnProperty("otel.exporter.logging.enabled") + static class LoggingEnabled {} + + @ConditionalOnProperty("otel.exporter.logging.metrics.enabled") + static class LoggingMetricsEnabled {} + } } diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/exporters/logging/LoggingSpanExporterAutoConfiguration.java b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/exporters/logging/LoggingSpanExporterAutoConfiguration.java index 20af6bb4e2a3..a8ac9c501f34 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/exporters/logging/LoggingSpanExporterAutoConfiguration.java +++ b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/exporters/logging/LoggingSpanExporterAutoConfiguration.java @@ -8,21 +8,20 @@ import io.opentelemetry.exporter.logging.LoggingSpanExporter; import io.opentelemetry.instrumentation.spring.autoconfigure.OpenTelemetryAutoConfiguration; import org.springframework.boot.autoconfigure.AutoConfigureBefore; +import org.springframework.boot.autoconfigure.condition.AnyNestedCondition; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; /** Configures {@link LoggingSpanExporter} bean for tracing. */ @Configuration @EnableConfigurationProperties(LoggingExporterProperties.class) @AutoConfigureBefore(OpenTelemetryAutoConfiguration.class) -@ConditionalOnProperty( - prefix = "otel.exporter.logging", - name = {"enabled", "traces.enabled"}, - matchIfMissing = true) +@Conditional(LoggingSpanExporterAutoConfiguration.AnyPropertyEnabled.class) @ConditionalOnClass(LoggingSpanExporter.class) public class LoggingSpanExporterAutoConfiguration { @@ -31,4 +30,17 @@ public class LoggingSpanExporterAutoConfiguration { public LoggingSpanExporter otelLoggingSpanExporter() { return LoggingSpanExporter.create(); } + + static final class AnyPropertyEnabled extends AnyNestedCondition { + + AnyPropertyEnabled() { + super(ConfigurationPhase.PARSE_CONFIGURATION); + } + + @ConditionalOnProperty("otel.exporter.logging.enabled") + static class LoggingEnabled {} + + @ConditionalOnProperty("otel.exporter.logging.traces.enabled") + static class LoggingTracesEnabled {} + } } diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/kafka/ConcurrentKafkaListenerContainerFactoryPostProcessor.java b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/kafka/ConcurrentKafkaListenerContainerFactoryPostProcessor.java new file mode 100644 index 000000000000..4a39a5d296ef --- /dev/null +++ b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/kafka/ConcurrentKafkaListenerContainerFactoryPostProcessor.java @@ -0,0 +1,35 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.spring.autoconfigure.kafka; + +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.instrumentation.spring.kafka.v2_7.SpringKafkaTelemetry; +import org.springframework.beans.factory.config.BeanPostProcessor; +import org.springframework.kafka.config.ConcurrentKafkaListenerContainerFactory; + +class ConcurrentKafkaListenerContainerFactoryPostProcessor implements BeanPostProcessor { + + private final OpenTelemetry openTelemetry; + + ConcurrentKafkaListenerContainerFactoryPostProcessor(OpenTelemetry openTelemetry) { + this.openTelemetry = openTelemetry; + } + + @Override + public Object postProcessAfterInitialization(Object bean, String beanName) { + if (!(bean instanceof ConcurrentKafkaListenerContainerFactory)) { + return bean; + } + + ConcurrentKafkaListenerContainerFactory listenerContainerFactory = + (ConcurrentKafkaListenerContainerFactory) bean; + SpringKafkaTelemetry springKafkaTelemetry = SpringKafkaTelemetry.create(openTelemetry); + listenerContainerFactory.setBatchInterceptor(springKafkaTelemetry.createBatchInterceptor()); + listenerContainerFactory.setRecordInterceptor(springKafkaTelemetry.createRecordInterceptor()); + + return listenerContainerFactory; + } +} diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/kafka/KafkaInstrumentationAutoConfiguration.java b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/kafka/KafkaInstrumentationAutoConfiguration.java new file mode 100644 index 000000000000..ace3b7a4c7de --- /dev/null +++ b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/kafka/KafkaInstrumentationAutoConfiguration.java @@ -0,0 +1,37 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.spring.autoconfigure.kafka; + +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.instrumentation.kafkaclients.KafkaTelemetry; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.autoconfigure.kafka.DefaultKafkaProducerFactoryCustomizer; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.kafka.config.ConcurrentKafkaListenerContainerFactory; +import org.springframework.kafka.core.KafkaTemplate; + +@Configuration +@EnableConfigurationProperties(KafkaInstrumentationProperties.class) +@ConditionalOnProperty(name = "otel.springboot.kafka.enabled", matchIfMissing = true) +@ConditionalOnClass({KafkaTemplate.class, ConcurrentKafkaListenerContainerFactory.class}) +public class KafkaInstrumentationAutoConfiguration { + + @Bean + public DefaultKafkaProducerFactoryCustomizer producerInstrumentation( + OpenTelemetry openTelemetry) { + KafkaTelemetry kafkaTelemetry = KafkaTelemetry.create(openTelemetry); + return producerFactory -> producerFactory.addPostProcessor(kafkaTelemetry::wrap); + } + + @Bean + public ConcurrentKafkaListenerContainerFactoryPostProcessor + concurrentKafkaListenerContainerFactoryPostProcessor(OpenTelemetry openTelemetry) { + return new ConcurrentKafkaListenerContainerFactoryPostProcessor(openTelemetry); + } +} diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/kafka/KafkaInstrumentationProperties.java b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/kafka/KafkaInstrumentationProperties.java new file mode 100644 index 000000000000..73eac23125bb --- /dev/null +++ b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/kafka/KafkaInstrumentationProperties.java @@ -0,0 +1,22 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.spring.autoconfigure.kafka; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +@ConfigurationProperties(prefix = "otel.springboot.kafka") +public class KafkaInstrumentationProperties { + + private boolean enabled = true; + + public boolean isEnabled() { + return enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } +} diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/metrics/MicrometerShimAutoConfiguration.java b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/metrics/MicrometerShimAutoConfiguration.java index 217b3ab3742f..ef08bcee061b 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/metrics/MicrometerShimAutoConfiguration.java +++ b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/metrics/MicrometerShimAutoConfiguration.java @@ -8,7 +8,7 @@ import io.micrometer.core.instrument.Clock; import io.micrometer.core.instrument.MeterRegistry; import io.opentelemetry.api.OpenTelemetry; -import io.opentelemetry.micrometer1shim.OpenTelemetryMeterRegistry; +import io.opentelemetry.instrumentation.micrometer.v1_5.OpenTelemetryMeterRegistry; import org.springframework.boot.actuate.autoconfigure.metrics.CompositeMeterRegistryAutoConfiguration; import org.springframework.boot.actuate.autoconfigure.metrics.MetricsAutoConfiguration; import org.springframework.boot.autoconfigure.AutoConfigureAfter; diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/propagators/CompositeTextMapPropagatorFactory.java b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/propagators/CompositeTextMapPropagatorFactory.java index f520da6a0c61..4969e8366101 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/propagators/CompositeTextMapPropagatorFactory.java +++ b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/propagators/CompositeTextMapPropagatorFactory.java @@ -92,4 +92,6 @@ static TextMapPropagator getCompositeTextMapPropagator( private static boolean isOnClasspath(String clazz) { return ClassUtils.isPresent(clazz, null); } + + private CompositeTextMapPropagatorFactory() {} } diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/resources/OtelResourceAutoConfiguration.java b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/resources/OtelResourceAutoConfiguration.java index aa5bf5b63fd3..722677db6816 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/resources/OtelResourceAutoConfiguration.java +++ b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/resources/OtelResourceAutoConfiguration.java @@ -5,18 +5,18 @@ package io.opentelemetry.instrumentation.spring.autoconfigure.resources; +import io.opentelemetry.instrumentation.resources.ContainerResource; +import io.opentelemetry.instrumentation.resources.ContainerResourceProvider; +import io.opentelemetry.instrumentation.resources.HostResource; +import io.opentelemetry.instrumentation.resources.HostResourceProvider; +import io.opentelemetry.instrumentation.resources.OsResource; +import io.opentelemetry.instrumentation.resources.OsResourceProvider; +import io.opentelemetry.instrumentation.resources.ProcessResource; +import io.opentelemetry.instrumentation.resources.ProcessResourceProvider; +import io.opentelemetry.instrumentation.resources.ProcessRuntimeResource; +import io.opentelemetry.instrumentation.resources.ProcessRuntimeResourceProvider; import io.opentelemetry.instrumentation.spring.autoconfigure.OpenTelemetryAutoConfiguration; import io.opentelemetry.sdk.autoconfigure.spi.ResourceProvider; -import io.opentelemetry.sdk.extension.resources.ContainerResource; -import io.opentelemetry.sdk.extension.resources.ContainerResourceProvider; -import io.opentelemetry.sdk.extension.resources.HostResource; -import io.opentelemetry.sdk.extension.resources.HostResourceProvider; -import io.opentelemetry.sdk.extension.resources.OsResource; -import io.opentelemetry.sdk.extension.resources.OsResourceProvider; -import io.opentelemetry.sdk.extension.resources.ProcessResource; -import io.opentelemetry.sdk.extension.resources.ProcessResourceProvider; -import io.opentelemetry.sdk.extension.resources.ProcessRuntimeResource; -import io.opentelemetry.sdk.extension.resources.ProcessRuntimeResourceProvider; import org.springframework.boot.autoconfigure.AutoConfigureBefore; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/webmvc/WebMvcFilterAutoConfiguration.java b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/webmvc/WebMvcFilterAutoConfiguration.java index c73cdc2a7f6a..8ffdf2895a67 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/webmvc/WebMvcFilterAutoConfiguration.java +++ b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/webmvc/WebMvcFilterAutoConfiguration.java @@ -6,24 +6,27 @@ package io.opentelemetry.instrumentation.spring.autoconfigure.webmvc; import io.opentelemetry.api.OpenTelemetry; -import io.opentelemetry.instrumentation.spring.webmvc.SpringWebMvcTelemetry; +import io.opentelemetry.instrumentation.spring.webmvc.v5_3.SpringWebMvcTelemetry; import javax.servlet.Filter; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.filter.OncePerRequestFilter; +import org.springframework.web.servlet.DispatcherServlet; /** Configures {@link SpringWebMvcTelemetry} for tracing. */ @Configuration @EnableConfigurationProperties(WebMvcProperties.class) @ConditionalOnProperty(prefix = "otel.springboot.web", name = "enabled", matchIfMissing = true) -@ConditionalOnClass(OncePerRequestFilter.class) +@ConditionalOnClass({OncePerRequestFilter.class, DispatcherServlet.class}) +@ConditionalOnBean(OpenTelemetry.class) public class WebMvcFilterAutoConfiguration { @Bean - public Filter otelWebMvcTracingFilter(OpenTelemetry openTelemetry) { - return SpringWebMvcTelemetry.create(openTelemetry).newServletFilter(); + public Filter otelWebMvcInstrumentationFilter(OpenTelemetry openTelemetry) { + return SpringWebMvcTelemetry.create(openTelemetry).createServletFilter(); } } 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 0c92cbbab63e..229b05f41c03 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 @@ -8,6 +8,7 @@ io.opentelemetry.instrumentation.spring.autoconfigure.exporters.otlp.OtlpSpanExp io.opentelemetry.instrumentation.spring.autoconfigure.exporters.zipkin.ZipkinSpanExporterAutoConfiguration,\ io.opentelemetry.instrumentation.spring.autoconfigure.httpclients.resttemplate.RestTemplateAutoConfiguration,\ io.opentelemetry.instrumentation.spring.autoconfigure.httpclients.webclient.WebClientAutoConfiguration,\ +io.opentelemetry.instrumentation.spring.autoconfigure.kafka.KafkaInstrumentationAutoConfiguration,\ io.opentelemetry.instrumentation.spring.autoconfigure.metrics.MicrometerShimAutoConfiguration,\ io.opentelemetry.instrumentation.spring.autoconfigure.propagators.PropagationAutoConfiguration,\ io.opentelemetry.instrumentation.spring.autoconfigure.resources.OtelResourceAutoConfiguration,\ diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/aspects/WithSpanAspectTest.java b/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/aspects/AbstractWithSpanAspectTest.java similarity index 61% rename from instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/aspects/WithSpanAspectTest.java rename to instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/aspects/AbstractWithSpanAspectTest.java index cf7a9efd94c2..614daf2fe25b 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/aspects/WithSpanAspectTest.java +++ b/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/aspects/AbstractWithSpanAspectTest.java @@ -5,15 +5,16 @@ package io.opentelemetry.instrumentation.spring.autoconfigure.aspects; +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.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; import static io.opentelemetry.sdk.testing.assertj.TracesAssert.assertThat; +import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.CODE_FUNCTION; +import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.CODE_NAMESPACE; import static org.assertj.core.api.Assertions.assertThatThrownBy; -import io.opentelemetry.api.common.AttributeKey; -import io.opentelemetry.api.common.Attributes; -import io.opentelemetry.extension.annotations.SpanAttribute; -import io.opentelemetry.extension.annotations.WithSpan; +import io.opentelemetry.api.OpenTelemetry; import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension; import io.opentelemetry.sdk.trace.data.SpanData; import io.opentelemetry.sdk.trace.data.StatusData; @@ -31,58 +32,48 @@ import org.springframework.core.ParameterNameDiscoverer; /** Spring AOP Test for {@link WithSpanAspect}. */ -public class WithSpanAspectTest { +abstract class AbstractWithSpanAspectTest { @RegisterExtension static final LibraryInstrumentationExtension testing = LibraryInstrumentationExtension.create(); - static class WithSpanTester { - @WithSpan - public String testWithSpan() { - return "Span with name testWithSpan was created"; - } + private WithSpanTester withSpanTester; + private String unproxiedTesterSimpleClassName; + private String unproxiedTesterClassName; - @WithSpan("greatestSpanEver") - public String testWithSpanWithValue() { - return "Span with name greatestSpanEver was created"; - } + public interface WithSpanTester { + String testWithSpan(); - @WithSpan - public String testWithSpanWithException() throws Exception { - throw new Exception("Test @WithSpan With Exception"); - } + String testWithSpanWithValue(); - @WithSpan(kind = CLIENT) - public String testWithClientSpan() { - return "Span with name testWithClientSpan and SpanKind.CLIENT was created"; - } + String testWithSpanWithException() throws Exception; - @WithSpan - public CompletionStage testAsyncCompletionStage(CompletionStage stage) { - return stage; - } + String testWithClientSpan(); - @WithSpan - public CompletableFuture testAsyncCompletableFuture(CompletableFuture stage) { - return stage; - } + CompletionStage testAsyncCompletionStage(CompletionStage stage); - @WithSpan - public String withSpanAttributes( - @SpanAttribute String discoveredName, - @SpanAttribute String implicitName, - @SpanAttribute("explicitName") String parameter, - @SpanAttribute("nullAttribute") String nullAttribute, - String notTraced) { + CompletableFuture testAsyncCompletableFuture(CompletableFuture stage); - return "hello!"; - } + String withSpanAttributes( + String discoveredName, + String implicitName, + String parameter, + String nullAttribute, + String notTraced); } - private WithSpanTester withSpanTester; + abstract WithSpanTester newWithSpanTester(); + + abstract WithSpanAspect newWithSpanAspect( + OpenTelemetry openTelemetry, ParameterNameDiscoverer parameterNameDiscoverer); @BeforeEach void setup() { - AspectJProxyFactory factory = new AspectJProxyFactory(new WithSpanTester()); + WithSpanTester unproxiedTester = newWithSpanTester(); + unproxiedTesterSimpleClassName = unproxiedTester.getClass().getSimpleName(); + unproxiedTesterClassName = unproxiedTester.getClass().getName(); + + AspectJProxyFactory factory = new AspectJProxyFactory(); + factory.setTarget(unproxiedTester); ParameterNameDiscoverer parameterNameDiscoverer = new ParameterNameDiscoverer() { @Override @@ -96,7 +87,7 @@ public String[] getParameterNames(Constructor constructor) { } }; - WithSpanAspect aspect = new WithSpanAspect(testing.getOpenTelemetry(), parameterNameDiscoverer); + WithSpanAspect aspect = newWithSpanAspect(testing.getOpenTelemetry(), parameterNameDiscoverer); factory.addAspect(aspect); withSpanTester = factory.getProxy(); @@ -104,7 +95,7 @@ public String[] getParameterNames(Constructor constructor) { @Test @DisplayName("when method is annotated with @WithSpan should wrap method execution in a Span") - void withSpanWithDefaults() throws Throwable { + void withSpanWithDefaults() { // when testing.runWithSpan("parent", withSpanTester::testWithSpan); @@ -116,17 +107,18 @@ void withSpanWithDefaults() throws Throwable { trace.hasSpansSatisfyingExactly( parentSpan -> parentSpan.hasName("parent").hasKind(INTERNAL), span -> - span.hasName("WithSpanTester.testWithSpan") + span.hasName(unproxiedTesterSimpleClassName + ".testWithSpan") .hasKind(INTERNAL) - // otel SDK assertions need some work before we can comfortably use - // them in this project... - .hasParentSpanId(traces.get(0).get(0).getSpanId()))); + .hasParent(trace.getSpan(0)) + .hasAttributesSatisfyingExactly( + equalTo(CODE_NAMESPACE, unproxiedTesterClassName), + equalTo(CODE_FUNCTION, "testWithSpan")))); } @Test @DisplayName( "when @WithSpan value is set should wrap method execution in a Span with custom name") - void withSpanName() throws Throwable { + void withSpanName() { // when testing.runWithSpan("parent", () -> withSpanTester.testWithSpanWithValue()); @@ -140,13 +132,16 @@ void withSpanName() throws Throwable { span -> span.hasName("greatestSpanEver") .hasKind(INTERNAL) - .hasParentSpanId(traces.get(0).get(0).getSpanId()))); + .hasParent(trace.getSpan(0)) + .hasAttributesSatisfyingExactly( + equalTo(CODE_NAMESPACE, unproxiedTesterClassName), + equalTo(CODE_FUNCTION, "testWithSpanWithValue")))); } @Test @DisplayName( "when method is annotated with @WithSpan AND an exception is thrown span should record the exception") - void withSpanError() throws Throwable { + void withSpanError() { assertThatThrownBy(() -> withSpanTester.testWithSpanWithException()) .isInstanceOf(Exception.class); @@ -156,15 +151,18 @@ void withSpanError() throws Throwable { trace -> trace.hasSpansSatisfyingExactly( span -> - span.hasName("WithSpanTester.testWithSpanWithException") + span.hasName(unproxiedTesterSimpleClassName + ".testWithSpanWithException") .hasKind(INTERNAL) - .hasStatus(StatusData.error()))); + .hasStatus(StatusData.error()) + .hasAttributesSatisfyingExactly( + equalTo(CODE_NAMESPACE, unproxiedTesterClassName), + equalTo(CODE_FUNCTION, "testWithSpanWithException")))); } @Test @DisplayName( "when method is annotated with @WithSpan(kind=CLIENT) should build span with the declared SpanKind") - void withSpanKind() throws Throwable { + void withSpanKind() { // when testing.runWithSpan("parent", () -> withSpanTester.testWithClientSpan()); @@ -176,14 +174,16 @@ void withSpanKind() throws Throwable { trace.hasSpansSatisfyingExactly( parentSpan -> parentSpan.hasName("parent").hasKind(INTERNAL), span -> - span.hasName("WithSpanTester.testWithClientSpan") + span.hasName(unproxiedTesterSimpleClassName + ".testWithClientSpan") .hasKind(CLIENT) - .hasParentSpanId(traces.get(0).get(0).getSpanId()))); + .hasParent(trace.getSpan(0)) + .hasAttributesSatisfyingExactly( + equalTo(CODE_NAMESPACE, unproxiedTesterClassName), + equalTo(CODE_FUNCTION, "testWithClientSpan")))); } @Test - @DisplayName("") - void withSpanAttributes() throws Throwable { + void withSpanAttributes() { // when testing.runWithSpan( "parent", () -> withSpanTester.withSpanAttributes("foo", "bar", "baz", null, "fizz")); @@ -196,14 +196,15 @@ void withSpanAttributes() throws Throwable { trace.hasSpansSatisfyingExactly( parentSpan -> parentSpan.hasName("parent").hasKind(INTERNAL), span -> - span.hasName("WithSpanTester.withSpanAttributes") + span.hasName(unproxiedTesterSimpleClassName + ".withSpanAttributes") .hasKind(INTERNAL) - .hasAttributes( - Attributes.of( - AttributeKey.stringKey("discoveredName"), "foo", - AttributeKey.stringKey("implicitName"), "bar", - AttributeKey.stringKey("explicitName"), "baz")) - .hasParentSpanId(traces.get(0).get(0).getSpanId()))); + .hasParent(trace.getSpan(0)) + .hasAttributesSatisfyingExactly( + equalTo(CODE_NAMESPACE, unproxiedTesterClassName), + equalTo(CODE_FUNCTION, "withSpanAttributes"), + equalTo(stringKey("discoveredName"), "foo"), + equalTo(stringKey("implicitName"), "bar"), + equalTo(stringKey("explicitName"), "baz")))); } @Nested @@ -212,7 +213,7 @@ class WithCompletionStage { @Test @DisplayName("should end Span on complete") - void onComplete() throws Throwable { + void onComplete() { CompletableFuture future = new CompletableFuture<>(); // when @@ -237,14 +238,17 @@ void onComplete() throws Throwable { trace.hasSpansSatisfyingExactly( parentSpan -> parentSpan.hasName("parent").hasKind(INTERNAL), span -> - span.hasName("WithSpanTester.testAsyncCompletionStage") + span.hasName(unproxiedTesterSimpleClassName + ".testAsyncCompletionStage") .hasKind(INTERNAL) - .hasParentSpanId(traces.get(0).get(0).getSpanId()))); + .hasParent(trace.getSpan(0)) + .hasAttributesSatisfyingExactly( + equalTo(CODE_NAMESPACE, unproxiedTesterClassName), + equalTo(CODE_FUNCTION, "testAsyncCompletionStage")))); } @Test @DisplayName("should end Span on completeException AND should record the exception") - void onCompleteExceptionally() throws Throwable { + void onCompleteExceptionally() { CompletableFuture future = new CompletableFuture<>(); // when @@ -269,15 +273,18 @@ void onCompleteExceptionally() throws Throwable { trace.hasSpansSatisfyingExactly( parentSpan -> parentSpan.hasName("parent").hasKind(INTERNAL), span -> - span.hasName("WithSpanTester.testAsyncCompletionStage") + span.hasName(unproxiedTesterSimpleClassName + ".testAsyncCompletionStage") .hasKind(INTERNAL) .hasStatus(StatusData.error()) - .hasParentSpanId(traces.get(0).get(0).getSpanId()))); + .hasParent(trace.getSpan(0)) + .hasAttributesSatisfyingExactly( + equalTo(CODE_NAMESPACE, unproxiedTesterClassName), + equalTo(CODE_FUNCTION, "testAsyncCompletionStage")))); } @Test @DisplayName("should end Span on incompatible return value") - void onIncompatibleReturnValue() throws Throwable { + void onIncompatibleReturnValue() { // when testing.runWithSpan("parent", () -> withSpanTester.testAsyncCompletionStage(null)); @@ -289,9 +296,12 @@ void onIncompatibleReturnValue() throws Throwable { trace.hasSpansSatisfyingExactly( parentSpan -> parentSpan.hasName("parent").hasKind(INTERNAL), span -> - span.hasName("WithSpanTester.testAsyncCompletionStage") + span.hasName(unproxiedTesterSimpleClassName + ".testAsyncCompletionStage") .hasKind(INTERNAL) - .hasParentSpanId(traces.get(0).get(0).getSpanId()))); + .hasParent(trace.getSpan(0)) + .hasAttributesSatisfyingExactly( + equalTo(CODE_NAMESPACE, unproxiedTesterClassName), + equalTo(CODE_FUNCTION, "testAsyncCompletionStage")))); } } @@ -301,7 +311,7 @@ class WithCompletableFuture { @Test @DisplayName("should end Span on complete") - void onComplete() throws Throwable { + void onComplete() { CompletableFuture future = new CompletableFuture<>(); // when @@ -326,14 +336,18 @@ void onComplete() throws Throwable { trace.hasSpansSatisfyingExactly( parentSpan -> parentSpan.hasName("parent").hasKind(INTERNAL), span -> - span.hasName("WithSpanTester.testAsyncCompletableFuture") + span.hasName( + unproxiedTesterSimpleClassName + ".testAsyncCompletableFuture") .hasKind(INTERNAL) - .hasParentSpanId(traces.get(0).get(0).getSpanId()))); + .hasParent(trace.getSpan(0)) + .hasAttributesSatisfyingExactly( + equalTo(CODE_NAMESPACE, unproxiedTesterClassName), + equalTo(CODE_FUNCTION, "testAsyncCompletableFuture")))); } @Test @DisplayName("should end Span on completeException AND should record the exception") - void onCompleteExceptionally() throws Throwable { + void onCompleteExceptionally() { CompletableFuture future = new CompletableFuture<>(); // when @@ -358,15 +372,19 @@ void onCompleteExceptionally() throws Throwable { trace.hasSpansSatisfyingExactly( parentSpan -> parentSpan.hasName("parent").hasKind(INTERNAL), span -> - span.hasName("WithSpanTester.testAsyncCompletableFuture") + span.hasName( + unproxiedTesterSimpleClassName + ".testAsyncCompletableFuture") .hasKind(INTERNAL) .hasStatus(StatusData.error()) - .hasParentSpanId(traces.get(0).get(0).getSpanId()))); + .hasParent(trace.getSpan(0)) + .hasAttributesSatisfyingExactly( + equalTo(CODE_NAMESPACE, unproxiedTesterClassName), + equalTo(CODE_FUNCTION, "testAsyncCompletableFuture")))); } @Test @DisplayName("should end the Span when already complete") - void onCompletedFuture() throws Throwable { + void onCompletedFuture() { CompletableFuture future = CompletableFuture.completedFuture("Done"); // when @@ -380,14 +398,18 @@ void onCompletedFuture() throws Throwable { trace.hasSpansSatisfyingExactly( parentSpan -> parentSpan.hasName("parent").hasKind(INTERNAL), span -> - span.hasName("WithSpanTester.testAsyncCompletableFuture") + span.hasName( + unproxiedTesterSimpleClassName + ".testAsyncCompletableFuture") .hasKind(INTERNAL) - .hasParentSpanId(traces.get(0).get(0).getSpanId()))); + .hasParent(trace.getSpan(0)) + .hasAttributesSatisfyingExactly( + equalTo(CODE_NAMESPACE, unproxiedTesterClassName), + equalTo(CODE_FUNCTION, "testAsyncCompletableFuture")))); } @Test @DisplayName("should end the Span when already failed") - void onFailedFuture() throws Throwable { + void onFailedFuture() { CompletableFuture future = new CompletableFuture<>(); future.completeExceptionally(new Exception("Test @WithSpan With completeExceptionally")); @@ -402,15 +424,19 @@ void onFailedFuture() throws Throwable { trace.hasSpansSatisfyingExactly( parentSpan -> parentSpan.hasName("parent").hasKind(INTERNAL), span -> - span.hasName("WithSpanTester.testAsyncCompletableFuture") + span.hasName( + unproxiedTesterSimpleClassName + ".testAsyncCompletableFuture") .hasKind(INTERNAL) .hasStatus(StatusData.error()) - .hasParentSpanId(traces.get(0).get(0).getSpanId()))); + .hasParent(trace.getSpan(0)) + .hasAttributesSatisfyingExactly( + equalTo(CODE_NAMESPACE, unproxiedTesterClassName), + equalTo(CODE_FUNCTION, "testAsyncCompletableFuture")))); } @Test @DisplayName("should end Span on incompatible return value") - void onIncompatibleReturnValue() throws Throwable { + void onIncompatibleReturnValue() { // when testing.runWithSpan("parent", () -> withSpanTester.testAsyncCompletableFuture(null)); @@ -422,9 +448,13 @@ void onIncompatibleReturnValue() throws Throwable { trace.hasSpansSatisfyingExactly( parentSpan -> parentSpan.hasName("parent").hasKind(INTERNAL), span -> - span.hasName("WithSpanTester.testAsyncCompletableFuture") + span.hasName( + unproxiedTesterSimpleClassName + ".testAsyncCompletableFuture") .hasKind(INTERNAL) - .hasParentSpanId(traces.get(0).get(0).getSpanId()))); + .hasParent(trace.getSpan(0)) + .hasAttributesSatisfyingExactly( + equalTo(CODE_NAMESPACE, unproxiedTesterClassName), + equalTo(CODE_FUNCTION, "testAsyncCompletableFuture")))); } } } diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/aspects/InstrumentationWithSpanAspectTest.java b/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/aspects/InstrumentationWithSpanAspectTest.java new file mode 100644 index 000000000000..25e61b7222e2 --- /dev/null +++ b/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/aspects/InstrumentationWithSpanAspectTest.java @@ -0,0 +1,79 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.spring.autoconfigure.aspects; + +import static io.opentelemetry.api.trace.SpanKind.CLIENT; + +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.instrumentation.annotations.SpanAttribute; +import io.opentelemetry.instrumentation.annotations.WithSpan; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionStage; +import org.springframework.core.ParameterNameDiscoverer; + +class InstrumentationWithSpanAspectTest extends AbstractWithSpanAspectTest { + + @Override + WithSpanTester newWithSpanTester() { + return new InstrumentationWithSpanTester(); + } + + @Override + WithSpanAspect newWithSpanAspect( + OpenTelemetry openTelemetry, ParameterNameDiscoverer parameterNameDiscoverer) { + return new InstrumentationWithSpanAspect(openTelemetry, parameterNameDiscoverer); + } + + static class InstrumentationWithSpanTester implements WithSpanTester { + @Override + @WithSpan + public String testWithSpan() { + return "Span with name testWithSpan was created"; + } + + @Override + @WithSpan("greatestSpanEver") + public String testWithSpanWithValue() { + return "Span with name greatestSpanEver was created"; + } + + @Override + @WithSpan + public String testWithSpanWithException() throws Exception { + throw new Exception("Test @WithSpan With Exception"); + } + + @Override + @WithSpan(kind = CLIENT) + public String testWithClientSpan() { + return "Span with name testWithClientSpan and SpanKind.CLIENT was created"; + } + + @Override + @WithSpan + public CompletionStage testAsyncCompletionStage(CompletionStage stage) { + return stage; + } + + @Override + @WithSpan + public CompletableFuture testAsyncCompletableFuture(CompletableFuture stage) { + return stage; + } + + @Override + @WithSpan + public String withSpanAttributes( + @SpanAttribute String discoveredName, + @SpanAttribute String implicitName, + @SpanAttribute("explicitName") String parameter, + @SpanAttribute("nullAttribute") String nullAttribute, + String notTraced) { + + return "hello!"; + } + } +} diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/aspects/SdkExtensionWithSpanAspectTest.java b/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/aspects/SdkExtensionWithSpanAspectTest.java new file mode 100644 index 000000000000..4e8a8f63f798 --- /dev/null +++ b/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/aspects/SdkExtensionWithSpanAspectTest.java @@ -0,0 +1,78 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.spring.autoconfigure.aspects; + +import static io.opentelemetry.api.trace.SpanKind.CLIENT; + +import io.opentelemetry.api.OpenTelemetry; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionStage; +import org.springframework.core.ParameterNameDiscoverer; + +@SuppressWarnings("deprecation") // instrumenting deprecated class for backwards compatibility +class SdkExtensionWithSpanAspectTest extends AbstractWithSpanAspectTest { + + @Override + WithSpanTester newWithSpanTester() { + return new SdkExtensionWithSpanTester(); + } + + @Override + WithSpanAspect newWithSpanAspect( + OpenTelemetry openTelemetry, ParameterNameDiscoverer parameterNameDiscoverer) { + return new SdkExtensionWithSpanAspect(openTelemetry, parameterNameDiscoverer); + } + + static class SdkExtensionWithSpanTester implements WithSpanTester { + @Override + @io.opentelemetry.extension.annotations.WithSpan + public String testWithSpan() { + return "Span with name testWithSpan was created"; + } + + @Override + @io.opentelemetry.extension.annotations.WithSpan("greatestSpanEver") + public String testWithSpanWithValue() { + return "Span with name greatestSpanEver was created"; + } + + @Override + @io.opentelemetry.extension.annotations.WithSpan + public String testWithSpanWithException() throws Exception { + throw new Exception("Test @WithSpan With Exception"); + } + + @Override + @io.opentelemetry.extension.annotations.WithSpan(kind = CLIENT) + public String testWithClientSpan() { + return "Span with name testWithClientSpan and SpanKind.CLIENT was created"; + } + + @Override + @io.opentelemetry.extension.annotations.WithSpan + public CompletionStage testAsyncCompletionStage(CompletionStage stage) { + return stage; + } + + @Override + @io.opentelemetry.extension.annotations.WithSpan + public CompletableFuture testAsyncCompletableFuture(CompletableFuture stage) { + return stage; + } + + @Override + @io.opentelemetry.extension.annotations.WithSpan + public String withSpanAttributes( + @io.opentelemetry.extension.annotations.SpanAttribute String discoveredName, + @io.opentelemetry.extension.annotations.SpanAttribute String implicitName, + @io.opentelemetry.extension.annotations.SpanAttribute("explicitName") String parameter, + @io.opentelemetry.extension.annotations.SpanAttribute("nullAttribute") String nullAttribute, + String notTraced) { + + return "hello!"; + } + } +} diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/aspects/TraceAspectAutoConfigurationTest.java b/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/aspects/TraceAspectAutoConfigurationTest.java index 5958ad830269..3dc05203bf0a 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/aspects/TraceAspectAutoConfigurationTest.java +++ b/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/aspects/TraceAspectAutoConfigurationTest.java @@ -29,7 +29,9 @@ void aspectsEnabled() { .withPropertyValues("otel.springboot.aspects.enabled=true") .run( context -> - assertThat(context.getBean("withSpanAspect", WithSpanAspect.class)).isNotNull()); + assertThat(context) + .hasBean("instrumentationWithSpanAspect") + .hasBean("sdkExtensionWithSpanAspect")); } @Test @@ -37,13 +39,20 @@ void aspectsEnabled() { void disabledProperty() { this.contextRunner .withPropertyValues("otel.springboot.aspects.enabled=false") - .run(context -> assertThat(context.containsBean("withSpanAspect")).isFalse()); + .run( + context -> + assertThat(context) + .doesNotHaveBean("instrumentationWithSpanAspect") + .doesNotHaveBean("sdkExtensionWithSpanAspect")); } @Test @DisplayName("when aspects enabled property is MISSING should initialize WithSpanAspect bean") void noProperty() { this.contextRunner.run( - context -> assertThat(context.getBean("withSpanAspect", WithSpanAspect.class)).isNotNull()); + context -> + assertThat(context) + .hasBean("instrumentationWithSpanAspect") + .hasBean("sdkExtensionWithSpanAspect")); } } diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/exporters/MetricExporterAutoConfigurationTest.java b/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/exporters/MetricExporterAutoConfigurationTest.java new file mode 100644 index 000000000000..6d42070a16f2 --- /dev/null +++ b/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/exporters/MetricExporterAutoConfigurationTest.java @@ -0,0 +1,55 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.spring.autoconfigure.exporters; + +import static org.assertj.core.api.Assertions.assertThat; + +import io.opentelemetry.exporter.logging.LoggingMetricExporter; +import io.opentelemetry.exporter.otlp.metrics.OtlpGrpcMetricExporter; +import io.opentelemetry.instrumentation.spring.autoconfigure.exporters.logging.LoggingMetricExporterAutoConfiguration; +import io.opentelemetry.instrumentation.spring.autoconfigure.exporters.otlp.OtlpMetricExporterAutoConfiguration; +import org.junit.jupiter.api.Test; +import org.springframework.boot.autoconfigure.AutoConfigurations; +import org.springframework.boot.test.context.runner.ApplicationContextRunner; + +class MetricExporterAutoConfigurationTest { + + private final ApplicationContextRunner contextRunner = + new ApplicationContextRunner() + .withConfiguration( + AutoConfigurations.of( + OtlpMetricExporterAutoConfiguration.class, + LoggingMetricExporterAutoConfiguration.class)); + + @Test + void defaultConfiguration() { + contextRunner.run( + context -> { + assertThat(context.getBean("otelOtlpGrpcMetricExporter", OtlpGrpcMetricExporter.class)) + .as("OTLP exporter is enabled by default") + .isNotNull(); + assertThat(context.containsBean("otelLoggingMetricExporter")) + .as("Logging exporter is not created unless explicitly configured") + .isFalse(); + }); + } + + @Test + void loggingEnabledByConfiguration() { + contextRunner + .withPropertyValues("otel.exporter.logging.enabled=true") + .run( + context -> { + assertThat( + context.getBean("otelOtlpGrpcMetricExporter", OtlpGrpcMetricExporter.class)) + .as("OTLP exporter is present even with logging enabled") + .isNotNull(); + assertThat(context.getBean("otelLoggingMetricExporter", LoggingMetricExporter.class)) + .as("Logging exporter is explicitly enabled") + .isNotNull(); + }); + } +} diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/exporters/SpanExporterAutoConfigurationTest.java b/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/exporters/SpanExporterAutoConfigurationTest.java new file mode 100644 index 000000000000..81f805bcc982 --- /dev/null +++ b/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/exporters/SpanExporterAutoConfigurationTest.java @@ -0,0 +1,54 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.spring.autoconfigure.exporters; + +import static org.assertj.core.api.Assertions.assertThat; + +import io.opentelemetry.exporter.logging.LoggingSpanExporter; +import io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporter; +import io.opentelemetry.instrumentation.spring.autoconfigure.exporters.logging.LoggingSpanExporterAutoConfiguration; +import io.opentelemetry.instrumentation.spring.autoconfigure.exporters.otlp.OtlpSpanExporterAutoConfiguration; +import org.junit.jupiter.api.Test; +import org.springframework.boot.autoconfigure.AutoConfigurations; +import org.springframework.boot.test.context.runner.ApplicationContextRunner; + +class SpanExporterAutoConfigurationTest { + + private final ApplicationContextRunner contextRunner = + new ApplicationContextRunner() + .withConfiguration( + AutoConfigurations.of( + OtlpSpanExporterAutoConfiguration.class, + LoggingSpanExporterAutoConfiguration.class)); + + @Test + void defaultConfiguration() { + contextRunner.run( + context -> { + assertThat(context.getBean("otelOtlpGrpcSpanExporter", OtlpGrpcSpanExporter.class)) + .as("OTLP exporter is enabled by default") + .isNotNull(); + assertThat(context.containsBean("otelLoggingSpanExporter")) + .as("Logging exporter is not created unless explicitly configured") + .isFalse(); + }); + } + + @Test + void loggingEnabledByConfiguration() { + contextRunner + .withPropertyValues("otel.exporter.logging.enabled=true") + .run( + context -> { + assertThat(context.getBean("otelOtlpGrpcSpanExporter", OtlpGrpcSpanExporter.class)) + .as("OTLP exporter is present even with logging enabled") + .isNotNull(); + assertThat(context.getBean("otelLoggingSpanExporter", LoggingSpanExporter.class)) + .as("Logging exporter is explicitly enabled") + .isNotNull(); + }); + } +} diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/exporters/JaegerSpanExporterAutoConfigurationTest.java b/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/exporters/jaeger/JaegerSpanExporterAutoConfigurationTest.java similarity index 92% rename from instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/exporters/JaegerSpanExporterAutoConfigurationTest.java rename to instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/exporters/jaeger/JaegerSpanExporterAutoConfigurationTest.java index 1dece99afeb8..b1d0bf4492d5 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/exporters/JaegerSpanExporterAutoConfigurationTest.java +++ b/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/exporters/jaeger/JaegerSpanExporterAutoConfigurationTest.java @@ -3,14 +3,12 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.instrumentation.spring.autoconfigure.exporters; +package io.opentelemetry.instrumentation.spring.autoconfigure.exporters.jaeger; import static org.assertj.core.api.Assertions.assertThat; import io.opentelemetry.exporter.jaeger.JaegerGrpcSpanExporter; import io.opentelemetry.instrumentation.spring.autoconfigure.OpenTelemetryAutoConfiguration; -import io.opentelemetry.instrumentation.spring.autoconfigure.exporters.jaeger.JaegerSpanExporterAutoConfiguration; -import io.opentelemetry.instrumentation.spring.autoconfigure.exporters.jaeger.JaegerSpanExporterProperties; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.boot.autoconfigure.AutoConfigurations; diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/exporters/logging/LoggingMetricExporterAutoConfigurationTest.java b/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/exporters/logging/LoggingMetricExporterAutoConfigurationTest.java index f5db0e577a77..5b1e8355e1ec 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/exporters/logging/LoggingMetricExporterAutoConfigurationTest.java +++ b/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/exporters/logging/LoggingMetricExporterAutoConfigurationTest.java @@ -60,9 +60,6 @@ void loggingMetricsDisabled() { @Test void noProperties() { - runner.run( - context -> - assertThat(context.getBean("otelLoggingMetricExporter", LoggingMetricExporter.class)) - .isNotNull()); + runner.run(context -> assertThat(context.containsBean("otelLoggingMetricExporter")).isFalse()); } } diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/exporters/logging/LoggingSpanExporterAutoConfigurationTest.java b/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/exporters/logging/LoggingSpanExporterAutoConfigurationTest.java index cd53f3520432..34abb3200ec2 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/exporters/logging/LoggingSpanExporterAutoConfigurationTest.java +++ b/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/exporters/logging/LoggingSpanExporterAutoConfigurationTest.java @@ -27,7 +27,7 @@ class LoggingSpanExporterAutoConfigurationTest { @Test @DisplayName("when exporters are ENABLED should initialize LoggingSpanExporter bean") void loggingEnabled() { - this.contextRunner + contextRunner .withPropertyValues("otel.exporter.logging.enabled=true") .run( context -> @@ -37,7 +37,7 @@ void loggingEnabled() { @Test void loggingTracesEnabled() { - this.contextRunner + contextRunner .withPropertyValues("otel.exporter.logging.traces.enabled=true") .run( context -> @@ -48,7 +48,7 @@ void loggingTracesEnabled() { @Test @DisplayName("when exporters are DISABLED should NOT initialize LoggingSpanExporter bean") void loggingDisabled() { - this.contextRunner + contextRunner .withPropertyValues("otel.exporter.logging.enabled=false") .run(context -> assertThat(context.containsBean("otelLoggingSpanExporter")).isFalse()); } @@ -56,7 +56,7 @@ void loggingDisabled() { @Test @DisplayName("when exporters are DISABLED should NOT initialize LoggingSpanExporter bean") void loggingTracesDisabled() { - this.contextRunner + contextRunner .withPropertyValues("otel.exporter.logging.traces.enabled=false") .run(context -> assertThat(context.containsBean("otelLoggingSpanExporter")).isFalse()); } @@ -65,9 +65,7 @@ void loggingTracesDisabled() { @DisplayName( "when exporter enabled property is MISSING should initialize LoggingSpanExporter bean") void exporterPresentByDefault() { - this.contextRunner.run( - context -> - assertThat(context.getBean("otelLoggingSpanExporter", LoggingSpanExporter.class)) - .isNotNull()); + contextRunner.run( + context -> assertThat(context.containsBean("otelLoggingSpanExporter")).isFalse()); } } diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/exporters/ZipkinSpanExporterAutoConfigurationTest.java b/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/exporters/zipkin/ZipkinSpanExporterAutoConfigurationTest.java similarity index 91% rename from instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/exporters/ZipkinSpanExporterAutoConfigurationTest.java rename to instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/exporters/zipkin/ZipkinSpanExporterAutoConfigurationTest.java index 4147826e69b3..7e6aa89a4515 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/exporters/ZipkinSpanExporterAutoConfigurationTest.java +++ b/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/exporters/zipkin/ZipkinSpanExporterAutoConfigurationTest.java @@ -3,14 +3,12 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.instrumentation.spring.autoconfigure.exporters; +package io.opentelemetry.instrumentation.spring.autoconfigure.exporters.zipkin; import static org.assertj.core.api.Assertions.assertThat; import io.opentelemetry.exporter.zipkin.ZipkinSpanExporter; import io.opentelemetry.instrumentation.spring.autoconfigure.OpenTelemetryAutoConfiguration; -import io.opentelemetry.instrumentation.spring.autoconfigure.exporters.zipkin.ZipkinSpanExporterAutoConfiguration; -import io.opentelemetry.instrumentation.spring.autoconfigure.exporters.zipkin.ZipkinSpanExporterProperties; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.boot.autoconfigure.AutoConfigurations; diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/kafka/KafkaIntegrationTest.java b/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/kafka/KafkaIntegrationTest.java new file mode 100644 index 000000000000..1611b69f3c44 --- /dev/null +++ b/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/kafka/KafkaIntegrationTest.java @@ -0,0 +1,143 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.spring.autoconfigure.kafka; + +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies; + +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension; +import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; +import java.time.Duration; +import org.apache.kafka.clients.admin.NewTopic; +import org.apache.kafka.clients.consumer.ConsumerRecord; +import org.assertj.core.api.AbstractLongAssert; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.springframework.boot.autoconfigure.AutoConfigurations; +import org.springframework.boot.autoconfigure.kafka.KafkaAutoConfiguration; +import org.springframework.boot.test.context.runner.ApplicationContextRunner; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.kafka.annotation.KafkaListener; +import org.springframework.kafka.config.TopicBuilder; +import org.springframework.kafka.core.KafkaTemplate; +import org.testcontainers.containers.KafkaContainer; +import org.testcontainers.containers.wait.strategy.Wait; +import org.testcontainers.utility.DockerImageName; + +class KafkaIntegrationTest { + + @RegisterExtension + static final LibraryInstrumentationExtension testing = LibraryInstrumentationExtension.create(); + + static KafkaContainer kafka; + + private ApplicationContextRunner contextRunner; + + @BeforeAll + static void setUpKafka() { + kafka = + new KafkaContainer(DockerImageName.parse("confluentinc/cp-kafka:5.4.3")) + .waitingFor(Wait.forLogMessage(".*started \\(kafka.server.KafkaServer\\).*", 1)) + .withStartupTimeout(Duration.ofMinutes(1)); + kafka.start(); + } + + @AfterAll + static void tearDownKafka() { + kafka.stop(); + } + + @BeforeEach + void setUpContext() { + contextRunner = + new ApplicationContextRunner() + .withConfiguration( + AutoConfigurations.of( + KafkaAutoConfiguration.class, + KafkaInstrumentationAutoConfiguration.class, + TestConfig.class)) + .withBean("openTelemetry", OpenTelemetry.class, testing::getOpenTelemetry) + .withPropertyValues( + "spring.kafka.bootstrap-servers=" + kafka.getBootstrapServers(), + "spring.kafka.consumer.auto-offset-reset=earliest", + "spring.kafka.consumer.linger-ms=10", + "spring.kafka.listener.idle-between-polls=1000", + "spring.kafka.producer.transaction-id-prefix=test-"); + } + + @Disabled + @Test + void shouldInstrumentProducerAndConsumer() { + contextRunner.run(KafkaIntegrationTest::runShouldInstrumentProducerAndConsumer); + } + + @SuppressWarnings("unchecked") + private static void runShouldInstrumentProducerAndConsumer( + ConfigurableApplicationContext applicationContext) { + KafkaTemplate kafkaTemplate = applicationContext.getBean(KafkaTemplate.class); + + testing.runWithSpan( + "producer", + () -> { + kafkaTemplate.executeInTransaction( + ops -> { + ops.usingCompletableFuture().send("testTopic", "10", "testSpan"); + return 0; + }); + }); + + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> span.hasName("producer"), + span -> + span.hasName("testTopic send") + .hasKind(SpanKind.PRODUCER) + .hasParent(trace.getSpan(0)) + .hasAttributesSatisfyingExactly( + equalTo(SemanticAttributes.MESSAGING_SYSTEM, "kafka"), + equalTo(SemanticAttributes.MESSAGING_DESTINATION, "testTopic"), + equalTo(SemanticAttributes.MESSAGING_DESTINATION_KIND, "topic")), + span -> + span.hasName("testTopic process") + .hasKind(SpanKind.CONSUMER) + .hasParent(trace.getSpan(1)) + .hasAttributesSatisfyingExactly( + equalTo(SemanticAttributes.MESSAGING_SYSTEM, "kafka"), + equalTo(SemanticAttributes.MESSAGING_DESTINATION, "testTopic"), + equalTo(SemanticAttributes.MESSAGING_DESTINATION_KIND, "topic"), + equalTo(SemanticAttributes.MESSAGING_OPERATION, "process"), + satisfies( + SemanticAttributes.MESSAGING_MESSAGE_PAYLOAD_SIZE_BYTES, + AbstractLongAssert::isNotNegative), + satisfies( + SemanticAttributes.MESSAGING_KAFKA_PARTITION, + AbstractLongAssert::isNotNegative)), + span -> span.hasName("consumer").hasParent(trace.getSpan(2)))); + } + + @Configuration + static class TestConfig { + + @Bean + public NewTopic testTopic() { + return TopicBuilder.name("testTopic").partitions(1).replicas(1).build(); + } + + @KafkaListener(id = "testListener", topics = "testTopic") + public void listener(ConsumerRecord record) { + testing.runWithSpan("consumer", () -> {}); + } + } +} diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/metrics/MicrometerShimAutoConfigurationTest.java b/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/metrics/MicrometerShimAutoConfigurationTest.java index 2e21b4940686..7e66044cd0db 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/metrics/MicrometerShimAutoConfigurationTest.java +++ b/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/metrics/MicrometerShimAutoConfigurationTest.java @@ -8,8 +8,8 @@ import static org.assertj.core.api.Assertions.assertThat; import io.micrometer.core.instrument.MeterRegistry; +import io.opentelemetry.instrumentation.micrometer.v1_5.OpenTelemetryMeterRegistry; import io.opentelemetry.instrumentation.spring.autoconfigure.OpenTelemetryAutoConfiguration; -import io.opentelemetry.micrometer1shim.OpenTelemetryMeterRegistry; import org.junit.jupiter.api.Test; import org.springframework.boot.actuate.autoconfigure.metrics.MetricsAutoConfiguration; import org.springframework.boot.autoconfigure.AutoConfigurations; diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/webmvc/WebMvcFilterAutoConfigurationTest.java b/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/webmvc/WebMvcFilterAutoConfigurationTest.java index 666c452c59a2..302ae77c9ca8 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/webmvc/WebMvcFilterAutoConfigurationTest.java +++ b/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/webmvc/WebMvcFilterAutoConfigurationTest.java @@ -29,7 +29,8 @@ void webEnabled() { .withPropertyValues("otel.springboot.web.enabled=true") .run( context -> - assertThat(context.getBean("otelWebMvcTracingFilter", Filter.class)).isNotNull()); + assertThat(context.getBean("otelWebMvcInstrumentationFilter", Filter.class)) + .isNotNull()); } @Test @@ -37,7 +38,9 @@ void webEnabled() { void disabledProperty() { this.contextRunner .withPropertyValues("otel.springboot.web.enabled=false") - .run(context -> assertThat(context.containsBean("otelWebMvcTracingFilter")).isFalse()); + .run( + context -> + assertThat(context.containsBean("otelWebMvcInstrumentationFilter")).isFalse()); } @Test @@ -45,6 +48,7 @@ void disabledProperty() { void noProperty() { this.contextRunner.run( context -> - assertThat(context.getBean("otelWebMvcTracingFilter", Filter.class)).isNotNull()); + assertThat(context.getBean("otelWebMvcInstrumentationFilter", Filter.class)) + .isNotNull()); } } diff --git a/instrumentation/spring/spring-boot-resources/library/build.gradle.kts b/instrumentation/spring/spring-boot-resources/library/build.gradle.kts new file mode 100644 index 000000000000..90e4f61f16f4 --- /dev/null +++ b/instrumentation/spring/spring-boot-resources/library/build.gradle.kts @@ -0,0 +1,15 @@ +plugins { + id("otel.sdk-extension") +} + +dependencies { + compileOnly("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure-spi") + + annotationProcessor("com.google.auto.service:auto-service") + compileOnly("com.google.auto.service:auto-service-annotations") + testCompileOnly("com.google.auto.service:auto-service-annotations") + + implementation("org.yaml:snakeyaml") + + testImplementation("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure-spi") +} diff --git a/instrumentation/spring/spring-boot-resources/library/src/main/java/io/opentelemetry/instrumentation/spring/resources/SpringBootServiceNameDetector.java b/instrumentation/spring/spring-boot-resources/library/src/main/java/io/opentelemetry/instrumentation/spring/resources/SpringBootServiceNameDetector.java new file mode 100644 index 000000000000..ba7bd65e6b91 --- /dev/null +++ b/instrumentation/spring/spring-boot-resources/library/src/main/java/io/opentelemetry/instrumentation/spring/resources/SpringBootServiceNameDetector.java @@ -0,0 +1,300 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.spring.resources; + +import static java.util.logging.Level.FINE; + +import com.google.auto.service.AutoService; +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.resource.attributes.ResourceAttributes; +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.Method; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.Properties; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Stream; +import javax.annotation.Nullable; +import org.yaml.snakeyaml.Yaml; + +/** + * A ResourceProvider that will attempt to guess the application name for a Spring Boot service. + * When successful, it will return a Resource that has the service name attribute populated with the + * name of the Spring Boot application. It uses the following strategies, and the first successful + * strategy wins: + * + *

    + *
  • Check for the SPRING_APPLICATION_NAME environment variable + *
  • Check for spring.application.name system property + *
  • Check for application.properties file on the classpath + *
  • Check for application.properties in the current working dir + *
  • Check for application.yml on the classpath + *
  • 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 + *
+ */ +@AutoService(ResourceProvider.class) +public class SpringBootServiceNameDetector implements ConditionalResourceProvider { + + private static final Logger logger = + Logger.getLogger(SpringBootServiceNameDetector.class.getName()); + private static final String COMMANDLINE_ARG_PREFIX = "--spring.application.name="; + private static final Pattern COMMANDLINE_PATTERN = + Pattern.compile("--spring\\.application\\.name=([a-zA-Z.\\-_]+)"); + private final SystemHelper system; + + @SuppressWarnings("unused") + public SpringBootServiceNameDetector() { + this(new SystemHelper()); + } + + // Exists for testing + SpringBootServiceNameDetector(SystemHelper system) { + this.system = system; + } + + @Override + public Resource createResource(ConfigProperties config) { + + logger.log(Level.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". + // The docs for Spring are here: + // https://docs.spring.io/spring-boot/docs/current/reference/html/features.html#features.external-config + Stream> finders = + Stream.of( + this::findByCommandlineArgument, + this::findBySystemProperties, + this::findByEnvironmentVariable, + this::findByCurrentDirectoryApplicationProperties, + this::findByCurrentDirectoryApplicationYaml, + this::findByClasspathApplicationProperties, + this::findByClasspathApplicationYaml); + return finders + .map(Supplier::get) + .filter(Objects::nonNull) + .findFirst() + .map( + serviceName -> { + logger.log(FINE, "Auto-detected Spring Boot service name: {0}", serviceName); + return Resource.builder().put(ResourceAttributes.SERVICE_NAME, serviceName).build(); + }) + .orElseGet(Resource::empty); + } + + @Override + public boolean shouldApply(ConfigProperties config, Resource resource) { + // we're skipping this provider if the service name was manually set by the user -- no need to + // waste time trying to compute the service name if it's going to be overridden anyway + String serviceName = config.getString("otel.service.name"); + Map resourceAttributes = config.getMap("otel.resource.attributes"); + return serviceName == null + && !resourceAttributes.containsKey(ResourceAttributes.SERVICE_NAME.getKey()) + && "unknown_service:java".equals(resource.getAttribute(ResourceAttributes.SERVICE_NAME)); + } + + @Override + public int order() { + // make it run later than the default set of providers + return 100; + } + + @Nullable + private String findByEnvironmentVariable() { + String result = system.getenv("SPRING_APPLICATION_NAME"); + logger.log(Level.FINER, "Checking for SPRING_APPLICATION_NAME in env: {0}", result); + return result; + } + + @Nullable + private String findBySystemProperties() { + String result = system.getProperty("spring.application.name"); + logger.log(Level.FINER, "Checking for spring.application.name system property: {0}", result); + return result; + } + + @Nullable + private String findByClasspathApplicationProperties() { + String result = readNameFromAppProperties(); + logger.log( + Level.FINER, + "Checking for spring.application.name in application.properties file: {0}", + result); + return result; + } + + @Nullable + private String findByCurrentDirectoryApplicationProperties() { + String result = null; + try (InputStream in = system.openFile("application.properties")) { + result = getAppNamePropertyFromStream(in); + } catch (Exception e) { + // expected to fail sometimes + } + logger.log(Level.FINER, "Checking application.properties in current dir: {0}", result); + return result; + } + + @Nullable + private String findByClasspathApplicationYaml() { + String result = + loadFromClasspath("application.yml", SpringBootServiceNameDetector::parseNameFromYaml); + logger.log(Level.FINER, "Checking application.yml in classpath: {0}", result); + return result; + } + + @Nullable + private String findByCurrentDirectoryApplicationYaml() { + String result = null; + try (InputStream in = system.openFile("application.yml")) { + result = parseNameFromYaml(in); + } catch (Exception e) { + // expected to fail sometimes + } + logger.log(Level.FINER, "Checking application.yml in current dir: {0}", result); + return result; + } + + @Nullable + @SuppressWarnings("unchecked") + private static String parseNameFromYaml(InputStream in) { + Yaml yaml = new Yaml(); + try { + Map data = yaml.load(in); + Map> spring = + (Map>) data.get("spring"); + if (spring != null) { + Map app = spring.get("application"); + if (app != null) { + Object name = app.get("name"); + return (String) name; + } + } + } catch (RuntimeException e) { + // expected to fail sometimes + } + return null; + } + + @Nullable + private String findByCommandlineArgument() { + String result = attemptProcessHandleReflection(); + if (result == null) { + String javaCommand = system.getProperty("sun.java.command"); + result = parseNameFromCommandLine(javaCommand); + } + logger.log(Level.FINER, "Checking application commandline args: {0}", result); + return result; + } + + @Nullable + private String attemptProcessHandleReflection() { + try { + String[] args = system.attemptGetCommandLineArgsViaReflection(); + return parseNameFromProcessArgs(args); + } catch (Exception e) { + return null; + } + } + + @Nullable + private static String parseNameFromCommandLine(@Nullable String commandLine) { + if (commandLine == null) { + return null; + } + Matcher matcher = COMMANDLINE_PATTERN.matcher(commandLine); + if (matcher.find()) { // Required before group() + return matcher.group(1); + } + return null; + } + + @Nullable + private static String parseNameFromProcessArgs(String[] args) { + return Stream.of(args) + .filter(arg -> arg.startsWith(COMMANDLINE_ARG_PREFIX)) + .map(arg -> arg.substring(COMMANDLINE_ARG_PREFIX.length())) + .findFirst() + .orElse(null); + } + + @Nullable + private String readNameFromAppProperties() { + return loadFromClasspath( + "application.properties", SpringBootServiceNameDetector::getAppNamePropertyFromStream); + } + + @Nullable + private static String getAppNamePropertyFromStream(InputStream in) { + Properties properties = new Properties(); + try { + // Note: load() uses ISO 8859-1 encoding, same as spring uses by default for property files + properties.load(in); + return properties.getProperty("spring.application.name"); + } catch (IOException e) { + return null; + } + } + + @Nullable + private String loadFromClasspath(String filename, Function parser) { + try (InputStream in = system.openClasspathResource(filename)) { + return parser.apply(in); + } catch (Exception e) { + return null; + } + } + + // Exists for testing + static class SystemHelper { + + String getenv(String name) { + return System.getenv(name); + } + + String getProperty(String key) { + return System.getProperty(key); + } + + InputStream openClasspathResource(String filename) { + return ClassLoader.getSystemClassLoader().getResourceAsStream(filename); + } + + InputStream openFile(String filename) throws Exception { + return Files.newInputStream(Paths.get(filename)); + } + + /** + * Attempts to use ProcessHandle to get the full commandline of the current process (including + * the main method arguments). Will only succeed on java 9+. + */ + @SuppressWarnings("unchecked") + String[] attemptGetCommandLineArgsViaReflection() throws Exception { + Class clazz = Class.forName("java.lang.ProcessHandle"); + Method currentMethod = clazz.getDeclaredMethod("current"); + Method infoMethod = clazz.getDeclaredMethod("info"); + Object currentInstance = currentMethod.invoke(null); + Object info = infoMethod.invoke(currentInstance); + Class infoClass = Class.forName("java.lang.ProcessHandle$Info"); + Method argumentsMethod = infoClass.getMethod("arguments"); + Optional optionalArgs = (Optional) argumentsMethod.invoke(info); + return optionalArgs.orElse(new String[0]); + } + } +} diff --git a/instrumentation/spring/spring-boot-resources/library/src/test/java/io/opentelemetry/instrumentation/spring/resources/SpringBootServiceNameDetectorTest.java b/instrumentation/spring/spring-boot-resources/library/src/test/java/io/opentelemetry/instrumentation/spring/resources/SpringBootServiceNameDetectorTest.java new file mode 100644 index 000000000000..c7a4782cf51b --- /dev/null +++ b/instrumentation/spring/spring-boot-resources/library/src/test/java/io/opentelemetry/instrumentation/spring/resources/SpringBootServiceNameDetectorTest.java @@ -0,0 +1,159 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.spring.resources; + +import static io.opentelemetry.semconv.resource.attributes.ResourceAttributes.SERVICE_NAME; +import static java.nio.charset.StandardCharsets.UTF_8; +import static java.util.Collections.singletonMap; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.when; + +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; +import io.opentelemetry.sdk.resources.Resource; +import java.io.OutputStream; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +class SpringBootServiceNameDetectorTest { + + static final String PROPS = "application.properties"; + static final String APPLICATION_YML = "application.yml"; + @Mock ConfigProperties config; + @Mock SpringBootServiceNameDetector.SystemHelper system; + + @Test + void findByEnvVar() { + String expected = "fur-city"; + when(system.getenv("SPRING_APPLICATION_NAME")).thenReturn(expected); + + SpringBootServiceNameDetector guesser = new SpringBootServiceNameDetector(system); + + Resource result = guesser.createResource(config); + expectServiceName(result, expected); + } + + @Test + void classpathApplicationProperties() { + when(system.openClasspathResource(PROPS)).thenCallRealMethod(); + SpringBootServiceNameDetector guesser = new SpringBootServiceNameDetector(system); + Resource result = guesser.createResource(config); + expectServiceName(result, "dog-store"); + } + + @Test + void propertiesFileInCurrentDir() throws Exception { + Path propsPath = Paths.get(PROPS); + try { + writeString(propsPath, "spring.application.name=fish-tank\n"); + when(system.openFile(PROPS)).thenCallRealMethod(); + SpringBootServiceNameDetector guesser = new SpringBootServiceNameDetector(system); + Resource result = guesser.createResource(config); + expectServiceName(result, "fish-tank"); + } finally { + Files.delete(propsPath); + } + } + + @Test + void classpathApplicationYaml() { + when(system.openClasspathResource(APPLICATION_YML)).thenCallRealMethod(); + SpringBootServiceNameDetector guesser = new SpringBootServiceNameDetector(system); + Resource result = guesser.createResource(config); + expectServiceName(result, "cat-store"); + } + + @Test + void yamlFileInCurrentDir() throws Exception { + Path yamlPath = Paths.get(APPLICATION_YML); + try { + URL url = getClass().getClassLoader().getResource(APPLICATION_YML); + String content = readString(Paths.get(url.toURI())); + writeString(yamlPath, content); + when(system.openFile(APPLICATION_YML)).thenCallRealMethod(); + SpringBootServiceNameDetector guesser = new SpringBootServiceNameDetector(system); + Resource result = guesser.createResource(config); + expectServiceName(result, "cat-store"); + } finally { + Files.delete(yamlPath); + } + } + + @Test + void getFromCommandlineArgsWithProcessHandle() throws Exception { + when(system.attemptGetCommandLineArgsViaReflection()) + .thenReturn( + new String[] { + "/bin/java", + "sweet-spring.jar", + "--spring.application.name=tiger-town", + "--quiet=never" + }); + SpringBootServiceNameDetector guesser = new SpringBootServiceNameDetector(system); + Resource result = guesser.createResource(config); + expectServiceName(result, "tiger-town"); + } + + @Test + void getFromCommandlineArgsWithSystemProperty() throws Exception { + when(system.getProperty("sun.java.command")) + .thenReturn("/bin/java sweet-spring.jar --spring.application.name=bullpen --quiet=never"); + SpringBootServiceNameDetector guesser = new SpringBootServiceNameDetector(system); + Resource result = guesser.createResource(config); + expectServiceName(result, "bullpen"); + } + + @Test + void shouldApply() { + SpringBootServiceNameDetector guesser = new SpringBootServiceNameDetector(system); + assertThat(guesser.shouldApply(config, Resource.getDefault())).isTrue(); + } + + @Test + void shouldNotApplyWhenResourceHasServiceName() { + SpringBootServiceNameDetector guesser = new SpringBootServiceNameDetector(system); + Resource resource = + Resource.getDefault().merge(Resource.create(Attributes.of(SERVICE_NAME, "test-service"))); + assertThat(guesser.shouldApply(config, resource)).isFalse(); + } + + @Test + void shouldNotApplyIfConfigHasServiceName() { + SpringBootServiceNameDetector guesser = new SpringBootServiceNameDetector(system); + when(config.getString("otel.service.name")).thenReturn("test-service"); + assertThat(guesser.shouldApply(config, Resource.getDefault())).isFalse(); + } + + @Test + void shouldNotApplyIfConfigHasServiceNameResourceAttribute() { + SpringBootServiceNameDetector guesser = new SpringBootServiceNameDetector(system); + when(config.getMap("otel.resource.attributes")) + .thenReturn(singletonMap(SERVICE_NAME.getKey(), "test-service")); + assertThat(guesser.shouldApply(config, Resource.getDefault())).isFalse(); + } + + private static void expectServiceName(Resource result, String expected) { + assertThat(result.getAttribute(SERVICE_NAME)).isEqualTo(expected); + } + + private static void writeString(Path path, String value) throws Exception { + try (OutputStream out = Files.newOutputStream(path)) { + out.write(value.getBytes(UTF_8)); + } + } + + private static String readString(Path path) throws Exception { + byte[] allBytes = Files.readAllBytes(path); + return new String(allBytes, UTF_8); + } +} diff --git a/instrumentation/spring/spring-boot-resources/library/src/test/resources/application.properties b/instrumentation/spring/spring-boot-resources/library/src/test/resources/application.properties new file mode 100644 index 000000000000..1b5b5c8d01cc --- /dev/null +++ b/instrumentation/spring/spring-boot-resources/library/src/test/resources/application.properties @@ -0,0 +1,3 @@ +server.port=777 +server.context-path=/meow +spring.application.name=dog-store diff --git a/instrumentation/spring/spring-boot-resources/library/src/test/resources/application.yml b/instrumentation/spring/spring-boot-resources/library/src/test/resources/application.yml new file mode 100644 index 000000000000..3bfd3386b84e --- /dev/null +++ b/instrumentation/spring/spring-boot-resources/library/src/test/resources/application.yml @@ -0,0 +1,14 @@ +flib: + something: + 12 + +section: + two: 2 + +server: + port: 777 + context-path: /meow + +spring: + application: + name: cat-store \ No newline at end of file diff --git a/instrumentation/spring/spring-data-1.8/javaagent/build.gradle.kts b/instrumentation/spring/spring-data-1.8/javaagent/build.gradle.kts index 73e830b25b7e..097fe8d3cecb 100644 --- a/instrumentation/spring/spring-data-1.8/javaagent/build.gradle.kts +++ b/instrumentation/spring/spring-data-1.8/javaagent/build.gradle.kts @@ -20,8 +20,6 @@ muzzle { } } -val versions: Map by project - // DQH - API changes that impact instrumentation occurred in spring-data-commons in March 2014. // For now, that limits support to spring-data-commons 1.9.0 (maybe 1.8.0). // For testing, chose a couple spring-data modules that are old enough to work with 1.9.0. @@ -29,7 +27,7 @@ dependencies { library("org.springframework.data:spring-data-commons:1.8.0.RELEASE") compileOnly("org.springframework:spring-aop:1.2") - testImplementation("org.spockframework:spock-spring:${versions["org.spockframework"]}") + testImplementation("org.spockframework:spock-spring") testLibrary("org.springframework:spring-test:3.0.0.RELEASE") testLibrary("org.springframework.data:spring-data-jpa:1.8.0.RELEASE") @@ -46,4 +44,5 @@ tasks.withType().configureEach { // required on jdk17 jvmArgs("--add-opens=java.base/java.lang.invoke=ALL-UNNAMED") jvmArgs("-XX:+IgnoreUnrecognizedVMOptions") + jvmArgs("-Dotel.instrumentation.common.db-statement-sanitizer.enabled=true") } diff --git a/instrumentation/spring/spring-data-1.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/data/SpringDataInstrumentationModule.java b/instrumentation/spring/spring-data-1.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/data/SpringDataInstrumentationModule.java index a1147bf06308..4377191c9fd0 100644 --- a/instrumentation/spring/spring-data-1.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/data/SpringDataInstrumentationModule.java +++ b/instrumentation/spring/spring-data-1.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/data/SpringDataInstrumentationModule.java @@ -14,7 +14,7 @@ import com.google.auto.service.AutoService; import io.opentelemetry.context.Context; import io.opentelemetry.context.Scope; -import io.opentelemetry.instrumentation.api.util.ClassAndMethod; +import io.opentelemetry.instrumentation.api.instrumenter.util.ClassAndMethod; import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; diff --git a/instrumentation/spring/spring-data-1.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/data/SpringDataSingletons.java b/instrumentation/spring/spring-data-1.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/data/SpringDataSingletons.java index 575ca4373070..60d5ee4e7970 100644 --- a/instrumentation/spring/spring-data-1.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/data/SpringDataSingletons.java +++ b/instrumentation/spring/spring-data-1.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/data/SpringDataSingletons.java @@ -7,15 +7,27 @@ import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; -import io.opentelemetry.instrumentation.api.util.ClassAndMethod; -import io.opentelemetry.instrumentation.api.util.SpanNames; +import io.opentelemetry.instrumentation.api.instrumenter.code.CodeAttributesExtractor; +import io.opentelemetry.instrumentation.api.instrumenter.code.CodeAttributesGetter; +import io.opentelemetry.instrumentation.api.instrumenter.code.CodeSpanNameExtractor; +import io.opentelemetry.instrumentation.api.instrumenter.util.ClassAndMethod; public final class SpringDataSingletons { - private static final Instrumenter INSTRUMENTER = - Instrumenter.builder( - GlobalOpenTelemetry.get(), "io.opentelemetry.spring-data-1.8", SpanNames::fromMethod) - .newInstrumenter(); + private static final Instrumenter INSTRUMENTER; + + static { + CodeAttributesGetter codeAttributesGetter = + ClassAndMethod.codeAttributesGetter(); + + INSTRUMENTER = + Instrumenter.builder( + GlobalOpenTelemetry.get(), + "io.opentelemetry.spring-data-1.8", + CodeSpanNameExtractor.create(codeAttributesGetter)) + .addAttributesExtractor(CodeAttributesExtractor.create(codeAttributesGetter)) + .buildInstrumenter(); + } public static Instrumenter instrumenter() { return INSTRUMENTER; diff --git a/instrumentation/spring/spring-data-1.8/javaagent/src/test/groovy/SpringJpaTest.groovy b/instrumentation/spring/spring-data-1.8/javaagent/src/test/groovy/SpringJpaTest.groovy index 784e2dd309d5..c82362c85720 100644 --- a/instrumentation/spring/spring-data-1.8/javaagent/src/test/groovy/SpringJpaTest.groovy +++ b/instrumentation/spring/spring-data-1.8/javaagent/src/test/groovy/SpringJpaTest.groovy @@ -63,6 +63,8 @@ class SpringJpaTest extends AgentInstrumentationSpecification { name "JpaCustomerRepository.findAll" kind INTERNAL attributes { + "$SemanticAttributes.CODE_NAMESPACE" JpaCustomerRepository.name + "$SemanticAttributes.CODE_FUNCTION" "findAll" } } span(1) { // select @@ -95,6 +97,8 @@ class SpringJpaTest extends AgentInstrumentationSpecification { name "JpaCustomerRepository.save" kind INTERNAL attributes { + "$SemanticAttributes.CODE_NAMESPACE" JpaCustomerRepository.name + "$SemanticAttributes.CODE_FUNCTION" "save" } } def offset = 0 @@ -144,6 +148,8 @@ class SpringJpaTest extends AgentInstrumentationSpecification { name "JpaCustomerRepository.save" kind INTERNAL attributes { + "$SemanticAttributes.CODE_NAMESPACE" JpaCustomerRepository.name + "$SemanticAttributes.CODE_FUNCTION" "save" } } span(1) { // select @@ -188,6 +194,8 @@ class SpringJpaTest extends AgentInstrumentationSpecification { name "JpaCustomerRepository.findByLastName" kind INTERNAL attributes { + "$SemanticAttributes.CODE_NAMESPACE" JpaCustomerRepository.name + "$SemanticAttributes.CODE_FUNCTION" "findByLastName" } } span(1) { // select @@ -218,6 +226,8 @@ class SpringJpaTest extends AgentInstrumentationSpecification { name "JpaCustomerRepository.delete" kind INTERNAL attributes { + "$SemanticAttributes.CODE_NAMESPACE" JpaCustomerRepository.name + "$SemanticAttributes.CODE_FUNCTION" "delete" } } span(1) { // select @@ -271,6 +281,8 @@ class SpringJpaTest extends AgentInstrumentationSpecification { name "JpaCustomerRepository.findSpecialCustomers" kind INTERNAL attributes { + "$SemanticAttributes.CODE_NAMESPACE" JpaCustomerRepository.name + "$SemanticAttributes.CODE_FUNCTION" "findSpecialCustomers" } } span(1) { // select 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 ccef2d4d5889..0164503680b1 100644 --- a/instrumentation/spring/spring-integration-4.1/javaagent/build.gradle.kts +++ b/instrumentation/spring/spring-integration-4.1/javaagent/build.gradle.kts @@ -70,6 +70,14 @@ tasks { withType().configureEach { systemProperty("testLatestDeps", findProperty("testLatestDeps") as Boolean) - usesService(gradle.sharedServices.registrations["testcontainersBuildService"].getService()) + usesService(gradle.sharedServices.registrations["testcontainersBuildService"].service) + } +} + +configurations.testRuntimeClasspath { + resolutionStrategy { + // requires old logback (and therefore also old slf4j) + force("ch.qos.logback:logback-classic:1.2.11") + force("org.slf4j:slf4j-api:1.7.36") } } diff --git a/instrumentation/spring/spring-integration-4.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/integration/ApplicationContextInstrumentation.java b/instrumentation/spring/spring-integration-4.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/integration/ApplicationContextInstrumentation.java index 8b3fc3cde277..182e8f0e08f4 100644 --- a/instrumentation/spring/spring-integration-4.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/integration/ApplicationContextInstrumentation.java +++ b/instrumentation/spring/spring-integration-4.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/integration/ApplicationContextInstrumentation.java @@ -46,6 +46,7 @@ public void transform(TypeTransformer transformer) { ApplicationContextInstrumentation.class.getName() + "$PostProcessBeanFactoryAdvice"); } + @SuppressWarnings("unused") public static class PostProcessBeanFactoryAdvice { @Advice.OnMethodEnter(suppress = Throwable.class) public static void onEnter(@Advice.Argument(0) ConfigurableListableBeanFactory beanFactory) { diff --git a/instrumentation/spring/spring-integration-4.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/integration/SpringIntegrationIgnoredTypesConfigurer.java b/instrumentation/spring/spring-integration-4.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/integration/SpringIntegrationIgnoredTypesConfigurer.java index 919902104570..fe6ed89abaa6 100644 --- a/instrumentation/spring/spring-integration-4.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/integration/SpringIntegrationIgnoredTypesConfigurer.java +++ b/instrumentation/spring/spring-integration-4.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/integration/SpringIntegrationIgnoredTypesConfigurer.java @@ -13,7 +13,7 @@ @AutoService(IgnoredTypesConfigurer.class) public class SpringIntegrationIgnoredTypesConfigurer implements IgnoredTypesConfigurer { @Override - public void configure(ConfigProperties config, IgnoredTypesBuilder builder) { + public void configure(IgnoredTypesBuilder builder, ConfigProperties config) { // we don't instrument any messaging classes builder.ignoreClass("org.springframework.messaging"); } diff --git a/instrumentation/spring/spring-integration-4.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/integration/SpringIntegrationSingletons.java b/instrumentation/spring/spring-integration-4.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/integration/SpringIntegrationSingletons.java index e7f96144e9cb..99b1faef5af2 100644 --- a/instrumentation/spring/spring-integration-4.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/integration/SpringIntegrationSingletons.java +++ b/instrumentation/spring/spring-integration-4.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/integration/SpringIntegrationSingletons.java @@ -9,6 +9,7 @@ import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.instrumentation.spring.integration.SpringIntegrationTelemetry; +import io.opentelemetry.javaagent.bootstrap.internal.ExperimentalConfig; import io.opentelemetry.javaagent.bootstrap.internal.InstrumentationConfig; import java.util.List; import org.springframework.messaging.support.ChannelInterceptor; @@ -23,6 +24,7 @@ public final class SpringIntegrationSingletons { private static final ChannelInterceptor INTERCEPTOR = SpringIntegrationTelemetry.builder(GlobalOpenTelemetry.get()) + .setCapturedHeaders(ExperimentalConfig.get().getMessagingHeaders()) .setProducerSpanEnabled( InstrumentationConfig.get() .getBoolean("otel.instrumentation.spring-integration.producer.enabled", false)) diff --git a/instrumentation/spring/spring-integration-4.1/javaagent/src/test/groovy/SpringIntegrationAndRabbitTest.groovy b/instrumentation/spring/spring-integration-4.1/javaagent/src/test/groovy/SpringIntegrationAndRabbitTest.groovy index b182119e6c36..e77394b07fa3 100644 --- a/instrumentation/spring/spring-integration-4.1/javaagent/src/test/groovy/SpringIntegrationAndRabbitTest.groovy +++ b/instrumentation/spring/spring-integration-4.1/javaagent/src/test/groovy/SpringIntegrationAndRabbitTest.groovy @@ -6,7 +6,6 @@ import io.opentelemetry.instrumentation.test.AgentInstrumentationSpecification import io.opentelemetry.semconv.trace.attributes.SemanticAttributes -import static com.google.common.net.InetAddresses.isInetAddress import static io.opentelemetry.api.trace.SpanKind.CLIENT import static io.opentelemetry.api.trace.SpanKind.CONSUMER import static io.opentelemetry.api.trace.SpanKind.PRODUCER @@ -47,9 +46,9 @@ class SpringIntegrationAndRabbitTest extends AgentInstrumentationSpecification i childOf span(1) kind CLIENT attributes { - "$SemanticAttributes.NET_PEER_NAME" { it == null || it == "localhost" } - "$SemanticAttributes.NET_PEER_IP" { isInetAddress(it as String) } - "$SemanticAttributes.NET_PEER_PORT" { it == null || it instanceof Long } + "$SemanticAttributes.NET_SOCK_PEER_ADDR" { it == "127.0.0.1" || it == "0:0:0:0:0:0:0:1" || it == null } + "$SemanticAttributes.NET_SOCK_PEER_PORT" Long + "$SemanticAttributes.NET_SOCK_FAMILY" { it == SemanticAttributes.NetSockFamilyValues.INET6 || it == null } "$SemanticAttributes.MESSAGING_SYSTEM" "rabbitmq" "$SemanticAttributes.MESSAGING_DESTINATION_KIND" "queue" } @@ -60,15 +59,15 @@ class SpringIntegrationAndRabbitTest extends AgentInstrumentationSpecification i childOf span(1) kind PRODUCER attributes { - // "localhost" on linux, null on windows - "$SemanticAttributes.NET_PEER_NAME" { it == "localhost" || it == null } - "$SemanticAttributes.NET_PEER_IP" { isInetAddress(it as String) } - "$SemanticAttributes.NET_PEER_PORT" Long + "$SemanticAttributes.NET_SOCK_PEER_ADDR" { it == "127.0.0.1" || it == "0:0:0:0:0:0:0:1" || it == null } + "$SemanticAttributes.NET_SOCK_PEER_PORT" Long + "$SemanticAttributes.NET_SOCK_FAMILY" { it == SemanticAttributes.NetSockFamilyValues.INET6 || it == null } "$SemanticAttributes.MESSAGING_SYSTEM" "rabbitmq" "$SemanticAttributes.MESSAGING_DESTINATION" "testTopic" "$SemanticAttributes.MESSAGING_DESTINATION_KIND" "queue" "$SemanticAttributes.MESSAGING_MESSAGE_PAYLOAD_SIZE_BYTES" Long "$SemanticAttributes.MESSAGING_RABBITMQ_ROUTING_KEY" String + "messaging.payload" String } } // spring-cloud-stream-binder-rabbit listener puts all messages into a BlockingQueue immediately after receiving @@ -85,6 +84,7 @@ class SpringIntegrationAndRabbitTest extends AgentInstrumentationSpecification i "$SemanticAttributes.MESSAGING_OPERATION" "process" "$SemanticAttributes.MESSAGING_MESSAGE_PAYLOAD_SIZE_BYTES" Long "$SemanticAttributes.MESSAGING_RABBITMQ_ROUTING_KEY" String + "messaging.payload" String } } // spring-integration will detect that spring-rabbit has already created a consumer span and back off @@ -100,6 +100,7 @@ class SpringIntegrationAndRabbitTest extends AgentInstrumentationSpecification i "$SemanticAttributes.MESSAGING_OPERATION" "process" "$SemanticAttributes.MESSAGING_MESSAGE_ID" String "$SemanticAttributes.MESSAGING_MESSAGE_PAYLOAD_SIZE_BYTES" Long + "messaging.payload" String } } span(6) { @@ -115,10 +116,9 @@ class SpringIntegrationAndRabbitTest extends AgentInstrumentationSpecification i name "basic.ack" kind CLIENT attributes { - // "localhost" on linux, null on windows - "$SemanticAttributes.NET_PEER_NAME" { it == "localhost" || it == null } - "$SemanticAttributes.NET_PEER_IP" { isInetAddress(it as String) } - "$SemanticAttributes.NET_PEER_PORT" Long + "$SemanticAttributes.NET_SOCK_PEER_ADDR" { it == "127.0.0.1" || it == "0:0:0:0:0:0:0:1" || it == null } + "$SemanticAttributes.NET_SOCK_PEER_PORT" Long + "$SemanticAttributes.NET_SOCK_FAMILY" { it == SemanticAttributes.NetSockFamilyValues.INET6 || it == null } "$SemanticAttributes.MESSAGING_SYSTEM" "rabbitmq" "$SemanticAttributes.MESSAGING_DESTINATION_KIND" "queue" } diff --git a/instrumentation/spring/spring-integration-4.1/library/build.gradle.kts b/instrumentation/spring/spring-integration-4.1/library/build.gradle.kts index 1e07bf01d62f..0631b4be1cfe 100644 --- a/instrumentation/spring/spring-integration-4.1/library/build.gradle.kts +++ b/instrumentation/spring/spring-integration-4.1/library/build.gradle.kts @@ -22,3 +22,11 @@ tasks { usesService(gradle.sharedServices.registrations["testcontainersBuildService"].service) } } + +configurations.testRuntimeClasspath { + resolutionStrategy { + // requires old logback (and therefore also old slf4j) + force("ch.qos.logback:logback-classic:1.2.11") + force("org.slf4j:slf4j-api:1.7.36") + } +} diff --git a/instrumentation/spring/spring-integration-4.1/library/src/main/java/io/opentelemetry/instrumentation/spring/integration/SpringIntegrationTelemetryBuilder.java b/instrumentation/spring/spring-integration-4.1/library/src/main/java/io/opentelemetry/instrumentation/spring/integration/SpringIntegrationTelemetryBuilder.java index b1887a452a19..adb64992057c 100644 --- a/instrumentation/spring/spring-integration-4.1/library/src/main/java/io/opentelemetry/instrumentation/spring/integration/SpringIntegrationTelemetryBuilder.java +++ b/instrumentation/spring/spring-integration-4.1/library/src/main/java/io/opentelemetry/instrumentation/spring/integration/SpringIntegrationTelemetryBuilder.java @@ -5,6 +5,9 @@ package io.opentelemetry.instrumentation.spring.integration; +import static java.util.Collections.emptyList; + +import com.google.errorprone.annotations.CanIgnoreReturnValue; import io.opentelemetry.api.OpenTelemetry; import io.opentelemetry.api.trace.SpanKind; import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor; @@ -12,6 +15,7 @@ import io.opentelemetry.instrumentation.api.instrumenter.SpanKindExtractor; import io.opentelemetry.instrumentation.api.instrumenter.messaging.MessageOperation; import io.opentelemetry.instrumentation.api.instrumenter.messaging.MessagingAttributesExtractor; +import io.opentelemetry.instrumentation.api.instrumenter.messaging.MessagingAttributesGetter; import java.util.ArrayList; import java.util.List; @@ -23,6 +27,7 @@ public final class SpringIntegrationTelemetryBuilder { private final List> additionalAttributeExtractors = new ArrayList<>(); + private List capturedHeaders = emptyList(); private boolean producerSpanEnabled = false; SpringIntegrationTelemetryBuilder(OpenTelemetry openTelemetry) { @@ -33,16 +38,29 @@ public final class SpringIntegrationTelemetryBuilder { * Adds an additional {@link AttributesExtractor} to invoke to set attributes to instrumented * items. */ + @CanIgnoreReturnValue public SpringIntegrationTelemetryBuilder addAttributesExtractor( AttributesExtractor attributesExtractor) { additionalAttributeExtractors.add(attributesExtractor); return this; } + /** + * Configures the messaging headers that will be captured as span attributes. + * + * @param capturedHeaders A list of messaging header names. + */ + @CanIgnoreReturnValue + public SpringIntegrationTelemetryBuilder setCapturedHeaders(List capturedHeaders) { + this.capturedHeaders = capturedHeaders; + return this; + } + /** * Sets whether additional {@link SpanKind#PRODUCER PRODUCER} span should be emitted by this * instrumentation. */ + @CanIgnoreReturnValue public SpringIntegrationTelemetryBuilder setProducerSpanEnabled(boolean producerSpanEnabled) { this.producerSpanEnabled = producerSpanEnabled; return this; @@ -68,9 +86,11 @@ public SpringIntegrationTelemetry build() { SpringIntegrationTelemetryBuilder::consumerSpanName) .addAttributesExtractors(additionalAttributeExtractors) .addAttributesExtractor( - MessagingAttributesExtractor.create( - SpringMessagingAttributesGetter.INSTANCE, MessageOperation.PROCESS)) - .newConsumerInstrumenter(MessageHeadersGetter.INSTANCE); + buildMessagingAttributesExtractor( + SpringMessagingAttributesGetter.INSTANCE, + MessageOperation.PROCESS, + capturedHeaders)) + .buildConsumerInstrumenter(MessageHeadersGetter.INSTANCE); Instrumenter producerInstrumenter = Instrumenter.builder( @@ -79,13 +99,25 @@ public SpringIntegrationTelemetry build() { SpringIntegrationTelemetryBuilder::producerSpanName) .addAttributesExtractors(additionalAttributeExtractors) .addAttributesExtractor( - MessagingAttributesExtractor.create( - SpringMessagingAttributesGetter.INSTANCE, MessageOperation.SEND)) - .newInstrumenter(SpanKindExtractor.alwaysProducer()); + buildMessagingAttributesExtractor( + SpringMessagingAttributesGetter.INSTANCE, + MessageOperation.SEND, + capturedHeaders)) + .buildInstrumenter(SpanKindExtractor.alwaysProducer()); return new SpringIntegrationTelemetry( openTelemetry.getPropagators(), consumerInstrumenter, producerInstrumenter, producerSpanEnabled); } + + private static MessagingAttributesExtractor + buildMessagingAttributesExtractor( + MessagingAttributesGetter getter, + MessageOperation operation, + List capturedHeaders) { + return MessagingAttributesExtractor.builder(getter, operation) + .setCapturedHeaders(capturedHeaders) + .build(); + } } diff --git a/instrumentation/spring/spring-integration-4.1/library/src/main/java/io/opentelemetry/instrumentation/spring/integration/SpringMessagingAttributesGetter.java b/instrumentation/spring/spring-integration-4.1/library/src/main/java/io/opentelemetry/instrumentation/spring/integration/SpringMessagingAttributesGetter.java index d6565c0ae7d0..85f545f509c9 100644 --- a/instrumentation/spring/spring-integration-4.1/library/src/main/java/io/opentelemetry/instrumentation/spring/integration/SpringMessagingAttributesGetter.java +++ b/instrumentation/spring/spring-integration-4.1/library/src/main/java/io/opentelemetry/instrumentation/spring/integration/SpringMessagingAttributesGetter.java @@ -6,6 +6,8 @@ package io.opentelemetry.instrumentation.spring.integration; import io.opentelemetry.instrumentation.api.instrumenter.messaging.MessagingAttributesGetter; +import java.util.Collections; +import java.util.List; import javax.annotation.Nullable; // this class is needed mostly for correct CONSUMER span suppression @@ -77,4 +79,19 @@ public Long messagePayloadCompressedSize(MessageWithChannel messageWithChannel) public String messageId(MessageWithChannel messageWithChannel, @Nullable Void unused) { return null; } + + @Override + public List header(MessageWithChannel request, String name) { + Object value = request.getMessage().getHeaders().get(name); + if (value != null) { + return Collections.singletonList(value.toString()); + } + return Collections.emptyList(); + } + + @Nullable + @Override + public String messagePayload(MessageWithChannel messageWithChannel) { + return null; + } } diff --git a/instrumentation/spring/spring-integration-4.1/library/src/test/groovy/GlobalInterceptorSpringConfig.groovy b/instrumentation/spring/spring-integration-4.1/library/src/test/groovy/GlobalInterceptorSpringConfig.groovy index 57e1e8dc9cbb..c12f390e1be0 100644 --- a/instrumentation/spring/spring-integration-4.1/library/src/test/groovy/GlobalInterceptorSpringConfig.groovy +++ b/instrumentation/spring/spring-integration-4.1/library/src/test/groovy/GlobalInterceptorSpringConfig.groovy @@ -10,12 +10,17 @@ import org.springframework.context.annotation.Configuration import org.springframework.integration.config.GlobalChannelInterceptor import org.springframework.messaging.support.ChannelInterceptor +import static java.util.Collections.singletonList + @Configuration class GlobalInterceptorSpringConfig { @GlobalChannelInterceptor @Bean ChannelInterceptor otelInterceptor() { - SpringIntegrationTelemetry.create(GlobalOpenTelemetry.get()).newChannelInterceptor() + SpringIntegrationTelemetry.builder(GlobalOpenTelemetry.get()) + .setCapturedHeaders(singletonList("test-message-header")) + .build() + .newChannelInterceptor() } } diff --git a/instrumentation/spring/spring-integration-4.1/testing/src/main/groovy/AbstractComplexPropagationTest.groovy b/instrumentation/spring/spring-integration-4.1/testing/src/main/groovy/AbstractComplexPropagationTest.groovy index 06f087af7518..0d1ce31d9a9f 100644 --- a/instrumentation/spring/spring-integration-4.1/testing/src/main/groovy/AbstractComplexPropagationTest.groovy +++ b/instrumentation/spring/spring-integration-4.1/testing/src/main/groovy/AbstractComplexPropagationTest.groovy @@ -110,7 +110,7 @@ abstract class AbstractComplexPropagationTest extends InstrumentationSpecificati new LinkedBlockingQueue() } - @Bean + @Bean(destroyMethod = "shutdownNow") ExecutorService consumerThread() { Executors.newSingleThreadExecutor() } diff --git a/instrumentation/spring/spring-integration-4.1/testing/src/main/groovy/AbstractSpringIntegrationTracingTest.groovy b/instrumentation/spring/spring-integration-4.1/testing/src/main/groovy/AbstractSpringIntegrationTracingTest.groovy index b24a972b7066..92f26657e2ae 100644 --- a/instrumentation/spring/spring-integration-4.1/testing/src/main/groovy/AbstractSpringIntegrationTracingTest.groovy +++ b/instrumentation/spring/spring-integration-4.1/testing/src/main/groovy/AbstractSpringIntegrationTracingTest.groovy @@ -191,6 +191,41 @@ abstract class AbstractSpringIntegrationTracingTest extends InstrumentationSpeci channel2.unsubscribe(messageHandler) } + def "capture message header"() { + given: + def channel = applicationContext.getBean("directChannel", SubscribableChannel) + + def messageHandler = new CapturingMessageHandler() + channel.subscribe(messageHandler) + + when: + channel.send(MessageBuilder.withPayload("test") + .setHeader("test-message-header", "test") + .build()) + + then: + def capturedMessage = messageHandler.join() + + assertTraces(1) { + trace(0, 2) { + span(0) { + name "application.directChannel process" + kind CONSUMER + } + span(1) { + name "handler" + childOf span(0) + } + + def interceptorSpan = span(0) + verifyCorrectSpanWasPropagated(capturedMessage, interceptorSpan) + } + } + + cleanup: + channel.unsubscribe(messageHandler) + } + static void verifyCorrectSpanWasPropagated(Message capturedMessage, SpanData parentSpan) { def propagatedSpan = capturedMessage.headers.get("traceparent") as String assert propagatedSpan.contains(parentSpan.traceId), "wrong trace id" diff --git a/instrumentation/spring/spring-jms-2.0/javaagent/build.gradle.kts b/instrumentation/spring/spring-jms-2.0/javaagent/build.gradle.kts new file mode 100644 index 000000000000..e707734a0ab5 --- /dev/null +++ b/instrumentation/spring/spring-jms-2.0/javaagent/build.gradle.kts @@ -0,0 +1,56 @@ +plugins { + id("otel.javaagent-instrumentation") + id("org.unbroken-dome.test-sets") +} + +muzzle { + pass { + group.set("org.springframework") + module.set("spring-jms") + versions.set("[2.0,)") + extraDependency("javax.jms:jms-api:1.1-rev-1") + assertInverse.set(true) + } +} + +testSets { + create("testReceiveSpansDisabled") +} + +tasks { + test { + usesService(gradle.sharedServices.registrations["testcontainersBuildService"].service) + jvmArgs("-Dotel.instrumentation.messaging.experimental.receive-telemetry.enabled=true") + } + + val testReceiveSpansDisabled by existing + + check { + dependsOn(testReceiveSpansDisabled) + } +} + +dependencies { + implementation(project(":instrumentation:jms-1.1:javaagent")) + library("org.springframework:spring-jms:2.0") + compileOnly("javax.jms:jms-api:1.1-rev-1") + + compileOnly("com.google.auto.value:auto-value-annotations") + annotationProcessor("com.google.auto.value:auto-value") + + testInstrumentation(project(":instrumentation:jms-1.1:javaagent")) + + testImplementation("org.springframework.boot:spring-boot-starter-activemq:2.5.3") + testImplementation("org.springframework.boot:spring-boot-starter-test:2.5.3") { + exclude("org.junit.vintage", "junit-vintage-engine") + } + + testImplementation("org.hornetq:hornetq-jms-client:2.4.7.Final") + testImplementation("org.hornetq:hornetq-jms-server:2.4.7.Final") { + // this doesn't exist in maven central, and doesn't seem to be needed anyways + exclude("org.jboss.naming", "jnpserver") + } + + // this is just to avoid a bit more copy-pasting + add("testReceiveSpansDisabledImplementation", sourceSets["test"].output) +} diff --git a/instrumentation/spring/spring-jms-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/jms/SpringJmsInstrumentationModule.java b/instrumentation/spring/spring-jms-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/jms/SpringJmsInstrumentationModule.java new file mode 100644 index 000000000000..78d2b47b7d2f --- /dev/null +++ b/instrumentation/spring/spring-jms-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/jms/SpringJmsInstrumentationModule.java @@ -0,0 +1,25 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.spring.jms; + +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 java.util.List; + +@AutoService(InstrumentationModule.class) +public class SpringJmsInstrumentationModule extends InstrumentationModule { + public SpringJmsInstrumentationModule() { + super("spring-jms", "spring-jms-2.0"); + } + + @Override + public List typeInstrumentations() { + return singletonList(new SpringJmsMessageListenerInstrumentation()); + } +} diff --git a/instrumentation/spring/spring-jms-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/jms/SpringJmsMessageListenerInstrumentation.java b/instrumentation/spring/spring-jms-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/jms/SpringJmsMessageListenerInstrumentation.java new file mode 100644 index 000000000000..0c129d675526 --- /dev/null +++ b/instrumentation/spring/spring-jms-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/jms/SpringJmsMessageListenerInstrumentation.java @@ -0,0 +1,84 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.spring.jms; + +import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed; +import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.implementsInterface; +import static io.opentelemetry.javaagent.instrumentation.spring.jms.SpringJmsSingletons.listenerInstrumenter; +import static net.bytebuddy.matcher.ElementMatchers.isPublic; +import static net.bytebuddy.matcher.ElementMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.takesArgument; +import static net.bytebuddy.matcher.ElementMatchers.takesArguments; + +import io.opentelemetry.context.Context; +import io.opentelemetry.context.Scope; +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.jms.MessageWithDestination; +import javax.jms.Message; +import net.bytebuddy.asm.Advice; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; + +public class SpringJmsMessageListenerInstrumentation implements TypeInstrumentation { + + @Override + public ElementMatcher classLoaderOptimization() { + return hasClassesNamed("org.springframework.jms.listener.SessionAwareMessageListener"); + } + + @Override + public ElementMatcher typeMatcher() { + return implementsInterface( + named("org.springframework.jms.listener.SessionAwareMessageListener")); + } + + @Override + public void transform(TypeTransformer transformer) { + transformer.applyAdviceToMethod( + named("onMessage") + .and(isPublic()) + .and(takesArguments(2)) + .and(takesArgument(0, named("javax.jms.Message"))), + SpringJmsMessageListenerInstrumentation.class.getName() + "$MessageListenerAdvice"); + } + + @SuppressWarnings("unused") + public static class MessageListenerAdvice { + + @Advice.OnMethodEnter(suppress = Throwable.class) + public static void onEnter( + @Advice.Argument(0) Message message, + @Advice.Local("otelRequest") MessageWithDestination request, + @Advice.Local("otelContext") Context context, + @Advice.Local("otelScope") Scope scope) { + + Context parentContext = Java8BytecodeBridge.currentContext(); + request = MessageWithDestination.create(message, null); + + if (!listenerInstrumenter().shouldStart(parentContext, request)) { + return; + } + + context = listenerInstrumenter().start(parentContext, request); + scope = context.makeCurrent(); + } + + @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) + public static void stopSpan( + @Advice.Local("otelRequest") MessageWithDestination request, + @Advice.Local("otelContext") Context context, + @Advice.Local("otelScope") Scope scope, + @Advice.Thrown Throwable throwable) { + if (scope == null) { + return; + } + scope.close(); + listenerInstrumenter().end(context, request, null, throwable); + } + } +} diff --git a/instrumentation/spring/spring-jms-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/jms/SpringJmsSingletons.java b/instrumentation/spring/spring-jms-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/jms/SpringJmsSingletons.java new file mode 100644 index 000000000000..cffb4270cc35 --- /dev/null +++ b/instrumentation/spring/spring-jms-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/jms/SpringJmsSingletons.java @@ -0,0 +1,44 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.spring.jms; + +import io.opentelemetry.api.GlobalOpenTelemetry; +import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; +import io.opentelemetry.instrumentation.api.instrumenter.messaging.MessageOperation; +import io.opentelemetry.instrumentation.api.instrumenter.messaging.MessagingAttributesExtractor; +import io.opentelemetry.instrumentation.api.instrumenter.messaging.MessagingSpanNameExtractor; +import io.opentelemetry.javaagent.bootstrap.internal.ExperimentalConfig; +import io.opentelemetry.javaagent.instrumentation.jms.JmsMessageAttributesGetter; +import io.opentelemetry.javaagent.instrumentation.jms.MessagePropertyGetter; +import io.opentelemetry.javaagent.instrumentation.jms.MessageWithDestination; + +public final class SpringJmsSingletons { + private static final String INSTRUMENTATION_NAME = "io.opentelemetry.spring-jms-2.0"; + + private static final Instrumenter LISTENER_INSTRUMENTER = + buildListenerInstrumenter(); + + private static Instrumenter buildListenerInstrumenter() { + JmsMessageAttributesGetter getter = JmsMessageAttributesGetter.INSTANCE; + MessageOperation operation = MessageOperation.PROCESS; + + return Instrumenter.builder( + GlobalOpenTelemetry.get(), + INSTRUMENTATION_NAME, + MessagingSpanNameExtractor.create(getter, operation)) + .addAttributesExtractor( + MessagingAttributesExtractor.builder(getter, operation) + .setCapturedHeaders(ExperimentalConfig.get().getMessagingHeaders()) + .build()) + .buildConsumerInstrumenter(MessagePropertyGetter.INSTANCE); + } + + public static Instrumenter listenerInstrumenter() { + return LISTENER_INSTRUMENTER; + } + + private SpringJmsSingletons() {} +} diff --git a/instrumentation/spring/spring-jms-2.0/javaagent/src/test/groovy/SpringListenerTest.groovy b/instrumentation/spring/spring-jms-2.0/javaagent/src/test/groovy/SpringListenerTest.groovy new file mode 100644 index 000000000000..a700455b810c --- /dev/null +++ b/instrumentation/spring/spring-jms-2.0/javaagent/src/test/groovy/SpringListenerTest.groovy @@ -0,0 +1,101 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +import io.opentelemetry.instrumentation.test.AgentInstrumentationSpecification +import io.opentelemetry.instrumentation.test.asserts.TraceAssert +import io.opentelemetry.sdk.trace.data.SpanData +import io.opentelemetry.semconv.trace.attributes.SemanticAttributes +import listener.AnnotatedListenerConfig +import listener.ManualListenerConfig +import org.springframework.context.annotation.AnnotationConfigApplicationContext +import org.springframework.jms.core.JmsTemplate + +import javax.jms.ConnectionFactory + +import static io.opentelemetry.api.trace.SpanKind.CONSUMER +import static io.opentelemetry.api.trace.SpanKind.PRODUCER + +class SpringListenerTest extends AgentInstrumentationSpecification { + def "receiving message in spring listener generates spans"() { + setup: + def context = new AnnotationConfigApplicationContext(config) + def factory = context.getBean(ConnectionFactory) + def template = new JmsTemplate(factory) + + template.convertAndSend("SpringListenerJms2", "a message") + + expect: + assertTraces(2) { + traces.sort(orderByRootSpanKind(CONSUMER, PRODUCER)) + + trace(0, 1) { + consumerSpan(it, 0, "queue", "SpringListenerJms2", "", null, "receive") + } + trace(1, 2) { + producerSpan(it, 0, "queue", "SpringListenerJms2") + consumerSpan(it, 1, "queue", "SpringListenerJms2", "", span(0), "process") + } + } + + cleanup: + context.close() + + where: + config << [AnnotatedListenerConfig, ManualListenerConfig] + } + + static producerSpan(TraceAssert trace, int index, String destinationType, String destinationName, boolean testHeaders = false) { + trace.span(index) { + name destinationName + " send" + kind PRODUCER + hasNoParent() + attributes { + "$SemanticAttributes.MESSAGING_SYSTEM" "jms" + "$SemanticAttributes.MESSAGING_DESTINATION" destinationName + "$SemanticAttributes.MESSAGING_DESTINATION_KIND" destinationType + if (destinationName == "(temporary)") { + "$SemanticAttributes.MESSAGING_TEMP_DESTINATION" true + } + "$SemanticAttributes.MESSAGING_MESSAGE_ID" String + if (testHeaders) { + "messaging.header.test_message_header" { it == ["test"] } + "messaging.header.test_message_int_header" { it == ["1234"] } + } + } + } + } + + // passing messageId = null will verify message.id is not captured, + // passing messageId = "" will verify message.id is captured (but won't verify anything about the value), + // any other value for messageId will verify that message.id is captured and has that same value + static consumerSpan(TraceAssert trace, int index, String destinationType, String destinationName, String messageId, Object parentOrLinkedSpan, String operation, boolean testHeaders = false) { + trace.span(index) { + name destinationName + " " + operation + kind CONSUMER + if (parentOrLinkedSpan != null) { + childOf((SpanData) parentOrLinkedSpan) + } else { + hasNoParent() + } + attributes { + "$SemanticAttributes.MESSAGING_SYSTEM" "jms" + "$SemanticAttributes.MESSAGING_DESTINATION" destinationName + "$SemanticAttributes.MESSAGING_DESTINATION_KIND" destinationType + "$SemanticAttributes.MESSAGING_OPERATION" operation + if (messageId != null) { + //In some tests we don't know exact messageId, so we pass "" and verify just the existence of the attribute + "$SemanticAttributes.MESSAGING_MESSAGE_ID" { it == messageId || messageId == "" } + } + if (destinationName == "(temporary)") { + "$SemanticAttributes.MESSAGING_TEMP_DESTINATION" true + } + if (testHeaders) { + "messaging.header.test_message_header" { it == ["test"] } + "messaging.header.test_message_int_header" { it == ["1234"] } + } + } + } + } +} diff --git a/instrumentation/jms-1.1/javaagent/src/jms2Test/groovy/SpringTemplateJms2Test.groovy b/instrumentation/spring/spring-jms-2.0/javaagent/src/test/groovy/SpringTemplateTest.groovy similarity index 79% rename from instrumentation/jms-1.1/javaagent/src/jms2Test/groovy/SpringTemplateJms2Test.groovy rename to instrumentation/spring/spring-jms-2.0/javaagent/src/test/groovy/SpringTemplateTest.groovy index c0aafe73c5d0..0233c552eb25 100644 --- a/instrumentation/jms-1.1/javaagent/src/jms2Test/groovy/SpringTemplateJms2Test.groovy +++ b/instrumentation/spring/spring-jms-2.0/javaagent/src/test/groovy/SpringTemplateTest.groovy @@ -5,7 +5,8 @@ import com.google.common.io.Files import io.opentelemetry.instrumentation.test.AgentInstrumentationSpecification -import javax.jms.Connection +import javax.jms.JMSException +import javax.jms.Message import org.hornetq.api.core.TransportConfiguration import org.hornetq.api.core.client.HornetQClient import org.hornetq.api.jms.HornetQJMSClient @@ -18,17 +19,19 @@ import org.hornetq.core.remoting.impl.invm.InVMConnectorFactory import org.hornetq.core.server.HornetQServer import org.hornetq.core.server.HornetQServers import org.springframework.jms.core.JmsTemplate +import org.springframework.jms.core.MessagePostProcessor import spock.lang.Shared +import javax.jms.Connection import javax.jms.Session import javax.jms.TextMessage import java.util.concurrent.TimeUnit import java.util.concurrent.atomic.AtomicReference -import static Jms2Test.consumerSpan -import static Jms2Test.producerSpan +import static SpringListenerTest.consumerSpan +import static SpringListenerTest.producerSpan -class SpringTemplateJms2Test extends AgentInstrumentationSpecification { +class SpringTemplateTest extends AgentInstrumentationSpecification { @Shared HornetQServer server @Shared @@ -148,4 +151,32 @@ class SpringTemplateJms2Test extends AgentInstrumentationSpecification { destination | destinationType | destinationName session.createQueue("SpringTemplateJms2") | "queue" | "SpringTemplateJms2" } + + def "capture message header as span attribute"() { + setup: + template.convertAndSend(destination, messageText, new MessagePostProcessor() { + @Override + Message postProcessMessage(Message message) throws JMSException { + message.setStringProperty("test_message_header", "test") + message.setIntProperty("test_message_int_header", 1234) + return message + } + }) + TextMessage receivedMessage = template.receive(destination) + + expect: + receivedMessage.text == messageText + assertTraces(2) { + trace(0, 1) { + producerSpan(it, 0, destinationType, destinationName, true) + } + trace(1, 1) { + consumerSpan(it, 0, destinationType, destinationName, receivedMessage.getJMSMessageID(), null, "receive", true) + } + } + + where: + destination | destinationType | destinationName + session.createQueue("SpringTemplateJms2") | "queue" | "SpringTemplateJms2" + } } diff --git a/instrumentation/jms-1.1/javaagent/src/jms2Test/groovy/listener/Config.groovy b/instrumentation/spring/spring-jms-2.0/javaagent/src/test/groovy/listener/AbstractConfig.groovy similarity index 88% rename from instrumentation/jms-1.1/javaagent/src/jms2Test/groovy/listener/Config.groovy rename to instrumentation/spring/spring-jms-2.0/javaagent/src/test/groovy/listener/AbstractConfig.groovy index 2d82ad65adf3..2e7a99a85e09 100644 --- a/instrumentation/jms-1.1/javaagent/src/jms2Test/groovy/listener/Config.groovy +++ b/instrumentation/spring/spring-jms-2.0/javaagent/src/test/groovy/listener/AbstractConfig.groovy @@ -17,19 +17,13 @@ import org.hornetq.core.remoting.impl.invm.InVMConnectorFactory import org.hornetq.core.server.HornetQServer import org.hornetq.core.server.HornetQServers import org.springframework.context.annotation.Bean -import org.springframework.context.annotation.ComponentScan -import org.springframework.context.annotation.Configuration -import org.springframework.jms.annotation.EnableJms import org.springframework.jms.config.DefaultJmsListenerContainerFactory import org.springframework.jms.config.JmsListenerContainerFactory import javax.annotation.PreDestroy import javax.jms.ConnectionFactory -@Configuration -@ComponentScan -@EnableJms -class Config { +class AbstractConfig { private HornetQServer server @@ -64,7 +58,7 @@ class Config { } @Bean - JmsListenerContainerFactory containerFactory(ConnectionFactory connectionFactory) { + JmsListenerContainerFactory jmsListenerContainerFactory(ConnectionFactory connectionFactory) { DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory() factory.setConnectionFactory(connectionFactory) return factory diff --git a/instrumentation/spring/spring-jms-2.0/javaagent/src/test/groovy/listener/AnnotatedListenerConfig.groovy b/instrumentation/spring/spring-jms-2.0/javaagent/src/test/groovy/listener/AnnotatedListenerConfig.groovy new file mode 100644 index 000000000000..af0cbe676d34 --- /dev/null +++ b/instrumentation/spring/spring-jms-2.0/javaagent/src/test/groovy/listener/AnnotatedListenerConfig.groovy @@ -0,0 +1,14 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package listener + +import org.springframework.context.annotation.ComponentScan +import org.springframework.jms.annotation.EnableJms + +@ComponentScan +@EnableJms +class AnnotatedListenerConfig extends AbstractConfig { +} diff --git a/instrumentation/spring/spring-jms-2.0/javaagent/src/test/groovy/listener/ManualListenerConfig.groovy b/instrumentation/spring/spring-jms-2.0/javaagent/src/test/groovy/listener/ManualListenerConfig.groovy new file mode 100644 index 000000000000..e2d7f6b2461f --- /dev/null +++ b/instrumentation/spring/spring-jms-2.0/javaagent/src/test/groovy/listener/ManualListenerConfig.groovy @@ -0,0 +1,45 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package listener + + +import org.springframework.jms.annotation.EnableJms +import org.springframework.jms.annotation.JmsListenerConfigurer +import org.springframework.jms.config.JmsListenerEndpoint +import org.springframework.jms.config.JmsListenerEndpointRegistrar +import org.springframework.jms.listener.AbstractMessageListenerContainer +import org.springframework.jms.listener.MessageListenerContainer +import org.springframework.jms.listener.SessionAwareMessageListener + +import javax.jms.JMSException +import javax.jms.Message +import javax.jms.Session + +@EnableJms +class ManualListenerConfig extends AbstractConfig implements JmsListenerConfigurer { + + @Override + void configureJmsListeners(JmsListenerEndpointRegistrar registrar) { + registrar.registerEndpoint(new JmsListenerEndpoint() { + @Override + String getId() { + return "testid" + } + + @Override + void setupListenerContainer(MessageListenerContainer listenerContainer) { + var container = (AbstractMessageListenerContainer) listenerContainer + container.setDestinationName("SpringListenerJms2") + container.setupMessageListener(new SessionAwareMessageListener() { + @Override + void onMessage(Message message, Session session) throws JMSException { + println "received: " + message + } + }) + } + }) + } +} diff --git a/instrumentation/jms-1.1/javaagent/src/test/groovy/listener/TestListener.groovy b/instrumentation/spring/spring-jms-2.0/javaagent/src/test/groovy/listener/TestListener.groovy similarity index 78% rename from instrumentation/jms-1.1/javaagent/src/test/groovy/listener/TestListener.groovy rename to instrumentation/spring/spring-jms-2.0/javaagent/src/test/groovy/listener/TestListener.groovy index 2d7674883030..7b97d2b6f710 100644 --- a/instrumentation/jms-1.1/javaagent/src/test/groovy/listener/TestListener.groovy +++ b/instrumentation/spring/spring-jms-2.0/javaagent/src/test/groovy/listener/TestListener.groovy @@ -11,7 +11,7 @@ import org.springframework.stereotype.Component @Component class TestListener { - @JmsListener(destination = "SpringListenerJms1", containerFactory = "containerFactory") + @JmsListener(destination = "SpringListenerJms2") void receiveMessage(String message) { println "received: " + message } diff --git a/instrumentation/jms-1.1/javaagent/src/jms2TestReceiveSpansDisabled/groovy/SpringListenerJms2SuppressReceiveSpansTest.groovy b/instrumentation/spring/spring-jms-2.0/javaagent/src/testReceiveSpansDisabled/groovy/SpringListenerSuppressReceiveSpansTest.groovy similarity index 63% rename from instrumentation/jms-1.1/javaagent/src/jms2TestReceiveSpansDisabled/groovy/SpringListenerJms2SuppressReceiveSpansTest.groovy rename to instrumentation/spring/spring-jms-2.0/javaagent/src/testReceiveSpansDisabled/groovy/SpringListenerSuppressReceiveSpansTest.groovy index 790193e6540a..8516b872633f 100644 --- a/instrumentation/jms-1.1/javaagent/src/jms2TestReceiveSpansDisabled/groovy/SpringListenerJms2SuppressReceiveSpansTest.groovy +++ b/instrumentation/spring/spring-jms-2.0/javaagent/src/testReceiveSpansDisabled/groovy/SpringListenerSuppressReceiveSpansTest.groovy @@ -4,16 +4,16 @@ */ import io.opentelemetry.instrumentation.test.AgentInstrumentationSpecification -import listener.Config +import listener.AnnotatedListenerConfig import org.springframework.context.annotation.AnnotationConfigApplicationContext import org.springframework.jms.core.JmsTemplate import javax.jms.ConnectionFactory -class SpringListenerJms2SuppressReceiveSpansTest extends AgentInstrumentationSpecification { +class SpringListenerSuppressReceiveSpansTest extends AgentInstrumentationSpecification { def "receiving message in spring listener generates spans"() { setup: - def context = new AnnotationConfigApplicationContext(Config) + def context = new AnnotationConfigApplicationContext(AnnotatedListenerConfig) def factory = context.getBean(ConnectionFactory) def template = new JmsTemplate(factory) @@ -22,8 +22,8 @@ class SpringListenerJms2SuppressReceiveSpansTest extends AgentInstrumentationSpe expect: assertTraces(1) { trace(0, 2) { - Jms2Test.producerSpan(it, 0, "queue", "SpringListenerJms2") - Jms2Test.consumerSpan(it, 1, "queue", "SpringListenerJms2", "", span(0), "process") + SpringListenerTest.producerSpan(it, 0, "queue", "SpringListenerJms2") + SpringListenerTest.consumerSpan(it, 1, "queue", "SpringListenerJms2", "", span(0), "process") } } diff --git a/instrumentation/spring/spring-kafka-2.7/javaagent/build.gradle.kts b/instrumentation/spring/spring-kafka-2.7/javaagent/build.gradle.kts index 7eb196122d99..bee6cdf1960c 100644 --- a/instrumentation/spring/spring-kafka-2.7/javaagent/build.gradle.kts +++ b/instrumentation/spring/spring-kafka-2.7/javaagent/build.gradle.kts @@ -17,6 +17,7 @@ dependencies { bootstrap(project(":instrumentation:kafka:kafka-clients:kafka-clients-0.11:bootstrap")) implementation(project(":instrumentation:kafka:kafka-clients:kafka-clients-common:library")) + implementation(project(":instrumentation:spring:spring-kafka-2.7:library")) library("org.springframework.kafka:spring-kafka:2.7.0") @@ -64,3 +65,19 @@ tasks { dependsOn(testing.suites) } } + +configurations { + listOf( + testRuntimeClasspath, + named("testNoReceiveTelemetryRuntimeClasspath") + ) + .forEach { + it.configure { + resolutionStrategy { + // requires old logback (and therefore also old slf4j) + force("ch.qos.logback:logback-classic:1.2.11") + force("org.slf4j:slf4j-api:1.7.36") + } + } + } +} diff --git a/instrumentation/spring/spring-kafka-2.7/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/kafka/AbstractMessageListenerContainerInstrumentation.java b/instrumentation/spring/spring-kafka-2.7/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/kafka/AbstractMessageListenerContainerInstrumentation.java index f9324fb69182..5a7a8ebbb11b 100644 --- a/instrumentation/spring/spring-kafka-2.7/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/kafka/AbstractMessageListenerContainerInstrumentation.java +++ b/instrumentation/spring/spring-kafka-2.7/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/kafka/AbstractMessageListenerContainerInstrumentation.java @@ -5,20 +5,17 @@ package io.opentelemetry.javaagent.instrumentation.spring.kafka; +import static io.opentelemetry.javaagent.instrumentation.spring.kafka.SpringKafkaSingletons.telemetry; import static net.bytebuddy.matcher.ElementMatchers.isProtected; import static net.bytebuddy.matcher.ElementMatchers.named; import static net.bytebuddy.matcher.ElementMatchers.returns; import static net.bytebuddy.matcher.ElementMatchers.takesArguments; -import io.opentelemetry.context.Context; -import io.opentelemetry.instrumentation.api.util.VirtualField; 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.apache.kafka.clients.consumer.ConsumerRecord; -import org.apache.kafka.clients.consumer.ConsumerRecords; import org.springframework.kafka.listener.BatchInterceptor; import org.springframework.kafka.listener.RecordInterceptor; @@ -56,13 +53,13 @@ public static class GetBatchInterceptorAdvice { public static void onExit( @Advice.Return(readOnly = false) BatchInterceptor interceptor) { - if (!(interceptor instanceof InstrumentedBatchInterceptor)) { - VirtualField, Context> receiveContextField = - VirtualField.find(ConsumerRecords.class, Context.class); - VirtualField, State>> stateField = - VirtualField.find(ConsumerRecords.class, State.class); - interceptor = - new InstrumentedBatchInterceptor<>(receiveContextField, stateField, interceptor); + if (interceptor == null + || !interceptor + .getClass() + .getName() + .equals( + "io.opentelemetry.instrumentation.spring.kafka.v2_7.InstrumentedBatchInterceptor")) { + interceptor = telemetry().createBatchInterceptor(interceptor); } } } @@ -74,13 +71,13 @@ public static class GetRecordInterceptorAdvice { public static void onExit( @Advice.Return(readOnly = false) RecordInterceptor interceptor) { - if (!(interceptor instanceof InstrumentedRecordInterceptor)) { - VirtualField, Context> receiveContextField = - VirtualField.find(ConsumerRecord.class, Context.class); - VirtualField, State>> stateField = - VirtualField.find(ConsumerRecord.class, State.class); - interceptor = - new InstrumentedRecordInterceptor<>(receiveContextField, stateField, interceptor); + if (interceptor == null + || !interceptor + .getClass() + .getName() + .equals( + "io.opentelemetry.instrumentation.spring.kafka.v2_7.InstrumentedRecordInterceptor")) { + interceptor = telemetry().createRecordInterceptor(interceptor); } } } diff --git a/instrumentation/spring/spring-kafka-2.7/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/kafka/SpringKafkaSingletons.java b/instrumentation/spring/spring-kafka-2.7/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/kafka/SpringKafkaSingletons.java index f157950ea5a3..eaced3762408 100644 --- a/instrumentation/spring/spring-kafka-2.7/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/kafka/SpringKafkaSingletons.java +++ b/instrumentation/spring/spring-kafka-2.7/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/kafka/SpringKafkaSingletons.java @@ -6,42 +6,23 @@ package io.opentelemetry.javaagent.instrumentation.spring.kafka; import io.opentelemetry.api.GlobalOpenTelemetry; -import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; -import io.opentelemetry.instrumentation.kafka.internal.KafkaInstrumenterFactory; +import io.opentelemetry.instrumentation.spring.kafka.v2_7.SpringKafkaTelemetry; import io.opentelemetry.javaagent.bootstrap.internal.ExperimentalConfig; import io.opentelemetry.javaagent.bootstrap.internal.InstrumentationConfig; -import org.apache.kafka.clients.consumer.ConsumerRecord; -import org.apache.kafka.clients.consumer.ConsumerRecords; public final class SpringKafkaSingletons { - private static final String INSTRUMENTATION_NAME = "io.opentelemetry.spring-kafka-2.7"; - - private static final Instrumenter, Void> BATCH_PROCESS_INSTRUMENTER; - private static final Instrumenter, Void> PROCESS_INSTRUMENTER; - - static { - KafkaInstrumenterFactory factory = - new KafkaInstrumenterFactory(GlobalOpenTelemetry.get(), INSTRUMENTATION_NAME) - .setCaptureExperimentalSpanAttributes( - InstrumentationConfig.get() - .getBoolean("otel.instrumentation.kafka.experimental-span-attributes", false)) - .setPropagationEnabled( - InstrumentationConfig.get() - .getBoolean("otel.instrumentation.kafka.client-propagation.enabled", true)) - .setMessagingReceiveInstrumentationEnabled( - ExperimentalConfig.get().messagingReceiveInstrumentationEnabled()) - .setErrorCauseExtractor(SpringKafkaErrorCauseExtractor.INSTANCE); - BATCH_PROCESS_INSTRUMENTER = factory.createBatchProcessInstrumenter(); - PROCESS_INSTRUMENTER = factory.createConsumerProcessInstrumenter(); - } - - public static Instrumenter, Void> batchProcessInstrumenter() { - return BATCH_PROCESS_INSTRUMENTER; - } - - public static Instrumenter, Void> processInstrumenter() { - return PROCESS_INSTRUMENTER; + private static final SpringKafkaTelemetry TELEMETRY = + SpringKafkaTelemetry.builder(GlobalOpenTelemetry.get()) + .setCaptureExperimentalSpanAttributes( + InstrumentationConfig.get() + .getBoolean("otel.instrumentation.kafka.experimental-span-attributes", false)) + .setMessagingReceiveInstrumentationEnabled( + ExperimentalConfig.get().messagingReceiveInstrumentationEnabled()) + .build(); + + public static SpringKafkaTelemetry telemetry() { + return TELEMETRY; } private SpringKafkaSingletons() {} diff --git a/instrumentation/spring/spring-kafka-2.7/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/kafka/State.java b/instrumentation/spring/spring-kafka-2.7/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/kafka/State.java deleted file mode 100644 index f09bbe61c45b..000000000000 --- a/instrumentation/spring/spring-kafka-2.7/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/kafka/State.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.javaagent.instrumentation.spring.kafka; - -import com.google.auto.value.AutoValue; -import io.opentelemetry.context.Context; -import io.opentelemetry.context.Scope; - -@AutoValue -public abstract class State { - - public static State create(REQUEST request, Context context, Scope scope) { - return new AutoValue_State<>(request, context, scope); - } - - public abstract REQUEST request(); - - public abstract Context context(); - - public abstract Scope scope(); - - State() {} -} diff --git a/instrumentation/spring/spring-kafka-2.7/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/kafka/SpringKafkaTest.java b/instrumentation/spring/spring-kafka-2.7/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/kafka/SpringKafkaTest.java index 966306767970..a7621f3eac2c 100644 --- a/instrumentation/spring/spring-kafka-2.7/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/kafka/SpringKafkaTest.java +++ b/instrumentation/spring/spring-kafka-2.7/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/kafka/SpringKafkaTest.java @@ -9,21 +9,40 @@ import static io.opentelemetry.instrumentation.testing.util.TelemetryDataUtil.orderByRootSpanKind; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies; +import static java.util.Collections.emptyList; +import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; import io.opentelemetry.sdk.trace.data.LinkData; import io.opentelemetry.sdk.trace.data.SpanData; import io.opentelemetry.sdk.trace.data.StatusData; import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; import io.opentelemetry.testing.AbstractSpringKafkaTest; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicReference; import org.assertj.core.api.AbstractLongAssert; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; class SpringKafkaTest extends AbstractSpringKafkaTest { + @RegisterExtension + protected static final InstrumentationExtension testing = AgentInstrumentationExtension.create(); + + @Override + protected InstrumentationExtension testing() { + return testing; + } + + @Override + protected List> additionalSpringConfigs() { + return emptyList(); + } + @Test void shouldCreateSpansForSingleRecordProcess() { testing.runWithSpan( @@ -50,7 +69,8 @@ void shouldCreateSpansForSingleRecordProcess() { .hasAttributesSatisfyingExactly( equalTo(SemanticAttributes.MESSAGING_SYSTEM, "kafka"), equalTo(SemanticAttributes.MESSAGING_DESTINATION, "testSingleTopic"), - equalTo(SemanticAttributes.MESSAGING_DESTINATION_KIND, "topic"))); + equalTo(SemanticAttributes.MESSAGING_DESTINATION_KIND, "topic"), + equalTo(AttributeKey.stringKey("messaging.payload"), "testSpan"))); producer.set(trace.getSpan(1)); }, @@ -84,7 +104,8 @@ void shouldCreateSpansForSingleRecordProcess() { satisfies(longKey("kafka.offset"), AbstractLongAssert::isNotNegative), satisfies( longKey("kafka.record.queue_time_ms"), - AbstractLongAssert::isNotNegative)), + AbstractLongAssert::isNotNegative), + equalTo(AttributeKey.stringKey("messaging.payload"), "testSpan")), span -> span.hasName("consumer").hasParent(trace.getSpan(1)))); } @@ -114,7 +135,8 @@ void shouldHandleFailureInKafkaListener() { .hasAttributesSatisfyingExactly( equalTo(SemanticAttributes.MESSAGING_SYSTEM, "kafka"), equalTo(SemanticAttributes.MESSAGING_DESTINATION, "testSingleTopic"), - equalTo(SemanticAttributes.MESSAGING_DESTINATION_KIND, "topic"))); + equalTo(SemanticAttributes.MESSAGING_DESTINATION_KIND, "topic"), + equalTo(AttributeKey.stringKey("messaging.payload"), "error"))); producer.set(trace.getSpan(1)); }, @@ -150,7 +172,8 @@ void shouldHandleFailureInKafkaListener() { satisfies(longKey("kafka.offset"), AbstractLongAssert::isNotNegative), satisfies( longKey("kafka.record.queue_time_ms"), - AbstractLongAssert::isNotNegative)), + AbstractLongAssert::isNotNegative), + equalTo(AttributeKey.stringKey("messaging.payload"), "error")), span -> span.hasName("consumer").hasParent(trace.getSpan(1)))); } @@ -176,7 +199,8 @@ void shouldCreateSpansForBatchReceiveAndProcess() throws InterruptedException { .hasAttributesSatisfyingExactly( equalTo(SemanticAttributes.MESSAGING_SYSTEM, "kafka"), equalTo(SemanticAttributes.MESSAGING_DESTINATION, "testBatchTopic"), - equalTo(SemanticAttributes.MESSAGING_DESTINATION_KIND, "topic")), + equalTo(SemanticAttributes.MESSAGING_DESTINATION_KIND, "topic"), + equalTo(AttributeKey.stringKey("messaging.payload"), "testSpan2")), span -> span.hasName("testBatchTopic send") .hasKind(SpanKind.PRODUCER) @@ -184,7 +208,8 @@ void shouldCreateSpansForBatchReceiveAndProcess() throws InterruptedException { .hasAttributesSatisfyingExactly( equalTo(SemanticAttributes.MESSAGING_SYSTEM, "kafka"), equalTo(SemanticAttributes.MESSAGING_DESTINATION, "testBatchTopic"), - equalTo(SemanticAttributes.MESSAGING_DESTINATION_KIND, "topic"))); + equalTo(SemanticAttributes.MESSAGING_DESTINATION_KIND, "topic"), + equalTo(AttributeKey.stringKey("messaging.payload"), "testSpan1"))); producer1.set(trace.getSpan(1)); producer2.set(trace.getSpan(2)); @@ -241,7 +266,8 @@ void shouldHandleFailureInKafkaBatchListener() { .hasAttributesSatisfyingExactly( equalTo(SemanticAttributes.MESSAGING_SYSTEM, "kafka"), equalTo(SemanticAttributes.MESSAGING_DESTINATION, "testBatchTopic"), - equalTo(SemanticAttributes.MESSAGING_DESTINATION_KIND, "topic"))); + equalTo(SemanticAttributes.MESSAGING_DESTINATION_KIND, "topic"), + equalTo(AttributeKey.stringKey("messaging.payload"), "error"))); producer.set(trace.getSpan(1)); }, diff --git a/instrumentation/spring/spring-kafka-2.7/javaagent/src/testNoReceiveTelemetry/java/io/opentelemetry/javaagent/instrumentation/spring/kafka/SpringKafkaNoReceiveTelemetryTest.java b/instrumentation/spring/spring-kafka-2.7/javaagent/src/testNoReceiveTelemetry/java/io/opentelemetry/javaagent/instrumentation/spring/kafka/SpringKafkaNoReceiveTelemetryTest.java index 9ec21dd8a53b..e40a0d7c6ec3 100644 --- a/instrumentation/spring/spring-kafka-2.7/javaagent/src/testNoReceiveTelemetry/java/io/opentelemetry/javaagent/instrumentation/spring/kafka/SpringKafkaNoReceiveTelemetryTest.java +++ b/instrumentation/spring/spring-kafka-2.7/javaagent/src/testNoReceiveTelemetry/java/io/opentelemetry/javaagent/instrumentation/spring/kafka/SpringKafkaNoReceiveTelemetryTest.java @@ -5,206 +5,26 @@ package io.opentelemetry.javaagent.instrumentation.spring.kafka; -import static io.opentelemetry.instrumentation.testing.util.TelemetryDataUtil.orderByRootSpanKind; -import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; -import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies; +import static java.util.Collections.emptyList; -import io.opentelemetry.api.trace.SpanKind; -import io.opentelemetry.sdk.trace.data.LinkData; -import io.opentelemetry.sdk.trace.data.SpanData; -import io.opentelemetry.sdk.trace.data.StatusData; -import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; -import io.opentelemetry.testing.AbstractSpringKafkaTest; -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.atomic.AtomicReference; -import org.assertj.core.api.AbstractLongAssert; -import org.junit.jupiter.api.Test; +import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.testing.AbstractSpringKafkaNoReceiveTelemetryTest; +import java.util.List; +import org.junit.jupiter.api.extension.RegisterExtension; -class SpringKafkaNoReceiveTelemetryTest extends AbstractSpringKafkaTest { +class SpringKafkaNoReceiveTelemetryTest extends AbstractSpringKafkaNoReceiveTelemetryTest { - @Test - void shouldCreateSpansForSingleRecordProcess() { - testing.runWithSpan( - "producer", - () -> { - kafkaTemplate.executeInTransaction( - ops -> { - ops.send("testSingleTopic", "10", "testSpan"); - return 0; - }); - }); + @RegisterExtension + protected static final InstrumentationExtension testing = AgentInstrumentationExtension.create(); - testing.waitAndAssertTraces( - trace -> - trace.hasSpansSatisfyingExactly( - span -> span.hasName("producer"), - span -> - span.hasName("testSingleTopic send") - .hasKind(SpanKind.PRODUCER) - .hasParent(trace.getSpan(0)) - .hasAttributesSatisfyingExactly( - equalTo(SemanticAttributes.MESSAGING_SYSTEM, "kafka"), - equalTo(SemanticAttributes.MESSAGING_DESTINATION, "testSingleTopic"), - equalTo(SemanticAttributes.MESSAGING_DESTINATION_KIND, "topic")), - span -> - span.hasName("testSingleTopic process") - .hasKind(SpanKind.CONSUMER) - .hasParent(trace.getSpan(1)) - .hasAttributesSatisfyingExactly( - equalTo(SemanticAttributes.MESSAGING_SYSTEM, "kafka"), - equalTo(SemanticAttributes.MESSAGING_DESTINATION, "testSingleTopic"), - equalTo(SemanticAttributes.MESSAGING_DESTINATION_KIND, "topic"), - equalTo(SemanticAttributes.MESSAGING_OPERATION, "process"), - satisfies( - SemanticAttributes.MESSAGING_MESSAGE_PAYLOAD_SIZE_BYTES, - AbstractLongAssert::isNotNegative), - satisfies( - SemanticAttributes.MESSAGING_KAFKA_PARTITION, - AbstractLongAssert::isNotNegative)), - span -> span.hasName("consumer").hasParent(trace.getSpan(2)))); + @Override + protected InstrumentationExtension testing() { + return testing; } - @Test - void shouldHandleFailureInKafkaListener() { - testing.runWithSpan( - "producer", - () -> { - kafkaTemplate.executeInTransaction( - ops -> { - ops.send("testSingleTopic", "10", "error"); - return 0; - }); - }); - - testing.waitAndAssertTraces( - trace -> - trace.hasSpansSatisfyingExactly( - span -> span.hasName("producer"), - span -> - span.hasName("testSingleTopic send") - .hasKind(SpanKind.PRODUCER) - .hasParent(trace.getSpan(0)) - .hasAttributesSatisfyingExactly( - equalTo(SemanticAttributes.MESSAGING_SYSTEM, "kafka"), - equalTo(SemanticAttributes.MESSAGING_DESTINATION, "testSingleTopic"), - equalTo(SemanticAttributes.MESSAGING_DESTINATION_KIND, "topic")), - span -> - span.hasName("testSingleTopic process") - .hasKind(SpanKind.CONSUMER) - .hasParent(trace.getSpan(1)) - .hasStatus(StatusData.error()) - .hasException(new IllegalArgumentException("boom")) - .hasAttributesSatisfyingExactly( - equalTo(SemanticAttributes.MESSAGING_SYSTEM, "kafka"), - equalTo(SemanticAttributes.MESSAGING_DESTINATION, "testSingleTopic"), - equalTo(SemanticAttributes.MESSAGING_DESTINATION_KIND, "topic"), - equalTo(SemanticAttributes.MESSAGING_OPERATION, "process"), - satisfies( - SemanticAttributes.MESSAGING_MESSAGE_PAYLOAD_SIZE_BYTES, - AbstractLongAssert::isNotNegative), - satisfies( - SemanticAttributes.MESSAGING_KAFKA_PARTITION, - AbstractLongAssert::isNotNegative)), - span -> span.hasName("consumer").hasParent(trace.getSpan(2)))); - } - - @Test - void shouldCreateSpansForBatchReceiveAndProcess() throws InterruptedException { - Map batchMessages = new HashMap<>(); - batchMessages.put("10", "testSpan1"); - batchMessages.put("20", "testSpan2"); - sendBatchMessages(batchMessages); - - AtomicReference producer1 = new AtomicReference<>(); - AtomicReference producer2 = new AtomicReference<>(); - - testing.waitAndAssertSortedTraces( - orderByRootSpanKind(SpanKind.INTERNAL, SpanKind.CONSUMER), - trace -> { - trace.hasSpansSatisfyingExactly( - span -> span.hasName("producer"), - span -> - span.hasName("testBatchTopic send") - .hasKind(SpanKind.PRODUCER) - .hasParent(trace.getSpan(0)) - .hasAttributesSatisfyingExactly( - equalTo(SemanticAttributes.MESSAGING_SYSTEM, "kafka"), - equalTo(SemanticAttributes.MESSAGING_DESTINATION, "testBatchTopic"), - equalTo(SemanticAttributes.MESSAGING_DESTINATION_KIND, "topic")), - span -> - span.hasName("testBatchTopic send") - .hasKind(SpanKind.PRODUCER) - .hasParent(trace.getSpan(0)) - .hasAttributesSatisfyingExactly( - equalTo(SemanticAttributes.MESSAGING_SYSTEM, "kafka"), - equalTo(SemanticAttributes.MESSAGING_DESTINATION, "testBatchTopic"), - equalTo(SemanticAttributes.MESSAGING_DESTINATION_KIND, "topic"))); - - producer1.set(trace.getSpan(1)); - producer2.set(trace.getSpan(2)); - }, - trace -> - trace.hasSpansSatisfyingExactly( - span -> - span.hasName("testBatchTopic process") - .hasKind(SpanKind.CONSUMER) - .hasNoParent() - .hasLinks( - LinkData.create(producer1.get().getSpanContext()), - LinkData.create(producer2.get().getSpanContext())) - .hasAttributesSatisfyingExactly( - equalTo(SemanticAttributes.MESSAGING_SYSTEM, "kafka"), - equalTo(SemanticAttributes.MESSAGING_DESTINATION, "testBatchTopic"), - equalTo(SemanticAttributes.MESSAGING_DESTINATION_KIND, "topic"), - equalTo(SemanticAttributes.MESSAGING_OPERATION, "process")), - span -> span.hasName("consumer").hasParent(trace.getSpan(0)))); - } - - @Test - void shouldHandleFailureInKafkaBatchListener() { - testing.runWithSpan( - "producer", - () -> { - kafkaTemplate.executeInTransaction( - ops -> { - ops.send("testBatchTopic", "10", "error"); - return 0; - }); - }); - - AtomicReference producer = new AtomicReference<>(); - - testing.waitAndAssertSortedTraces( - orderByRootSpanKind(SpanKind.INTERNAL, SpanKind.CONSUMER), - trace -> { - trace.hasSpansSatisfyingExactly( - span -> span.hasName("producer"), - span -> - span.hasName("testBatchTopic send") - .hasKind(SpanKind.PRODUCER) - .hasParent(trace.getSpan(0)) - .hasAttributesSatisfyingExactly( - equalTo(SemanticAttributes.MESSAGING_SYSTEM, "kafka"), - equalTo(SemanticAttributes.MESSAGING_DESTINATION, "testBatchTopic"), - equalTo(SemanticAttributes.MESSAGING_DESTINATION_KIND, "topic"))); - - producer.set(trace.getSpan(1)); - }, - trace -> - trace.hasSpansSatisfyingExactly( - span -> - span.hasName("testBatchTopic process") - .hasKind(SpanKind.CONSUMER) - .hasNoParent() - .hasLinks(LinkData.create(producer.get().getSpanContext())) - .hasStatus(StatusData.error()) - .hasException(new IllegalArgumentException("boom")) - .hasAttributesSatisfyingExactly( - equalTo(SemanticAttributes.MESSAGING_SYSTEM, "kafka"), - equalTo(SemanticAttributes.MESSAGING_DESTINATION, "testBatchTopic"), - equalTo(SemanticAttributes.MESSAGING_DESTINATION_KIND, "topic"), - equalTo(SemanticAttributes.MESSAGING_OPERATION, "process")), - span -> span.hasName("consumer").hasParent(trace.getSpan(0)))); + @Override + protected List> additionalSpringConfigs() { + return emptyList(); } } diff --git a/instrumentation/spring/spring-kafka-2.7/library/build.gradle.kts b/instrumentation/spring/spring-kafka-2.7/library/build.gradle.kts new file mode 100644 index 000000000000..e2624d5feefd --- /dev/null +++ b/instrumentation/spring/spring-kafka-2.7/library/build.gradle.kts @@ -0,0 +1,29 @@ +plugins { + id("otel.library-instrumentation") +} + +dependencies { + compileOnly("com.google.auto.value:auto-value-annotations") + annotationProcessor("com.google.auto.value:auto-value") + + implementation(project(":instrumentation:kafka:kafka-clients:kafka-clients-common:library")) + + compileOnly("org.springframework.kafka:spring-kafka:2.7.0") + + testImplementation(project(":instrumentation:spring:spring-kafka-2.7:testing")) + testImplementation(project(":instrumentation:kafka:kafka-clients:kafka-clients-2.6:library")) + + // 2.7.0 has a bug that makes decorating a Kafka Producer impossible + testLibrary("org.springframework.kafka:spring-kafka:2.7.1") + + testLibrary("org.springframework.boot:spring-boot-starter-test:2.5.3") + testLibrary("org.springframework.boot:spring-boot-starter:2.5.3") +} + +configurations.testRuntimeClasspath { + resolutionStrategy { + // requires old logback (and therefore also old slf4j) + force("ch.qos.logback:logback-classic:1.2.11") + force("org.slf4j:slf4j-api:1.7.36") + } +} diff --git a/instrumentation/spring/spring-kafka-2.7/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/kafka/InstrumentedBatchInterceptor.java b/instrumentation/spring/spring-kafka-2.7/library/src/main/java/io/opentelemetry/instrumentation/spring/kafka/v2_7/InstrumentedBatchInterceptor.java similarity index 63% rename from instrumentation/spring/spring-kafka-2.7/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/kafka/InstrumentedBatchInterceptor.java rename to instrumentation/spring/spring-kafka-2.7/library/src/main/java/io/opentelemetry/instrumentation/spring/kafka/v2_7/InstrumentedBatchInterceptor.java index f95d5afa2a14..a35cefd5f4c4 100644 --- a/instrumentation/spring/spring-kafka-2.7/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/kafka/InstrumentedBatchInterceptor.java +++ b/instrumentation/spring/spring-kafka-2.7/library/src/main/java/io/opentelemetry/instrumentation/spring/kafka/v2_7/InstrumentedBatchInterceptor.java @@ -3,30 +3,31 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.javaagent.instrumentation.spring.kafka; - -import static io.opentelemetry.javaagent.instrumentation.spring.kafka.SpringKafkaSingletons.batchProcessInstrumenter; +package io.opentelemetry.instrumentation.spring.kafka.v2_7; import io.opentelemetry.context.Context; import io.opentelemetry.context.Scope; +import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; import io.opentelemetry.instrumentation.api.util.VirtualField; import javax.annotation.Nullable; import org.apache.kafka.clients.consumer.Consumer; import org.apache.kafka.clients.consumer.ConsumerRecords; import org.springframework.kafka.listener.BatchInterceptor; -public final class InstrumentedBatchInterceptor implements BatchInterceptor { +final class InstrumentedBatchInterceptor implements BatchInterceptor { + + private static final VirtualField, Context> receiveContextField = + VirtualField.find(ConsumerRecords.class, Context.class); + private static final VirtualField, State>> + stateField = VirtualField.find(ConsumerRecords.class, State.class); - private final VirtualField, Context> receiveContextField; - private final VirtualField, State>> stateField; + private final Instrumenter, Void> batchProcessInstrumenter; @Nullable private final BatchInterceptor decorated; - public InstrumentedBatchInterceptor( - VirtualField, Context> receiveContextField, - VirtualField, State>> stateField, + InstrumentedBatchInterceptor( + Instrumenter, Void> batchProcessInstrumenter, @Nullable BatchInterceptor decorated) { - this.receiveContextField = receiveContextField; - this.stateField = stateField; + this.batchProcessInstrumenter = batchProcessInstrumenter; this.decorated = decorated; } @@ -34,8 +35,8 @@ public InstrumentedBatchInterceptor( public ConsumerRecords intercept(ConsumerRecords records, Consumer consumer) { Context parentContext = getParentContext(records); - if (batchProcessInstrumenter().shouldStart(parentContext, records)) { - Context context = batchProcessInstrumenter().start(parentContext, records); + if (batchProcessInstrumenter.shouldStart(parentContext, records)) { + Context context = batchProcessInstrumenter.start(parentContext, records); Scope scope = context.makeCurrent(); stateField.set(records, State.create(records, context, scope)); } @@ -67,11 +68,11 @@ public void failure(ConsumerRecords records, Exception exception, Consumer } private void end(ConsumerRecords records, @Nullable Throwable error) { - State> state = stateField.get(records); + State> state = stateField.get(records); stateField.set(records, null); if (state != null) { state.scope().close(); - batchProcessInstrumenter().end(state.context(), state.request(), null, error); + batchProcessInstrumenter.end(state.context(), state.request(), null, error); } } } diff --git a/instrumentation/spring/spring-kafka-2.7/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/kafka/InstrumentedRecordInterceptor.java b/instrumentation/spring/spring-kafka-2.7/library/src/main/java/io/opentelemetry/instrumentation/spring/kafka/v2_7/InstrumentedRecordInterceptor.java similarity index 67% rename from instrumentation/spring/spring-kafka-2.7/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/kafka/InstrumentedRecordInterceptor.java rename to instrumentation/spring/spring-kafka-2.7/library/src/main/java/io/opentelemetry/instrumentation/spring/kafka/v2_7/InstrumentedRecordInterceptor.java index f3c3719108f6..dccb77578e2b 100644 --- a/instrumentation/spring/spring-kafka-2.7/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/kafka/InstrumentedRecordInterceptor.java +++ b/instrumentation/spring/spring-kafka-2.7/library/src/main/java/io/opentelemetry/instrumentation/spring/kafka/v2_7/InstrumentedRecordInterceptor.java @@ -3,30 +3,31 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.javaagent.instrumentation.spring.kafka; - -import static io.opentelemetry.javaagent.instrumentation.spring.kafka.SpringKafkaSingletons.processInstrumenter; +package io.opentelemetry.instrumentation.spring.kafka.v2_7; import io.opentelemetry.context.Context; import io.opentelemetry.context.Scope; +import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; import io.opentelemetry.instrumentation.api.util.VirtualField; import javax.annotation.Nullable; import org.apache.kafka.clients.consumer.Consumer; import org.apache.kafka.clients.consumer.ConsumerRecord; import org.springframework.kafka.listener.RecordInterceptor; -public final class InstrumentedRecordInterceptor implements RecordInterceptor { +final class InstrumentedRecordInterceptor implements RecordInterceptor { + + private static final VirtualField, Context> receiveContextField = + VirtualField.find(ConsumerRecord.class, Context.class); + private static final VirtualField, State>> stateField = + VirtualField.find(ConsumerRecord.class, State.class); - private final VirtualField, Context> receiveContextField; - private final VirtualField, State>> stateField; + private final Instrumenter, Void> processInstrumenter; @Nullable private final RecordInterceptor decorated; - public InstrumentedRecordInterceptor( - VirtualField, Context> receiveContextField, - VirtualField, State>> stateField, + InstrumentedRecordInterceptor( + Instrumenter, Void> processInstrumenter, @Nullable RecordInterceptor decorated) { - this.receiveContextField = receiveContextField; - this.stateField = stateField; + this.processInstrumenter = processInstrumenter; this.decorated = decorated; } @@ -46,8 +47,8 @@ public ConsumerRecord intercept(ConsumerRecord record, Consumer record) { Context parentContext = getParentContext(record); - if (processInstrumenter().shouldStart(parentContext, record)) { - Context context = processInstrumenter().start(parentContext, record); + if (processInstrumenter.shouldStart(parentContext, record)) { + Context context = processInstrumenter.start(parentContext, record); Scope scope = context.makeCurrent(); stateField.set(record, State.create(record, context, scope)); } @@ -77,11 +78,11 @@ public void failure(ConsumerRecord record, Exception exception, Consumer record, @Nullable Throwable error) { - State> state = stateField.get(record); + State> state = stateField.get(record); stateField.set(record, null); if (state != null) { state.scope().close(); - processInstrumenter().end(state.context(), state.request(), null, error); + processInstrumenter.end(state.context(), state.request(), null, error); } } } diff --git a/instrumentation/spring/spring-kafka-2.7/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/kafka/SpringKafkaErrorCauseExtractor.java b/instrumentation/spring/spring-kafka-2.7/library/src/main/java/io/opentelemetry/instrumentation/spring/kafka/v2_7/SpringKafkaErrorCauseExtractor.java similarity index 81% rename from instrumentation/spring/spring-kafka-2.7/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/kafka/SpringKafkaErrorCauseExtractor.java rename to instrumentation/spring/spring-kafka-2.7/library/src/main/java/io/opentelemetry/instrumentation/spring/kafka/v2_7/SpringKafkaErrorCauseExtractor.java index a495bb4d6934..463ce1b13b0d 100644 --- a/instrumentation/spring/spring-kafka-2.7/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/kafka/SpringKafkaErrorCauseExtractor.java +++ b/instrumentation/spring/spring-kafka-2.7/library/src/main/java/io/opentelemetry/instrumentation/spring/kafka/v2_7/SpringKafkaErrorCauseExtractor.java @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.javaagent.instrumentation.spring.kafka; +package io.opentelemetry.instrumentation.spring.kafka.v2_7; import io.opentelemetry.instrumentation.api.instrumenter.ErrorCauseExtractor; import org.springframework.kafka.listener.ListenerExecutionFailedException; @@ -16,6 +16,6 @@ public Throwable extract(Throwable error) { if (error instanceof ListenerExecutionFailedException && error.getCause() != null) { error = error.getCause(); } - return ErrorCauseExtractor.jdk().extract(error); + return ErrorCauseExtractor.getDefault().extract(error); } } diff --git a/instrumentation/spring/spring-kafka-2.7/library/src/main/java/io/opentelemetry/instrumentation/spring/kafka/v2_7/SpringKafkaTelemetry.java b/instrumentation/spring/spring-kafka-2.7/library/src/main/java/io/opentelemetry/instrumentation/spring/kafka/v2_7/SpringKafkaTelemetry.java new file mode 100644 index 000000000000..085bc7b1835a --- /dev/null +++ b/instrumentation/spring/spring-kafka-2.7/library/src/main/java/io/opentelemetry/instrumentation/spring/kafka/v2_7/SpringKafkaTelemetry.java @@ -0,0 +1,84 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.spring.kafka.v2_7; + +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; +import org.apache.kafka.clients.consumer.ConsumerRecord; +import org.apache.kafka.clients.consumer.ConsumerRecords; +import org.springframework.kafka.listener.AbstractMessageListenerContainer; +import org.springframework.kafka.listener.BatchInterceptor; +import org.springframework.kafka.listener.RecordInterceptor; + +/** Entrypoint for instrumenting Spring Kafka listeners. */ +public final class SpringKafkaTelemetry { + + /** Returns a new {@link SpringKafkaTelemetry} configured with the given {@link OpenTelemetry}. */ + public static SpringKafkaTelemetry create(OpenTelemetry openTelemetry) { + return builder(openTelemetry).build(); + } + + /** + * Returns a new {@link SpringKafkaTelemetryBuilder} configured with the given {@link + * OpenTelemetry}. + */ + public static SpringKafkaTelemetryBuilder builder(OpenTelemetry openTelemetry) { + return new SpringKafkaTelemetryBuilder(openTelemetry); + } + + private final Instrumenter, Void> processInstrumenter; + private final Instrumenter, Void> batchProcessInstrumenter; + + SpringKafkaTelemetry( + Instrumenter, Void> processInstrumenter, + Instrumenter, Void> batchProcessInstrumenter) { + this.processInstrumenter = processInstrumenter; + this.batchProcessInstrumenter = batchProcessInstrumenter; + } + + /** + * Returns a new {@link RecordInterceptor} that decorates a message listener with a {@link + * SpanKind#CONSUMER CONSUMER} span. Can be set on a {@link AbstractMessageListenerContainer} + * using the {@link AbstractMessageListenerContainer#setRecordInterceptor(RecordInterceptor)} + * method. + */ + public RecordInterceptor createRecordInterceptor() { + return createRecordInterceptor(null); + } + + /** + * Returns a new {@link RecordInterceptor} that decorates a message listener with a {@link + * SpanKind#CONSUMER CONSUMER} span, and then delegates to a provided {@code + * decoratedInterceptor}. Can be set on a {@link AbstractMessageListenerContainer} using the + * {@link AbstractMessageListenerContainer#setRecordInterceptor(RecordInterceptor)} method. + */ + public RecordInterceptor createRecordInterceptor( + RecordInterceptor decoratedInterceptor) { + return new InstrumentedRecordInterceptor<>(processInstrumenter, decoratedInterceptor); + } + + /** + * Returns a new {@link BatchInterceptor} that decorates a message listener with a {@link + * SpanKind#CONSUMER CONSUMER} span. Can be set on a {@link AbstractMessageListenerContainer} + * using the {@link AbstractMessageListenerContainer#setBatchInterceptor(BatchInterceptor)} + * method. + */ + public BatchInterceptor createBatchInterceptor() { + return createBatchInterceptor(null); + } + + /** + * Returns a new {@link BatchInterceptor} that decorates a message listener with a {@link + * SpanKind#CONSUMER CONSUMER} span, and then delegates to a provided {@code + * decoratedInterceptor}. Can be set on a {@link AbstractMessageListenerContainer} using the + * {@link AbstractMessageListenerContainer#setBatchInterceptor(BatchInterceptor)} method. + */ + public BatchInterceptor createBatchInterceptor( + BatchInterceptor decoratedInterceptor) { + return new InstrumentedBatchInterceptor<>(batchProcessInstrumenter, decoratedInterceptor); + } +} diff --git a/instrumentation/spring/spring-kafka-2.7/library/src/main/java/io/opentelemetry/instrumentation/spring/kafka/v2_7/SpringKafkaTelemetryBuilder.java b/instrumentation/spring/spring-kafka-2.7/library/src/main/java/io/opentelemetry/instrumentation/spring/kafka/v2_7/SpringKafkaTelemetryBuilder.java new file mode 100644 index 000000000000..b65725b5e628 --- /dev/null +++ b/instrumentation/spring/spring-kafka-2.7/library/src/main/java/io/opentelemetry/instrumentation/spring/kafka/v2_7/SpringKafkaTelemetryBuilder.java @@ -0,0 +1,75 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.spring.kafka.v2_7; + +import static java.util.Collections.emptyList; + +import com.google.errorprone.annotations.CanIgnoreReturnValue; +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.instrumentation.kafka.internal.KafkaInstrumenterFactory; +import java.util.List; + +/** A builder of {@link SpringKafkaTelemetry}. */ +public final class SpringKafkaTelemetryBuilder { + + private static final String INSTRUMENTATION_NAME = "io.opentelemetry.spring-kafka-2.7"; + + private final OpenTelemetry openTelemetry; + private List capturedHeaders = emptyList(); + private boolean captureExperimentalSpanAttributes = false; + private boolean messagingReceiveInstrumentationEnabled = false; + + SpringKafkaTelemetryBuilder(OpenTelemetry openTelemetry) { + this.openTelemetry = openTelemetry; + } + + @CanIgnoreReturnValue + public SpringKafkaTelemetryBuilder setCapturedHeaders(List capturedHeaders) { + this.capturedHeaders = capturedHeaders; + return this; + } + + @CanIgnoreReturnValue + public SpringKafkaTelemetryBuilder setCaptureExperimentalSpanAttributes( + boolean captureExperimentalSpanAttributes) { + this.captureExperimentalSpanAttributes = captureExperimentalSpanAttributes; + return this; + } + + /** + * @deprecated if you have a need for this configuration option please open an issue in the opentelemetry-java-instrumentation + * repository. + */ + @Deprecated + @CanIgnoreReturnValue + public SpringKafkaTelemetryBuilder setPropagationEnabled(boolean propagationEnabled) { + return this; + } + + @CanIgnoreReturnValue + public SpringKafkaTelemetryBuilder setMessagingReceiveInstrumentationEnabled( + boolean messagingReceiveInstrumentationEnabled) { + this.messagingReceiveInstrumentationEnabled = messagingReceiveInstrumentationEnabled; + return this; + } + + /** + * Returns a new {@link SpringKafkaTelemetry} with the settings of this {@link + * SpringKafkaTelemetryBuilder}. + */ + public SpringKafkaTelemetry build() { + KafkaInstrumenterFactory factory = + new KafkaInstrumenterFactory(openTelemetry, INSTRUMENTATION_NAME) + .setCapturedHeaders(capturedHeaders) + .setCaptureExperimentalSpanAttributes(captureExperimentalSpanAttributes) + .setMessagingReceiveInstrumentationEnabled(messagingReceiveInstrumentationEnabled) + .setErrorCauseExtractor(SpringKafkaErrorCauseExtractor.INSTANCE); + + return new SpringKafkaTelemetry( + factory.createConsumerProcessInstrumenter(), factory.createBatchProcessInstrumenter()); + } +} diff --git a/instrumentation/spring/spring-kafka-2.7/library/src/main/java/io/opentelemetry/instrumentation/spring/kafka/v2_7/State.java b/instrumentation/spring/spring-kafka-2.7/library/src/main/java/io/opentelemetry/instrumentation/spring/kafka/v2_7/State.java new file mode 100644 index 000000000000..47dbf0a3758b --- /dev/null +++ b/instrumentation/spring/spring-kafka-2.7/library/src/main/java/io/opentelemetry/instrumentation/spring/kafka/v2_7/State.java @@ -0,0 +1,26 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.spring.kafka.v2_7; + +import com.google.auto.value.AutoValue; +import io.opentelemetry.context.Context; +import io.opentelemetry.context.Scope; + +@AutoValue +abstract class State { + + static State create(REQUEST request, Context context, Scope scope) { + return new AutoValue_State<>(request, context, scope); + } + + abstract REQUEST request(); + + abstract Context context(); + + abstract Scope scope(); + + State() {} +} diff --git a/instrumentation/spring/spring-kafka-2.7/library/src/test/java/io/opentelemetry/instrumentation/spring/kafka/v2_7/SpringKafkaNoReceiveTelemetryTest.java b/instrumentation/spring/spring-kafka-2.7/library/src/test/java/io/opentelemetry/instrumentation/spring/kafka/v2_7/SpringKafkaNoReceiveTelemetryTest.java new file mode 100644 index 000000000000..2f051c866ba8 --- /dev/null +++ b/instrumentation/spring/spring-kafka-2.7/library/src/test/java/io/opentelemetry/instrumentation/spring/kafka/v2_7/SpringKafkaNoReceiveTelemetryTest.java @@ -0,0 +1,57 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.spring.kafka.v2_7; + +import static java.util.Collections.singletonList; + +import io.opentelemetry.instrumentation.kafkaclients.KafkaTelemetry; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension; +import io.opentelemetry.testing.AbstractSpringKafkaNoReceiveTelemetryTest; +import java.util.List; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.springframework.boot.autoconfigure.kafka.DefaultKafkaProducerFactoryCustomizer; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.kafka.config.ContainerCustomizer; +import org.springframework.kafka.listener.ConcurrentMessageListenerContainer; + +class SpringKafkaNoReceiveTelemetryTest extends AbstractSpringKafkaNoReceiveTelemetryTest { + + @RegisterExtension + static final InstrumentationExtension testing = LibraryInstrumentationExtension.create(); + + @Override + protected InstrumentationExtension testing() { + return testing; + } + + @Override + protected List> additionalSpringConfigs() { + return singletonList(KafkaInstrumentationConfig.class); + } + + @Configuration + public static class KafkaInstrumentationConfig { + + @Bean + public DefaultKafkaProducerFactoryCustomizer producerInstrumentation() { + KafkaTelemetry kafkaTelemetry = KafkaTelemetry.create(testing.getOpenTelemetry()); + return producerFactory -> producerFactory.addPostProcessor(kafkaTelemetry::wrap); + } + + @Bean + public ContainerCustomizer> + listenerCustomizer() { + SpringKafkaTelemetry springKafkaTelemetry = + SpringKafkaTelemetry.create(testing.getOpenTelemetry()); + return container -> { + container.setRecordInterceptor(springKafkaTelemetry.createRecordInterceptor()); + container.setBatchInterceptor(springKafkaTelemetry.createBatchInterceptor()); + }; + } + } +} diff --git a/instrumentation/spring/spring-kafka-2.7/testing/src/main/java/io/opentelemetry/testing/AbstractSpringKafkaNoReceiveTelemetryTest.java b/instrumentation/spring/spring-kafka-2.7/testing/src/main/java/io/opentelemetry/testing/AbstractSpringKafkaNoReceiveTelemetryTest.java new file mode 100644 index 000000000000..f319e6f4c910 --- /dev/null +++ b/instrumentation/spring/spring-kafka-2.7/testing/src/main/java/io/opentelemetry/testing/AbstractSpringKafkaNoReceiveTelemetryTest.java @@ -0,0 +1,228 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.testing; + +import static io.opentelemetry.instrumentation.testing.util.TelemetryDataUtil.orderByRootSpanKind; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies; + +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.sdk.trace.data.SpanData; +import io.opentelemetry.sdk.trace.data.StatusData; +import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.atomic.AtomicReference; +import org.assertj.core.api.AbstractLongAssert; +import org.junit.jupiter.api.Test; + +public abstract class AbstractSpringKafkaNoReceiveTelemetryTest extends AbstractSpringKafkaTest { + + @Test + void shouldCreateSpansForSingleRecordProcess() { + testing() + .runWithSpan( + "producer", + () -> { + kafkaTemplate.executeInTransaction( + ops -> { + ops.send("testSingleTopic", "10", "testSpan"); + return 0; + }); + }); + + testing() + .waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> span.hasName("producer"), + span -> + span.hasName("testSingleTopic send") + .hasKind(SpanKind.PRODUCER) + .hasParent(trace.getSpan(0)) + .hasAttributesSatisfyingExactly( + equalTo(SemanticAttributes.MESSAGING_SYSTEM, "kafka"), + equalTo( + SemanticAttributes.MESSAGING_DESTINATION, "testSingleTopic"), + equalTo(SemanticAttributes.MESSAGING_DESTINATION_KIND, "topic"), + equalTo(AttributeKey.stringKey("messaging.payload"), "testSpan")), + span -> + span.hasName("testSingleTopic process") + .hasKind(SpanKind.CONSUMER) + .hasParent(trace.getSpan(1)) + .hasAttributesSatisfyingExactly( + equalTo(SemanticAttributes.MESSAGING_SYSTEM, "kafka"), + equalTo( + SemanticAttributes.MESSAGING_DESTINATION, "testSingleTopic"), + equalTo(SemanticAttributes.MESSAGING_DESTINATION_KIND, "topic"), + equalTo(SemanticAttributes.MESSAGING_OPERATION, "process"), + satisfies( + SemanticAttributes.MESSAGING_MESSAGE_PAYLOAD_SIZE_BYTES, + AbstractLongAssert::isNotNegative), + satisfies( + SemanticAttributes.MESSAGING_KAFKA_PARTITION, + AbstractLongAssert::isNotNegative), + equalTo(AttributeKey.stringKey("messaging.payload"), "testSpan")), + span -> span.hasName("consumer").hasParent(trace.getSpan(2)))); + } + + @Test + void shouldHandleFailureInKafkaListener() { + testing() + .runWithSpan( + "producer", + () -> { + kafkaTemplate.executeInTransaction( + ops -> { + ops.send("testSingleTopic", "10", "error"); + return 0; + }); + }); + + testing() + .waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> span.hasName("producer"), + span -> + span.hasName("testSingleTopic send") + .hasKind(SpanKind.PRODUCER) + .hasParent(trace.getSpan(0)) + .hasAttributesSatisfyingExactly( + equalTo(SemanticAttributes.MESSAGING_SYSTEM, "kafka"), + equalTo( + SemanticAttributes.MESSAGING_DESTINATION, "testSingleTopic"), + equalTo(SemanticAttributes.MESSAGING_DESTINATION_KIND, "topic"), + equalTo(AttributeKey.stringKey("messaging.payload"), "error")), + span -> + span.hasName("testSingleTopic process") + .hasKind(SpanKind.CONSUMER) + .hasParent(trace.getSpan(1)) + .hasStatus(StatusData.error()) + .hasException(new IllegalArgumentException("boom")) + .hasAttributesSatisfyingExactly( + equalTo(SemanticAttributes.MESSAGING_SYSTEM, "kafka"), + equalTo( + SemanticAttributes.MESSAGING_DESTINATION, "testSingleTopic"), + equalTo(SemanticAttributes.MESSAGING_DESTINATION_KIND, "topic"), + equalTo(SemanticAttributes.MESSAGING_OPERATION, "process"), + satisfies( + SemanticAttributes.MESSAGING_MESSAGE_PAYLOAD_SIZE_BYTES, + AbstractLongAssert::isNotNegative), + satisfies( + SemanticAttributes.MESSAGING_KAFKA_PARTITION, + AbstractLongAssert::isNotNegative), + equalTo(AttributeKey.stringKey("messaging.payload"), "error")), + span -> span.hasName("consumer").hasParent(trace.getSpan(2)))); + } + + @Test + void shouldCreateSpansForBatchReceiveAndProcess() throws InterruptedException { + Map batchMessages = new HashMap<>(); + batchMessages.put("10", "testSpan1"); + batchMessages.put("20", "testSpan2"); + sendBatchMessages(batchMessages); + + AtomicReference producer1 = new AtomicReference<>(); + AtomicReference producer2 = new AtomicReference<>(); + + testing() + .waitAndAssertSortedTraces( + orderByRootSpanKind(SpanKind.INTERNAL, SpanKind.CONSUMER), + trace -> { + trace.hasSpansSatisfyingExactly( + span -> span.hasName("producer"), + span -> + span.hasName("testBatchTopic send") + .hasKind(SpanKind.PRODUCER) + .hasParent(trace.getSpan(0)) + .hasAttributesSatisfyingExactly( + equalTo(SemanticAttributes.MESSAGING_SYSTEM, "kafka"), + equalTo(SemanticAttributes.MESSAGING_DESTINATION, "testBatchTopic"), + equalTo(SemanticAttributes.MESSAGING_DESTINATION_KIND, "topic"), + equalTo(AttributeKey.stringKey("messaging.payload"), "testSpan2")), + span -> + span.hasName("testBatchTopic send") + .hasKind(SpanKind.PRODUCER) + .hasParent(trace.getSpan(0)) + .hasAttributesSatisfyingExactly( + equalTo(SemanticAttributes.MESSAGING_SYSTEM, "kafka"), + equalTo(SemanticAttributes.MESSAGING_DESTINATION, "testBatchTopic"), + equalTo(SemanticAttributes.MESSAGING_DESTINATION_KIND, "topic"), + equalTo(AttributeKey.stringKey("messaging.payload"), "testSpan1"))); + + producer1.set(trace.getSpan(1)); + producer2.set(trace.getSpan(2)); + }, + trace -> + trace.hasSpansSatisfyingExactly( + span -> + span.hasName("testBatchTopic process") + .hasKind(SpanKind.CONSUMER) + .hasNoParent() + .hasLinksSatisfying( + links( + producer1.get().getSpanContext(), + producer2.get().getSpanContext())) + .hasAttributesSatisfyingExactly( + equalTo(SemanticAttributes.MESSAGING_SYSTEM, "kafka"), + equalTo(SemanticAttributes.MESSAGING_DESTINATION, "testBatchTopic"), + equalTo(SemanticAttributes.MESSAGING_DESTINATION_KIND, "topic"), + equalTo(SemanticAttributes.MESSAGING_OPERATION, "process")), + span -> span.hasName("consumer").hasParent(trace.getSpan(0)))); + } + + @Test + void shouldHandleFailureInKafkaBatchListener() { + testing() + .runWithSpan( + "producer", + () -> { + kafkaTemplate.executeInTransaction( + ops -> { + ops.send("testBatchTopic", "10", "error"); + return 0; + }); + }); + + AtomicReference producer = new AtomicReference<>(); + + testing() + .waitAndAssertSortedTraces( + orderByRootSpanKind(SpanKind.INTERNAL, SpanKind.CONSUMER), + trace -> { + trace.hasSpansSatisfyingExactly( + span -> span.hasName("producer"), + span -> + span.hasName("testBatchTopic send") + .hasKind(SpanKind.PRODUCER) + .hasParent(trace.getSpan(0)) + .hasAttributesSatisfyingExactly( + equalTo(SemanticAttributes.MESSAGING_SYSTEM, "kafka"), + equalTo(SemanticAttributes.MESSAGING_DESTINATION, "testBatchTopic"), + equalTo(SemanticAttributes.MESSAGING_DESTINATION_KIND, "topic"), + equalTo(AttributeKey.stringKey("messaging.payload"), "error"))); + + producer.set(trace.getSpan(1)); + }, + trace -> + trace.hasSpansSatisfyingExactly( + span -> + span.hasName("testBatchTopic process") + .hasKind(SpanKind.CONSUMER) + .hasNoParent() + .hasLinksSatisfying(links(producer.get().getSpanContext())) + .hasStatus(StatusData.error()) + .hasException(new IllegalArgumentException("boom")) + .hasAttributesSatisfyingExactly( + equalTo(SemanticAttributes.MESSAGING_SYSTEM, "kafka"), + equalTo(SemanticAttributes.MESSAGING_DESTINATION, "testBatchTopic"), + equalTo(SemanticAttributes.MESSAGING_DESTINATION_KIND, "topic"), + equalTo(SemanticAttributes.MESSAGING_OPERATION, "process")), + span -> span.hasName("consumer").hasParent(trace.getSpan(0)))); + } +} diff --git a/instrumentation/spring/spring-kafka-2.7/testing/src/main/java/io/opentelemetry/testing/AbstractSpringKafkaTest.java b/instrumentation/spring/spring-kafka-2.7/testing/src/main/java/io/opentelemetry/testing/AbstractSpringKafkaTest.java index 952061b1bd54..419d42ee8b28 100644 --- a/instrumentation/spring/spring-kafka-2.7/testing/src/main/java/io/opentelemetry/testing/AbstractSpringKafkaTest.java +++ b/instrumentation/spring/spring-kafka-2.7/testing/src/main/java/io/opentelemetry/testing/AbstractSpringKafkaTest.java @@ -5,14 +5,20 @@ package io.opentelemetry.testing; -import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension; +import static org.assertj.core.api.Assertions.assertThat; + +import io.opentelemetry.api.trace.SpanContext; import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.sdk.trace.data.LinkData; import java.time.Duration; import java.util.HashMap; +import java.util.List; import java.util.Map; +import java.util.function.Consumer; import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.extension.RegisterExtension; +import org.junit.jupiter.api.BeforeEach; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.SpringApplication; @@ -26,22 +32,32 @@ public abstract class AbstractSpringKafkaTest { private static final Logger logger = LoggerFactory.getLogger(AbstractSpringKafkaTest.class); - @RegisterExtension - protected static final InstrumentationExtension testing = AgentInstrumentationExtension.create(); - static KafkaContainer kafka; - static ConfigurableApplicationContext applicationContext; - protected static KafkaTemplate kafkaTemplate; - @SuppressWarnings("unchecked") + ConfigurableApplicationContext applicationContext; + protected KafkaTemplate kafkaTemplate; + @BeforeAll - static void setUp() { + static void setUpKafka() { kafka = new KafkaContainer(DockerImageName.parse("confluentinc/cp-kafka:5.4.3")) .waitingFor(Wait.forLogMessage(".*started \\(kafka.server.KafkaServer\\).*", 1)) .withStartupTimeout(Duration.ofMinutes(1)); kafka.start(); + } + + @AfterAll + static void tearDownKafka() { + kafka.stop(); + } + + protected abstract InstrumentationExtension testing(); + protected abstract List> additionalSpringConfigs(); + + @SuppressWarnings("unchecked") + @BeforeEach + void setUpApp() { Map props = new HashMap<>(); props.put("spring.jmx.enabled", false); props.put("spring.main.web-application-type", "none"); @@ -53,16 +69,16 @@ static void setUp() { props.put("spring.kafka.producer.transaction-id-prefix", "test-"); SpringApplication app = new SpringApplication(ConsumerConfig.class); + app.addPrimarySources(additionalSpringConfigs()); app.setDefaultProperties(props); applicationContext = app.run(); kafkaTemplate = applicationContext.getBean("kafkaTemplate", KafkaTemplate.class); } - @AfterAll - static void tearDown() { - kafka.stop(); + @AfterEach + void tearDownApp() { if (applicationContext != null) { - applicationContext.stop(); + applicationContext.close(); } } @@ -75,25 +91,45 @@ protected void sendBatchMessages(Map keyToData) throws Interrupt for (int i = 1; i <= maxAttempts; i++) { BatchRecordListener.reset(); - testing.runWithSpan( - "producer", - () -> { - kafkaTemplate.executeInTransaction( - ops -> { - keyToData.forEach((key, data) -> ops.send("testBatchTopic", key, data)); - return 0; - }); - }); + testing() + .runWithSpan( + "producer", + () -> { + kafkaTemplate.executeInTransaction( + ops -> { + keyToData.forEach((key, data) -> ops.send("testBatchTopic", key, data)); + return 0; + }); + }); BatchRecordListener.waitForMessages(); if (BatchRecordListener.getLastBatchSize() == 2) { break; } else if (i < maxAttempts) { - testing.waitForTraces(2); + testing().waitForTraces(2); Thread.sleep(1_000); // sleep a bit to give time for all the spans to arrive - testing.clearData(); + testing().clearData(); logger.info("Messages weren't received as batch, retrying"); } } } + + protected static Consumer> links(SpanContext... spanContexts) { + return links -> { + assertThat(links).hasSize(spanContexts.length); + for (SpanContext spanContext : spanContexts) { + assertThat(links) + .anySatisfy( + link -> { + assertThat(link.getSpanContext().getTraceId()) + .isEqualTo(spanContext.getTraceId()); + assertThat(link.getSpanContext().getSpanId()).isEqualTo(spanContext.getSpanId()); + assertThat(link.getSpanContext().getTraceFlags()) + .isEqualTo(spanContext.getTraceFlags()); + assertThat(link.getSpanContext().getTraceState()) + .isEqualTo(spanContext.getTraceState()); + }); + } + }; + } } diff --git a/instrumentation/spring/spring-kafka-2.7/testing/src/main/java/io/opentelemetry/testing/ConsumerConfig.java b/instrumentation/spring/spring-kafka-2.7/testing/src/main/java/io/opentelemetry/testing/ConsumerConfig.java index 26ec9452997a..f24070bc1bda 100644 --- a/instrumentation/spring/spring-kafka-2.7/testing/src/main/java/io/opentelemetry/testing/ConsumerConfig.java +++ b/instrumentation/spring/spring-kafka-2.7/testing/src/main/java/io/opentelemetry/testing/ConsumerConfig.java @@ -6,12 +6,15 @@ package io.opentelemetry.testing; import org.apache.kafka.clients.admin.NewTopic; +import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.SpringBootConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.context.annotation.Bean; import org.springframework.kafka.config.ConcurrentKafkaListenerContainerFactory; +import org.springframework.kafka.config.ContainerCustomizer; import org.springframework.kafka.config.TopicBuilder; import org.springframework.kafka.core.ConsumerFactory; +import org.springframework.kafka.listener.ConcurrentMessageListenerContainer; @SpringBootConfiguration @EnableAutoConfiguration @@ -39,7 +42,11 @@ public SingleRecordListener singleRecordListener() { @Bean public ConcurrentKafkaListenerContainerFactory batchFactory( - ConsumerFactory consumerFactory) { + ConsumerFactory consumerFactory, + ObjectProvider< + ContainerCustomizer< + String, String, ConcurrentMessageListenerContainer>> + customizerProvider) { ConcurrentKafkaListenerContainerFactory factory = new ConcurrentKafkaListenerContainerFactory<>(); // do not retry failed records @@ -47,12 +54,17 @@ public ConcurrentKafkaListenerContainerFactory batchFactory( factory.setConsumerFactory(consumerFactory); factory.setBatchListener(true); factory.setAutoStartup(true); + customizerProvider.ifAvailable(factory::setContainerCustomizer); return factory; } @Bean public ConcurrentKafkaListenerContainerFactory singleFactory( - ConsumerFactory consumerFactory) { + ConsumerFactory consumerFactory, + ObjectProvider< + ContainerCustomizer< + String, String, ConcurrentMessageListenerContainer>> + customizerProvider) { ConcurrentKafkaListenerContainerFactory factory = new ConcurrentKafkaListenerContainerFactory<>(); // do not retry failed records @@ -60,6 +72,7 @@ public ConcurrentKafkaListenerContainerFactory singleFactory( factory.setConsumerFactory(consumerFactory); factory.setBatchListener(false); factory.setAutoStartup(true); + customizerProvider.ifAvailable(factory::setContainerCustomizer); return factory; } } diff --git a/instrumentation/spring/spring-rabbit-1.0/javaagent/build.gradle.kts b/instrumentation/spring/spring-rabbit-1.0/javaagent/build.gradle.kts index 09f0bf372934..cf21a5dfcfd9 100644 --- a/instrumentation/spring/spring-rabbit-1.0/javaagent/build.gradle.kts +++ b/instrumentation/spring/spring-rabbit-1.0/javaagent/build.gradle.kts @@ -25,6 +25,14 @@ dependencies { tasks { test { - usesService(gradle.sharedServices.registrations["testcontainersBuildService"].getService()) + usesService(gradle.sharedServices.registrations["testcontainersBuildService"].service) + } +} + +configurations.testRuntimeClasspath { + resolutionStrategy { + // requires old logback (and therefore also old slf4j) + force("ch.qos.logback:logback-classic:1.2.11") + force("org.slf4j:slf4j-api:1.7.36") } } diff --git a/instrumentation/spring/spring-rabbit-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/rabbit/SpringRabbitIgnoredTypesConfigurer.java b/instrumentation/spring/spring-rabbit-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/rabbit/SpringRabbitIgnoredTypesConfigurer.java index a2825918bba7..4fe168ed16f3 100644 --- a/instrumentation/spring/spring-rabbit-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/rabbit/SpringRabbitIgnoredTypesConfigurer.java +++ b/instrumentation/spring/spring-rabbit-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/rabbit/SpringRabbitIgnoredTypesConfigurer.java @@ -13,7 +13,7 @@ @AutoService(IgnoredTypesConfigurer.class) public class SpringRabbitIgnoredTypesConfigurer implements IgnoredTypesConfigurer { @Override - public void configure(ConfigProperties config, IgnoredTypesBuilder builder) { + public void configure(IgnoredTypesBuilder builder, ConfigProperties config) { // contains a Runnable that servers as a worker that continuously reads messages from queue builder .ignoreClass("org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer$") diff --git a/instrumentation/spring/spring-rabbit-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/rabbit/SpringRabbitMessageAttributesGetter.java b/instrumentation/spring/spring-rabbit-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/rabbit/SpringRabbitMessageAttributesGetter.java index 41a92aaadb9d..b5b712edc701 100644 --- a/instrumentation/spring/spring-rabbit-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/rabbit/SpringRabbitMessageAttributesGetter.java +++ b/instrumentation/spring/spring-rabbit-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/rabbit/SpringRabbitMessageAttributesGetter.java @@ -6,6 +6,9 @@ package io.opentelemetry.javaagent.instrumentation.spring.rabbit; import io.opentelemetry.instrumentation.api.instrumenter.messaging.MessagingAttributesGetter; +import java.nio.charset.StandardCharsets; +import java.util.Collections; +import java.util.List; import javax.annotation.Nullable; import org.springframework.amqp.core.Message; @@ -73,4 +76,28 @@ public Long messagePayloadCompressedSize(Message message) { public String messageId(Message message, @Nullable Void unused) { return message.getMessageProperties().getMessageId(); } + + @Override + public List header(Message message, String name) { + Object value = message.getMessageProperties().getHeaders().get(name); + if (value != null) { + return Collections.singletonList(value.toString()); + } + return Collections.emptyList(); + } + + @Nullable + @Override + public String messagePayload(Message message) { + if (message == null) { + return null; + } + + byte[] body = message.getBody(); + if (body == null) { + return null; + } + + return new String(body, StandardCharsets.UTF_8); + } } diff --git a/instrumentation/spring/spring-rabbit-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/rabbit/SpringRabbitSingletons.java b/instrumentation/spring/spring-rabbit-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/rabbit/SpringRabbitSingletons.java index 6808fd6001f4..b63ac6763a43 100644 --- a/instrumentation/spring/spring-rabbit-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/rabbit/SpringRabbitSingletons.java +++ b/instrumentation/spring/spring-rabbit-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/rabbit/SpringRabbitSingletons.java @@ -10,6 +10,7 @@ import io.opentelemetry.instrumentation.api.instrumenter.messaging.MessageOperation; import io.opentelemetry.instrumentation.api.instrumenter.messaging.MessagingAttributesExtractor; import io.opentelemetry.instrumentation.api.instrumenter.messaging.MessagingSpanNameExtractor; +import io.opentelemetry.javaagent.bootstrap.internal.ExperimentalConfig; import org.springframework.amqp.core.Message; public final class SpringRabbitSingletons { @@ -27,8 +28,11 @@ public final class SpringRabbitSingletons { GlobalOpenTelemetry.get(), INSTRUMENTATION_NAME, MessagingSpanNameExtractor.create(getter, operation)) - .addAttributesExtractor(MessagingAttributesExtractor.create(getter, operation)) - .newConsumerInstrumenter(MessageHeaderGetter.INSTANCE); + .addAttributesExtractor( + MessagingAttributesExtractor.builder(getter, operation) + .setCapturedHeaders(ExperimentalConfig.get().getMessagingHeaders()) + .build()) + .buildConsumerInstrumenter(MessageHeaderGetter.INSTANCE); } public static Instrumenter instrumenter() { diff --git a/instrumentation/spring/spring-rabbit-1.0/javaagent/src/test/groovy/ContextPropagationTest.groovy b/instrumentation/spring/spring-rabbit-1.0/javaagent/src/test/groovy/ContextPropagationTest.groovy index 0b4379f626f2..d6ac0b38d713 100644 --- a/instrumentation/spring/spring-rabbit-1.0/javaagent/src/test/groovy/ContextPropagationTest.groovy +++ b/instrumentation/spring/spring-rabbit-1.0/javaagent/src/test/groovy/ContextPropagationTest.groovy @@ -7,7 +7,10 @@ import com.rabbitmq.client.ConnectionFactory import io.opentelemetry.instrumentation.test.AgentInstrumentationSpecification import io.opentelemetry.instrumentation.testing.GlobalTraceUtil import io.opentelemetry.semconv.trace.attributes.SemanticAttributes +import org.springframework.amqp.AmqpException import org.springframework.amqp.core.AmqpTemplate +import org.springframework.amqp.core.Message +import org.springframework.amqp.core.MessagePostProcessor import org.springframework.amqp.core.Queue import org.springframework.amqp.rabbit.annotation.RabbitListener import org.springframework.boot.SpringApplication @@ -20,8 +23,8 @@ import org.testcontainers.containers.wait.strategy.Wait import spock.lang.Shared import java.time.Duration +import spock.lang.Unroll -import static com.google.common.net.InetAddresses.isInetAddress import static io.opentelemetry.api.trace.SpanKind.CLIENT import static io.opentelemetry.api.trace.SpanKind.CONSUMER import static io.opentelemetry.api.trace.SpanKind.PRODUCER @@ -62,15 +65,27 @@ class ContextPropagationTest extends AgentInstrumentationSpecification { applicationContext?.close() } - def "should propagate context to consumer"() { + @Unroll + def "should propagate context to consumer, test headers: #testHeaders"() { given: def connection = connectionFactory.newConnection() def channel = connection.createChannel() when: runWithSpan("parent") { - applicationContext.getBean(AmqpTemplate) - .convertAndSend(ConsumerConfig.TEST_QUEUE, "test") + if (testHeaders) { + applicationContext.getBean(AmqpTemplate) + .convertAndSend(ConsumerConfig.TEST_QUEUE, (Object) "test payload", new MessagePostProcessor() { + @Override + Message postProcessMessage(Message message) throws AmqpException { + message.getMessageProperties().setHeader("test-message-header", "test") + return message + } + }) + } else { + applicationContext.getBean(AmqpTemplate) + .convertAndSend(ConsumerConfig.TEST_QUEUE, "test payload") + } } then: @@ -85,15 +100,17 @@ class ContextPropagationTest extends AgentInstrumentationSpecification { kind PRODUCER childOf span(0) attributes { - // "localhost" on linux, null on windows - "$SemanticAttributes.NET_PEER_NAME" { it == "localhost" || it == null } - "$SemanticAttributes.NET_PEER_IP" { isInetAddress(it as String) } - "$SemanticAttributes.NET_PEER_PORT" Long + "$SemanticAttributes.NET_SOCK_PEER_ADDR" { it == "127.0.0.1" || it == null } + "$SemanticAttributes.NET_SOCK_PEER_PORT" Long "$SemanticAttributes.MESSAGING_SYSTEM" "rabbitmq" "$SemanticAttributes.MESSAGING_DESTINATION" "" "$SemanticAttributes.MESSAGING_DESTINATION_KIND" "queue" "$SemanticAttributes.MESSAGING_MESSAGE_PAYLOAD_SIZE_BYTES" Long "$SemanticAttributes.MESSAGING_RABBITMQ_ROUTING_KEY" String + if (testHeaders) { + "messaging.header.test_message_header" { it == ["test"] } + } + "messaging.payload" "test payload" } } // spring-cloud-stream-binder-rabbit listener puts all messages into a BlockingQueue immediately after receiving @@ -110,6 +127,10 @@ class ContextPropagationTest extends AgentInstrumentationSpecification { "$SemanticAttributes.MESSAGING_OPERATION" "process" "$SemanticAttributes.MESSAGING_MESSAGE_PAYLOAD_SIZE_BYTES" Long "$SemanticAttributes.MESSAGING_RABBITMQ_ROUTING_KEY" String + if (testHeaders) { + "messaging.header.test_message_header" { it == ["test"] } + } + "messaging.payload" "test payload" } } span(3) { @@ -123,6 +144,10 @@ class ContextPropagationTest extends AgentInstrumentationSpecification { "$SemanticAttributes.MESSAGING_DESTINATION_KIND" "queue" "$SemanticAttributes.MESSAGING_OPERATION" "process" "$SemanticAttributes.MESSAGING_MESSAGE_PAYLOAD_SIZE_BYTES" Long + if (testHeaders) { + "messaging.header.test_message_header" { it == ["test"] } + } + "messaging.payload" "test payload" } } span(4) { @@ -136,10 +161,8 @@ class ContextPropagationTest extends AgentInstrumentationSpecification { name "basic.ack" kind CLIENT attributes { - // "localhost" on linux, null on windows - "$SemanticAttributes.NET_PEER_NAME" { it == "localhost" || it == null } - "$SemanticAttributes.NET_PEER_IP" { isInetAddress(it as String) } - "$SemanticAttributes.NET_PEER_PORT" Long + "$SemanticAttributes.NET_SOCK_PEER_ADDR" { it == "127.0.0.1" || it == null } + "$SemanticAttributes.NET_SOCK_PEER_PORT" Long "$SemanticAttributes.MESSAGING_SYSTEM" "rabbitmq" "$SemanticAttributes.MESSAGING_DESTINATION_KIND" "queue" } @@ -150,6 +173,9 @@ class ContextPropagationTest extends AgentInstrumentationSpecification { cleanup: channel?.close() connection?.close() + + where: + testHeaders << [false, true] } @SpringBootConfiguration diff --git a/instrumentation/spring/spring-rmi-4.0/javaagent/build.gradle.kts b/instrumentation/spring/spring-rmi-4.0/javaagent/build.gradle.kts index f601432a9cb7..ae12de7ae749 100644 --- a/instrumentation/spring/spring-rmi-4.0/javaagent/build.gradle.kts +++ b/instrumentation/spring/spring-rmi-4.0/javaagent/build.gradle.kts @@ -25,3 +25,11 @@ dependencies { tasks.withType().configureEach { jvmArgs("-Djava.rmi.server.hostname=127.0.0.1") } + +configurations.testRuntimeClasspath { + resolutionStrategy { + // requires old logback (and therefore also old slf4j) + force("ch.qos.logback:logback-classic:1.2.11") + force("org.slf4j:slf4j-api:1.7.36") + } +} diff --git a/instrumentation/spring/spring-rmi-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/springrmi/SpringRmiSingletons.java b/instrumentation/spring/spring-rmi-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/springrmi/SpringRmiSingletons.java index a8d17a43366f..feb629aaa939 100644 --- a/instrumentation/spring/spring-rmi-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/springrmi/SpringRmiSingletons.java +++ b/instrumentation/spring/spring-rmi-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/springrmi/SpringRmiSingletons.java @@ -11,7 +11,7 @@ import io.opentelemetry.instrumentation.api.instrumenter.rpc.RpcClientAttributesExtractor; import io.opentelemetry.instrumentation.api.instrumenter.rpc.RpcServerAttributesExtractor; import io.opentelemetry.instrumentation.api.instrumenter.rpc.RpcSpanNameExtractor; -import io.opentelemetry.instrumentation.api.util.ClassAndMethod; +import io.opentelemetry.instrumentation.api.instrumenter.util.ClassAndMethod; import io.opentelemetry.javaagent.instrumentation.springrmi.client.ClientAttributesGetter; import io.opentelemetry.javaagent.instrumentation.springrmi.server.ServerAttributesGetter; import java.lang.reflect.Method; @@ -31,7 +31,7 @@ private static Instrumenter buildClientInstrumenter() { INSTRUMENTATION_NAME, RpcSpanNameExtractor.create(rpcAttributesGetter)) .addAttributesExtractor(RpcClientAttributesExtractor.create(rpcAttributesGetter)) - .newInstrumenter(SpanKindExtractor.alwaysClient()); + .buildInstrumenter(SpanKindExtractor.alwaysClient()); } private static Instrumenter buildServerInstrumenter() { @@ -42,7 +42,7 @@ private static Instrumenter buildServerInstrumenter() { INSTRUMENTATION_NAME, RpcSpanNameExtractor.create(rpcAttributesGetter)) .addAttributesExtractor(RpcServerAttributesExtractor.create(rpcAttributesGetter)) - .newInstrumenter(SpanKindExtractor.alwaysServer()); + .buildInstrumenter(SpanKindExtractor.alwaysServer()); } public static Instrumenter clientInstrumenter() { diff --git a/instrumentation/spring/spring-rmi-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/springrmi/client/ClientInstrumentation.java b/instrumentation/spring/spring-rmi-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/springrmi/client/ClientInstrumentation.java index 133ca2a52c19..ddf04a9babd9 100644 --- a/instrumentation/spring/spring-rmi-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/springrmi/client/ClientInstrumentation.java +++ b/instrumentation/spring/spring-rmi-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/springrmi/client/ClientInstrumentation.java @@ -5,8 +5,6 @@ package io.opentelemetry.javaagent.instrumentation.springrmi.client; -import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.extendsClass; -import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed; import static io.opentelemetry.javaagent.instrumentation.springrmi.SpringRmiSingletons.clientInstrumenter; import static net.bytebuddy.matcher.ElementMatchers.isMethod; import static net.bytebuddy.matcher.ElementMatchers.named; @@ -24,14 +22,10 @@ import org.aopalliance.intercept.MethodInvocation; public class ClientInstrumentation implements TypeInstrumentation { - @Override - public ElementMatcher classLoaderOptimization() { - return hasClassesNamed("org.springframework.remoting.rmi.RmiClientInterceptor"); - } @Override public ElementMatcher typeMatcher() { - return extendsClass(named("org.springframework.remoting.rmi.RmiClientInterceptor")); + return named("org.springframework.remoting.rmi.RmiClientInterceptor"); } @Override diff --git a/instrumentation/spring/spring-rmi-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/springrmi/server/ServerAttributesGetter.java b/instrumentation/spring/spring-rmi-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/springrmi/server/ServerAttributesGetter.java index 9e55688b0dda..885ddef3bfd5 100644 --- a/instrumentation/spring/spring-rmi-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/springrmi/server/ServerAttributesGetter.java +++ b/instrumentation/spring/spring-rmi-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/springrmi/server/ServerAttributesGetter.java @@ -6,7 +6,7 @@ package io.opentelemetry.javaagent.instrumentation.springrmi.server; import io.opentelemetry.instrumentation.api.instrumenter.rpc.RpcAttributesGetter; -import io.opentelemetry.instrumentation.api.util.ClassAndMethod; +import io.opentelemetry.instrumentation.api.instrumenter.util.ClassAndMethod; public enum ServerAttributesGetter implements RpcAttributesGetter { INSTANCE; diff --git a/instrumentation/spring/spring-rmi-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/springrmi/server/ServerInstrumentation.java b/instrumentation/spring/spring-rmi-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/springrmi/server/ServerInstrumentation.java index b75514da612e..eec0e4049dfd 100644 --- a/instrumentation/spring/spring-rmi-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/springrmi/server/ServerInstrumentation.java +++ b/instrumentation/spring/spring-rmi-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/springrmi/server/ServerInstrumentation.java @@ -13,7 +13,7 @@ import io.opentelemetry.context.Context; import io.opentelemetry.context.Scope; -import io.opentelemetry.instrumentation.api.util.ClassAndMethod; +import io.opentelemetry.instrumentation.api.instrumenter.util.ClassAndMethod; import io.opentelemetry.javaagent.bootstrap.CallDepth; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; 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 a1a20e6093f6..91ee94cf095c 100644 --- a/instrumentation/spring/spring-scheduling-3.1/javaagent/build.gradle.kts +++ b/instrumentation/spring/spring-scheduling-3.1/javaagent/build.gradle.kts @@ -17,3 +17,12 @@ dependencies { library("org.springframework:spring-context:3.1.0.RELEASE") testLibrary("org.springframework:spring-context:3.2.3.RELEASE") } + +tasks.withType().configureEach { + // TODO run tests both with and without experimental span attributes + jvmArgs("-Dotel.instrumentation.spring-scheduling.experimental-span-attributes=true") + + // required on jdk17 + jvmArgs("--add-opens=java.base/java.lang=ALL-UNNAMED") + jvmArgs("-XX:+IgnoreUnrecognizedVMOptions") +} diff --git a/instrumentation/spring/spring-scheduling-3.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/scheduling/SpringSchedulingSingletons.java b/instrumentation/spring/spring-scheduling-3.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/scheduling/SpringSchedulingSingletons.java index 8636b285a8de..de2f253d3ce3 100644 --- a/instrumentation/spring/spring-scheduling-3.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/scheduling/SpringSchedulingSingletons.java +++ b/instrumentation/spring/spring-scheduling-3.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/scheduling/SpringSchedulingSingletons.java @@ -6,24 +6,39 @@ package io.opentelemetry.javaagent.instrumentation.spring.scheduling; import io.opentelemetry.api.GlobalOpenTelemetry; +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; +import io.opentelemetry.instrumentation.api.instrumenter.InstrumenterBuilder; import io.opentelemetry.instrumentation.api.instrumenter.code.CodeAttributesExtractor; import io.opentelemetry.instrumentation.api.instrumenter.code.CodeSpanNameExtractor; +import io.opentelemetry.javaagent.bootstrap.internal.InstrumentationConfig; public final class SpringSchedulingSingletons { + private static final boolean CAPTURE_EXPERIMENTAL_SPAN_ATTRIBUTES = + InstrumentationConfig.get() + .getBoolean("otel.instrumentation.spring-scheduling.experimental-span-attributes", false); + private static final Instrumenter INSTRUMENTER; static { SpringSchedulingCodeAttributesGetter codeAttributesGetter = new SpringSchedulingCodeAttributesGetter(); - INSTRUMENTER = + + InstrumenterBuilder builder = Instrumenter.builder( GlobalOpenTelemetry.get(), "io.opentelemetry.spring-scheduling-3.1", CodeSpanNameExtractor.create(codeAttributesGetter)) - .addAttributesExtractor(CodeAttributesExtractor.create(codeAttributesGetter)) - .newInstrumenter(); + .addAttributesExtractor(CodeAttributesExtractor.create(codeAttributesGetter)); + + if (CAPTURE_EXPERIMENTAL_SPAN_ATTRIBUTES) { + builder.addAttributesExtractor( + AttributesExtractor.constant(AttributeKey.stringKey("job.system"), "spring_scheduling")); + } + + INSTRUMENTER = builder.buildInstrumenter(); } public static Instrumenter instrumenter() { diff --git a/instrumentation/spring/spring-scheduling-3.1/javaagent/src/test/groovy/SpringSchedulingTest.groovy b/instrumentation/spring/spring-scheduling-3.1/javaagent/src/test/groovy/SpringSchedulingTest.groovy index 32f5a6077dd0..47cdf10dd3a6 100644 --- a/instrumentation/spring/spring-scheduling-3.1/javaagent/src/test/groovy/SpringSchedulingTest.groovy +++ b/instrumentation/spring/spring-scheduling-3.1/javaagent/src/test/groovy/SpringSchedulingTest.groovy @@ -26,6 +26,7 @@ class SpringSchedulingTest extends AgentInstrumentationSpecification { name "TriggerTask.run" hasNoParent() attributes { + "job.system" "spring_scheduling" "code.namespace" "TriggerTask" "code.function" "run" } @@ -49,6 +50,7 @@ class SpringSchedulingTest extends AgentInstrumentationSpecification { name "IntervalTask.run" hasNoParent() attributes { + "job.system" "spring_scheduling" "code.namespace" "IntervalTask" "code.function" "run" } @@ -68,10 +70,11 @@ class SpringSchedulingTest extends AgentInstrumentationSpecification { assertTraces(1) { trace(0, 1) { span(0) { - nameContains "LambdaTaskConfigurer\$\$Lambda\$" + name "LambdaTaskConfigurer\$\$Lambda\$.run" hasNoParent() attributes { - "code.namespace" { it.contains("LambdaTaskConfigurer\$\$Lambda\$") } + "job.system" "spring_scheduling" + "code.namespace" { it.startsWith("LambdaTaskConfigurer\$\$Lambda\$") } "code.function" "run" } } @@ -98,6 +101,7 @@ class SpringSchedulingTest extends AgentInstrumentationSpecification { name "EnhancedClassTaskConfig.run" hasNoParent() attributes { + "job.system" "spring_scheduling" "code.namespace" "EnhancedClassTaskConfig" "code.function" "run" } diff --git a/instrumentation/spring/spring-web-3.1/library/README.md b/instrumentation/spring/spring-web-3.1/library/README.md index a78cfc402946..2576508c70f3 100644 --- a/instrumentation/spring/spring-web-3.1/library/README.md +++ b/instrumentation/spring/spring-web-3.1/library/README.md @@ -1,4 +1,4 @@ -# Manual Instrumentation for Spring-Web +# Library Instrumentation for Spring Web version 3.1 and higher Provides OpenTelemetry instrumentation for Spring's RestTemplate. @@ -9,14 +9,12 @@ Provides OpenTelemetry instrumentation for Spring's RestTemplate. Replace `SPRING_VERSION` with the version of spring you're using. `Minimum version: 3.1` -Replace `OPENTELEMETRY_VERSION` with the latest -stable [release](https://mvnrepository.com/artifact/io.opentelemetry). -`Minimum version: 1.4.0` +Replace `OPENTELEMETRY_VERSION` with the [latest +release](https://search.maven.org/search?q=g:io.opentelemetry.instrumentation%20AND%20a:opentelemetry-spring-web-3.1). -For Maven add to your `pom.xml`: +For Maven, add to your `pom.xml` dependencies: ```xml - @@ -43,7 +41,7 @@ For Maven add to your `pom.xml`: ``` -For Gradle add to your dependencies: +For Gradle, add to your dependencies: ```groovy implementation("io.opentelemetry.instrumentation:opentelemetry-spring-web-3.1:OPENTELEMETRY_VERSION") diff --git a/instrumentation/spring/spring-web-3.1/library/src/main/java/io/opentelemetry/instrumentation/spring/web/SpringWebHttpAttributesGetter.java b/instrumentation/spring/spring-web-3.1/library/src/main/java/io/opentelemetry/instrumentation/spring/web/SpringWebHttpAttributesGetter.java index b18574fd50dd..50eaf7062f04 100644 --- a/instrumentation/spring/spring-web-3.1/library/src/main/java/io/opentelemetry/instrumentation/spring/web/SpringWebHttpAttributesGetter.java +++ b/instrumentation/spring/spring-web-3.1/library/src/main/java/io/opentelemetry/instrumentation/spring/web/SpringWebHttpAttributesGetter.java @@ -35,20 +35,6 @@ public List requestHeader(HttpRequest httpRequest, String name) { return httpRequest.getHeaders().getOrDefault(name, emptyList()); } - @Override - @Nullable - public Long requestContentLength( - HttpRequest httpRequest, @Nullable ClientHttpResponse clientHttpResponse) { - return null; - } - - @Override - @Nullable - public Long requestContentLengthUncompressed( - HttpRequest httpRequest, @Nullable ClientHttpResponse clientHttpResponse) { - return null; - } - @Override @Nullable public String flavor(HttpRequest httpRequest, @Nullable ClientHttpResponse clientHttpResponse) { @@ -56,7 +42,8 @@ public String flavor(HttpRequest httpRequest, @Nullable ClientHttpResponse clien } @Override - public Integer statusCode(HttpRequest httpRequest, ClientHttpResponse clientHttpResponse) { + public Integer statusCode( + HttpRequest httpRequest, ClientHttpResponse clientHttpResponse, @Nullable Throwable error) { try { return clientHttpResponse.getStatusCode().value(); } catch (IOException e) { @@ -64,20 +51,6 @@ public Integer statusCode(HttpRequest httpRequest, ClientHttpResponse clientHttp } } - @Override - @Nullable - public Long responseContentLength( - HttpRequest httpRequest, ClientHttpResponse clientHttpResponse) { - return null; - } - - @Override - @Nullable - public Long responseContentLengthUncompressed( - HttpRequest httpRequest, ClientHttpResponse clientHttpResponse) { - return null; - } - @Override public List responseHeader( HttpRequest httpRequest, ClientHttpResponse clientHttpResponse, String name) { diff --git a/instrumentation/spring/spring-web-3.1/library/src/main/java/io/opentelemetry/instrumentation/spring/web/SpringWebNetAttributesGetter.java b/instrumentation/spring/spring-web-3.1/library/src/main/java/io/opentelemetry/instrumentation/spring/web/SpringWebNetAttributesGetter.java index e3f49cb78cdf..4adaa197bb8d 100644 --- a/instrumentation/spring/spring-web-3.1/library/src/main/java/io/opentelemetry/instrumentation/spring/web/SpringWebNetAttributesGetter.java +++ b/instrumentation/spring/spring-web-3.1/library/src/main/java/io/opentelemetry/instrumentation/spring/web/SpringWebNetAttributesGetter.java @@ -13,6 +13,7 @@ final class SpringWebNetAttributesGetter implements NetClientAttributesGetter { + @Override public String transport(HttpRequest httpRequest, @Nullable ClientHttpResponse response) { return SemanticAttributes.NetTransportValues.IP_TCP; @@ -20,18 +21,12 @@ public String transport(HttpRequest httpRequest, @Nullable ClientHttpResponse re @Override @Nullable - public String peerName(HttpRequest httpRequest, @Nullable ClientHttpResponse response) { + public String peerName(HttpRequest httpRequest) { return httpRequest.getURI().getHost(); } @Override - public Integer peerPort(HttpRequest httpRequest, @Nullable ClientHttpResponse response) { + public Integer peerPort(HttpRequest httpRequest) { return httpRequest.getURI().getPort(); } - - @Override - @Nullable - public String peerIp(HttpRequest httpRequest, @Nullable ClientHttpResponse response) { - return null; - } } diff --git a/instrumentation/spring/spring-web-3.1/library/src/main/java/io/opentelemetry/instrumentation/spring/web/SpringWebTelemetryBuilder.java b/instrumentation/spring/spring-web-3.1/library/src/main/java/io/opentelemetry/instrumentation/spring/web/SpringWebTelemetryBuilder.java index 6b02fc8da7ac..80c5d56e7b01 100644 --- a/instrumentation/spring/spring-web-3.1/library/src/main/java/io/opentelemetry/instrumentation/spring/web/SpringWebTelemetryBuilder.java +++ b/instrumentation/spring/spring-web-3.1/library/src/main/java/io/opentelemetry/instrumentation/spring/web/SpringWebTelemetryBuilder.java @@ -5,6 +5,7 @@ package io.opentelemetry.instrumentation.spring.web; +import com.google.errorprone.annotations.CanIgnoreReturnValue; import io.opentelemetry.api.OpenTelemetry; import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; @@ -38,6 +39,7 @@ public final class SpringWebTelemetryBuilder { * Adds an additional {@link AttributesExtractor} to invoke to set attributes to instrumented * items. */ + @CanIgnoreReturnValue public SpringWebTelemetryBuilder addAttributesExtractor( AttributesExtractor attributesExtractor) { additionalExtractors.add(attributesExtractor); @@ -49,6 +51,7 @@ public SpringWebTelemetryBuilder addAttributesExtractor( * * @param requestHeaders A list of HTTP header names. */ + @CanIgnoreReturnValue public SpringWebTelemetryBuilder setCapturedRequestHeaders(List requestHeaders) { httpAttributesExtractorBuilder.setCapturedRequestHeaders(requestHeaders); return this; @@ -59,6 +62,7 @@ public SpringWebTelemetryBuilder setCapturedRequestHeaders(List requestH * * @param responseHeaders A list of HTTP header names. */ + @CanIgnoreReturnValue public SpringWebTelemetryBuilder setCapturedResponseHeaders(List responseHeaders) { httpAttributesExtractorBuilder.setCapturedResponseHeaders(responseHeaders); return this; @@ -82,7 +86,7 @@ public SpringWebTelemetry build() { .addAttributesExtractor(NetClientAttributesExtractor.create(netAttributesGetter)) .addAttributesExtractors(additionalExtractors) .addOperationMetrics(HttpClientMetrics.get()) - .newClientInstrumenter(HttpRequestSetter.INSTANCE); + .buildClientInstrumenter(HttpRequestSetter.INSTANCE); return new SpringWebTelemetry(instrumenter); } diff --git a/instrumentation/spring/spring-web-3.1/library/src/test/groovy/SpringWebInstrumentationTest.groovy b/instrumentation/spring/spring-web-3.1/library/src/test/groovy/SpringWebInstrumentationTest.groovy deleted file mode 100644 index 4d12600e3b9f..000000000000 --- a/instrumentation/spring/spring-web-3.1/library/src/test/groovy/SpringWebInstrumentationTest.groovy +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -import io.opentelemetry.api.common.AttributeKey -import io.opentelemetry.instrumentation.spring.web.SpringWebTelemetry -import io.opentelemetry.instrumentation.test.LibraryTestTrait -import io.opentelemetry.instrumentation.test.base.HttpClientTest -import io.opentelemetry.instrumentation.testing.junit.http.AbstractHttpClientTest -import io.opentelemetry.semconv.trace.attributes.SemanticAttributes -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 -import spock.lang.Shared - -class SpringWebInstrumentationTest extends HttpClientTest> implements LibraryTestTrait { - @Shared - RestTemplate restTemplate - - def setupSpec() { - if (restTemplate == null) { - def requestFactory = new SimpleClientHttpRequestFactory() - requestFactory.setConnectTimeout(CONNECT_TIMEOUT_MS) - restTemplate = new RestTemplate(requestFactory) - restTemplate.getInterceptors().add(SpringWebTelemetry.create(getOpenTelemetry()).newInterceptor()) - } - } - - @Override - HttpEntity buildRequest(String method, URI uri, Map headers) { - def httpHeaders = new HttpHeaders() - headers.each { httpHeaders.put(it.key, [it.value]) } - return new HttpEntity(httpHeaders) - } - - @Override - int sendRequest(HttpEntity request, String method, URI uri, Map headers) { - try { - return restTemplate.exchange(uri, HttpMethod.valueOf(method), request, String) - .statusCode - .value() - } catch (ResourceAccessException exception) { - throw exception.getCause() - } - } - - @Override - void sendRequestWithCallback(HttpEntity request, String method, URI uri, Map headers, AbstractHttpClientTest.RequestResult requestResult) { - try { - restTemplate.execute(uri, HttpMethod.valueOf(method), { req -> - headers.forEach(req.getHeaders().&add) - }, { response -> - requestResult.complete(response.statusCode.value()) - }) - } catch (ResourceAccessException exception) { - requestResult.complete(exception.getCause()) - } - } - - @Override - boolean testCircularRedirects() { - false - } - - @Override - Set> httpAttributes(URI uri) { - def attributes = super.httpAttributes(uri) - attributes.remove(SemanticAttributes.HTTP_FLAVOR) - attributes.add(SemanticAttributes.HTTP_SCHEME) - attributes - } -} diff --git a/instrumentation/spring/spring-web-3.1/library/src/test/java/io/opentelemetry/instrumentation/spring/web/SpringWebInstrumentationTest.java b/instrumentation/spring/spring-web-3.1/library/src/test/java/io/opentelemetry/instrumentation/spring/web/SpringWebInstrumentationTest.java new file mode 100644 index 000000000000..ad980068841f --- /dev/null +++ b/instrumentation/spring/spring-web-3.1/library/src/test/java/io/opentelemetry/instrumentation/spring/web/SpringWebInstrumentationTest.java @@ -0,0 +1,99 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.spring.web; + +import static java.util.Collections.singletonList; + +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.http.AbstractHttpClientTest; +import io.opentelemetry.instrumentation.testing.junit.http.HttpClientInstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.http.HttpClientTestOptions; +import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; +import java.net.URI; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import org.junit.jupiter.api.BeforeAll; +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; + +public class SpringWebInstrumentationTest extends AbstractHttpClientTest> { + + @RegisterExtension + static final InstrumentationExtension testing = HttpClientInstrumentationExtension.forLibrary(); + + static RestTemplate restTemplate; + + @BeforeAll + static void setUp() { + SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory(); + requestFactory.setConnectTimeout((int) CONNECTION_TIMEOUT.toMillis()); + restTemplate = new RestTemplate(requestFactory); + restTemplate + .getInterceptors() + .add(SpringWebTelemetry.create(testing.getOpenTelemetry()).newInterceptor()); + } + + @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 restTemplate + .exchange(uri, 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, + AbstractHttpClientTest.RequestResult requestResult) { + try { + restTemplate.execute( + uri, + HttpMethod.valueOf(method), + req -> headers.forEach(req.getHeaders()::add), + response -> { + requestResult.complete(response.getStatusCode().value()); + return null; + }); + } catch (ResourceAccessException exception) { + requestResult.complete(exception.getCause()); + } + } + + @Override + protected void configure(HttpClientTestOptions options) { + options.disableTestCircularRedirects(); + options.setHttpAttributes( + uri -> { + Set> attributes = + new HashSet<>(HttpClientTestOptions.DEFAULT_HTTP_ATTRIBUTES); + attributes.remove(SemanticAttributes.HTTP_FLAVOR); + return attributes; + }); + } +} diff --git a/instrumentation/spring/spring-web-3.1/library/src/test/java/io/opentelemetry/instrumentation/spring/web/SpringWebTelemetryTest.java b/instrumentation/spring/spring-web-3.1/library/src/test/java/io/opentelemetry/instrumentation/spring/web/SpringWebTelemetryTest.java deleted file mode 100644 index 7ba551ba4cfb..000000000000 --- a/instrumentation/spring/spring-web-3.1/library/src/test/java/io/opentelemetry/instrumentation/spring/web/SpringWebTelemetryTest.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.instrumentation.spring.web; - -import static io.opentelemetry.sdk.testing.assertj.TracesAssert.assertThat; -import static org.mockito.BDDMockito.then; - -import io.opentelemetry.api.trace.SpanKind; -import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension; -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; -import org.springframework.http.HttpRequest; -import org.springframework.http.client.ClientHttpRequestExecution; -import org.springframework.http.client.ClientHttpRequestInterceptor; - -@ExtendWith(MockitoExtension.class) -class SpringWebTelemetryTest { - @RegisterExtension - static final LibraryInstrumentationExtension testing = LibraryInstrumentationExtension.create(); - - @Mock HttpRequest httpRequestMock; - @Mock ClientHttpRequestExecution requestExecutionMock; - static final byte[] requestBody = new byte[0]; - - @Test - void shouldSkipWhenContextHasClientSpan() throws Exception { - // given - ClientHttpRequestInterceptor interceptor = - SpringWebTelemetry.create(testing.getOpenTelemetry()).newInterceptor(); - - // when - testing.runWithHttpClientSpan( - "parent", - () -> { - interceptor.intercept(httpRequestMock, requestBody, requestExecutionMock); - }); - - // then - then(requestExecutionMock).should().execute(httpRequestMock, requestBody); - - assertThat(testing.waitForTraces(1)) - .hasTracesSatisfyingExactly( - trace -> - trace.hasSpansSatisfyingExactly( - span -> span.hasName("parent").hasKind(SpanKind.CLIENT))); - } -} diff --git a/instrumentation/spring/spring-web-3.1/testing/src/test/groovy/SpringRestTemplateTest.groovy b/instrumentation/spring/spring-web-3.1/testing/src/test/groovy/SpringRestTemplateTest.groovy deleted file mode 100644 index a09a822f0359..000000000000 --- a/instrumentation/spring/spring-web-3.1/testing/src/test/groovy/SpringRestTemplateTest.groovy +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -import io.opentelemetry.instrumentation.test.AgentTestTrait -import io.opentelemetry.instrumentation.test.base.HttpClientTest -import io.opentelemetry.instrumentation.testing.junit.http.AbstractHttpClientTest -import org.springframework.http.HttpEntity -import org.springframework.http.HttpHeaders -import org.springframework.http.HttpMethod -import org.springframework.http.client.ClientHttpRequestFactory -import org.springframework.http.client.SimpleClientHttpRequestFactory -import org.springframework.web.client.ResourceAccessException -import org.springframework.web.client.RestTemplate -import spock.lang.Shared - -class SpringRestTemplateTest extends HttpClientTest> implements AgentTestTrait { - - @Shared - ClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory() - @Shared - RestTemplate restTemplate = new RestTemplate(factory) - - def setupSpec() { - factory.connectTimeout = CONNECT_TIMEOUT_MS - } - - @Override - HttpEntity buildRequest(String method, URI uri, Map headers) { - def httpHeaders = new HttpHeaders() - headers.each { httpHeaders.put(it.key, [it.value]) } - return new HttpEntity(httpHeaders) - } - - @Override - int sendRequest(HttpEntity request, String method, URI uri, Map headers) { - try { - return restTemplate.exchange(uri, HttpMethod.valueOf(method), request, String) - .statusCode - .value() - } catch (ResourceAccessException exception) { - throw exception.getCause() - } - } - - @Override - void sendRequestWithCallback(HttpEntity request, String method, URI uri, Map headers = [:], AbstractHttpClientTest.RequestResult requestResult) { - try { - restTemplate.execute(uri, HttpMethod.valueOf(method), { req -> - req.getHeaders().putAll(request.getHeaders()) - }, { response -> - // read request body to avoid broken pipe errors on the server side - byte[] buffer = new byte[1024] - try (InputStream inputStream = response.body) { - while (inputStream.read(buffer) >= 0) { - } - } - requestResult.complete(response.statusCode.value()) - }) - } catch (ResourceAccessException exception) { - requestResult.complete(exception.getCause()) - } - } - - @Override - int maxRedirects() { - 20 - } - - @Override - Integer responseCodeOnRedirectError() { - return 302 - } -} diff --git a/instrumentation/spring/spring-web-3.1/testing/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/web/SpringRestTemplateTest.java b/instrumentation/spring/spring-web-3.1/testing/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/web/SpringRestTemplateTest.java new file mode 100644 index 000000000000..9fae1840f5b0 --- /dev/null +++ b/instrumentation/spring/spring-web-3.1/testing/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/web/SpringRestTemplateTest.java @@ -0,0 +1,91 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.spring.web; + +import static java.util.Collections.singletonList; + +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.http.AbstractHttpClientTest; +import io.opentelemetry.instrumentation.testing.junit.http.HttpClientInstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.http.HttpClientTestOptions; +import java.io.InputStream; +import java.net.URI; +import java.util.Map; +import org.junit.jupiter.api.BeforeAll; +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; + +public class SpringRestTemplateTest extends AbstractHttpClientTest> { + + @RegisterExtension + static final InstrumentationExtension testing = HttpClientInstrumentationExtension.forAgent(); + + static RestTemplate restTemplate; + + @BeforeAll + static void setUp() { + SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory(); + factory.setConnectTimeout((int) CONNECTION_TIMEOUT.toMillis()); + restTemplate = new RestTemplate(factory); + } + + @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 restTemplate + .exchange(uri, 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, + AbstractHttpClientTest.RequestResult requestResult) { + try { + restTemplate.execute( + uri, + 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) {} + } + requestResult.complete(response.getStatusCode().value()); + return null; + }); + } catch (ResourceAccessException exception) { + requestResult.complete(exception.getCause()); + } + } + + @Override + protected void configure(HttpClientTestOptions options) { + options.setMaxRedirects(20); + options.setResponseCodeOnRedirectError(302); + } +} diff --git a/instrumentation/spring/spring-webflux-5.0/javaagent/build.gradle.kts b/instrumentation/spring/spring-webflux-5.0/javaagent/build.gradle.kts index f9899791d83e..3d612fdf614d 100644 --- a/instrumentation/spring/spring-webflux-5.0/javaagent/build.gradle.kts +++ b/instrumentation/spring/spring-webflux-5.0/javaagent/build.gradle.kts @@ -62,6 +62,17 @@ dependencies { tasks.withType().configureEach { // TODO run tests both with and without experimental span attributes jvmArgs("-Dotel.instrumentation.spring-webflux.experimental-span-attributes=true") + // required on jdk17 + jvmArgs("--add-opens=java.base/java.lang=ALL-UNNAMED") + jvmArgs("-XX:+IgnoreUnrecognizedVMOptions") systemProperty("testLatestDeps", findProperty("testLatestDeps") as Boolean) } + +configurations.testRuntimeClasspath { + resolutionStrategy { + // requires old logback (and therefore also old slf4j) + force("ch.qos.logback:logback-classic:1.2.11") + force("org.slf4j:slf4j-api:1.7.36") + } +} diff --git a/instrumentation/spring/spring-webflux-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/webflux/SpringWebfluxConfig.java b/instrumentation/spring/spring-webflux-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/webflux/SpringWebfluxConfig.java index 7094567cfcec..5f13c856157c 100644 --- a/instrumentation/spring/spring-webflux-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/webflux/SpringWebfluxConfig.java +++ b/instrumentation/spring/spring-webflux-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/webflux/SpringWebfluxConfig.java @@ -7,7 +7,7 @@ import io.opentelemetry.javaagent.bootstrap.internal.InstrumentationConfig; -public class SpringWebfluxConfig { +public final class SpringWebfluxConfig { private static final boolean CAPTURE_EXPERIMENTAL_SPAN_ATTRIBUTES = InstrumentationConfig.get() @@ -16,4 +16,6 @@ public class SpringWebfluxConfig { public static boolean captureExperimentalSpanAttributes() { return CAPTURE_EXPERIMENTAL_SPAN_ATTRIBUTES; } + + private SpringWebfluxConfig() {} } diff --git a/instrumentation/spring/spring-webflux-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/webflux/client/WebClientHelper.java b/instrumentation/spring/spring-webflux-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/webflux/client/WebClientHelper.java index 4bc8c4ff991b..aae44e64e506 100644 --- a/instrumentation/spring/spring-webflux-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/webflux/client/WebClientHelper.java +++ b/instrumentation/spring/spring-webflux-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/webflux/client/WebClientHelper.java @@ -6,15 +6,24 @@ package io.opentelemetry.javaagent.instrumentation.spring.webflux.client; import io.opentelemetry.api.GlobalOpenTelemetry; +import io.opentelemetry.instrumentation.api.instrumenter.net.PeerServiceAttributesExtractor; import io.opentelemetry.instrumentation.spring.webflux.client.SpringWebfluxTelemetry; +import io.opentelemetry.instrumentation.spring.webflux.client.internal.SpringWebfluxNetAttributesGetter; +import io.opentelemetry.javaagent.bootstrap.internal.CommonConfig; import io.opentelemetry.javaagent.bootstrap.internal.InstrumentationConfig; import java.util.List; import org.springframework.web.reactive.function.client.ExchangeFilterFunction; -public class WebClientHelper { +public final class WebClientHelper { private static final SpringWebfluxTelemetry INSTRUMENTATION = SpringWebfluxTelemetry.builder(GlobalOpenTelemetry.get()) + .setCapturedRequestHeaders(CommonConfig.get().getClientRequestHeaders()) + .setCapturedResponseHeaders(CommonConfig.get().getClientResponseHeaders()) + .addAttributesExtractor( + PeerServiceAttributesExtractor.create( + new SpringWebfluxNetAttributesGetter(), + CommonConfig.get().getPeerServiceMapping())) .setCaptureExperimentalSpanAttributes( InstrumentationConfig.get() .getBoolean( @@ -24,4 +33,6 @@ public class WebClientHelper { public static void addFilter(List exchangeFilterFunctions) { INSTRUMENTATION.addClientTracingFilter(exchangeFilterFunctions); } + + private WebClientHelper() {} } diff --git a/instrumentation/spring/spring-webflux-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/webflux/server/AdviceUtils.java b/instrumentation/spring/spring-webflux-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/webflux/server/AdviceUtils.java index 6d24e95666d7..9edcf9fd0106 100644 --- a/instrumentation/spring/spring-webflux-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/webflux/server/AdviceUtils.java +++ b/instrumentation/spring/spring-webflux-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/webflux/server/AdviceUtils.java @@ -8,25 +8,14 @@ import static io.opentelemetry.javaagent.instrumentation.spring.webflux.server.WebfluxSingletons.instrumenter; import io.opentelemetry.context.Context; -import io.opentelemetry.instrumentation.api.util.ClassNames; import javax.annotation.Nullable; import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Mono; -public class AdviceUtils { +public final class AdviceUtils { public static final String ON_SPAN_END = AdviceUtils.class.getName() + ".Context"; - public static String spanNameForHandler(Object handler) { - String className = ClassNames.simpleName(handler.getClass()); - int lambdaIdx = className.indexOf("$$Lambda$"); - - if (lambdaIdx > -1) { - return className.substring(0, lambdaIdx) + ".lambda"; - } - return className + ".handle"; - } - public static void registerOnSpanEnd( ServerWebExchange exchange, Context context, Object handler) { exchange @@ -53,4 +42,6 @@ private static void end(ServerWebExchange exchange, @Nullable Throwable throwabl interface OnSpanEnd { void end(Throwable throwable); } + + private AdviceUtils() {} } diff --git a/instrumentation/spring/spring-webflux-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/webflux/server/HandlerCodeAttributesGetter.java b/instrumentation/spring/spring-webflux-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/webflux/server/HandlerCodeAttributesGetter.java new file mode 100644 index 000000000000..7ff5f25f88be --- /dev/null +++ b/instrumentation/spring/spring-webflux-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/webflux/server/HandlerCodeAttributesGetter.java @@ -0,0 +1,23 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.spring.webflux.server; + +import io.opentelemetry.instrumentation.api.instrumenter.code.CodeAttributesGetter; +import javax.annotation.Nullable; + +public class HandlerCodeAttributesGetter implements CodeAttributesGetter { + @Nullable + @Override + public Class codeClass(Object handler) { + return handler.getClass(); + } + + @Nullable + @Override + public String methodName(Object handler) { + return "handle"; + } +} diff --git a/instrumentation/spring/spring-webflux-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/webflux/server/WebfluxSingletons.java b/instrumentation/spring/spring-webflux-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/webflux/server/WebfluxSingletons.java index b028743f3784..7a79e8b1e8aa 100644 --- a/instrumentation/spring/spring-webflux-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/webflux/server/WebfluxSingletons.java +++ b/instrumentation/spring/spring-webflux-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/webflux/server/WebfluxSingletons.java @@ -30,7 +30,9 @@ public final class WebfluxSingletons { } INSTRUMENTER = - builder.setEnabled(ExperimentalConfig.get().controllerTelemetryEnabled()).newInstrumenter(); + builder + .setEnabled(ExperimentalConfig.get().controllerTelemetryEnabled()) + .buildInstrumenter(); } public static Instrumenter instrumenter() { @@ -39,9 +41,14 @@ public static Instrumenter instrumenter() { public static HttpRouteGetter httpRouteGetter() { return (context, exchange) -> { - PathPattern bestPattern = - exchange.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE); - return bestPattern == null ? null : bestPattern.getPatternString(); + Object bestPatternObj = exchange.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE); + if (bestPatternObj == null) { + return null; + } + if (bestPatternObj instanceof PathPattern) { + return ((PathPattern) bestPatternObj).getPatternString(); + } + return bestPatternObj.toString(); }; } diff --git a/instrumentation/spring/spring-webflux-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/webflux/server/WebfluxSpanNameExtractor.java b/instrumentation/spring/spring-webflux-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/webflux/server/WebfluxSpanNameExtractor.java index 80e6299a49fa..ce495dc04e6d 100644 --- a/instrumentation/spring/spring-webflux-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/webflux/server/WebfluxSpanNameExtractor.java +++ b/instrumentation/spring/spring-webflux-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/webflux/server/WebfluxSpanNameExtractor.java @@ -6,10 +6,15 @@ package io.opentelemetry.javaagent.instrumentation.spring.webflux.server; import io.opentelemetry.instrumentation.api.instrumenter.SpanNameExtractor; -import io.opentelemetry.instrumentation.api.util.SpanNames; +import io.opentelemetry.instrumentation.api.instrumenter.code.CodeSpanNameExtractor; +import io.opentelemetry.instrumentation.api.instrumenter.util.SpanNames; import org.springframework.web.method.HandlerMethod; public class WebfluxSpanNameExtractor implements SpanNameExtractor { + + private final SpanNameExtractor handlerSpanNameExtractor = + CodeSpanNameExtractor.create(new HandlerCodeAttributesGetter()); + @Override public String extract(Object handler) { if (handler instanceof HandlerMethod) { @@ -17,7 +22,7 @@ public String extract(Object handler) { HandlerMethod handlerMethod = (HandlerMethod) handler; return SpanNames.fromMethod(handlerMethod.getMethod()); } else { - return AdviceUtils.spanNameForHandler(handler); + return handlerSpanNameExtractor.extract(handler); } } } diff --git a/instrumentation/spring/spring-webflux-5.0/javaagent/src/test/groovy/SpringWebfluxTest.groovy b/instrumentation/spring/spring-webflux-5.0/javaagent/src/test/groovy/SpringWebfluxTest.groovy index bc60631e4309..7cb61ce83b1c 100644 --- a/instrumentation/spring/spring-webflux-5.0/javaagent/src/test/groovy/SpringWebfluxTest.groovy +++ b/instrumentation/spring/spring-webflux-5.0/javaagent/src/test/groovy/SpringWebfluxTest.groovy @@ -81,10 +81,11 @@ class SpringWebfluxTest extends AgentInstrumentationSpecification { hasNoParent() attributes { "$SemanticAttributes.NET_TRANSPORT" IP_TCP - "$SemanticAttributes.NET_PEER_NAME" { it == null || it == "localhost" } - "$SemanticAttributes.NET_PEER_IP" "127.0.0.1" - "$SemanticAttributes.NET_PEER_PORT" Long - "$SemanticAttributes.HTTP_HOST" { it == "localhost" || it == "localhost:${port}" } + "$SemanticAttributes.NET_SOCK_PEER_ADDR" "127.0.0.1" + "$SemanticAttributes.NET_SOCK_PEER_PORT" Long + "$SemanticAttributes.NET_SOCK_HOST_ADDR" "127.0.0.1" + "$SemanticAttributes.NET_HOST_NAME" "localhost" + "$SemanticAttributes.NET_HOST_PORT" Long "$SemanticAttributes.HTTP_TARGET" urlPath "$SemanticAttributes.HTTP_METHOD" "GET" "$SemanticAttributes.HTTP_STATUS_CODE" 200 @@ -92,6 +93,8 @@ class SpringWebfluxTest extends AgentInstrumentationSpecification { "$SemanticAttributes.HTTP_FLAVOR" "1.1" "$SemanticAttributes.HTTP_USER_AGENT" String "$SemanticAttributes.HTTP_ROUTE" urlPathWithVariables + "$SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH" { it == null || it instanceof Long } + "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" { it == null || it instanceof Long } } } span(1) { @@ -148,10 +151,11 @@ class SpringWebfluxTest extends AgentInstrumentationSpecification { hasNoParent() attributes { "$SemanticAttributes.NET_TRANSPORT" IP_TCP - "$SemanticAttributes.NET_PEER_NAME" { it == null || it == "localhost" } - "$SemanticAttributes.NET_PEER_IP" "127.0.0.1" - "$SemanticAttributes.NET_PEER_PORT" Long - "$SemanticAttributes.HTTP_HOST" { it == "localhost" || it == "localhost:${port}" } + "$SemanticAttributes.NET_SOCK_PEER_ADDR" "127.0.0.1" + "$SemanticAttributes.NET_SOCK_PEER_PORT" Long + "$SemanticAttributes.NET_SOCK_HOST_ADDR" "127.0.0.1" + "$SemanticAttributes.NET_HOST_NAME" "localhost" + "$SemanticAttributes.NET_HOST_PORT" Long "$SemanticAttributes.HTTP_TARGET" urlPath "$SemanticAttributes.HTTP_METHOD" "GET" "$SemanticAttributes.HTTP_STATUS_CODE" 200 @@ -159,6 +163,8 @@ class SpringWebfluxTest extends AgentInstrumentationSpecification { "$SemanticAttributes.HTTP_FLAVOR" "1.1" "$SemanticAttributes.HTTP_USER_AGENT" String "$SemanticAttributes.HTTP_ROUTE" urlPathWithVariables + "$SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH" { it == null || it instanceof Long } + "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" { it == null || it instanceof Long } } } span(1) { @@ -235,10 +241,11 @@ class SpringWebfluxTest extends AgentInstrumentationSpecification { hasNoParent() attributes { "$SemanticAttributes.NET_TRANSPORT" IP_TCP - "$SemanticAttributes.NET_PEER_NAME" { it == null || it == "localhost" } - "$SemanticAttributes.NET_PEER_IP" "127.0.0.1" - "$SemanticAttributes.NET_PEER_PORT" Long - "$SemanticAttributes.HTTP_HOST" { it == "localhost" || it == "localhost:${port}" } + "$SemanticAttributes.NET_SOCK_PEER_ADDR" "127.0.0.1" + "$SemanticAttributes.NET_SOCK_PEER_PORT" Long + "$SemanticAttributes.NET_SOCK_HOST_ADDR" "127.0.0.1" + "$SemanticAttributes.NET_HOST_NAME" "localhost" + "$SemanticAttributes.NET_HOST_PORT" Long "$SemanticAttributes.HTTP_TARGET" urlPath "$SemanticAttributes.HTTP_METHOD" "GET" "$SemanticAttributes.HTTP_STATUS_CODE" 200 @@ -246,6 +253,8 @@ class SpringWebfluxTest extends AgentInstrumentationSpecification { "$SemanticAttributes.HTTP_FLAVOR" "1.1" "$SemanticAttributes.HTTP_USER_AGENT" String "$SemanticAttributes.HTTP_ROUTE" urlPathWithVariables + "$SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH" { it == null || it instanceof Long } + "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" { it == null || it instanceof Long } } } span(1) { @@ -300,10 +309,11 @@ class SpringWebfluxTest extends AgentInstrumentationSpecification { status UNSET attributes { "$SemanticAttributes.NET_TRANSPORT" IP_TCP - "$SemanticAttributes.NET_PEER_NAME" { it == null || it == "localhost" } - "$SemanticAttributes.NET_PEER_IP" "127.0.0.1" - "$SemanticAttributes.NET_PEER_PORT" Long - "$SemanticAttributes.HTTP_HOST" { it == "localhost" || it == "localhost:${port}" } + "$SemanticAttributes.NET_SOCK_PEER_ADDR" "127.0.0.1" + "$SemanticAttributes.NET_SOCK_PEER_PORT" Long + "$SemanticAttributes.NET_SOCK_HOST_ADDR" "127.0.0.1" + "$SemanticAttributes.NET_HOST_NAME" "localhost" + "$SemanticAttributes.NET_HOST_PORT" Long "$SemanticAttributes.HTTP_TARGET" "/notfoundgreet" "$SemanticAttributes.HTTP_METHOD" "GET" "$SemanticAttributes.HTTP_STATUS_CODE" 404 @@ -311,6 +321,8 @@ class SpringWebfluxTest extends AgentInstrumentationSpecification { "$SemanticAttributes.HTTP_FLAVOR" "1.1" "$SemanticAttributes.HTTP_USER_AGENT" String "$SemanticAttributes.HTTP_ROUTE" "/**" + "$SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH" { it == null || it instanceof Long } + "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" { it == null || it instanceof Long } } } span(1) { @@ -344,10 +356,11 @@ class SpringWebfluxTest extends AgentInstrumentationSpecification { hasNoParent() attributes { "$SemanticAttributes.NET_TRANSPORT" IP_TCP - "$SemanticAttributes.NET_PEER_NAME" { it == null || it == "localhost" } - "$SemanticAttributes.NET_PEER_IP" "127.0.0.1" - "$SemanticAttributes.NET_PEER_PORT" Long - "$SemanticAttributes.HTTP_HOST" { it == "localhost" || it == "localhost:${port}" } + "$SemanticAttributes.NET_SOCK_PEER_ADDR" "127.0.0.1" + "$SemanticAttributes.NET_SOCK_PEER_PORT" Long + "$SemanticAttributes.NET_SOCK_HOST_ADDR" "127.0.0.1" + "$SemanticAttributes.NET_HOST_NAME" "localhost" + "$SemanticAttributes.NET_HOST_PORT" Long "$SemanticAttributes.HTTP_TARGET" "/echo" "$SemanticAttributes.HTTP_METHOD" "POST" "$SemanticAttributes.HTTP_STATUS_CODE" 202 @@ -355,6 +368,8 @@ class SpringWebfluxTest extends AgentInstrumentationSpecification { "$SemanticAttributes.HTTP_FLAVOR" "1.1" "$SemanticAttributes.HTTP_USER_AGENT" String "$SemanticAttributes.HTTP_ROUTE" "/echo" + "$SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH" { it == null || it instanceof Long } + "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" { it == null || it instanceof Long } } } span(1) { @@ -393,10 +408,11 @@ class SpringWebfluxTest extends AgentInstrumentationSpecification { hasNoParent() attributes { "$SemanticAttributes.NET_TRANSPORT" IP_TCP - "$SemanticAttributes.NET_PEER_NAME" { it == null || it == "localhost" } - "$SemanticAttributes.NET_PEER_IP" "127.0.0.1" - "$SemanticAttributes.NET_PEER_PORT" Long - "$SemanticAttributes.HTTP_HOST" { it == "localhost" || it == "localhost:${port}" } + "$SemanticAttributes.NET_SOCK_PEER_ADDR" "127.0.0.1" + "$SemanticAttributes.NET_SOCK_PEER_PORT" Long + "$SemanticAttributes.NET_SOCK_HOST_ADDR" "127.0.0.1" + "$SemanticAttributes.NET_HOST_NAME" "localhost" + "$SemanticAttributes.NET_HOST_PORT" Long "$SemanticAttributes.HTTP_TARGET" urlPath "$SemanticAttributes.HTTP_METHOD" "GET" "$SemanticAttributes.HTTP_STATUS_CODE" 500 @@ -404,6 +420,8 @@ class SpringWebfluxTest extends AgentInstrumentationSpecification { "$SemanticAttributes.HTTP_FLAVOR" "1.1" "$SemanticAttributes.HTTP_USER_AGENT" String "$SemanticAttributes.HTTP_ROUTE" urlPathWithVariables + "$SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH" { it == null || it instanceof Long } + "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" { it == null || it instanceof Long } } } span(1) { @@ -457,10 +475,11 @@ class SpringWebfluxTest extends AgentInstrumentationSpecification { hasNoParent() attributes { "$SemanticAttributes.NET_TRANSPORT" IP_TCP - "$SemanticAttributes.NET_PEER_NAME" { it == null || it == "localhost" } - "$SemanticAttributes.NET_PEER_IP" "127.0.0.1" - "$SemanticAttributes.NET_PEER_PORT" Long - "$SemanticAttributes.HTTP_HOST" { it == "localhost" || it == "localhost:${port}" } + "$SemanticAttributes.NET_SOCK_PEER_ADDR" "127.0.0.1" + "$SemanticAttributes.NET_SOCK_PEER_PORT" Long + "$SemanticAttributes.NET_SOCK_HOST_ADDR" "127.0.0.1" + "$SemanticAttributes.NET_HOST_NAME" "localhost" + "$SemanticAttributes.NET_HOST_PORT" Long "$SemanticAttributes.HTTP_TARGET" "/double-greet-redirect" "$SemanticAttributes.HTTP_METHOD" "GET" "$SemanticAttributes.HTTP_STATUS_CODE" 307 @@ -468,17 +487,16 @@ class SpringWebfluxTest extends AgentInstrumentationSpecification { "$SemanticAttributes.HTTP_FLAVOR" "1.1" "$SemanticAttributes.HTTP_USER_AGENT" String "$SemanticAttributes.HTTP_ROUTE" "/double-greet-redirect" + "$SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH" { it == null || it instanceof Long } + "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" { it == null || it instanceof Long } } } span(1) { - name "RedirectComponent.lambda" + name "RedirectComponent\$\$Lambda\$.handle" kind INTERNAL childOf span(0) attributes { - "spring-webflux.handler.type" { String tagVal -> - return (tagVal.contains(INNER_HANDLER_FUNCTION_CLASS_TAG_PREFIX) - || tagVal.contains("Lambda")) - } + "spring-webflux.handler.type" { it.startsWith("server.RedirectComponent\$\$Lambda\$") } } } } @@ -489,10 +507,11 @@ class SpringWebfluxTest extends AgentInstrumentationSpecification { hasNoParent() attributes { "$SemanticAttributes.NET_TRANSPORT" IP_TCP - "$SemanticAttributes.NET_PEER_NAME" { it == null || it == "localhost" } - "$SemanticAttributes.NET_PEER_IP" "127.0.0.1" - "$SemanticAttributes.NET_PEER_PORT" Long - "$SemanticAttributes.HTTP_HOST" { it == "localhost" || it == "localhost:${port}" } + "$SemanticAttributes.NET_SOCK_PEER_ADDR" "127.0.0.1" + "$SemanticAttributes.NET_SOCK_PEER_PORT" Long + "$SemanticAttributes.NET_SOCK_HOST_ADDR" "127.0.0.1" + "$SemanticAttributes.NET_HOST_NAME" "localhost" + "$SemanticAttributes.NET_HOST_PORT" Long "$SemanticAttributes.HTTP_TARGET" "/double-greet" "$SemanticAttributes.HTTP_METHOD" "GET" "$SemanticAttributes.HTTP_STATUS_CODE" 200 @@ -500,6 +519,8 @@ class SpringWebfluxTest extends AgentInstrumentationSpecification { "$SemanticAttributes.HTTP_FLAVOR" "1.1" "$SemanticAttributes.HTTP_USER_AGENT" String "$SemanticAttributes.HTTP_ROUTE" "/double-greet" + "$SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH" { it == null || it instanceof Long } + "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" { it == null || it instanceof Long } } } span(1) { @@ -536,10 +557,11 @@ class SpringWebfluxTest extends AgentInstrumentationSpecification { hasNoParent() attributes { "$SemanticAttributes.NET_TRANSPORT" IP_TCP - "$SemanticAttributes.NET_PEER_NAME" { it == null || it == "localhost" } - "$SemanticAttributes.NET_PEER_IP" "127.0.0.1" - "$SemanticAttributes.NET_PEER_PORT" Long - "$SemanticAttributes.HTTP_HOST" { it == "localhost" || it == "localhost:${port}" } + "$SemanticAttributes.NET_SOCK_PEER_ADDR" "127.0.0.1" + "$SemanticAttributes.NET_SOCK_PEER_PORT" Long + "$SemanticAttributes.NET_SOCK_HOST_ADDR" "127.0.0.1" + "$SemanticAttributes.NET_HOST_NAME" "localhost" + "$SemanticAttributes.NET_HOST_PORT" Long "$SemanticAttributes.HTTP_TARGET" urlPath "$SemanticAttributes.HTTP_METHOD" "GET" "$SemanticAttributes.HTTP_STATUS_CODE" 200 @@ -547,6 +569,8 @@ class SpringWebfluxTest extends AgentInstrumentationSpecification { "$SemanticAttributes.HTTP_FLAVOR" "1.1" "$SemanticAttributes.HTTP_USER_AGENT" String "$SemanticAttributes.HTTP_ROUTE" urlPathWithVariables + "$SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH" { it == null || it instanceof Long } + "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" { it == null || it instanceof Long } } } span(1) { diff --git a/instrumentation/spring/spring-webflux-5.0/javaagent/src/test/groovy/client/SpringWebfluxHttpClientTest.groovy b/instrumentation/spring/spring-webflux-5.0/javaagent/src/test/groovy/client/SpringWebfluxHttpClientTest.groovy index 9c2317293aee..674a5da83c4a 100644 --- a/instrumentation/spring/spring-webflux-5.0/javaagent/src/test/groovy/client/SpringWebfluxHttpClientTest.groovy +++ b/instrumentation/spring/spring-webflux-5.0/javaagent/src/test/groovy/client/SpringWebfluxHttpClientTest.groovy @@ -80,6 +80,11 @@ class SpringWebfluxHttpClientTest extends HttpClientTest> httpAttributes(URI uri) { def attributes = super.httpAttributes(uri) diff --git a/instrumentation/spring/spring-webflux-5.0/javaagent/src/test/groovy/server/base/HandlerSpringWebFluxServerTest.groovy b/instrumentation/spring/spring-webflux-5.0/javaagent/src/test/groovy/server/base/HandlerSpringWebFluxServerTest.groovy index 3a1ee6f3ddfe..172b03942c02 100644 --- a/instrumentation/spring/spring-webflux-5.0/javaagent/src/test/groovy/server/base/HandlerSpringWebFluxServerTest.groovy +++ b/instrumentation/spring/spring-webflux-5.0/javaagent/src/test/groovy/server/base/HandlerSpringWebFluxServerTest.groovy @@ -18,7 +18,7 @@ import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint abstract class HandlerSpringWebFluxServerTest extends SpringWebFluxServerTest { @Override void handlerSpan(TraceAssert trace, int index, Object parent, String method, ServerEndpoint endpoint) { - def handlerSpanName = "${ServerTestRouteFactory.simpleName}.lambda" + def handlerSpanName = "${ServerTestRouteFactory.simpleName}\$\$Lambda\$.handle" if (endpoint == NOT_FOUND) { handlerSpanName = "ResourceWebHandler.handle" } diff --git a/instrumentation/spring/spring-webflux-5.0/library/README.md b/instrumentation/spring/spring-webflux-5.0/library/README.md index e7bc99d1ca23..710771a11113 100644 --- a/instrumentation/spring/spring-webflux-5.0/library/README.md +++ b/instrumentation/spring/spring-webflux-5.0/library/README.md @@ -1,4 +1,4 @@ -# Manual Instrumentation for Spring Webflux +# Library Instrumentation for Spring Webflux version 5.0 and higher Provides OpenTelemetry instrumentation for Spring's `WebClient`. @@ -9,10 +9,10 @@ Provides OpenTelemetry instrumentation for Spring's `WebClient`. Replace `SPRING_VERSION` with the version of spring you're using. `Minimum version: 5.0` -Replace `OPENTELEMETRY_VERSION` with the latest stable [release](https://mvnrepository.com/artifact/io.opentelemetry). -`Minimum version: 1.8.0` +Replace `OPENTELEMETRY_VERSION` with the [latest +release](https://search.maven.org/search?q=g:io.opentelemetry.instrumentation%20AND%20a:opentelemetry-spring-webflux-5.0). -For Maven add to your `pom.xml`: +For Maven, add to your `pom.xml` dependencies: ```xml @@ -42,7 +42,7 @@ For Maven add to your `pom.xml`: ``` -For Gradle add to your dependencies: +For Gradle, add to your dependencies: ```groovy // opentelemetry instrumentation diff --git a/instrumentation/spring/spring-webflux-5.0/library/src/main/java/io/opentelemetry/instrumentation/spring/webflux/client/SpringWebfluxHttpAttributesGetter.java b/instrumentation/spring/spring-webflux-5.0/library/src/main/java/io/opentelemetry/instrumentation/spring/webflux/client/SpringWebfluxHttpAttributesGetter.java index 6f673424b102..47933ed91b4b 100644 --- a/instrumentation/spring/spring-webflux-5.0/library/src/main/java/io/opentelemetry/instrumentation/spring/webflux/client/SpringWebfluxHttpAttributesGetter.java +++ b/instrumentation/spring/spring-webflux-5.0/library/src/main/java/io/opentelemetry/instrumentation/spring/webflux/client/SpringWebfluxHttpAttributesGetter.java @@ -55,21 +55,9 @@ public List requestHeader(ClientRequest request, String name) { return request.headers().getOrDefault(name, emptyList()); } - @Nullable - @Override - public Long requestContentLength(ClientRequest request, @Nullable ClientResponse response) { - return null; - } - - @Nullable - @Override - public Long requestContentLengthUncompressed( - ClientRequest request, @Nullable ClientResponse response) { - return null; - } - @Override - public Integer statusCode(ClientRequest request, ClientResponse response) { + public Integer statusCode( + ClientRequest request, ClientResponse response, @Nullable Throwable error) { if (RAW_STATUS_CODE != null) { // rawStatusCode() method was introduced in webflux 5.1 try { @@ -83,18 +71,6 @@ public Integer statusCode(ClientRequest request, ClientResponse response) { return response.statusCode().value(); } - @Nullable - @Override - public Long responseContentLength(ClientRequest request, ClientResponse response) { - return null; - } - - @Nullable - @Override - public Long responseContentLengthUncompressed(ClientRequest request, ClientResponse response) { - return null; - } - @Override public List responseHeader(ClientRequest request, ClientResponse response, String name) { return response.headers().header(name); diff --git a/instrumentation/spring/spring-webflux-5.0/library/src/main/java/io/opentelemetry/instrumentation/spring/webflux/client/SpringWebfluxTelemetryBuilder.java b/instrumentation/spring/spring-webflux-5.0/library/src/main/java/io/opentelemetry/instrumentation/spring/webflux/client/SpringWebfluxTelemetryBuilder.java index 2444f8b8bb76..2491c84ee8ca 100644 --- a/instrumentation/spring/spring-webflux-5.0/library/src/main/java/io/opentelemetry/instrumentation/spring/webflux/client/SpringWebfluxTelemetryBuilder.java +++ b/instrumentation/spring/spring-webflux-5.0/library/src/main/java/io/opentelemetry/instrumentation/spring/webflux/client/SpringWebfluxTelemetryBuilder.java @@ -7,17 +7,18 @@ import static io.opentelemetry.instrumentation.api.instrumenter.SpanKindExtractor.alwaysClient; +import com.google.errorprone.annotations.CanIgnoreReturnValue; import io.opentelemetry.api.OpenTelemetry; import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; import io.opentelemetry.instrumentation.api.instrumenter.InstrumenterBuilder; -import io.opentelemetry.instrumentation.api.instrumenter.PeerServiceAttributesExtractor; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpClientAttributesExtractor; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpClientAttributesExtractorBuilder; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpClientMetrics; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanNameExtractor; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanStatusExtractor; import io.opentelemetry.instrumentation.api.instrumenter.net.NetClientAttributesExtractor; +import io.opentelemetry.instrumentation.spring.webflux.client.internal.SpringWebfluxNetAttributesGetter; import java.util.ArrayList; import java.util.List; import org.springframework.web.reactive.function.client.ClientRequest; @@ -43,6 +44,7 @@ public final class SpringWebfluxTelemetryBuilder { * Adds an additional {@link AttributesExtractor} to invoke to set attributes to instrumented * items. */ + @CanIgnoreReturnValue public SpringWebfluxTelemetryBuilder addAttributesExtractor( AttributesExtractor attributesExtractor) { additionalExtractors.add(attributesExtractor); @@ -54,6 +56,7 @@ public SpringWebfluxTelemetryBuilder addAttributesExtractor( * * @param requestHeaders A list of HTTP header names. */ + @CanIgnoreReturnValue public SpringWebfluxTelemetryBuilder setCapturedRequestHeaders(List requestHeaders) { httpAttributesExtractorBuilder.setCapturedRequestHeaders(requestHeaders); return this; @@ -64,6 +67,7 @@ public SpringWebfluxTelemetryBuilder setCapturedRequestHeaders(List requ * * @param responseHeaders A list of HTTP header names. */ + @CanIgnoreReturnValue public SpringWebfluxTelemetryBuilder setCapturedResponseHeaders(List responseHeaders) { httpAttributesExtractorBuilder.setCapturedResponseHeaders(responseHeaders); return this; @@ -74,6 +78,7 @@ public SpringWebfluxTelemetryBuilder setCapturedResponseHeaders(List res * removed in the future, so only enable this if you know you do not require attributes filled by * this instrumentation to be stable across versions. */ + @CanIgnoreReturnValue public SpringWebfluxTelemetryBuilder setCaptureExperimentalSpanAttributes( boolean captureExperimentalSpanAttributes) { this.captureExperimentalSpanAttributes = captureExperimentalSpanAttributes; @@ -87,9 +92,7 @@ public SpringWebfluxTelemetryBuilder setCaptureExperimentalSpanAttributes( public SpringWebfluxTelemetry build() { SpringWebfluxHttpAttributesGetter httpAttributesGetter = SpringWebfluxHttpAttributesGetter.INSTANCE; - SpringWebfluxNetAttributesGetter attributesGetter = new SpringWebfluxNetAttributesGetter(); - NetClientAttributesExtractor attributesExtractor = - NetClientAttributesExtractor.create(attributesGetter); + SpringWebfluxNetAttributesGetter netAttributesGetter = new SpringWebfluxNetAttributesGetter(); InstrumenterBuilder builder = Instrumenter.builder( @@ -98,8 +101,7 @@ public SpringWebfluxTelemetry build() { HttpSpanNameExtractor.create(httpAttributesGetter)) .setSpanStatusExtractor(HttpSpanStatusExtractor.create(httpAttributesGetter)) .addAttributesExtractor(httpAttributesExtractorBuilder.build()) - .addAttributesExtractor(attributesExtractor) - .addAttributesExtractor(PeerServiceAttributesExtractor.create(attributesGetter)) + .addAttributesExtractor(NetClientAttributesExtractor.create(netAttributesGetter)) .addAttributesExtractors(additionalExtractors) .addOperationMetrics(HttpClientMetrics.get()); @@ -109,7 +111,7 @@ public SpringWebfluxTelemetry build() { // headers are injected elsewhere; ClientRequest is immutable Instrumenter instrumenter = - builder.newInstrumenter(alwaysClient()); + builder.buildInstrumenter(alwaysClient()); return new SpringWebfluxTelemetry(instrumenter, openTelemetry.getPropagators()); } diff --git a/instrumentation/spring/spring-webflux-5.0/library/src/main/java/io/opentelemetry/instrumentation/spring/webflux/client/SpringWebfluxNetAttributesGetter.java b/instrumentation/spring/spring-webflux-5.0/library/src/main/java/io/opentelemetry/instrumentation/spring/webflux/client/internal/SpringWebfluxNetAttributesGetter.java similarity index 65% rename from instrumentation/spring/spring-webflux-5.0/library/src/main/java/io/opentelemetry/instrumentation/spring/webflux/client/SpringWebfluxNetAttributesGetter.java rename to instrumentation/spring/spring-webflux-5.0/library/src/main/java/io/opentelemetry/instrumentation/spring/webflux/client/internal/SpringWebfluxNetAttributesGetter.java index 04517b075374..dc5eb21111a2 100644 --- a/instrumentation/spring/spring-webflux-5.0/library/src/main/java/io/opentelemetry/instrumentation/spring/webflux/client/SpringWebfluxNetAttributesGetter.java +++ b/instrumentation/spring/spring-webflux-5.0/library/src/main/java/io/opentelemetry/instrumentation/spring/webflux/client/internal/SpringWebfluxNetAttributesGetter.java @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.instrumentation.spring.webflux.client; +package io.opentelemetry.instrumentation.spring.webflux.client.internal; import io.opentelemetry.instrumentation.api.instrumenter.net.NetClientAttributesGetter; import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; @@ -11,7 +11,11 @@ import org.springframework.web.reactive.function.client.ClientRequest; import org.springframework.web.reactive.function.client.ClientResponse; -final class SpringWebfluxNetAttributesGetter +/** + * This class is internal and is hence not for public use. Its APIs are unstable and can change at + * any time. + */ +public final class SpringWebfluxNetAttributesGetter implements NetClientAttributesGetter { @Override @@ -21,18 +25,12 @@ public String transport(ClientRequest request, @Nullable ClientResponse response @Nullable @Override - public String peerName(ClientRequest request, @Nullable ClientResponse response) { + public String peerName(ClientRequest request) { return request.url().getHost(); } @Override - public Integer peerPort(ClientRequest request, @Nullable ClientResponse response) { + public Integer peerPort(ClientRequest request) { return request.url().getPort(); } - - @Nullable - @Override - public String peerIp(ClientRequest request, @Nullable ClientResponse response) { - return null; - } } diff --git a/instrumentation/spring/spring-webmvc-3.1/javaagent/build.gradle.kts b/instrumentation/spring/spring-webmvc-3.1/javaagent/build.gradle.kts index fa5da8baee8d..f615912ca43e 100644 --- a/instrumentation/spring/spring-webmvc-3.1/javaagent/build.gradle.kts +++ b/instrumentation/spring/spring-webmvc-3.1/javaagent/build.gradle.kts @@ -17,8 +17,6 @@ muzzle { } } -val versions: Map by project - dependencies { bootstrap(project(":instrumentation:servlet:servlet-common:bootstrap")) @@ -36,7 +34,7 @@ dependencies { testImplementation("javax.validation:validation-api:1.1.0.Final") testImplementation("org.hibernate:hibernate-validator:5.4.2.Final") - testImplementation("org.spockframework:spock-spring:${versions["org.spockframework"]}") + testImplementation("org.spockframework:spock-spring") testLibrary("org.springframework.boot:spring-boot-starter-test:1.5.17.RELEASE") testLibrary("org.springframework.boot:spring-boot-starter-web:1.5.17.RELEASE") @@ -52,4 +50,15 @@ dependencies { tasks.withType().configureEach { // TODO run tests both with and without experimental span attributes jvmArgs("-Dotel.instrumentation.spring-webmvc.experimental-span-attributes=true") + // required on jdk17 + jvmArgs("--add-opens=java.base/java.lang=ALL-UNNAMED") + jvmArgs("-XX:+IgnoreUnrecognizedVMOptions") +} + +configurations.testRuntimeClasspath { + resolutionStrategy { + // requires old logback (and therefore also old slf4j) + force("ch.qos.logback:logback-classic:1.2.11") + force("org.slf4j:slf4j-api:1.7.36") + } } diff --git a/instrumentation/spring/spring-webmvc-3.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/springwebmvc/HandlerSpanNameExtractor.java b/instrumentation/spring/spring-webmvc-3.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/springwebmvc/HandlerSpanNameExtractor.java index ef4c177f3f12..a88faa2e0e3a 100644 --- a/instrumentation/spring/spring-webmvc-3.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/springwebmvc/HandlerSpanNameExtractor.java +++ b/instrumentation/spring/spring-webmvc-3.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/springwebmvc/HandlerSpanNameExtractor.java @@ -6,7 +6,7 @@ package io.opentelemetry.javaagent.instrumentation.springwebmvc; import io.opentelemetry.instrumentation.api.instrumenter.SpanNameExtractor; -import io.opentelemetry.instrumentation.api.util.SpanNames; +import io.opentelemetry.instrumentation.api.instrumenter.util.SpanNames; import java.lang.reflect.Method; import javax.servlet.Servlet; import org.springframework.web.HttpRequestHandler; diff --git a/instrumentation/spring/spring-webmvc-3.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/springwebmvc/ModelAndViewAttributesExtractor.java b/instrumentation/spring/spring-webmvc-3.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/springwebmvc/ModelAndViewAttributesExtractor.java index 25786aa51617..f977729d50bc 100644 --- a/instrumentation/spring/spring-webmvc-3.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/springwebmvc/ModelAndViewAttributesExtractor.java +++ b/instrumentation/spring/spring-webmvc-3.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/springwebmvc/ModelAndViewAttributesExtractor.java @@ -8,7 +8,6 @@ import io.opentelemetry.api.common.AttributesBuilder; import io.opentelemetry.context.Context; import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor; -import io.opentelemetry.instrumentation.api.util.ClassNames; import io.opentelemetry.javaagent.bootstrap.internal.InstrumentationConfig; import javax.annotation.Nullable; import org.springframework.web.servlet.ModelAndView; @@ -27,7 +26,7 @@ public void onStart( attributes.put("spring-webmvc.view.name", modelAndView.getViewName()); View view = modelAndView.getView(); if (view != null) { - attributes.put("spring-webmvc.view.type", ClassNames.simpleName(view.getClass())); + attributes.put("spring-webmvc.view.type", view.getClass().getName()); } } } diff --git a/instrumentation/spring/spring-webmvc-3.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/springwebmvc/SpringWebMvcInstrumentationModule.java b/instrumentation/spring/spring-webmvc-3.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/springwebmvc/SpringWebMvcInstrumentationModule.java index c171597d5586..6e63da40e067 100644 --- a/instrumentation/spring/spring-webmvc-3.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/springwebmvc/SpringWebMvcInstrumentationModule.java +++ b/instrumentation/spring/spring-webmvc-3.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/springwebmvc/SpringWebMvcInstrumentationModule.java @@ -20,8 +20,9 @@ public SpringWebMvcInstrumentationModule() { @Override public boolean isHelperClass(String className) { - return className.startsWith( - "org.springframework.web.servlet.OpenTelemetryHandlerMappingFilter"); + return className.startsWith("org.springframework.web.servlet.OpenTelemetryHandlerMappingFilter") + || className.startsWith("org.springframework.web.servlet.ContentCachingResponseWrapper") + || className.startsWith("org.springframework.web.servlet.ContentCachingRequestWrapper"); } @Override diff --git a/instrumentation/spring/spring-webmvc-3.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/springwebmvc/SpringWebMvcSingletons.java b/instrumentation/spring/spring-webmvc-3.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/springwebmvc/SpringWebMvcSingletons.java index 11bcc3192b62..6844b6c7b7a5 100644 --- a/instrumentation/spring/spring-webmvc-3.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/springwebmvc/SpringWebMvcSingletons.java +++ b/instrumentation/spring/spring-webmvc-3.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/springwebmvc/SpringWebMvcSingletons.java @@ -22,7 +22,7 @@ public final class SpringWebMvcSingletons { Instrumenter.builder( GlobalOpenTelemetry.get(), INSTRUMENTATION_NAME, new HandlerSpanNameExtractor()) .setEnabled(ExperimentalConfig.get().controllerTelemetryEnabled()) - .newInstrumenter(); + .buildInstrumenter(); MODEL_AND_VIEW_INSTRUMENTER = Instrumenter.builder( @@ -31,7 +31,7 @@ public final class SpringWebMvcSingletons { new ModelAndViewSpanNameExtractor()) .addAttributesExtractor(new ModelAndViewAttributesExtractor()) .setEnabled(ExperimentalConfig.get().viewTelemetryEnabled()) - .newInstrumenter(); + .buildInstrumenter(); } public static Instrumenter handlerInstrumenter() { diff --git a/instrumentation/spring/spring-webmvc-3.1/javaagent/src/main/java/org/springframework/web/servlet/ContentCachingRequestWrapper.java b/instrumentation/spring/spring-webmvc-3.1/javaagent/src/main/java/org/springframework/web/servlet/ContentCachingRequestWrapper.java new file mode 100644 index 000000000000..ef723ea39f78 --- /dev/null +++ b/instrumentation/spring/spring-webmvc-3.1/javaagent/src/main/java/org/springframework/web/servlet/ContentCachingRequestWrapper.java @@ -0,0 +1,255 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.springframework.web.servlet; + +import java.io.BufferedReader; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.URLEncoder; +import java.util.Arrays; +import java.util.Enumeration; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import javax.annotation.Nullable; +import javax.servlet.ReadListener; +import javax.servlet.ServletInputStream; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletRequestWrapper; +import org.springframework.http.HttpMethod; +import org.springframework.http.MediaType; +import org.springframework.web.util.WebUtils; + +public class ContentCachingRequestWrapper extends HttpServletRequestWrapper { + + private final ByteArrayOutputStream cachedContent; + + @Nullable private final Integer contentCacheLimit; + + @Nullable private ServletInputStream inputStream; + + @Nullable private BufferedReader reader; + + /** + * Create a new ContentCachingRequestWrapper for the given servlet request. + * + * @param request the original servlet request + */ + public ContentCachingRequestWrapper(HttpServletRequest request) { + super(request); + int contentLength = request.getContentLength(); + this.cachedContent = new ByteArrayOutputStream(contentLength >= 0 ? contentLength : 1024); + this.contentCacheLimit = null; + } + + /** + * Create a new ContentCachingRequestWrapper for the given servlet request. + * + * @param request the original servlet request + * @param contentCacheLimit the maximum number of bytes to cache per request + * @since 4.3.6 + * @see #handleContentOverflow(int) + */ + public ContentCachingRequestWrapper(HttpServletRequest request, int contentCacheLimit) { + super(request); + this.cachedContent = new ByteArrayOutputStream(contentCacheLimit); + this.contentCacheLimit = contentCacheLimit; + } + + @Override + public ServletInputStream getInputStream() throws IOException { + if (this.inputStream == null) { + this.inputStream = new ContentCachingInputStream(getRequest().getInputStream()); + } + return this.inputStream; + } + + @Override + public String getCharacterEncoding() { + String enc = super.getCharacterEncoding(); + return (enc != null ? enc : WebUtils.DEFAULT_CHARACTER_ENCODING); + } + + @Override + public BufferedReader getReader() throws IOException { + if (this.reader == null) { + this.reader = + new BufferedReader(new InputStreamReader(getInputStream(), getCharacterEncoding())); + } + return this.reader; + } + + @Override + public String getParameter(String name) { + if (this.cachedContent.size() == 0 && isFormPost()) { + writeRequestParametersToCachedContent(); + } + return super.getParameter(name); + } + + @Override + public Map getParameterMap() { + if (this.cachedContent.size() == 0 && isFormPost()) { + writeRequestParametersToCachedContent(); + } + return super.getParameterMap(); + } + + @Override + public Enumeration getParameterNames() { + if (this.cachedContent.size() == 0 && isFormPost()) { + writeRequestParametersToCachedContent(); + } + return super.getParameterNames(); + } + + @Override + public String[] getParameterValues(String name) { + if (this.cachedContent.size() == 0 && isFormPost()) { + writeRequestParametersToCachedContent(); + } + return super.getParameterValues(name); + } + + private boolean isFormPost() { + String contentType = getContentType(); + return (contentType != null + && contentType.contains(MediaType.APPLICATION_FORM_URLENCODED_VALUE) + && HttpMethod.POST.toString().equals(getMethod())); + } + + void writeRequestParametersToCachedContent() { + try { + if (this.cachedContent.size() == 0) { + String requestEncoding = getCharacterEncoding(); + Map form = super.getParameterMap(); + for (Iterator nameIterator = form.keySet().iterator(); nameIterator.hasNext(); ) { + String name = nameIterator.next(); + List values = Arrays.asList(form.get(name)); + for (Iterator valueIterator = values.iterator(); valueIterator.hasNext(); ) { + String value = valueIterator.next(); + this.cachedContent.write(URLEncoder.encode(name, requestEncoding).getBytes()); + if (value != null) { + this.cachedContent.write('='); + this.cachedContent.write(URLEncoder.encode(value, requestEncoding).getBytes()); + if (valueIterator.hasNext()) { + this.cachedContent.write('&'); + } + } + } + if (nameIterator.hasNext()) { + this.cachedContent.write('&'); + } + } + } + } catch (IOException ex) { + throw new IllegalStateException("Failed to write request parameters to cached content", ex); + } + } + + /** + * Return the cached request content as a byte array. + * + *

The returned array will never be larger than the content cache limit. + * + *

Note: The byte array returned from this method reflects the amount of + * content that has been read at the time when it is called. If the application does not read the + * content, this method returns an empty array. + * + * @see #ContentCachingRequestWrapper(HttpServletRequest, int) + */ + public byte[] getContentAsByteArray() { + return this.cachedContent.toByteArray(); + } + + /** + * Template method for handling a content overflow: specifically, a request body being read that + * exceeds the specified content cache limit. + * + *

The default implementation is empty. Subclasses may override this to throw a + * payload-too-large exception or the like. + * + * @param contentCacheLimit the maximum number of bytes to cache per request which has just been + * exceeded + * @since 4.3.6 + * @see #ContentCachingRequestWrapper(HttpServletRequest, int) + */ + protected void handleContentOverflow(int contentCacheLimit) {} + + private class ContentCachingInputStream extends ServletInputStream { + + private final ServletInputStream is; + + private boolean overflow = false; + + public ContentCachingInputStream(ServletInputStream is) { + this.is = is; + } + + private void writeToCache(final byte[] b, final int off, int count) { + if (!this.overflow && count > 0) { + if (contentCacheLimit != null && count + cachedContent.size() > contentCacheLimit) { + this.overflow = true; + cachedContent.write(b, off, contentCacheLimit - cachedContent.size()); + handleContentOverflow(contentCacheLimit); + return; + } + cachedContent.write(b, off, count); + } + } + + @Override + public int read() throws IOException { + int ch = this.is.read(); + if (ch != -1 && !this.overflow) { + if (contentCacheLimit != null && cachedContent.size() == contentCacheLimit) { + this.overflow = true; + handleContentOverflow(contentCacheLimit); + } else { + cachedContent.write(ch); + } + } + return ch; + } + + @Override + public int read(byte[] b) throws IOException { + int count = this.is.read(b); + writeToCache(b, 0, count); + return count; + } + + @Override + public int read(final byte[] b, final int off, final int len) throws IOException { + int count = this.is.read(b, off, len); + writeToCache(b, off, count); + return count; + } + + @Override + public int readLine(final byte[] b, final int off, final int len) throws IOException { + int count = this.is.readLine(b, off, len); + writeToCache(b, off, count); + return count; + } + + @Override + public boolean isFinished() { + return this.is.isFinished(); + } + + @Override + public boolean isReady() { + return this.is.isReady(); + } + + @Override + public void setReadListener(ReadListener readListener) { + this.is.setReadListener(readListener); + } + } +} diff --git a/instrumentation/spring/spring-webmvc-3.1/javaagent/src/main/java/org/springframework/web/servlet/ContentCachingResponseWrapper.java b/instrumentation/spring/spring-webmvc-3.1/javaagent/src/main/java/org/springframework/web/servlet/ContentCachingResponseWrapper.java new file mode 100644 index 000000000000..76c42f26560e --- /dev/null +++ b/instrumentation/spring/spring-webmvc-3.1/javaagent/src/main/java/org/springframework/web/servlet/ContentCachingResponseWrapper.java @@ -0,0 +1,156 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.springframework.web.servlet; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.io.UnsupportedEncodingException; +import javax.servlet.ServletOutputStream; +import javax.servlet.WriteListener; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpServletResponseWrapper; +import org.springframework.web.util.WebUtils; + +public class ContentCachingResponseWrapper extends HttpServletResponseWrapper { + private final ByteArrayOutputStream content = new ByteArrayOutputStream(1024); + + private final ServletOutputStream outputStream = new ResponseServletOutputStream(); + + private PrintWriter writer; + + private int statusCode = HttpServletResponse.SC_OK; + + public ContentCachingResponseWrapper(HttpServletResponse response) { + super(response); + } + + @Override + public void setStatus(int sc) { + super.setStatus(sc); + this.statusCode = sc; + } + + @SuppressWarnings("deprecation") + @Override + public void setStatus(int sc, String sm) { + super.setStatus(sc, sm); + this.statusCode = sc; + } + + @Override + public void sendError(int sc) throws IOException { + copyBodyToResponse(); + super.sendError(sc); + this.statusCode = sc; + } + + @Override + public void sendError(int sc, String msg) throws IOException { + copyBodyToResponse(); + super.sendError(sc, msg); + this.statusCode = sc; + } + + @Override + public void sendRedirect(String location) throws IOException { + copyBodyToResponse(); + super.sendRedirect(location); + } + + @Override + public ServletOutputStream getOutputStream() { + return this.outputStream; + } + + @Override + public PrintWriter getWriter() throws IOException { + if (this.writer == null) { + String characterEncoding = getCharacterEncoding(); + this.writer = + (characterEncoding != null + ? new ResponsePrintWriter(characterEncoding) + : new ResponsePrintWriter(WebUtils.DEFAULT_CHARACTER_ENCODING)); + } + return this.writer; + } + + @Override + public void resetBuffer() { + this.content.reset(); + } + + @Override + public void reset() { + super.reset(); + this.content.reset(); + } + + /** Return the status code as specified on the response. */ + public int getStatusCode() { + return this.statusCode; + } + + /** Return the cached response content as a byte array. */ + public byte[] getContentAsByteArray() { + return this.content.toByteArray(); + } + + void copyBodyToResponse() throws IOException { + if (this.content.size() > 0) { + getResponse().setContentLength(this.content.size()); + getResponse().getOutputStream().write(this.content.toByteArray()); + this.content.reset(); + } + } + + private class ResponseServletOutputStream extends ServletOutputStream { + + @Override + public void write(int b) throws IOException { + content.write(b); + } + + @Override + public void write(byte[] b, int off, int len) throws IOException { + content.write(b, off, len); + } + + @Override + public boolean isReady() { + return false; + } + + @Override + public void setWriteListener(WriteListener writeListener) {} + } + + private class ResponsePrintWriter extends PrintWriter { + + public ResponsePrintWriter(String characterEncoding) throws UnsupportedEncodingException { + super(new OutputStreamWriter(content, characterEncoding)); + } + + @Override + public void write(char[] buf, int off, int len) { + super.write(buf, off, len); + super.flush(); + } + + @Override + public void write(String s, int off, int len) { + super.write(s, off, len); + super.flush(); + } + + @Override + public void write(int c) { + super.write(c); + super.flush(); + } + } +} diff --git a/instrumentation/spring/spring-webmvc-3.1/javaagent/src/main/java/org/springframework/web/servlet/OpenTelemetryHandlerMappingFilter.java b/instrumentation/spring/spring-webmvc-3.1/javaagent/src/main/java/org/springframework/web/servlet/OpenTelemetryHandlerMappingFilter.java index 82c25a456e8d..e791ff29b3a5 100644 --- a/instrumentation/spring/spring-webmvc-3.1/javaagent/src/main/java/org/springframework/web/servlet/OpenTelemetryHandlerMappingFilter.java +++ b/instrumentation/spring/spring-webmvc-3.1/javaagent/src/main/java/org/springframework/web/servlet/OpenTelemetryHandlerMappingFilter.java @@ -6,7 +6,9 @@ package org.springframework.web.servlet; import static io.opentelemetry.instrumentation.api.instrumenter.http.HttpRouteSource.CONTROLLER; +import static java.nio.charset.StandardCharsets.UTF_8; +import io.opentelemetry.api.trace.Span; import io.opentelemetry.context.Context; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpRouteGetter; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpRouteHolder; @@ -77,17 +79,42 @@ public void doFilter(ServletRequest request, ServletResponse response, FilterCha return; } + ContentCachingResponseWrapper responseWrapper = + new ContentCachingResponseWrapper((HttpServletResponse) response); + + ContentCachingRequestWrapper requestWrapper = + new ContentCachingRequestWrapper((HttpServletRequest) request); + try { - filterChain.doFilter(request, response); + filterChain.doFilter(requestWrapper, responseWrapper); } finally { if (handlerMappings != null) { Context context = Context.current(); + setAttributes(requestWrapper, responseWrapper, context); HttpRouteHolder.updateHttpRoute( context, CONTROLLER, serverSpanName, (HttpServletRequest) request); } } } + private void setAttributes( + ContentCachingRequestWrapper requestWrapper, + ContentCachingResponseWrapper responseWrapper, + Context context) + throws IOException { + Span span = Span.fromContext(context); + requestWrapper.writeRequestParametersToCachedContent(); + byte[] requestContentAsByteArray = requestWrapper.getContentAsByteArray(); + if (requestContentAsByteArray != null && requestContentAsByteArray.length > 0) { + span.setAttribute("http.request.body", new String(requestContentAsByteArray, UTF_8)); + } + byte[] responseContentAsByteArray = responseWrapper.getContentAsByteArray(); + if (responseContentAsByteArray != null && responseContentAsByteArray.length > 0) { + span.setAttribute("http.response.body", new String(responseContentAsByteArray, UTF_8)); + responseWrapper.copyBodyToResponse(); + } + } + @Override public void destroy() {} diff --git a/instrumentation/spring/spring-webmvc-3.1/javaagent/src/test/groovy/test/boot/SpringBootBasedTest.groovy b/instrumentation/spring/spring-webmvc-3.1/javaagent/src/test/groovy/test/boot/SpringBootBasedTest.groovy index f5d191e10777..94cbd9cc75b1 100644 --- a/instrumentation/spring/spring-webmvc-3.1/javaagent/src/test/groovy/test/boot/SpringBootBasedTest.groovy +++ b/instrumentation/spring/spring-webmvc-3.1/javaagent/src/test/groovy/test/boot/SpringBootBasedTest.groovy @@ -5,18 +5,21 @@ package test.boot +import io.opentelemetry.api.common.AttributeKey import io.opentelemetry.api.trace.StatusCode import io.opentelemetry.instrumentation.test.AgentTestTrait import io.opentelemetry.instrumentation.test.asserts.TraceAssert import io.opentelemetry.instrumentation.test.base.HttpServerTest import io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint import io.opentelemetry.sdk.trace.data.SpanData +import io.opentelemetry.semconv.trace.attributes.SemanticAttributes import io.opentelemetry.testing.internal.armeria.common.AggregatedHttpRequest import io.opentelemetry.testing.internal.armeria.common.HttpData import io.opentelemetry.testing.internal.armeria.common.MediaType import io.opentelemetry.testing.internal.armeria.common.QueryParams import org.springframework.boot.SpringApplication import org.springframework.context.ConfigurableApplicationContext +import org.springframework.security.web.util.OnCommittedResponseWrapper import org.springframework.web.servlet.view.RedirectView import static io.opentelemetry.api.trace.SpanKind.INTERNAL @@ -113,6 +116,35 @@ class SpringBootBasedTest extends HttpServerTest } } + def "test payload extraction"() { + setup: + def authProvider = server.getBean(SavingAuthenticationProvider) + def body = "abcd=1234" + def request = AggregatedHttpRequest.of( + request(PATH_PARAM, "POST").headers().toBuilder().contentType(MediaType.FORM_DATA).build(), + HttpData.ofUtf8(body)) + + when: + authProvider.latestAuthentications.clear() + def response = client.execute(request).aggregate().join() + + then: + response.status().code() == 200 + def respBody = response.contentUtf8() + assert respBody == PATH_PARAM.body + + and: + def httpSpan = testRunner().getExportedSpans()[2] + def requestBody = (String) httpSpan.attributes.asMap().find { + e -> e.key.key == "http.request.body" + }.value + assert requestBody.equals(body) + def responseBody = (String) httpSpan.attributes.asMap().find { + e -> e.key.key == "http.response.body" + }.value + assert responseBody.equals(respBody) + } + def "test character encoding of #testPassword"() { setup: def authProvider = server.getBean(SavingAuthenticationProvider) @@ -132,6 +164,15 @@ class SpringBootBasedTest extends HttpServerTest and: assertTraces(1) { + def httpSpan = testRunner().getExportedSpans()[1] + assert httpSpan.resource.getAttribute(AttributeKey.stringKey("telemetry.sdk.name")).equalsIgnoreCase("helios-opentelemetry-javaagent") + def requestBody = (String) httpSpan.attributes.asMap().find { + e -> e.key.key == "http.request.body" + }.value + assert requestBody.startsWith("username=test&password=") + def responseBodyKey = (Boolean) httpSpan.attributes.asMap().containsKey("http.response.body") + assert !responseBodyKey + trace(0, 2) { serverSpan(it, 0, null, null, "POST", response.contentUtf8().length(), LOGIN) redirectSpan(it, 1, span(0)) @@ -154,11 +195,13 @@ class SpringBootBasedTest extends HttpServerTest @Override void responseSpan(TraceAssert trace, int index, Object parent, String method = "GET", ServerEndpoint endpoint = SUCCESS) { - def responseSpanName = endpoint == NOT_FOUND ? "OnCommittedResponseWrapper.sendError" : "OnCommittedResponseWrapper.sendRedirect" + def methodName = endpoint == NOT_FOUND ? "sendError" : "sendRedirect" trace.span(index) { - name responseSpanName + name "OnCommittedResponseWrapper.$methodName" kind INTERNAL attributes { + "$SemanticAttributes.CODE_NAMESPACE" OnCommittedResponseWrapper.name + "$SemanticAttributes.CODE_FUNCTION" methodName } } } @@ -169,7 +212,7 @@ class SpringBootBasedTest extends HttpServerTest name "Render RedirectView" kind INTERNAL attributes { - "spring-webmvc.view.type" RedirectView.simpleName + "spring-webmvc.view.type" RedirectView.name } } } diff --git a/instrumentation/spring/spring-webmvc-3.1/library/README.md b/instrumentation/spring/spring-webmvc-3.1/library/README.md deleted file mode 100644 index 022dd124d88b..000000000000 --- a/instrumentation/spring/spring-webmvc-3.1/library/README.md +++ /dev/null @@ -1,93 +0,0 @@ -# Manual Instrumentation for Spring Web MVC - -Provides OpenTelemetry tracing for spring-webmvc RestControllers by leveraging spring-webmvc [filters](https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/filter). - -## Quickstart - -### Add these dependencies to your project. - -Replace `SPRING_VERSION` with the version of spring you're using. - - `Minimum version: 3.1` - -Replace `OPENTELEMETRY_VERSION` with the latest stable [release](https://mvnrepository.com/artifact/io.opentelemetry). - - `Minimum version: 1.7.0` - -For Maven add to your `pom.xml`: - -```xml - - - - io.opentelemetry.instrumentation - opentelemetry-spring-webmvc-3.1 - OPENTELEMETRY_VERSION - - - - - - io.opentelemetry - opentelemetry-exporter-logging - OPENTELEMETRY_VERSION - - - - - - org.springframework - spring-webmvc - SPRING_VERSION - - - -``` - -For Gradle add to your dependencies: - -```groovy - -// opentelemetry instrumentation -implementation("io.opentelemetry.instrumentation:opentelemetry-spring-webmvc-3.1:OPENTELEMETRY_VERSION") - -// opentelemetry exporter -// replace this default exporter with your opentelemetry exporter (ex. otlp/zipkin/jaeger/..) -implementation("io.opentelemetry:opentelemetry-exporter-logging:OPENTELEMETRY_VERSION") - -// required to instrument spring-webmvc -// this artifact should already be present in your application -implementation("org.springframework:spring-webmvc:SPRING_VERSION") -``` - -### Features - -#### SpringWebMvcTracing - -`SpringWebMvcTracing` adds OpenTelemetry server spans to requests processed by request dispatch, on any spring servlet container. An example is shown below: - -##### Usage in Spring Boot - -Spring Boot allows servlet `Filter`s to be registered as beans: - -```java -import io.opentelemetry.instrumentation.spring.webmvc.SpringWebMvcTracing; -import io.opentelemetry.api.trace.Tracer; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.web.client.RestTemplate; - -@Configuration -public class WebMvcTracingFilterConfig { - - @Bean - public Filter webMvcTracingFilter(OpenTelemetry openTelemetry) { - return SpringWebMvcTracing.create(openTelemetry).newServletFilter(); - } -} -``` - -### Starter Guide - -Check out [OpenTelemetry Manual Instrumentation](https://opentelemetry.io/docs/instrumentation/java/manual/) to learn more about -using the OpenTelemetry API to instrument your code. diff --git a/instrumentation/spring/spring-webmvc-3.1/library/build.gradle.kts b/instrumentation/spring/spring-webmvc-3.1/library/build.gradle.kts deleted file mode 100644 index 0d5ec74e0136..000000000000 --- a/instrumentation/spring/spring-webmvc-3.1/library/build.gradle.kts +++ /dev/null @@ -1,8 +0,0 @@ -plugins { - id("otel.library-instrumentation") -} - -dependencies { - compileOnly("org.springframework:spring-webmvc:3.1.0.RELEASE") - compileOnly("javax.servlet:javax.servlet-api:3.1.0") -} diff --git a/instrumentation/spring/spring-webmvc-3.1/library/src/main/java/io/opentelemetry/instrumentation/spring/webmvc/SpringWebMvcNetAttributesGetter.java b/instrumentation/spring/spring-webmvc-3.1/library/src/main/java/io/opentelemetry/instrumentation/spring/webmvc/SpringWebMvcNetAttributesGetter.java deleted file mode 100644 index 0d41652a0563..000000000000 --- a/instrumentation/spring/spring-webmvc-3.1/library/src/main/java/io/opentelemetry/instrumentation/spring/webmvc/SpringWebMvcNetAttributesGetter.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.instrumentation.spring.webmvc; - -import io.opentelemetry.instrumentation.api.instrumenter.net.NetServerAttributesGetter; -import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; -import javax.annotation.Nullable; -import javax.servlet.http.HttpServletRequest; - -final class SpringWebMvcNetAttributesGetter - implements NetServerAttributesGetter { - @Override - public String transport(HttpServletRequest request) { - return SemanticAttributes.NetTransportValues.IP_TCP; - } - - @Override - public Integer peerPort(HttpServletRequest request) { - return request.getRemotePort(); - } - - @Override - @Nullable - public String peerIp(HttpServletRequest request) { - return request.getRemoteAddr(); - } -} diff --git a/instrumentation/spring/spring-webmvc-3.1/library/src/main/java/io/opentelemetry/instrumentation/spring/webmvc/StatusCodeExtractor.java b/instrumentation/spring/spring-webmvc-3.1/library/src/main/java/io/opentelemetry/instrumentation/spring/webmvc/StatusCodeExtractor.java deleted file mode 100644 index 1401b40246d9..000000000000 --- a/instrumentation/spring/spring-webmvc-3.1/library/src/main/java/io/opentelemetry/instrumentation/spring/webmvc/StatusCodeExtractor.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.instrumentation.spring.webmvc; - -import io.opentelemetry.api.common.AttributesBuilder; -import io.opentelemetry.context.Context; -import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor; -import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; -import javax.annotation.Nullable; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -final class StatusCodeExtractor - implements AttributesExtractor { - - @Override - public void onStart( - AttributesBuilder attributes, Context parentContext, HttpServletRequest httpServletRequest) {} - - @Override - public void onEnd( - AttributesBuilder attributes, - Context context, - HttpServletRequest httpServletRequest, - @Nullable HttpServletResponse response, - @Nullable Throwable error) { - if (response != null) { - long statusCode; - // if response is not committed and there is a throwable set status to 500 / - // INTERNAL_SERVER_ERROR, due to servlet spec - // https://javaee.github.io/servlet-spec/downloads/servlet-4.0/servlet-4_0_FINAL.pdf: - // "If a servlet generates an error that is not handled by the error page mechanism as - // described above, the container must ensure to send a response with status 500." - if (!response.isCommitted() && error != null) { - statusCode = 500; - } else { - statusCode = response.getStatus(); - } - - attributes.put(SemanticAttributes.HTTP_STATUS_CODE, statusCode); - } - } -} diff --git a/instrumentation/spring/spring-webmvc-5.3/library/README.md b/instrumentation/spring/spring-webmvc-5.3/library/README.md new file mode 100644 index 000000000000..b28bc2d6f7f6 --- /dev/null +++ b/instrumentation/spring/spring-webmvc-5.3/library/README.md @@ -0,0 +1,93 @@ +# Library Instrumentation for Spring Web MVC version 5.3 and higher + +Provides OpenTelemetry instrumentation for Spring WebMVC controllers. + +## Quickstart + +### Add these dependencies to your project. + +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). + +For Maven add the following to your `pom.xml`: + +```xml + + + + io.opentelemetry.instrumentation + opentelemetry-spring-webmvc-5.3 + OPENTELEMETRY_VERSION + + + + + + io.opentelemetry + opentelemetry-exporter-logging + OPENTELEMETRY_VERSION + + + + + + org.springframework + spring-webmvc + SPRING_VERSION + + + +``` + +For Gradle add the following to your dependencies: + +```groovy + +// OpenTelemetry instrumentation +implementation("io.opentelemetry.instrumentation:opentelemetry-spring-webmvc-5.3:OPENTELEMETRY_VERSION") + +// OpenTelemetry exporter +// replace this default exporter with your OpenTelemetry exporter (ex. otlp/zipkin/jaeger/..) +implementation("io.opentelemetry:opentelemetry-exporter-logging:OPENTELEMETRY_VERSION") + +// required to instrument Spring WebMVC +// this artifact should already be present in your application +implementation("org.springframework:spring-webmvc:SPRING_VERSION") +``` + +### Features + +#### `SpringWebMvcTelemetry` + +`SpringWebMvcTelemetry` enables creating OpenTelemetry server spans around HTTP requests processed +by the Spring servlet container. + +##### Usage in Spring Boot + +Spring Boot allows servlet `Filter`s to be registered as beans: + +```java +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.instrumentation.spring.webmvc.v5_3.SpringWebMvcTelemetry; +import javax.servlet.Filter; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class SpringWebMvcTelemetryConfiguration { + + @Bean + public Filter telemetryFilter(OpenTelemetry openTelemetry) { + return SpringWebMvcTelemetry.create(openTelemetry).createServletFilter(); + } +} +``` + +### Starter Guide + +Check +out [OpenTelemetry Manual Instrumentation](https://opentelemetry.io/docs/instrumentation/java/manual/) +to learn more about using the OpenTelemetry API to instrument your code. diff --git a/instrumentation/spring/spring-webmvc-5.3/library/build.gradle.kts b/instrumentation/spring/spring-webmvc-5.3/library/build.gradle.kts new file mode 100644 index 000000000000..920cebcbeab5 --- /dev/null +++ b/instrumentation/spring/spring-webmvc-5.3/library/build.gradle.kts @@ -0,0 +1,25 @@ +plugins { + id("otel.library-instrumentation") +} + +val versions: Map by project +val springBootVersion = versions["org.springframework.boot"] + +dependencies { + compileOnly("org.springframework:spring-webmvc:5.3.0") + compileOnly("javax.servlet:javax.servlet-api:4.0.1") + + testImplementation(project(":testing-common")) + testImplementation("org.springframework.boot:spring-boot-starter-web:$springBootVersion") + testImplementation("org.springframework.boot:spring-boot-starter-test:$springBootVersion") { + exclude("org.junit.vintage", "junit-vintage-engine") + } +} + +configurations.testRuntimeClasspath { + resolutionStrategy { + // requires old logback (and therefore also old slf4j) + force("ch.qos.logback:logback-classic:1.2.11") + force("org.slf4j:slf4j-api:1.7.36") + } +} diff --git a/instrumentation/spring/spring-webmvc-5.3/library/src/main/java/io/opentelemetry/instrumentation/spring/webmvc/v5_3/HttpRouteSupport.java b/instrumentation/spring/spring-webmvc-5.3/library/src/main/java/io/opentelemetry/instrumentation/spring/webmvc/v5_3/HttpRouteSupport.java new file mode 100644 index 000000000000..cafac0b13abb --- /dev/null +++ b/instrumentation/spring/spring-webmvc-5.3/library/src/main/java/io/opentelemetry/instrumentation/spring/webmvc/v5_3/HttpRouteSupport.java @@ -0,0 +1,153 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.spring.webmvc.v5_3; + +import static java.util.Objects.requireNonNull; +import static org.springframework.web.util.ServletRequestPathUtils.PATH_ATTRIBUTE; + +import io.opentelemetry.context.Context; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.concurrent.atomic.AtomicBoolean; +import javax.annotation.Nullable; +import javax.servlet.FilterConfig; +import javax.servlet.http.HttpServletRequest; +import org.springframework.context.ApplicationListener; +import org.springframework.context.event.ContextRefreshedEvent; +import org.springframework.web.context.ConfigurableWebApplicationContext; +import org.springframework.web.context.WebApplicationContext; +import org.springframework.web.context.support.WebApplicationContextUtils; +import org.springframework.web.servlet.DispatcherServlet; +import org.springframework.web.servlet.HandlerExecutionChain; +import org.springframework.web.servlet.HandlerMapping; +import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; +import org.springframework.web.util.ServletRequestPathUtils; + +final class HttpRouteSupport { + + private final AtomicBoolean contextRefreshTriggered = new AtomicBoolean(); + @Nullable private volatile DispatcherServlet dispatcherServlet; + @Nullable private volatile List handlerMappings; + private volatile boolean parseRequestPath; + + void onFilterInit(FilterConfig filterConfig) { + WebApplicationContext context = + WebApplicationContextUtils.getWebApplicationContext(filterConfig.getServletContext()); + if (!(context instanceof ConfigurableWebApplicationContext)) { + return; + } + + DispatcherServlet servlet = context.getBeanProvider(DispatcherServlet.class).getIfAvailable(); + if (servlet != null) { + dispatcherServlet = servlet; + + ((ConfigurableWebApplicationContext) context) + .addApplicationListener(new WebContextRefreshListener()); + } + } + + // we can't retrieve the handler mappings from the DispatcherServlet in the onRefresh listener, + // because it loads them just after the application context refreshed event is processed + // to work around this, we're setting a boolean flag that'll cause this filter to load the + // mappings the next time it attempts to set the http.route + final class WebContextRefreshListener implements ApplicationListener { + + @Override + public void onApplicationEvent(ContextRefreshedEvent event) { + contextRefreshTriggered.set(true); + } + } + + boolean hasMappings() { + if (contextRefreshTriggered.compareAndSet(true, false)) { + // reload the handler mappings only if the web app context was recently refreshed + Optional.ofNullable(dispatcherServlet) + .map(DispatcherServlet::getHandlerMappings) + .ifPresent(this::setHandlerMappings); + } + return handlerMappings != null; + } + + private void setHandlerMappings(List mappings) { + List handlerMappings = new ArrayList<>(); + for (HandlerMapping mapping : mappings) { + // Originally we ran findMapping at the very beginning of the request. This turned out to have + // application-crashing side-effects with grails. That is why we don't add all HandlerMapping + // classes here. Although now that we run findMapping after the request, and only when server + // span name has not been updated by a controller, the probability of bad side-effects is much + // reduced even if we did add all HandlerMapping classes here. + if (mapping instanceof RequestMappingHandlerMapping) { + handlerMappings.add(mapping); + if (mapping.usesPathPatterns()) { + this.parseRequestPath = true; + } + } + } + if (!handlerMappings.isEmpty()) { + this.handlerMappings = handlerMappings; + } + } + + @Nullable + String getHttpRoute(Context context, HttpServletRequest request) { + boolean parsePath = this.parseRequestPath; + Object previousValue = null; + if (parsePath) { + previousValue = request.getAttribute(PATH_ATTRIBUTE); + // sets new value for PATH_ATTRIBUTE of request + ServletRequestPathUtils.parseAndCache(request); + } + try { + if (findMapping(request)) { + // Name the parent span based on the matching pattern + // Let the parent span resource name be set with the attribute set in findMapping. + Object bestMatchingPattern = + request.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE); + if (bestMatchingPattern != null) { + return prependContextPath(request, bestMatchingPattern.toString()); + } + } + } finally { + // mimic spring DispatcherServlet and restore the previous value of PATH_ATTRIBUTE + if (parsePath) { + if (previousValue == null) { + request.removeAttribute(PATH_ATTRIBUTE); + } else { + request.setAttribute(PATH_ATTRIBUTE, previousValue); + } + } + } + return null; + } + + /** + * When a HandlerMapping matches a request, it sets HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE + * as an attribute on the request. This attribute set as the HTTP route. + */ + private boolean findMapping(HttpServletRequest request) { + try { + // handlerMapping already null-checked above + for (HandlerMapping mapping : requireNonNull(handlerMappings)) { + HandlerExecutionChain handler = mapping.getHandler(request); + if (handler != null) { + return true; + } + } + } catch (Exception ignored) { + // mapping.getHandler() threw exception. Ignore + } + return false; + } + + private static String prependContextPath(HttpServletRequest request, String route) { + String contextPath = request.getContextPath(); + if (contextPath == null) { + return route; + } + return contextPath + (route.startsWith("/") ? route : ("/" + route)); + } +} diff --git a/instrumentation/spring/spring-webmvc-3.1/library/src/main/java/io/opentelemetry/instrumentation/spring/webmvc/JavaxHttpServletRequestGetter.java b/instrumentation/spring/spring-webmvc-5.3/library/src/main/java/io/opentelemetry/instrumentation/spring/webmvc/v5_3/JavaxHttpServletRequestGetter.java similarity index 90% rename from instrumentation/spring/spring-webmvc-3.1/library/src/main/java/io/opentelemetry/instrumentation/spring/webmvc/JavaxHttpServletRequestGetter.java rename to instrumentation/spring/spring-webmvc-5.3/library/src/main/java/io/opentelemetry/instrumentation/spring/webmvc/v5_3/JavaxHttpServletRequestGetter.java index 6f0174dc9a14..3fba07f6341e 100644 --- a/instrumentation/spring/spring-webmvc-3.1/library/src/main/java/io/opentelemetry/instrumentation/spring/webmvc/JavaxHttpServletRequestGetter.java +++ b/instrumentation/spring/spring-webmvc-5.3/library/src/main/java/io/opentelemetry/instrumentation/spring/webmvc/v5_3/JavaxHttpServletRequestGetter.java @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.instrumentation.spring.webmvc; +package io.opentelemetry.instrumentation.spring.webmvc.v5_3; import io.opentelemetry.context.propagation.TextMapGetter; import java.util.Collections; diff --git a/instrumentation/spring/spring-webmvc-3.1/library/src/main/java/io/opentelemetry/instrumentation/spring/webmvc/SpringWebMvcHttpAttributesGetter.java b/instrumentation/spring/spring-webmvc-5.3/library/src/main/java/io/opentelemetry/instrumentation/spring/webmvc/v5_3/SpringWebMvcHttpAttributesGetter.java similarity index 66% rename from instrumentation/spring/spring-webmvc-3.1/library/src/main/java/io/opentelemetry/instrumentation/spring/webmvc/SpringWebMvcHttpAttributesGetter.java rename to instrumentation/spring/spring-webmvc-5.3/library/src/main/java/io/opentelemetry/instrumentation/spring/webmvc/v5_3/SpringWebMvcHttpAttributesGetter.java index b13d852d9f81..2ff288491415 100644 --- a/instrumentation/spring/spring-webmvc-3.1/library/src/main/java/io/opentelemetry/instrumentation/spring/webmvc/SpringWebMvcHttpAttributesGetter.java +++ b/instrumentation/spring/spring-webmvc-5.3/library/src/main/java/io/opentelemetry/instrumentation/spring/webmvc/v5_3/SpringWebMvcHttpAttributesGetter.java @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.instrumentation.spring.webmvc; +package io.opentelemetry.instrumentation.spring.webmvc.v5_3; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpServerAttributesGetter; import java.util.ArrayList; @@ -31,44 +31,36 @@ public List requestHeader(HttpServletRequest request, String name) { return headers == null ? Collections.emptyList() : Collections.list(headers); } - @Override - @Nullable - public Long requestContentLength( - HttpServletRequest request, @Nullable HttpServletResponse response) { - return null; - } - - @Override - @Nullable - public Long requestContentLengthUncompressed( - HttpServletRequest request, @Nullable HttpServletResponse response) { - return null; - } - @Override @Nullable public String flavor(HttpServletRequest request) { - return request.getProtocol(); - } - - @Override - @Nullable - public Integer statusCode(HttpServletRequest request, HttpServletResponse response) { - // set in StatusCodeExtractor - return null; + String flavor = request.getProtocol(); + if (flavor == null) { + return null; + } + if (flavor.startsWith("HTTP/")) { + flavor = flavor.substring("HTTP/".length()); + } + return flavor; } @Override - @Nullable - public Long responseContentLength(HttpServletRequest request, HttpServletResponse response) { - return null; - } + public Integer statusCode( + HttpServletRequest request, HttpServletResponse response, @Nullable Throwable error) { + + int statusCode; + // if response is not committed and there is a throwable set status to 500 / + // INTERNAL_SERVER_ERROR, due to servlet spec + // https://javaee.github.io/servlet-spec/downloads/servlet-4.0/servlet-4_0_FINAL.pdf: + // "If a servlet generates an error that is not handled by the error page mechanism as + // described above, the container must ensure to send a response with status 500." + if (!response.isCommitted() && error != null) { + statusCode = 500; + } else { + statusCode = response.getStatus(); + } - @Override - @Nullable - public Long responseContentLengthUncompressed( - HttpServletRequest request, HttpServletResponse response) { - return null; + return statusCode; } @Override @@ -106,10 +98,4 @@ public String route(HttpServletRequest request) { public String scheme(HttpServletRequest request) { return request.getScheme(); } - - @Override - @Nullable - public String serverName(HttpServletRequest request) { - return null; - } } diff --git a/instrumentation/spring/spring-webmvc-5.3/library/src/main/java/io/opentelemetry/instrumentation/spring/webmvc/v5_3/SpringWebMvcNetAttributesGetter.java b/instrumentation/spring/spring-webmvc-5.3/library/src/main/java/io/opentelemetry/instrumentation/spring/webmvc/v5_3/SpringWebMvcNetAttributesGetter.java new file mode 100644 index 000000000000..79a9f82daef3 --- /dev/null +++ b/instrumentation/spring/spring-webmvc-5.3/library/src/main/java/io/opentelemetry/instrumentation/spring/webmvc/v5_3/SpringWebMvcNetAttributesGetter.java @@ -0,0 +1,53 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.spring.webmvc.v5_3; + +import io.opentelemetry.instrumentation.api.instrumenter.net.NetServerAttributesGetter; +import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; +import javax.annotation.Nullable; +import javax.servlet.http.HttpServletRequest; + +enum SpringWebMvcNetAttributesGetter implements NetServerAttributesGetter { + INSTANCE; + + @Override + public String transport(HttpServletRequest request) { + return SemanticAttributes.NetTransportValues.IP_TCP; + } + + @Nullable + @Override + public String hostName(HttpServletRequest request) { + return request.getServerName(); + } + + @Override + public Integer hostPort(HttpServletRequest request) { + return request.getServerPort(); + } + + @Override + @Nullable + public String sockPeerAddr(HttpServletRequest request) { + return request.getRemoteAddr(); + } + + @Override + public Integer sockPeerPort(HttpServletRequest request) { + return request.getRemotePort(); + } + + @Nullable + @Override + public String sockHostAddr(HttpServletRequest request) { + return request.getLocalAddr(); + } + + @Override + public Integer sockHostPort(HttpServletRequest request) { + return request.getLocalPort(); + } +} diff --git a/instrumentation/spring/spring-webmvc-3.1/library/src/main/java/io/opentelemetry/instrumentation/spring/webmvc/SpringWebMvcTelemetry.java b/instrumentation/spring/spring-webmvc-5.3/library/src/main/java/io/opentelemetry/instrumentation/spring/webmvc/v5_3/SpringWebMvcTelemetry.java similarity index 88% rename from instrumentation/spring/spring-webmvc-3.1/library/src/main/java/io/opentelemetry/instrumentation/spring/webmvc/SpringWebMvcTelemetry.java rename to instrumentation/spring/spring-webmvc-5.3/library/src/main/java/io/opentelemetry/instrumentation/spring/webmvc/v5_3/SpringWebMvcTelemetry.java index 161eb7444bb7..5e0d652672e9 100644 --- a/instrumentation/spring/spring-webmvc-3.1/library/src/main/java/io/opentelemetry/instrumentation/spring/webmvc/SpringWebMvcTelemetry.java +++ b/instrumentation/spring/spring-webmvc-5.3/library/src/main/java/io/opentelemetry/instrumentation/spring/webmvc/v5_3/SpringWebMvcTelemetry.java @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.instrumentation.spring.webmvc; +package io.opentelemetry.instrumentation.spring.webmvc.v5_3; import io.opentelemetry.api.OpenTelemetry; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; @@ -36,7 +36,7 @@ public static SpringWebMvcTelemetryBuilder builder(OpenTelemetry openTelemetry) } /** Returns a new {@link Filter} that generates telemetry for received HTTP requests. */ - public Filter newServletFilter() { - return new WebMvcTracingFilter(instrumenter); + public Filter createServletFilter() { + return new WebMvcTelemetryProducingFilter(instrumenter); } } diff --git a/instrumentation/spring/spring-webmvc-3.1/library/src/main/java/io/opentelemetry/instrumentation/spring/webmvc/SpringWebMvcTelemetryBuilder.java b/instrumentation/spring/spring-webmvc-5.3/library/src/main/java/io/opentelemetry/instrumentation/spring/webmvc/v5_3/SpringWebMvcTelemetryBuilder.java similarity index 86% rename from instrumentation/spring/spring-webmvc-3.1/library/src/main/java/io/opentelemetry/instrumentation/spring/webmvc/SpringWebMvcTelemetryBuilder.java rename to instrumentation/spring/spring-webmvc-5.3/library/src/main/java/io/opentelemetry/instrumentation/spring/webmvc/v5_3/SpringWebMvcTelemetryBuilder.java index 6f46e56303cf..ddd5cfe4fe02 100644 --- a/instrumentation/spring/spring-webmvc-3.1/library/src/main/java/io/opentelemetry/instrumentation/spring/webmvc/SpringWebMvcTelemetryBuilder.java +++ b/instrumentation/spring/spring-webmvc-5.3/library/src/main/java/io/opentelemetry/instrumentation/spring/webmvc/v5_3/SpringWebMvcTelemetryBuilder.java @@ -3,8 +3,9 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.instrumentation.spring.webmvc; +package io.opentelemetry.instrumentation.spring.webmvc.v5_3; +import com.google.errorprone.annotations.CanIgnoreReturnValue; import io.opentelemetry.api.OpenTelemetry; import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; @@ -14,7 +15,6 @@ import io.opentelemetry.instrumentation.api.instrumenter.http.HttpServerMetrics; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanNameExtractor; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanStatusExtractor; -import io.opentelemetry.instrumentation.api.instrumenter.net.NetServerAttributesExtractor; import java.util.ArrayList; import java.util.List; import javax.servlet.http.HttpServletRequest; @@ -23,14 +23,15 @@ /** A builder of {@link SpringWebMvcTelemetry}. */ public final class SpringWebMvcTelemetryBuilder { - private static final String INSTRUMENTATION_NAME = "io.opentelemetry.spring-webmvc-3.1"; + private static final String INSTRUMENTATION_NAME = "io.opentelemetry.spring-webmvc-5.3"; private final OpenTelemetry openTelemetry; private final List> additionalExtractors = new ArrayList<>(); private final HttpServerAttributesExtractorBuilder httpAttributesExtractorBuilder = - HttpServerAttributesExtractor.builder(SpringWebMvcHttpAttributesGetter.INSTANCE); + HttpServerAttributesExtractor.builder( + SpringWebMvcHttpAttributesGetter.INSTANCE, SpringWebMvcNetAttributesGetter.INSTANCE); SpringWebMvcTelemetryBuilder(OpenTelemetry openTelemetry) { this.openTelemetry = openTelemetry; @@ -40,6 +41,7 @@ public final class SpringWebMvcTelemetryBuilder { * Adds an additional {@link AttributesExtractor} to invoke to set attributes to instrumented * items. */ + @CanIgnoreReturnValue public SpringWebMvcTelemetryBuilder addAttributesExtractor( AttributesExtractor attributesExtractor) { additionalExtractors.add(attributesExtractor); @@ -51,6 +53,7 @@ public SpringWebMvcTelemetryBuilder addAttributesExtractor( * * @param requestHeaders A list of HTTP header names. */ + @CanIgnoreReturnValue public SpringWebMvcTelemetryBuilder setCapturedRequestHeaders(List requestHeaders) { httpAttributesExtractorBuilder.setCapturedRequestHeaders(requestHeaders); return this; @@ -61,6 +64,7 @@ public SpringWebMvcTelemetryBuilder setCapturedRequestHeaders(List reque * * @param responseHeaders A list of HTTP header names. */ + @CanIgnoreReturnValue public SpringWebMvcTelemetryBuilder setCapturedResponseHeaders(List responseHeaders) { httpAttributesExtractorBuilder.setCapturedResponseHeaders(responseHeaders); return this; @@ -81,13 +85,10 @@ public SpringWebMvcTelemetry build() { HttpSpanNameExtractor.create(httpAttributesGetter)) .setSpanStatusExtractor(HttpSpanStatusExtractor.create(httpAttributesGetter)) .addAttributesExtractor(httpAttributesExtractorBuilder.build()) - .addAttributesExtractor(new StatusCodeExtractor()) - .addAttributesExtractor( - NetServerAttributesExtractor.create(new SpringWebMvcNetAttributesGetter())) .addAttributesExtractors(additionalExtractors) .addOperationMetrics(HttpServerMetrics.get()) .addContextCustomizer(HttpRouteHolder.get()) - .newServerInstrumenter(JavaxHttpServletRequestGetter.INSTANCE); + .buildServerInstrumenter(JavaxHttpServletRequestGetter.INSTANCE); return new SpringWebMvcTelemetry(instrumenter); } diff --git a/instrumentation/spring/spring-webmvc-3.1/library/src/main/java/io/opentelemetry/instrumentation/spring/webmvc/WebMvcTracingFilter.java b/instrumentation/spring/spring-webmvc-5.3/library/src/main/java/io/opentelemetry/instrumentation/spring/webmvc/v5_3/WebMvcTelemetryProducingFilter.java similarity index 55% rename from instrumentation/spring/spring-webmvc-3.1/library/src/main/java/io/opentelemetry/instrumentation/spring/webmvc/WebMvcTracingFilter.java rename to instrumentation/spring/spring-webmvc-5.3/library/src/main/java/io/opentelemetry/instrumentation/spring/webmvc/v5_3/WebMvcTelemetryProducingFilter.java index 44306a85a5a8..8be211250556 100644 --- a/instrumentation/spring/spring-webmvc-3.1/library/src/main/java/io/opentelemetry/instrumentation/spring/webmvc/WebMvcTracingFilter.java +++ b/instrumentation/spring/spring-webmvc-5.3/library/src/main/java/io/opentelemetry/instrumentation/spring/webmvc/v5_3/WebMvcTelemetryProducingFilter.java @@ -3,11 +3,15 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.instrumentation.spring.webmvc; +package io.opentelemetry.instrumentation.spring.webmvc.v5_3; + +import static io.opentelemetry.instrumentation.api.instrumenter.http.HttpRouteSource.CONTROLLER; +import static java.util.Objects.requireNonNull; import io.opentelemetry.context.Context; import io.opentelemetry.context.Scope; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; +import io.opentelemetry.instrumentation.api.instrumenter.http.HttpRouteHolder; import java.io.IOException; import javax.servlet.FilterChain; import javax.servlet.ServletException; @@ -16,14 +20,27 @@ import org.springframework.core.Ordered; import org.springframework.web.filter.OncePerRequestFilter; -final class WebMvcTracingFilter extends OncePerRequestFilter implements Ordered { +final class WebMvcTelemetryProducingFilter extends OncePerRequestFilter implements Ordered { private final Instrumenter instrumenter; + private final HttpRouteSupport httpRouteSupport = new HttpRouteSupport(); - WebMvcTracingFilter(Instrumenter instrumenter) { + WebMvcTelemetryProducingFilter( + Instrumenter instrumenter) { this.instrumenter = instrumenter; } + @Override + public void afterPropertiesSet() { + // don't do anything, in particular do not call initFilterBean() + } + + @Override + protected void initFilterBean() { + // FilterConfig must be non-null at this point + httpRouteSupport.onFilterInit(requireNonNull(getFilterConfig())); + } + @Override public void doFilterInternal( HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) @@ -36,12 +53,18 @@ public void doFilterInternal( } Context context = instrumenter.start(parentContext, request); + Throwable error = null; try (Scope ignored = context.makeCurrent()) { filterChain.doFilter(request, response); - instrumenter.end(context, request, response, null); } catch (Throwable t) { - instrumenter.end(context, request, response, t); + error = t; throw t; + } finally { + if (httpRouteSupport.hasMappings()) { + HttpRouteHolder.updateHttpRoute( + context, CONTROLLER, httpRouteSupport::getHttpRoute, request); + } + instrumenter.end(context, request, response, error); } } diff --git a/instrumentation/spring/spring-webmvc-5.3/library/src/test/java/io/opentelemetry/instrumentation/spring/webmvc/v5_3/TestWebSpringBootApp.java b/instrumentation/spring/spring-webmvc-5.3/library/src/test/java/io/opentelemetry/instrumentation/spring/webmvc/v5_3/TestWebSpringBootApp.java new file mode 100644 index 000000000000..d0175f9d0c5d --- /dev/null +++ b/instrumentation/spring/spring-webmvc-5.3/library/src/test/java/io/opentelemetry/instrumentation/spring/webmvc/v5_3/TestWebSpringBootApp.java @@ -0,0 +1,130 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.spring.webmvc.v5_3; + +import static io.opentelemetry.instrumentation.testing.junit.http.AbstractHttpServerTest.controller; +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.CAPTURE_HEADERS; +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.ERROR; +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.EXCEPTION; +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.INDEXED_CHILD; +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.PATH_PARAM; +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.QUERY_PARAM; +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.REDIRECT; +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.SUCCESS; +import static java.util.Collections.singletonList; + +import io.opentelemetry.api.GlobalOpenTelemetry; +import io.opentelemetry.instrumentation.testing.junit.http.AbstractHttpServerTest; +import java.util.Properties; +import javax.servlet.Filter; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.servlet.view.RedirectView; + +@SpringBootApplication +class TestWebSpringBootApp { + + static ConfigurableApplicationContext start(int port, String contextPath) { + Properties props = new Properties(); + props.put("server.port", port); + props.put("server.servlet.contextPath", contextPath); + + SpringApplication app = new SpringApplication(TestWebSpringBootApp.class); + app.setDefaultProperties(props); + return app.run(); + } + + @Bean + Filter telemetryFilter() { + return SpringWebMvcTelemetry.builder(GlobalOpenTelemetry.get()) + .setCapturedRequestHeaders(singletonList(AbstractHttpServerTest.TEST_REQUEST_HEADER)) + .setCapturedResponseHeaders(singletonList(AbstractHttpServerTest.TEST_RESPONSE_HEADER)) + .build() + .createServletFilter(); + } + + @Controller + static class TestController { + + @RequestMapping("/success") + @ResponseBody + String success() { + return controller(SUCCESS, SUCCESS::getBody); + } + + @RequestMapping("/query") + @ResponseBody + String query_param(@RequestParam("some") String param) { + return controller(QUERY_PARAM, () -> "some=" + param); + } + + @RequestMapping("/redirect") + @ResponseBody + RedirectView redirect() { + return controller(REDIRECT, () -> new RedirectView(REDIRECT.getBody())); + } + + @RequestMapping("/error-status") + ResponseEntity error() { + return controller( + ERROR, + () -> new ResponseEntity<>(ERROR.getBody(), HttpStatus.valueOf(ERROR.getStatus()))); + } + + @RequestMapping("/exception") + ResponseEntity exception() { + return controller( + EXCEPTION, + () -> { + throw new RuntimeException(EXCEPTION.getBody()); + }); + } + + @RequestMapping("/captureHeaders") + ResponseEntity capture_headers( + @RequestHeader("X-Test-Request") String testRequestHeader) { + return controller( + CAPTURE_HEADERS, + () -> + ResponseEntity.ok() + .header("X-Test-Response", testRequestHeader) + .body(CAPTURE_HEADERS.getBody())); + } + + @RequestMapping("/path/{id}/param") + @ResponseBody + String path_param(@PathVariable("id") int id) { + return controller(PATH_PARAM, () -> String.valueOf(id)); + } + + @RequestMapping("/child") + @ResponseBody + String indexed_child(@RequestParam("id") String id) { + return controller( + INDEXED_CHILD, + () -> { + INDEXED_CHILD.collectSpanAttributes(name -> "id".equals(name) ? id : null); + return INDEXED_CHILD.getBody(); + }); + } + + @ExceptionHandler + ResponseEntity handleException(Throwable throwable) { + return new ResponseEntity<>(throwable.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR); + } + } +} diff --git a/instrumentation/spring/spring-webmvc-5.3/library/src/test/java/io/opentelemetry/instrumentation/spring/webmvc/v5_3/WebMvcHttpServerTest.java b/instrumentation/spring/spring-webmvc-5.3/library/src/test/java/io/opentelemetry/instrumentation/spring/webmvc/v5_3/WebMvcHttpServerTest.java new file mode 100644 index 000000000000..9283a640d125 --- /dev/null +++ b/instrumentation/spring/spring-webmvc-5.3/library/src/test/java/io/opentelemetry/instrumentation/spring/webmvc/v5_3/WebMvcHttpServerTest.java @@ -0,0 +1,48 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.spring.webmvc.v5_3; + +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.http.AbstractHttpServerTest; +import io.opentelemetry.instrumentation.testing.junit.http.HttpServerInstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.http.HttpServerTestOptions; +import io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.springframework.context.ConfigurableApplicationContext; + +class WebMvcHttpServerTest extends AbstractHttpServerTest { + + private static final String CONTEXT_PATH = "/test"; + + @RegisterExtension + static final InstrumentationExtension testing = HttpServerInstrumentationExtension.forLibrary(); + + @Override + protected ConfigurableApplicationContext setupServer() { + return TestWebSpringBootApp.start(port, CONTEXT_PATH); + } + + @Override + protected void stopServer(ConfigurableApplicationContext applicationContext) { + applicationContext.close(); + } + + @Override + protected void configure(HttpServerTestOptions options) { + options.setContextPath(CONTEXT_PATH); + options.setTestPathParam(true); + // servlet filters don't capture exceptions thrown in controllers + options.setTestException(false); + + options.setExpectedHttpRoute( + endpoint -> { + if (endpoint == ServerEndpoint.PATH_PARAM) { + return CONTEXT_PATH + "/path/{id}/param"; + } + return expectedHttpRoute(endpoint); + }); + } +} 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 205c5b49f1bc..1a5338d0ba95 100644 --- a/instrumentation/spring/spring-ws-2.0/javaagent/build.gradle.kts +++ b/instrumentation/spring/spring-ws-2.0/javaagent/build.gradle.kts @@ -46,3 +46,17 @@ dependencies { testInstrumentation(project(":instrumentation:servlet:servlet-3.0:javaagent")) } + +tasks.withType().configureEach { + // required on jdk17 + jvmArgs("--add-opens=java.base/java.lang=ALL-UNNAMED") + jvmArgs("-XX:+IgnoreUnrecognizedVMOptions") +} + +configurations.testRuntimeClasspath { + resolutionStrategy { + // requires old logback (and therefore also old slf4j) + force("ch.qos.logback:logback-classic:1.2.11") + force("org.slf4j:slf4j-api:1.7.36") + } +} diff --git a/instrumentation/spring/spring-ws-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/ws/SpringWsSingletons.java b/instrumentation/spring/spring-ws-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/ws/SpringWsSingletons.java index 73d7f5b6f347..ada337d9118d 100644 --- a/instrumentation/spring/spring-ws-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/ws/SpringWsSingletons.java +++ b/instrumentation/spring/spring-ws-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/ws/SpringWsSingletons.java @@ -26,7 +26,7 @@ public class SpringWsSingletons { CodeSpanNameExtractor.create(codeAttributesGetter)) .addAttributesExtractor(CodeAttributesExtractor.create(codeAttributesGetter)) .setEnabled(ExperimentalConfig.get().controllerTelemetryEnabled()) - .newInstrumenter(); + .buildInstrumenter(); } public static Instrumenter instrumenter() { diff --git a/instrumentation/spring/starters/jaeger-exporter-starter/README.md b/instrumentation/spring/starters/jaeger-spring-boot-starter/README.md similarity index 87% rename from instrumentation/spring/starters/jaeger-exporter-starter/README.md rename to instrumentation/spring/starters/jaeger-spring-boot-starter/README.md index 4e1f6fb88f20..cf095b36c890 100644 --- a/instrumentation/spring/starters/jaeger-exporter-starter/README.md +++ b/instrumentation/spring/starters/jaeger-spring-boot-starter/README.md @@ -14,20 +14,18 @@ Replace `OPENTELEMETRY_VERSION` with the latest stable [release](https://search. ```xml - io.opentelemetry.instrumentation - opentelemetry-jaeger-exporter-starter + opentelemetry-jaeger-spring-boot-starter OPENTELEMETRY_VERSION - ``` #### Gradle ```groovy -implementation("io.opentelemetry.instrumentation:opentelemetry-jaeger-exporter-starter:OPENTELEMETRY_VERSION") +implementation("io.opentelemetry.instrumentation:opentelemetry-spring-boot-exporter-starter:OPENTELEMETRY_VERSION") ``` ### Starter Guide diff --git a/instrumentation/spring/starters/jaeger-exporter-starter/build.gradle.kts b/instrumentation/spring/starters/jaeger-spring-boot-starter/build.gradle.kts similarity index 53% rename from instrumentation/spring/starters/jaeger-exporter-starter/build.gradle.kts rename to instrumentation/spring/starters/jaeger-spring-boot-starter/build.gradle.kts index f426855737eb..c199378daa1f 100644 --- a/instrumentation/spring/starters/jaeger-exporter-starter/build.gradle.kts +++ b/instrumentation/spring/starters/jaeger-spring-boot-starter/build.gradle.kts @@ -6,9 +6,10 @@ plugins { group = "io.opentelemetry.instrumentation" val versions: Map by project +val springBootVersion = versions["org.springframework.boot"] dependencies { - api("org.springframework.boot:spring-boot-starter:${versions["org.springframework.boot"]}") - api(project(":instrumentation:spring:starters:spring-starter")) + api("org.springframework.boot:spring-boot-starter:$springBootVersion") + api(project(":instrumentation:spring:starters:spring-boot-starter")) api("io.opentelemetry:opentelemetry-exporter-jaeger") } diff --git a/instrumentation/spring/starters/otlp-exporter-starter/README.md b/instrumentation/spring/starters/otlp-exporter-starter/README.md deleted file mode 100644 index 29a1d028fe33..000000000000 --- a/instrumentation/spring/starters/otlp-exporter-starter/README.md +++ /dev/null @@ -1,36 +0,0 @@ -# OpenTelemetry OTLP Exporter Starter - -OpenTelemetry OTLP Exporter Starter is a starter package that includes the opentelemetry-api, opentelemetry-sdk, opentelemetry-extension-annotations, opentelmetry-logging-exporter, opentelemetry-spring-boot-autoconfigurations and spring framework starters required to setup distributed tracing. It also provides the [opentelemetry-exporters-otlp](https://github.com/open-telemetry/opentelemetry-java/tree/main/exporters/otlp) artifact and corresponding exporter auto-configuration. Check out [opentelemetry-spring-boot-autoconfigure](../../spring-boot-autoconfigure/README.md#features) for the list of supported libraries and features. - -## Quickstart - -### Add these dependencies to your project. - -Replace `OPENTELEMETRY_VERSION` with the latest stable [release](https://search.maven.org/search?q=g:io.opentelemetry). - - Minimum version: `1.1.0` - - -#### Maven - -```xml - - - - io.opentelemetry.instrumentation - opentelemetry-otlp-exporter-starter - OPENTELEMETRY_VERSION - - - -``` - -#### Gradle - -```groovy -implementation("io.opentelemetry.instrumentation:opentelemetry-otlp-exporter-starter:OPENTELEMETRY_VERSION") -``` - -### Starter Guide - -Check out [OpenTelemetry Manual Instrumentation](https://opentelemetry.io/docs/instrumentation/java/manual/) to learn more about -using the OpenTelemetry API to instrument your code. diff --git a/instrumentation/spring/starters/otlp-exporter-starter/build.gradle.kts b/instrumentation/spring/starters/otlp-exporter-starter/build.gradle.kts deleted file mode 100644 index 1dba59fe2031..000000000000 --- a/instrumentation/spring/starters/otlp-exporter-starter/build.gradle.kts +++ /dev/null @@ -1,14 +0,0 @@ -plugins { - id("otel.java-conventions") - id("otel.publish-conventions") -} - -group = "io.opentelemetry.instrumentation" - -val versions: Map by project - -dependencies { - api("org.springframework.boot:spring-boot-starter:${versions["org.springframework.boot"]}") - api(project(":instrumentation:spring:starters:spring-starter")) - api("io.opentelemetry:opentelemetry-exporter-otlp") -} diff --git a/instrumentation/spring/starters/spring-starter/README.md b/instrumentation/spring/starters/spring-boot-starter/README.md similarity index 92% rename from instrumentation/spring/starters/spring-starter/README.md rename to instrumentation/spring/starters/spring-boot-starter/README.md index dbd0978b431b..437cd8510706 100644 --- a/instrumentation/spring/starters/spring-starter/README.md +++ b/instrumentation/spring/starters/spring-boot-starter/README.md @@ -11,27 +11,26 @@ This version is compatible with Spring Boot 2.0. Replace `OPENTELEMETRY_VERSION` with the latest stable [release](https://search.maven.org/search?q=g:io.opentelemetry). - Minimum version: `1.1.0` - ### Maven + Add the following dependencies to your `pom.xml` file: ```xml - io.opentelemetry.instrumentation - opentelemetry-spring-starter + opentelemetry-spring-boot-starter OPENTELEMETRY_VERSION - ``` ### Gradle + Add the following dependencies to your gradle.build file: ```groovy -implementation("io.opentelemetry.instrumentation:opentelemetry-spring-starter:OPENTELEMETRY_VERSION") +implementation("io.opentelemetry.instrumentation:opentelemetry-spring-boot-starter:OPENTELEMETRY_VERSION") ``` ### Starter Guide diff --git a/instrumentation/spring/starters/spring-starter/build.gradle.kts b/instrumentation/spring/starters/spring-boot-starter/build.gradle.kts similarity index 60% rename from instrumentation/spring/starters/spring-starter/build.gradle.kts rename to instrumentation/spring/starters/spring-boot-starter/build.gradle.kts index c21740dd58f6..cd1b989913f0 100644 --- a/instrumentation/spring/starters/spring-starter/build.gradle.kts +++ b/instrumentation/spring/starters/spring-boot-starter/build.gradle.kts @@ -6,14 +6,16 @@ plugins { group = "io.opentelemetry.instrumentation" val versions: Map by project +val springBootVersion = versions["org.springframework.boot"] dependencies { - api("org.springframework.boot:spring-boot-starter:${versions["org.springframework.boot"]}") - api("org.springframework.boot:spring-boot-starter-aop:${versions["org.springframework.boot"]}") + api("org.springframework.boot:spring-boot-starter:$springBootVersion") + api("org.springframework.boot:spring-boot-starter-aop:$springBootVersion") api(project(":instrumentation:spring:spring-boot-autoconfigure")) - api("io.opentelemetry:opentelemetry-extension-annotations") api("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure-spi") api("io.opentelemetry:opentelemetry-api") api("io.opentelemetry:opentelemetry-exporter-logging") + api("io.opentelemetry:opentelemetry-exporter-otlp") api("io.opentelemetry:opentelemetry-sdk") + api(project(":instrumentation-annotations")) } diff --git a/instrumentation/spring/starters/zipkin-exporter-starter/README.md b/instrumentation/spring/starters/zipkin-spring-boot-starter/README.md similarity index 93% rename from instrumentation/spring/starters/zipkin-exporter-starter/README.md rename to instrumentation/spring/starters/zipkin-spring-boot-starter/README.md index 1e8ba2a314ff..13bc98ede5aa 100644 --- a/instrumentation/spring/starters/zipkin-exporter-starter/README.md +++ b/instrumentation/spring/starters/zipkin-spring-boot-starter/README.md @@ -16,20 +16,18 @@ Replace `OPENTELEMETRY_VERSION` with the latest stable [release](https://search. ```xml - io.opentelemetry.instrumentation - opentelemetry-zipkin-exporter-starter + opentelemetry-zipkin-spring-boot-starter OPENTELEMETRY_VERSION - ``` #### Gradle ```groovy -implementation("io.opentelemetry.instrumentation:opentelemetry-zipkin-exporter-starter:OPENTELEMETRY_VERSION") +implementation("io.opentelemetry.instrumentation:opentelemetry-zipkin-spring-boot-starter:OPENTELEMETRY_VERSION") ``` ### Starter Guide diff --git a/instrumentation/spring/starters/zipkin-exporter-starter/build.gradle.kts b/instrumentation/spring/starters/zipkin-spring-boot-starter/build.gradle.kts similarity index 53% rename from instrumentation/spring/starters/zipkin-exporter-starter/build.gradle.kts rename to instrumentation/spring/starters/zipkin-spring-boot-starter/build.gradle.kts index 2ad37b33a550..9de0496a94bb 100644 --- a/instrumentation/spring/starters/zipkin-exporter-starter/build.gradle.kts +++ b/instrumentation/spring/starters/zipkin-spring-boot-starter/build.gradle.kts @@ -6,9 +6,10 @@ plugins { group = "io.opentelemetry.instrumentation" val versions: Map by project +val springBootVersion = versions["org.springframework.boot"] dependencies { - api("org.springframework.boot:spring-boot-starter:${versions["org.springframework.boot"]}") - api(project(":instrumentation:spring:starters:spring-starter")) + api("org.springframework.boot:spring-boot-starter:$springBootVersion") + api(project(":instrumentation:spring:starters:spring-boot-starter")) api("io.opentelemetry:opentelemetry-exporter-zipkin") } diff --git a/instrumentation/spymemcached-2.12/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spymemcached/SpymemcachedSingletons.java b/instrumentation/spymemcached-2.12/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spymemcached/SpymemcachedSingletons.java index 35a96745b1bc..29915cde16fa 100644 --- a/instrumentation/spymemcached-2.12/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spymemcached/SpymemcachedSingletons.java +++ b/instrumentation/spymemcached-2.12/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spymemcached/SpymemcachedSingletons.java @@ -25,7 +25,7 @@ public final class SpymemcachedSingletons { INSTRUMENTATION_NAME, DbClientSpanNameExtractor.create(dbAttributesGetter)) .addAttributesExtractor(DbClientAttributesExtractor.create(dbAttributesGetter)) - .newInstrumenter(SpanKindExtractor.alwaysClient()); + .buildInstrumenter(SpanKindExtractor.alwaysClient()); } public static Instrumenter instrumenter() { diff --git a/instrumentation/struts-2.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/struts2/StrutsSingletons.java b/instrumentation/struts-2.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/struts2/StrutsSingletons.java index 97b5eb1ae26f..e80083091eb3 100644 --- a/instrumentation/struts-2.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/struts2/StrutsSingletons.java +++ b/instrumentation/struts-2.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/struts2/StrutsSingletons.java @@ -27,7 +27,7 @@ public class StrutsSingletons { CodeSpanNameExtractor.create(codeAttributesGetter)) .addAttributesExtractor(CodeAttributesExtractor.create(codeAttributesGetter)) .setEnabled(ExperimentalConfig.get().controllerTelemetryEnabled()) - .newInstrumenter(); + .buildInstrumenter(); } public static Instrumenter instrumenter() { diff --git a/instrumentation/tapestry-5.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/tapestry/TapestrySingletons.java b/instrumentation/tapestry-5.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/tapestry/TapestrySingletons.java index b9df3d9b73c8..e3e2e279885e 100644 --- a/instrumentation/tapestry-5.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/tapestry/TapestrySingletons.java +++ b/instrumentation/tapestry-5.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/tapestry/TapestrySingletons.java @@ -25,10 +25,10 @@ public class TapestrySingletons { if (error instanceof ComponentEventException) { error = error.getCause(); } - return ErrorCauseExtractor.jdk().extract(error); + return ErrorCauseExtractor.getDefault().extract(error); }) .setEnabled(ExperimentalConfig.get().controllerTelemetryEnabled()) - .newInstrumenter(); + .buildInstrumenter(); } public static Instrumenter instrumenter() { diff --git a/instrumentation/tomcat/tomcat-10.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/tomcat/v10_0/Tomcat10AttachResponseAdvice.java b/instrumentation/tomcat/tomcat-10.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/tomcat/v10_0/Tomcat10AttachResponseAdvice.java index a83c5da92e10..086c49662200 100644 --- a/instrumentation/tomcat/tomcat-10.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/tomcat/v10_0/Tomcat10AttachResponseAdvice.java +++ b/instrumentation/tomcat/tomcat-10.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/tomcat/v10_0/Tomcat10AttachResponseAdvice.java @@ -11,6 +11,7 @@ import org.apache.coyote.Request; import org.apache.coyote.Response; +@SuppressWarnings("unused") public class Tomcat10AttachResponseAdvice { @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) diff --git a/instrumentation/tomcat/tomcat-10.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/tomcat/v10_0/Tomcat10InstrumentationModule.java b/instrumentation/tomcat/tomcat-10.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/tomcat/v10_0/Tomcat10InstrumentationModule.java index 7f77d7485af1..22ac157bcac2 100644 --- a/instrumentation/tomcat/tomcat-10.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/tomcat/v10_0/Tomcat10InstrumentationModule.java +++ b/instrumentation/tomcat/tomcat-10.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/tomcat/v10_0/Tomcat10InstrumentationModule.java @@ -11,12 +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.instrumentation.tomcat.common.TomcatInstrumentationModule; import io.opentelemetry.javaagent.instrumentation.tomcat.common.TomcatServerHandlerInstrumentation; import java.util.List; import net.bytebuddy.matcher.ElementMatcher; @AutoService(InstrumentationModule.class) -public class Tomcat10InstrumentationModule extends InstrumentationModule { +public class Tomcat10InstrumentationModule extends TomcatInstrumentationModule { public Tomcat10InstrumentationModule() { super("tomcat", "tomcat-10.0"); diff --git a/instrumentation/tomcat/tomcat-10.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/tomcat/v10_0/Tomcat10ServerHandlerAdvice.java b/instrumentation/tomcat/tomcat-10.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/tomcat/v10_0/Tomcat10ServerHandlerAdvice.java index 6a3dda0fc6af..739f58b94e66 100644 --- a/instrumentation/tomcat/tomcat-10.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/tomcat/v10_0/Tomcat10ServerHandlerAdvice.java +++ b/instrumentation/tomcat/tomcat-10.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/tomcat/v10_0/Tomcat10ServerHandlerAdvice.java @@ -30,7 +30,6 @@ public static void onEnter( } context = helper().start(parentContext, request); - scope = context.makeCurrent(); } diff --git a/instrumentation/tomcat/tomcat-10.0/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/tomcat/v10_0/TomcatHandlerTest.groovy b/instrumentation/tomcat/tomcat-10.0/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/tomcat/v10_0/TomcatHandlerTest.groovy index a190601dcecc..f640e25dae86 100644 --- a/instrumentation/tomcat/tomcat-10.0/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/tomcat/v10_0/TomcatHandlerTest.groovy +++ b/instrumentation/tomcat/tomcat-10.0/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/tomcat/v10_0/TomcatHandlerTest.groovy @@ -5,11 +5,12 @@ package io.opentelemetry.javaagent.instrumentation.tomcat.v10_0 - +import io.opentelemetry.api.common.AttributeKey import io.opentelemetry.instrumentation.test.AgentTestTrait import io.opentelemetry.instrumentation.test.asserts.TraceAssert import io.opentelemetry.instrumentation.test.base.HttpServerTest import io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint +import io.opentelemetry.sdk.trace.data.SpanData import org.apache.catalina.Context import org.apache.catalina.connector.Request import org.apache.catalina.connector.Response @@ -75,6 +76,20 @@ class TomcatHandlerTest extends HttpServerTest implements AgentTestTrait @Override void responseSpan(TraceAssert trace, int index, Object parent, String method, ServerEndpoint endpoint) { + SpanData span = trace.span(0) + String requestHeaders = "" + String responseHeaders = "" + for (Map.Entry entry : span.getAttributes().asMap().entrySet()) { + if (entry.key.key == "http.request.headers") { + requestHeaders = entry.value + } else if (entry.key.key == "http.response.headers") { + responseHeaders = entry.value + } + } + + assert requestHeaders.length() > 0 + assert responseHeaders.length() > 0 + switch (endpoint) { case REDIRECT: redirectSpan(trace, index, parent) @@ -90,7 +105,7 @@ class TomcatHandlerTest extends HttpServerTest implements AgentTestTrait class ErrorHandlerValve extends ErrorReportValve { @Override protected void report(Request request, Response response, Throwable t) { - if (response.getStatus() < 400 || response.getContentWritten() > 0 || !response.setErrorReported()) { + if (response.getStatus() < 400 || response.getContentWritten() > 0 || !response.isError()) { return } try { diff --git a/instrumentation/tomcat/tomcat-7.0/javaagent/build.gradle.kts b/instrumentation/tomcat/tomcat-7.0/javaagent/build.gradle.kts index 2b48089932ac..5e43f949cf93 100644 --- a/instrumentation/tomcat/tomcat-7.0/javaagent/build.gradle.kts +++ b/instrumentation/tomcat/tomcat-7.0/javaagent/build.gradle.kts @@ -32,4 +32,7 @@ dependencies { tasks.withType().configureEach { jvmArgs("-Dotel.instrumentation.servlet.experimental.capture-request-parameters=test-parameter") + // required on jdk17 + jvmArgs("--add-opens=java.base/java.util=ALL-UNNAMED") + jvmArgs("-XX:+IgnoreUnrecognizedVMOptions") } diff --git a/instrumentation/tomcat/tomcat-7.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/tomcat/v7_0/Tomcat7AttachResponseAdvice.java b/instrumentation/tomcat/tomcat-7.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/tomcat/v7_0/Tomcat7AttachResponseAdvice.java index 22dfac2dd3cb..ed7f49bd9752 100644 --- a/instrumentation/tomcat/tomcat-7.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/tomcat/v7_0/Tomcat7AttachResponseAdvice.java +++ b/instrumentation/tomcat/tomcat-7.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/tomcat/v7_0/Tomcat7AttachResponseAdvice.java @@ -11,6 +11,7 @@ import org.apache.coyote.Request; import org.apache.coyote.Response; +@SuppressWarnings("unused") public class Tomcat7AttachResponseAdvice { @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) diff --git a/instrumentation/tomcat/tomcat-7.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/tomcat/v7_0/Tomcat7InstrumentationModule.java b/instrumentation/tomcat/tomcat-7.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/tomcat/v7_0/Tomcat7InstrumentationModule.java index 1a3afb393f50..77241fc00163 100644 --- a/instrumentation/tomcat/tomcat-7.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/tomcat/v7_0/Tomcat7InstrumentationModule.java +++ b/instrumentation/tomcat/tomcat-7.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/tomcat/v7_0/Tomcat7InstrumentationModule.java @@ -12,12 +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.instrumentation.tomcat.common.TomcatInstrumentationModule; import io.opentelemetry.javaagent.instrumentation.tomcat.common.TomcatServerHandlerInstrumentation; import java.util.List; import net.bytebuddy.matcher.ElementMatcher; @AutoService(InstrumentationModule.class) -public class Tomcat7InstrumentationModule extends InstrumentationModule { +public class Tomcat7InstrumentationModule extends TomcatInstrumentationModule { public Tomcat7InstrumentationModule() { super("tomcat", "tomcat-7.0"); diff --git a/instrumentation/tomcat/tomcat-7.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/tomcat/v7_0/Tomcat7ServerHandlerAdvice.java b/instrumentation/tomcat/tomcat-7.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/tomcat/v7_0/Tomcat7ServerHandlerAdvice.java index ead23c643723..288c32219030 100644 --- a/instrumentation/tomcat/tomcat-7.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/tomcat/v7_0/Tomcat7ServerHandlerAdvice.java +++ b/instrumentation/tomcat/tomcat-7.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/tomcat/v7_0/Tomcat7ServerHandlerAdvice.java @@ -30,7 +30,6 @@ public static void onEnter( } context = helper().start(parentContext, request); - scope = context.makeCurrent(); } diff --git a/instrumentation/tomcat/tomcat-7.0/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/tomcat/v7_0/TomcatHandlerTest.groovy b/instrumentation/tomcat/tomcat-7.0/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/tomcat/v7_0/TomcatHandlerTest.groovy index 87d2205f622e..2f4c06b70deb 100644 --- a/instrumentation/tomcat/tomcat-7.0/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/tomcat/v7_0/TomcatHandlerTest.groovy +++ b/instrumentation/tomcat/tomcat-7.0/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/tomcat/v7_0/TomcatHandlerTest.groovy @@ -5,11 +5,12 @@ package io.opentelemetry.javaagent.instrumentation.tomcat.v7_0 - +import io.opentelemetry.api.common.AttributeKey import io.opentelemetry.instrumentation.test.AgentTestTrait import io.opentelemetry.instrumentation.test.asserts.TraceAssert import io.opentelemetry.instrumentation.test.base.HttpServerTest import io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint +import io.opentelemetry.sdk.trace.data.SpanData import org.apache.catalina.Context import org.apache.catalina.connector.Request import org.apache.catalina.connector.Response @@ -75,6 +76,20 @@ class TomcatHandlerTest extends HttpServerTest implements AgentTestTrait @Override void responseSpan(TraceAssert trace, int index, Object parent, String method, ServerEndpoint endpoint) { + SpanData span = trace.span(0) + String requestHeaders = "" + String responseHeaders = "" + for (Map.Entry entry : span.getAttributes().asMap().entrySet()) { + if (entry.key.key == "http.request.headers") { + requestHeaders = entry.value + } else if (entry.key.key == "http.response.headers") { + responseHeaders = entry.value + } + } + + assert requestHeaders.length() > 0 + assert responseHeaders.length() > 0 + switch (endpoint) { case REDIRECT: redirectSpan(trace, index, parent) @@ -90,7 +105,7 @@ class TomcatHandlerTest extends HttpServerTest implements AgentTestTrait class ErrorHandlerValve extends ErrorReportValve { @Override protected void report(Request request, Response response, Throwable t) { - if (response.getStatus() < 400 || response.getContentWritten() > 0 || !response.setErrorReported()) { + if (response.getStatus() < 400 || response.getContentWritten() > 0 || !response.isError()) { return } try { diff --git a/instrumentation/tomcat/tomcat-common/javaagent/build.gradle.kts b/instrumentation/tomcat/tomcat-common/javaagent/build.gradle.kts index 3a3cacd62854..084e3faad71c 100644 --- a/instrumentation/tomcat/tomcat-common/javaagent/build.gradle.kts +++ b/instrumentation/tomcat/tomcat-common/javaagent/build.gradle.kts @@ -3,6 +3,8 @@ plugins { } dependencies { + implementation("org.json:json:20220320") + api(project(":instrumentation:servlet:servlet-common:javaagent")) compileOnly(project(":instrumentation:servlet:servlet-common:bootstrap")) diff --git a/instrumentation/tomcat/tomcat-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/tomcat/common/TomcatHelper.java b/instrumentation/tomcat/tomcat-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/tomcat/common/TomcatHelper.java index 0cc65cee7547..253c2ba118f1 100644 --- a/instrumentation/tomcat/tomcat-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/tomcat/common/TomcatHelper.java +++ b/instrumentation/tomcat/tomcat-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/tomcat/common/TomcatHelper.java @@ -5,13 +5,18 @@ package io.opentelemetry.javaagent.instrumentation.tomcat.common; +import io.opentelemetry.api.trace.Span; import io.opentelemetry.context.Context; import io.opentelemetry.context.Scope; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; import io.opentelemetry.javaagent.bootstrap.servlet.AppServerBridge; import io.opentelemetry.javaagent.instrumentation.servlet.ServletHelper; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Map; import org.apache.coyote.Request; import org.apache.coyote.Response; +import org.apache.tomcat.util.buf.MessageBytes; public class TomcatHelper { protected final Instrumenter instrumenter; @@ -66,4 +71,52 @@ public void attachResponseToRequest(Request request, Response response) { servletHelper.setAsyncListenerResponse(servletRequest, servletResponse); } } + + static String messageBytesToString(MessageBytes messageBytes) { + // on tomcat 10.1.0 MessageBytes.toString() has a side effect. Calling it caches the string + // value and changes type of the MessageBytes from T_BYTES to T_STR which breaks request + // processing in CoyoteAdapter.postParseRequest when it is called on MessageBytes from + // request.requestURI(). + if (messageBytes.getType() == MessageBytes.T_BYTES) { + return messageBytes.getByteChunk().toString(); + } + return messageBytes.toString(); + } + + public void attachRequestHeadersToSpan(Request request, Span span) { + Map requestHeaders = this.extractRequestHeaders(request); + span.setAttribute("http.request.headers", String.valueOf(requestHeaders)); + } + + public void attachResponseHeadersToSpan(Response response, Span span) { + Map responseHeaders = this.extractResponseHeaders(response); + span.setAttribute("http.response.headers", String.valueOf(responseHeaders)); + } + + private Map extractRequestHeaders(Request request) { + Enumeration requestHeaderNames = request.getMimeHeaders().names(); + Map requestHeaders = new HashMap<>(); + + if (requestHeaderNames != null) { + while (requestHeaderNames.hasMoreElements()) { + String headerName = requestHeaderNames.nextElement(); + requestHeaders.put(headerName, request.getHeader(headerName)); + } + } + + return requestHeaders; + } + + private Map extractResponseHeaders(Response response) { + Map responseHeaders = new HashMap<>(); + Enumeration responseHeaderNames = response.getMimeHeaders().names(); + if (responseHeaderNames != null) { + while (responseHeaderNames.hasMoreElements()) { + String headerName = responseHeaderNames.nextElement(); + responseHeaders.put(headerName, response.getMimeHeaders().getHeader(headerName)); + } + } + + return responseHeaders; + } } diff --git a/instrumentation/tomcat/tomcat-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/tomcat/common/TomcatHttpAttributesGetter.java b/instrumentation/tomcat/tomcat-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/tomcat/common/TomcatHttpAttributesGetter.java index 486e7ff97e11..7035a0bcc6e6 100644 --- a/instrumentation/tomcat/tomcat-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/tomcat/common/TomcatHttpAttributesGetter.java +++ b/instrumentation/tomcat/tomcat-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/tomcat/common/TomcatHttpAttributesGetter.java @@ -5,26 +5,33 @@ package io.opentelemetry.javaagent.instrumentation.tomcat.common; +import static io.opentelemetry.javaagent.instrumentation.tomcat.common.TomcatHelper.messageBytesToString; + import io.opentelemetry.instrumentation.api.instrumenter.http.HttpServerAttributesGetter; import java.util.Collections; import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; import javax.annotation.Nullable; import org.apache.coyote.Request; import org.apache.coyote.Response; import org.apache.tomcat.util.buf.MessageBytes; +import org.apache.tomcat.util.http.MimeHeaders; +import org.json.JSONObject; public class TomcatHttpAttributesGetter implements HttpServerAttributesGetter { @Override public String method(Request request) { - return request.method().toString(); + return messageBytesToString(request.method()); } @Override @Nullable public String target(Request request) { - String target = request.requestURI().toString(); - String queryString = request.queryString().toString(); + String target = messageBytesToString(request.requestURI()); + String queryString = messageBytesToString(request.queryString()); if (queryString != null) { target += "?" + queryString; } @@ -35,7 +42,7 @@ public String target(Request request) { @Nullable public String scheme(Request request) { MessageBytes schemeMessageBytes = request.scheme(); - return schemeMessageBytes.isNull() ? "http" : schemeMessageBytes.toString(); + return schemeMessageBytes.isNull() ? "http" : messageBytesToString(schemeMessageBytes); } @Override @@ -43,23 +50,10 @@ public List requestHeader(Request request, String name) { return Collections.list(request.getMimeHeaders().values(name)); } - @Override - @Nullable - public Long requestContentLength(Request request, @Nullable Response response) { - long contentLength = request.getContentLengthLong(); - return contentLength != -1 ? contentLength : null; - } - - @Override - @Nullable - public Long requestContentLengthUncompressed(Request request, @Nullable Response response) { - return null; - } - @Override @Nullable public String flavor(Request request) { - String flavor = request.protocol().toString(); + String flavor = messageBytesToString(request.protocol()); if (flavor != null) { // remove HTTP/ prefix to comply with semantic conventions if (flavor.startsWith("HTTP/")) { @@ -71,23 +65,10 @@ public String flavor(Request request) { @Override @Nullable - public Integer statusCode(Request request, Response response) { + public Integer statusCode(Request request, Response response, @Nullable Throwable error) { return response.getStatus(); } - @Override - @Nullable - public Long responseContentLength(Request request, Response response) { - long contentLength = response.getContentLengthLong(); - return contentLength != -1 ? contentLength : null; - } - - @Override - @Nullable - public Long responseContentLengthUncompressed(Request request, Response response) { - return null; - } - @Override public List responseHeader(Request request, Response response, String name) { return Collections.list(response.getMimeHeaders().values(name)); @@ -104,4 +85,25 @@ public String route(Request request) { public String serverName(Request request) { return null; } + + @Nullable + @Override + public String requestHeaders(Request request, @Nullable Response response) { + return toJsonString(mimeHeadersToMap(request.getMimeHeaders())); + } + + @Nullable + @Override + public String responseHeaders(Request request, Response response) { + return toJsonString(mimeHeadersToMap(response.getMimeHeaders())); + } + + private Map mimeHeadersToMap(MimeHeaders headers) { + return Collections.list(headers.names()).stream() + .collect(Collectors.toMap(Function.identity(), headers::getHeader)); + } + + private static String toJsonString(Map m) { + return new JSONObject(m).toString(); + } } diff --git a/instrumentation/tomcat/tomcat-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/tomcat/common/TomcatInstrumentationModule.java b/instrumentation/tomcat/tomcat-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/tomcat/common/TomcatInstrumentationModule.java new file mode 100644 index 000000000000..f7f142516b41 --- /dev/null +++ b/instrumentation/tomcat/tomcat-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/tomcat/common/TomcatInstrumentationModule.java @@ -0,0 +1,21 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.tomcat.common; + +import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule; + +public abstract class TomcatInstrumentationModule extends InstrumentationModule { + + public TomcatInstrumentationModule( + String mainInstrumentationName, String... additionalInstrumentationNames) { + super(mainInstrumentationName, additionalInstrumentationNames); + } + + @Override + public boolean isHelperClass(String className) { + return className.startsWith("org.json"); + } +} diff --git a/instrumentation/tomcat/tomcat-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/tomcat/common/TomcatInstrumenterFactory.java b/instrumentation/tomcat/tomcat-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/tomcat/common/TomcatInstrumenterFactory.java index 322b91202f2d..a9959b5fef5c 100644 --- a/instrumentation/tomcat/tomcat-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/tomcat/common/TomcatInstrumenterFactory.java +++ b/instrumentation/tomcat/tomcat-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/tomcat/common/TomcatInstrumenterFactory.java @@ -12,7 +12,7 @@ import io.opentelemetry.instrumentation.api.instrumenter.http.HttpServerMetrics; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanNameExtractor; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanStatusExtractor; -import io.opentelemetry.instrumentation.api.instrumenter.net.NetServerAttributesExtractor; +import io.opentelemetry.javaagent.bootstrap.internal.CommonConfig; import io.opentelemetry.javaagent.bootstrap.servlet.AppServerBridge; import io.opentelemetry.javaagent.instrumentation.servlet.ServletAccessor; import io.opentelemetry.javaagent.instrumentation.servlet.ServletErrorCauseExtractor; @@ -34,8 +34,11 @@ public static Instrumenter create( HttpSpanNameExtractor.create(httpAttributesGetter)) .setSpanStatusExtractor(HttpSpanStatusExtractor.create(httpAttributesGetter)) .setErrorCauseExtractor(new ServletErrorCauseExtractor<>(accessor)) - .addAttributesExtractor(HttpServerAttributesExtractor.create(httpAttributesGetter)) - .addAttributesExtractor(NetServerAttributesExtractor.create(netAttributesGetter)) + .addAttributesExtractor( + HttpServerAttributesExtractor.builder(httpAttributesGetter, netAttributesGetter) + .setCapturedRequestHeaders(CommonConfig.get().getServerRequestHeaders()) + .setCapturedResponseHeaders(CommonConfig.get().getServerResponseHeaders()) + .build()) .addContextCustomizer(HttpRouteHolder.get()) .addContextCustomizer( (context, request, attributes) -> @@ -44,6 +47,6 @@ public static Instrumenter create( .recordException() .init(context)) .addOperationMetrics(HttpServerMetrics.get()) - .newServerInstrumenter(TomcatRequestGetter.INSTANCE); + .buildServerInstrumenter(TomcatRequestGetter.INSTANCE); } } diff --git a/instrumentation/tomcat/tomcat-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/tomcat/common/TomcatNetAttributesGetter.java b/instrumentation/tomcat/tomcat-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/tomcat/common/TomcatNetAttributesGetter.java index 0ac80ec45ad9..e75d60055224 100644 --- a/instrumentation/tomcat/tomcat-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/tomcat/common/TomcatNetAttributesGetter.java +++ b/instrumentation/tomcat/tomcat-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/tomcat/common/TomcatNetAttributesGetter.java @@ -5,6 +5,8 @@ package io.opentelemetry.javaagent.instrumentation.tomcat.common; +import static io.opentelemetry.javaagent.instrumentation.tomcat.common.TomcatHelper.messageBytesToString; + import io.opentelemetry.instrumentation.api.instrumenter.net.NetServerAttributesGetter; import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; import javax.annotation.Nullable; @@ -19,17 +21,42 @@ public String transport(Request request) { return SemanticAttributes.NetTransportValues.IP_TCP; } + @Nullable + @Override + public String hostName(Request request) { + return messageBytesToString(request.serverName()); + } + + @Override + public Integer hostPort(Request request) { + return request.getServerPort(); + } + + @Override + @Nullable + public String sockPeerAddr(Request request) { + request.action(ActionCode.REQ_HOST_ADDR_ATTRIBUTE, request); + return messageBytesToString(request.remoteAddr()); + } + @Override @Nullable - public Integer peerPort(Request request) { + public Integer sockPeerPort(Request request) { request.action(ActionCode.REQ_REMOTEPORT_ATTRIBUTE, request); return request.getRemotePort(); } + @Nullable @Override + public String sockHostAddr(Request request) { + request.action(ActionCode.REQ_LOCAL_ADDR_ATTRIBUTE, request); + return messageBytesToString(request.localAddr()); + } + @Nullable - public String peerIp(Request request) { - request.action(ActionCode.REQ_HOST_ADDR_ATTRIBUTE, request); - return request.remoteAddr().toString(); + @Override + public Integer sockHostPort(Request request) { + request.action(ActionCode.REQ_LOCALPORT_ATTRIBUTE, request); + return request.getLocalPort(); } } diff --git a/instrumentation/tomcat/tomcat-jdbc/src/test/java/io/opentelemetry/javaagent/instrumentation/tomcat/jdbc/TomcatJdbcInstrumentationTest.java b/instrumentation/tomcat/tomcat-jdbc/src/test/java/io/opentelemetry/javaagent/instrumentation/tomcat/jdbc/TomcatJdbcInstrumentationTest.java index c2902c228fe8..5d0765d153ef 100644 --- a/instrumentation/tomcat/tomcat-jdbc/src/test/java/io/opentelemetry/javaagent/instrumentation/tomcat/jdbc/TomcatJdbcInstrumentationTest.java +++ b/instrumentation/tomcat/tomcat-jdbc/src/test/java/io/opentelemetry/javaagent/instrumentation/tomcat/jdbc/TomcatJdbcInstrumentationTest.java @@ -6,7 +6,7 @@ package io.opentelemetry.javaagent.instrumentation.tomcat.jdbc; import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.when; import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension; import io.opentelemetry.instrumentation.testing.junit.db.DbConnectionPoolMetricsAssertions; @@ -31,7 +31,7 @@ public class TomcatJdbcInstrumentationTest { @Test void shouldReportMetrics() throws Exception { // given - given(dataSourceMock.getConnection()).willReturn(connectionMock); + when(dataSourceMock.getConnection()).thenReturn(connectionMock); DataSource tomcatDataSource = new DataSource(); tomcatDataSource.setDataSource(dataSourceMock); diff --git a/instrumentation/twilio-6.6/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/twilio/TwilioSingletons.java b/instrumentation/twilio-6.6/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/twilio/TwilioSingletons.java index 18635e21f716..e77644850869 100644 --- a/instrumentation/twilio-6.6/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/twilio/TwilioSingletons.java +++ b/instrumentation/twilio-6.6/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/twilio/TwilioSingletons.java @@ -10,10 +10,10 @@ import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; import io.opentelemetry.instrumentation.api.instrumenter.InstrumenterBuilder; -import io.opentelemetry.instrumentation.api.util.SpanNames; +import io.opentelemetry.instrumentation.api.instrumenter.util.SpanNames; import io.opentelemetry.javaagent.bootstrap.internal.InstrumentationConfig; -public class TwilioSingletons { +public final class TwilioSingletons { private static final boolean CAPTURE_EXPERIMENTAL_SPAN_ATTRIBUTES = InstrumentationConfig.get() @@ -23,14 +23,13 @@ public class TwilioSingletons { static { InstrumenterBuilder instrumenterBuilder = - Instrumenter.builder( - GlobalOpenTelemetry.get(), "io.opentelemetry.twilio-6.6", str -> str); + Instrumenter.builder(GlobalOpenTelemetry.get(), "io.opentelemetry.twilio-6.6", str -> str); if (CAPTURE_EXPERIMENTAL_SPAN_ATTRIBUTES) { instrumenterBuilder.addAttributesExtractor(new TwilioExperimentalAttributesExtractor()); } - INSTRUMENTER = instrumenterBuilder.newInstrumenter(alwaysClient()); + INSTRUMENTER = instrumenterBuilder.buildInstrumenter(alwaysClient()); } public static Instrumenter instrumenter() { @@ -41,4 +40,6 @@ public static Instrumenter instrumenter() { public static String spanName(Object serviceExecutor, String methodName) { return SpanNames.fromMethod(serviceExecutor.getClass(), methodName); } + + private TwilioSingletons() {} } diff --git a/instrumentation/undertow-1.4/javaagent/build.gradle.kts b/instrumentation/undertow-1.4/javaagent/build.gradle.kts index 4e4daaee1d73..d8f8637ba6dc 100644 --- a/instrumentation/undertow-1.4/javaagent/build.gradle.kts +++ b/instrumentation/undertow-1.4/javaagent/build.gradle.kts @@ -14,6 +14,8 @@ muzzle { dependencies { library("io.undertow:undertow-core:2.0.0.Final") + implementation("org.json:json:20220320") + bootstrap(project(":instrumentation:executors:bootstrap")) bootstrap(project(":instrumentation:servlet:servlet-common:bootstrap")) bootstrap(project(":instrumentation:undertow-1.4:bootstrap")) diff --git a/instrumentation/undertow-1.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/undertow/UndertowHttpAttributesGetter.java b/instrumentation/undertow-1.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/undertow/UndertowHttpAttributesGetter.java index bcb4096484f8..a9bece8fb006 100644 --- a/instrumentation/undertow-1.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/undertow/UndertowHttpAttributesGetter.java +++ b/instrumentation/undertow-1.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/undertow/UndertowHttpAttributesGetter.java @@ -8,9 +8,14 @@ import io.opentelemetry.instrumentation.api.instrumenter.http.HttpServerAttributesGetter; import io.undertow.server.HttpServerExchange; import io.undertow.util.HeaderValues; +import io.undertow.util.HttpString; import java.util.Collections; import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; import javax.annotation.Nullable; +import org.json.JSONObject; public class UndertowHttpAttributesGetter implements HttpServerAttributesGetter { @@ -26,19 +31,31 @@ public List requestHeader(HttpServerExchange exchange, String name) { return values == null ? Collections.emptyList() : values; } + private static String firstListValue(List values) { + return values.isEmpty() ? "" : values.get(0); + } + @Override @Nullable - public Long requestContentLength( - HttpServerExchange exchange, @Nullable HttpServerExchange unused) { - long requestContentLength = exchange.getRequestContentLength(); - return requestContentLength != -1 ? requestContentLength : null; + public String requestHeaders(HttpServerExchange exchange, HttpServerExchange unused) { + return toJsonString( + exchange.getRequestHeaders().getHeaderNames().stream() + .map(HttpString::toString) + .collect( + Collectors.toMap( + Function.identity(), (h) -> firstListValue(requestHeader(exchange, h))))); } @Override @Nullable - public Long requestContentLengthUncompressed( - HttpServerExchange exchange, @Nullable HttpServerExchange unused) { - return null; + public String responseHeaders(HttpServerExchange unused, HttpServerExchange exchange) { + return toJsonString( + exchange.getResponseHeaders().getHeaderNames().stream() + .map(HttpString::toString) + .collect( + Collectors.toMap( + Function.identity(), + (h) -> firstListValue(responseHeader(exchange, exchange, h))))); } @Override @@ -52,24 +69,11 @@ public String flavor(HttpServerExchange exchange) { } @Override - public Integer statusCode(HttpServerExchange exchange, HttpServerExchange unused) { + public Integer statusCode( + HttpServerExchange exchange, HttpServerExchange unused, @Nullable Throwable error) { return exchange.getStatusCode(); } - @Override - @Nullable - public Long responseContentLength(HttpServerExchange exchange, HttpServerExchange unused) { - long responseContentLength = exchange.getResponseContentLength(); - return responseContentLength != -1 ? responseContentLength : null; - } - - @Override - @Nullable - public Long responseContentLengthUncompressed( - HttpServerExchange exchange, HttpServerExchange unused) { - return null; - } - @Override public List responseHeader( HttpServerExchange exchange, HttpServerExchange unused, String name) { @@ -100,9 +104,8 @@ public String route(HttpServerExchange exchange) { return null; } - @Override @Nullable - public String serverName(HttpServerExchange exchange) { - return null; + static String toJsonString(Map m) { + return new JSONObject(m).toString(); } } diff --git a/instrumentation/undertow-1.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/undertow/UndertowInstrumentationModule.java b/instrumentation/undertow-1.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/undertow/UndertowInstrumentationModule.java index 4810eca4dd3c..b09c5470587c 100644 --- a/instrumentation/undertow-1.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/undertow/UndertowInstrumentationModule.java +++ b/instrumentation/undertow-1.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/undertow/UndertowInstrumentationModule.java @@ -19,6 +19,12 @@ public UndertowInstrumentationModule() { super("undertow", "undertow-1.4"); } + @Override + public boolean isHelperClass(String className) { + return className.startsWith("com.opentelemetry.javaagent.instrumentation.undertow") + || className.startsWith("org.json"); + } + @Override public List typeInstrumentations() { return asList(new HandlerInstrumentation(), new HttpServerExchangeInstrumentation()); diff --git a/instrumentation/undertow-1.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/undertow/UndertowNetAttributesGetter.java b/instrumentation/undertow-1.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/undertow/UndertowNetAttributesGetter.java index f4fe5e4a61cf..73044ace65b8 100644 --- a/instrumentation/undertow-1.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/undertow/UndertowNetAttributesGetter.java +++ b/instrumentation/undertow-1.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/undertow/UndertowNetAttributesGetter.java @@ -15,13 +15,31 @@ public class UndertowNetAttributesGetter extends InetSocketAddressNetServerAttributesGetter { @Override + public String transport(HttpServerExchange exchange) { + return SemanticAttributes.NetTransportValues.IP_TCP; + } + + @Nullable + @Override + public String hostName(HttpServerExchange exchange) { + return exchange.getHostName(); + } + @Nullable - public InetSocketAddress getAddress(HttpServerExchange exchange) { + @Override + public Integer hostPort(HttpServerExchange exchange) { + return exchange.getHostPort(); + } + + @Override + @Nullable + protected InetSocketAddress getPeerSocketAddress(HttpServerExchange exchange) { return exchange.getConnection().getPeerAddress(InetSocketAddress.class); } + @Nullable @Override - public String transport(HttpServerExchange exchange) { - return SemanticAttributes.NetTransportValues.IP_TCP; + protected InetSocketAddress getHostSocketAddress(HttpServerExchange exchange) { + return exchange.getConnection().getLocalAddress(InetSocketAddress.class); } } diff --git a/instrumentation/undertow-1.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/undertow/UndertowSingletons.java b/instrumentation/undertow-1.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/undertow/UndertowSingletons.java index 69ad8a3802d9..06be9d773008 100644 --- a/instrumentation/undertow-1.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/undertow/UndertowSingletons.java +++ b/instrumentation/undertow-1.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/undertow/UndertowSingletons.java @@ -12,7 +12,7 @@ import io.opentelemetry.instrumentation.api.instrumenter.http.HttpServerMetrics; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanNameExtractor; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanStatusExtractor; -import io.opentelemetry.instrumentation.api.instrumenter.net.NetServerAttributesExtractor; +import io.opentelemetry.javaagent.bootstrap.internal.CommonConfig; import io.opentelemetry.javaagent.bootstrap.servlet.AppServerBridge; import io.opentelemetry.javaagent.bootstrap.undertow.UndertowActiveHandlers; import io.undertow.server.HttpServerExchange; @@ -32,8 +32,11 @@ public final class UndertowSingletons { INSTRUMENTATION_NAME, HttpSpanNameExtractor.create(httpAttributesGetter)) .setSpanStatusExtractor(HttpSpanStatusExtractor.create(httpAttributesGetter)) - .addAttributesExtractor(HttpServerAttributesExtractor.create(httpAttributesGetter)) - .addAttributesExtractor(NetServerAttributesExtractor.create(netAttributesGetter)) + .addAttributesExtractor( + HttpServerAttributesExtractor.builder(httpAttributesGetter, netAttributesGetter) + .setCapturedRequestHeaders(CommonConfig.get().getServerRequestHeaders()) + .setCapturedResponseHeaders(CommonConfig.get().getServerResponseHeaders()) + .build()) .addContextCustomizer(HttpRouteHolder.get()) .addContextCustomizer( (context, request, attributes) -> { @@ -47,7 +50,7 @@ public final class UndertowSingletons { .init(context); }) .addOperationMetrics(HttpServerMetrics.get()) - .newServerInstrumenter(UndertowExchangeGetter.INSTANCE); + .buildServerInstrumenter(UndertowExchangeGetter.INSTANCE); } private static final UndertowHelper HELPER = new UndertowHelper(INSTRUMENTER); diff --git a/instrumentation/undertow-1.4/javaagent/src/test/groovy/UndertowServerDispatchTest.groovy b/instrumentation/undertow-1.4/javaagent/src/test/groovy/UndertowServerDispatchTest.groovy index e262fb3cfb95..40d3a14a1132 100644 --- a/instrumentation/undertow-1.4/javaagent/src/test/groovy/UndertowServerDispatchTest.groovy +++ b/instrumentation/undertow-1.4/javaagent/src/test/groovy/UndertowServerDispatchTest.groovy @@ -101,7 +101,6 @@ class UndertowServerDispatchTest extends HttpServerTest implements Age Set> httpAttributes(ServerEndpoint endpoint) { def attributes = super.httpAttributes(endpoint) attributes.remove(SemanticAttributes.HTTP_ROUTE) - attributes.add(SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH) attributes } } diff --git a/instrumentation/undertow-1.4/javaagent/src/test/groovy/UndertowServerTest.groovy b/instrumentation/undertow-1.4/javaagent/src/test/groovy/UndertowServerTest.groovy index a07e616c2378..f3c74f4f9ee8 100644 --- a/instrumentation/undertow-1.4/javaagent/src/test/groovy/UndertowServerTest.groovy +++ b/instrumentation/undertow-1.4/javaagent/src/test/groovy/UndertowServerTest.groovy @@ -19,6 +19,7 @@ import io.undertow.util.HttpString import io.undertow.util.StatusCodes import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.CAPTURE_HEADERS +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.CAPTURE_HEADERS_AS_JSON import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.ERROR import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.EXCEPTION import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.INDEXED_CHILD @@ -29,6 +30,11 @@ import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint //TODO make test which mixes handlers and servlets class UndertowServerTest extends HttpServerTest implements AgentTestTrait { + @Override + boolean testCapturedHttpHeadersAsJson() { + return true + } + @Override Undertow startServer(int port) { Undertow server = Undertow.builder() @@ -58,6 +64,12 @@ class UndertowServerTest extends HttpServerTest implements AgentTestTr exchange.getResponseSender().send(CAPTURE_HEADERS.body) } } + .addExactPath(CAPTURE_HEADERS_AS_JSON.rawPath()) { exchange -> + controller(CAPTURE_HEADERS_AS_JSON) { + exchange.setStatusCode(StatusCodes.OK) + exchange.getResponseSender().send(CAPTURE_HEADERS_AS_JSON.body) + } + } .addExactPath(ERROR.rawPath()) { exchange -> controller(ERROR) { exchange.setStatusCode(ERROR.status) @@ -110,7 +122,6 @@ class UndertowServerTest extends HttpServerTest implements AgentTestTr Set> httpAttributes(ServerEndpoint endpoint) { def attributes = super.httpAttributes(endpoint) attributes.remove(SemanticAttributes.HTTP_ROUTE) - attributes.add(SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH) attributes } @@ -139,23 +150,24 @@ class UndertowServerTest extends HttpServerTest implements AgentTestTr } attributes { - "$SemanticAttributes.NET_PEER_PORT" { it instanceof Long } - "$SemanticAttributes.NET_PEER_IP" "127.0.0.1" "$SemanticAttributes.HTTP_CLIENT_IP" TEST_CLIENT_IP "$SemanticAttributes.HTTP_SCHEME" uri.getScheme() - "$SemanticAttributes.HTTP_HOST" uri.getHost() + ":" + uri.getPort() "$SemanticAttributes.HTTP_TARGET" uri.getPath() "$SemanticAttributes.HTTP_METHOD" "GET" "$SemanticAttributes.HTTP_STATUS_CODE" 200 "$SemanticAttributes.HTTP_FLAVOR" "1.1" "$SemanticAttributes.HTTP_USER_AGENT" TEST_USER_AGENT - "$SemanticAttributes.HTTP_HOST" "localhost:${port}" "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" Long "$SemanticAttributes.HTTP_SCHEME" "http" "$SemanticAttributes.HTTP_TARGET" "/sendResponse" - // net.peer.name resolves to "127.0.0.1" on windows which is same as net.peer.ip so then not captured - "$SemanticAttributes.NET_PEER_NAME" { it == "localhost" || it == null } "$SemanticAttributes.NET_TRANSPORT" SemanticAttributes.NetTransportValues.IP_TCP + "$SemanticAttributes.NET_HOST_NAME" uri.host + "$SemanticAttributes.NET_HOST_PORT" uri.port + "$SemanticAttributes.NET_SOCK_PEER_ADDR" "127.0.0.1" + "$SemanticAttributes.NET_SOCK_PEER_PORT" Long + "$SemanticAttributes.NET_SOCK_HOST_ADDR" "127.0.0.1" + "http.request.headers" { it != null } + "http.response.headers" { it != null } } } span(1) { @@ -194,23 +206,24 @@ class UndertowServerTest extends HttpServerTest implements AgentTestTr errorEvent(Exception, "exception after sending response", 2) attributes { - "$SemanticAttributes.NET_PEER_PORT" { it instanceof Long } - "$SemanticAttributes.NET_PEER_IP" "127.0.0.1" "$SemanticAttributes.HTTP_CLIENT_IP" TEST_CLIENT_IP "$SemanticAttributes.HTTP_SCHEME" uri.getScheme() - "$SemanticAttributes.HTTP_HOST" uri.getHost() + ":" + uri.getPort() "$SemanticAttributes.HTTP_TARGET" uri.getPath() "$SemanticAttributes.HTTP_METHOD" "GET" "$SemanticAttributes.HTTP_STATUS_CODE" 200 "$SemanticAttributes.HTTP_FLAVOR" "1.1" "$SemanticAttributes.HTTP_USER_AGENT" TEST_USER_AGENT - "$SemanticAttributes.HTTP_HOST" "localhost:${port}" "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" Long "$SemanticAttributes.HTTP_SCHEME" "http" "$SemanticAttributes.HTTP_TARGET" "/sendResponseWithException" - // net.peer.name resolves to "127.0.0.1" on windows which is same as net.peer.ip so then not captured - "$SemanticAttributes.NET_PEER_NAME" { it == "localhost" || it == null } "$SemanticAttributes.NET_TRANSPORT" SemanticAttributes.NetTransportValues.IP_TCP + "$SemanticAttributes.NET_HOST_NAME" uri.host + "$SemanticAttributes.NET_HOST_PORT" uri.port + "$SemanticAttributes.NET_SOCK_PEER_ADDR" "127.0.0.1" + "$SemanticAttributes.NET_SOCK_PEER_PORT" Long + "$SemanticAttributes.NET_SOCK_HOST_ADDR" "127.0.0.1" + "http.request.headers" { it != null } + "http.response.headers" { it != null } } } span(1) { diff --git a/instrumentation/vaadin-14.2/javaagent/build.gradle.kts b/instrumentation/vaadin-14.2/javaagent/build.gradle.kts index a064cb4ae2ea..329be5272791 100644 --- a/instrumentation/vaadin-14.2/javaagent/build.gradle.kts +++ b/instrumentation/vaadin-14.2/javaagent/build.gradle.kts @@ -46,7 +46,7 @@ tasks { if (findProperty("testLatestDeps") as Boolean) { dependsOn(vaadin14LatestTest) } - usesService(gradle.sharedServices.registrations["testcontainersBuildService"].getService()) + usesService(gradle.sharedServices.registrations["testcontainersBuildService"].service) } } @@ -69,7 +69,23 @@ dependencies { add("vaadin14LatestTestImplementation", "com.vaadin:vaadin-spring-boot-starter:14.+") add("latestDepTestImplementation", "com.vaadin:vaadin-spring-boot-starter:+") - // to work around https://github.com/vaadin/flow/issues/13952 - // can be removed after a new version of vaadin-spring-boot-starter has been released - add("latestDepTestImplementation", "com.vaadin:flow-server:+") +} + +configurations { + listOf( + testRuntimeClasspath, + named("vaadin142TestRuntimeClasspath"), + named("vaadin14LatestTestRuntimeClasspath"), + named("vaadin16TestRuntimeClasspath"), + named("latestDepTestRuntimeClasspath") + ) + .forEach { + it.configure { + resolutionStrategy { + // requires old logback (and therefore also old slf4j) + force("ch.qos.logback:logback-classic:1.2.11") + force("org.slf4j:slf4j-api:1.7.36") + } + } + } } diff --git a/instrumentation/vaadin-14.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vaadin/VaadinHandlerRequest.java b/instrumentation/vaadin-14.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vaadin/VaadinHandlerRequest.java index 9d0bbd96ee07..e4d599322f77 100644 --- a/instrumentation/vaadin-14.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vaadin/VaadinHandlerRequest.java +++ b/instrumentation/vaadin-14.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vaadin/VaadinHandlerRequest.java @@ -6,7 +6,7 @@ package io.opentelemetry.javaagent.instrumentation.vaadin; import com.google.auto.value.AutoValue; -import io.opentelemetry.instrumentation.api.util.SpanNames; +import io.opentelemetry.instrumentation.api.instrumenter.util.SpanNames; @AutoValue public abstract class VaadinHandlerRequest { diff --git a/instrumentation/vaadin-14.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vaadin/VaadinServiceRequest.java b/instrumentation/vaadin-14.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vaadin/VaadinServiceRequest.java index ce383327eaec..12fbadb69b7e 100644 --- a/instrumentation/vaadin-14.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vaadin/VaadinServiceRequest.java +++ b/instrumentation/vaadin-14.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vaadin/VaadinServiceRequest.java @@ -6,7 +6,7 @@ package io.opentelemetry.javaagent.instrumentation.vaadin; import com.google.auto.value.AutoValue; -import io.opentelemetry.instrumentation.api.util.SpanNames; +import io.opentelemetry.instrumentation.api.instrumenter.util.SpanNames; @AutoValue public abstract class VaadinServiceRequest { diff --git a/instrumentation/vaadin-14.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vaadin/VaadinSingletons.java b/instrumentation/vaadin-14.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vaadin/VaadinSingletons.java index b88a44ad54a0..4b46dd63bf60 100644 --- a/instrumentation/vaadin-14.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vaadin/VaadinSingletons.java +++ b/instrumentation/vaadin-14.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vaadin/VaadinSingletons.java @@ -11,7 +11,7 @@ import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; import io.opentelemetry.instrumentation.api.instrumenter.code.CodeAttributesExtractor; import io.opentelemetry.instrumentation.api.instrumenter.code.CodeSpanNameExtractor; -import io.opentelemetry.instrumentation.api.util.SpanNames; +import io.opentelemetry.instrumentation.api.instrumenter.util.SpanNames; import io.opentelemetry.javaagent.bootstrap.internal.ExperimentalConfig; public class VaadinSingletons { @@ -38,7 +38,7 @@ public class VaadinSingletons { CodeSpanNameExtractor.create(clientCallableAttributesGetter)) .setEnabled(ExperimentalConfig.get().controllerTelemetryEnabled()) .addAttributesExtractor(CodeAttributesExtractor.create(clientCallableAttributesGetter)) - .newInstrumenter(); + .buildInstrumenter(); REQUEST_HANDLER_INSTRUMENTER = Instrumenter.builder( @@ -48,13 +48,13 @@ public class VaadinSingletons { .addContextCustomizer( (context, vaadinHandlerRequest, startAttributes) -> context.with(REQUEST_HANDLER_CONTEXT_KEY, Boolean.TRUE)) - .newInstrumenter(); + .buildInstrumenter(); RPC_INSTRUMENTER = Instrumenter.builder( GlobalOpenTelemetry.get(), INSTRUMENTATION_NAME, VaadinSingletons::rpcSpanName) .setEnabled(ExperimentalConfig.get().controllerTelemetryEnabled()) - .newInstrumenter(); + .buildInstrumenter(); SERVICE_INSTRUMENTER = Instrumenter.builder( @@ -64,7 +64,7 @@ public class VaadinSingletons { .addContextCustomizer( (context, vaadinServiceRequest, startAttributes) -> context.with(SERVICE_CONTEXT_KEY, new VaadinServiceContext())) - .newInstrumenter(); + .buildInstrumenter(); HELPER = new VaadinHelper(REQUEST_HANDLER_INSTRUMENTER, SERVICE_INSTRUMENTER); } diff --git a/instrumentation/vaadin-14.2/testing/src/main/groovy/test/vaadin/AbstractVaadin16Test.groovy b/instrumentation/vaadin-14.2/testing/src/main/groovy/test/vaadin/AbstractVaadin16Test.groovy index 870e3398f421..c2256108f38c 100644 --- a/instrumentation/vaadin-14.2/testing/src/main/groovy/test/vaadin/AbstractVaadin16Test.groovy +++ b/instrumentation/vaadin-14.2/testing/src/main/groovy/test/vaadin/AbstractVaadin16Test.groovy @@ -8,6 +8,7 @@ package test.vaadin import com.vaadin.flow.server.Version import io.opentelemetry.api.trace.SpanKind +import io.opentelemetry.sdk.trace.data.SpanData abstract class AbstractVaadin16Test extends AbstractVaadinTest { static final boolean VAADIN_17 = Version.majorVersion >= 4 @@ -15,11 +16,14 @@ abstract class AbstractVaadin16Test extends AbstractVaadinTest { static final boolean VAADIN_21 = Version.majorVersion >= 8 static final boolean VAADIN_22 = Version.majorVersion >= 9 static final boolean VAADIN_23 = Version.majorVersion >= 23 + static final boolean VAADIN_23_2 = Version.majorVersion > 23 || (Version.majorVersion == 23 && Version.minorVersion >= 2) @Override List getRequestHandlers() { List handlers = [] - if (VAADIN_22) { + if (VAADIN_23_2) { + handlers.add("ViteHandler") + } else if (VAADIN_22) { handlers.add("WebpackHandler") } else if (VAADIN_21) { handlers.add("DevModeHandlerImpl") @@ -41,9 +45,17 @@ abstract class AbstractVaadin16Test extends AbstractVaadinTest { @Override void assertFirstRequest() { - assertTraces(VAADIN_17 ? 9 : 8) { + def tracesCount + if (VAADIN_23_2) { + tracesCount = 12 + } else if (VAADIN_17) { + tracesCount = 9 + } else { + tracesCount = 8 + } + assertTraces(tracesCount) { traces.sort(orderByRootSpanName("IndexHtmlRequestHandler.handleRequest", - getContextPath() + "/main", getContextPath(), getContextPath() + "/*", + getContextPath() + "/main", getContextPath(), getContextPath() + "/", getContextPath() + "/*", getContextPath() + "/VAADIN/*")) def handlers = getRequestHandlers("IndexHtmlRequestHandler") @@ -55,6 +67,7 @@ abstract class AbstractVaadin16Test extends AbstractVaadinTest { childOf span(0) } int spanIndex = 2 + sortHandlerSpans(spans, spanIndex, handlers) handlers.each { handler -> span(spanIndex++) { name handler + ".handleRequest" @@ -73,6 +86,7 @@ abstract class AbstractVaadin16Test extends AbstractVaadinTest { } int spanIndex = 2 + sortHandlerSpans(spans, spanIndex, handlers) handlers.each { handler -> span(spanIndex++) { name handler + ".handleRequest" @@ -101,6 +115,7 @@ abstract class AbstractVaadin16Test extends AbstractVaadinTest { childOf span(0) } int spanIndex = 2 + sortHandlerSpans(spans, spanIndex, handlers) handlers.each { handler -> span(spanIndex++) { name handler + ".handleRequest" @@ -110,10 +125,15 @@ abstract class AbstractVaadin16Test extends AbstractVaadinTest { } } // following traces are for javascript files used on page - def count = VAADIN_17 ? 5 : 4 + def count = traces.size() - 4 for (i in 0..count) { trace(3 + i, 1) { - def spanName = VAADIN_23 && i != 0 ? getContextPath() + "/VAADIN/*" : getContextPath() + "/*" + def spanName + if (VAADIN_23_2) { + spanName = i != 0 ? getContextPath() + "/*" : getContextPath() + "/" + } else { + spanName = VAADIN_23 && i != 0 ? getContextPath() + "/VAADIN/*" : getContextPath() + "/*" + } serverSpan(it, 0, spanName) } } @@ -133,6 +153,7 @@ abstract class AbstractVaadin16Test extends AbstractVaadinTest { } int spanIndex = 2 + sortHandlerSpans(spans, spanIndex, handlers) handlers.each { handler -> span(spanIndex++) { name handler + ".handleRequest" @@ -149,4 +170,12 @@ abstract class AbstractVaadin16Test extends AbstractVaadinTest { } } } + + static void sortHandlerSpans(List spans, int startIndex, List handlers) { + spans.subList(startIndex, startIndex + handlers.size()).sort({ + // strip .handleRequest from span name to get the handler name + def handlerName = it.name.substring(0, it.name.indexOf('.')) + return handlers.indexOf(handlerName) + }) + } } 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 24d7826794e7..f36c3fdd4474 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 @@ -30,6 +30,7 @@ public void transform(TypeTransformer transformer) { isConstructor(), HttpClientImplInstrumentation.class.getName() + "$AttachStateAdvice"); } + @SuppressWarnings("unused") public static class AttachStateAdvice { @Advice.OnMethodExit(suppress = Throwable.class) public static void attachHttpClientOptions( 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 c300788504a7..1b720657ecc6 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 @@ -45,6 +45,7 @@ public void transform(TypeTransformer transformer) { HttpRequestImplInstrumentation.class.getName() + "$Vertx37Advice"); } + @SuppressWarnings("unused") public static class Vertx30Advice { @Advice.OnMethodExit(suppress = Throwable.class) public static void attachRequestInfo( @@ -58,10 +59,11 @@ public static void attachRequestInfo( .set( request, VertxRequestInfo.create( - httpClientOptions != null ? httpClientOptions.isSsl() : false, host, port)); + httpClientOptions != null && httpClientOptions.isSsl(), host, port)); } } + @SuppressWarnings("unused") public static class Vertx34Advice { @Advice.OnMethodExit(suppress = Throwable.class) public static void attachRequestInfo( @@ -74,6 +76,7 @@ public static void attachRequestInfo( } } + @SuppressWarnings("unused") public static class Vertx37Advice { @Advice.OnMethodExit(suppress = Throwable.class) public static void attachRequestInfo( 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 b92954a2e882..ecaf34449e42 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 @@ -32,6 +32,10 @@ public void transform(TypeTransformer transformer) { transformer.applyAdviceToMethod( named("getConnection").and(takesArgument(3, named("io.vertx.core.Handler"))), ConnectionManagerInstrumentation.class.getName() + "$GetConnectionArg3Advice"); + // since 4.3.4 + transformer.applyAdviceToMethod( + named("getConnection").and(takesArgument(4, named("io.vertx.core.Handler"))), + ConnectionManagerInstrumentation.class.getName() + "$GetConnectionArg4Advice"); } @SuppressWarnings("unused") @@ -51,4 +55,13 @@ public static void wrapHandler( handler = HandlerWrapper.wrap(handler); } } + + @SuppressWarnings("unused") + public static class GetConnectionArg4Advice { + @Advice.OnMethodEnter(suppress = Throwable.class) + public static void wrapHandler( + @Advice.Argument(value = 4, readOnly = false) Handler handler) { + handler = 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/Vertx4NetAttributesGetter.java b/instrumentation/vertx/vertx-http-client/vertx-http-client-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/v4_0/client/Vertx4NetAttributesGetter.java index c85f74962c39..bf5fccbb8e7b 100644 --- a/instrumentation/vertx/vertx-http-client/vertx-http-client-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/v4_0/client/Vertx4NetAttributesGetter.java +++ b/instrumentation/vertx/vertx-http-client/vertx-http-client-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/v4_0/client/Vertx4NetAttributesGetter.java @@ -5,14 +5,16 @@ package io.opentelemetry.javaagent.instrumentation.vertx.v4_0.client; -import io.opentelemetry.instrumentation.api.instrumenter.net.NetClientAttributesGetter; +import io.opentelemetry.instrumentation.api.instrumenter.net.InetSocketAddressNetClientAttributesGetter; import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; import io.vertx.core.http.HttpClientRequest; import io.vertx.core.http.HttpClientResponse; +import io.vertx.core.net.SocketAddress; +import java.net.InetSocketAddress; import javax.annotation.Nullable; final class Vertx4NetAttributesGetter - implements NetClientAttributesGetter { + extends InetSocketAddressNetClientAttributesGetter { @Override public String transport(HttpClientRequest request, @Nullable HttpClientResponse response) { @@ -21,18 +23,26 @@ public String transport(HttpClientRequest request, @Nullable HttpClientResponse @Nullable @Override - public String peerName(HttpClientRequest request, @Nullable HttpClientResponse response) { + public String peerName(HttpClientRequest request) { return request.getHost(); } @Override - public Integer peerPort(HttpClientRequest request, @Nullable HttpClientResponse response) { + public Integer peerPort(HttpClientRequest request) { return request.getPort(); } @Nullable @Override - public String peerIp(HttpClientRequest request, @Nullable HttpClientResponse response) { + protected InetSocketAddress getPeerSocketAddress( + HttpClientRequest request, @Nullable HttpClientResponse response) { + if (response == null) { + return null; + } + SocketAddress address = response.netSocket().remoteAddress(); + if (address instanceof InetSocketAddress) { + return (InetSocketAddress) address; + } return null; } } diff --git a/instrumentation/vertx/vertx-http-client/vertx-http-client-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/client/AbstractVertxHttpAttributesGetter.java b/instrumentation/vertx/vertx-http-client/vertx-http-client-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/client/AbstractVertxHttpAttributesGetter.java index 69f1a22ee40c..dd9a9ec221c9 100644 --- a/instrumentation/vertx/vertx-http-client/vertx-http-client-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/client/AbstractVertxHttpAttributesGetter.java +++ b/instrumentation/vertx/vertx-http-client/vertx-http-client-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/client/AbstractVertxHttpAttributesGetter.java @@ -25,38 +25,12 @@ public List requestHeader(HttpClientRequest request, String name) { return request.headers().getAll(name); } - @Nullable - @Override - public Long requestContentLength( - HttpClientRequest request, @Nullable HttpClientResponse response) { - return null; - } - - @Nullable - @Override - public Long requestContentLengthUncompressed( - HttpClientRequest request, @Nullable HttpClientResponse response) { - return null; - } - @Override - public Integer statusCode(HttpClientRequest request, HttpClientResponse response) { + public Integer statusCode( + HttpClientRequest request, HttpClientResponse response, @Nullable Throwable error) { return response.statusCode(); } - @Nullable - @Override - public Long responseContentLength(HttpClientRequest request, HttpClientResponse response) { - return null; - } - - @Nullable - @Override - public Long responseContentLengthUncompressed( - HttpClientRequest request, HttpClientResponse response) { - return null; - } - @Override public List responseHeader( HttpClientRequest request, HttpClientResponse response, String name) { diff --git a/instrumentation/vertx/vertx-http-client/vertx-http-client-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/client/VertxClientInstrumenterFactory.java b/instrumentation/vertx/vertx-http-client/vertx-http-client-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/client/VertxClientInstrumenterFactory.java index 9f7313885c95..c3451fb126e8 100644 --- a/instrumentation/vertx/vertx-http-client/vertx-http-client-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/client/VertxClientInstrumenterFactory.java +++ b/instrumentation/vertx/vertx-http-client/vertx-http-client-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/client/VertxClientInstrumenterFactory.java @@ -8,13 +8,14 @@ import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; import io.opentelemetry.instrumentation.api.instrumenter.InstrumenterBuilder; -import io.opentelemetry.instrumentation.api.instrumenter.PeerServiceAttributesExtractor; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpClientAttributesExtractor; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpClientMetrics; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanNameExtractor; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanStatusExtractor; import io.opentelemetry.instrumentation.api.instrumenter.net.NetClientAttributesExtractor; import io.opentelemetry.instrumentation.api.instrumenter.net.NetClientAttributesGetter; +import io.opentelemetry.instrumentation.api.instrumenter.net.PeerServiceAttributesExtractor; +import io.opentelemetry.javaagent.bootstrap.internal.CommonConfig; import io.vertx.core.http.HttpClientRequest; import io.vertx.core.http.HttpClientResponse; import javax.annotation.Nullable; @@ -33,18 +34,22 @@ public static Instrumenter create( instrumentationName, HttpSpanNameExtractor.create(httpAttributesGetter)) .setSpanStatusExtractor(HttpSpanStatusExtractor.create(httpAttributesGetter)) - .addAttributesExtractor(HttpClientAttributesExtractor.create(httpAttributesGetter)) + .addAttributesExtractor( + HttpClientAttributesExtractor.builder(httpAttributesGetter) + .setCapturedRequestHeaders(CommonConfig.get().getClientRequestHeaders()) + .setCapturedResponseHeaders(CommonConfig.get().getClientResponseHeaders()) + .build()) .addOperationMetrics(HttpClientMetrics.get()); if (netAttributesGetter != null) { - NetClientAttributesExtractor netAttributesExtractor = - NetClientAttributesExtractor.create(netAttributesGetter); builder - .addAttributesExtractor(netAttributesExtractor) - .addAttributesExtractor(PeerServiceAttributesExtractor.create(netAttributesGetter)); + .addAttributesExtractor(NetClientAttributesExtractor.create(netAttributesGetter)) + .addAttributesExtractor( + PeerServiceAttributesExtractor.create( + netAttributesGetter, CommonConfig.get().getPeerServiceMapping())); } - return builder.newClientInstrumenter(new HttpRequestHeaderSetter()); + return builder.buildClientInstrumenter(new HttpRequestHeaderSetter()); } private VertxClientInstrumenterFactory() {} diff --git a/instrumentation/vertx/vertx-kafka-client-3.6/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/kafka/v3_6/VertxKafkaSingletons.java b/instrumentation/vertx/vertx-kafka-client-3.6/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/kafka/v3_6/VertxKafkaSingletons.java index 9cf1d4156d28..91d9f29f6c67 100644 --- a/instrumentation/vertx/vertx-kafka-client-3.6/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/kafka/v3_6/VertxKafkaSingletons.java +++ b/instrumentation/vertx/vertx-kafka-client-3.6/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/kafka/v3_6/VertxKafkaSingletons.java @@ -23,12 +23,10 @@ public final class VertxKafkaSingletons { static { KafkaInstrumenterFactory factory = new KafkaInstrumenterFactory(GlobalOpenTelemetry.get(), INSTRUMENTATION_NAME) + .setCapturedHeaders(ExperimentalConfig.get().getMessagingHeaders()) .setCaptureExperimentalSpanAttributes( InstrumentationConfig.get() .getBoolean("otel.instrumentation.kafka.experimental-span-attributes", false)) - .setPropagationEnabled( - InstrumentationConfig.get() - .getBoolean("otel.instrumentation.kafka.client-propagation.enabled", true)) .setMessagingReceiveInstrumentationEnabled( ExperimentalConfig.get().messagingReceiveInstrumentationEnabled()); BATCH_PROCESS_INSTRUMENTER = factory.createBatchProcessInstrumenter(); diff --git a/instrumentation/vertx/vertx-kafka-client-3.6/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/vertx/kafka/v3_6/BatchRecordsVertxKafkaTest.java b/instrumentation/vertx/vertx-kafka-client-3.6/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/vertx/kafka/v3_6/BatchRecordsVertxKafkaTest.java index 73bd1af93611..1d411fcbe020 100644 --- a/instrumentation/vertx/vertx-kafka-client-3.6/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/vertx/kafka/v3_6/BatchRecordsVertxKafkaTest.java +++ b/instrumentation/vertx/vertx-kafka-client-3.6/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/vertx/kafka/v3_6/BatchRecordsVertxKafkaTest.java @@ -11,6 +11,7 @@ import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies; import static org.junit.jupiter.api.Assertions.assertTrue; +import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.api.trace.SpanKind; import io.opentelemetry.sdk.trace.data.LinkData; import io.opentelemetry.sdk.trace.data.SpanData; @@ -69,7 +70,8 @@ void shouldCreateSpansForBatchReceiveAndProcess() throws InterruptedException { .hasAttributesSatisfyingExactly( equalTo(SemanticAttributes.MESSAGING_SYSTEM, "kafka"), equalTo(SemanticAttributes.MESSAGING_DESTINATION, "testBatchTopic"), - equalTo(SemanticAttributes.MESSAGING_DESTINATION_KIND, "topic")), + equalTo(SemanticAttributes.MESSAGING_DESTINATION_KIND, "topic"), + equalTo(AttributeKey.stringKey("messaging.payload"), "testSpan1")), span -> span.hasName("testBatchTopic send") .hasKind(SpanKind.PRODUCER) @@ -77,7 +79,8 @@ void shouldCreateSpansForBatchReceiveAndProcess() throws InterruptedException { .hasAttributesSatisfyingExactly( equalTo(SemanticAttributes.MESSAGING_SYSTEM, "kafka"), equalTo(SemanticAttributes.MESSAGING_DESTINATION, "testBatchTopic"), - equalTo(SemanticAttributes.MESSAGING_DESTINATION_KIND, "topic"))); + equalTo(SemanticAttributes.MESSAGING_DESTINATION_KIND, "topic"), + equalTo(AttributeKey.stringKey("messaging.payload"), "testSpan2"))); producer1.set(trace.getSpan(1)); producer2.set(trace.getSpan(2)); @@ -129,7 +132,8 @@ void shouldCreateSpansForBatchReceiveAndProcess() throws InterruptedException { satisfies(longKey("kafka.offset"), AbstractLongAssert::isNotNegative), satisfies( longKey("kafka.record.queue_time_ms"), - AbstractLongAssert::isNotNegative)), + AbstractLongAssert::isNotNegative), + equalTo(AttributeKey.stringKey("messaging.payload"), "testSpan1")), span -> span.hasName("process testSpan1").hasParent(trace.getSpan(3)), // single consumer 2 @@ -152,7 +156,8 @@ void shouldCreateSpansForBatchReceiveAndProcess() throws InterruptedException { satisfies(longKey("kafka.offset"), AbstractLongAssert::isNotNegative), satisfies( longKey("kafka.record.queue_time_ms"), - AbstractLongAssert::isNotNegative)), + AbstractLongAssert::isNotNegative), + equalTo(AttributeKey.stringKey("messaging.payload"), "testSpan2")), span -> span.hasName("process testSpan2").hasParent(trace.getSpan(5)))); } @@ -180,7 +185,8 @@ void shouldHandleFailureInKafkaBatchListener() throws InterruptedException { .hasAttributesSatisfyingExactly( equalTo(SemanticAttributes.MESSAGING_SYSTEM, "kafka"), equalTo(SemanticAttributes.MESSAGING_DESTINATION, "testBatchTopic"), - equalTo(SemanticAttributes.MESSAGING_DESTINATION_KIND, "topic"))); + equalTo(SemanticAttributes.MESSAGING_DESTINATION_KIND, "topic"), + equalTo(AttributeKey.stringKey("messaging.payload"), "error"))); producer.set(trace.getSpan(1)); }, @@ -230,7 +236,8 @@ void shouldHandleFailureInKafkaBatchListener() throws InterruptedException { satisfies(longKey("kafka.offset"), AbstractLongAssert::isNotNegative), satisfies( longKey("kafka.record.queue_time_ms"), - AbstractLongAssert::isNotNegative)), + AbstractLongAssert::isNotNegative), + equalTo(AttributeKey.stringKey("messaging.payload"), "error")), span -> span.hasName("process error").hasParent(trace.getSpan(3)))); } } diff --git a/instrumentation/vertx/vertx-kafka-client-3.6/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/vertx/kafka/v3_6/SingleRecordVertxKafkaTest.java b/instrumentation/vertx/vertx-kafka-client-3.6/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/vertx/kafka/v3_6/SingleRecordVertxKafkaTest.java index 306d26993b30..efcc091da15b 100644 --- a/instrumentation/vertx/vertx-kafka-client-3.6/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/vertx/kafka/v3_6/SingleRecordVertxKafkaTest.java +++ b/instrumentation/vertx/vertx-kafka-client-3.6/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/vertx/kafka/v3_6/SingleRecordVertxKafkaTest.java @@ -11,6 +11,7 @@ import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies; import static org.junit.jupiter.api.Assertions.assertTrue; +import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.api.trace.SpanKind; import io.opentelemetry.sdk.trace.data.LinkData; import io.opentelemetry.sdk.trace.data.SpanData; @@ -69,7 +70,8 @@ void shouldCreateSpansForSingleRecordProcess() throws InterruptedException { .hasAttributesSatisfyingExactly( equalTo(SemanticAttributes.MESSAGING_SYSTEM, "kafka"), equalTo(SemanticAttributes.MESSAGING_DESTINATION, "testSingleTopic"), - equalTo(SemanticAttributes.MESSAGING_DESTINATION_KIND, "topic"))); + equalTo(SemanticAttributes.MESSAGING_DESTINATION_KIND, "topic"), + equalTo(AttributeKey.stringKey("messaging.payload"), "testSpan"))); producer.set(trace.getSpan(1)); }, @@ -103,7 +105,8 @@ void shouldCreateSpansForSingleRecordProcess() throws InterruptedException { satisfies(longKey("kafka.offset"), AbstractLongAssert::isNotNegative), satisfies( longKey("kafka.record.queue_time_ms"), - AbstractLongAssert::isNotNegative)), + AbstractLongAssert::isNotNegative), + equalTo(AttributeKey.stringKey("messaging.payload"), "testSpan")), span -> span.hasName("consumer").hasParent(trace.getSpan(1)))); } @@ -134,7 +137,8 @@ void shouldHandleFailureInSingleRecordHandler() throws InterruptedException { .hasAttributesSatisfyingExactly( equalTo(SemanticAttributes.MESSAGING_SYSTEM, "kafka"), equalTo(SemanticAttributes.MESSAGING_DESTINATION, "testSingleTopic"), - equalTo(SemanticAttributes.MESSAGING_DESTINATION_KIND, "topic"))); + equalTo(SemanticAttributes.MESSAGING_DESTINATION_KIND, "topic"), + equalTo(AttributeKey.stringKey("messaging.payload"), "error"))); producer.set(trace.getSpan(1)); }, @@ -170,7 +174,8 @@ void shouldHandleFailureInSingleRecordHandler() throws InterruptedException { satisfies(longKey("kafka.offset"), AbstractLongAssert::isNotNegative), satisfies( longKey("kafka.record.queue_time_ms"), - AbstractLongAssert::isNotNegative)), + AbstractLongAssert::isNotNegative), + equalTo(AttributeKey.stringKey("messaging.payload"), "error")), span -> span.hasName("consumer").hasParent(trace.getSpan(1)))); } } diff --git a/instrumentation/vertx/vertx-kafka-client-3.6/javaagent/src/testNoReceiveTelemetry/java/io/opentelemetry/javaagent/instrumentation/vertx/kafka/v3_6/NoReceiveTelemetryBatchRecordsVertxKafkaTest.java b/instrumentation/vertx/vertx-kafka-client-3.6/javaagent/src/testNoReceiveTelemetry/java/io/opentelemetry/javaagent/instrumentation/vertx/kafka/v3_6/NoReceiveTelemetryBatchRecordsVertxKafkaTest.java index 93d4937acfa5..69a527a28814 100644 --- a/instrumentation/vertx/vertx-kafka-client-3.6/javaagent/src/testNoReceiveTelemetry/java/io/opentelemetry/javaagent/instrumentation/vertx/kafka/v3_6/NoReceiveTelemetryBatchRecordsVertxKafkaTest.java +++ b/instrumentation/vertx/vertx-kafka-client-3.6/javaagent/src/testNoReceiveTelemetry/java/io/opentelemetry/javaagent/instrumentation/vertx/kafka/v3_6/NoReceiveTelemetryBatchRecordsVertxKafkaTest.java @@ -10,6 +10,7 @@ import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies; import static org.junit.jupiter.api.Assertions.assertTrue; +import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.api.trace.SpanKind; import io.opentelemetry.sdk.trace.data.LinkData; import io.opentelemetry.sdk.trace.data.SpanData; @@ -70,7 +71,8 @@ void shouldCreateSpansForBatchReceiveAndProcess() throws InterruptedException { .hasAttributesSatisfyingExactly( equalTo(SemanticAttributes.MESSAGING_SYSTEM, "kafka"), equalTo(SemanticAttributes.MESSAGING_DESTINATION, "testBatchTopic"), - equalTo(SemanticAttributes.MESSAGING_DESTINATION_KIND, "topic")), + equalTo(SemanticAttributes.MESSAGING_DESTINATION_KIND, "topic"), + equalTo(AttributeKey.stringKey("messaging.payload"), "testSpan1")), span -> span.hasName("testBatchTopic process") .hasKind(SpanKind.CONSUMER) @@ -85,7 +87,8 @@ void shouldCreateSpansForBatchReceiveAndProcess() throws InterruptedException { AbstractLongAssert::isNotNegative), satisfies( SemanticAttributes.MESSAGING_KAFKA_PARTITION, - AbstractLongAssert::isNotNegative)), + AbstractLongAssert::isNotNegative), + equalTo(AttributeKey.stringKey("messaging.payload"), "testSpan1")), span -> span.hasName("process testSpan1").hasParent(trace.getSpan(2)), // second record @@ -96,7 +99,8 @@ void shouldCreateSpansForBatchReceiveAndProcess() throws InterruptedException { .hasAttributesSatisfyingExactly( equalTo(SemanticAttributes.MESSAGING_SYSTEM, "kafka"), equalTo(SemanticAttributes.MESSAGING_DESTINATION, "testBatchTopic"), - equalTo(SemanticAttributes.MESSAGING_DESTINATION_KIND, "topic")), + equalTo(SemanticAttributes.MESSAGING_DESTINATION_KIND, "topic"), + equalTo(AttributeKey.stringKey("messaging.payload"), "testSpan2")), span -> span.hasName("testBatchTopic process") .hasKind(SpanKind.CONSUMER) @@ -111,7 +115,8 @@ void shouldCreateSpansForBatchReceiveAndProcess() throws InterruptedException { AbstractLongAssert::isNotNegative), satisfies( SemanticAttributes.MESSAGING_KAFKA_PARTITION, - AbstractLongAssert::isNotNegative)), + AbstractLongAssert::isNotNegative), + equalTo(AttributeKey.stringKey("messaging.payload"), "testSpan2")), span -> span.hasName("process testSpan2").hasParent(trace.getSpan(5))); producer1.set(trace.getSpan(1)); @@ -159,7 +164,8 @@ void shouldHandleFailureInKafkaBatchListener() throws InterruptedException { .hasAttributesSatisfyingExactly( equalTo(SemanticAttributes.MESSAGING_SYSTEM, "kafka"), equalTo(SemanticAttributes.MESSAGING_DESTINATION, "testBatchTopic"), - equalTo(SemanticAttributes.MESSAGING_DESTINATION_KIND, "topic")), + equalTo(SemanticAttributes.MESSAGING_DESTINATION_KIND, "topic"), + equalTo(AttributeKey.stringKey("messaging.payload"), "error")), span -> span.hasName("testBatchTopic process") .hasKind(SpanKind.CONSUMER) @@ -174,7 +180,8 @@ void shouldHandleFailureInKafkaBatchListener() throws InterruptedException { AbstractLongAssert::isNotNegative), satisfies( SemanticAttributes.MESSAGING_KAFKA_PARTITION, - AbstractLongAssert::isNotNegative)), + AbstractLongAssert::isNotNegative), + equalTo(AttributeKey.stringKey("messaging.payload"), "error")), span -> span.hasName("process error").hasParent(trace.getSpan(2))); producer.set(trace.getSpan(1)); diff --git a/instrumentation/vertx/vertx-kafka-client-3.6/javaagent/src/testNoReceiveTelemetry/java/io/opentelemetry/javaagent/instrumentation/vertx/kafka/v3_6/NoReceiveTelemetrySingleRecordVertxKafkaTest.java b/instrumentation/vertx/vertx-kafka-client-3.6/javaagent/src/testNoReceiveTelemetry/java/io/opentelemetry/javaagent/instrumentation/vertx/kafka/v3_6/NoReceiveTelemetrySingleRecordVertxKafkaTest.java index 3a1f70094b32..a91b2117b82d 100644 --- a/instrumentation/vertx/vertx-kafka-client-3.6/javaagent/src/testNoReceiveTelemetry/java/io/opentelemetry/javaagent/instrumentation/vertx/kafka/v3_6/NoReceiveTelemetrySingleRecordVertxKafkaTest.java +++ b/instrumentation/vertx/vertx-kafka-client-3.6/javaagent/src/testNoReceiveTelemetry/java/io/opentelemetry/javaagent/instrumentation/vertx/kafka/v3_6/NoReceiveTelemetrySingleRecordVertxKafkaTest.java @@ -9,6 +9,7 @@ import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies; import static org.junit.jupiter.api.Assertions.assertTrue; +import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.api.trace.SpanKind; import io.opentelemetry.sdk.trace.data.StatusData; import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; @@ -61,7 +62,8 @@ void shouldCreateSpansForSingleRecordProcess() throws InterruptedException { .hasAttributesSatisfyingExactly( equalTo(SemanticAttributes.MESSAGING_SYSTEM, "kafka"), equalTo(SemanticAttributes.MESSAGING_DESTINATION, "testSingleTopic"), - equalTo(SemanticAttributes.MESSAGING_DESTINATION_KIND, "topic")), + equalTo(SemanticAttributes.MESSAGING_DESTINATION_KIND, "topic"), + equalTo(AttributeKey.stringKey("messaging.payload"), "testSpan")), span -> span.hasName("testSingleTopic process") .hasKind(SpanKind.CONSUMER) @@ -76,7 +78,8 @@ void shouldCreateSpansForSingleRecordProcess() throws InterruptedException { AbstractLongAssert::isNotNegative), satisfies( SemanticAttributes.MESSAGING_KAFKA_PARTITION, - AbstractLongAssert::isNotNegative)), + AbstractLongAssert::isNotNegative), + equalTo(AttributeKey.stringKey("messaging.payload"), "testSpan")), span -> span.hasName("consumer").hasParent(trace.getSpan(2)))); } @@ -104,7 +107,8 @@ void shouldHandleFailureInSingleRecordHandler() throws InterruptedException { .hasAttributesSatisfyingExactly( equalTo(SemanticAttributes.MESSAGING_SYSTEM, "kafka"), equalTo(SemanticAttributes.MESSAGING_DESTINATION, "testSingleTopic"), - equalTo(SemanticAttributes.MESSAGING_DESTINATION_KIND, "topic")), + equalTo(SemanticAttributes.MESSAGING_DESTINATION_KIND, "topic"), + equalTo(AttributeKey.stringKey("messaging.payload"), "error")), span -> span.hasName("testSingleTopic process") .hasKind(SpanKind.CONSUMER) @@ -121,7 +125,8 @@ void shouldHandleFailureInSingleRecordHandler() throws InterruptedException { AbstractLongAssert::isNotNegative), satisfies( SemanticAttributes.MESSAGING_KAFKA_PARTITION, - AbstractLongAssert::isNotNegative)), + AbstractLongAssert::isNotNegative), + equalTo(AttributeKey.stringKey("messaging.payload"), "error")), span -> span.hasName("consumer").hasParent(trace.getSpan(2)))); } } diff --git a/instrumentation/vertx/vertx-rx-java-3.5/javaagent/build.gradle.kts b/instrumentation/vertx/vertx-rx-java-3.5/javaagent/build.gradle.kts index b070b71b30d4..4d1e6d49f6c6 100644 --- a/instrumentation/vertx/vertx-rx-java-3.5/javaagent/build.gradle.kts +++ b/instrumentation/vertx/vertx-rx-java-3.5/javaagent/build.gradle.kts @@ -22,6 +22,10 @@ tasks { } } +tasks.withType().configureEach { + jvmArgs("-Dotel.instrumentation.common.db-statement-sanitizer.enabled=true") +} + // The first Vert.x version that uses rx-java 2 val vertxVersion = "3.5.0" diff --git a/instrumentation/vertx/vertx-rx-java-3.5/javaagent/src/latestDepTest/groovy/VertxReactivePropagationTest.groovy b/instrumentation/vertx/vertx-rx-java-3.5/javaagent/src/latestDepTest/groovy/VertxReactivePropagationTest.groovy index bf748c67f68a..9ed5613d20c8 100644 --- a/instrumentation/vertx/vertx-rx-java-3.5/javaagent/src/latestDepTest/groovy/VertxReactivePropagationTest.groovy +++ b/instrumentation/vertx/vertx-rx-java-3.5/javaagent/src/latestDepTest/groovy/VertxReactivePropagationTest.groovy @@ -64,10 +64,11 @@ class VertxReactivePropagationTest extends AgentInstrumentationSpecification { hasNoParent() attributes { "$SemanticAttributes.NET_TRANSPORT" IP_TCP - "$SemanticAttributes.NET_PEER_NAME" { it == null || it == "localhost" } - "$SemanticAttributes.NET_PEER_PORT" Long - "$SemanticAttributes.NET_PEER_IP" "127.0.0.1" - "$SemanticAttributes.HTTP_HOST" { it == "localhost" || it == "localhost:${port}" } + "$SemanticAttributes.NET_SOCK_PEER_ADDR" "127.0.0.1" + "$SemanticAttributes.NET_SOCK_PEER_PORT" Long + "$SemanticAttributes.NET_SOCK_HOST_ADDR" "127.0.0.1" + "$SemanticAttributes.NET_HOST_NAME" "localhost" + "$SemanticAttributes.NET_HOST_PORT" Long "$SemanticAttributes.HTTP_TARGET" "/listProducts" "$SemanticAttributes.HTTP_METHOD" "GET" "$SemanticAttributes.HTTP_STATUS_CODE" 200 @@ -75,6 +76,7 @@ class VertxReactivePropagationTest extends AgentInstrumentationSpecification { "$SemanticAttributes.HTTP_FLAVOR" "1.1" "$SemanticAttributes.HTTP_USER_AGENT" String "$SemanticAttributes.HTTP_ROUTE" "/listProducts" + "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" Long } } span(1) { @@ -155,10 +157,11 @@ class VertxReactivePropagationTest extends AgentInstrumentationSpecification { childOf(span(0)) attributes { "$SemanticAttributes.NET_TRANSPORT" IP_TCP - "$SemanticAttributes.NET_PEER_NAME" { it == null || it == "localhost" } - "$SemanticAttributes.NET_PEER_PORT" Long - "$SemanticAttributes.NET_PEER_IP" "127.0.0.1" - "$SemanticAttributes.HTTP_HOST" { it == "localhost" || it == "localhost:${port}" } + "$SemanticAttributes.NET_SOCK_PEER_ADDR" "127.0.0.1" + "$SemanticAttributes.NET_SOCK_PEER_PORT" Long + "$SemanticAttributes.NET_SOCK_HOST_ADDR" "127.0.0.1" + "$SemanticAttributes.NET_HOST_NAME" "localhost" + "$SemanticAttributes.NET_HOST_PORT" Long "$SemanticAttributes.HTTP_TARGET" "$baseUrl?$TEST_REQUEST_ID_PARAMETER=$requestId" "$SemanticAttributes.HTTP_METHOD" "GET" "$SemanticAttributes.HTTP_STATUS_CODE" 200 @@ -166,6 +169,7 @@ class VertxReactivePropagationTest extends AgentInstrumentationSpecification { "$SemanticAttributes.HTTP_FLAVOR" "1.1" "$SemanticAttributes.HTTP_USER_AGENT" String "$SemanticAttributes.HTTP_ROUTE" "/listProducts" + "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" Long "${TEST_REQUEST_ID_ATTRIBUTE}" requestId } } diff --git a/instrumentation/vertx/vertx-rx-java-3.5/javaagent/src/version35Test/groovy/VertxReactivePropagationTest.groovy b/instrumentation/vertx/vertx-rx-java-3.5/javaagent/src/version35Test/groovy/VertxReactivePropagationTest.groovy index bf748c67f68a..9ed5613d20c8 100644 --- a/instrumentation/vertx/vertx-rx-java-3.5/javaagent/src/version35Test/groovy/VertxReactivePropagationTest.groovy +++ b/instrumentation/vertx/vertx-rx-java-3.5/javaagent/src/version35Test/groovy/VertxReactivePropagationTest.groovy @@ -64,10 +64,11 @@ class VertxReactivePropagationTest extends AgentInstrumentationSpecification { hasNoParent() attributes { "$SemanticAttributes.NET_TRANSPORT" IP_TCP - "$SemanticAttributes.NET_PEER_NAME" { it == null || it == "localhost" } - "$SemanticAttributes.NET_PEER_PORT" Long - "$SemanticAttributes.NET_PEER_IP" "127.0.0.1" - "$SemanticAttributes.HTTP_HOST" { it == "localhost" || it == "localhost:${port}" } + "$SemanticAttributes.NET_SOCK_PEER_ADDR" "127.0.0.1" + "$SemanticAttributes.NET_SOCK_PEER_PORT" Long + "$SemanticAttributes.NET_SOCK_HOST_ADDR" "127.0.0.1" + "$SemanticAttributes.NET_HOST_NAME" "localhost" + "$SemanticAttributes.NET_HOST_PORT" Long "$SemanticAttributes.HTTP_TARGET" "/listProducts" "$SemanticAttributes.HTTP_METHOD" "GET" "$SemanticAttributes.HTTP_STATUS_CODE" 200 @@ -75,6 +76,7 @@ class VertxReactivePropagationTest extends AgentInstrumentationSpecification { "$SemanticAttributes.HTTP_FLAVOR" "1.1" "$SemanticAttributes.HTTP_USER_AGENT" String "$SemanticAttributes.HTTP_ROUTE" "/listProducts" + "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" Long } } span(1) { @@ -155,10 +157,11 @@ class VertxReactivePropagationTest extends AgentInstrumentationSpecification { childOf(span(0)) attributes { "$SemanticAttributes.NET_TRANSPORT" IP_TCP - "$SemanticAttributes.NET_PEER_NAME" { it == null || it == "localhost" } - "$SemanticAttributes.NET_PEER_PORT" Long - "$SemanticAttributes.NET_PEER_IP" "127.0.0.1" - "$SemanticAttributes.HTTP_HOST" { it == "localhost" || it == "localhost:${port}" } + "$SemanticAttributes.NET_SOCK_PEER_ADDR" "127.0.0.1" + "$SemanticAttributes.NET_SOCK_PEER_PORT" Long + "$SemanticAttributes.NET_SOCK_HOST_ADDR" "127.0.0.1" + "$SemanticAttributes.NET_HOST_NAME" "localhost" + "$SemanticAttributes.NET_HOST_PORT" Long "$SemanticAttributes.HTTP_TARGET" "$baseUrl?$TEST_REQUEST_ID_PARAMETER=$requestId" "$SemanticAttributes.HTTP_METHOD" "GET" "$SemanticAttributes.HTTP_STATUS_CODE" 200 @@ -166,6 +169,7 @@ class VertxReactivePropagationTest extends AgentInstrumentationSpecification { "$SemanticAttributes.HTTP_FLAVOR" "1.1" "$SemanticAttributes.HTTP_USER_AGENT" String "$SemanticAttributes.HTTP_ROUTE" "/listProducts" + "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" Long "${TEST_REQUEST_ID_ATTRIBUTE}" requestId } } diff --git a/instrumentation/vertx/vertx-web-3.0/javaagent/build.gradle.kts b/instrumentation/vertx/vertx-web-3.0/javaagent/build.gradle.kts index 62f2f386f516..5a2f16dc6972 100644 --- a/instrumentation/vertx/vertx-web-3.0/javaagent/build.gradle.kts +++ b/instrumentation/vertx/vertx-web-3.0/javaagent/build.gradle.kts @@ -23,6 +23,10 @@ tasks { } } +tasks.withType().configureEach { + jvmArgs("-Dotel.instrumentation.common.db-statement-sanitizer.enabled=true") +} + dependencies { compileOnly("io.vertx:vertx-web:3.0.0") diff --git a/instrumentation/vibur-dbcp-11.0/library/README.md b/instrumentation/vibur-dbcp-11.0/library/README.md index 08e9c595a1a4..2ea37c8339ec 100644 --- a/instrumentation/vibur-dbcp-11.0/library/README.md +++ b/instrumentation/vibur-dbcp-11.0/library/README.md @@ -1,4 +1,4 @@ -# Manual Instrumentation for Vibur DBCP +# Library Instrumentation for Vibur DBCP version 11.0 and higher Provides OpenTelemetry instrumentation for [Vibur DBCP](https://www.vibur.org/). @@ -6,13 +6,12 @@ Provides OpenTelemetry instrumentation for [Vibur DBCP](https://www.vibur.org/). ### Add these dependencies to your project: -Replace `OPENTELEMETRY_VERSION` with the latest stable -[release](https://mvnrepository.com/artifact/io.opentelemetry). `Minimum version: 1.15.0` +Replace `OPENTELEMETRY_VERSION` with the [latest +release](https://search.maven.org/search?q=g:io.opentelemetry.instrumentation%20AND%20a:opentelemetry-vibur-dbcp-11.0). For Maven, add to your `pom.xml` dependencies: ```xml - io.opentelemetry.instrumentation diff --git a/javaagent-bootstrap/build.gradle.kts b/javaagent-bootstrap/build.gradle.kts index 915b6db36ae4..8cff55f24e24 100644 --- a/javaagent-bootstrap/build.gradle.kts +++ b/javaagent-bootstrap/build.gradle.kts @@ -7,9 +7,12 @@ group = "io.opentelemetry.javaagent" dependencies { implementation(project(":instrumentation-api")) - implementation(project(":instrumentation-appender-api-internal")) - implementation("org.slf4j:slf4j-api") - implementation("org.slf4j:slf4j-simple") testImplementation(project(":testing-common")) } + +tasks.withType().configureEach { + // required on jdk17 + jvmArgs("--add-opens=java.base/java.lang=ALL-UNNAMED") + jvmArgs("-XX:+IgnoreUnrecognizedVMOptions") +} 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 8fa4976f7fb6..41b250484989 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 @@ -33,9 +33,8 @@ */ public class AgentClassLoader extends URLClassLoader { - // NOTE it's important not to use slf4j in this class, because this class is used before slf4j is - // configured, and so using slf4j here would initialize slf4j-simple before we have a chance to - // configure the logging levels + // NOTE it's important not to use logging in this class, because this class is used before logging + // is initialized static { ClassLoader.registerAsParallelCapable(); diff --git a/javaagent-bootstrap/src/main/java/io/opentelemetry/javaagent/bootstrap/AgentLogEmitterProvider.java b/javaagent-bootstrap/src/main/java/io/opentelemetry/javaagent/bootstrap/AgentLogEmitterProvider.java deleted file mode 100644 index 55820d769bcc..000000000000 --- a/javaagent-bootstrap/src/main/java/io/opentelemetry/javaagent/bootstrap/AgentLogEmitterProvider.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.javaagent.bootstrap; - -import io.opentelemetry.instrumentation.api.appender.internal.LogEmitterProvider; -import io.opentelemetry.instrumentation.api.appender.internal.LogEmitterProviderHolder; - -public final class AgentLogEmitterProvider { - - private static final LogEmitterProviderHolder delegate = new LogEmitterProviderHolder(); - - /** Returns the registered global {@link LogEmitterProvider}. */ - public static LogEmitterProvider get() { - return delegate.get(); - } - - /** - * Sets the {@link LogEmitterProvider} that should be used by the agent. Future calls to {@link - * #get()} will return the provided {@link LogEmitterProvider} instance. It should only be called - * once - an attempt to call it a second time will result in an error. If trying to set the - * instance {@link LogEmitterProvider} multiple times in tests, use {@link - * LogEmitterProviderHolder#resetForTest()} between them. - */ - public static void set(LogEmitterProvider logEmitterProvider) { - delegate.set(logEmitterProvider); - } - - /** - * Unsets the {@link LogEmitterProvider}. This is only meant to be used from tests which need to - * reconfigure {@link LogEmitterProvider}. - */ - public static void resetForTest() { - delegate.resetForTest(); - } - - private AgentLogEmitterProvider() {} -} diff --git a/javaagent-bootstrap/src/main/java/io/opentelemetry/javaagent/bootstrap/InstrumentationHolder.java b/javaagent-bootstrap/src/main/java/io/opentelemetry/javaagent/bootstrap/InstrumentationHolder.java index 039c55c25548..757c9d2f4996 100644 --- a/javaagent-bootstrap/src/main/java/io/opentelemetry/javaagent/bootstrap/InstrumentationHolder.java +++ b/javaagent-bootstrap/src/main/java/io/opentelemetry/javaagent/bootstrap/InstrumentationHolder.java @@ -9,7 +9,7 @@ import javax.annotation.Nullable; /** This class serves as an "everywhere accessible" source of {@link Instrumentation} instance. */ -public class InstrumentationHolder { +public final class InstrumentationHolder { @Nullable private static volatile Instrumentation instrumentation; @@ -21,4 +21,6 @@ public static Instrumentation getInstrumentation() { public static void setInstrumentation(Instrumentation instrumentation) { InstrumentationHolder.instrumentation = instrumentation; } + + private InstrumentationHolder() {} } diff --git a/javaagent-bootstrap/src/main/java/io/opentelemetry/javaagent/bootstrap/InstrumentedTaskClasses.java b/javaagent-bootstrap/src/main/java/io/opentelemetry/javaagent/bootstrap/InstrumentedTaskClasses.java index b1eea43b8d2d..2d4210d0cbed 100644 --- a/javaagent-bootstrap/src/main/java/io/opentelemetry/javaagent/bootstrap/InstrumentedTaskClasses.java +++ b/javaagent-bootstrap/src/main/java/io/opentelemetry/javaagent/bootstrap/InstrumentedTaskClasses.java @@ -5,13 +5,12 @@ package io.opentelemetry.javaagent.bootstrap; -import io.opentelemetry.instrumentation.api.config.Config; import java.util.function.Predicate; import java.util.logging.Logger; public final class InstrumentedTaskClasses { - private static final Logger logger = Logger.getLogger(Config.class.getName()); + private static final Logger logger = Logger.getLogger(InstrumentedTaskClasses.class.getName()); private static final String AGENT_CLASSLOADER_NAME = "io.opentelemetry.javaagent.bootstrap.AgentClassLoader"; diff --git a/javaagent-bootstrap/src/main/java/io/opentelemetry/javaagent/bootstrap/InternalLogger.java b/javaagent-bootstrap/src/main/java/io/opentelemetry/javaagent/bootstrap/InternalLogger.java new file mode 100644 index 000000000000..9bec7b9e2c60 --- /dev/null +++ b/javaagent-bootstrap/src/main/java/io/opentelemetry/javaagent/bootstrap/InternalLogger.java @@ -0,0 +1,50 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.bootstrap; + +import java.util.concurrent.atomic.AtomicReference; +import javax.annotation.Nullable; + +public abstract class InternalLogger { + + private static final AtomicReference loggerFactory = + new AtomicReference<>(NoopLoggerFactory.INSTANCE); + + public static void initialize(Factory factory) { + if (!loggerFactory.compareAndSet(NoopLoggerFactory.INSTANCE, factory)) { + factory + .create(InternalLogger.class.getName()) + .log( + Level.WARN, + "Developer error: logging system has already been initialized once", + null); + } + } + + static InternalLogger getLogger(String name) { + return loggerFactory.get().create(name); + } + + protected abstract boolean isLoggable(Level level); + + protected abstract void log(Level level, String message, @Nullable Throwable error); + + protected abstract String name(); + + public enum Level { + ERROR, + WARN, + INFO, + DEBUG, + TRACE + } + + @FunctionalInterface + public interface Factory { + + InternalLogger create(String name); + } +} diff --git a/javaagent-bootstrap/src/main/java/io/opentelemetry/javaagent/bootstrap/JavaagentFileHolder.java b/javaagent-bootstrap/src/main/java/io/opentelemetry/javaagent/bootstrap/JavaagentFileHolder.java index 0ead1a968ad1..1045f207d2f7 100644 --- a/javaagent-bootstrap/src/main/java/io/opentelemetry/javaagent/bootstrap/JavaagentFileHolder.java +++ b/javaagent-bootstrap/src/main/java/io/opentelemetry/javaagent/bootstrap/JavaagentFileHolder.java @@ -9,8 +9,9 @@ import javax.annotation.Nullable; // this is currently unused in this repository, but is available for use in distros + /** This class serves as an "everywhere accessible" source of the agent jar file. */ -public class JavaagentFileHolder { +public final class JavaagentFileHolder { @Nullable private static volatile File javaagentFile; @@ -22,4 +23,6 @@ public static File getJavaagentFile() { public static void setJavaagentFile(File javaagentFile) { JavaagentFileHolder.javaagentFile = javaagentFile; } + + private JavaagentFileHolder() {} } diff --git a/javaagent-bootstrap/src/main/java/io/opentelemetry/javaagent/bootstrap/NoopLoggerFactory.java b/javaagent-bootstrap/src/main/java/io/opentelemetry/javaagent/bootstrap/NoopLoggerFactory.java new file mode 100644 index 000000000000..c7e6368becbf --- /dev/null +++ b/javaagent-bootstrap/src/main/java/io/opentelemetry/javaagent/bootstrap/NoopLoggerFactory.java @@ -0,0 +1,42 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.bootstrap; + +import javax.annotation.Nullable; + +final class NoopLoggerFactory implements InternalLogger.Factory { + + static final InternalLogger.Factory INSTANCE = new NoopLoggerFactory(); + + private NoopLoggerFactory() {} + + @Override + public InternalLogger create(String name) { + return new NoopLogger(name); + } + + private static final class NoopLogger extends InternalLogger { + + private final String name; + + private NoopLogger(String name) { + this.name = name; + } + + @Override + public boolean isLoggable(Level level) { + return false; + } + + @Override + public void log(Level level, String message, @Nullable Throwable error) {} + + @Override + protected String name() { + return name; + } + } +} 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 05262afa40e6..5bb18d305c65 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 @@ -11,8 +11,6 @@ import java.util.logging.Handler; import java.util.logging.Level; import java.util.logging.LogRecord; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; /** * Dependencies of the agent sometimes call java.util.logging.Logger.getLogger(). This can have the @@ -20,7 +18,8 @@ * *

Shadow rewrites will redirect those calls to this class, which will return a safe PatchLogger. * - *

This also has the desired outcome of redirecting all logging to a single destination (SLF4J). + *

This also has the desired outcome of redirecting all logging to a single destination, as + * configured by the {@code LoggingCustomizer} implementation. */ public class PatchLogger { @@ -28,7 +27,7 @@ public class PatchLogger { public static final PatchLogger global = new PatchLogger(GLOBAL_LOGGER_NAME); - private final Logger slf4jLogger; + private final InternalLogger internalLogger; private ResourceBundle resourceBundle; @@ -41,55 +40,50 @@ public static PatchLogger getLogger(String name, String resourceBundleName) { } private PatchLogger(String name) { - this(LoggerFactory.getLogger(name)); + this(InternalLogger.getLogger(name)); } // visible for testing - PatchLogger(Logger slf4jLogger) { - this.slf4jLogger = slf4jLogger; - } - - // visible for testing - Logger getSlf4jLogger() { - return slf4jLogger; + PatchLogger(InternalLogger internalLogger) { + this.internalLogger = internalLogger; } public String getName() { - return slf4jLogger.getName(); + return internalLogger.name(); } public void severe(String msg) { - slf4jLogger.error(msg); + internalLogger.log(InternalLogger.Level.ERROR, msg, null); } public void severe(Supplier msgSupplier) { - if (slf4jLogger.isErrorEnabled()) { - slf4jLogger.error(msgSupplier.get()); + if (internalLogger.isLoggable(InternalLogger.Level.ERROR)) { + internalLogger.log(InternalLogger.Level.ERROR, msgSupplier.get(), null); } } public void warning(String msg) { - slf4jLogger.warn(msg); + internalLogger.log(InternalLogger.Level.WARN, msg, null); } public void warning(Supplier msgSupplier) { - if (slf4jLogger.isWarnEnabled()) { - slf4jLogger.warn(msgSupplier.get()); + if (internalLogger.isLoggable(InternalLogger.Level.WARN)) { + internalLogger.log(InternalLogger.Level.WARN, msgSupplier.get(), null); } } public void info(String msg) { - slf4jLogger.info(msg); + internalLogger.log(InternalLogger.Level.INFO, msg, null); } public void info(Supplier msgSupplier) { - if (slf4jLogger.isInfoEnabled()) { - slf4jLogger.info(msgSupplier.get()); + if (internalLogger.isLoggable(InternalLogger.Level.INFO)) { + internalLogger.log(InternalLogger.Level.INFO, msgSupplier.get(), null); } } public void config(String msg) { - slf4jLogger.info(msg); + info(msg); } public void config(Supplier msgSupplier) { @@ -97,27 +91,27 @@ public void config(Supplier msgSupplier) { } public void fine(String msg) { - slf4jLogger.debug(msg); + internalLogger.log(InternalLogger.Level.DEBUG, msg, null); } public void fine(Supplier msgSupplier) { - if (slf4jLogger.isDebugEnabled()) { - slf4jLogger.debug(msgSupplier.get()); + if (internalLogger.isLoggable(InternalLogger.Level.DEBUG)) { + internalLogger.log(InternalLogger.Level.DEBUG, msgSupplier.get(), null); } } public void finer(String msg) { - slf4jLogger.trace(msg); + internalLogger.log(InternalLogger.Level.TRACE, msg, null); } public void finer(Supplier msgSupplier) { - if (slf4jLogger.isTraceEnabled()) { - slf4jLogger.trace(msgSupplier.get()); + if (internalLogger.isLoggable(InternalLogger.Level.TRACE)) { + internalLogger.log(InternalLogger.Level.TRACE, msgSupplier.get(), null); } } public void finest(String msg) { - slf4jLogger.trace(msg); + finer(msg); } public void finest(Supplier msgSupplier) { @@ -125,170 +119,82 @@ public void finest(Supplier msgSupplier) { } public void log(LogRecord record) { - Level level = record.getLevel(); - if (level.intValue() >= Level.SEVERE.intValue()) { - if (slf4jLogger.isErrorEnabled()) { - slf4jLogger.error(getMessage(record), record.getThrown()); - } - } else if (level.intValue() >= Level.WARNING.intValue()) { - if (slf4jLogger.isWarnEnabled()) { - slf4jLogger.warn(getMessage(record), record.getThrown()); - } - } else if (level.intValue() >= Level.CONFIG.intValue()) { - if (slf4jLogger.isInfoEnabled()) { - slf4jLogger.info(getMessage(record), record.getThrown()); - } - } else if (level.intValue() >= Level.FINE.intValue()) { - if (slf4jLogger.isDebugEnabled()) { - slf4jLogger.debug(getMessage(record), record.getThrown()); - } - } else { - if (slf4jLogger.isTraceEnabled()) { - slf4jLogger.trace(getMessage(record), record.getThrown()); - } + InternalLogger.Level internalLevel = toInternalLevel(record.getLevel()); + if (internalLogger.isLoggable(internalLevel)) { + internalLogger.log(internalLevel, getMessage(record), record.getThrown()); } } public void log(Level level, String msg) { - if (level.intValue() >= Level.SEVERE.intValue()) { - slf4jLogger.error(msg); - } else if (level.intValue() >= Level.WARNING.intValue()) { - slf4jLogger.warn(msg); - } else if (level.intValue() >= Level.CONFIG.intValue()) { - slf4jLogger.info(msg); - } else if (level.intValue() >= Level.FINE.intValue()) { - slf4jLogger.debug(msg); - } else { - slf4jLogger.trace(msg); - } + internalLogger.log(toInternalLevel(level), msg, null); } public void log(Level level, String msg, Object param1) { - if (level.intValue() >= Level.SEVERE.intValue()) { - if (slf4jLogger.isErrorEnabled()) { - slf4jLogger.error(MessageFormat.format(msg, param1)); - } - } else if (level.intValue() >= Level.WARNING.intValue()) { - if (slf4jLogger.isWarnEnabled()) { - slf4jLogger.warn(MessageFormat.format(msg, param1)); - } - } else if (level.intValue() >= Level.CONFIG.intValue()) { - if (slf4jLogger.isInfoEnabled()) { - slf4jLogger.info(MessageFormat.format(msg, param1)); - } - } else if (level.intValue() >= Level.FINE.intValue()) { - if (slf4jLogger.isDebugEnabled()) { - slf4jLogger.debug(MessageFormat.format(msg, param1)); - } - } else { - if (slf4jLogger.isTraceEnabled()) { - slf4jLogger.trace(MessageFormat.format(msg, param1)); - } + InternalLogger.Level internalLevel = toInternalLevel(level); + if (internalLogger.isLoggable(internalLevel)) { + internalLogger.log(internalLevel, MessageFormat.format(msg, param1), null); } } public void log(Level level, String msg, Object[] params) { - if (level.intValue() >= Level.SEVERE.intValue()) { - if (slf4jLogger.isErrorEnabled()) { - slf4jLogger.error(MessageFormat.format(msg, params)); - } - } else if (level.intValue() >= Level.WARNING.intValue()) { - if (slf4jLogger.isWarnEnabled()) { - slf4jLogger.warn(MessageFormat.format(msg, params)); - } - } else if (level.intValue() >= Level.CONFIG.intValue()) { - if (slf4jLogger.isInfoEnabled()) { - slf4jLogger.info(MessageFormat.format(msg, params)); - } - } else if (level.intValue() >= Level.FINE.intValue()) { - if (slf4jLogger.isDebugEnabled()) { - slf4jLogger.debug(MessageFormat.format(msg, params)); - } - } else { - if (slf4jLogger.isTraceEnabled()) { - slf4jLogger.trace(MessageFormat.format(msg, params)); - } + InternalLogger.Level internalLevel = toInternalLevel(level); + if (internalLogger.isLoggable(internalLevel)) { + internalLogger.log(internalLevel, MessageFormat.format(msg, params), null); } } public void log(Level level, String msg, Throwable thrown) { - if (level.intValue() >= Level.SEVERE.intValue()) { - slf4jLogger.error(msg, thrown); - } else if (level.intValue() >= Level.WARNING.intValue()) { - slf4jLogger.warn(msg, thrown); - } else if (level.intValue() >= Level.CONFIG.intValue()) { - slf4jLogger.info(msg, thrown); - } else if (level.intValue() >= Level.FINE.intValue()) { - slf4jLogger.debug(msg, thrown); - } else { - slf4jLogger.trace(msg, thrown); - } + internalLogger.log(toInternalLevel(level), msg, thrown); } public void log(Level level, Supplier msgSupplier) { - if (!isLoggable(level)) { - return; - } - if (level.intValue() >= Level.SEVERE.intValue()) { - slf4jLogger.error(msgSupplier.get()); - } else if (level.intValue() >= Level.WARNING.intValue()) { - slf4jLogger.warn(msgSupplier.get()); - } else if (level.intValue() >= Level.CONFIG.intValue()) { - slf4jLogger.info(msgSupplier.get()); - } else if (level.intValue() >= Level.FINE.intValue()) { - slf4jLogger.debug(msgSupplier.get()); - } else { - slf4jLogger.trace(msgSupplier.get()); + InternalLogger.Level internalLevel = toInternalLevel(level); + if (internalLogger.isLoggable(internalLevel)) { + internalLogger.log(internalLevel, msgSupplier.get(), null); } } public void log(Level level, Throwable thrown, Supplier msgSupplier) { - if (!isLoggable(level)) { - return; - } - if (level.intValue() >= Level.SEVERE.intValue()) { - slf4jLogger.error(msgSupplier.get(), thrown); - } else if (level.intValue() >= Level.WARNING.intValue()) { - slf4jLogger.warn(msgSupplier.get(), thrown); - } else if (level.intValue() >= Level.CONFIG.intValue()) { - slf4jLogger.info(msgSupplier.get(), thrown); - } else if (level.intValue() >= Level.FINE.intValue()) { - slf4jLogger.debug(msgSupplier.get(), thrown); - } else { - slf4jLogger.trace(msgSupplier.get(), thrown); + InternalLogger.Level internalLevel = toInternalLevel(level); + if (internalLogger.isLoggable(internalLevel)) { + internalLogger.log(internalLevel, msgSupplier.get(), thrown); } } public boolean isLoggable(Level level) { - if (level.intValue() >= Level.SEVERE.intValue()) { - return slf4jLogger.isErrorEnabled(); - } else if (level.intValue() >= Level.WARNING.intValue()) { - return slf4jLogger.isWarnEnabled(); - } else if (level.intValue() >= Level.CONFIG.intValue()) { - return slf4jLogger.isInfoEnabled(); - } else if (level.intValue() >= Level.FINE.intValue()) { - return slf4jLogger.isDebugEnabled(); - } else { - return slf4jLogger.isTraceEnabled(); - } + return internalLogger.isLoggable(toInternalLevel(level)); } public Level getLevel() { - if (slf4jLogger.isErrorEnabled()) { + if (internalLogger.isLoggable(InternalLogger.Level.ERROR)) { return Level.SEVERE; - } else if (slf4jLogger.isWarnEnabled()) { + } else if (internalLogger.isLoggable(InternalLogger.Level.WARN)) { return Level.WARNING; - } else if (slf4jLogger.isInfoEnabled()) { + } else if (internalLogger.isLoggable(InternalLogger.Level.INFO)) { return Level.CONFIG; - } else if (slf4jLogger.isDebugEnabled()) { + } else if (internalLogger.isLoggable(InternalLogger.Level.DEBUG)) { return Level.FINE; - } else if (slf4jLogger.isTraceEnabled()) { + } else if (internalLogger.isLoggable(InternalLogger.Level.TRACE)) { return Level.FINEST; } else { return Level.OFF; } } + private static InternalLogger.Level toInternalLevel(Level level) { + if (level.intValue() >= Level.SEVERE.intValue()) { + return InternalLogger.Level.ERROR; + } else if (level.intValue() >= Level.WARNING.intValue()) { + return InternalLogger.Level.WARN; + } else if (level.intValue() >= Level.CONFIG.intValue()) { + return InternalLogger.Level.INFO; + } else if (level.intValue() >= Level.FINE.intValue()) { + return InternalLogger.Level.DEBUG; + } else { + return InternalLogger.Level.TRACE; + } + } + public void logp(Level level, String sourceClass, String sourceMethod, String msg) { log(level, msg); } 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 c18ee8b309e7..77a446ef926e 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 @@ -6,6 +6,7 @@ package io.opentelemetry.javaagent.bootstrap; import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; @@ -63,19 +64,13 @@ void testImplementsAllMethods() { assertThat(patchLoggerMethods).containsAll(julLoggerMethods); } - @Test - void testGetLogger() { - PatchLogger logger = PatchLogger.getLogger("abc"); - assertThat(logger.getSlf4jLogger().getName()).isEqualTo("abc"); - } - @Test void testGetName() { // given - org.slf4j.Logger slf4jLogger = mock(org.slf4j.Logger.class); - when(slf4jLogger.getName()).thenReturn("xyz"); + InternalLogger internalLogger = mock(InternalLogger.class); + when(internalLogger.name()).thenReturn("xyz"); // when - PatchLogger logger = new PatchLogger(slf4jLogger); + PatchLogger logger = new PatchLogger(internalLogger); // then assertThat(logger.getName()).isEqualTo("xyz"); } @@ -83,8 +78,8 @@ void testGetName() { @Test void testNormalMethods() { // given - org.slf4j.Logger slf4jLogger = mock(org.slf4j.Logger.class); - PatchLogger logger = new PatchLogger(slf4jLogger); + InternalLogger internalLogger = mock(InternalLogger.class); + PatchLogger logger = new PatchLogger(internalLogger); // when logger.severe("ereves"); @@ -96,22 +91,22 @@ void testNormalMethods() { logger.finest("tsenif"); // then - InOrder inOrder = Mockito.inOrder(slf4jLogger); - inOrder.verify(slf4jLogger).error("ereves"); - inOrder.verify(slf4jLogger).warn("gninraw"); - inOrder.verify(slf4jLogger).info("ofni"); - inOrder.verify(slf4jLogger).info("gifnoc"); - inOrder.verify(slf4jLogger).debug("enif"); - inOrder.verify(slf4jLogger).trace("renif"); - inOrder.verify(slf4jLogger).trace("tsenif"); - verifyNoMoreInteractions(slf4jLogger); + InOrder inOrder = Mockito.inOrder(internalLogger); + inOrder.verify(internalLogger).log(InternalLogger.Level.ERROR, "ereves", null); + inOrder.verify(internalLogger).log(InternalLogger.Level.WARN, "gninraw", null); + inOrder.verify(internalLogger).log(InternalLogger.Level.INFO, "ofni", null); + inOrder.verify(internalLogger).log(InternalLogger.Level.INFO, "gifnoc", null); + inOrder.verify(internalLogger).log(InternalLogger.Level.DEBUG, "enif", null); + inOrder.verify(internalLogger).log(InternalLogger.Level.TRACE, "renif", null); + inOrder.verify(internalLogger).log(InternalLogger.Level.TRACE, "tsenif", null); + verifyNoMoreInteractions(internalLogger); } @Test void testParameterizedLevelMethodsWithNoParams() { // given - org.slf4j.Logger slf4jLogger = mock(org.slf4j.Logger.class); - PatchLogger logger = new PatchLogger(slf4jLogger); + InternalLogger internalLogger = mock(InternalLogger.class); + PatchLogger logger = new PatchLogger(internalLogger); // when logger.log(Level.SEVERE, "ereves"); @@ -123,27 +118,23 @@ void testParameterizedLevelMethodsWithNoParams() { logger.log(Level.FINEST, "tsenif"); // then - InOrder inOrder = Mockito.inOrder(slf4jLogger); - inOrder.verify(slf4jLogger).error("ereves"); - inOrder.verify(slf4jLogger).warn("gninraw"); - inOrder.verify(slf4jLogger).info("ofni"); - inOrder.verify(slf4jLogger).info("gifnoc"); - inOrder.verify(slf4jLogger).debug("enif"); - inOrder.verify(slf4jLogger).trace("renif"); - inOrder.verify(slf4jLogger).trace("tsenif"); - verifyNoMoreInteractions(slf4jLogger); + InOrder inOrder = Mockito.inOrder(internalLogger); + inOrder.verify(internalLogger).log(InternalLogger.Level.ERROR, "ereves", null); + inOrder.verify(internalLogger).log(InternalLogger.Level.WARN, "gninraw", null); + inOrder.verify(internalLogger).log(InternalLogger.Level.INFO, "ofni", null); + inOrder.verify(internalLogger).log(InternalLogger.Level.INFO, "gifnoc", null); + inOrder.verify(internalLogger).log(InternalLogger.Level.DEBUG, "enif", null); + inOrder.verify(internalLogger).log(InternalLogger.Level.TRACE, "renif", null); + inOrder.verify(internalLogger).log(InternalLogger.Level.TRACE, "tsenif", null); + verifyNoMoreInteractions(internalLogger); } @Test void testParameterizedLevelMethodsWithSingleParam() { // given - org.slf4j.Logger slf4jLogger = mock(org.slf4j.Logger.class); - when(slf4jLogger.isTraceEnabled()).thenReturn(true); - when(slf4jLogger.isDebugEnabled()).thenReturn(true); - when(slf4jLogger.isInfoEnabled()).thenReturn(true); - when(slf4jLogger.isWarnEnabled()).thenReturn(true); - when(slf4jLogger.isErrorEnabled()).thenReturn(true); - PatchLogger logger = new PatchLogger(slf4jLogger); + InternalLogger internalLogger = mock(InternalLogger.class); + when(internalLogger.isLoggable(any())).thenReturn(true); + PatchLogger logger = new PatchLogger(internalLogger); // when logger.log(Level.SEVERE, "ereves: {0}", "a"); @@ -155,34 +146,30 @@ void testParameterizedLevelMethodsWithSingleParam() { logger.log(Level.FINEST, "tsenif: {0}", "g"); // then - InOrder inOrder = Mockito.inOrder(slf4jLogger); - inOrder.verify(slf4jLogger).isErrorEnabled(); - inOrder.verify(slf4jLogger).error("ereves: a"); - inOrder.verify(slf4jLogger).isWarnEnabled(); - inOrder.verify(slf4jLogger).warn("gninraw: b"); - inOrder.verify(slf4jLogger).isInfoEnabled(); - inOrder.verify(slf4jLogger).info("ofni: c"); - inOrder.verify(slf4jLogger).isInfoEnabled(); - inOrder.verify(slf4jLogger).info("gifnoc: d"); - inOrder.verify(slf4jLogger).isDebugEnabled(); - inOrder.verify(slf4jLogger).debug("enif: e"); - inOrder.verify(slf4jLogger).isTraceEnabled(); - inOrder.verify(slf4jLogger).trace("renif: f"); - inOrder.verify(slf4jLogger).isTraceEnabled(); - inOrder.verify(slf4jLogger).trace("tsenif: g"); - verifyNoMoreInteractions(slf4jLogger); + InOrder inOrder = Mockito.inOrder(internalLogger); + inOrder.verify(internalLogger).isLoggable(InternalLogger.Level.ERROR); + inOrder.verify(internalLogger).log(InternalLogger.Level.ERROR, "ereves: a", null); + inOrder.verify(internalLogger).isLoggable(InternalLogger.Level.WARN); + inOrder.verify(internalLogger).log(InternalLogger.Level.WARN, "gninraw: b", null); + inOrder.verify(internalLogger).isLoggable(InternalLogger.Level.INFO); + inOrder.verify(internalLogger).log(InternalLogger.Level.INFO, "ofni: c", null); + inOrder.verify(internalLogger).isLoggable(InternalLogger.Level.INFO); + inOrder.verify(internalLogger).log(InternalLogger.Level.INFO, "gifnoc: d", null); + inOrder.verify(internalLogger).isLoggable(InternalLogger.Level.DEBUG); + inOrder.verify(internalLogger).log(InternalLogger.Level.DEBUG, "enif: e", null); + inOrder.verify(internalLogger).isLoggable(InternalLogger.Level.TRACE); + inOrder.verify(internalLogger).log(InternalLogger.Level.TRACE, "renif: f", null); + inOrder.verify(internalLogger).isLoggable(InternalLogger.Level.TRACE); + inOrder.verify(internalLogger).log(InternalLogger.Level.TRACE, "tsenif: g", null); + verifyNoMoreInteractions(internalLogger); } @Test void testParameterizedLevelMethodsWithArrayOfParams() { // given - org.slf4j.Logger slf4jLogger = mock(org.slf4j.Logger.class); - when(slf4jLogger.isTraceEnabled()).thenReturn(true); - when(slf4jLogger.isDebugEnabled()).thenReturn(true); - when(slf4jLogger.isInfoEnabled()).thenReturn(true); - when(slf4jLogger.isWarnEnabled()).thenReturn(true); - when(slf4jLogger.isErrorEnabled()).thenReturn(true); - PatchLogger logger = new PatchLogger(slf4jLogger); + InternalLogger internalLogger = mock(InternalLogger.class); + when(internalLogger.isLoggable(any())).thenReturn(true); + PatchLogger logger = new PatchLogger(internalLogger); // when logger.log(Level.SEVERE, "ereves: {0},{1}", new Object[] {"a", "b"}); @@ -194,29 +181,29 @@ void testParameterizedLevelMethodsWithArrayOfParams() { logger.log(Level.FINEST, "tsenif: {0},{1}", new Object[] {"g", "h"}); // then - InOrder inOrder = Mockito.inOrder(slf4jLogger); - inOrder.verify(slf4jLogger).isErrorEnabled(); - inOrder.verify(slf4jLogger).error("ereves: a,b"); - inOrder.verify(slf4jLogger).isWarnEnabled(); - inOrder.verify(slf4jLogger).warn("gninraw: b,c"); - inOrder.verify(slf4jLogger).isInfoEnabled(); - inOrder.verify(slf4jLogger).info("ofni: c,d"); - inOrder.verify(slf4jLogger).isInfoEnabled(); - inOrder.verify(slf4jLogger).info("gifnoc: d,e"); - inOrder.verify(slf4jLogger).isDebugEnabled(); - inOrder.verify(slf4jLogger).debug("enif: e,f"); - inOrder.verify(slf4jLogger).isTraceEnabled(); - inOrder.verify(slf4jLogger).trace("renif: f,g"); - inOrder.verify(slf4jLogger).isTraceEnabled(); - inOrder.verify(slf4jLogger).trace("tsenif: g,h"); - verifyNoMoreInteractions(slf4jLogger); + InOrder inOrder = Mockito.inOrder(internalLogger); + inOrder.verify(internalLogger).isLoggable(InternalLogger.Level.ERROR); + inOrder.verify(internalLogger).log(InternalLogger.Level.ERROR, "ereves: a,b", null); + inOrder.verify(internalLogger).isLoggable(InternalLogger.Level.WARN); + inOrder.verify(internalLogger).log(InternalLogger.Level.WARN, "gninraw: b,c", null); + inOrder.verify(internalLogger).isLoggable(InternalLogger.Level.INFO); + inOrder.verify(internalLogger).log(InternalLogger.Level.INFO, "ofni: c,d", null); + inOrder.verify(internalLogger).isLoggable(InternalLogger.Level.INFO); + inOrder.verify(internalLogger).log(InternalLogger.Level.INFO, "gifnoc: d,e", null); + inOrder.verify(internalLogger).isLoggable(InternalLogger.Level.DEBUG); + inOrder.verify(internalLogger).log(InternalLogger.Level.DEBUG, "enif: e,f", null); + inOrder.verify(internalLogger).isLoggable(InternalLogger.Level.TRACE); + inOrder.verify(internalLogger).log(InternalLogger.Level.TRACE, "renif: f,g", null); + inOrder.verify(internalLogger).isLoggable(InternalLogger.Level.TRACE); + inOrder.verify(internalLogger).log(InternalLogger.Level.TRACE, "tsenif: g,h", null); + verifyNoMoreInteractions(internalLogger); } @Test void testParameterizedLevelMethodsWithThrowable() { // given - org.slf4j.Logger slf4jLogger = mock(org.slf4j.Logger.class); - PatchLogger logger = new PatchLogger(slf4jLogger); + InternalLogger internalLogger = mock(InternalLogger.class); + PatchLogger logger = new PatchLogger(internalLogger); Throwable a = new Throwable(); Throwable b = new Throwable(); Throwable c = new Throwable(); @@ -235,29 +222,25 @@ void testParameterizedLevelMethodsWithThrowable() { logger.log(Level.FINEST, "tsenif", g); // then - InOrder inOrder = Mockito.inOrder(slf4jLogger); - inOrder.verify(slf4jLogger).error("ereves", a); - inOrder.verify(slf4jLogger).warn("gninraw", b); - inOrder.verify(slf4jLogger).info("ofni", c); - inOrder.verify(slf4jLogger).info("gifnoc", d); - inOrder.verify(slf4jLogger).debug("enif", e); - inOrder.verify(slf4jLogger).trace("renif", f); - inOrder.verify(slf4jLogger).trace("tsenif", g); - verifyNoMoreInteractions(slf4jLogger); + InOrder inOrder = Mockito.inOrder(internalLogger); + inOrder.verify(internalLogger).log(InternalLogger.Level.ERROR, "ereves", a); + inOrder.verify(internalLogger).log(InternalLogger.Level.WARN, "gninraw", b); + inOrder.verify(internalLogger).log(InternalLogger.Level.INFO, "ofni", c); + inOrder.verify(internalLogger).log(InternalLogger.Level.INFO, "gifnoc", d); + inOrder.verify(internalLogger).log(InternalLogger.Level.DEBUG, "enif", e); + inOrder.verify(internalLogger).log(InternalLogger.Level.TRACE, "renif", f); + inOrder.verify(internalLogger).log(InternalLogger.Level.TRACE, "tsenif", g); + verifyNoMoreInteractions(internalLogger); } @Test void testIsLoggableAll() { // given - org.slf4j.Logger slf4jLogger = mock(org.slf4j.Logger.class); - when(slf4jLogger.isTraceEnabled()).thenReturn(true); - when(slf4jLogger.isDebugEnabled()).thenReturn(true); - when(slf4jLogger.isInfoEnabled()).thenReturn(true); - when(slf4jLogger.isWarnEnabled()).thenReturn(true); - when(slf4jLogger.isErrorEnabled()).thenReturn(true); + InternalLogger internalLogger = mock(InternalLogger.class); + when(internalLogger.isLoggable(any())).thenReturn(true); // when - PatchLogger logger = new PatchLogger(slf4jLogger); + PatchLogger logger = new PatchLogger(internalLogger); // then assertThat(logger.isLoggable(Level.SEVERE)).isTrue(); @@ -272,15 +255,12 @@ void testIsLoggableAll() { @Test void testIsLoggableSome() { // given - org.slf4j.Logger slf4jLogger = mock(org.slf4j.Logger.class); - when(slf4jLogger.isTraceEnabled()).thenReturn(false); - when(slf4jLogger.isDebugEnabled()).thenReturn(false); - when(slf4jLogger.isInfoEnabled()).thenReturn(false); - when(slf4jLogger.isWarnEnabled()).thenReturn(true); - when(slf4jLogger.isErrorEnabled()).thenReturn(true); + InternalLogger internalLogger = mock(InternalLogger.class); + when(internalLogger.isLoggable(InternalLogger.Level.ERROR)).thenReturn(true); + when(internalLogger.isLoggable(InternalLogger.Level.WARN)).thenReturn(true); // when - PatchLogger logger = new PatchLogger(slf4jLogger); + PatchLogger logger = new PatchLogger(internalLogger); // then assertThat(logger.isLoggable(Level.SEVERE)).isTrue(); @@ -295,15 +275,10 @@ void testIsLoggableSome() { @Test void testIsLoggableNone() { // given - org.slf4j.Logger slf4jLogger = mock(org.slf4j.Logger.class); - when(slf4jLogger.isTraceEnabled()).thenReturn(false); - when(slf4jLogger.isDebugEnabled()).thenReturn(false); - when(slf4jLogger.isInfoEnabled()).thenReturn(false); - when(slf4jLogger.isWarnEnabled()).thenReturn(false); - when(slf4jLogger.isErrorEnabled()).thenReturn(false); + InternalLogger internalLogger = mock(InternalLogger.class); // when - PatchLogger logger = new PatchLogger(slf4jLogger); + PatchLogger logger = new PatchLogger(internalLogger); // then assertThat(logger.isLoggable(Level.SEVERE)).isFalse(); @@ -318,10 +293,10 @@ void testIsLoggableNone() { @Test void testGetLevelSevere() { // given - org.slf4j.Logger slf4jLogger = mock(org.slf4j.Logger.class); - when(slf4jLogger.isErrorEnabled()).thenReturn(true); + InternalLogger internalLogger = mock(InternalLogger.class); + when(internalLogger.isLoggable(InternalLogger.Level.ERROR)).thenReturn(true); // when - PatchLogger logger = new PatchLogger(slf4jLogger); + PatchLogger logger = new PatchLogger(internalLogger); // then assertThat(logger.getLevel()).isEqualTo(Level.SEVERE); } @@ -329,10 +304,10 @@ void testGetLevelSevere() { @Test void testGetLevelWarning() { // given - org.slf4j.Logger slf4jLogger = mock(org.slf4j.Logger.class); - when(slf4jLogger.isWarnEnabled()).thenReturn(true); + InternalLogger internalLogger = mock(InternalLogger.class); + when(internalLogger.isLoggable(InternalLogger.Level.WARN)).thenReturn(true); // when - PatchLogger logger = new PatchLogger(slf4jLogger); + PatchLogger logger = new PatchLogger(internalLogger); // then assertThat(logger.getLevel()).isEqualTo(Level.WARNING); } @@ -340,10 +315,10 @@ void testGetLevelWarning() { @Test void testGetLevelConfig() { // given - org.slf4j.Logger slf4jLogger = mock(org.slf4j.Logger.class); - when(slf4jLogger.isInfoEnabled()).thenReturn(true); + InternalLogger internalLogger = mock(InternalLogger.class); + when(internalLogger.isLoggable(InternalLogger.Level.INFO)).thenReturn(true); // when - PatchLogger logger = new PatchLogger(slf4jLogger); + PatchLogger logger = new PatchLogger(internalLogger); // then assertThat(logger.getLevel()).isEqualTo(Level.CONFIG); } @@ -351,10 +326,10 @@ void testGetLevelConfig() { @Test void testGetLevelFine() { // given - org.slf4j.Logger slf4jLogger = mock(org.slf4j.Logger.class); - when(slf4jLogger.isDebugEnabled()).thenReturn(true); + InternalLogger internalLogger = mock(InternalLogger.class); + when(internalLogger.isLoggable(InternalLogger.Level.DEBUG)).thenReturn(true); // when - PatchLogger logger = new PatchLogger(slf4jLogger); + PatchLogger logger = new PatchLogger(internalLogger); // then assertThat(logger.getLevel()).isEqualTo(Level.FINE); } @@ -362,10 +337,10 @@ void testGetLevelFine() { @Test void testGetLevelFinest() { // given - org.slf4j.Logger slf4jLogger = mock(org.slf4j.Logger.class); - when(slf4jLogger.isTraceEnabled()).thenReturn(true); + InternalLogger internalLogger = mock(InternalLogger.class); + when(internalLogger.isLoggable(InternalLogger.Level.TRACE)).thenReturn(true); // when - PatchLogger logger = new PatchLogger(slf4jLogger); + PatchLogger logger = new PatchLogger(internalLogger); // then assertThat(logger.getLevel()).isEqualTo(Level.FINEST); } @@ -373,9 +348,9 @@ void testGetLevelFinest() { @Test void testGetLevelOff() { // given - org.slf4j.Logger slf4jLogger = mock(org.slf4j.Logger.class); + InternalLogger internalLogger = mock(InternalLogger.class); // when - PatchLogger logger = new PatchLogger(slf4jLogger); + PatchLogger logger = new PatchLogger(internalLogger); // then assertThat(logger.getLevel()).isEqualTo(Level.OFF); } @@ -383,8 +358,8 @@ void testGetLevelOff() { @Test void testLogpParameterizedLevelMethodsWithNoParams() { // given - org.slf4j.Logger slf4jLogger = mock(org.slf4j.Logger.class); - PatchLogger logger = new PatchLogger(slf4jLogger); + InternalLogger internalLogger = mock(InternalLogger.class); + PatchLogger logger = new PatchLogger(internalLogger); // when logger.logp(Level.SEVERE, null, null, "ereves"); @@ -396,27 +371,23 @@ void testLogpParameterizedLevelMethodsWithNoParams() { logger.logp(Level.FINEST, null, null, "tsenif"); // then - InOrder inOrder = Mockito.inOrder(slf4jLogger); - inOrder.verify(slf4jLogger).error("ereves"); - inOrder.verify(slf4jLogger).warn("gninraw"); - inOrder.verify(slf4jLogger).info("ofni"); - inOrder.verify(slf4jLogger).info("gifnoc"); - inOrder.verify(slf4jLogger).debug("enif"); - inOrder.verify(slf4jLogger).trace("renif"); - inOrder.verify(slf4jLogger).trace("tsenif"); - verifyNoMoreInteractions(slf4jLogger); + InOrder inOrder = Mockito.inOrder(internalLogger); + inOrder.verify(internalLogger).log(InternalLogger.Level.ERROR, "ereves", null); + inOrder.verify(internalLogger).log(InternalLogger.Level.WARN, "gninraw", null); + inOrder.verify(internalLogger).log(InternalLogger.Level.INFO, "ofni", null); + inOrder.verify(internalLogger).log(InternalLogger.Level.INFO, "gifnoc", null); + inOrder.verify(internalLogger).log(InternalLogger.Level.DEBUG, "enif", null); + inOrder.verify(internalLogger).log(InternalLogger.Level.TRACE, "renif", null); + inOrder.verify(internalLogger).log(InternalLogger.Level.TRACE, "tsenif", null); + verifyNoMoreInteractions(internalLogger); } @Test void testLogpParameterizedLevelMethodsWithSingleParam() { // given - org.slf4j.Logger slf4jLogger = mock(org.slf4j.Logger.class); - when(slf4jLogger.isTraceEnabled()).thenReturn(true); - when(slf4jLogger.isDebugEnabled()).thenReturn(true); - when(slf4jLogger.isInfoEnabled()).thenReturn(true); - when(slf4jLogger.isWarnEnabled()).thenReturn(true); - when(slf4jLogger.isErrorEnabled()).thenReturn(true); - PatchLogger logger = new PatchLogger(slf4jLogger); + InternalLogger internalLogger = mock(InternalLogger.class); + when(internalLogger.isLoggable(any())).thenReturn(true); + PatchLogger logger = new PatchLogger(internalLogger); // when logger.logp(Level.SEVERE, null, null, "ereves: {0}", "a"); @@ -428,34 +399,30 @@ void testLogpParameterizedLevelMethodsWithSingleParam() { logger.logp(Level.FINEST, null, null, "tsenif: {0}", "g"); // then - InOrder inOrder = Mockito.inOrder(slf4jLogger); - inOrder.verify(slf4jLogger).isErrorEnabled(); - inOrder.verify(slf4jLogger).error("ereves: a"); - inOrder.verify(slf4jLogger).isWarnEnabled(); - inOrder.verify(slf4jLogger).warn("gninraw: b"); - inOrder.verify(slf4jLogger).isInfoEnabled(); - inOrder.verify(slf4jLogger).info("ofni: c"); - inOrder.verify(slf4jLogger).isInfoEnabled(); - inOrder.verify(slf4jLogger).info("gifnoc: d"); - inOrder.verify(slf4jLogger).isDebugEnabled(); - inOrder.verify(slf4jLogger).debug("enif: e"); - inOrder.verify(slf4jLogger).isTraceEnabled(); - inOrder.verify(slf4jLogger).trace("renif: f"); - inOrder.verify(slf4jLogger).isTraceEnabled(); - inOrder.verify(slf4jLogger).trace("tsenif: g"); - verifyNoMoreInteractions(slf4jLogger); + InOrder inOrder = Mockito.inOrder(internalLogger); + inOrder.verify(internalLogger).isLoggable(InternalLogger.Level.ERROR); + inOrder.verify(internalLogger).log(InternalLogger.Level.ERROR, "ereves: a", null); + inOrder.verify(internalLogger).isLoggable(InternalLogger.Level.WARN); + inOrder.verify(internalLogger).log(InternalLogger.Level.WARN, "gninraw: b", null); + inOrder.verify(internalLogger).isLoggable(InternalLogger.Level.INFO); + inOrder.verify(internalLogger).log(InternalLogger.Level.INFO, "ofni: c", null); + inOrder.verify(internalLogger).isLoggable(InternalLogger.Level.INFO); + inOrder.verify(internalLogger).log(InternalLogger.Level.INFO, "gifnoc: d", null); + inOrder.verify(internalLogger).isLoggable(InternalLogger.Level.DEBUG); + inOrder.verify(internalLogger).log(InternalLogger.Level.DEBUG, "enif: e", null); + inOrder.verify(internalLogger).isLoggable(InternalLogger.Level.TRACE); + inOrder.verify(internalLogger).log(InternalLogger.Level.TRACE, "renif: f", null); + inOrder.verify(internalLogger).isLoggable(InternalLogger.Level.TRACE); + inOrder.verify(internalLogger).log(InternalLogger.Level.TRACE, "tsenif: g", null); + verifyNoMoreInteractions(internalLogger); } @Test void testLogpParameterizedLevelMethodsWithArrayOfParams() { // given - org.slf4j.Logger slf4jLogger = mock(org.slf4j.Logger.class); - when(slf4jLogger.isTraceEnabled()).thenReturn(true); - when(slf4jLogger.isDebugEnabled()).thenReturn(true); - when(slf4jLogger.isInfoEnabled()).thenReturn(true); - when(slf4jLogger.isWarnEnabled()).thenReturn(true); - when(slf4jLogger.isErrorEnabled()).thenReturn(true); - PatchLogger logger = new PatchLogger(slf4jLogger); + InternalLogger internalLogger = mock(InternalLogger.class); + when(internalLogger.isLoggable(any())).thenReturn(true); + PatchLogger logger = new PatchLogger(internalLogger); // when logger.logp(Level.SEVERE, null, null, "ereves: {0},{1}", new Object[] {"a", "b"}); @@ -467,29 +434,29 @@ void testLogpParameterizedLevelMethodsWithArrayOfParams() { logger.logp(Level.FINEST, null, null, "tsenif: {0},{1}", new Object[] {"g", "h"}); // then - InOrder inOrder = Mockito.inOrder(slf4jLogger); - inOrder.verify(slf4jLogger).isErrorEnabled(); - inOrder.verify(slf4jLogger).error("ereves: a,b"); - inOrder.verify(slf4jLogger).isWarnEnabled(); - inOrder.verify(slf4jLogger).warn("gninraw: b,c"); - inOrder.verify(slf4jLogger).isInfoEnabled(); - inOrder.verify(slf4jLogger).info("ofni: c,d"); - inOrder.verify(slf4jLogger).isInfoEnabled(); - inOrder.verify(slf4jLogger).info("gifnoc: d,e"); - inOrder.verify(slf4jLogger).isDebugEnabled(); - inOrder.verify(slf4jLogger).debug("enif: e,f"); - inOrder.verify(slf4jLogger).isTraceEnabled(); - inOrder.verify(slf4jLogger).trace("renif: f,g"); - inOrder.verify(slf4jLogger).isTraceEnabled(); - inOrder.verify(slf4jLogger).trace("tsenif: g,h"); - verifyNoMoreInteractions(slf4jLogger); + InOrder inOrder = Mockito.inOrder(internalLogger); + inOrder.verify(internalLogger).isLoggable(InternalLogger.Level.ERROR); + inOrder.verify(internalLogger).log(InternalLogger.Level.ERROR, "ereves: a,b", null); + inOrder.verify(internalLogger).isLoggable(InternalLogger.Level.WARN); + inOrder.verify(internalLogger).log(InternalLogger.Level.WARN, "gninraw: b,c", null); + inOrder.verify(internalLogger).isLoggable(InternalLogger.Level.INFO); + inOrder.verify(internalLogger).log(InternalLogger.Level.INFO, "ofni: c,d", null); + inOrder.verify(internalLogger).isLoggable(InternalLogger.Level.INFO); + inOrder.verify(internalLogger).log(InternalLogger.Level.INFO, "gifnoc: d,e", null); + inOrder.verify(internalLogger).isLoggable(InternalLogger.Level.DEBUG); + inOrder.verify(internalLogger).log(InternalLogger.Level.DEBUG, "enif: e,f", null); + inOrder.verify(internalLogger).isLoggable(InternalLogger.Level.TRACE); + inOrder.verify(internalLogger).log(InternalLogger.Level.TRACE, "renif: f,g", null); + inOrder.verify(internalLogger).isLoggable(InternalLogger.Level.TRACE); + inOrder.verify(internalLogger).log(InternalLogger.Level.TRACE, "tsenif: g,h", null); + verifyNoMoreInteractions(internalLogger); } @Test void testLogpParameterizedLevelMethodsWithThrowable() { // given - org.slf4j.Logger slf4jLogger = mock(org.slf4j.Logger.class); - PatchLogger logger = new PatchLogger(slf4jLogger); + InternalLogger internalLogger = mock(InternalLogger.class); + PatchLogger logger = new PatchLogger(internalLogger); Throwable a = new Throwable(); Throwable b = new Throwable(); Throwable c = new Throwable(); @@ -508,22 +475,22 @@ void testLogpParameterizedLevelMethodsWithThrowable() { logger.logp(Level.FINEST, null, null, "tsenif", g); // then - InOrder inOrder = Mockito.inOrder(slf4jLogger); - inOrder.verify(slf4jLogger).error("ereves", a); - inOrder.verify(slf4jLogger).warn("gninraw", b); - inOrder.verify(slf4jLogger).info("ofni", c); - inOrder.verify(slf4jLogger).info("gifnoc", d); - inOrder.verify(slf4jLogger).debug("enif", e); - inOrder.verify(slf4jLogger).trace("renif", f); - inOrder.verify(slf4jLogger).trace("tsenif", g); - verifyNoMoreInteractions(slf4jLogger); + InOrder inOrder = Mockito.inOrder(internalLogger); + inOrder.verify(internalLogger).log(InternalLogger.Level.ERROR, "ereves", a); + inOrder.verify(internalLogger).log(InternalLogger.Level.WARN, "gninraw", b); + inOrder.verify(internalLogger).log(InternalLogger.Level.INFO, "ofni", c); + inOrder.verify(internalLogger).log(InternalLogger.Level.INFO, "gifnoc", d); + inOrder.verify(internalLogger).log(InternalLogger.Level.DEBUG, "enif", e); + inOrder.verify(internalLogger).log(InternalLogger.Level.TRACE, "renif", f); + inOrder.verify(internalLogger).log(InternalLogger.Level.TRACE, "tsenif", g); + verifyNoMoreInteractions(internalLogger); } @Test void testLogrbParameterizedLevelMethodsWithNoParams() { // given - org.slf4j.Logger slf4jLogger = mock(org.slf4j.Logger.class); - PatchLogger logger = new PatchLogger(slf4jLogger); + InternalLogger internalLogger = mock(InternalLogger.class); + PatchLogger logger = new PatchLogger(internalLogger); // when logger.logrb(Level.SEVERE, null, null, null, "ereves"); @@ -535,27 +502,23 @@ void testLogrbParameterizedLevelMethodsWithNoParams() { logger.logrb(Level.FINEST, null, null, null, "tsenif"); // then - InOrder inOrder = Mockito.inOrder(slf4jLogger); - inOrder.verify(slf4jLogger).error("ereves"); - inOrder.verify(slf4jLogger).warn("gninraw"); - inOrder.verify(slf4jLogger).info("ofni"); - inOrder.verify(slf4jLogger).info("gifnoc"); - inOrder.verify(slf4jLogger).debug("enif"); - inOrder.verify(slf4jLogger).trace("renif"); - inOrder.verify(slf4jLogger).trace("tsenif"); - verifyNoMoreInteractions(slf4jLogger); + InOrder inOrder = Mockito.inOrder(internalLogger); + inOrder.verify(internalLogger).log(InternalLogger.Level.ERROR, "ereves", null); + inOrder.verify(internalLogger).log(InternalLogger.Level.WARN, "gninraw", null); + inOrder.verify(internalLogger).log(InternalLogger.Level.INFO, "ofni", null); + inOrder.verify(internalLogger).log(InternalLogger.Level.INFO, "gifnoc", null); + inOrder.verify(internalLogger).log(InternalLogger.Level.DEBUG, "enif", null); + inOrder.verify(internalLogger).log(InternalLogger.Level.TRACE, "renif", null); + inOrder.verify(internalLogger).log(InternalLogger.Level.TRACE, "tsenif", null); + verifyNoMoreInteractions(internalLogger); } @Test void testLogrbParameterizedLevelMethodsWithSingleParam() { // given - org.slf4j.Logger slf4jLogger = mock(org.slf4j.Logger.class); - when(slf4jLogger.isTraceEnabled()).thenReturn(true); - when(slf4jLogger.isDebugEnabled()).thenReturn(true); - when(slf4jLogger.isInfoEnabled()).thenReturn(true); - when(slf4jLogger.isWarnEnabled()).thenReturn(true); - when(slf4jLogger.isErrorEnabled()).thenReturn(true); - PatchLogger logger = new PatchLogger(slf4jLogger); + InternalLogger internalLogger = mock(InternalLogger.class); + when(internalLogger.isLoggable(any())).thenReturn(true); + PatchLogger logger = new PatchLogger(internalLogger); // when logger.logrb(Level.SEVERE, null, null, null, "ereves: {0}", "a"); @@ -567,34 +530,30 @@ void testLogrbParameterizedLevelMethodsWithSingleParam() { logger.logrb(Level.FINEST, null, null, null, "tsenif: {0}", "g"); // then - InOrder inOrder = Mockito.inOrder(slf4jLogger); - inOrder.verify(slf4jLogger).isErrorEnabled(); - inOrder.verify(slf4jLogger).error("ereves: a"); - inOrder.verify(slf4jLogger).isWarnEnabled(); - inOrder.verify(slf4jLogger).warn("gninraw: b"); - inOrder.verify(slf4jLogger).isInfoEnabled(); - inOrder.verify(slf4jLogger).info("ofni: c"); - inOrder.verify(slf4jLogger).isInfoEnabled(); - inOrder.verify(slf4jLogger).info("gifnoc: d"); - inOrder.verify(slf4jLogger).isDebugEnabled(); - inOrder.verify(slf4jLogger).debug("enif: e"); - inOrder.verify(slf4jLogger).isTraceEnabled(); - inOrder.verify(slf4jLogger).trace("renif: f"); - inOrder.verify(slf4jLogger).isTraceEnabled(); - inOrder.verify(slf4jLogger).trace("tsenif: g"); - verifyNoMoreInteractions(slf4jLogger); + InOrder inOrder = Mockito.inOrder(internalLogger); + inOrder.verify(internalLogger).isLoggable(InternalLogger.Level.ERROR); + inOrder.verify(internalLogger).log(InternalLogger.Level.ERROR, "ereves: a", null); + inOrder.verify(internalLogger).isLoggable(InternalLogger.Level.WARN); + inOrder.verify(internalLogger).log(InternalLogger.Level.WARN, "gninraw: b", null); + inOrder.verify(internalLogger).isLoggable(InternalLogger.Level.INFO); + inOrder.verify(internalLogger).log(InternalLogger.Level.INFO, "ofni: c", null); + inOrder.verify(internalLogger).isLoggable(InternalLogger.Level.INFO); + inOrder.verify(internalLogger).log(InternalLogger.Level.INFO, "gifnoc: d", null); + inOrder.verify(internalLogger).isLoggable(InternalLogger.Level.DEBUG); + inOrder.verify(internalLogger).log(InternalLogger.Level.DEBUG, "enif: e", null); + inOrder.verify(internalLogger).isLoggable(InternalLogger.Level.TRACE); + inOrder.verify(internalLogger).log(InternalLogger.Level.TRACE, "renif: f", null); + inOrder.verify(internalLogger).isLoggable(InternalLogger.Level.TRACE); + inOrder.verify(internalLogger).log(InternalLogger.Level.TRACE, "tsenif: g", null); + verifyNoMoreInteractions(internalLogger); } @Test void testLogrbParameterizedLevelMethodsWithArrayOfParams() { // given - org.slf4j.Logger slf4jLogger = mock(org.slf4j.Logger.class); - when(slf4jLogger.isTraceEnabled()).thenReturn(true); - when(slf4jLogger.isDebugEnabled()).thenReturn(true); - when(slf4jLogger.isInfoEnabled()).thenReturn(true); - when(slf4jLogger.isWarnEnabled()).thenReturn(true); - when(slf4jLogger.isErrorEnabled()).thenReturn(true); - PatchLogger logger = new PatchLogger(slf4jLogger); + InternalLogger internalLogger = mock(InternalLogger.class); + when(internalLogger.isLoggable(any())).thenReturn(true); + PatchLogger logger = new PatchLogger(internalLogger); // when logger.logrb( @@ -610,34 +569,30 @@ void testLogrbParameterizedLevelMethodsWithArrayOfParams() { Level.FINEST, null, null, (String) null, "tsenif: {0},{1}", new Object[] {"g", "h"}); // then - InOrder inOrder = Mockito.inOrder(slf4jLogger); - inOrder.verify(slf4jLogger).isErrorEnabled(); - inOrder.verify(slf4jLogger).error("ereves: a,b"); - inOrder.verify(slf4jLogger).isWarnEnabled(); - inOrder.verify(slf4jLogger).warn("gninraw: b,c"); - inOrder.verify(slf4jLogger).isInfoEnabled(); - inOrder.verify(slf4jLogger).info("ofni: c,d"); - inOrder.verify(slf4jLogger).isInfoEnabled(); - inOrder.verify(slf4jLogger).info("gifnoc: d,e"); - inOrder.verify(slf4jLogger).isDebugEnabled(); - inOrder.verify(slf4jLogger).debug("enif: e,f"); - inOrder.verify(slf4jLogger).isTraceEnabled(); - inOrder.verify(slf4jLogger).trace("renif: f,g"); - inOrder.verify(slf4jLogger).isTraceEnabled(); - inOrder.verify(slf4jLogger).trace("tsenif: g,h"); - verifyNoMoreInteractions(slf4jLogger); + InOrder inOrder = Mockito.inOrder(internalLogger); + inOrder.verify(internalLogger).isLoggable(InternalLogger.Level.ERROR); + inOrder.verify(internalLogger).log(InternalLogger.Level.ERROR, "ereves: a,b", null); + inOrder.verify(internalLogger).isLoggable(InternalLogger.Level.WARN); + inOrder.verify(internalLogger).log(InternalLogger.Level.WARN, "gninraw: b,c", null); + inOrder.verify(internalLogger).isLoggable(InternalLogger.Level.INFO); + inOrder.verify(internalLogger).log(InternalLogger.Level.INFO, "ofni: c,d", null); + inOrder.verify(internalLogger).isLoggable(InternalLogger.Level.INFO); + inOrder.verify(internalLogger).log(InternalLogger.Level.INFO, "gifnoc: d,e", null); + inOrder.verify(internalLogger).isLoggable(InternalLogger.Level.DEBUG); + inOrder.verify(internalLogger).log(InternalLogger.Level.DEBUG, "enif: e,f", null); + inOrder.verify(internalLogger).isLoggable(InternalLogger.Level.TRACE); + inOrder.verify(internalLogger).log(InternalLogger.Level.TRACE, "renif: f,g", null); + inOrder.verify(internalLogger).isLoggable(InternalLogger.Level.TRACE); + inOrder.verify(internalLogger).log(InternalLogger.Level.TRACE, "tsenif: g,h", null); + verifyNoMoreInteractions(internalLogger); } @Test void testLogrbParameterizedLevelMethodsWithVarArgsOfParams() { // given - org.slf4j.Logger slf4jLogger = mock(org.slf4j.Logger.class); - when(slf4jLogger.isTraceEnabled()).thenReturn(true); - when(slf4jLogger.isDebugEnabled()).thenReturn(true); - when(slf4jLogger.isInfoEnabled()).thenReturn(true); - when(slf4jLogger.isWarnEnabled()).thenReturn(true); - when(slf4jLogger.isErrorEnabled()).thenReturn(true); - PatchLogger logger = new PatchLogger(slf4jLogger); + InternalLogger internalLogger = mock(InternalLogger.class); + when(internalLogger.isLoggable(any())).thenReturn(true); + PatchLogger logger = new PatchLogger(internalLogger); // when logger.logrb(Level.SEVERE, (String) null, null, null, "ereves: {0},{1}", "a", "b"); @@ -649,34 +604,30 @@ void testLogrbParameterizedLevelMethodsWithVarArgsOfParams() { logger.logrb(Level.FINEST, (String) null, null, null, "tsenif: {0},{1}", "g", "h"); // then - InOrder inOrder = Mockito.inOrder(slf4jLogger); - inOrder.verify(slf4jLogger).isErrorEnabled(); - inOrder.verify(slf4jLogger).error("ereves: a,b"); - inOrder.verify(slf4jLogger).isWarnEnabled(); - inOrder.verify(slf4jLogger).warn("gninraw: b,c"); - inOrder.verify(slf4jLogger).isInfoEnabled(); - inOrder.verify(slf4jLogger).info("ofni: c,d"); - inOrder.verify(slf4jLogger).isInfoEnabled(); - inOrder.verify(slf4jLogger).info("gifnoc: d,e"); - inOrder.verify(slf4jLogger).isDebugEnabled(); - inOrder.verify(slf4jLogger).debug("enif: e,f"); - inOrder.verify(slf4jLogger).isTraceEnabled(); - inOrder.verify(slf4jLogger).trace("renif: f,g"); - inOrder.verify(slf4jLogger).isTraceEnabled(); - inOrder.verify(slf4jLogger).trace("tsenif: g,h"); - verifyNoMoreInteractions(slf4jLogger); + InOrder inOrder = Mockito.inOrder(internalLogger); + inOrder.verify(internalLogger).isLoggable(InternalLogger.Level.ERROR); + inOrder.verify(internalLogger).log(InternalLogger.Level.ERROR, "ereves: a,b", null); + inOrder.verify(internalLogger).isLoggable(InternalLogger.Level.WARN); + inOrder.verify(internalLogger).log(InternalLogger.Level.WARN, "gninraw: b,c", null); + inOrder.verify(internalLogger).isLoggable(InternalLogger.Level.INFO); + inOrder.verify(internalLogger).log(InternalLogger.Level.INFO, "ofni: c,d", null); + inOrder.verify(internalLogger).isLoggable(InternalLogger.Level.INFO); + inOrder.verify(internalLogger).log(InternalLogger.Level.INFO, "gifnoc: d,e", null); + inOrder.verify(internalLogger).isLoggable(InternalLogger.Level.DEBUG); + inOrder.verify(internalLogger).log(InternalLogger.Level.DEBUG, "enif: e,f", null); + inOrder.verify(internalLogger).isLoggable(InternalLogger.Level.TRACE); + inOrder.verify(internalLogger).log(InternalLogger.Level.TRACE, "renif: f,g", null); + inOrder.verify(internalLogger).isLoggable(InternalLogger.Level.TRACE); + inOrder.verify(internalLogger).log(InternalLogger.Level.TRACE, "tsenif: g,h", null); + verifyNoMoreInteractions(internalLogger); } @Test void testLogrbParameterizedLevelMethodsWithVarArgsOfParams2() { // given - org.slf4j.Logger slf4jLogger = mock(org.slf4j.Logger.class); - when(slf4jLogger.isTraceEnabled()).thenReturn(true); - when(slf4jLogger.isDebugEnabled()).thenReturn(true); - when(slf4jLogger.isInfoEnabled()).thenReturn(true); - when(slf4jLogger.isWarnEnabled()).thenReturn(true); - when(slf4jLogger.isErrorEnabled()).thenReturn(true); - PatchLogger logger = new PatchLogger(slf4jLogger); + InternalLogger internalLogger = mock(InternalLogger.class); + when(internalLogger.isLoggable(any())).thenReturn(true); + PatchLogger logger = new PatchLogger(internalLogger); // when logger.logrb(Level.SEVERE, (ResourceBundle) null, "ereves: {0},{1}", "a", "b"); @@ -688,29 +639,29 @@ void testLogrbParameterizedLevelMethodsWithVarArgsOfParams2() { logger.logrb(Level.FINEST, (ResourceBundle) null, "tsenif: {0},{1}", "g", "h"); // then - InOrder inOrder = Mockito.inOrder(slf4jLogger); - inOrder.verify(slf4jLogger).isErrorEnabled(); - inOrder.verify(slf4jLogger).error("ereves: a,b"); - inOrder.verify(slf4jLogger).isWarnEnabled(); - inOrder.verify(slf4jLogger).warn("gninraw: b,c"); - inOrder.verify(slf4jLogger).isInfoEnabled(); - inOrder.verify(slf4jLogger).info("ofni: c,d"); - inOrder.verify(slf4jLogger).isInfoEnabled(); - inOrder.verify(slf4jLogger).info("gifnoc: d,e"); - inOrder.verify(slf4jLogger).isDebugEnabled(); - inOrder.verify(slf4jLogger).debug("enif: e,f"); - inOrder.verify(slf4jLogger).isTraceEnabled(); - inOrder.verify(slf4jLogger).trace("renif: f,g"); - inOrder.verify(slf4jLogger).isTraceEnabled(); - inOrder.verify(slf4jLogger).trace("tsenif: g,h"); - verifyNoMoreInteractions(slf4jLogger); + InOrder inOrder = Mockito.inOrder(internalLogger); + inOrder.verify(internalLogger).isLoggable(InternalLogger.Level.ERROR); + inOrder.verify(internalLogger).log(InternalLogger.Level.ERROR, "ereves: a,b", null); + inOrder.verify(internalLogger).isLoggable(InternalLogger.Level.WARN); + inOrder.verify(internalLogger).log(InternalLogger.Level.WARN, "gninraw: b,c", null); + inOrder.verify(internalLogger).isLoggable(InternalLogger.Level.INFO); + inOrder.verify(internalLogger).log(InternalLogger.Level.INFO, "ofni: c,d", null); + inOrder.verify(internalLogger).isLoggable(InternalLogger.Level.INFO); + inOrder.verify(internalLogger).log(InternalLogger.Level.INFO, "gifnoc: d,e", null); + inOrder.verify(internalLogger).isLoggable(InternalLogger.Level.DEBUG); + inOrder.verify(internalLogger).log(InternalLogger.Level.DEBUG, "enif: e,f", null); + inOrder.verify(internalLogger).isLoggable(InternalLogger.Level.TRACE); + inOrder.verify(internalLogger).log(InternalLogger.Level.TRACE, "renif: f,g", null); + inOrder.verify(internalLogger).isLoggable(InternalLogger.Level.TRACE); + inOrder.verify(internalLogger).log(InternalLogger.Level.TRACE, "tsenif: g,h", null); + verifyNoMoreInteractions(internalLogger); } @Test void testLogrbParameterizedLevelMethodsWithThrowable() { // given - org.slf4j.Logger slf4jLogger = mock(org.slf4j.Logger.class); - PatchLogger logger = new PatchLogger(slf4jLogger); + InternalLogger internalLogger = mock(InternalLogger.class); + PatchLogger logger = new PatchLogger(internalLogger); Throwable a = new Throwable(); Throwable b = new Throwable(); Throwable c = new Throwable(); @@ -729,22 +680,22 @@ void testLogrbParameterizedLevelMethodsWithThrowable() { logger.logrb(Level.FINEST, null, null, (String) null, "tsenif", g); // then - InOrder inOrder = Mockito.inOrder(slf4jLogger); - inOrder.verify(slf4jLogger).error("ereves", a); - inOrder.verify(slf4jLogger).warn("gninraw", b); - inOrder.verify(slf4jLogger).info("ofni", c); - inOrder.verify(slf4jLogger).info("gifnoc", d); - inOrder.verify(slf4jLogger).debug("enif", e); - inOrder.verify(slf4jLogger).trace("renif", f); - inOrder.verify(slf4jLogger).trace("tsenif", g); - verifyNoMoreInteractions(slf4jLogger); + InOrder inOrder = Mockito.inOrder(internalLogger); + inOrder.verify(internalLogger).log(InternalLogger.Level.ERROR, "ereves", a); + inOrder.verify(internalLogger).log(InternalLogger.Level.WARN, "gninraw", b); + inOrder.verify(internalLogger).log(InternalLogger.Level.INFO, "ofni", c); + inOrder.verify(internalLogger).log(InternalLogger.Level.INFO, "gifnoc", d); + inOrder.verify(internalLogger).log(InternalLogger.Level.DEBUG, "enif", e); + inOrder.verify(internalLogger).log(InternalLogger.Level.TRACE, "renif", f); + inOrder.verify(internalLogger).log(InternalLogger.Level.TRACE, "tsenif", g); + verifyNoMoreInteractions(internalLogger); } @Test void testLogrbParameterizedLevelMethodsWithThrowable2() { // given - org.slf4j.Logger slf4jLogger = mock(org.slf4j.Logger.class); - PatchLogger logger = new PatchLogger(slf4jLogger); + InternalLogger internalLogger = mock(InternalLogger.class); + PatchLogger logger = new PatchLogger(internalLogger); Throwable a = new Throwable(); Throwable b = new Throwable(); Throwable c = new Throwable(); @@ -763,22 +714,22 @@ void testLogrbParameterizedLevelMethodsWithThrowable2() { logger.logrb(Level.FINEST, null, null, (ResourceBundle) null, "tsenif", g); // then - InOrder inOrder = Mockito.inOrder(slf4jLogger); - inOrder.verify(slf4jLogger).error("ereves", a); - inOrder.verify(slf4jLogger).warn("gninraw", b); - inOrder.verify(slf4jLogger).info("ofni", c); - inOrder.verify(slf4jLogger).info("gifnoc", d); - inOrder.verify(slf4jLogger).debug("enif", e); - inOrder.verify(slf4jLogger).trace("renif", f); - inOrder.verify(slf4jLogger).trace("tsenif", g); - verifyNoMoreInteractions(slf4jLogger); + InOrder inOrder = Mockito.inOrder(internalLogger); + inOrder.verify(internalLogger).log(InternalLogger.Level.ERROR, "ereves", a); + inOrder.verify(internalLogger).log(InternalLogger.Level.WARN, "gninraw", b); + inOrder.verify(internalLogger).log(InternalLogger.Level.INFO, "ofni", c); + inOrder.verify(internalLogger).log(InternalLogger.Level.INFO, "gifnoc", d); + inOrder.verify(internalLogger).log(InternalLogger.Level.DEBUG, "enif", e); + inOrder.verify(internalLogger).log(InternalLogger.Level.TRACE, "renif", f); + inOrder.verify(internalLogger).log(InternalLogger.Level.TRACE, "tsenif", g); + verifyNoMoreInteractions(internalLogger); } @Test void testLogrbParameterizedLevelMethodsWithResourceBundleObjectAndThrowable() { // given - org.slf4j.Logger slf4jLogger = mock(org.slf4j.Logger.class); - PatchLogger logger = new PatchLogger(slf4jLogger); + InternalLogger internalLogger = mock(InternalLogger.class); + PatchLogger logger = new PatchLogger(internalLogger); Throwable a = new Throwable(); Throwable b = new Throwable(); Throwable c = new Throwable(); @@ -797,22 +748,22 @@ void testLogrbParameterizedLevelMethodsWithResourceBundleObjectAndThrowable() { logger.logrb(Level.FINEST, null, null, (ResourceBundle) null, "tsenif", g); // then - InOrder inOrder = Mockito.inOrder(slf4jLogger); - inOrder.verify(slf4jLogger).error("ereves", a); - inOrder.verify(slf4jLogger).warn("gninraw", b); - inOrder.verify(slf4jLogger).info("ofni", c); - inOrder.verify(slf4jLogger).info("gifnoc", d); - inOrder.verify(slf4jLogger).debug("enif", e); - inOrder.verify(slf4jLogger).trace("renif", f); - inOrder.verify(slf4jLogger).trace("tsenif", g); - verifyNoMoreInteractions(slf4jLogger); + InOrder inOrder = Mockito.inOrder(internalLogger); + inOrder.verify(internalLogger).log(InternalLogger.Level.ERROR, "ereves", a); + inOrder.verify(internalLogger).log(InternalLogger.Level.WARN, "gninraw", b); + inOrder.verify(internalLogger).log(InternalLogger.Level.INFO, "ofni", c); + inOrder.verify(internalLogger).log(InternalLogger.Level.INFO, "gifnoc", d); + inOrder.verify(internalLogger).log(InternalLogger.Level.DEBUG, "enif", e); + inOrder.verify(internalLogger).log(InternalLogger.Level.TRACE, "renif", f); + inOrder.verify(internalLogger).log(InternalLogger.Level.TRACE, "tsenif", g); + verifyNoMoreInteractions(internalLogger); } @Test void testEnteringExitingThrowingMethods() { // given - org.slf4j.Logger slf4jLogger = mock(org.slf4j.Logger.class); - PatchLogger logger = new PatchLogger(slf4jLogger); + InternalLogger internalLogger = mock(InternalLogger.class); + PatchLogger logger = new PatchLogger(internalLogger); // when logger.entering(null, null); @@ -823,21 +774,21 @@ void testEnteringExitingThrowingMethods() { logger.throwing(null, null, null); // then - verifyNoMoreInteractions(slf4jLogger); + verifyNoMoreInteractions(internalLogger); } @Test void testResourceBundle() { // given - org.slf4j.Logger slf4jLogger = mock(org.slf4j.Logger.class); + InternalLogger internalLogger = mock(InternalLogger.class); // when - PatchLogger logger = new PatchLogger(slf4jLogger); + PatchLogger logger = new PatchLogger(internalLogger); // then assertThat(logger.getResourceBundle()).isNull(); assertThat(logger.getResourceBundleName()).isNull(); - verifyNoMoreInteractions(slf4jLogger); + verifyNoMoreInteractions(internalLogger); } static class MethodSignature { diff --git a/javaagent-extension-api/src/main/java/io/opentelemetry/javaagent/bootstrap/internal/CommonConfig.java b/javaagent-extension-api/src/main/java/io/opentelemetry/javaagent/bootstrap/internal/CommonConfig.java new file mode 100644 index 000000000000..7072f64809c9 --- /dev/null +++ b/javaagent-extension-api/src/main/java/io/opentelemetry/javaagent/bootstrap/internal/CommonConfig.java @@ -0,0 +1,71 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.bootstrap.internal; + +import static java.util.Collections.emptyList; +import static java.util.Collections.emptyMap; + +import java.util.List; +import java.util.Map; + +/** + * This class is internal and is hence not for public use. Its APIs are unstable and can change at + * any time. + */ +public final class CommonConfig { + + private static final CommonConfig instance = new CommonConfig(InstrumentationConfig.get()); + + public static CommonConfig get() { + return instance; + } + + private final Map peerServiceMapping; + private final List clientRequestHeaders; + private final List clientResponseHeaders; + private final List serverRequestHeaders; + private final List serverResponseHeaders; + private final boolean statementSanitizationEnabled; + + CommonConfig(InstrumentationConfig config) { + peerServiceMapping = + config.getMap("otel.instrumentation.common.peer-service-mapping", emptyMap()); + clientRequestHeaders = + config.getList("otel.instrumentation.http.capture-headers.client.request", emptyList()); + clientResponseHeaders = + config.getList("otel.instrumentation.http.capture-headers.client.response", emptyList()); + serverRequestHeaders = + config.getList("otel.instrumentation.http.capture-headers.server.request", emptyList()); + serverResponseHeaders = + config.getList("otel.instrumentation.http.capture-headers.server.response", emptyList()); + statementSanitizationEnabled = + config.getBoolean("otel.instrumentation.common.db-statement-sanitizer.enabled", true); + } + + public Map getPeerServiceMapping() { + return peerServiceMapping; + } + + public List getClientRequestHeaders() { + return clientRequestHeaders; + } + + public List getClientResponseHeaders() { + return clientResponseHeaders; + } + + public List getServerRequestHeaders() { + return serverRequestHeaders; + } + + public List getServerResponseHeaders() { + return serverResponseHeaders; + } + + public boolean isStatementSanitizationEnabled() { + return statementSanitizationEnabled; + } +} diff --git a/javaagent-extension-api/src/main/java/io/opentelemetry/javaagent/bootstrap/internal/DeprecatedConfigPropertyWarning.java b/javaagent-extension-api/src/main/java/io/opentelemetry/javaagent/bootstrap/internal/DeprecatedConfigProperties.java similarity index 55% rename from javaagent-extension-api/src/main/java/io/opentelemetry/javaagent/bootstrap/internal/DeprecatedConfigPropertyWarning.java rename to javaagent-extension-api/src/main/java/io/opentelemetry/javaagent/bootstrap/internal/DeprecatedConfigProperties.java index 9cb91e46c6ea..3f9e2a7f8cf6 100644 --- a/javaagent-extension-api/src/main/java/io/opentelemetry/javaagent/bootstrap/internal/DeprecatedConfigPropertyWarning.java +++ b/javaagent-extension-api/src/main/java/io/opentelemetry/javaagent/bootstrap/internal/DeprecatedConfigProperties.java @@ -13,20 +13,24 @@ * This class is internal and is hence not for public use. Its APIs are unstable and can change at * any time. */ -public final class DeprecatedConfigPropertyWarning { +public final class DeprecatedConfigProperties { - private static final Logger logger = - Logger.getLogger(DeprecatedConfigPropertyWarning.class.getName()); + private static final Logger logger = Logger.getLogger(DeprecatedConfigProperties.class.getName()); - public static void warnIfUsed( - InstrumentationConfig config, String deprecatedPropertyName, String newPropertyName) { + public static boolean getBoolean( + InstrumentationConfig config, + String deprecatedPropertyName, + String newPropertyName, + boolean defaultValue) { if (config.getString(deprecatedPropertyName) != null) { logger.log( WARNING, "Deprecated property \"{0}\" was used; use the \"{1}\" property instead", new Object[] {deprecatedPropertyName, newPropertyName}); } + boolean value = config.getBoolean(deprecatedPropertyName, defaultValue); + return config.getBoolean(newPropertyName, value); } - private DeprecatedConfigPropertyWarning() {} + private DeprecatedConfigProperties() {} } diff --git a/javaagent-extension-api/src/main/java/io/opentelemetry/javaagent/bootstrap/internal/ExperimentalConfig.java b/javaagent-extension-api/src/main/java/io/opentelemetry/javaagent/bootstrap/internal/ExperimentalConfig.java index 48a887876f76..c0f19055429a 100644 --- a/javaagent-extension-api/src/main/java/io/opentelemetry/javaagent/bootstrap/internal/ExperimentalConfig.java +++ b/javaagent-extension-api/src/main/java/io/opentelemetry/javaagent/bootstrap/internal/ExperimentalConfig.java @@ -5,6 +5,10 @@ package io.opentelemetry.javaagent.bootstrap.internal; +import static java.util.Collections.emptyList; + +import java.util.List; + /** * This class is internal and is hence not for public use. Its APIs are unstable and can change at * any time. @@ -15,6 +19,7 @@ public final class ExperimentalConfig { new ExperimentalConfig(InstrumentationConfig.get()); private final InstrumentationConfig config; + private final List messagingHeaders; /** Returns the global agent configuration. */ public static ExperimentalConfig get() { @@ -23,45 +28,26 @@ public static ExperimentalConfig get() { public ExperimentalConfig(InstrumentationConfig config) { this.config = config; + messagingHeaders = + config.getList("otel.instrumentation.messaging.experimental.capture-headers", emptyList()); } public boolean controllerTelemetryEnabled() { - // TODO: remove that `suppress...` flag after 1.13 release - DeprecatedConfigPropertyWarning.warnIfUsed( - config, - "otel.instrumentation.common.experimental.suppress-controller-spans", - "otel.instrumentation.common.experimental.controller-telemetry.enabled"); - boolean suppressControllerSpans = - config.getBoolean( - "otel.instrumentation.common.experimental.suppress-controller-spans", false); return config.getBoolean( - "otel.instrumentation.common.experimental.controller-telemetry.enabled", - !suppressControllerSpans); + "otel.instrumentation.common.experimental.controller-telemetry.enabled", true); } public boolean viewTelemetryEnabled() { - // TODO: remove that `suppress...` flag after 1.13 release - DeprecatedConfigPropertyWarning.warnIfUsed( - config, - "otel.instrumentation.common.experimental.suppress-view-spans", - "otel.instrumentation.common.experimental.view-telemetry.enabled"); - boolean suppressViewSpans = - config.getBoolean("otel.instrumentation.common.experimental.suppress-view-spans", false); return config.getBoolean( - "otel.instrumentation.common.experimental.view-telemetry.enabled", !suppressViewSpans); + "otel.instrumentation.common.experimental.view-telemetry.enabled", true); } public boolean messagingReceiveInstrumentationEnabled() { - // TODO: remove that `suppress...` flag after 1.13 release - DeprecatedConfigPropertyWarning.warnIfUsed( - config, - "otel.instrumentation.common.experimental.suppress-messaging-receive-spans", - "otel.instrumentation.messaging.experimental.receive-telemetry.enabled"); - boolean receiveSpansSuppressed = - config.getBoolean( - "otel.instrumentation.common.experimental.suppress-messaging-receive-spans", true); return config.getBoolean( - "otel.instrumentation.messaging.experimental.receive-telemetry.enabled", - !receiveSpansSuppressed); + "otel.instrumentation.messaging.experimental.receive-telemetry.enabled", false); + } + + public List getMessagingHeaders() { + return messagingHeaders; } } diff --git a/javaagent-extension-api/src/main/java/io/opentelemetry/javaagent/bootstrap/internal/InstrumentationConfig.java b/javaagent-extension-api/src/main/java/io/opentelemetry/javaagent/bootstrap/internal/InstrumentationConfig.java index 3646e8ec1f94..f0601f423662 100644 --- a/javaagent-extension-api/src/main/java/io/opentelemetry/javaagent/bootstrap/internal/InstrumentationConfig.java +++ b/javaagent-extension-api/src/main/java/io/opentelemetry/javaagent/bootstrap/internal/InstrumentationConfig.java @@ -7,7 +7,6 @@ import static java.util.Objects.requireNonNull; -import io.opentelemetry.instrumentation.api.config.Config; import java.time.Duration; import java.util.List; import java.util.Map; @@ -21,9 +20,9 @@ * *

In case any {@code get*()} method variant gets called for the same property more than once * (e.g. each time an advice class executes) it is suggested to cache the result instead of - * repeatedly calling {@link Config}. Instrumentation configuration does not change during the - * runtime so retrieving the property once and storing its result in a static final field allows JIT - * to do its magic and remove some code branches. + * repeatedly calling {@link InstrumentationConfig}. Instrumentation configuration does not change + * during the runtime so retrieving the property once and storing its result in a static final field + * allows JIT to do its magic and remove some code branches. * *

This class is internal and is hence not for public use. Its APIs are unstable and can change * at any time. diff --git a/javaagent-extension-api/src/main/java/io/opentelemetry/javaagent/extension/AgentListener.java b/javaagent-extension-api/src/main/java/io/opentelemetry/javaagent/extension/AgentListener.java index 389774f93c3e..4cbcbfb8970b 100644 --- a/javaagent-extension-api/src/main/java/io/opentelemetry/javaagent/extension/AgentListener.java +++ b/javaagent-extension-api/src/main/java/io/opentelemetry/javaagent/extension/AgentListener.java @@ -5,8 +5,8 @@ package io.opentelemetry.javaagent.extension; -import io.opentelemetry.instrumentation.api.config.Config; import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk; +import io.opentelemetry.sdk.autoconfigure.spi.Ordered; import java.lang.instrument.Instrumentation; import net.bytebuddy.agent.builder.AgentBuilder; @@ -22,25 +22,7 @@ public interface AgentListener extends Ordered { /** * Runs after instrumentations are added to {@link AgentBuilder} and after the agent is installed - * on an {@link Instrumentation}. Not called if noop api enabled via {@code - * otel.javaagent.experimental.use-noop-api}. - * - * @deprecated Implement {{@link #afterAgent(AutoConfiguredOpenTelemetrySdk)}} instead. + * on an {@link Instrumentation}. */ - @Deprecated - default void afterAgent( - Config config, AutoConfiguredOpenTelemetrySdk autoConfiguredOpenTelemetrySdk) { - throw new UnsupportedOperationException( - "This method is deprecated and will be removed in a future release;" - + " implement AgentListener#afterAgent(AutoConfiguredOpenTelemetrySdk) instead"); - } - - /** - * Runs after instrumentations are added to {@link AgentBuilder} and after the agent is installed - * on an {@link Instrumentation}. Not called if noop api enabled via {@code - * otel.javaagent.experimental.use-noop-api}. - */ - default void afterAgent(AutoConfiguredOpenTelemetrySdk autoConfiguredOpenTelemetrySdk) { - afterAgent(Config.get(), autoConfiguredOpenTelemetrySdk); - } + void afterAgent(AutoConfiguredOpenTelemetrySdk autoConfiguredOpenTelemetrySdk); } diff --git a/javaagent-extension-api/src/main/java/io/opentelemetry/javaagent/extension/Ordered.java b/javaagent-extension-api/src/main/java/io/opentelemetry/javaagent/extension/Ordered.java deleted file mode 100644 index 76b7f0dd18e7..000000000000 --- a/javaagent-extension-api/src/main/java/io/opentelemetry/javaagent/extension/Ordered.java +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.javaagent.extension; - -public interface Ordered { - /** - * Returns the order of applying the SPI implementing this interface. Higher values are added - * later, for example: an SPI with order=1 will run after an SPI with order=0. - */ - default int order() { - return 0; - } -} diff --git a/javaagent-extension-api/src/main/java/io/opentelemetry/javaagent/extension/config/ConfigCustomizer.java b/javaagent-extension-api/src/main/java/io/opentelemetry/javaagent/extension/config/ConfigCustomizer.java deleted file mode 100644 index da31904204e7..000000000000 --- a/javaagent-extension-api/src/main/java/io/opentelemetry/javaagent/extension/config/ConfigCustomizer.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.javaagent.extension.config; - -import io.opentelemetry.instrumentation.api.config.Config; -import io.opentelemetry.javaagent.extension.Ordered; -import java.util.Collections; -import java.util.Map; - -/** - * A service provider that allows to override default OTel javaagent configuration, and customize - * the config just before it is set as the global. - * - *

This is a service provider interface that requires implementations to be registered in a - * provider-configuration file stored in the {@code META-INF/services} resource directory. - * - * @deprecated Use the {@link ConfigPropertySource} SPI instead. - */ -@Deprecated -public interface ConfigCustomizer extends Ordered { - - /** - * Returns properties with their default values. Properties returned by implementations of this - * interface will be used after the following methods fail to find a non-empty property value: - * system properties, environment variables, properties configuration file. - * - *

Key of the map is the propertyName (same as system property name, e.g. {@code - * otel.traces.exporter}), value is the property value. - */ - default Map defaultProperties() { - return Collections.emptyMap(); - } - - /** Allows to change the javaagent configuration just before it is first used. */ - default Config customize(Config config) { - return config; - } -} diff --git a/javaagent-extension-api/src/main/java/io/opentelemetry/javaagent/extension/config/ConfigPropertySource.java b/javaagent-extension-api/src/main/java/io/opentelemetry/javaagent/extension/config/ConfigPropertySource.java deleted file mode 100644 index c944f49234c4..000000000000 --- a/javaagent-extension-api/src/main/java/io/opentelemetry/javaagent/extension/config/ConfigPropertySource.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.javaagent.extension.config; - -import io.opentelemetry.javaagent.extension.Ordered; -import java.util.Map; - -/** - * A service provider that allows to override default OTel agent configuration. Properties returned - * by implementations of this interface will be used after the following methods fail to find a - * non-empty property value: system properties, environment variables, properties configuration - * file. - * - *

This is a service provider interface that requires implementations to be registered in a - * provider-configuration file stored in the {@code META-INF/services} resource directory. - */ -public interface ConfigPropertySource extends Ordered { - - /** - * Returns all properties whose default values are overridden by this property source. Key of the - * map is the propertyName (same as system property name, e.g. {@code otel.traces.exporter}), - * value is the property value. - */ - Map getProperties(); -} diff --git a/javaagent-extension-api/src/main/java/io/opentelemetry/javaagent/extension/ignore/IgnoredTypesConfigurer.java b/javaagent-extension-api/src/main/java/io/opentelemetry/javaagent/extension/ignore/IgnoredTypesConfigurer.java index e047a2a8999e..1f2b20327335 100644 --- a/javaagent-extension-api/src/main/java/io/opentelemetry/javaagent/extension/ignore/IgnoredTypesConfigurer.java +++ b/javaagent-extension-api/src/main/java/io/opentelemetry/javaagent/extension/ignore/IgnoredTypesConfigurer.java @@ -5,9 +5,8 @@ package io.opentelemetry.javaagent.extension.ignore; -import io.opentelemetry.instrumentation.api.config.Config; -import io.opentelemetry.javaagent.extension.Ordered; import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; +import io.opentelemetry.sdk.autoconfigure.spi.Ordered; /** * An {@link IgnoredTypesConfigurer} can be used to augment built-in instrumentation restrictions: @@ -22,21 +21,6 @@ public interface IgnoredTypesConfigurer extends Ordered { /** * Configure the passed {@code builder} and define which classes should be ignored when * instrumenting. - * - * @deprecated Use {@link #configure(ConfigProperties, IgnoredTypesBuilder)} instead. */ - @Deprecated - default void configure(Config config, IgnoredTypesBuilder builder) { - throw new UnsupportedOperationException( - "This method is deprecated and will be removed in a future release;" - + " implement IgnoredTypesConfigurer#configure(ConfigProperties, IgnoredTypesBuilder) instead"); - } - - /** - * Configure the passed {@code builder} and define which classes should be ignored when - * instrumenting. - */ - default void configure(ConfigProperties config, IgnoredTypesBuilder builder) { - configure(Config.get(), builder); - } + void configure(IgnoredTypesBuilder builder, ConfigProperties config); } diff --git a/javaagent-extension-api/src/main/java/io/opentelemetry/javaagent/extension/instrumentation/InstrumentationModule.java b/javaagent-extension-api/src/main/java/io/opentelemetry/javaagent/extension/instrumentation/InstrumentationModule.java index 65dce41eeab3..f57d1e67ee51 100644 --- a/javaagent-extension-api/src/main/java/io/opentelemetry/javaagent/extension/instrumentation/InstrumentationModule.java +++ b/javaagent-extension-api/src/main/java/io/opentelemetry/javaagent/extension/instrumentation/InstrumentationModule.java @@ -9,8 +9,8 @@ import static java.util.Collections.unmodifiableSet; import static net.bytebuddy.matcher.ElementMatchers.any; -import io.opentelemetry.instrumentation.api.config.Config; -import io.opentelemetry.javaagent.extension.Ordered; +import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; +import io.opentelemetry.sdk.autoconfigure.spi.Ordered; import java.util.Collections; import java.util.LinkedHashSet; import java.util.List; @@ -30,8 +30,6 @@ * java.util.ServiceLoader} for more details. */ public abstract class InstrumentationModule implements Ordered { - private static final boolean DEFAULT_ENABLED = - Config.get().getBoolean("otel.instrumentation.common.default-enabled", true); private final Set instrumentationNames; @@ -83,8 +81,8 @@ public final String instrumentationName() { * Allows instrumentation modules to disable themselves by default, or to additionally disable * themselves on some other condition. */ - public boolean defaultEnabled() { - return DEFAULT_ENABLED; + public boolean defaultEnabled(ConfigProperties config) { + return config.getBoolean("otel.instrumentation.common.default-enabled", true); } /** diff --git a/javaagent-internal-logging-simple/build.gradle.kts b/javaagent-internal-logging-simple/build.gradle.kts new file mode 100644 index 000000000000..125653c86421 --- /dev/null +++ b/javaagent-internal-logging-simple/build.gradle.kts @@ -0,0 +1,36 @@ +import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar + +plugins { + id("otel.java-conventions") + id("otel.publish-conventions") + + id("com.github.johnrengelman.shadow") +} + +group = "io.opentelemetry.javaagent" + +dependencies { + compileOnly(project(":javaagent-bootstrap")) + compileOnly(project(":javaagent-tooling")) + + implementation("org.slf4j:slf4j-api") + implementation("org.slf4j:slf4j-simple") + + annotationProcessor("com.google.auto.service:auto-service") + compileOnly("com.google.auto.service:auto-service-annotations") + testCompileOnly("com.google.auto.service:auto-service-annotations") +} + +tasks { + val shadowJar by existing(ShadowJar::class) { + // required for META-INF/services files relocation + mergeServiceFiles() + + // Prevents configuration naming conflict with other SLF4J instances + relocate("org.slf4j", "io.opentelemetry.javaagent.slf4j") + } + + assemble { + dependsOn(shadowJar) + } +} diff --git a/javaagent-internal-logging-simple/src/main/java/io/opentelemetry/javaagent/logging/simple/Slf4jSimpleLogger.java b/javaagent-internal-logging-simple/src/main/java/io/opentelemetry/javaagent/logging/simple/Slf4jSimpleLogger.java new file mode 100644 index 000000000000..d8bdfc771027 --- /dev/null +++ b/javaagent-internal-logging-simple/src/main/java/io/opentelemetry/javaagent/logging/simple/Slf4jSimpleLogger.java @@ -0,0 +1,55 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.logging.simple; + +import io.opentelemetry.javaagent.bootstrap.InternalLogger; +import javax.annotation.Nullable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +final class Slf4jSimpleLogger extends InternalLogger { + + static Slf4jSimpleLogger create(String name) { + return new Slf4jSimpleLogger(name); + } + + private final Logger logger; + + Slf4jSimpleLogger(String name) { + logger = LoggerFactory.getLogger(name); + } + + @Override + protected boolean isLoggable(Level level) { + return logger.isEnabledForLevel(toSlf4jLevel(level)); + } + + @Override + protected void log(Level level, String message, @Nullable Throwable error) { + logger.makeLoggingEventBuilder(toSlf4jLevel(level)).setCause(error).log(message); + } + + @Override + protected String name() { + return logger.getName(); + } + + private static org.slf4j.event.Level toSlf4jLevel(Level level) { + switch (level) { + case ERROR: + return org.slf4j.event.Level.ERROR; + case WARN: + return org.slf4j.event.Level.WARN; + case INFO: + return org.slf4j.event.Level.INFO; + case DEBUG: + return org.slf4j.event.Level.DEBUG; + case TRACE: + return org.slf4j.event.Level.TRACE; + } + throw new IllegalStateException("Missing logging level value in switch"); + } +} diff --git a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/DefaultLoggingCustomizer.java b/javaagent-internal-logging-simple/src/main/java/io/opentelemetry/javaagent/logging/simple/Slf4jSimpleLoggingCustomizer.java similarity index 69% rename from javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/DefaultLoggingCustomizer.java rename to javaagent-internal-logging-simple/src/main/java/io/opentelemetry/javaagent/logging/simple/Slf4jSimpleLoggingCustomizer.java index 76dbad80eab7..c0116d429a1f 100644 --- a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/DefaultLoggingCustomizer.java +++ b/javaagent-internal-logging-simple/src/main/java/io/opentelemetry/javaagent/logging/simple/Slf4jSimpleLoggingCustomizer.java @@ -3,22 +3,27 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.javaagent.tooling; +package io.opentelemetry.javaagent.logging.simple; +import com.google.auto.service.AutoService; +import io.opentelemetry.javaagent.bootstrap.InternalLogger; +import io.opentelemetry.javaagent.tooling.LoggingCustomizer; import java.util.Locale; +import org.slf4j.LoggerFactory; -final class DefaultLoggingCustomizer implements LoggingCustomizer { +@AutoService(LoggingCustomizer.class) +public final class Slf4jSimpleLoggingCustomizer implements LoggingCustomizer { + // org.slf4j package name in the constants will be shaded too private static final String SIMPLE_LOGGER_SHOW_DATE_TIME_PROPERTY = - "io.opentelemetry.javaagent.slf4j.simpleLogger.showDateTime"; + "org.slf4j.simpleLogger.showDateTime"; private static final String SIMPLE_LOGGER_DATE_TIME_FORMAT_PROPERTY = - "io.opentelemetry.javaagent.slf4j.simpleLogger.dateTimeFormat"; + "org.slf4j.simpleLogger.dateTimeFormat"; private static final String SIMPLE_LOGGER_DATE_TIME_FORMAT_DEFAULT = "'[otel.javaagent 'yyyy-MM-dd HH:mm:ss:SSS Z']'"; private static final String SIMPLE_LOGGER_DEFAULT_LOG_LEVEL_PROPERTY = - "io.opentelemetry.javaagent.slf4j.simpleLogger.defaultLogLevel"; - private static final String SIMPLE_LOGGER_PREFIX = - "io.opentelemetry.javaagent.slf4j.simpleLogger.log."; + "org.slf4j.simpleLogger.defaultLogLevel"; + private static final String SIMPLE_LOGGER_PREFIX = "org.slf4j.simpleLogger.log."; @Override public void init() { @@ -33,6 +38,11 @@ public void init() { // by default muzzle warnings are turned off setSystemPropertyDefault(SIMPLE_LOGGER_PREFIX + "muzzleMatcher", "OFF"); } + + // trigger loading the provider from the agent CL + LoggerFactory.getILoggerFactory(); + + InternalLogger.initialize(Slf4jSimpleLogger::create); } @Override @@ -60,6 +70,11 @@ private static void setSystemPropertyDefault(String property, String value) { private static boolean isDebugMode() { String tracerDebugLevelSysprop = "otel.javaagent.debug"; String tracerDebugLevelProp = System.getProperty(tracerDebugLevelSysprop); + String heliosDebugEnv = System.getenv("HS_DEBUG"); + + if (heliosDebugEnv != null && Boolean.parseBoolean(heliosDebugEnv)) { + return true; + } if (tracerDebugLevelProp != null) { return Boolean.parseBoolean(tracerDebugLevelProp); diff --git a/javaagent-tooling/build.gradle.kts b/javaagent-tooling/build.gradle.kts index 53b2f67d80a3..43817420b740 100644 --- a/javaagent-tooling/build.gradle.kts +++ b/javaagent-tooling/build.gradle.kts @@ -14,11 +14,11 @@ dependencies { implementation(project(":javaagent-tooling:javaagent-tooling-java9")) implementation(project(":instrumentation-api")) implementation(project(":instrumentation-annotations-support")) - implementation(project(":instrumentation-appender-api-internal")) - implementation(project(":instrumentation-appender-sdk-internal")) + implementation(project(":instrumentation:resources:library")) implementation(project(":muzzle")) implementation("io.opentelemetry:opentelemetry-api") + implementation("io.opentelemetry:opentelemetry-api-logs") implementation("io.opentelemetry:opentelemetry-sdk") implementation("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure") implementation("io.opentelemetry:opentelemetry-sdk-metrics") @@ -26,9 +26,8 @@ dependencies { implementation("io.opentelemetry:opentelemetry-extension-kotlin") implementation("io.opentelemetry:opentelemetry-extension-aws") implementation("io.opentelemetry:opentelemetry-extension-trace-propagators") - implementation("io.opentelemetry:opentelemetry-sdk-extension-resources") - implementation("io.opentelemetry:opentelemetry-extension-noop-api") - implementation("io.opentelemetry:opentelemetry-sdk-extension-metric-incubator") + // the incubator's ViewConfigCustomizer is used to support loading yaml-based metric views + implementation("io.opentelemetry:opentelemetry-sdk-extension-incubator") // Exporters with dependencies implementation("io.opentelemetry:opentelemetry-exporter-jaeger") @@ -54,6 +53,7 @@ dependencies { testImplementation(project(":testing-common")) testImplementation("com.google.guava:guava") + testImplementation("org.junit-pioneer:junit-pioneer") } testing { @@ -68,6 +68,18 @@ testing { compileOnly("com.google.code.findbugs:annotations") } } + + val testMissingType by registering(JvmTestSuite::class) { + dependencies { + implementation(project(":javaagent-bootstrap")) + implementation(project(":javaagent-tooling")) + implementation("net.bytebuddy:byte-buddy-dep") + compileOnly("com.google.guava:guava") + + // Used by byte-buddy but not brought in as a transitive dependency. + compileOnly("com.google.code.findbugs:annotations") + } + } } } @@ -77,6 +89,11 @@ tasks { withType().configureEach { environment("OTEL_TRACES_EXPORTER", "none") environment("OTEL_METRICS_EXPORTER", "none") + + // required on jdk17 + jvmArgs("--add-opens=java.base/java.lang=ALL-UNNAMED") + jvmArgs("--add-opens=java.base/java.util=ALL-UNNAMED") + jvmArgs("-XX:+IgnoreUnrecognizedVMOptions") } // TODO this should live in jmh-conventions diff --git a/javaagent-tooling/src/jmh/java/io/opentelemetry/javaagent/tooling/ignore/IgnoredTypesMatcherBenchmark.java b/javaagent-tooling/src/jmh/java/io/opentelemetry/javaagent/tooling/ignore/IgnoredTypesMatcherBenchmark.java index b2ab5c1a099e..73e7d7ea5e0f 100644 --- a/javaagent-tooling/src/jmh/java/io/opentelemetry/javaagent/tooling/ignore/IgnoredTypesMatcherBenchmark.java +++ b/javaagent-tooling/src/jmh/java/io/opentelemetry/javaagent/tooling/ignore/IgnoredTypesMatcherBenchmark.java @@ -34,7 +34,7 @@ public class IgnoredTypesMatcherBenchmark { static { IgnoredTypesBuilderImpl builder = new IgnoredTypesBuilderImpl(); new AdditionalLibraryIgnoredTypesConfigurer() - .configure(EmptyConfigProperties.INSTANCE, builder); + .configure(builder, EmptyConfigProperties.INSTANCE); ignoredTypesMatcher = new IgnoredTypesMatcher(builder.buildIgnoredTypesTrie()); } diff --git a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/AgentExtension.java b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/AgentExtension.java index ede4a0ff7caa..556c1bbfdf2d 100644 --- a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/AgentExtension.java +++ b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/AgentExtension.java @@ -5,8 +5,9 @@ package io.opentelemetry.javaagent.tooling; -import io.opentelemetry.javaagent.extension.Ordered; import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule; +import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; +import io.opentelemetry.sdk.autoconfigure.spi.Ordered; import net.bytebuddy.agent.builder.AgentBuilder; /** @@ -25,7 +26,7 @@ public interface AgentExtension extends Ordered { * @return The customized agent. Note that this method MUST return a non-null {@link AgentBuilder} * instance that contains all customizations defined in this extension. */ - AgentBuilder extend(AgentBuilder agentBuilder); + AgentBuilder extend(AgentBuilder agentBuilder, ConfigProperties config); /** * Returns the name of the extension. It does not have to be unique, but it should be diff --git a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/AgentInstaller.java b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/AgentInstaller.java index 2cfc71aaee4d..ced2f9f60e26 100644 --- a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/AgentInstaller.java +++ b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/AgentInstaller.java @@ -13,12 +13,10 @@ import static java.util.logging.Level.SEVERE; import static net.bytebuddy.matcher.ElementMatchers.any; -import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.context.Context; import io.opentelemetry.context.ContextStorage; import io.opentelemetry.context.Scope; -import io.opentelemetry.extension.noopapi.NoopOpenTelemetry; -import io.opentelemetry.instrumentation.api.config.Config; +import io.opentelemetry.instrumentation.api.internal.ConfigPropertiesUtil; import io.opentelemetry.instrumentation.api.internal.EmbeddedInstrumentationProperties; import io.opentelemetry.javaagent.bootstrap.AgentClassLoader; import io.opentelemetry.javaagent.bootstrap.AgentInitializer; @@ -32,6 +30,7 @@ import io.opentelemetry.javaagent.tooling.asyncannotationsupport.WeakRefAsyncOperationEndStrategies; import io.opentelemetry.javaagent.tooling.bootstrap.BootstrapPackagesBuilderImpl; import io.opentelemetry.javaagent.tooling.bootstrap.BootstrapPackagesConfigurer; +import io.opentelemetry.javaagent.tooling.bytebuddy.SafeTypeStrategy; import io.opentelemetry.javaagent.tooling.config.AgentConfig; import io.opentelemetry.javaagent.tooling.config.ConfigPropertiesBridge; import io.opentelemetry.javaagent.tooling.ignore.IgnoredClassLoadersMatcher; @@ -52,11 +51,13 @@ import java.util.stream.Stream; import java.util.stream.StreamSupport; import javax.annotation.Nullable; +import net.bytebuddy.ByteBuddy; import net.bytebuddy.agent.builder.AgentBuilder; import net.bytebuddy.agent.builder.ResettableClassFileTransformer; import net.bytebuddy.description.type.TypeDefinition; import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.dynamic.DynamicType; +import net.bytebuddy.dynamic.scaffold.MethodGraph; import net.bytebuddy.utility.JavaModule; public class AgentInstaller { @@ -64,7 +65,6 @@ public class AgentInstaller { private static final Logger logger = Logger.getLogger(AgentInstaller.class.getName()); static final String JAVAAGENT_ENABLED_CONFIG = "otel.javaagent.enabled"; - static final String JAVAAGENT_NOOP_CONFIG = "otel.javaagent.experimental.use-noop-api"; // This property may be set to force synchronous AgentListener#afterAgent() execution: the // condition for delaying the AgentListener initialization is pretty broad and in case it covers @@ -78,7 +78,7 @@ public class AgentInstaller { private static final Map> CLASS_LOAD_CALLBACKS = new HashMap<>(); - public static void installBytebuddyAgent(Instrumentation inst, Config config) { + public static void installBytebuddyAgent(Instrumentation inst) { addByteBuddyRawSetting(); Integer strictContextStressorMillis = Integer.getInteger(STRICT_CONTEXT_STRESSOR_MILLIS); @@ -88,50 +88,48 @@ public static void installBytebuddyAgent(Instrumentation inst, Config config) { } logVersionInfo(); - if (config.getBoolean(JAVAAGENT_ENABLED_CONFIG, true)) { + if (ConfigPropertiesUtil.getBoolean(JAVAAGENT_ENABLED_CONFIG, true)) { setupUnsafe(inst); List agentListeners = loadOrdered(AgentListener.class); - installBytebuddyAgent(inst, config, agentListeners); + installBytebuddyAgent(inst, agentListeners); } else { logger.fine("Tracing is disabled, not installing instrumentations."); } } private static void installBytebuddyAgent( - Instrumentation inst, Config config, Iterable agentListeners) { + Instrumentation inst, Iterable agentListeners) { WeakRefAsyncOperationEndStrategies.initialize(); EmbeddedInstrumentationProperties.setPropertiesLoader( AgentInitializer.getExtensionsClassLoader()); - setBootstrapPackages(config); setDefineClassHandler(); // If noop OpenTelemetry is enabled, autoConfiguredSdk will be null and AgentListeners are not // called - AutoConfiguredOpenTelemetrySdk autoConfiguredSdk = null; - if (config.getBoolean(JAVAAGENT_NOOP_CONFIG, false)) { - logger.info("Tracing and metrics are disabled because noop is enabled."); - GlobalOpenTelemetry.set(NoopOpenTelemetry.getInstance()); - } else { - autoConfiguredSdk = installOpenTelemetrySdk(config); - } + AutoConfiguredOpenTelemetrySdk autoConfiguredSdk = installOpenTelemetrySdk(); - ConfigProperties sdkConfig = EmptyConfigProperties.INSTANCE; - if (autoConfiguredSdk != null) { - sdkConfig = autoConfiguredSdk.getConfig(); - InstrumentationConfig.internalInitializeConfig(new ConfigPropertiesBridge(sdkConfig)); - copyNecessaryConfigToSystemProperties(sdkConfig); + ConfigProperties sdkConfig = autoConfiguredSdk.getConfig(); + InstrumentationConfig.internalInitializeConfig(new ConfigPropertiesBridge(sdkConfig)); + copyNecessaryConfigToSystemProperties(sdkConfig); - for (BeforeAgentListener agentListener : loadOrdered(BeforeAgentListener.class)) { - agentListener.beforeAgent(autoConfiguredSdk); - } + setBootstrapPackages(sdkConfig); + + for (BeforeAgentListener agentListener : loadOrdered(BeforeAgentListener.class)) { + agentListener.beforeAgent(autoConfiguredSdk); } AgentBuilder agentBuilder = - new AgentBuilder.Default() + new AgentBuilder.Default( + // default method graph compiler inspects the class hierarchy, we don't need it, so + // we use a simpler and faster strategy instead + new ByteBuddy().with(MethodGraph.Compiler.ForDeclaredMethods.INSTANCE)) .disableClassFormatChanges() + // disableClassFormatChanges sets type strategy to TypeStrategy.Default.REDEFINE_FROZEN + // we'll wrap it with our own strategy + .with(new SafeTypeStrategy(AgentBuilder.TypeStrategy.Default.REDEFINE_FROZEN)) .with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION) .with(new RedefinitionDiscoveryStrategy()) .with(AgentBuilder.DescriptionStrategy.Default.POOL_ONLY) @@ -144,7 +142,7 @@ private static void installBytebuddyAgent( agentBuilder = configureIgnoredTypes(sdkConfig, agentBuilder); - if (AgentConfig.get().isDebugModeEnabled()) { + if (AgentConfig.isDebugModeEnabled(sdkConfig)) { agentBuilder = agentBuilder .with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION) @@ -162,7 +160,7 @@ private static void installBytebuddyAgent( new Object[] {agentExtension.extensionName(), agentExtension.getClass().getName()}); } try { - agentBuilder = agentExtension.extend(agentBuilder); + agentBuilder = agentExtension.extend(agentBuilder, sdkConfig); numberOfLoadedExtensions++; } catch (Exception | LinkageError e) { logger.log( @@ -180,9 +178,7 @@ private static void installBytebuddyAgent( ResettableClassFileTransformer resettableClassFileTransformer = agentBuilder.installOn(inst); ClassFileTransformerHolder.setClassFileTransformer(resettableClassFileTransformer); - if (autoConfiguredSdk != null) { - runAfterAgentListeners(agentListeners, autoConfiguredSdk); - } + runAfterAgentListeners(agentListeners, autoConfiguredSdk); } private static void copyNecessaryConfigToSystemProperties(ConfigProperties config) { @@ -200,10 +196,10 @@ private static void setupUnsafe(Instrumentation inst) { } } - private static void setBootstrapPackages(Config config) { + private static void setBootstrapPackages(ConfigProperties config) { BootstrapPackagesBuilderImpl builder = new BootstrapPackagesBuilderImpl(); for (BootstrapPackagesConfigurer configurer : load(BootstrapPackagesConfigurer.class)) { - configurer.configure(config, builder); + configurer.configure(builder, config); } BootstrapPackagePrefixesHolder.setBoostrapPackagePrefixes(builder.build()); } @@ -216,7 +212,7 @@ private static AgentBuilder configureIgnoredTypes( ConfigProperties config, AgentBuilder agentBuilder) { IgnoredTypesBuilderImpl builder = new IgnoredTypesBuilderImpl(); for (IgnoredTypesConfigurer configurer : loadOrdered(IgnoredTypesConfigurer.class)) { - configurer.configure(config, builder); + configurer.configure(builder, config); } Trie ignoredTasksTrie = builder.buildIgnoredTasksTrie(); @@ -250,7 +246,7 @@ private static void runAfterAgentListeners( // the application is already setting the global LogManager and AgentListener won't be able // to touch it due to class loader locking. boolean shouldForceSynchronousAgentListenersCalls = - Config.get().getBoolean(FORCE_SYNCHRONOUS_AGENT_LISTENERS_CONFIG, false); + autoConfiguredSdk.getConfig().getBoolean(FORCE_SYNCHRONOUS_AGENT_LISTENERS_CONFIG, false); boolean javaBefore9 = isJavaBefore9(); if (!shouldForceSynchronousAgentListenersCalls && javaBefore9 && isAppUsingCustomLogManager()) { logger.fine("Custom JUL LogManager detected: delaying AgentListener#afterAgent() calls"); diff --git a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/AgentStarterImpl.java b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/AgentStarterImpl.java index 04f3185df077..d895b511431e 100644 --- a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/AgentStarterImpl.java +++ b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/AgentStarterImpl.java @@ -5,10 +5,8 @@ package io.opentelemetry.javaagent.tooling; -import io.opentelemetry.instrumentation.api.config.Config; import io.opentelemetry.javaagent.bootstrap.AgentInitializer; import io.opentelemetry.javaagent.bootstrap.AgentStarter; -import io.opentelemetry.javaagent.tooling.config.ConfigInitializer; import java.io.File; import java.lang.instrument.ClassFileTransformer; import java.lang.instrument.Instrumentation; @@ -79,14 +77,13 @@ private void internalStart() { if (loggingCustomizers.hasNext()) { loggingCustomizer = loggingCustomizers.next(); } else { - loggingCustomizer = new DefaultLoggingCustomizer(); + loggingCustomizer = NoopLoggingCustomizer.INSTANCE; } Throwable startupError = null; try { loggingCustomizer.init(); - ConfigInitializer.initialize(); - AgentInstaller.installBytebuddyAgent(instrumentation, Config.get()); + AgentInstaller.installBytebuddyAgent(instrumentation); } catch (Throwable t) { // this is logged below and not rethrown to avoid logging it twice startupError = t; 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 0b69d797b6b3..8c245a22f4fc 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 @@ -6,17 +6,20 @@ package io.opentelemetry.javaagent.tooling; import static io.opentelemetry.javaagent.tooling.AgentInstaller.JAVAAGENT_ENABLED_CONFIG; +import static io.opentelemetry.javaagent.tooling.HeliosConfiguration.getHeliosSamplingRatioProperty; +import static java.util.Collections.emptyList; import com.google.auto.service.AutoService; import io.opentelemetry.exporter.logging.LoggingSpanExporter; -import io.opentelemetry.instrumentation.api.config.Config; import io.opentelemetry.javaagent.tooling.config.AgentConfig; import io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizer; import io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizerProvider; import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; +import io.opentelemetry.sdk.metrics.SdkMeterProvider; +import io.opentelemetry.sdk.metrics.SdkMeterProviderBuilder; import io.opentelemetry.sdk.trace.SdkTracerProviderBuilder; import io.opentelemetry.sdk.trace.export.SimpleSpanProcessor; -import java.util.Collections; +import java.util.Optional; @AutoService(AutoConfigurationCustomizerProvider.class) public class AgentTracerProviderConfigurer implements AutoConfigurationCustomizerProvider { @@ -26,36 +29,49 @@ public class AgentTracerProviderConfigurer implements AutoConfigurationCustomize public void customize(AutoConfigurationCustomizer autoConfigurationCustomizer) { autoConfigurationCustomizer.addTracerProviderCustomizer( AgentTracerProviderConfigurer::configure); + autoConfigurationCustomizer.addMeterProviderCustomizer( + AgentTracerProviderConfigurer::configureMeterProvider); + autoConfigurationCustomizer.addMetricExporterCustomizer( + (metricExporter, configProperties) -> new NoopMeterExporter()); + } + + private static SdkMeterProviderBuilder configureMeterProvider( + SdkMeterProviderBuilder meterProviderBuilder, ConfigProperties configProperties) { + return SdkMeterProvider.builder(); } private static SdkTracerProviderBuilder configure( SdkTracerProviderBuilder sdkTracerProviderBuilder, ConfigProperties config) { - if (!Config.get().getBoolean(JAVAAGENT_ENABLED_CONFIG, true)) { + if (!config.getBoolean(JAVAAGENT_ENABLED_CONFIG, true)) { return sdkTracerProviderBuilder; } // Register additional thread details logging span processor - if (Config.get().getBoolean(ADD_THREAD_DETAILS, true)) { + if (config.getBoolean(ADD_THREAD_DETAILS, true)) { sdkTracerProviderBuilder.addSpanProcessor(new AddThreadDetailsSpanProcessor()); } + sdkTracerProviderBuilder.addSpanProcessor(new HeliosProcessor()); + + Optional heliosRatioProperty = getHeliosSamplingRatioProperty(); + heliosRatioProperty.ifPresent( + ratioProperty -> sdkTracerProviderBuilder.setSampler(new HeliosSampler(ratioProperty))); - maybeEnableLoggingExporter(sdkTracerProviderBuilder); + maybeEnableLoggingExporter(sdkTracerProviderBuilder, config); return sdkTracerProviderBuilder; } - private static void maybeEnableLoggingExporter(SdkTracerProviderBuilder builder) { - if (AgentConfig.get().isDebugModeEnabled()) { + private static void maybeEnableLoggingExporter( + SdkTracerProviderBuilder builder, ConfigProperties config) { + if (AgentConfig.isDebugModeEnabled(config)) { // don't install another instance if the user has already explicitly requested it. - if (loggingExporterIsNotAlreadyConfigured()) { + if (loggingExporterIsNotAlreadyConfigured(config)) { builder.addSpanProcessor(SimpleSpanProcessor.create(LoggingSpanExporter.create())); } } } - private static boolean loggingExporterIsNotAlreadyConfigured() { - return !Config.get() - .getList("otel.traces.exporter", Collections.emptyList()) - .contains("logging"); + private static boolean loggingExporterIsNotAlreadyConfigured(ConfigProperties config) { + return !config.getList("otel.traces.exporter", emptyList()).contains("logging"); } } diff --git a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/AutoResourceProvider.java b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/AutoResourceProvider.java new file mode 100644 index 000000000000..862536e8a156 --- /dev/null +++ b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/AutoResourceProvider.java @@ -0,0 +1,57 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.tooling; + +import static io.opentelemetry.javaagent.tooling.HeliosConfiguration.getEnvironmentName; +import static io.opentelemetry.javaagent.tooling.HeliosConfiguration.getHeliosSamplingRatioProperty; +import static io.opentelemetry.javaagent.tooling.HeliosConfiguration.getServiceName; +import static io.opentelemetry.semconv.resource.attributes.ResourceAttributes.TELEMETRY_SDK_NAME; +import static io.opentelemetry.semconv.resource.attributes.ResourceAttributes.TELEMETRY_SDK_VERSION; + +import com.google.auto.service.AutoService; +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.common.AttributesBuilder; +import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; +import io.opentelemetry.sdk.autoconfigure.spi.ResourceProvider; +import io.opentelemetry.sdk.resources.Resource; +import java.util.Optional; + +@AutoService(ResourceProvider.class) +public class AutoResourceProvider implements ResourceProvider { + + private static final AttributeKey TELEMETRY_AUTO_VERSION = + AttributeKey.stringKey("telemetry.auto.version"); + + private static final AttributeKey DEPLOYMENT_ENVIRONMENT = + AttributeKey.stringKey("deployment.environment"); + + private static final AttributeKey TELEMETRY_SAMPLING_RATIO = + AttributeKey.stringKey("telemetry.sdk.sampling_ratio"); + + private static final AttributeKey SERVICE_NAME = AttributeKey.stringKey("service.name"); + private static final String TELEMETRY_SDK_NAME_VALUE = "helios-opentelemetry-javaagent"; + + @Override + public Resource createResource(ConfigProperties config) { + AttributesBuilder attributesBuilder = Attributes.builder(); + attributesBuilder.put(TELEMETRY_SDK_NAME, TELEMETRY_SDK_NAME_VALUE); + attributesBuilder.put(TELEMETRY_SDK_VERSION, AgentVersion.VERSION); + attributesBuilder.put(TELEMETRY_AUTO_VERSION, AgentVersion.VERSION); + String environmentNameByHelios = getEnvironmentName(); + if (environmentNameByHelios != null) { + attributesBuilder.put(DEPLOYMENT_ENVIRONMENT, environmentNameByHelios); + } + attributesBuilder.put(SERVICE_NAME, getServiceName()); + + Optional heliosRatioProperty = getHeliosSamplingRatioProperty(); + heliosRatioProperty.ifPresent( + ratio -> attributesBuilder.put(TELEMETRY_SAMPLING_RATIO, Double.toString(ratio))); + + Attributes attributes = attributesBuilder.build(); + return AgentVersion.VERSION == null ? Resource.empty() : Resource.create(attributes); + } +} diff --git a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/AutoVersionResourceProvider.java b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/AutoVersionResourceProvider.java deleted file mode 100644 index 4010f191da29..000000000000 --- a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/AutoVersionResourceProvider.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.javaagent.tooling; - -import com.google.auto.service.AutoService; -import io.opentelemetry.api.common.AttributeKey; -import io.opentelemetry.api.common.Attributes; -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 AutoVersionResourceProvider implements ResourceProvider { - - private static final AttributeKey TELEMETRY_AUTO_VERSION = - AttributeKey.stringKey("telemetry.auto.version"); - - @Override - public Resource createResource(ConfigProperties config) { - return AgentVersion.VERSION == null - ? Resource.empty() - : Resource.create(Attributes.of(TELEMETRY_AUTO_VERSION, AgentVersion.VERSION)); - } -} diff --git a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/BeforeAgentListener.java b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/BeforeAgentListener.java index 15904c2bab8f..e7597599fee1 100644 --- a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/BeforeAgentListener.java +++ b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/BeforeAgentListener.java @@ -5,8 +5,8 @@ package io.opentelemetry.javaagent.tooling; -import io.opentelemetry.javaagent.extension.Ordered; import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk; +import io.opentelemetry.sdk.autoconfigure.spi.Ordered; import net.bytebuddy.agent.builder.AgentBuilder; /** @@ -17,9 +17,6 @@ */ public interface BeforeAgentListener extends Ordered { - /** - * Runs before {@link AgentBuilder} construction, before any instrumentation is added. Not called - * if noop api enabled via {@code otel.javaagent.experimental.use-noop-api}. - */ + /** Runs before {@link AgentBuilder} construction, before any instrumentation is added. */ void beforeAgent(AutoConfiguredOpenTelemetrySdk autoConfiguredOpenTelemetrySdk); } diff --git a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/Constants.java b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/Constants.java index 56d8160483fc..948566f5090d 100644 --- a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/Constants.java +++ b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/Constants.java @@ -20,9 +20,7 @@ public final class Constants { public static final List BOOTSTRAP_PACKAGE_PREFIXES = Collections.unmodifiableList( Arrays.asList( - "io.opentelemetry.javaagent.bootstrap", - "io.opentelemetry.javaagent.shaded", - "io.opentelemetry.javaagent.slf4j")); + "io.opentelemetry.javaagent.bootstrap", "io.opentelemetry.javaagent.shaded")); private Constants() {} } diff --git a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/ExtensionClassLoader.java b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/ExtensionClassLoader.java index 696515f667a0..f6ed198b3497 100644 --- a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/ExtensionClassLoader.java +++ b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/ExtensionClassLoader.java @@ -37,9 +37,8 @@ public class ExtensionClassLoader extends URLClassLoader { public static final String EXTENSIONS_CONFIG = "otel.javaagent.extensions"; - // NOTE it's important not to use slf4j in this class, because this class is used before slf4j is - // configured, and so using slf4j here would initialize slf4j-simple before we have a chance to - // configure the logging levels + // NOTE it's important not to use logging in this class, because this class is used before logging + // is initialized static { ClassLoader.registerAsParallelCapable(); diff --git a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/HeliosConfiguration.java b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/HeliosConfiguration.java new file mode 100644 index 000000000000..115fedd4beb4 --- /dev/null +++ b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/HeliosConfiguration.java @@ -0,0 +1,75 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.tooling; + +import static java.util.logging.Level.WARNING; + +import java.util.Optional; +import java.util.logging.Logger; + +public class HeliosConfiguration { + + private HeliosConfiguration() {} + + private static final Logger logger = Logger.getLogger(HeliosConfiguration.class.getName()); + public static final String HELIOS_TEST_TRIGGERED_TRACE = "hs-triggered-test"; + public static final String HELIOS_ENVIRONMENT_ENV_VAR = "HS_ENVIRONMENT"; + public static final String HELIOS_SERVICE_NAME_ENV_VAR = "HS_SERVICE_NAME"; + public static final String HELIOS_TOKEN_ENV_VAR = "HS_TOKEN"; + public static final String HELIOS_COLLECTOR_ENDPOINT_ENV_VAR = "HS_COLLECTOR_ENDPOINT"; + public static final String DEFAULT_COLLECTOR_ENDPOINT = "https://collector.heliosphere.io/traces"; + + public static String getEnvironmentName() { + return System.getenv(HELIOS_ENVIRONMENT_ENV_VAR); + } + + public static String getServiceName() { + String serviceName = System.getenv(HELIOS_SERVICE_NAME_ENV_VAR); + if (serviceName == null) { + logger.log(WARNING, "service name is mandatory and wasn't defined"); + } + return serviceName; + } + + public static String getHsToken() { + return System.getenv(HELIOS_TOKEN_ENV_VAR); + } + + public static String getCollectorEndpoint() { + String result = System.getenv(HELIOS_COLLECTOR_ENDPOINT_ENV_VAR); + return result == null ? DEFAULT_COLLECTOR_ENDPOINT : result; + } + + public static Optional getHeliosSamplingRatioProperty() { + try { + String ratio = System.getenv(String.valueOf(RatioProperty.HS_SAMPLING_RATIO)); + if (ratio == null) { + ratio = System.getProperty(RatioProperty.HS_SAMPLING_RATIO.propertyName()); + } + if (ratio != null) { + return Optional.of(Double.parseDouble(ratio)); + } + } catch (RuntimeException e) { + System.out.println("Exception while getting ratio property: " + e); + } + + return Optional.empty(); + } + + private enum RatioProperty { + HS_SAMPLING_RATIO("hs.sampling.ratio"); + + private final String propertyName; + + RatioProperty(String propertyName) { + this.propertyName = propertyName; + } + + private String propertyName() { + return propertyName; + } + } +} diff --git a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/HeliosProcessor.java b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/HeliosProcessor.java new file mode 100644 index 000000000000..024780c61959 --- /dev/null +++ b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/HeliosProcessor.java @@ -0,0 +1,37 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.tooling; + +import static io.opentelemetry.javaagent.tooling.HeliosConfiguration.HELIOS_TEST_TRIGGERED_TRACE; + +import io.opentelemetry.api.baggage.Baggage; +import io.opentelemetry.context.Context; +import io.opentelemetry.sdk.trace.ReadWriteSpan; +import io.opentelemetry.sdk.trace.ReadableSpan; +import io.opentelemetry.sdk.trace.SpanProcessor; + +public class HeliosProcessor implements SpanProcessor { + @Override + public void onStart(Context parentContext, ReadWriteSpan span) { + Baggage baggage = Baggage.fromContext(parentContext); + if (baggage.getEntryValue(HELIOS_TEST_TRIGGERED_TRACE) != null) { + span.setAttribute(HELIOS_TEST_TRIGGERED_TRACE, "true"); + } + } + + @Override + public boolean isStartRequired() { + return true; + } + + @Override + public void onEnd(ReadableSpan span) {} + + @Override + public boolean isEndRequired() { + return false; + } +} diff --git a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/HeliosSampler.java b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/HeliosSampler.java new file mode 100644 index 000000000000..a0ef816c14e2 --- /dev/null +++ b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/HeliosSampler.java @@ -0,0 +1,62 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.tooling; + +import static io.opentelemetry.javaagent.tooling.HeliosConfiguration.HELIOS_TEST_TRIGGERED_TRACE; +import static java.util.logging.Level.WARNING; + +import io.opentelemetry.api.baggage.Baggage; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.context.Context; +import io.opentelemetry.sdk.trace.data.LinkData; +import io.opentelemetry.sdk.trace.samplers.Sampler; +import io.opentelemetry.sdk.trace.samplers.SamplingResult; +import java.util.List; +import java.util.logging.Logger; + +public class HeliosSampler implements Sampler { + + private static final Logger logger = Logger.getLogger(HeliosSampler.class.getName()); + + private final Sampler ratioBasedSampler; + + public HeliosSampler(double ratio) { + this.ratioBasedSampler = Sampler.traceIdRatioBased(ratio); + } + + @Override + public SamplingResult shouldSample( + Context context, + String traceId, + String name, + SpanKind spanKind, + Attributes attributes, + List list) { + try { + Baggage baggage = Baggage.fromContext(context); + if (baggage.getEntryValue(HELIOS_TEST_TRIGGERED_TRACE) != null) { + return SamplingResult.recordAndSample(); + } + Span currentSpan = Span.fromContext(context); + if (currentSpan != null + && currentSpan.getSpanContext() != null + && currentSpan.getSpanContext().isSampled()) { + return SamplingResult.recordAndSample(); + } + } catch (RuntimeException e) { + logger.log(WARNING, "Got exception when trying to sample span: " + e); + } + + return this.ratioBasedSampler.shouldSample(context, traceId, name, spanKind, attributes, list); + } + + @Override + public String getDescription() { + return HeliosSampler.class.getName(); + } +} diff --git a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/LoggingCustomizer.java b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/LoggingCustomizer.java index 1e4c9c013181..1b81ff6d0975 100644 --- a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/LoggingCustomizer.java +++ b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/LoggingCustomizer.java @@ -5,7 +5,6 @@ package io.opentelemetry.javaagent.tooling; -import io.opentelemetry.instrumentation.api.config.Config; import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk; // only one LoggingCustomizer is allowed, and its presence will suppress the @@ -21,7 +20,7 @@ public interface LoggingCustomizer { * Register a callback which will be called on synchronous startup success. * *

Synchronous startup may or may not include running {@link - * io.opentelemetry.javaagent.extension.AgentListener#afterAgent(Config, + * io.opentelemetry.javaagent.extension.AgentListener#afterAgent( * AutoConfiguredOpenTelemetrySdk)}" listeners. */ void onStartupSuccess(); @@ -31,7 +30,7 @@ public interface LoggingCustomizer { * #init()} fails). * *

Synchronous startup may or may not include running {@link - * io.opentelemetry.javaagent.extension.AgentListener#afterAgent(Config, + * io.opentelemetry.javaagent.extension.AgentListener#afterAgent( * AutoConfiguredOpenTelemetrySdk)}" listeners. */ void onStartupFailure(Throwable throwable); diff --git a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/NoopLoggingCustomizer.java b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/NoopLoggingCustomizer.java new file mode 100644 index 000000000000..1f90e249b69d --- /dev/null +++ b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/NoopLoggingCustomizer.java @@ -0,0 +1,24 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.tooling; + +enum NoopLoggingCustomizer implements LoggingCustomizer { + INSTANCE; + + @Override + public void init() {} + + @Override + @SuppressWarnings("SystemOut") + public void onStartupFailure(Throwable throwable) { + // there's no logging implementation installed, just print out the exception + System.err.println("OpenTelemetry Javaagent failed to start"); + throwable.printStackTrace(); + } + + @Override + public void onStartupSuccess() {} +} diff --git a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/NoopMeterExporter.java b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/NoopMeterExporter.java new file mode 100644 index 000000000000..ef9b1844dedc --- /dev/null +++ b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/NoopMeterExporter.java @@ -0,0 +1,35 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.tooling; + +import io.opentelemetry.sdk.common.CompletableResultCode; +import io.opentelemetry.sdk.metrics.InstrumentType; +import io.opentelemetry.sdk.metrics.data.AggregationTemporality; +import io.opentelemetry.sdk.metrics.data.MetricData; +import io.opentelemetry.sdk.metrics.export.MetricExporter; +import java.util.Collection; + +public class NoopMeterExporter implements MetricExporter { + @Override + public CompletableResultCode export(Collection metrics) { + return CompletableResultCode.ofSuccess(); + } + + @Override + public CompletableResultCode flush() { + return CompletableResultCode.ofSuccess(); + } + + @Override + public CompletableResultCode shutdown() { + return CompletableResultCode.ofSuccess(); + } + + @Override + public AggregationTemporality getAggregationTemporality(InstrumentType instrumentType) { + return AggregationTemporality.CUMULATIVE; + } +} 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 e799769fc841..177e683c291c 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,20 +5,20 @@ package io.opentelemetry.javaagent.tooling; -import io.opentelemetry.instrumentation.api.appender.internal.LogEmitterProvider; -import io.opentelemetry.instrumentation.api.config.Config; -import io.opentelemetry.instrumentation.sdk.appender.internal.DelegatingLogEmitterProvider; +import static io.opentelemetry.javaagent.tooling.HeliosConfiguration.getCollectorEndpoint; +import static io.opentelemetry.javaagent.tooling.HeliosConfiguration.getEnvironmentName; +import static io.opentelemetry.javaagent.tooling.HeliosConfiguration.getHsToken; +import static io.opentelemetry.javaagent.tooling.HeliosConfiguration.getServiceName; + import io.opentelemetry.javaagent.bootstrap.AgentInitializer; -import io.opentelemetry.javaagent.bootstrap.AgentLogEmitterProvider; import io.opentelemetry.javaagent.bootstrap.OpenTelemetrySdkAccess; import io.opentelemetry.sdk.OpenTelemetrySdk; import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk; import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdkBuilder; import io.opentelemetry.sdk.common.CompletableResultCode; -import io.opentelemetry.sdk.logs.SdkLogEmitterProvider; import java.util.Arrays; -public class OpenTelemetryInstaller { +public final class OpenTelemetryInstaller { /** * Install the {@link OpenTelemetrySdk} using autoconfigure, and return the {@link @@ -26,13 +26,9 @@ public class OpenTelemetryInstaller { * * @return the {@link AutoConfiguredOpenTelemetrySdk} */ - static AutoConfiguredOpenTelemetrySdk installOpenTelemetrySdk(Config config) { - System.setProperty("io.opentelemetry.context.contextStorageProvider", "default"); - + public static AutoConfiguredOpenTelemetrySdk installOpenTelemetrySdk() { AutoConfiguredOpenTelemetrySdkBuilder builder = - AutoConfiguredOpenTelemetrySdk.builder() - .setResultAsGlobal(true) - .addPropertiesSupplier(config::getAllProperties); + AutoConfiguredOpenTelemetrySdk.builder().setResultAsGlobal(true); ClassLoader classLoader = AgentInitializer.getExtensionsClassLoader(); if (classLoader != null) { @@ -40,23 +36,46 @@ static AutoConfiguredOpenTelemetrySdk installOpenTelemetrySdk(Config config) { builder.setServiceClassLoader(classLoader); } + setHeliosSystemProperties(); AutoConfiguredOpenTelemetrySdk autoConfiguredSdk = builder.build(); OpenTelemetrySdk sdk = autoConfiguredSdk.getOpenTelemetrySdk(); + printInitializationMessage(); OpenTelemetrySdkAccess.internalSetForceFlush( (timeout, unit) -> { CompletableResultCode traceResult = sdk.getSdkTracerProvider().forceFlush(); CompletableResultCode metricsResult = sdk.getSdkMeterProvider().forceFlush(); - CompletableResultCode.ofAll(Arrays.asList(traceResult, metricsResult)) + CompletableResultCode logsResult = sdk.getSdkLoggerProvider().forceFlush(); + CompletableResultCode.ofAll(Arrays.asList(traceResult, metricsResult, logsResult)) .join(timeout, unit); }); - SdkLogEmitterProvider sdkLogEmitterProvider = - autoConfiguredSdk.getOpenTelemetrySdk().getSdkLogEmitterProvider(); - LogEmitterProvider logEmitterProvider = - DelegatingLogEmitterProvider.from(sdkLogEmitterProvider); - AgentLogEmitterProvider.set(logEmitterProvider); - return autoConfiguredSdk; } + + static void setHeliosSystemProperties() { + String hsToken = getHsToken(); + + if (hsToken != null) { + System.setProperty("otel.exporter.otlp.headers", String.format("Authorization=%s", hsToken)); + System.setProperty("otel.exporter.otlp.traces.endpoint", getCollectorEndpoint()); + System.setProperty("otel.exporter.otlp.traces.protocol", "http/protobuf"); + } + } + + static void printInitializationMessage() { + String hsToken = getHsToken(); + if (hsToken != null) { + String serviceName = getServiceName(); + String environmentName = getEnvironmentName(); + if (serviceName != null) { + System.out.println( + String.format( + "Helios tracing initialized (service: %1$s, token: %2$s*****, environment: %3$s)", + serviceName, hsToken.substring(0, 3), environmentName)); + } + } + } + + private OpenTelemetryInstaller() {} } diff --git a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/RemappingUrlConnection.java b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/RemappingUrlConnection.java index 233d2854ba35..209d7683e04c 100644 --- a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/RemappingUrlConnection.java +++ b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/RemappingUrlConnection.java @@ -38,8 +38,7 @@ public class RemappingUrlConnection extends URLConnection { rule( "#io.opentelemetry.extension.aws", "#io.opentelemetry.javaagent.shaded.io.opentelemetry.extension.aws"), - rule("#java.util.logging.Logger", "#io.opentelemetry.javaagent.bootstrap.PatchLogger"), - rule("#org.slf4j", "#io.opentelemetry.javaagent.slf4j")); + rule("#java.util.logging.Logger", "#io.opentelemetry.javaagent.bootstrap.PatchLogger")); private final JarFile delegateJarFile; private final JarEntry entry; diff --git a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/SafeServiceLoader.java b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/SafeServiceLoader.java index eba3c98f8fb6..3d56248387da 100644 --- a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/SafeServiceLoader.java +++ b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/SafeServiceLoader.java @@ -7,7 +7,7 @@ import static java.util.logging.Level.FINE; -import io.opentelemetry.javaagent.extension.Ordered; +import io.opentelemetry.sdk.autoconfigure.spi.Ordered; import java.util.ArrayList; import java.util.Comparator; import java.util.Iterator; diff --git a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/bootstrap/BootstrapPackagesBuilderImpl.java b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/bootstrap/BootstrapPackagesBuilderImpl.java index 829118802316..6508b6412226 100644 --- a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/bootstrap/BootstrapPackagesBuilderImpl.java +++ b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/bootstrap/BootstrapPackagesBuilderImpl.java @@ -5,6 +5,7 @@ package io.opentelemetry.javaagent.tooling.bootstrap; +import com.google.errorprone.annotations.CanIgnoreReturnValue; import io.opentelemetry.javaagent.tooling.Constants; import java.util.ArrayList; import java.util.Collection; @@ -16,12 +17,14 @@ public class BootstrapPackagesBuilderImpl implements BootstrapPackagesBuilder { private final List packages = new ArrayList<>(Constants.BOOTSTRAP_PACKAGE_PREFIXES); @Override + @CanIgnoreReturnValue public BootstrapPackagesBuilder add(String classNameOrPrefix) { packages.add(classNameOrPrefix); return this; } @Override + @CanIgnoreReturnValue public BootstrapPackagesBuilder addAll(Collection classNamesOrPrefixes) { packages.addAll(classNamesOrPrefixes); return this; diff --git a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/bootstrap/BootstrapPackagesConfigurer.java b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/bootstrap/BootstrapPackagesConfigurer.java index 6990c135567f..30d0e56a9a8c 100644 --- a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/bootstrap/BootstrapPackagesConfigurer.java +++ b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/bootstrap/BootstrapPackagesConfigurer.java @@ -5,7 +5,7 @@ package io.opentelemetry.javaagent.tooling.bootstrap; -import io.opentelemetry.instrumentation.api.config.Config; +import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; /** * This SPI can be used to define which packages/classes belong to the bootstrap class loader: all @@ -24,5 +24,5 @@ public interface BootstrapPackagesConfigurer { * Configure the passed {@code builder} and define which classes should always be loaded by the * bootstrap class loader. */ - void configure(Config config, BootstrapPackagesBuilder builder); + void configure(BootstrapPackagesBuilder builder, ConfigProperties config); } diff --git a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/bytebuddy/SafeTypeStrategy.java b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/bytebuddy/SafeTypeStrategy.java new file mode 100644 index 000000000000..8d78b3f8f9cf --- /dev/null +++ b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/bytebuddy/SafeTypeStrategy.java @@ -0,0 +1,86 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.tooling.bytebuddy; + +import static net.bytebuddy.matcher.ElementMatchers.failSafe; +import static net.bytebuddy.matcher.ElementMatchers.hasParameters; +import static net.bytebuddy.matcher.ElementMatchers.hasType; +import static net.bytebuddy.matcher.ElementMatchers.isVisibleTo; +import static net.bytebuddy.matcher.ElementMatchers.not; +import static net.bytebuddy.matcher.ElementMatchers.returns; +import static net.bytebuddy.matcher.ElementMatchers.whereNone; + +import java.security.ProtectionDomain; +import net.bytebuddy.ByteBuddy; +import net.bytebuddy.agent.builder.AgentBuilder; +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.description.method.MethodList; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.dynamic.ClassFileLocator; +import net.bytebuddy.dynamic.DynamicType; +import net.bytebuddy.dynamic.scaffold.inline.MethodNameTransformer; +import net.bytebuddy.utility.JavaModule; + +/** + * Wrapper for {@link AgentBuilder.TypeStrategy} that excludes methods with missing return or + * parameter types. By default, byte-buddy fails transforming such classes. + */ +public final class SafeTypeStrategy implements AgentBuilder.TypeStrategy { + private final AgentBuilder.TypeStrategy delegate; + + public SafeTypeStrategy(AgentBuilder.TypeStrategy delegate) { + this.delegate = delegate; + } + + @Override + public DynamicType.Builder builder( + TypeDescription typeDescription, + ByteBuddy byteBuddy, + ClassFileLocator classFileLocator, + MethodNameTransformer methodNameTransformer, + ClassLoader classLoader, + JavaModule module, + ProtectionDomain protectionDomain) { + // type description wrapper that removes methods with missing return or parameter types + TypeDescription newTypeDescription = + new TypeDescription.AbstractBase.OfSimpleType.WithDelegation() { + + @Override + public String getName() { + return delegate().getName(); + } + + @Override + protected TypeDescription delegate() { + return typeDescription; + } + + @Override + public MethodList getDeclaredMethods() { + MethodList methodList = super.getDeclaredMethods(); + return filterMethods(methodList, typeDescription); + } + }; + + return delegate.builder( + newTypeDescription, + byteBuddy, + classFileLocator, + methodNameTransformer, + classLoader, + module, + protectionDomain); + } + + private static MethodList filterMethods( + MethodList methodList, TypeDescription viewPoint) { + // filter out methods missing return or parameter types + return methodList.filter( + failSafe( + returns(isVisibleTo(viewPoint)) + .and(hasParameters(whereNone(hasType(not(isVisibleTo(viewPoint)))))))); + } +} diff --git a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/config/AgentConfig.java b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/config/AgentConfig.java index bc5b51812065..95c16a334d84 100644 --- a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/config/AgentConfig.java +++ b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/config/AgentConfig.java @@ -5,24 +5,12 @@ package io.opentelemetry.javaagent.tooling.config; -import io.opentelemetry.instrumentation.api.config.Config; +import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; public final class AgentConfig { - private static final AgentConfig instance = new AgentConfig(Config.get()); - - private final Config config; - - public static AgentConfig get() { - return instance; - } - - public AgentConfig(Config config) { - this.config = config; - } - - public boolean isInstrumentationEnabled( - Iterable instrumentationNames, boolean defaultEnabled) { + public static boolean isInstrumentationEnabled( + ConfigProperties config, Iterable instrumentationNames, boolean defaultEnabled) { // If default is enabled, we want to enable individually, // if default is disabled, we want to disable individually. boolean anyEnabled = defaultEnabled; @@ -39,7 +27,14 @@ public boolean isInstrumentationEnabled( return anyEnabled; } - public boolean isDebugModeEnabled() { + public static boolean isDebugModeEnabled(ConfigProperties config) { + String heliosDebugEnv = System.getenv("HS_DEBUG"); + + if (heliosDebugEnv != null && Boolean.parseBoolean(heliosDebugEnv)) { + return true; + } return config.getBoolean("otel.javaagent.debug", false); } + + private AgentConfig() {} } diff --git a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/config/ConfigInitializer.java b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/config/ConfigInitializer.java deleted file mode 100644 index 79e505dfb54a..000000000000 --- a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/config/ConfigInitializer.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.javaagent.tooling.config; - -import static io.opentelemetry.javaagent.tooling.SafeServiceLoader.loadOrdered; -import static java.util.logging.Level.SEVERE; - -import io.opentelemetry.instrumentation.api.config.Config; -import io.opentelemetry.javaagent.extension.config.ConfigPropertySource; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStreamReader; -import java.nio.charset.StandardCharsets; -import java.util.List; -import java.util.Properties; -import java.util.logging.Logger; - -public final class ConfigInitializer { - private static final Logger logger = Logger.getLogger(ConfigInitializer.class.getName()); - - // visible for testing - static final String CONFIGURATION_FILE_PROPERTY = "otel.javaagent.configuration-file"; - static final String CONFIGURATION_FILE_ENV_VAR = "OTEL_JAVAAGENT_CONFIGURATION_FILE"; - - @SuppressWarnings("deprecation") // loads the ConfigCustomizer SPI - public static void initialize() { - List customizers = - loadOrdered(io.opentelemetry.javaagent.extension.config.ConfigCustomizer.class); - Config config = create(loadSpiConfiguration(customizers), loadConfigurationFile()); - for (io.opentelemetry.javaagent.extension.config.ConfigCustomizer customizer : customizers) { - config = customizer.customize(config); - } - Config.internalInitializeConfig(config); - } - - // visible for testing - static Config create(Properties spiConfiguration, Properties configurationFile) { - return Config.builder() - .addProperties(spiConfiguration) - .addProperties(configurationFile) - .addEnvironmentVariables() - .addSystemProperties() - .build(); - } - - /** Retrieves all default configuration overloads using SPI and initializes Config. */ - @SuppressWarnings("deprecation") // loads the ConfigCustomizer SPI - private static Properties loadSpiConfiguration( - List customizers) { - Properties propertiesFromSpi = new Properties(); - for (ConfigPropertySource propertySource : loadOrdered(ConfigPropertySource.class)) { - propertiesFromSpi.putAll(propertySource.getProperties()); - } - for (io.opentelemetry.javaagent.extension.config.ConfigCustomizer customizer : customizers) { - propertiesFromSpi.putAll(customizer.defaultProperties()); - } - return propertiesFromSpi; - } - - /** - * Loads the optional configuration properties file into the global {@link Properties} object. - * - * @return The {@link Properties} object. the returned instance might be empty of file does not - * exist or if it is in a wrong format. - */ - // visible for testing - static Properties loadConfigurationFile() { - Properties properties = new Properties(); - - // Reading from system property first and from env after - String configurationFilePath = System.getProperty(CONFIGURATION_FILE_PROPERTY); - if (configurationFilePath == null) { - configurationFilePath = System.getenv(CONFIGURATION_FILE_ENV_VAR); - } - if (configurationFilePath == null) { - return properties; - } - - // Normalizing tilde (~) paths for unix systems - configurationFilePath = - configurationFilePath.replaceFirst("^~", System.getProperty("user.home")); - - // Configuration properties file is optional - File configurationFile = new File(configurationFilePath); - if (!configurationFile.exists()) { - logger.log(SEVERE, "Configuration file \"{0}\" not found.", configurationFilePath); - return properties; - } - - try (InputStreamReader reader = - new InputStreamReader(new FileInputStream(configurationFile), StandardCharsets.UTF_8)) { - properties.load(reader); - } catch (FileNotFoundException fnf) { - logger.log(SEVERE, "Configuration file \"{0}\" not found.", configurationFilePath); - } catch (IOException ioe) { - logger.log( - SEVERE, - "Configuration file \"{0}\" cannot be accessed or correctly parsed.", - configurationFilePath); - } - - return properties; - } - - private ConfigInitializer() {} -} diff --git a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/config/ConfigPropertiesBridge.java b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/config/ConfigPropertiesBridge.java index f9fe051e4b15..c690921de4a8 100644 --- a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/config/ConfigPropertiesBridge.java +++ b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/config/ConfigPropertiesBridge.java @@ -10,7 +10,6 @@ import io.opentelemetry.sdk.autoconfigure.spi.ConfigurationException; import java.time.Duration; import java.util.List; -import java.util.Locale; import java.util.Map; import javax.annotation.Nullable; @@ -26,7 +25,7 @@ public ConfigPropertiesBridge(ConfigProperties configProperties) { @Override public String getString(String name) { try { - return configProperties.getString(normalize(name)); + return configProperties.getString(name); } catch (ConfigurationException ignored) { return null; } @@ -35,7 +34,7 @@ public String getString(String name) { @Override public String getString(String name, String defaultValue) { try { - return configProperties.getString(normalize(name), defaultValue); + return configProperties.getString(name, defaultValue); } catch (ConfigurationException ignored) { return defaultValue; } @@ -44,7 +43,7 @@ public String getString(String name, String defaultValue) { @Override public boolean getBoolean(String name, boolean defaultValue) { try { - return configProperties.getBoolean(normalize(name), defaultValue); + return configProperties.getBoolean(name, defaultValue); } catch (ConfigurationException ignored) { return defaultValue; } @@ -53,7 +52,7 @@ public boolean getBoolean(String name, boolean defaultValue) { @Override public int getInt(String name, int defaultValue) { try { - return configProperties.getInt(normalize(name), defaultValue); + return configProperties.getInt(name, defaultValue); } catch (ConfigurationException ignored) { return defaultValue; } @@ -62,7 +61,7 @@ public int getInt(String name, int defaultValue) { @Override public long getLong(String name, long defaultValue) { try { - return configProperties.getLong(normalize(name), defaultValue); + return configProperties.getLong(name, defaultValue); } catch (ConfigurationException ignored) { return defaultValue; } @@ -71,7 +70,7 @@ public long getLong(String name, long defaultValue) { @Override public double getDouble(String name, double defaultValue) { try { - return configProperties.getDouble(normalize(name), defaultValue); + return configProperties.getDouble(name, defaultValue); } catch (ConfigurationException ignored) { return defaultValue; } @@ -80,7 +79,7 @@ public double getDouble(String name, double defaultValue) { @Override public Duration getDuration(String name, Duration defaultValue) { try { - return configProperties.getDuration(normalize(name), defaultValue); + return configProperties.getDuration(name, defaultValue); } catch (ConfigurationException ignored) { return defaultValue; } @@ -89,7 +88,7 @@ public Duration getDuration(String name, Duration defaultValue) { @Override public List getList(String name, List defaultValue) { try { - return configProperties.getList(normalize(name), defaultValue); + return configProperties.getList(name, defaultValue); } catch (ConfigurationException ignored) { return defaultValue; } @@ -98,14 +97,9 @@ public List getList(String name, List defaultValue) { @Override public Map getMap(String name, Map defaultValue) { try { - return configProperties.getMap(normalize(name), defaultValue); + return configProperties.getMap(name, defaultValue); } catch (ConfigurationException ignored) { return defaultValue; } } - - // TODO: remove after https://github.com/open-telemetry/opentelemetry-java/issues/4562 is fixed - private static String normalize(String key) { - return key.toLowerCase(Locale.ROOT).replace('-', '.'); - } } diff --git a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/config/ConfigurationFileLoader.java b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/config/ConfigurationFileLoader.java new file mode 100644 index 000000000000..52df5b1a7f01 --- /dev/null +++ b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/config/ConfigurationFileLoader.java @@ -0,0 +1,79 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.tooling.config; + +import static java.util.Collections.emptyMap; +import static java.util.logging.Level.SEVERE; + +import com.google.auto.service.AutoService; +import io.opentelemetry.instrumentation.api.internal.ConfigPropertiesUtil; +import io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizer; +import io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizerProvider; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.util.Map; +import java.util.Properties; +import java.util.logging.Logger; +import java.util.stream.Collectors; + +@AutoService(AutoConfigurationCustomizerProvider.class) +public final class ConfigurationFileLoader implements AutoConfigurationCustomizerProvider { + + private static final Logger logger = Logger.getLogger(ConfigurationFileLoader.class.getName()); + + static final String CONFIGURATION_FILE_PROPERTY = "otel.javaagent.configuration-file"; + + @Override + public void customize(AutoConfigurationCustomizer autoConfiguration) { + autoConfiguration.addPropertiesSupplier(ConfigurationFileLoader::loadConfigFile); + } + + // visible for tests + static Map loadConfigFile() { + // Reading from system property first and from env after + String configurationFilePath = ConfigPropertiesUtil.getString(CONFIGURATION_FILE_PROPERTY); + if (configurationFilePath == null) { + return emptyMap(); + } + + // Normalizing tilde (~) paths for unix systems + configurationFilePath = + configurationFilePath.replaceFirst("^~", System.getProperty("user.home")); + + // Configuration properties file is optional + File configurationFile = new File(configurationFilePath); + if (!configurationFile.exists()) { + logger.log(SEVERE, "Configuration file \"{0}\" not found.", configurationFilePath); + return emptyMap(); + } + + Properties properties = new Properties(); + try (InputStreamReader reader = + new InputStreamReader(new FileInputStream(configurationFile), StandardCharsets.UTF_8)) { + properties.load(reader); + } catch (FileNotFoundException fnf) { + logger.log(SEVERE, "Configuration file \"{0}\" not found.", configurationFilePath); + } catch (IOException ioe) { + logger.log( + SEVERE, + "Configuration file \"{0}\" cannot be accessed or correctly parsed.", + configurationFilePath); + } + + return properties.entrySet().stream() + .collect(Collectors.toMap(e -> e.getKey().toString(), e -> e.getValue().toString())); + } + + @Override + public int order() { + // make sure it runs after all the user-provided customizers + return Integer.MAX_VALUE; + } +} diff --git a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/field/FieldBackedImplementationInstaller.java b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/field/FieldBackedImplementationInstaller.java index 7e9a3c58abdf..2e39407eaabf 100644 --- a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/field/FieldBackedImplementationInstaller.java +++ b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/field/FieldBackedImplementationInstaller.java @@ -11,9 +11,9 @@ import static net.bytebuddy.matcher.ElementMatchers.named; import static net.bytebuddy.matcher.ElementMatchers.not; -import io.opentelemetry.instrumentation.api.config.Config; import io.opentelemetry.javaagent.bootstrap.InstrumentationHolder; import io.opentelemetry.javaagent.bootstrap.VirtualFieldDetector; +import io.opentelemetry.javaagent.bootstrap.internal.InstrumentationConfig; import io.opentelemetry.javaagent.tooling.HelperInjector; import io.opentelemetry.javaagent.tooling.TransformSafeLogger; import io.opentelemetry.javaagent.tooling.instrumentation.InstrumentationModuleInstaller; @@ -21,6 +21,7 @@ import io.opentelemetry.javaagent.tooling.util.IgnoreFailedTypeMatcher; import io.opentelemetry.javaagent.tooling.util.NamedMatcher; import java.lang.instrument.Instrumentation; +import java.security.ProtectionDomain; import java.util.Collection; import java.util.HashSet; import java.util.Map; @@ -62,7 +63,8 @@ final class FieldBackedImplementationInstaller implements VirtualFieldImplementa TransformSafeLogger.getLogger(FieldBackedImplementationInstaller.class); private static final boolean FIELD_INJECTION_ENABLED = - Config.get().getBoolean("otel.javaagent.experimental.field-injection.enabled", true); + InstrumentationConfig.get() + .getBoolean("otel.javaagent.experimental.field-injection.enabled", true); private final Class instrumenterClass; private final VirtualFieldMappings virtualFieldMappings; @@ -144,13 +146,15 @@ public DynamicType.Builder transform( DynamicType.Builder builder, TypeDescription typeDescription, ClassLoader classLoader, - JavaModule module) { + JavaModule javaModule, + ProtectionDomain protectionDomain) { return injector.transform( builder, typeDescription, // virtual field implementation classes will always go to the bootstrap null, - module); + javaModule, + protectionDomain); } }; } @@ -243,7 +247,8 @@ private static AgentBuilder.RawMatcher safeToInjectFieldsMatcher() { } private static AgentBuilder.Transformer getTransformerForAsmVisitor(AsmVisitorWrapper visitor) { - return (builder, typeDescription, classLoader, module) -> builder.visit(visitor); + return (builder, typeDescription, classLoader, javaModule, protectionDomain) -> + builder.visit(visitor); } // Originally found in AgentBuilder.Transformer.NoOp, but removed in 1.10.7 @@ -255,7 +260,8 @@ public DynamicType.Builder transform( DynamicType.Builder builder, TypeDescription typeDescription, ClassLoader classLoader, - JavaModule module) { + JavaModule javaModule, + ProtectionDomain protectionDomain) { return builder; } } 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 a66ce0d9942c..9ab4e6c8b29f 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 @@ -9,7 +9,6 @@ import io.opentelemetry.javaagent.extension.ignore.IgnoredTypesBuilder; import io.opentelemetry.javaagent.extension.ignore.IgnoredTypesConfigurer; import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; -import java.util.Locale; /** * Additional global ignore settings that are used to reduce number of classes we try to apply @@ -29,8 +28,8 @@ public class AdditionalLibraryIgnoredTypesConfigurer implements IgnoredTypesConf "otel.javaagent.testing.additional-library-ignores.enabled"; @Override - public void configure(ConfigProperties config, IgnoredTypesBuilder builder) { - if (config.getBoolean(normalize(ADDITIONAL_LIBRARY_IGNORES_ENABLED), true)) { + public void configure(IgnoredTypesBuilder builder, ConfigProperties config) { + if (config.getBoolean(ADDITIONAL_LIBRARY_IGNORES_ENABLED, true)) { configure(builder); } } @@ -214,11 +213,6 @@ public void configure(IgnoredTypesBuilder builder) { .allowClass("ch.qos.logback.classic.spi.LoggingEvent") .allowClass("ch.qos.logback.classic.spi.LoggingEventVO"); - builder - .ignoreClass("com.codahale.metrics.") - // We instrument servlets - .allowClass("com.codahale.metrics.servlets."); - builder .ignoreClass("com.couchbase.client.deps.") // Couchbase library includes some packaged dependencies, unfortunately some of them are @@ -276,9 +270,4 @@ public void configure(IgnoredTypesBuilder builder) { // kotlin, note we do not ignore kotlinx because we instrument coroutines code builder.ignoreClass("kotlin.").allowClass("kotlin.coroutines.jvm.internal.DebugProbesKt"); } - - // TODO: remove after https://github.com/open-telemetry/opentelemetry-java/issues/4562 is fixed - private static String normalize(String key) { - return key.toLowerCase(Locale.ROOT).replace('-', '.'); - } } diff --git a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/ignore/GlobalIgnoredTypesConfigurer.java b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/ignore/GlobalIgnoredTypesConfigurer.java index def4e1ce4b08..49dd5661dd2a 100644 --- a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/ignore/GlobalIgnoredTypesConfigurer.java +++ b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/ignore/GlobalIgnoredTypesConfigurer.java @@ -16,7 +16,7 @@ public class GlobalIgnoredTypesConfigurer implements IgnoredTypesConfigurer { @Override - public void configure(ConfigProperties config, IgnoredTypesBuilder builder) { + public void configure(IgnoredTypesBuilder builder, ConfigProperties config) { configureIgnoredTypes(builder); configureIgnoredClassLoaders(builder); configureIgnoredTasks(builder); @@ -61,7 +61,6 @@ private static void configureIgnoredTypes(IgnoredTypesBuilder builder) { // tests under "io.opentelemetry.javaagent." will still be instrumented builder.ignoreClass("io.opentelemetry.javaagent.bootstrap."); builder.ignoreClass("io.opentelemetry.javaagent.shaded."); - builder.ignoreClass("io.opentelemetry.javaagent.slf4j."); builder .ignoreClass("java.") diff --git a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/ignore/IgnoredTypesBuilderImpl.java b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/ignore/IgnoredTypesBuilderImpl.java index daaac80a26e9..25d02f9ce908 100644 --- a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/ignore/IgnoredTypesBuilderImpl.java +++ b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/ignore/IgnoredTypesBuilderImpl.java @@ -5,6 +5,7 @@ package io.opentelemetry.javaagent.tooling.ignore; +import com.google.errorprone.annotations.CanIgnoreReturnValue; import io.opentelemetry.javaagent.extension.ignore.IgnoredTypesBuilder; import io.opentelemetry.javaagent.tooling.util.Trie; @@ -14,30 +15,35 @@ public class IgnoredTypesBuilderImpl implements IgnoredTypesBuilder { private final Trie.Builder ignoredTasksTrie = Trie.builder(); @Override + @CanIgnoreReturnValue public IgnoredTypesBuilder ignoreClass(String classNameOrPrefix) { ignoredTypesTrie.put(classNameOrPrefix, IgnoreAllow.IGNORE); return this; } @Override + @CanIgnoreReturnValue public IgnoredTypesBuilder allowClass(String classNameOrPrefix) { ignoredTypesTrie.put(classNameOrPrefix, IgnoreAllow.ALLOW); return this; } @Override + @CanIgnoreReturnValue public IgnoredTypesBuilder ignoreClassLoader(String classNameOrPrefix) { ignoredClassLoadersTrie.put(classNameOrPrefix, IgnoreAllow.IGNORE); return this; } @Override + @CanIgnoreReturnValue public IgnoredTypesBuilder allowClassLoader(String classNameOrPrefix) { ignoredClassLoadersTrie.put(classNameOrPrefix, IgnoreAllow.ALLOW); return this; } @Override + @CanIgnoreReturnValue public IgnoredTypesBuilder ignoreTaskClass(String classNameOrPrefix) { ignoredTasksTrie.put(classNameOrPrefix, true); return this; diff --git a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/ignore/UserExcludedClassesConfigurer.java b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/ignore/UserExcludedClassesConfigurer.java index eb58dc65fe37..44a240545b16 100644 --- a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/ignore/UserExcludedClassesConfigurer.java +++ b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/ignore/UserExcludedClassesConfigurer.java @@ -12,7 +12,6 @@ import io.opentelemetry.javaagent.extension.ignore.IgnoredTypesConfigurer; import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import java.util.List; -import java.util.Locale; @AutoService(IgnoredTypesConfigurer.class) public class UserExcludedClassesConfigurer implements IgnoredTypesConfigurer { @@ -21,8 +20,8 @@ public class UserExcludedClassesConfigurer implements IgnoredTypesConfigurer { static final String EXCLUDED_CLASSES_CONFIG = "otel.javaagent.exclude-classes"; @Override - public void configure(ConfigProperties config, IgnoredTypesBuilder builder) { - List excludedClasses = config.getList(normalize(EXCLUDED_CLASSES_CONFIG), emptyList()); + public void configure(IgnoredTypesBuilder builder, ConfigProperties config) { + List excludedClasses = config.getList(EXCLUDED_CLASSES_CONFIG, emptyList()); for (String excludedClass : excludedClasses) { excludedClass = excludedClass.trim(); // remove the trailing * @@ -32,9 +31,4 @@ public void configure(ConfigProperties config, IgnoredTypesBuilder builder) { builder.ignoreClass(excludedClass); } } - - // TODO: remove after https://github.com/open-telemetry/opentelemetry-java/issues/4562 is fixed - private static String normalize(String key) { - return key.toLowerCase(Locale.ROOT).replace('-', '.'); - } } diff --git a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/instrumentation/ConstantAdjuster.java b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/instrumentation/ConstantAdjuster.java index eed70062037b..20b7a9ab3fd4 100644 --- a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/instrumentation/ConstantAdjuster.java +++ b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/instrumentation/ConstantAdjuster.java @@ -5,6 +5,7 @@ package io.opentelemetry.javaagent.tooling.instrumentation; +import java.security.ProtectionDomain; import net.bytebuddy.agent.builder.AgentBuilder; import net.bytebuddy.asm.TypeConstantAdjustment; import net.bytebuddy.description.type.TypeDescription; @@ -31,7 +32,8 @@ public DynamicType.Builder transform( DynamicType.Builder builder, TypeDescription typeDescription, ClassLoader classLoader, - JavaModule module) { + JavaModule javaModule, + ProtectionDomain protectionDomain) { return builder.visit(TypeConstantAdjustment.INSTANCE); } } diff --git a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/instrumentation/InstrumentationLoader.java b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/instrumentation/InstrumentationLoader.java index e5c49601c1e0..8ba286aafd95 100644 --- a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/instrumentation/InstrumentationLoader.java +++ b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/instrumentation/InstrumentationLoader.java @@ -13,6 +13,7 @@ import io.opentelemetry.javaagent.bootstrap.InstrumentationHolder; import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule; import io.opentelemetry.javaagent.tooling.AgentExtension; +import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import java.util.logging.Logger; import net.bytebuddy.agent.builder.AgentBuilder; @@ -24,7 +25,7 @@ public class InstrumentationLoader implements AgentExtension { new InstrumentationModuleInstaller(InstrumentationHolder.getInstrumentation()); @Override - public AgentBuilder extend(AgentBuilder agentBuilder) { + public AgentBuilder extend(AgentBuilder agentBuilder, ConfigProperties config) { int numberOfLoadedModules = 0; for (InstrumentationModule instrumentationModule : loadOrdered(InstrumentationModule.class)) { if (logger.isLoggable(FINE)) { @@ -37,7 +38,8 @@ public AgentBuilder extend(AgentBuilder agentBuilder) { }); } try { - agentBuilder = instrumentationModuleInstaller.install(instrumentationModule, agentBuilder); + agentBuilder = + instrumentationModuleInstaller.install(instrumentationModule, agentBuilder, config); numberOfLoadedModules++; } catch (Exception | LinkageError e) { logger.log( diff --git a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/instrumentation/InstrumentationModuleInstaller.java b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/instrumentation/InstrumentationModuleInstaller.java index fe4fa6302d4e..419a8c1ccde2 100644 --- a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/instrumentation/InstrumentationModuleInstaller.java +++ b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/instrumentation/InstrumentationModuleInstaller.java @@ -28,6 +28,7 @@ import io.opentelemetry.javaagent.tooling.muzzle.ReferenceMatcher; import io.opentelemetry.javaagent.tooling.util.IgnoreFailedTypeMatcher; import io.opentelemetry.javaagent.tooling.util.NamedMatcher; +import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import java.lang.instrument.Instrumentation; import java.security.ProtectionDomain; import java.util.List; @@ -58,10 +59,13 @@ public InstrumentationModuleInstaller(Instrumentation instrumentation) { } AgentBuilder install( - InstrumentationModule instrumentationModule, AgentBuilder parentAgentBuilder) { - if (!AgentConfig.get() - .isInstrumentationEnabled( - instrumentationModule.instrumentationNames(), instrumentationModule.defaultEnabled())) { + InstrumentationModule instrumentationModule, + AgentBuilder parentAgentBuilder, + ConfigProperties config) { + if (!AgentConfig.isInstrumentationEnabled( + config, + instrumentationModule.instrumentationNames(), + instrumentationModule.defaultEnabled(config))) { logger.log( FINE, "Instrumentation {0} is disabled", instrumentationModule.instrumentationName()); return parentAgentBuilder; diff --git a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/util/TrieImpl.java b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/util/TrieImpl.java index b4d4da309b0c..56f1e996563a 100644 --- a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/util/TrieImpl.java +++ b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/util/TrieImpl.java @@ -5,6 +5,7 @@ package io.opentelemetry.javaagent.tooling.util; +import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.util.Arrays; import java.util.HashMap; import java.util.Iterator; @@ -64,6 +65,7 @@ static final class BuilderImpl implements Builder { private final NodeBuilder root = new NodeBuilder<>(); @Override + @CanIgnoreReturnValue public Builder put(CharSequence str, V value) { put(root, str, 0, value); return this; diff --git a/javaagent-tooling/src/test/groovy/io/opentelemetry/javaagent/test/HelperInjectionTest.groovy b/javaagent-tooling/src/test/groovy/io/opentelemetry/javaagent/test/HelperInjectionTest.groovy index 84401d4dfdc3..a99118b5e07e 100644 --- a/javaagent-tooling/src/test/groovy/io/opentelemetry/javaagent/test/HelperInjectionTest.groovy +++ b/javaagent-tooling/src/test/groovy/io/opentelemetry/javaagent/test/HelperInjectionTest.groovy @@ -5,7 +5,7 @@ package io.opentelemetry.javaagent.test -import io.opentelemetry.instrumentation.api.config.Config + import io.opentelemetry.javaagent.tooling.AgentInstaller import io.opentelemetry.javaagent.tooling.HelperInjector import io.opentelemetry.javaagent.tooling.Utils @@ -39,7 +39,7 @@ class HelperInjectionTest extends Specification { thrown ClassNotFoundException when: - injector.transform(null, null, emptyLoader.get(), null) + injector.transform(null, null, emptyLoader.get(), null, null) HelperInjector.loadHelperClass(emptyLoader.get(), helperClassName) emptyLoader.get().loadClass(helperClassName) then: @@ -61,7 +61,7 @@ class HelperInjectionTest extends Specification { def "helpers injected on bootstrap classloader"() { setup: ByteBuddyAgent.install() - AgentInstaller.installBytebuddyAgent(ByteBuddyAgent.getInstrumentation(), Config.get()) + AgentInstaller.installBytebuddyAgent(ByteBuddyAgent.getInstrumentation()) String helperClassName = HelperInjectionTest.getPackage().getName() + '.HelperClass' HelperInjector injector = new HelperInjector("test", [helperClassName], [], this.class.classLoader, ByteBuddyAgent.getInstrumentation()) URLClassLoader bootstrapChild = new URLClassLoader(new URL[0], (ClassLoader) null) @@ -73,7 +73,7 @@ class HelperInjectionTest extends Specification { when: def bootstrapClassloader = null - injector.transform(null, null, bootstrapClassloader, null) + injector.transform(null, null, bootstrapClassloader, null, null) Class helperClass = bootstrapChild.loadClass(helperClassName) then: helperClass.getClassLoader() == bootstrapClassloader diff --git a/javaagent-tooling/src/test/groovy/io/opentelemetry/javaagent/tooling/OpenTelemetryInstallerTest.groovy b/javaagent-tooling/src/test/groovy/io/opentelemetry/javaagent/tooling/OpenTelemetryInstallerTest.groovy index 652d5196ff18..b63d9ceb7e3c 100755 --- a/javaagent-tooling/src/test/groovy/io/opentelemetry/javaagent/tooling/OpenTelemetryInstallerTest.groovy +++ b/javaagent-tooling/src/test/groovy/io/opentelemetry/javaagent/tooling/OpenTelemetryInstallerTest.groovy @@ -6,30 +6,29 @@ package io.opentelemetry.javaagent.tooling import io.opentelemetry.api.GlobalOpenTelemetry -import io.opentelemetry.extension.noopapi.NoopOpenTelemetry -import io.opentelemetry.instrumentation.api.config.Config -import io.opentelemetry.javaagent.bootstrap.AgentLogEmitterProvider +import io.opentelemetry.api.OpenTelemetry +import io.opentelemetry.api.logs.GlobalLoggerProvider import spock.lang.Specification class OpenTelemetryInstallerTest extends Specification { void setup() { GlobalOpenTelemetry.resetForTest() - AgentLogEmitterProvider.resetForTest() + GlobalLoggerProvider.resetForTest() } void cleanup() { GlobalOpenTelemetry.resetForTest() - AgentLogEmitterProvider.resetForTest() + GlobalLoggerProvider.resetForTest() } def "should initialize GlobalOpenTelemetry"() { when: - def otelInstaller = OpenTelemetryInstaller.installOpenTelemetrySdk(Config.builder().build()) + def otelInstaller = OpenTelemetryInstaller.installOpenTelemetrySdk() then: otelInstaller != null - GlobalOpenTelemetry.getTracerProvider() != NoopOpenTelemetry.getInstance().getTracerProvider() + GlobalOpenTelemetry.getTracerProvider() != OpenTelemetry.noop().getTracerProvider() } } diff --git a/javaagent-tooling/src/test/groovy/io/opentelemetry/javaagent/tooling/config/ConfigInitializerTest.groovy b/javaagent-tooling/src/test/groovy/io/opentelemetry/javaagent/tooling/config/ConfigInitializerTest.groovy deleted file mode 100644 index e55f74ebae58..000000000000 --- a/javaagent-tooling/src/test/groovy/io/opentelemetry/javaagent/tooling/config/ConfigInitializerTest.groovy +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.javaagent.tooling.config - -import org.junit.Rule -import org.junit.contrib.java.lang.system.EnvironmentVariables -import spock.lang.Specification -import spock.util.environment.RestoreSystemProperties - -@RestoreSystemProperties -class ConfigInitializerTest extends Specification { - - @Rule - public final EnvironmentVariables environmentVariables = new EnvironmentVariables() - - def "should use SPI properties"() { - given: - def spiConfiguration = new Properties() - spiConfiguration.put("property1", "spi-1") - spiConfiguration.put("property2", "spi-2") - spiConfiguration.put("property3", "spi-3") - spiConfiguration.put("property4", "spi-4") - - when: - def config = ConfigInitializer.create(spiConfiguration, new Properties()) - - then: - config.getString("property1") == "spi-1" - config.getString("property2") == "spi-2" - config.getString("property3") == "spi-3" - config.getString("property4") == "spi-4" - } - - def "should use configuration file properties (takes precedence over SPI)"() { - given: - def spiConfiguration = new Properties() - spiConfiguration.put("property1", "spi-1") - spiConfiguration.put("property2", "spi-2") - spiConfiguration.put("property3", "spi-3") - spiConfiguration.put("property4", "spi-4") - - def configurationFile = new Properties() - configurationFile.put("property1", "cf-1") - configurationFile.put("property2", "cf-2") - configurationFile.put("property3", "cf-3") - - when: - def config = ConfigInitializer.create(spiConfiguration, configurationFile) - - then: - config.getString("property1") == "cf-1" - config.getString("property2") == "cf-2" - config.getString("property3") == "cf-3" - config.getString("property4") == "spi-4" - } - - def "should use environment variables (takes precedence over configuration file)"() { - given: - def spiConfiguration = new Properties() - spiConfiguration.put("property1", "spi-1") - spiConfiguration.put("property2", "spi-2") - spiConfiguration.put("property3", "spi-3") - spiConfiguration.put("property4", "spi-4") - - def configurationFile = new Properties() - configurationFile.put("property1", "cf-1") - configurationFile.put("property2", "cf-2") - configurationFile.put("property3", "cf-3") - - environmentVariables.set("property1", "env-1") - environmentVariables.set("property2", "env-2") - - when: - def config = ConfigInitializer.create(spiConfiguration, configurationFile) - - then: - config.getString("property1") == "env-1" - config.getString("property2") == "env-2" - config.getString("property3") == "cf-3" - config.getString("property4") == "spi-4" - } - - def "should use system properties (takes precedence over environment variables)"() { - given: - def spiConfiguration = new Properties() - spiConfiguration.put("property1", "spi-1") - spiConfiguration.put("property2", "spi-2") - spiConfiguration.put("property3", "spi-3") - spiConfiguration.put("property4", "spi-4") - - def configurationFile = new Properties() - configurationFile.put("property1", "cf-1") - configurationFile.put("property2", "cf-2") - configurationFile.put("property3", "cf-3") - - environmentVariables.set("property1", "env-1") - environmentVariables.set("property2", "env-2") - - System.setProperty("property1", "sp-1") - - when: - def config = ConfigInitializer.create(spiConfiguration, configurationFile) - - then: - config.getString("property1") == "sp-1" - config.getString("property2") == "env-2" - config.getString("property3") == "cf-3" - config.getString("property4") == "spi-4" - } - - def "should normalize property names"() { - given: - def spiConfiguration = new Properties() - spiConfiguration.put("otel.some-property.from-spi", "value") - - def configurationFile = new Properties() - configurationFile.put("otel.some-property.from-file", "value") - - environmentVariables.set("OTEL_SOME_ENV_VAR", "value") - - System.setProperty("otel.some-system-property", "value") - - when: - def config = ConfigInitializer.create(spiConfiguration, configurationFile) - - then: - config.getString("otel.some-property.from-spi") == "value" - config.getString("otel.some-property.from-file") == "value" - config.getString("otel.some-env-var") == "value" - config.getString("otel.some-system-property") == "value" - } -} diff --git a/javaagent-tooling/src/test/groovy/io/opentelemetry/javaagent/tooling/config/ConfigurationFileTest.groovy b/javaagent-tooling/src/test/groovy/io/opentelemetry/javaagent/tooling/config/ConfigurationFileTest.groovy index 1608661a63df..ca65c997a51c 100644 --- a/javaagent-tooling/src/test/groovy/io/opentelemetry/javaagent/tooling/config/ConfigurationFileTest.groovy +++ b/javaagent-tooling/src/test/groovy/io/opentelemetry/javaagent/tooling/config/ConfigurationFileTest.groovy @@ -25,10 +25,10 @@ class ConfigurationFileTest extends Specification { def "should use env property"() { given: def path = createFile("config", "property1=val-env") - environmentVariables.set(ConfigInitializer.CONFIGURATION_FILE_ENV_VAR, path) + environmentVariables.set("OTEL_JAVAAGENT_CONFIGURATION_FILE", path) when: - def properties = ConfigInitializer.loadConfigurationFile() + def properties = ConfigurationFileLoader.loadConfigFile() then: properties.get("property1") == "val-env" @@ -37,10 +37,10 @@ class ConfigurationFileTest extends Specification { def "should use system property"() { given: def path = createFile("config", "property1=val-sys") - System.setProperty(ConfigInitializer.CONFIGURATION_FILE_PROPERTY, path) + System.setProperty("otel.javaagent.configuration-file", path) when: - def properties = ConfigInitializer.loadConfigurationFile() + def properties = ConfigurationFileLoader.loadConfigFile() then: properties.get("property1") == "val-sys" @@ -51,11 +51,11 @@ class ConfigurationFileTest extends Specification { def pathEnv = createFile("configEnv", "property1=val-env") def pathSys = createFile("configSys", "property1=val-sys") - environmentVariables.set(ConfigInitializer.CONFIGURATION_FILE_ENV_VAR, pathEnv) - System.setProperty(ConfigInitializer.CONFIGURATION_FILE_PROPERTY, pathSys) + environmentVariables.set("OTEL_JAVAAGENT_CONFIGURATION_FILE", pathEnv) + System.setProperty("otel.javaagent.configuration-file", pathSys) when: - def properties = ConfigInitializer.loadConfigurationFile() + def properties = ConfigurationFileLoader.loadConfigFile() then: properties.get("property1") == "val-sys" @@ -64,21 +64,21 @@ class ConfigurationFileTest extends Specification { def "should return empty properties if file does not exist"() { given: - environmentVariables.set(ConfigInitializer.CONFIGURATION_FILE_ENV_VAR, "somePath") + environmentVariables.set("OTEL_JAVAAGENT_CONFIGURATION_FILE", "somePath") when: - def properties = ConfigInitializer.loadConfigurationFile() + def properties = ConfigurationFileLoader.loadConfigFile() then: - !properties.propertyNames().hasMoreElements() + properties.isEmpty() } def "should return empty properties if property is not set"() { when: - def properties = ConfigInitializer.loadConfigurationFile() + def properties = ConfigurationFileLoader.loadConfigFile() then: - !properties.propertyNames().hasMoreElements() + properties.isEmpty() } def createFile(String name, String contents) { diff --git a/javaagent-tooling/src/test/java/io/opentelemetry/javaagent/tooling/config/AgentConfigTest.java b/javaagent-tooling/src/test/java/io/opentelemetry/javaagent/tooling/config/AgentConfigTest.java index cd1bb8c41427..e4b36be6c51a 100644 --- a/javaagent-tooling/src/test/java/io/opentelemetry/javaagent/tooling/config/AgentConfigTest.java +++ b/javaagent-tooling/src/test/java/io/opentelemetry/javaagent/tooling/config/AgentConfigTest.java @@ -6,12 +6,11 @@ package io.opentelemetry.javaagent.tooling.config; import static java.util.Arrays.asList; -import static java.util.Collections.emptyList; -import static java.util.Collections.singletonList; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; -import io.opentelemetry.instrumentation.api.config.Config; -import java.util.List; +import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import java.util.TreeSet; import java.util.stream.Stream; import org.junit.jupiter.api.extension.ExtensionContext; @@ -22,60 +21,41 @@ class AgentConfigTest { - @ParameterizedTest - @ArgumentsSource(AgentDebugParams.class) - void shouldCheckIfAgentDebugModeIsEnabled(String propertyValue, boolean expected) { - Config config = Config.builder().addProperty("otel.javaagent.debug", propertyValue).build(); - AgentConfig agentConfig = new AgentConfig(config); - - assertEquals(expected, agentConfig.isDebugModeEnabled()); - } - - private static class AgentDebugParams implements ArgumentsProvider { - @Override - public Stream provideArguments(ExtensionContext context) { - return Stream.of( - Arguments.of("true", true), Arguments.of("blather", false), Arguments.of(null, false)); - } - } - - @ParameterizedTest + @ParameterizedTest(name = "isInstrumentationEnabled({0}) = {4}") @ArgumentsSource(InstrumentationEnabledParams.class) - void shouldCheckIfInstrumentationIsEnabled( - List names, boolean defaultEnabled, boolean expected) { - Config config = - Config.builder() - .addProperty("otel.instrumentation.order.enabled", "true") - .addProperty("otel.instrumentation.test-prop.enabled", "true") - .addProperty("otel.instrumentation.disabled-prop.enabled", "false") - .addProperty("otel.instrumentation.test-env.enabled", "true") - .addProperty("otel.instrumentation.disabled-env.enabled", "false") - .build(); - AgentConfig agentConfig = new AgentConfig(config); + void testIsInstrumentationEnabled( + @SuppressWarnings("unused") String description, + boolean firstEnabled, + boolean secondEnabled, + boolean defaultEnabled, + boolean expected) { + + ConfigProperties config = mock(ConfigProperties.class); + when(config.getBoolean("otel.instrumentation.first.enabled", defaultEnabled)) + .thenReturn(firstEnabled); + when(config.getBoolean("otel.instrumentation.second.enabled", defaultEnabled)) + .thenReturn(secondEnabled); assertEquals( - expected, agentConfig.isInstrumentationEnabled(new TreeSet<>(names), defaultEnabled)); + expected, + AgentConfig.isInstrumentationEnabled( + config, new TreeSet<>(asList("first", "second")), defaultEnabled)); } private static class InstrumentationEnabledParams implements ArgumentsProvider { + @Override public Stream provideArguments(ExtensionContext context) { return Stream.of( - Arguments.of(emptyList(), true, true), - Arguments.of(emptyList(), false, false), - Arguments.of(singletonList("invalid"), true, true), - Arguments.of(singletonList("invalid"), false, false), - Arguments.of(singletonList("test-prop"), false, true), - Arguments.of(singletonList("test-env"), false, true), - Arguments.of(singletonList("disabled-prop"), true, false), - Arguments.of(singletonList("disabled-env"), true, false), - Arguments.of(asList("other", "test-prop"), false, true), - Arguments.of(asList("other", "test-env"), false, true), - Arguments.of(singletonList("order"), false, true), - Arguments.of(asList("test-prop", "disabled-prop"), false, true), - Arguments.of(asList("disabled-env", "test-env"), false, true), - Arguments.of(asList("test-prop", "disabled-prop"), true, false), - Arguments.of(asList("disabled-env", "test-env"), true, false)); + Arguments.of( + "enabled by default, both instrumentations are off", false, false, true, false), + Arguments.of("enabled by default, one instrumentation is on", true, false, true, false), + Arguments.of("enabled by default, both instrumentations are on", true, true, true, true), + Arguments.of( + "disabled by default, both instrumentations are off", false, false, false, false), + Arguments.of("disabled by default, one instrumentation is on", true, false, false, true), + Arguments.of( + "disabled by default, both instrumentation are on", true, true, false, true)); } } } diff --git a/javaagent-tooling/src/test/java/io/opentelemetry/javaagent/tooling/config/ConfigurationFileLoaderTest.java b/javaagent-tooling/src/test/java/io/opentelemetry/javaagent/tooling/config/ConfigurationFileLoaderTest.java new file mode 100644 index 000000000000..acfc2cae9972 --- /dev/null +++ b/javaagent-tooling/src/test/java/io/opentelemetry/javaagent/tooling/config/ConfigurationFileLoaderTest.java @@ -0,0 +1,63 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.tooling.config; + +import static java.util.Collections.singleton; +import static java.util.Collections.singletonMap; +import static org.assertj.core.api.Assertions.assertThat; + +import io.opentelemetry.api.GlobalOpenTelemetry; +import io.opentelemetry.api.logs.GlobalLoggerProvider; +import io.opentelemetry.javaagent.tooling.OpenTelemetryInstaller; +import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk; +import io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizer; +import io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizerProvider; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +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.junitpioneer.jupiter.ClearSystemProperty; +import org.junitpioneer.jupiter.SetSystemProperty; + +@ClearSystemProperty(key = ConfigurationFileLoader.CONFIGURATION_FILE_PROPERTY) +class ConfigurationFileLoaderTest { + + @BeforeAll + @AfterAll + static void cleanUp() { + GlobalOpenTelemetry.resetForTest(); + GlobalLoggerProvider.resetForTest(); + } + + // regression for https://github.com/open-telemetry/opentelemetry-java-instrumentation/issues/6696 + @SetSystemProperty(key = "otel.experimental.sdk.enabled", value = "false") // don't setup the SDK + @Test + void fileConfigOverwritesUserPropertiesSupplier(@TempDir Path tempDir) throws IOException { + // given + Path configFile = tempDir.resolve("test-config.properties"); + Files.write(configFile, singleton("custom.key = 42")); + System.setProperty(ConfigurationFileLoader.CONFIGURATION_FILE_PROPERTY, configFile.toString()); + + // when + AutoConfiguredOpenTelemetrySdk autoConfiguredSdk = + OpenTelemetryInstaller.installOpenTelemetrySdk(); + + // then + assertThat(autoConfiguredSdk.getConfig().getString("custom.key")).isEqualTo("42"); + } + + // SPI used in test + public static class UserCustomPropertiesSupplier implements AutoConfigurationCustomizerProvider { + + @Override + public void customize(AutoConfigurationCustomizer autoConfiguration) { + autoConfiguration.addPropertiesSupplier(() -> singletonMap("custom.key", "123")); + } + } +} diff --git a/javaagent-tooling/src/test/java/io/opentelemetry/javaagent/tooling/ignore/UserExcludedClassesConfigurerTest.java b/javaagent-tooling/src/test/java/io/opentelemetry/javaagent/tooling/ignore/UserExcludedClassesConfigurerTest.java index cab2fe0d3e09..f67d015cbf4b 100644 --- a/javaagent-tooling/src/test/java/io/opentelemetry/javaagent/tooling/ignore/UserExcludedClassesConfigurerTest.java +++ b/javaagent-tooling/src/test/java/io/opentelemetry/javaagent/tooling/ignore/UserExcludedClassesConfigurerTest.java @@ -31,7 +31,7 @@ class UserExcludedClassesConfigurerTest { @Test void shouldAddNothingToBuilderWhenPropertyIsEmpty() { // when - underTest.configure(config, builder); + underTest.configure(builder, config); // then verifyNoInteractions(builder); @@ -40,14 +40,12 @@ void shouldAddNothingToBuilderWhenPropertyIsEmpty() { @Test void shouldIgnoreClassesAndPackages() { // given - // TODO: remove normalization after - // https://github.com/open-telemetry/opentelemetry-java/issues/4562 is fixed - when(config.getList(EXCLUDED_CLASSES_CONFIG.replace('-', '.'), emptyList())) + when(config.getList(EXCLUDED_CLASSES_CONFIG, emptyList())) .thenReturn( asList("com.example.IgnoredClass", "com.example.ignored.*", "com.another_ignore")); // when - underTest.configure(config, builder); + underTest.configure(builder, config); // then verify(builder).ignoreClass("com.example.IgnoredClass"); diff --git a/javaagent-tooling/src/test/resources/META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizerProvider b/javaagent-tooling/src/test/resources/META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizerProvider new file mode 100644 index 000000000000..0b873a5b7c51 --- /dev/null +++ b/javaagent-tooling/src/test/resources/META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizerProvider @@ -0,0 +1,2 @@ +io.opentelemetry.javaagent.tooling.config.ConfigurationFileLoader +io.opentelemetry.javaagent.tooling.config.ConfigurationFileLoaderTest$UserCustomPropertiesSupplier diff --git a/javaagent-tooling/src/testExceptionHandler/java/io/opentelemetry/javaagent/tooling/bytebuddy/ExceptionHandlerTest.java b/javaagent-tooling/src/testExceptionHandler/java/io/opentelemetry/javaagent/tooling/bytebuddy/ExceptionHandlerTest.java index 97d20284b92e..92dd4046ba69 100644 --- a/javaagent-tooling/src/testExceptionHandler/java/io/opentelemetry/javaagent/tooling/bytebuddy/ExceptionHandlerTest.java +++ b/javaagent-tooling/src/testExceptionHandler/java/io/opentelemetry/javaagent/tooling/bytebuddy/ExceptionHandlerTest.java @@ -119,6 +119,7 @@ void exceptionHandlerSetsCorrectStackSize() { } public static class SomeClass { + public static boolean isInstrumented() { return false; } @@ -136,5 +137,7 @@ public static void largeStack() { Object o = new Object(); System.out.println("large stack: " + l + ' ' + i + ' ' + d + ' ' + o); } + + private SomeClass() {} } } diff --git a/javaagent-tooling/src/testMissingType/java/io/opentelemetry/javaagent/tooling/bytebuddy/MissingTypeTest.java b/javaagent-tooling/src/testMissingType/java/io/opentelemetry/javaagent/tooling/bytebuddy/MissingTypeTest.java new file mode 100644 index 000000000000..2e1a974a732c --- /dev/null +++ b/javaagent-tooling/src/testMissingType/java/io/opentelemetry/javaagent/tooling/bytebuddy/MissingTypeTest.java @@ -0,0 +1,113 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.tooling.bytebuddy; + +import static net.bytebuddy.matcher.ElementMatchers.isMethod; +import static net.bytebuddy.matcher.ElementMatchers.named; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.fail; + +import com.google.common.base.Joiner; +import java.util.concurrent.atomic.AtomicBoolean; +import net.bytebuddy.ByteBuddy; +import net.bytebuddy.agent.ByteBuddyAgent; +import net.bytebuddy.agent.builder.AgentBuilder; +import net.bytebuddy.agent.builder.ResettableClassFileTransformer; +import net.bytebuddy.dynamic.ClassFileLocator; +import net.bytebuddy.dynamic.scaffold.MethodGraph; +import net.bytebuddy.utility.JavaModule; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +class MissingTypeTest { + private static final Logger logger = LoggerFactory.getLogger(MissingTypeTest.class); + + private static ResettableClassFileTransformer transformer; + private static final AtomicBoolean hasErrors = new AtomicBoolean(false); + + @BeforeAll + static void setUp() { + AgentBuilder builder = + new AgentBuilder.Default( + new ByteBuddy().with(MethodGraph.Compiler.ForDeclaredMethods.INSTANCE)) + .disableClassFormatChanges() + .with(new SafeTypeStrategy(AgentBuilder.TypeStrategy.Default.REDEFINE_FROZEN)) + .with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION) + .with( + new AgentBuilder.Listener.Adapter() { + @Override + public void onError( + String typeName, + ClassLoader classLoader, + JavaModule module, + boolean loaded, + Throwable throwable) { + logger.error("Transformation error", throwable); + hasErrors.set(true); + } + }) + .type(named(MissingTypeTest.class.getName() + "$SomeClass")) + .transform( + new AgentBuilder.Transformer.ForAdvice() + .with( + new AgentBuilder.LocationStrategy.Simple( + ClassFileLocator.ForClassLoader.of(TestAdvice.class.getClassLoader()))) + .advice(isMethod().and(named("isInstrumented")), TestAdvice.class.getName())); + + ByteBuddyAgent.install(); + transformer = builder.installOn(ByteBuddyAgent.getInstrumentation()); + } + + @AfterAll + static void tearDown() { + transformer.reset( + ByteBuddyAgent.getInstrumentation(), AgentBuilder.RedefinitionStrategy.RETRANSFORMATION); + } + + @Test + void guavaNotAvailable() { + // these tests expect com.google.common.base.Joiner to be missing from runtime class path + try { + Class.forName("com.google.common.base.Joiner"); + fail("guava should not be available during runtime"); + } catch (ClassNotFoundException exception) { + // ignore + } + } + + @Test + void instrumented() { + assertThat(SomeClass.isInstrumented()).isTrue(); + } + + @Test + void hasNoErrors() { + assertThat(hasErrors.get()).isFalse(); + } + + // com.google.common.base.Joiner is missing from runtime class path + @SuppressWarnings({"UnusedMethod", "MethodCanBeStatic"}) + private static class SomeClass { + public static boolean isInstrumented() { + return false; + } + + public void methodWithMissingParameterType(Joiner joiner) {} + + public Joiner methodWithMissingReturnType() { + return null; + } + + public static void staticMethodWithMissingParameterType(Joiner joiner) {} + + public static Joiner staticMethodWithMissingReturnType() { + return null; + } + } +} diff --git a/javaagent-tooling/src/testMissingType/java/io/opentelemetry/javaagent/tooling/bytebuddy/TestAdvice.java b/javaagent-tooling/src/testMissingType/java/io/opentelemetry/javaagent/tooling/bytebuddy/TestAdvice.java new file mode 100644 index 000000000000..892f7d46efdf --- /dev/null +++ b/javaagent-tooling/src/testMissingType/java/io/opentelemetry/javaagent/tooling/bytebuddy/TestAdvice.java @@ -0,0 +1,17 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.tooling.bytebuddy; + +import net.bytebuddy.asm.Advice; + +@SuppressWarnings("unused") +public class TestAdvice { + + @Advice.OnMethodExit + public static void returnTrue(@Advice.Return(readOnly = false) boolean returnVal) { + returnVal = true; + } +} diff --git a/javaagent/build.gradle.kts b/javaagent/build.gradle.kts index a47421e607dc..900b7a9cbb5e 100644 --- a/javaagent/build.gradle.kts +++ b/javaagent/build.gradle.kts @@ -30,20 +30,11 @@ val javaagentLibs by configurations.creating { extendsFrom(baseJavaagentLibs) } -// exclude javaagent dependencies from the bootstrap classpath -bootstrapLibs.run { - exclude("net.bytebuddy") - exclude("org.ow2.asm") - exclude("io.opentelemetry", "opentelemetry-sdk") - exclude("io.opentelemetry", "opentelemetry-sdk-extension-autoconfigure") - exclude("io.opentelemetry", "opentelemetry-sdk-extension-autoconfigure-spi") -} - // exclude dependencies that are to be placed in bootstrap from agent libs - they won't be added to inst/ listOf(baseJavaagentLibs, javaagentLibs).forEach { it.run { - exclude("org.slf4j") exclude("io.opentelemetry", "opentelemetry-api") + exclude("io.opentelemetry", "opentelemetry-api-logs") exclude("io.opentelemetry", "opentelemetry-semconv") } } @@ -54,22 +45,30 @@ val licenseReportDependencies by configurations.creating { dependencies { bootstrapLibs(project(":instrumentation-api")) + // opentelemetry-api is an api dependency of :instrumentation-api, but opentelemetry-api-logs is not + bootstrapLibs("io.opentelemetry:opentelemetry-api-logs") bootstrapLibs(project(":instrumentation-api-semconv")) bootstrapLibs(project(":instrumentation-annotations-support")) - bootstrapLibs(project(":instrumentation-appender-api-internal")) bootstrapLibs(project(":javaagent-bootstrap")) // extension-api contains both bootstrap packages and agent packages - bootstrapLibs(project(":javaagent-extension-api")) + bootstrapLibs(project(":javaagent-extension-api")) { + // exclude javaagent dependencies from the bootstrap classpath + exclude("net.bytebuddy") + exclude("org.ow2.asm") + exclude("io.opentelemetry", "opentelemetry-sdk") + exclude("io.opentelemetry", "opentelemetry-sdk-extension-autoconfigure") + exclude("io.opentelemetry", "opentelemetry-sdk-extension-autoconfigure-spi") + } baseJavaagentLibs(project(":javaagent-extension-api")) baseJavaagentLibs(project(":javaagent-tooling")) + baseJavaagentLibs(project(":javaagent-internal-logging-simple", configuration = "shadow")) baseJavaagentLibs(project(":muzzle")) - // TODO (trask) replace with opentelemetry-instrumentation-annotations - baseJavaagentLibs(project(":instrumentation:opentelemetry-extension-annotations-1.0:javaagent")) baseJavaagentLibs(project(":instrumentation:opentelemetry-api:opentelemetry-api-1.0:javaagent")) baseJavaagentLibs(project(":instrumentation:opentelemetry-api:opentelemetry-api-1.4:javaagent")) baseJavaagentLibs(project(":instrumentation:opentelemetry-instrumentation-api:javaagent")) + baseJavaagentLibs(project(":instrumentation:opentelemetry-instrumentation-annotations-1.16:javaagent")) baseJavaagentLibs(project(":instrumentation:executors:javaagent")) baseJavaagentLibs(project(":instrumentation:internal:internal-class-loader:javaagent")) baseJavaagentLibs(project(":instrumentation:internal:internal-eclipse-osgi-3.6:javaagent")) @@ -84,6 +83,7 @@ dependencies { // in case there are dependencies (accidentally) pulled in by instrumentation modules // but I couldn't get that to work licenseReportDependencies(project(":javaagent-tooling")) + licenseReportDependencies(project(":javaagent-internal-logging-simple")) licenseReportDependencies(project(":javaagent-extension-api")) testCompileOnly(project(":javaagent-bootstrap")) @@ -111,6 +111,12 @@ project(":instrumentation").subprojects { add(javaagentLibs.name, project(subProj.path)) } } + + plugins.withId("otel.sdk-extension") { + javaagentDependencies.run { + add(javaagentLibs.name, project(subProj.path)) + } + } } tasks { @@ -257,9 +263,17 @@ licenseReport { configurations = arrayOf(licenseReportDependencies.name) + excludeBoms = true + excludeGroups = arrayOf( - "io.opentelemetry.instrumentation", - "io.opentelemetry.javaagent" + "io\\.opentelemetry\\.instrumentation", + "io\\.opentelemetry\\.javaagent", + "io\\.opentelemetry\\.dummy\\..*" + ) + + excludes = arrayOf( + "io.opentelemetry:opentelemetry-bom-alpha", + "opentelemetry-java-instrumentation:dependencyManagement" ) filters = arrayOf(LicenseBundleNormalizer("$projectDir/license-normalizer-bundle.json", true)) @@ -284,7 +298,6 @@ fun ShadowJar.excludeBootstrapClasses() { exclude(project(":instrumentation-api")) exclude(project(":instrumentation-api-semconv")) exclude(project(":instrumentation-annotations-support")) - exclude(project(":instrumentation-appender-api-internal")) exclude(project(":javaagent-bootstrap")) } diff --git a/javaagent/src/test/java/io/opentelemetry/javaagent/ClassToInstrument.java b/javaagent/src/test/java/io/opentelemetry/javaagent/ClassToInstrument.java index 38f9d2ea52cc..270e92d4c283 100644 --- a/javaagent/src/test/java/io/opentelemetry/javaagent/ClassToInstrument.java +++ b/javaagent/src/test/java/io/opentelemetry/javaagent/ClassToInstrument.java @@ -8,6 +8,9 @@ import io.opentracing.contrib.dropwizard.Trace; public class ClassToInstrument { + @Trace public static void someMethod() {} + + protected ClassToInstrument() {} } diff --git a/javaagent/src/test/java/io/opentelemetry/javaagent/IntegrationTestUtils.java b/javaagent/src/test/java/io/opentelemetry/javaagent/IntegrationTestUtils.java index 44bf92f39d96..9bca43c049f3 100644 --- a/javaagent/src/test/java/io/opentelemetry/javaagent/IntegrationTestUtils.java +++ b/javaagent/src/test/java/io/opentelemetry/javaagent/IntegrationTestUtils.java @@ -261,4 +261,6 @@ public void run() { } } } + + private IntegrationTestUtils() {} } diff --git a/javaagent/src/test/java/jvmbootstraptest/AgentLoadedChecker.java b/javaagent/src/test/java/jvmbootstraptest/AgentLoadedChecker.java index 996a6d731d68..3f2977513fdf 100644 --- a/javaagent/src/test/java/jvmbootstraptest/AgentLoadedChecker.java +++ b/javaagent/src/test/java/jvmbootstraptest/AgentLoadedChecker.java @@ -9,6 +9,7 @@ import java.net.URLClassLoader; public class AgentLoadedChecker { + public static void main(String[] args) throws ClassNotFoundException { // Empty class loader that delegates to bootstrap URLClassLoader emptyClassLoader = new URLClassLoader(new URL[] {}, null); @@ -20,4 +21,6 @@ public static void main(String[] args) throws ClassNotFoundException { "Agent loaded into class loader other than bootstrap: " + agentClass.getClassLoader()); } } + + private AgentLoadedChecker() {} } diff --git a/javaagent/src/test/java/jvmbootstraptest/LogLevelChecker.java b/javaagent/src/test/java/jvmbootstraptest/LogLevelChecker.java index 842644a58e95..644d8a095814 100644 --- a/javaagent/src/test/java/jvmbootstraptest/LogLevelChecker.java +++ b/javaagent/src/test/java/jvmbootstraptest/LogLevelChecker.java @@ -6,6 +6,7 @@ package jvmbootstraptest; public class LogLevelChecker { + // returns an exception if logs are not in DEBUG public static void main(String[] args) { @@ -16,4 +17,6 @@ public static void main(String[] args) { throw new IllegalStateException("debug mode not set"); } } + + private LogLevelChecker() {} } diff --git a/javaagent/src/test/java/jvmbootstraptest/MyClassLoaderIsNotBootstrap.java b/javaagent/src/test/java/jvmbootstraptest/MyClassLoaderIsNotBootstrap.java index 98622b4721f2..2a2578072293 100644 --- a/javaagent/src/test/java/jvmbootstraptest/MyClassLoaderIsNotBootstrap.java +++ b/javaagent/src/test/java/jvmbootstraptest/MyClassLoaderIsNotBootstrap.java @@ -6,10 +6,13 @@ package jvmbootstraptest; public class MyClassLoaderIsNotBootstrap { + public static void main(String[] args) { if (MyClassLoaderIsNotBootstrap.class.getClassLoader() == null) { throw new IllegalStateException( "Application level class was loaded by the bootstrap class loader"); } } + + private MyClassLoaderIsNotBootstrap() {} } diff --git a/licenses/byte-buddy-dep-1.12.9.jar/META-INF/LICENSE b/licenses/byte-buddy-dep-1.12.18.jar/META-INF/LICENSE similarity index 100% rename from licenses/byte-buddy-dep-1.12.9.jar/META-INF/LICENSE rename to licenses/byte-buddy-dep-1.12.18.jar/META-INF/LICENSE diff --git a/licenses/byte-buddy-dep-1.12.9.jar/META-INF/NOTICE b/licenses/byte-buddy-dep-1.12.18.jar/META-INF/NOTICE similarity index 100% rename from licenses/byte-buddy-dep-1.12.9.jar/META-INF/NOTICE rename to licenses/byte-buddy-dep-1.12.18.jar/META-INF/NOTICE diff --git a/licenses/jackson-core-2.13.2.jar/META-INF/LICENSE b/licenses/jackson-core-2.13.4.jar/META-INF/LICENSE similarity index 100% rename from licenses/jackson-core-2.13.2.jar/META-INF/LICENSE rename to licenses/jackson-core-2.13.4.jar/META-INF/LICENSE diff --git a/licenses/jackson-core-2.13.2.jar/META-INF/NOTICE b/licenses/jackson-core-2.13.4.jar/META-INF/NOTICE similarity index 100% rename from licenses/jackson-core-2.13.2.jar/META-INF/NOTICE rename to licenses/jackson-core-2.13.4.jar/META-INF/NOTICE diff --git a/licenses/jackson-jr-objects-2.13.2.jar/META-INF/LICENSE b/licenses/jackson-jr-objects-2.13.4.jar/META-INF/LICENSE similarity index 100% rename from licenses/jackson-jr-objects-2.13.2.jar/META-INF/LICENSE rename to licenses/jackson-jr-objects-2.13.4.jar/META-INF/LICENSE diff --git a/licenses/jackson-jr-objects-2.13.2.jar/META-INF/NOTICE b/licenses/jackson-jr-objects-2.13.4.jar/META-INF/NOTICE similarity index 100% rename from licenses/jackson-jr-objects-2.13.2.jar/META-INF/NOTICE rename to licenses/jackson-jr-objects-2.13.4.jar/META-INF/NOTICE diff --git a/licenses/licenses.md b/licenses/licenses.md index 80199d50e231..57faf575b786 100644 --- a/licenses/licenses.md +++ b/licenses/licenses.md @@ -1,7 +1,7 @@ #javaagent ##Dependency License Report -_2022-04-08 05:12:23 UTC_ +_2022-10-11 13:01:17 PDT_ ## Apache License, Version 2.0 **1** **Group:** `com.blogspot.mydailyjava` **Name:** `weak-lock-free` **Version:** `0.18` @@ -9,239 +9,216 @@ _2022-04-08 05:12:23 UTC_ > - **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) -**2** **Group:** `com.fasterxml.jackson.core` **Name:** `jackson-core` **Version:** `2.13.2` +**2** **Group:** `com.fasterxml.jackson.core` **Name:** `jackson-core` **Version:** `2.13.4` > - **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.13.2.jar/META-INF/LICENSE](jackson-core-2.13.2.jar/META-INF/LICENSE) - - [jackson-core-2.13.2.jar/META-INF/NOTICE](jackson-core-2.13.2.jar/META-INF/NOTICE) +> - **Embedded license files**: [jackson-core-2.13.4.jar/META-INF/LICENSE](jackson-core-2.13.4.jar/META-INF/LICENSE) + - [jackson-core-2.13.4.jar/META-INF/NOTICE](jackson-core-2.13.4.jar/META-INF/NOTICE) -**3** **Group:** `com.fasterxml.jackson.jr` **Name:** `jackson-jr-objects` **Version:** `2.13.2` +**3** **Group:** `com.fasterxml.jackson.jr` **Name:** `jackson-jr-objects` **Version:** `2.13.4` > - **Project URL**: [http://wiki.fasterxml.com/JacksonHome](http://wiki.fasterxml.com/JacksonHome) > - **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-jr-objects-2.13.2.jar/META-INF/LICENSE](jackson-jr-objects-2.13.2.jar/META-INF/LICENSE) - - [jackson-jr-objects-2.13.2.jar/META-INF/NOTICE](jackson-jr-objects-2.13.2.jar/META-INF/NOTICE) +> - **Embedded license files**: [jackson-jr-objects-2.13.4.jar/META-INF/LICENSE](jackson-jr-objects-2.13.4.jar/META-INF/LICENSE) + - [jackson-jr-objects-2.13.4.jar/META-INF/NOTICE](jackson-jr-objects-2.13.4.jar/META-INF/NOTICE) **4** **Group:** `com.googlecode.concurrentlinkedhashmap` **Name:** `concurrentlinkedhashmap-lru` **Version:** `1.4.2` > - **Manifest License**: Apache License, Version 2.0 (Not Packaged) > - **POM Project URL**: [http://code.google.com/p/concurrentlinkedhashmap](http://code.google.com/p/concurrentlinkedhashmap) > - **POM License**: Apache License, Version 2.0 - [http://www.apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0) -**5** **Group:** `com.squareup.okhttp3` **Name:** `okhttp` **Version:** `4.9.3` +**5** **Group:** `com.squareup.okhttp3` **Name:** `okhttp` **Version:** `4.10.0` > - **POM Project URL**: [https://square.github.io/okhttp/](https://square.github.io/okhttp/) > - **POM License**: Apache License, Version 2.0 - [http://www.apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0) -> - **Embedded license files**: [okhttp-4.9.3.jar/okhttp3/internal/publicsuffix/NOTICE](okhttp-4.9.3.jar/okhttp3/internal/publicsuffix/NOTICE) +> - **Embedded license files**: [okhttp-4.10.0.jar/okhttp3/internal/publicsuffix/NOTICE](okhttp-4.10.0.jar/okhttp3/internal/publicsuffix/NOTICE) -**6** **Group:** `com.squareup.okio` **Name:** `okio` **Version:** `2.8.0` +**6** **Group:** `com.squareup.okio` **Name:** `okio-jvm` **Version:** `3.0.0` > - **POM Project URL**: [https://github.com/square/okio/](https://github.com/square/okio/) > - **POM License**: Apache License, Version 2.0 - [http://www.apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0) -**7** **Group:** `io.opentelemetry` **Name:** `opentelemetry-api` **Version:** `1.12.0` +**7** **Group:** `io.opentelemetry` **Name:** `opentelemetry-api` **Version:** `1.19.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) -**8** **Group:** `io.opentelemetry` **Name:** `opentelemetry-context` **Version:** `1.12.0` +**8** **Group:** `io.opentelemetry` **Name:** `opentelemetry-api-logs` **Version:** `1.19.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) -**9** **Group:** `io.opentelemetry` **Name:** `opentelemetry-exporter-jaeger` **Version:** `1.12.0` +**9** **Group:** `io.opentelemetry` **Name:** `opentelemetry-context` **Version:** `1.19.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) - -**10** **Group:** `io.opentelemetry` **Name:** `opentelemetry-exporter-logging` **Version:** `1.12.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) - -**11** **Group:** `io.opentelemetry` **Name:** `opentelemetry-exporter-logging-otlp` **Version:** `1.12.0` -> - **POM Project URL**: [https://github.com/open-telemetry/opentelemetry-java](https://github.com/open-telemetry/opentelemetry-java) -> - **POM License**: Apache License, Version 2.0 - [http://www.apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0) - -**12** **Group:** `io.opentelemetry` **Name:** `opentelemetry-exporter-otlp` **Version:** `1.12.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) - -**13** **Group:** `io.opentelemetry` **Name:** `opentelemetry-exporter-otlp-common` **Version:** `1.12.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) +> - **POM License**: Apache License, Version 2.0 - [https://www.apache.org/licenses/LICENSE-2.0](https://www.apache.org/licenses/LICENSE-2.0) -**14** **Group:** `io.opentelemetry` **Name:** `opentelemetry-exporter-otlp-http-logs` **Version:** `1.12.0-alpha` +**10** **Group:** `io.opentelemetry` **Name:** `opentelemetry-exporter-common` **Version:** `1.19.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) -**15** **Group:** `io.opentelemetry` **Name:** `opentelemetry-exporter-otlp-http-metrics` **Version:** `1.12.0-alpha` +**11** **Group:** `io.opentelemetry` **Name:** `opentelemetry-exporter-jaeger` **Version:** `1.19.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-exporter-otlp-http-trace` **Version:** `1.12.0` +**12** **Group:** `io.opentelemetry` **Name:** `opentelemetry-exporter-logging` **Version:** `1.19.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-otlp-logs` **Version:** `1.12.0-alpha` +**13** **Group:** `io.opentelemetry` **Name:** `opentelemetry-exporter-logging-otlp` **Version:** `1.19.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) -**18** **Group:** `io.opentelemetry` **Name:** `opentelemetry-exporter-otlp-metrics` **Version:** `1.12.0-alpha` +**14** **Group:** `io.opentelemetry` **Name:** `opentelemetry-exporter-otlp` **Version:** `1.19.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) -**19** **Group:** `io.opentelemetry` **Name:** `opentelemetry-exporter-otlp-trace` **Version:** `1.12.0` +**15** **Group:** `io.opentelemetry` **Name:** `opentelemetry-exporter-otlp-common` **Version:** `1.19.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) -**20** **Group:** `io.opentelemetry` **Name:** `opentelemetry-exporter-prometheus` **Version:** `1.12.0-alpha` +**16** **Group:** `io.opentelemetry` **Name:** `opentelemetry-exporter-otlp-logs` **Version:** `1.19.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) -**21** **Group:** `io.opentelemetry` **Name:** `opentelemetry-exporter-zipkin` **Version:** `1.12.0` +**17** **Group:** `io.opentelemetry` **Name:** `opentelemetry-exporter-prometheus` **Version:** `1.19.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) -**22** **Group:** `io.opentelemetry` **Name:** `opentelemetry-extension-aws` **Version:** `1.12.0` +**18** **Group:** `io.opentelemetry` **Name:** `opentelemetry-exporter-zipkin` **Version:** `1.19.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) -**23** **Group:** `io.opentelemetry` **Name:** `opentelemetry-extension-kotlin` **Version:** `1.12.0` +**19** **Group:** `io.opentelemetry` **Name:** `opentelemetry-extension-aws` **Version:** `1.19.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) -**24** **Group:** `io.opentelemetry` **Name:** `opentelemetry-extension-noop-api` **Version:** `1.12.0-alpha` +**20** **Group:** `io.opentelemetry` **Name:** `opentelemetry-extension-kotlin` **Version:** `1.19.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) -**25** **Group:** `io.opentelemetry` **Name:** `opentelemetry-extension-trace-propagators` **Version:** `1.12.0` +**21** **Group:** `io.opentelemetry` **Name:** `opentelemetry-extension-trace-propagators` **Version:** `1.19.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) -**26** **Group:** `io.opentelemetry` **Name:** `opentelemetry-sdk` **Version:** `1.12.0` +**22** **Group:** `io.opentelemetry` **Name:** `opentelemetry-sdk` **Version:** `1.19.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) -**27** **Group:** `io.opentelemetry` **Name:** `opentelemetry-sdk-common` **Version:** `1.12.0` +**23** **Group:** `io.opentelemetry` **Name:** `opentelemetry-sdk-common` **Version:** `1.19.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) -**28** **Group:** `io.opentelemetry` **Name:** `opentelemetry-sdk-extension-autoconfigure` **Version:** `1.12.0-alpha` +**24** **Group:** `io.opentelemetry` **Name:** `opentelemetry-sdk-extension-autoconfigure` **Version:** `1.19.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) -**29** **Group:** `io.opentelemetry` **Name:** `opentelemetry-sdk-extension-autoconfigure-spi` **Version:** `1.12.0` +**25** **Group:** `io.opentelemetry` **Name:** `opentelemetry-sdk-extension-autoconfigure-spi` **Version:** `1.19.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) -**30** **Group:** `io.opentelemetry` **Name:** `opentelemetry-sdk-extension-jaeger-remote-sampler` **Version:** `1.12.0` +**26** **Group:** `io.opentelemetry` **Name:** `opentelemetry-sdk-extension-incubator` **Version:** `1.19.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) -**31** **Group:** `io.opentelemetry` **Name:** `opentelemetry-sdk-extension-resources` **Version:** `1.12.0` +**27** **Group:** `io.opentelemetry` **Name:** `opentelemetry-sdk-extension-jaeger-remote-sampler` **Version:** `1.19.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) -**32** **Group:** `io.opentelemetry` **Name:** `opentelemetry-sdk-logs` **Version:** `1.12.0-alpha` +**28** **Group:** `io.opentelemetry` **Name:** `opentelemetry-sdk-logs` **Version:** `1.19.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) -**33** **Group:** `io.opentelemetry` **Name:** `opentelemetry-sdk-metrics` **Version:** `1.12.0-alpha` +**29** **Group:** `io.opentelemetry` **Name:** `opentelemetry-sdk-metrics` **Version:** `1.19.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) -**34** **Group:** `io.opentelemetry` **Name:** `opentelemetry-sdk-trace` **Version:** `1.12.0` +**30** **Group:** `io.opentelemetry` **Name:** `opentelemetry-sdk-trace` **Version:** `1.19.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) -**35** **Group:** `io.opentelemetry` **Name:** `opentelemetry-semconv` **Version:** `1.12.0-alpha` +**31** **Group:** `io.opentelemetry` **Name:** `opentelemetry-semconv` **Version:** `1.19.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) -**36** **Group:** `io.prometheus` **Name:** `simpleclient` **Version:** `0.14.1` -> - **Manifest License**: Apache License, Version 2.0 (Not Packaged) -> - **POM License**: Apache License, Version 2.0 - [http://www.apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0) - -**37** **Group:** `io.prometheus` **Name:** `simpleclient_common` **Version:** `0.14.1` -> - **Manifest License**: Apache License, Version 2.0 (Not Packaged) -> - **POM License**: Apache License, Version 2.0 - [http://www.apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0) - -**38** **Group:** `io.prometheus` **Name:** `simpleclient_tracer_common` **Version:** `0.14.1` -> - **Manifest License**: Apache License, Version 2.0 (Not Packaged) -> - **POM License**: Apache License, Version 2.0 - [http://www.apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0) - -**39** **Group:** `io.prometheus` **Name:** `simpleclient_tracer_otel` **Version:** `0.14.1` -> - **Manifest License**: Apache License, Version 2.0 (Not Packaged) -> - **POM License**: Apache License, Version 2.0 - [http://www.apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0) - -**40** **Group:** `io.prometheus` **Name:** `simpleclient_tracer_otel_agent` **Version:** `0.14.1` -> - **Manifest License**: Apache License, Version 2.0 (Not Packaged) -> - **POM License**: Apache License, Version 2.0 - [http://www.apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0) - -**41** **Group:** `io.zipkin.reporter2` **Name:** `zipkin-reporter` **Version:** `2.16.3` +**32** **Group:** `io.zipkin.reporter2` **Name:** `zipkin-reporter` **Version:** `2.16.3` > - **Manifest Project URL**: [https://zipkin.io/](https://zipkin.io/) > - **Manifest License**: Apache License, Version 2.0 (Not Packaged) > - **POM License**: Apache License, Version 2.0 - [http://www.apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0) > - **Embedded license files**: [zipkin-reporter-2.16.3.jar/META-INF/LICENSE](zipkin-reporter-2.16.3.jar/META-INF/LICENSE) -**42** **Group:** `io.zipkin.reporter2` **Name:** `zipkin-sender-okhttp3` **Version:** `2.16.3` +**33** **Group:** `io.zipkin.reporter2` **Name:** `zipkin-sender-okhttp3` **Version:** `2.16.3` > - **Manifest Project URL**: [https://zipkin.io/](https://zipkin.io/) > - **Manifest License**: Apache License, Version 2.0 (Not Packaged) > - **POM License**: Apache License, Version 2.0 - [http://www.apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0) > - **Embedded license files**: [zipkin-sender-okhttp3-2.16.3.jar/META-INF/LICENSE](zipkin-sender-okhttp3-2.16.3.jar/META-INF/LICENSE) -**43** **Group:** `io.zipkin.zipkin2` **Name:** `zipkin` **Version:** `2.23.2` +**34** **Group:** `io.zipkin.zipkin2` **Name:** `zipkin` **Version:** `2.23.2` > - **Manifest Project URL**: [http://zipkin.io/](http://zipkin.io/) > - **Manifest License**: Apache License, Version 2.0 (Not Packaged) > - **POM License**: Apache License, Version 2.0 - [http://www.apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0) > - **Embedded license files**: [zipkin-2.23.2.jar/META-INF/LICENSE](zipkin-2.23.2.jar/META-INF/LICENSE) -**44** **Group:** `net.bytebuddy` **Name:** `byte-buddy-dep` **Version:** `1.12.9` +**35** **Group:** `net.bytebuddy` **Name:** `byte-buddy-dep` **Version:** `1.12.18` > - **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.12.9.jar/META-INF/LICENSE](byte-buddy-dep-1.12.9.jar/META-INF/LICENSE) - - [byte-buddy-dep-1.12.9.jar/META-INF/NOTICE](byte-buddy-dep-1.12.9.jar/META-INF/NOTICE) +> - **Embedded license files**: [byte-buddy-dep-1.12.18.jar/META-INF/LICENSE](byte-buddy-dep-1.12.18.jar/META-INF/LICENSE) + - [byte-buddy-dep-1.12.18.jar/META-INF/NOTICE](byte-buddy-dep-1.12.18.jar/META-INF/NOTICE) -**45** **Group:** `org.jetbrains` **Name:** `annotations` **Version:** `13.0` +**36** **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 - [http://www.apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0) -**46** **Group:** `org.jetbrains.kotlin` **Name:** `kotlin-stdlib` **Version:** `1.5.31` +**37** **Group:** `org.jetbrains.kotlin` **Name:** `kotlin-stdlib` **Version:** `1.6.20` +> - **POM Project URL**: [https://kotlinlang.org/](https://kotlinlang.org/) +> - **POM License**: Apache License, Version 2.0 - [http://www.apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0) + +**38** **Group:** `org.jetbrains.kotlin` **Name:** `kotlin-stdlib-common` **Version:** `1.6.20` > - **POM Project URL**: [https://kotlinlang.org/](https://kotlinlang.org/) > - **POM License**: Apache License, Version 2.0 - [http://www.apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0) -**47** **Group:** `org.jetbrains.kotlin` **Name:** `kotlin-stdlib-common` **Version:** `1.5.31` +**39** **Group:** `org.jetbrains.kotlin` **Name:** `kotlin-stdlib-jdk7` **Version:** `1.6.10` > - **POM Project URL**: [https://kotlinlang.org/](https://kotlinlang.org/) > - **POM License**: Apache License, Version 2.0 - [http://www.apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0) -**48** **Group:** `org.ow2.asm` **Name:** `asm` **Version:** `9.3` +**40** **Group:** `org.jetbrains.kotlin` **Name:** `kotlin-stdlib-jdk8` **Version:** `1.6.10` +> - **POM Project URL**: [https://kotlinlang.org/](https://kotlinlang.org/) +> - **POM License**: Apache License, Version 2.0 - [http://www.apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0) + +**41** **Group:** `org.ow2.asm` **Name:** `asm` **Version:** `9.4` > - **Manifest Project URL**: [http://asm.ow2.org](http://asm.ow2.org) > - **Manifest License**: The 3-Clause BSD License (Not Packaged) > - **POM Project URL**: [http://asm.ow2.io/](http://asm.ow2.io/) > - **POM License**: Apache License, Version 2.0 - [http://www.apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0) > - **POM License**: The 3-Clause BSD License - [https://opensource.org/licenses/BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) -**49** **Group:** `org.ow2.asm` **Name:** `asm-commons` **Version:** `9.3` +**42** **Group:** `org.ow2.asm` **Name:** `asm-commons` **Version:** `9.4` > - **Manifest Project URL**: [http://asm.ow2.org](http://asm.ow2.org) > - **Manifest License**: The 3-Clause BSD License (Not Packaged) > - **POM Project URL**: [http://asm.ow2.io/](http://asm.ow2.io/) > - **POM License**: Apache License, Version 2.0 - [http://www.apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0) > - **POM License**: The 3-Clause BSD License - [https://opensource.org/licenses/BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) +**43** **Group:** `org.yaml` **Name:** `snakeyaml` **Version:** `1.33` +> - **Manifest License**: Apache License, Version 2.0 (Not Packaged) +> - **POM Project URL**: [https://bitbucket.org/snakeyaml/snakeyaml](https://bitbucket.org/snakeyaml/snakeyaml) +> - **POM License**: Apache License, Version 2.0 - [https://www.apache.org/licenses/LICENSE-2.0](https://www.apache.org/licenses/LICENSE-2.0) + ## MIT License -**50** **Group:** `org.slf4j` **Name:** `slf4j-api` **Version:** `1.7.36` +**44** **Group:** `org.slf4j` **Name:** `slf4j-api` **Version:** `2.0.2` > - **POM Project URL**: [http://www.slf4j.org](http://www.slf4j.org) > - **POM License**: MIT License - [https://opensource.org/licenses/MIT](https://opensource.org/licenses/MIT) -**51** **Group:** `org.slf4j` **Name:** `slf4j-simple` **Version:** `1.7.36` +**45** **Group:** `org.slf4j` **Name:** `slf4j-simple` **Version:** `2.0.2` > - **POM Project URL**: [http://www.slf4j.org](http://www.slf4j.org) > - **POM License**: MIT License - [https://opensource.org/licenses/MIT](https://opensource.org/licenses/MIT) ## The 3-Clause BSD License -**52** **Group:** `org.ow2.asm` **Name:** `asm` **Version:** `9.3` +**46** **Group:** `org.ow2.asm` **Name:** `asm` **Version:** `9.4` > - **Manifest Project URL**: [http://asm.ow2.org](http://asm.ow2.org) > - **Manifest License**: The 3-Clause BSD License (Not Packaged) > - **POM Project URL**: [http://asm.ow2.io/](http://asm.ow2.io/) > - **POM License**: Apache License, Version 2.0 - [http://www.apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0) > - **POM License**: The 3-Clause BSD License - [https://opensource.org/licenses/BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) -**53** **Group:** `org.ow2.asm` **Name:** `asm-commons` **Version:** `9.3` +**47** **Group:** `org.ow2.asm` **Name:** `asm-commons` **Version:** `9.4` > - **Manifest Project URL**: [http://asm.ow2.org](http://asm.ow2.org) > - **Manifest License**: The 3-Clause BSD License (Not Packaged) > - **POM Project URL**: [http://asm.ow2.io/](http://asm.ow2.io/) @@ -250,30 +227,6 @@ _2022-04-08 05:12:23 UTC_ ## Unknown -**54** **Group:** `com.fasterxml.jackson` **Name:** `jackson-bom` **Version:** `2.13.2.20220328` - -**55** **Group:** `com.google.guava` **Name:** `guava-bom` **Version:** `31.1-jre` - -**56** **Group:** `io.opentelemetry` **Name:** `opentelemetry-bom` **Version:** `1.12.0` - -**57** **Group:** `io.opentelemetry` **Name:** `opentelemetry-bom-alpha` **Version:** `1.12.0-alpha` - -**58** **Group:** `io.opentelemetry.jaxrs-common` **Name:** `bootstrap` **Version:** `1.13.0-alpha-SNAPSHOT` - -**59** **Group:** `io.opentelemetry.kafka-clients-0.11` **Name:** `bootstrap` **Version:** `1.13.0-alpha-SNAPSHOT` - -**60** **Group:** `io.opentelemetry.rmi` **Name:** `bootstrap` **Version:** `1.13.0-alpha-SNAPSHOT` - -**61** **Group:** `io.opentelemetry.servlet-common` **Name:** `bootstrap` **Version:** `1.13.0-alpha-SNAPSHOT` - -**62** **Group:** `io.opentelemetry.undertow-1.4` **Name:** `bootstrap` **Version:** `1.13.0-alpha-SNAPSHOT` - -**63** **Group:** `opentelemetry-java-instrumentation` **Name:** `dependencyManagement` **Version:** `1.13.0-alpha-SNAPSHOT` - -**64** **Group:** `org.apache.groovy` **Name:** `groovy-bom` **Version:** `4.0.1` - -**65** **Group:** `org.jetbrains.kotlin` **Name:** `kotlin-bom` **Version:** `1.5.31` - -**66** **Group:** `org.junit` **Name:** `junit-bom` **Version:** `5.8.2` +**48** **Group:** `com.squareup.okio` **Name:** `okio` **Version:** `3.0.0` diff --git a/licenses/okhttp-4.9.3.jar/okhttp3/internal/publicsuffix/NOTICE b/licenses/okhttp-4.10.0.jar/okhttp3/internal/publicsuffix/NOTICE similarity index 100% rename from licenses/okhttp-4.9.3.jar/okhttp3/internal/publicsuffix/NOTICE rename to licenses/okhttp-4.10.0.jar/okhttp3/internal/publicsuffix/NOTICE diff --git a/muzzle/src/main/java/io/opentelemetry/javaagent/tooling/HelperInjector.java b/muzzle/src/main/java/io/opentelemetry/javaagent/tooling/HelperInjector.java index b652a9753125..8181fb6315ad 100644 --- a/muzzle/src/main/java/io/opentelemetry/javaagent/tooling/HelperInjector.java +++ b/muzzle/src/main/java/io/opentelemetry/javaagent/tooling/HelperInjector.java @@ -17,6 +17,7 @@ import java.lang.instrument.Instrumentation; import java.net.URL; import java.nio.file.Files; +import java.security.ProtectionDomain; import java.security.SecureClassLoader; import java.util.Collection; import java.util.Collections; @@ -167,7 +168,8 @@ public DynamicType.Builder transform( DynamicType.Builder builder, TypeDescription typeDescription, ClassLoader classLoader, - JavaModule module) { + JavaModule javaModule, + ProtectionDomain protectionDomain) { if (!helperClassNames.isEmpty()) { injectHelperClasses(typeDescription, classLoader); } diff --git a/muzzle/src/main/java/io/opentelemetry/javaagent/tooling/muzzle/AgentCachingPoolStrategy.java b/muzzle/src/main/java/io/opentelemetry/javaagent/tooling/muzzle/AgentCachingPoolStrategy.java index 1da3ef7cbb10..4e8191514dff 100644 --- a/muzzle/src/main/java/io/opentelemetry/javaagent/tooling/muzzle/AgentCachingPoolStrategy.java +++ b/muzzle/src/main/java/io/opentelemetry/javaagent/tooling/muzzle/AgentCachingPoolStrategy.java @@ -5,10 +5,10 @@ package io.opentelemetry.javaagent.tooling.muzzle; -import io.opentelemetry.instrumentation.api.config.Config; import io.opentelemetry.instrumentation.api.internal.cache.Cache; import io.opentelemetry.javaagent.bootstrap.InstrumentationHolder; import io.opentelemetry.javaagent.bootstrap.VirtualFieldAccessorMarker; +import io.opentelemetry.javaagent.bootstrap.internal.InstrumentationConfig; import java.lang.instrument.Instrumentation; import java.lang.ref.WeakReference; import java.lang.reflect.Method; @@ -52,7 +52,8 @@ public class AgentCachingPoolStrategy implements AgentBuilder.PoolStrategy { // others to avoid creation of synthetic accessors private static final boolean REFLECTION_ENABLED = - Config.get().getBoolean("otel.instrumentation.internal-reflection.enabled", true); + InstrumentationConfig.get() + .getBoolean("otel.instrumentation.internal-reflection.enabled", true); private static final Method findLoadedClassMethod = getFindLoadedClassMethod(); static final int TYPE_CAPACITY = 64; diff --git a/muzzle/src/main/java/io/opentelemetry/javaagent/tooling/muzzle/ClassLoaderMatcher.java b/muzzle/src/main/java/io/opentelemetry/javaagent/tooling/muzzle/ClassLoaderMatcher.java index 68dde4161266..c0bd3de6934b 100644 --- a/muzzle/src/main/java/io/opentelemetry/javaagent/tooling/muzzle/ClassLoaderMatcher.java +++ b/muzzle/src/main/java/io/opentelemetry/javaagent/tooling/muzzle/ClassLoaderMatcher.java @@ -93,7 +93,7 @@ private static List checkHelperInjection( helperResourceBuilder.getResources(), Thread.currentThread().getContextClassLoader(), null) - .transform(null, null, classLoader, null); + .transform(null, null, classLoader, null, null); } } catch (RuntimeException e) { mismatches = ReferenceMatcher.add(mismatches, new Mismatch.HelperClassesInjectionError()); diff --git a/muzzle/src/main/java/io/opentelemetry/javaagent/tooling/muzzle/HelperClassPredicate.java b/muzzle/src/main/java/io/opentelemetry/javaagent/tooling/muzzle/HelperClassPredicate.java index d64750f40c0e..cd33024802b8 100644 --- a/muzzle/src/main/java/io/opentelemetry/javaagent/tooling/muzzle/HelperClassPredicate.java +++ b/muzzle/src/main/java/io/opentelemetry/javaagent/tooling/muzzle/HelperClassPredicate.java @@ -49,8 +49,7 @@ private static boolean isBootstrapClass(String className) { || className.startsWith("io.opentelemetry.javaagent.bootstrap.") || className.startsWith("io.opentelemetry.api.") || className.startsWith("io.opentelemetry.context.") - || className.startsWith("io.opentelemetry.semconv.") - || className.startsWith("org.slf4j."); + || className.startsWith("io.opentelemetry.semconv."); } private static boolean isJavaagentHelperClass(String className) { diff --git a/muzzle/src/main/java/io/opentelemetry/javaagent/tooling/muzzle/ReferenceCollectingClassVisitor.java b/muzzle/src/main/java/io/opentelemetry/javaagent/tooling/muzzle/ReferenceCollectingClassVisitor.java index 7efea70e16e4..f72d2b6f3bac 100644 --- a/muzzle/src/main/java/io/opentelemetry/javaagent/tooling/muzzle/ReferenceCollectingClassVisitor.java +++ b/muzzle/src/main/java/io/opentelemetry/javaagent/tooling/muzzle/ReferenceCollectingClassVisitor.java @@ -545,7 +545,7 @@ public void visitMethodInsn( Type methodType = Type.getMethodType(descriptor); Type ownerType = Type.getType("L" + owner + ";"); - // remember used context classes if this is an VirtualField.get() call + // remember used context classes if this is an VirtualField.find() call if ("io.opentelemetry.instrumentation.api.util.VirtualField".equals(ownerType.getClassName()) && "find".equals(name) && methodType.getDescriptor().equals(getVirtualFieldDescriptor)) { diff --git a/muzzle/src/main/java/io/opentelemetry/javaagent/tooling/muzzle/VirtualFieldMappingsBuilderImpl.java b/muzzle/src/main/java/io/opentelemetry/javaagent/tooling/muzzle/VirtualFieldMappingsBuilderImpl.java index 579218f9055c..1529b53e687f 100644 --- a/muzzle/src/main/java/io/opentelemetry/javaagent/tooling/muzzle/VirtualFieldMappingsBuilderImpl.java +++ b/muzzle/src/main/java/io/opentelemetry/javaagent/tooling/muzzle/VirtualFieldMappingsBuilderImpl.java @@ -5,6 +5,7 @@ package io.opentelemetry.javaagent.tooling.muzzle; +import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.util.AbstractMap; import java.util.HashSet; import java.util.Map; @@ -14,6 +15,7 @@ public final class VirtualFieldMappingsBuilderImpl implements VirtualFieldMappin private final Set> entrySet = new HashSet<>(); @Override + @CanIgnoreReturnValue public VirtualFieldMappingsBuilder register(String typeName, String fieldTypeName) { entrySet.add(new AbstractMap.SimpleImmutableEntry<>(typeName, fieldTypeName)); return this; diff --git a/muzzle/src/main/java/io/opentelemetry/javaagent/tooling/muzzle/references/ClassRefBuilder.java b/muzzle/src/main/java/io/opentelemetry/javaagent/tooling/muzzle/references/ClassRefBuilder.java index b4fb091c5b7d..8df01589bf84 100644 --- a/muzzle/src/main/java/io/opentelemetry/javaagent/tooling/muzzle/references/ClassRefBuilder.java +++ b/muzzle/src/main/java/io/opentelemetry/javaagent/tooling/muzzle/references/ClassRefBuilder.java @@ -8,6 +8,7 @@ import static java.util.Arrays.asList; import static java.util.Collections.emptySet; +import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.util.ArrayList; import java.util.Collection; import java.util.LinkedHashSet; @@ -41,25 +42,30 @@ public final class ClassRefBuilder { this.className = className; } + @CanIgnoreReturnValue public ClassRefBuilder setSuperClassName(String superName) { this.superClassName = superName; return this; } + @CanIgnoreReturnValue public ClassRefBuilder addInterfaceNames(Collection interfaceNames) { this.interfaceNames.addAll(interfaceNames); return this; } + @CanIgnoreReturnValue public ClassRefBuilder addInterfaceName(String interfaceName) { interfaceNames.add(interfaceName); return this; } + @CanIgnoreReturnValue public ClassRefBuilder addSource(String sourceName) { return addSource(sourceName, 0); } + @CanIgnoreReturnValue public ClassRefBuilder addSource(String sourceName, int line) { if (COLLECT_SOURCES) { sources.add(new Source(sourceName, line)); @@ -67,11 +73,13 @@ public ClassRefBuilder addSource(String sourceName, int line) { return this; } + @CanIgnoreReturnValue public ClassRefBuilder addFlag(Flag flag) { flags.add(flag); return this; } + @CanIgnoreReturnValue public ClassRefBuilder addField( Source[] fieldSources, Flag[] fieldFlags, @@ -95,6 +103,7 @@ public ClassRefBuilder addField( return this; } + @CanIgnoreReturnValue public ClassRefBuilder addMethod( Source[] methodSources, Flag[] methodFlags, diff --git a/muzzle/src/test/groovy/io/opentelemetry/javaagent/tooling/muzzle/ReferenceMatcherTest.groovy b/muzzle/src/test/groovy/io/opentelemetry/javaagent/tooling/muzzle/ReferenceMatcherTest.groovy index eff8a12ff680..51e270b863c1 100644 --- a/muzzle/src/test/groovy/io/opentelemetry/javaagent/tooling/muzzle/ReferenceMatcherTest.groovy +++ b/muzzle/src/test/groovy/io/opentelemetry/javaagent/tooling/muzzle/ReferenceMatcherTest.groovy @@ -15,7 +15,7 @@ import io.opentelemetry.test.AnotherTestInterface import io.opentelemetry.test.TestAbstractSuperClass import io.opentelemetry.test.TestInterface import muzzle.TestClasses -import muzzle.TestClasses.MethodBodyAdvice +import muzzle.TestClasses.Nested import org.objectweb.asm.Type import spock.lang.Shared import spock.lang.Specification @@ -35,22 +35,22 @@ class ReferenceMatcherTest extends Specification { static final TEST_EXTERNAL_INSTRUMENTATION_PACKAGE = "com.external.otel.instrumentation" @Shared - ClassLoader safeClasspath = new URLClassLoader([ClasspathUtils.createJarWithClasses(MethodBodyAdvice.A, - MethodBodyAdvice.B, - MethodBodyAdvice.SomeInterface, - MethodBodyAdvice.SomeImplementation)] as URL[], + ClassLoader safeClasspath = new URLClassLoader([ClasspathUtils.createJarWithClasses(Nested.A, + Nested.B, + Nested.SomeInterface, + Nested.SomeImplementation)] as URL[], (ClassLoader) null) @Shared - ClassLoader unsafeClasspath = new URLClassLoader([ClasspathUtils.createJarWithClasses(MethodBodyAdvice.A, - MethodBodyAdvice.SomeInterface, - MethodBodyAdvice.SomeImplementation)] as URL[], + ClassLoader unsafeClasspath = new URLClassLoader([ClasspathUtils.createJarWithClasses(Nested.A, + Nested.SomeInterface, + Nested.SomeImplementation)] as URL[], (ClassLoader) null) def "match safe classpaths"() { setup: def collector = new ReferenceCollector({ false }) - collector.collectReferencesFromAdvice(MethodBodyAdvice.name) + collector.collectReferencesFromAdvice(TestClasses.MethodBodyAdvice.name) def refMatcher = createMatcher(collector.getReferences()) expect: @@ -60,7 +60,7 @@ class ReferenceMatcherTest extends Specification { def "matching does not hold a strong reference to classloaders"() { expect: - MuzzleWeakReferenceTest.classLoaderRefIsGarbageCollected() + MuzzleWeakReferenceTestUtil.classLoaderRefIsGarbageCollected() } private static class CountingClassLoader extends URLClassLoader { @@ -80,14 +80,14 @@ class ReferenceMatcherTest extends Specification { def "muzzle type pool caches"() { setup: def cl = new CountingClassLoader( - [ClasspathUtils.createJarWithClasses(MethodBodyAdvice.A, - MethodBodyAdvice.B, - MethodBodyAdvice.SomeInterface, - MethodBodyAdvice.SomeImplementation)] as URL[], + [ClasspathUtils.createJarWithClasses(Nested.A, + Nested.B, + Nested.SomeInterface, + Nested.SomeImplementation)] as URL[], (ClassLoader) null) def collector = new ReferenceCollector({ false }) - collector.collectReferencesFromAdvice(MethodBodyAdvice.name) + collector.collectReferencesFromAdvice(Nested.name) def refMatcher1 = createMatcher(collector.getReferences()) def refMatcher2 = createMatcher(collector.getReferences()) @@ -113,9 +113,9 @@ class ReferenceMatcherTest extends Specification { getMismatchClassSet(mismatches) == expectedMismatches as Set where: - referenceName | referenceFlag | classToCheck | expectedMismatches - MethodBodyAdvice.B.name | NON_INTERFACE | MethodBodyAdvice.B | [] - MethodBodyAdvice.B.name | INTERFACE | MethodBodyAdvice.B | [Mismatch.MissingFlag] + referenceName | referenceFlag | classToCheck | expectedMismatches + Nested.B.name | NON_INTERFACE | Nested.B | [] + Nested.B.name | INTERFACE | Nested.B | [Mismatch.MissingFlag] } def "method match #methodTestDesc"() { @@ -133,14 +133,14 @@ class ReferenceMatcherTest extends Specification { getMismatchClassSet(mismatches) == expectedMismatches as Set where: - methodName | methodDesc | methodFlags | classToCheck | expectedMismatches | methodTestDesc - "method" | "(Ljava/lang/String;)Ljava/lang/String;" | [] | MethodBodyAdvice.B | [] | "match method declared in class" - "hashCode" | "()I" | [] | MethodBodyAdvice.B | [] | "match method declared in superclass" - "someMethod" | "()V" | [] | MethodBodyAdvice.SomeInterface | [] | "match method declared in interface" - "privateStuff" | "()V" | [PRIVATE_OR_HIGHER] | MethodBodyAdvice.B | [] | "match private method" - "privateStuff" | "()V" | [PROTECTED_OR_HIGHER] | MethodBodyAdvice.B2 | [Mismatch.MissingFlag] | "fail match private in supertype" - "staticMethod" | "()V" | [NON_STATIC] | MethodBodyAdvice.B | [Mismatch.MissingFlag] | "static method mismatch" - "missingMethod" | "()V" | [] | MethodBodyAdvice.B | [Mismatch.MissingMethod] | "missing method mismatch" + methodName | methodDesc | methodFlags | classToCheck | expectedMismatches | methodTestDesc + "method" | "(Ljava/lang/String;)Ljava/lang/String;" | [] | Nested.B | [] | "match method declared in class" + "hashCode" | "()I" | [] | Nested.B | [] | "match method declared in superclass" + "someMethod" | "()V" | [] | Nested.SomeInterface | [] | "match method declared in interface" + "privateStuff" | "()V" | [PRIVATE_OR_HIGHER] | Nested.B | [] | "match private method" + "privateStuff" | "()V" | [PROTECTED_OR_HIGHER] | Nested.B2 | [Mismatch.MissingFlag] | "fail match private in supertype" + "staticMethod" | "()V" | [NON_STATIC] | Nested.B | [Mismatch.MissingFlag] | "static method mismatch" + "missingMethod" | "()V" | [] | Nested.B | [Mismatch.MissingMethod] | "missing method mismatch" } def "field match #fieldTestDesc"() { @@ -157,15 +157,15 @@ class ReferenceMatcherTest extends Specification { getMismatchClassSet(mismatches) == expectedMismatches as Set where: - fieldName | fieldType | fieldFlags | classToCheck | expectedMismatches | fieldTestDesc - "missingField" | "Ljava/lang/String;" | [] | MethodBodyAdvice.A | [Mismatch.MissingField] | "mismatch missing field" - "privateField" | "Ljava/lang/String;" | [] | MethodBodyAdvice.A | [Mismatch.MissingField] | "mismatch field type signature" - "privateField" | "Ljava/lang/Object;" | [PRIVATE_OR_HIGHER] | MethodBodyAdvice.A | [] | "match private field" - "privateField" | "Ljava/lang/Object;" | [PROTECTED_OR_HIGHER] | MethodBodyAdvice.A2 | [Mismatch.MissingFlag] | "mismatch private field in supertype" - "protectedField" | "Ljava/lang/Object;" | [STATIC] | MethodBodyAdvice.A | [Mismatch.MissingFlag] | "mismatch static field" - "staticB" | Type.getType(MethodBodyAdvice.B).getDescriptor() | [STATIC, PROTECTED_OR_HIGHER] | MethodBodyAdvice.A | [] | "match static field" - "number" | "I" | [PACKAGE_OR_HIGHER] | MethodBodyAdvice.Primitives | [] | "match primitive int" - "flag" | "Z" | [PACKAGE_OR_HIGHER] | MethodBodyAdvice.Primitives | [] | "match primitive boolean" + fieldName | fieldType | fieldFlags | classToCheck | expectedMismatches | fieldTestDesc + "missingField" | "Ljava/lang/String;" | [] | Nested.A | [Mismatch.MissingField] | "mismatch missing field" + "privateField" | "Ljava/lang/String;" | [] | Nested.A | [Mismatch.MissingField] | "mismatch field type signature" + "privateField" | "Ljava/lang/Object;" | [PRIVATE_OR_HIGHER] | Nested.A | [] | "match private field" + "privateField" | "Ljava/lang/Object;" | [PROTECTED_OR_HIGHER] | Nested.A2 | [Mismatch.MissingFlag] | "mismatch private field in supertype" + "protectedField" | "Ljava/lang/Object;" | [STATIC] | Nested.A | [Mismatch.MissingFlag] | "mismatch static field" + "staticB" | Type.getType(Nested.B).getDescriptor() | [STATIC, PROTECTED_OR_HIGHER] | Nested.A | [] | "match static field" + "number" | "I" | [PACKAGE_OR_HIGHER] | Nested.Primitives | [] | "match primitive int" + "flag" | "Z" | [PACKAGE_OR_HIGHER] | Nested.Primitives | [] | "match primitive boolean" } def "should not check abstract #desc helper classes"() { diff --git a/muzzle/src/test/java/io/opentelemetry/instrumentation/OtherTestHelperClasses.java b/muzzle/src/test/java/io/opentelemetry/instrumentation/OtherTestHelperClasses.java index a71afc8cc4e9..873328233a5a 100644 --- a/muzzle/src/test/java/io/opentelemetry/instrumentation/OtherTestHelperClasses.java +++ b/muzzle/src/test/java/io/opentelemetry/instrumentation/OtherTestHelperClasses.java @@ -8,7 +8,8 @@ import muzzle.TestClasses; public class OtherTestHelperClasses { - public static class Foo implements TestClasses.MethodBodyAdvice.SomeInterface { + + public static class Foo implements TestClasses.Nested.SomeInterface { @Override public void someMethod() {} } @@ -30,4 +31,6 @@ int getAnswer() { abstract int getAnswer(); } + + private OtherTestHelperClasses() {} } diff --git a/muzzle/src/test/java/io/opentelemetry/instrumentation/TestHelperClasses.java b/muzzle/src/test/java/io/opentelemetry/instrumentation/TestHelperClasses.java index d473428c5501..b049d125b625 100644 --- a/muzzle/src/test/java/io/opentelemetry/instrumentation/TestHelperClasses.java +++ b/muzzle/src/test/java/io/opentelemetry/instrumentation/TestHelperClasses.java @@ -10,6 +10,7 @@ import javax.annotation.Nullable; public class TestHelperClasses { + public static class Helper extends HelperSuperClass implements HelperInterface { @Override @@ -59,4 +60,6 @@ static int bar() { return 12345; } } + + private TestHelperClasses() {} } diff --git a/muzzle/src/test/java/io/opentelemetry/javaagent/tooling/muzzle/DeclaredFieldTestClass.java b/muzzle/src/test/java/io/opentelemetry/javaagent/tooling/muzzle/DeclaredFieldTestClass.java index b21b63d1042d..2d5e7456a5cb 100644 --- a/muzzle/src/test/java/io/opentelemetry/javaagent/tooling/muzzle/DeclaredFieldTestClass.java +++ b/muzzle/src/test/java/io/opentelemetry/javaagent/tooling/muzzle/DeclaredFieldTestClass.java @@ -7,6 +7,7 @@ @SuppressWarnings("unused") public class DeclaredFieldTestClass { + public static class Advice { public void instrument() { new Helper().foo(); @@ -25,4 +26,6 @@ public void foo() { public static class LibraryBaseClass { protected Object superField; } + + private DeclaredFieldTestClass() {} } diff --git a/muzzle/src/test/java/io/opentelemetry/javaagent/tooling/muzzle/MuzzleWeakReferenceTest.java b/muzzle/src/test/java/io/opentelemetry/javaagent/tooling/muzzle/MuzzleWeakReferenceTestUtil.java similarity index 84% rename from muzzle/src/test/java/io/opentelemetry/javaagent/tooling/muzzle/MuzzleWeakReferenceTest.java rename to muzzle/src/test/java/io/opentelemetry/javaagent/tooling/muzzle/MuzzleWeakReferenceTestUtil.java index e15f217ca100..d23ad66a6ec2 100644 --- a/muzzle/src/test/java/io/opentelemetry/javaagent/tooling/muzzle/MuzzleWeakReferenceTest.java +++ b/muzzle/src/test/java/io/opentelemetry/javaagent/tooling/muzzle/MuzzleWeakReferenceTestUtil.java @@ -10,9 +10,9 @@ import java.net.URL; import java.net.URLClassLoader; import java.util.Collections; -import muzzle.TestClasses; +import muzzle.TestClasses.MethodBodyAdvice; -public class MuzzleWeakReferenceTest { +public class MuzzleWeakReferenceTestUtil { // Spock holds strong references to all local variables. For weak reference testing we must create // our strong references away from Spock in this java class. @@ -21,7 +21,7 @@ public static boolean classLoaderRefIsGarbageCollected() throws InterruptedExcep ClassLoader loader = new URLClassLoader(new URL[0], null); WeakReference clRef = new WeakReference<>(loader); ReferenceCollector collector = new ReferenceCollector(className -> false); - collector.collectReferencesFromAdvice(TestClasses.MethodBodyAdvice.class.getName()); + collector.collectReferencesFromAdvice(MethodBodyAdvice.class.getName()); ReferenceMatcher refMatcher = new ReferenceMatcher( Collections.emptyList(), collector.getReferences(), className -> false); @@ -30,4 +30,6 @@ public static boolean classLoaderRefIsGarbageCollected() throws InterruptedExcep GcUtils.awaitGc(clRef); return clRef.get() == null; } + + private MuzzleWeakReferenceTestUtil() {} } diff --git a/muzzle/src/test/java/io/opentelemetry/javaagent/tooling/muzzle/ReferenceCollectorTest.java b/muzzle/src/test/java/io/opentelemetry/javaagent/tooling/muzzle/ReferenceCollectorTest.java index cafc28f4db7d..9ad2fcfa8a97 100644 --- a/muzzle/src/test/java/io/opentelemetry/javaagent/tooling/muzzle/ReferenceCollectorTest.java +++ b/muzzle/src/test/java/io/opentelemetry/javaagent/tooling/muzzle/ReferenceCollectorTest.java @@ -29,6 +29,7 @@ import muzzle.TestClasses.HelperAdvice; import muzzle.TestClasses.LdcAdvice; import muzzle.TestClasses.MethodBodyAdvice; +import muzzle.TestClasses.Nested; import org.assertj.core.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; @@ -48,17 +49,17 @@ public void methodBodyCreatesReferences() { assertThat(references) .containsOnlyKeys( - MethodBodyAdvice.A.class.getName(), - MethodBodyAdvice.B.class.getName(), - MethodBodyAdvice.SomeInterface.class.getName(), - MethodBodyAdvice.SomeImplementation.class.getName()); + Nested.A.class.getName(), + Nested.B.class.getName(), + Nested.SomeInterface.class.getName(), + Nested.SomeImplementation.class.getName()); - ClassRef refB = references.get(MethodBodyAdvice.B.class.getName()); - ClassRef refA = references.get(MethodBodyAdvice.A.class.getName()); + ClassRef refB = references.get(Nested.B.class.getName()); + ClassRef refA = references.get(Nested.A.class.getName()); // interface flags assertThat(refB.getFlags()).contains(ManifestationFlag.NON_INTERFACE); - assertThat(references.get(MethodBodyAdvice.SomeInterface.class.getName()).getFlags()) + assertThat(references.get(Nested.SomeInterface.class.getName()).getFlags()) .contains(ManifestationFlag.INTERFACE); // class access flags @@ -92,12 +93,12 @@ public void methodBodyCreatesReferences() { @Test public void protectedRefTest() { ReferenceCollector collector = new ReferenceCollector(s -> false); - collector.collectReferencesFromAdvice(MethodBodyAdvice.B2.class.getName()); + collector.collectReferencesFromAdvice(Nested.B2.class.getName()); collector.prune(); Map references = collector.getReferences(); assertMethod( - references.get(MethodBodyAdvice.B.class.getName()), + references.get(Nested.B.class.getName()), "protectedMethod", "()V", PROTECTED_OR_HIGHER, @@ -111,7 +112,7 @@ public void ldcCreatesReferences() { collector.prune(); Map references = collector.getReferences(); - assertThat(references).containsKey(MethodBodyAdvice.A.class.getName()); + assertThat(references).containsKey(Nested.A.class.getName()); } @Test @@ -121,7 +122,7 @@ public void instanceofCreatesReferences() { collector.prune(); Map references = collector.getReferences(); - assertThat(references).containsKey(MethodBodyAdvice.A.class.getName()); + assertThat(references).containsKey(Nested.A.class.getName()); } @Test @@ -131,23 +132,23 @@ public void invokedynamicCreatesReferences() { collector.prune(); Map references = collector.getReferences(); - assertThat(references).containsKey("muzzle.TestClasses$MethodBodyAdvice$SomeImplementation"); + assertThat(references).containsKey("muzzle.TestClasses$Nested$SomeImplementation"); assertMethod( - references.get("muzzle.TestClasses$MethodBodyAdvice$SomeImplementation"), + references.get("muzzle.TestClasses$Nested$SomeImplementation"), "someMethod", "()V", PROTECTED_OR_HIGHER, OwnershipFlag.NON_STATIC); - assertThat(references).containsKey("muzzle.TestClasses$MethodBodyAdvice$B"); + assertThat(references).containsKey("muzzle.TestClasses$Nested$B"); assertMethod( - references.get("muzzle.TestClasses$MethodBodyAdvice$B"), + references.get("muzzle.TestClasses$Nested$B"), "staticMethod", "()V", PROTECTED_OR_HIGHER, OwnershipFlag.STATIC); - assertThat(references).containsKey("muzzle.TestClasses$MethodBodyAdvice$A"); + assertThat(references).containsKey("muzzle.TestClasses$Nested$A"); assertMethod( - references.get("muzzle.TestClasses$MethodBodyAdvice$A"), + references.get("muzzle.TestClasses$Nested$A"), "", "()V", PROTECTED_OR_HIGHER, diff --git a/muzzle/src/test/java/io/opentelemetry/javaagent/tooling/muzzle/VirtualFieldTestClasses.java b/muzzle/src/test/java/io/opentelemetry/javaagent/tooling/muzzle/VirtualFieldTestClasses.java index 3cdd7538a232..697bd89256e7 100644 --- a/muzzle/src/test/java/io/opentelemetry/javaagent/tooling/muzzle/VirtualFieldTestClasses.java +++ b/muzzle/src/test/java/io/opentelemetry/javaagent/tooling/muzzle/VirtualFieldTestClasses.java @@ -8,8 +8,9 @@ import io.opentelemetry.context.Context; import io.opentelemetry.instrumentation.api.util.VirtualField; -@SuppressWarnings("ReturnValueIgnored") +@SuppressWarnings({"ReturnValueIgnored", "unused"}) public class VirtualFieldTestClasses { + public static class ValidAdvice { public static void advice() { Runnable.class.getName(); @@ -71,4 +72,6 @@ public static class Key1 {} public static class Key2 {} public static class State {} + + private VirtualFieldTestClasses() {} } diff --git a/muzzle/src/test/java/muzzle/HelperReferenceWrapperTestClasses.java b/muzzle/src/test/java/muzzle/HelperReferenceWrapperTestClasses.java index eb10c585b966..33aa01c97d48 100644 --- a/muzzle/src/test/java/muzzle/HelperReferenceWrapperTestClasses.java +++ b/muzzle/src/test/java/muzzle/HelperReferenceWrapperTestClasses.java @@ -7,6 +7,7 @@ @SuppressWarnings("unused") public class HelperReferenceWrapperTestClasses { + interface Interface1 { void foo(); } @@ -24,4 +25,6 @@ static void staticMethodsAreIgnored() {} @SuppressWarnings("MethodCanBeStatic") private void privateMethodsToo() {} } + + private HelperReferenceWrapperTestClasses() {} } diff --git a/muzzle/src/test/java/muzzle/TestClasses.java b/muzzle/src/test/java/muzzle/TestClasses.java index 2d5765ae3dc3..8b6b6dde089a 100644 --- a/muzzle/src/test/java/muzzle/TestClasses.java +++ b/muzzle/src/test/java/muzzle/TestClasses.java @@ -13,22 +13,24 @@ @SuppressWarnings("unused") public class TestClasses { - @SuppressWarnings("ClassNamedLikeTypeParameter") public static class MethodBodyAdvice { - @SuppressWarnings("ReturnValueIgnored") @Advice.OnMethodEnter + @SuppressWarnings("ReturnValueIgnored") public static void methodBodyAdvice() { - A a = new A(); - SomeInterface inter = new SomeImplementation(); + Nested.A a = new Nested.A(); + Nested.SomeInterface inter = new Nested.SomeImplementation(); inter.someMethod(); a.publicB.method("foo"); a.publicB.methodWithPrimitives(false); a.publicB.methodWithArrays(new String[0]); - B.staticMethod(); - A.staticB.method("bar"); + Nested.B.staticMethod(); + Nested.A.staticB.method("bar"); new int[0].clone(); } + } + @SuppressWarnings("ClassNamedLikeTypeParameter") + public static class Nested { public static class A { public B publicB = new B(); protected Object protectedField = null; @@ -85,6 +87,8 @@ public static class SomeClassWithFields { } public interface AnotherInterface extends SomeInterface {} + + private Nested() {} } public abstract static class BaseClassWithConstructor { @@ -94,21 +98,20 @@ protected BaseClassWithConstructor(long l) {} public static class LdcAdvice { @SuppressWarnings("ReturnValueIgnored") public static void ldcMethod() { - MethodBodyAdvice.A.class.getName(); + Nested.A.class.getName(); } } public static class InstanceofAdvice { public static boolean instanceofMethod(Object a) { - return a instanceof MethodBodyAdvice.A; + return a instanceof Nested.A; } } public static class InvokeDynamicAdvice { - public static MethodBodyAdvice.SomeInterface invokeDynamicMethod( - MethodBodyAdvice.SomeImplementation a) { - Runnable staticMethod = MethodBodyAdvice.B::staticMethod; - Runnable constructorMethod = MethodBodyAdvice.A::new; + public static Nested.SomeInterface invokeDynamicMethod(Nested.SomeImplementation a) { + Runnable staticMethod = Nested.B::staticMethod; + Runnable constructorMethod = Nested.A::new; return a::someMethod; } } @@ -130,4 +133,6 @@ public static void adviceMethod() { new ExternalHelper().instrument(); } } + + private TestClasses() {} } diff --git a/opentelemetry-api-shaded-for-instrumenting/build.gradle.kts b/opentelemetry-api-shaded-for-instrumenting/build.gradle.kts index 303b2a1e8b99..4845bbf4cd28 100644 --- a/opentelemetry-api-shaded-for-instrumenting/build.gradle.kts +++ b/opentelemetry-api-shaded-for-instrumenting/build.gradle.kts @@ -28,6 +28,7 @@ val v1_10 by configurations.creating { dependencies { latestDeps("io.opentelemetry:opentelemetry-api") + latestDeps("io.opentelemetry:opentelemetry-api-logs") listOf("opentelemetry-api", "opentelemetry-context").forEach { v1_10Deps("io.opentelemetry:$it") { diff --git a/settings.gradle.kts b/settings.gradle.kts index 30526d9f5808..fd7cf8d56775 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,27 +1,21 @@ pluginManagement { plugins { - id("com.bmuschko.docker-remote-api") version "7.3.0" - id("com.github.ben-manes.versions") version "0.42.0" + id("com.bmuschko.docker-remote-api") version "8.1.0" + id("com.github.ben-manes.versions") version "0.43.0" id("com.github.jk1.dependency-license-report") version "2.1" - id("com.google.cloud.tools.jib") version "3.2.1" + id("com.google.cloud.tools.jib") version "3.3.1" id("com.gradle.plugin-publish") version "1.0.0" id("io.github.gradle-nexus.publish-plugin") version "1.1.0" - id("org.jetbrains.kotlin.jvm") version "1.6.20" + id("org.jetbrains.kotlin.jvm") version "1.7.20" id("org.unbroken-dome.test-sets") version "4.0.0" id("org.xbib.gradle.plugin.jflex") version "1.6.0" id("org.unbroken-dome.xjc") version "2.0.0" } - - repositories { - gradlePluginPortal() - mavenCentral() - } } plugins { - id("com.gradle.enterprise") version "3.10.3" - id("com.github.burrunan.s3-build-cache") version "1.3" - id("com.gradle.common-custom-user-data-gradle-plugin") version "1.7.2" + id("com.gradle.enterprise") version "3.11.2" + id("com.gradle.common-custom-user-data-gradle-plugin") version "1.8.2" } dependencyResolutionManagement { @@ -89,7 +83,9 @@ buildCache { rootProject.name = "opentelemetry-java-instrumentation" -includeBuild("conventions") +// this is only split out due to a dependabot limitation +// for details see .github/project-root-duplicates/README.md +apply(from = "include-conventions.gradle.kts") include(":custom-checks") @@ -104,13 +100,13 @@ include(":javaagent-bootstrap") include(":javaagent-extension-api") include(":javaagent-tooling") include(":javaagent-tooling:javaagent-tooling-java9") +include(":javaagent-internal-logging-simple") include(":javaagent") +include(":bom") include(":bom-alpha") include(":instrumentation-api") include(":instrumentation-api-semconv") -include(":instrumentation-appender-api-internal") -include(":instrumentation-appender-sdk-internal") include(":instrumentation-annotations") include(":instrumentation-annotations-support") include(":instrumentation-annotations-support-testing") @@ -135,7 +131,7 @@ include(":smoke-tests:images:servlet:servlet-3.0") include(":smoke-tests:images:servlet:servlet-5.0") include(":smoke-tests:images:spring-boot") -include(":instrumentation:akka:akka-actor-2.5:javaagent") +include(":instrumentation:akka:akka-actor-2.3:javaagent") include(":instrumentation:akka:akka-actor-fork-join-2.5:javaagent") include(":instrumentation:akka:akka-http-10.0:javaagent") include(":instrumentation:apache-camel-2.20:javaagent") @@ -204,6 +200,7 @@ include(":instrumentation:couchbase:couchbase-3.1.6:tracing-opentelemetry-shaded include(":instrumentation:couchbase:couchbase-3.2:javaagent") include(":instrumentation:couchbase:couchbase-3.2:tracing-opentelemetry-shaded") include(":instrumentation:couchbase:couchbase-common:testing") +include(":instrumentation:dropwizard:dropwizard-metrics-4.0:javaagent") include(":instrumentation:dropwizard:dropwizard-views-0.7:javaagent") include(":instrumentation:dropwizard:dropwizard-testing") include(":instrumentation:elasticsearch:elasticsearch-rest-common:javaagent") @@ -319,8 +316,8 @@ include(":instrumentation:lettuce:lettuce-5.1:javaagent") include(":instrumentation:lettuce:lettuce-5.1:library") include(":instrumentation:lettuce:lettuce-5.1:testing") include(":instrumentation:liberty:compile-stub") -include(":instrumentation:liberty:liberty:javaagent") -include(":instrumentation:liberty:liberty-dispatcher:javaagent") +include(":instrumentation:liberty:liberty-20.0:javaagent") +include(":instrumentation:liberty:liberty-dispatcher-20.0:javaagent") include(":instrumentation:log4j:log4j-appender-1.2:javaagent") include(":instrumentation:log4j:log4j-mdc-1.2:javaagent") include(":instrumentation:log4j:log4j-context-data:log4j-context-data-2.7:javaagent") @@ -336,6 +333,8 @@ include(":instrumentation:logback:logback-mdc-1.0:library") include(":instrumentation:logback:logback-mdc-1.0:testing") include(":instrumentation:methods:javaagent") include(":instrumentation:micrometer:micrometer-1.5:javaagent") +include(":instrumentation:micrometer:micrometer-1.5:library") +include(":instrumentation:micrometer:micrometer-1.5:testing") include(":instrumentation:mongo:mongo-3.1:javaagent") include(":instrumentation:mongo:mongo-3.1:library") include(":instrumentation:mongo:mongo-3.1:testing") @@ -346,8 +345,11 @@ include(":instrumentation:mongo:mongo-common:testing") include(":instrumentation:netty:netty-3.8:javaagent") include(":instrumentation:netty:netty-4.0:javaagent") include(":instrumentation:netty:netty-4.1:javaagent") +include(":instrumentation:netty:netty-4.1:library") +include(":instrumentation:netty:netty-4.1:testing") include(":instrumentation:netty:netty-4-common:javaagent") -include(":instrumentation:netty:netty-common:javaagent") +include(":instrumentation:netty:netty-4-common:library") +include(":instrumentation:netty:netty-common:library") include(":instrumentation:okhttp:okhttp-2.2:javaagent") include(":instrumentation:okhttp:okhttp-3.0:javaagent") include(":instrumentation:okhttp:okhttp-3.0:library") @@ -355,6 +357,7 @@ include(":instrumentation:okhttp:okhttp-3.0:testing") include(":instrumentation:opentelemetry-api:opentelemetry-api-1.0:javaagent") include(":instrumentation:opentelemetry-api:opentelemetry-api-1.4:javaagent") include(":instrumentation:opentelemetry-api:opentelemetry-api-1.10:javaagent") +include(":instrumentation:opentelemetry-api:opentelemetry-api-logs-1.19:javaagent") include(":instrumentation:opentelemetry-extension-annotations-1.0:javaagent") include(":instrumentation:opentelemetry-instrumentation-annotations-1.16:javaagent") include(":instrumentation:opentelemetry-instrumentation-api:javaagent") @@ -372,6 +375,7 @@ include(":instrumentation:play:play-ws:play-ws-2.0:javaagent") include(":instrumentation:play:play-ws:play-ws-2.1:javaagent") include(":instrumentation:play:play-ws:play-ws-common:javaagent") include(":instrumentation:play:play-ws:play-ws-common:testing") +include("instrumentation:pubsub-1.0:javaagent") include(":instrumentation:quartz-2.0:javaagent") include(":instrumentation:quartz-2.0:library") include(":instrumentation:quartz-2.0:testing") @@ -384,22 +388,26 @@ include(":instrumentation:reactor:reactor-3.1:library") include(":instrumentation:reactor:reactor-3.1:testing") include(":instrumentation:reactor:reactor-netty:reactor-netty-0.9:javaagent") include(":instrumentation:reactor:reactor-netty:reactor-netty-1.0:javaagent") +include(":instrumentation:reactor:reactor-netty:reactor-netty-1.0:javaagent-unit-tests") include(":instrumentation:rediscala-1.8:javaagent") include(":instrumentation:redisson:redisson-3.0:javaagent") include(":instrumentation:redisson:redisson-3.17:javaagent") include(":instrumentation:redisson:redisson-common:javaagent") include(":instrumentation:redisson:redisson-common:testing") -include(":instrumentation:restlet:restlet-1.0:javaagent") -include(":instrumentation:restlet:restlet-1.0:library") -include(":instrumentation:restlet:restlet-1.0:testing") +include(":instrumentation:resources:library") +include(":instrumentation:restlet:restlet-1.1:javaagent") +include(":instrumentation:restlet:restlet-1.1:library") +include(":instrumentation:restlet:restlet-1.1:testing") include(":instrumentation:restlet:restlet-2.0:javaagent") include(":instrumentation:restlet:restlet-2.0:library") include(":instrumentation:restlet:restlet-2.0:testing") include(":instrumentation:rmi:bootstrap") include(":instrumentation:rmi:javaagent") -include(":instrumentation:rocketmq-client-4.8:javaagent") -include(":instrumentation:rocketmq-client-4.8:library") -include(":instrumentation:rocketmq-client-4.8:testing") +include(":instrumentation:rocketmq:rocketmq-client:rocketmq-client-4.8:javaagent") +include(":instrumentation:rocketmq:rocketmq-client:rocketmq-client-4.8:library") +include(":instrumentation:rocketmq:rocketmq-client:rocketmq-client-4.8:testing") +include(":instrumentation:rocketmq:rocketmq-client:rocketmq-client-5.0:javaagent") +include(":instrumentation:rocketmq:rocketmq-client:rocketmq-client-5.0:testing") include(":instrumentation:runtime-metrics:javaagent") include(":instrumentation:runtime-metrics:library") include(":instrumentation:rxjava:rxjava-1.0:library") @@ -422,12 +430,15 @@ include(":instrumentation:servlet:servlet-5.0:javaagent") include(":instrumentation:spark-2.3:javaagent") include(":instrumentation:spring:spring-batch-3.0:javaagent") include(":instrumentation:spring:spring-boot-actuator-autoconfigure-2.0:javaagent") +include(":instrumentation:spring:spring-boot-resources:library") include(":instrumentation:spring:spring-core-2.0:javaagent") include(":instrumentation:spring:spring-data-1.8:javaagent") include(":instrumentation:spring:spring-integration-4.1:javaagent") include(":instrumentation:spring:spring-integration-4.1:library") include(":instrumentation:spring:spring-integration-4.1:testing") +include(":instrumentation:spring:spring-jms-2.0:javaagent") include(":instrumentation:spring:spring-kafka-2.7:javaagent") +include(":instrumentation:spring:spring-kafka-2.7:library") include(":instrumentation:spring:spring-kafka-2.7:testing") include(":instrumentation:spring:spring-rabbit-1.0:javaagent") include(":instrumentation:spring:spring-rmi-4.0:javaagent") @@ -436,16 +447,15 @@ include(":instrumentation:spring:spring-web-3.1:javaagent") include(":instrumentation:spring:spring-web-3.1:library") include(":instrumentation:spring:spring-web-3.1:testing") include(":instrumentation:spring:spring-webmvc-3.1:javaagent") -include(":instrumentation:spring:spring-webmvc-3.1:library") include(":instrumentation:spring:spring-webmvc-3.1:wildfly-testing") +include(":instrumentation:spring:spring-webmvc-5.3:library") include(":instrumentation:spring:spring-webflux-5.0:javaagent") include(":instrumentation:spring:spring-webflux-5.0:library") include(":instrumentation:spring:spring-ws-2.0:javaagent") include(":instrumentation:spring:spring-boot-autoconfigure") -include(":instrumentation:spring:starters:spring-starter") -include(":instrumentation:spring:starters:jaeger-exporter-starter") -include(":instrumentation:spring:starters:otlp-exporter-starter") -include(":instrumentation:spring:starters:zipkin-exporter-starter") +include(":instrumentation:spring:starters:spring-boot-starter") +include(":instrumentation:spring:starters:jaeger-spring-boot-starter") +include(":instrumentation:spring:starters:zipkin-spring-boot-starter") include(":instrumentation:spymemcached-2.12:javaagent") include(":instrumentation:struts-2.3:javaagent") include(":instrumentation:tapestry-5.4:javaagent") diff --git a/smoke-tests/images/fake-backend/src/main/java/io/opentelemetry/smoketest/fakebackend/FakeBackendMain.java b/smoke-tests/images/fake-backend/src/main/java/io/opentelemetry/smoketest/fakebackend/FakeBackendMain.java index 6423f7a9e5cd..a09849fa4075 100644 --- a/smoke-tests/images/fake-backend/src/main/java/io/opentelemetry/smoketest/fakebackend/FakeBackendMain.java +++ b/smoke-tests/images/fake-backend/src/main/java/io/opentelemetry/smoketest/fakebackend/FakeBackendMain.java @@ -127,4 +127,6 @@ public static void main(String[] args) { server.start().join(); Runtime.getRuntime().addShutdownHook(new Thread(() -> server.stop().join())); } + + private FakeBackendMain() {} } diff --git a/smoke-tests/images/grpc/build.gradle.kts b/smoke-tests/images/grpc/build.gradle.kts index fb61375ef9b1..a22ba982d20e 100644 --- a/smoke-tests/images/grpc/build.gradle.kts +++ b/smoke-tests/images/grpc/build.gradle.kts @@ -17,7 +17,7 @@ dependencies { implementation("io.grpc:grpc-protobuf") implementation("io.grpc:grpc-stub") implementation("io.opentelemetry.proto:opentelemetry-proto:0.16.0-alpha") - implementation("io.opentelemetry:opentelemetry-extension-annotations") + implementation(project(":instrumentation-annotations")) implementation("org.apache.logging.log4j:log4j-core") runtimeOnly("org.apache.logging.log4j:log4j-slf4j-impl") @@ -25,9 +25,18 @@ dependencies { val targetJDK = project.findProperty("targetJDK") ?: "11" -val tag = findProperty("tag") ?: DateTimeFormatter.ofPattern("yyyyMMdd.HHmmSS").format(LocalDateTime.now()) +val tag = findProperty("tag") + ?: DateTimeFormatter.ofPattern("yyyyMMdd.HHmmSS").format(LocalDateTime.now()) + +java { + // this is needed to avoid jib failing with + // "Your project is using Java 17 but the base image is for Java 8" + // (it seems the jib plugins does not understand toolchains yet) + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 +} jib { - from.image = "openjdk:$targetJDK" + from.image = "eclipse-temurin:$targetJDK" to.image = "ghcr.io/open-telemetry/opentelemetry-java-instrumentation/smoke-test-grpc:jdk$targetJDK-$tag" } diff --git a/smoke-tests/images/grpc/src/main/java/io/opentelemetry/smoketest/grpc/TestMain.java b/smoke-tests/images/grpc/src/main/java/io/opentelemetry/smoketest/grpc/TestMain.java index 3e87801f10e7..b50a534a2e0c 100644 --- a/smoke-tests/images/grpc/src/main/java/io/opentelemetry/smoketest/grpc/TestMain.java +++ b/smoke-tests/images/grpc/src/main/java/io/opentelemetry/smoketest/grpc/TestMain.java @@ -22,4 +22,6 @@ public static void main(String[] args) throws Exception { logger.info("Server started at port 8080."); server.awaitTermination(); } + + private TestMain() {} } diff --git a/smoke-tests/images/grpc/src/main/java/io/opentelemetry/smoketest/grpc/TestService.java b/smoke-tests/images/grpc/src/main/java/io/opentelemetry/smoketest/grpc/TestService.java index 63cd4eacf610..d92ae970bbcc 100644 --- a/smoke-tests/images/grpc/src/main/java/io/opentelemetry/smoketest/grpc/TestService.java +++ b/smoke-tests/images/grpc/src/main/java/io/opentelemetry/smoketest/grpc/TestService.java @@ -6,7 +6,7 @@ package io.opentelemetry.smoketest.grpc; import io.grpc.stub.StreamObserver; -import io.opentelemetry.extension.annotations.WithSpan; +import io.opentelemetry.instrumentation.annotations.WithSpan; import io.opentelemetry.proto.collector.trace.v1.ExportTraceServiceRequest; import io.opentelemetry.proto.collector.trace.v1.ExportTraceServiceResponse; import io.opentelemetry.proto.collector.trace.v1.TraceServiceGrpc; diff --git a/smoke-tests/images/play/build.gradle.kts b/smoke-tests/images/play/build.gradle.kts index 3b4137099e7f..67aa3f5303d0 100644 --- a/smoke-tests/images/play/build.gradle.kts +++ b/smoke-tests/images/play/build.gradle.kts @@ -21,14 +21,6 @@ play { injectedRoutesGenerator.set(true) } -repositories { - mavenCentral() - maven { - setName("lightbend-maven-releases") - setUrl("https://repo.lightbend.com/lightbend/maven-release") - } -} - dependencies { implementation("com.typesafe.play:play-guice_$scalaVer:$playVer") implementation("com.typesafe.play:play-logback_$scalaVer:$playVer") @@ -37,10 +29,19 @@ dependencies { val targetJDK = project.findProperty("targetJDK") ?: "11" -val tag = findProperty("tag") ?: DateTimeFormatter.ofPattern("yyyyMMdd.HHmmSS").format(LocalDateTime.now()) +val tag = findProperty("tag") + ?: DateTimeFormatter.ofPattern("yyyyMMdd.HHmmSS").format(LocalDateTime.now()) + +java { + // this is needed to avoid jib failing with + // "Your project is using Java 17 but the base image is for Java 8" + // (it seems the jib plugins does not understand toolchains yet) + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 +} jib { - from.image = "openjdk:$targetJDK" + from.image = "eclipse-temurin:$targetJDK" to.image = "ghcr.io/open-telemetry/opentelemetry-java-instrumentation/smoke-test-play:jdk$targetJDK-$tag" container.mainClass = "play.core.server.ProdServerStart" } diff --git a/smoke-tests/images/quarkus/build.gradle.kts b/smoke-tests/images/quarkus/build.gradle.kts index c0b844889b9e..658d906ff3eb 100644 --- a/smoke-tests/images/quarkus/build.gradle.kts +++ b/smoke-tests/images/quarkus/build.gradle.kts @@ -28,10 +28,19 @@ quarkus { val targetJDK = project.findProperty("targetJDK") ?: "11" -val tag = findProperty("tag") ?: DateTimeFormatter.ofPattern("yyyyMMdd.HHmmSS").format(LocalDateTime.now()) +val tag = findProperty("tag") + ?: DateTimeFormatter.ofPattern("yyyyMMdd.HHmmSS").format(LocalDateTime.now()) + +java { + // this is needed to avoid jib failing with + // "Your project is using Java 17 but the base image is for Java 8" + // (it seems the jib plugins does not understand toolchains yet) + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 +} jib { - from.image = "openjdk:$targetJDK" + from.image = "eclipse-temurin:$targetJDK" to.image = "ghcr.io/open-telemetry/opentelemetry-java-instrumentation/smoke-test-quarkus:jdk$targetJDK-$tag" container { mainClass = "bogus" // to suppress Jib warning about missing main class @@ -54,4 +63,8 @@ tasks { withType().configureEach { dependsOn(quarkusBuild) } + + sourcesJar { + dependsOn(quarkusGenerateCode) + } } diff --git a/smoke-tests/images/servlet/build.gradle.kts b/smoke-tests/images/servlet/build.gradle.kts index 3d82fda27f96..5072ea830362 100644 --- a/smoke-tests/images/servlet/build.gradle.kts +++ b/smoke-tests/images/servlet/build.gradle.kts @@ -17,51 +17,51 @@ val extraTag = findProperty("extraTag") // Dockerfile name, args key passes raw arguments to docker build val targets = mapOf( "jetty" to listOf( - ImageTarget(listOf("9.4.39"), listOf("hotspot"), listOf("8", "11", "17", "18"), mapOf("sourceVersion" to "9.4.39.v20210325")), + ImageTarget(listOf("9.4.39"), listOf("hotspot"), listOf("8", "11", "17", "19", "20"), mapOf("sourceVersion" to "9.4.39.v20210325")), ImageTarget(listOf("9.4.39"), listOf("openj9"), listOf("8", "11", "16"), mapOf("sourceVersion" to "9.4.39.v20210325")), - ImageTarget(listOf("10.0.7"), listOf("hotspot"), listOf("11", "17", "18"), mapOf("sourceVersion" to "10.0.7")), + ImageTarget(listOf("10.0.7"), listOf("hotspot"), listOf("11", "17", "19", "20"), mapOf("sourceVersion" to "10.0.7")), ImageTarget(listOf("10.0.7"), listOf("openj9"), listOf("11", "16"), mapOf("sourceVersion" to "10.0.7")), - ImageTarget(listOf("11.0.7"), listOf("hotspot"), listOf("11", "17", "18"), mapOf("sourceVersion" to "11.0.7"), "servlet-5.0"), - ImageTarget(listOf("11.0.7"), listOf("openj9"), listOf("11", "16"), mapOf("sourceVersion" to "11.0.7"), "servlet-5.0"), + ImageTarget(listOf("11.0.7"), listOf("hotspot"), listOf("11", "17", "19", "20"), mapOf("sourceVersion" to "11.0.7"), "servlet-5.0"), + ImageTarget(listOf("11.0.7"), listOf("openj9"), listOf("11", "16"), mapOf("sourceVersion" to "11.0.7"), "servlet-5.0") ), "liberty" to listOf( // running configure.sh is failing while building the image with Java 17 ImageTarget(listOf("20.0.0.12"), listOf("hotspot", "openj9"), listOf("8", "11", "16"), mapOf("release" to "2020-11-11_0736")), - // running configure.sh is failing while building the image with Java 18 + // running configure.sh is failing while building the image with Java 19 ImageTarget(listOf("21.0.0.10"), listOf("hotspot"), listOf("8", "11", "17"), mapOf("release" to "2021-09-20_1900")), - ImageTarget(listOf("21.0.0.10"), listOf("openj9"), listOf("8", "11", "16"), mapOf("release" to "2021-09-20_1900")), + ImageTarget(listOf("21.0.0.10"), listOf("openj9"), listOf("8", "11", "16"), mapOf("release" to "2021-09-20_1900")) ), "payara" to listOf( ImageTarget(listOf("5.2020.6"), listOf("hotspot", "openj9"), listOf("8", "11")), - ImageTarget(listOf("5.2021.8"), listOf("hotspot", "openj9"), listOf("8", "11")), + ImageTarget(listOf("5.2021.8"), listOf("hotspot", "openj9"), listOf("8", "11")) ), "tomcat" to listOf( ImageTarget(listOf("7.0.109"), listOf("hotspot", "openj9"), listOf("8"), mapOf("majorVersion" to "7")), - ImageTarget(listOf("8.5.72"), listOf("hotspot"), listOf("8", "11", "17", "18"), mapOf("majorVersion" to "8")), + ImageTarget(listOf("8.5.72"), listOf("hotspot"), listOf("8", "11", "17", "19", "20"), mapOf("majorVersion" to "8")), ImageTarget(listOf("8.5.72"), listOf("openj9"), listOf("8", "11"), mapOf("majorVersion" to "8")), - ImageTarget(listOf("9.0.54"), listOf("hotspot"), listOf("8", "11", "17", "18"), mapOf("majorVersion" to "9")), + ImageTarget(listOf("9.0.54"), listOf("hotspot"), listOf("8", "11", "17", "19", "20"), mapOf("majorVersion" to "9")), ImageTarget(listOf("9.0.54"), listOf("openj9"), listOf("8", "11"), mapOf("majorVersion" to "9")), - ImageTarget(listOf("10.0.12"), listOf("hotspot"), listOf("8", "11", "17", "18"), mapOf("majorVersion" to "10"), "servlet-5.0"), - ImageTarget(listOf("10.0.12"), listOf("openj9"), listOf("8", "11"), mapOf("majorVersion" to "10"), "servlet-5.0"), + ImageTarget(listOf("10.0.12"), listOf("hotspot"), listOf("8", "11", "17", "19", "20"), mapOf("majorVersion" to "10"), "servlet-5.0"), + ImageTarget(listOf("10.0.12"), listOf("openj9"), listOf("8", "11"), mapOf("majorVersion" to "10"), "servlet-5.0") ), "tomee" to listOf( ImageTarget(listOf("7.0.9"), listOf("hotspot", "openj9"), listOf("8")), ImageTarget(listOf("7.1.4"), listOf("hotspot", "openj9"), listOf("8")), - ImageTarget(listOf("8.0.8"), listOf("hotspot"), listOf("8", "11", "17", "18")), + ImageTarget(listOf("8.0.8"), listOf("hotspot"), listOf("8", "11", "17", "19", "20")), ImageTarget(listOf("8.0.8"), listOf("openj9"), listOf("8", "11", "16")), - ImageTarget(listOf("9.0.0-M7"), listOf("hotspot"), listOf("8", "11", "17", "18"), war = "servlet-5.0"), - ImageTarget(listOf("9.0.0-M7"), listOf("openj9"), listOf("8", "11", "16"), war = "servlet-5.0"), + ImageTarget(listOf("9.0.0-M7"), listOf("hotspot"), listOf("8", "11", "17", "19", "20"), war = "servlet-5.0"), + ImageTarget(listOf("9.0.0-M7"), listOf("openj9"), listOf("8", "11", "16"), war = "servlet-5.0") ), "websphere" to listOf( // TODO (trask) this is a recent change, check back in a while and see if it's been fixed // 8.5.5.20 only has linux/ppc64le image - ImageTarget(listOf("8.5.5.19", "9.0.5.9"), listOf("openj9"), listOf("8"), windows = false), + ImageTarget(listOf("8.5.5.19", "9.0.5.9"), listOf("openj9"), listOf("8"), windows = false) ), "wildfly" to listOf( ImageTarget(listOf("13.0.0.Final"), listOf("hotspot", "openj9"), listOf("8")), - ImageTarget(listOf("17.0.1.Final", "21.0.0.Final", "25.0.1.Final"), listOf("hotspot"), listOf("8", "11", "17", "18")), - ImageTarget(listOf("17.0.1.Final", "21.0.0.Final", "25.0.1.Final"), listOf("openj9"), listOf("8", "11", "16")), - ), + ImageTarget(listOf("17.0.1.Final", "21.0.0.Final", "25.0.1.Final"), listOf("hotspot"), listOf("8", "11", "17", "19", "20")), + ImageTarget(listOf("17.0.1.Final", "21.0.0.Final", "25.0.1.Final"), listOf("openj9"), listOf("8", "11", "16")) + ) ) val matrix = mutableListOf() @@ -134,7 +134,11 @@ fun configureImage(parentTask: TaskProvider, server: String, dockerfil val image = "ghcr.io/open-telemetry/opentelemetry-java-instrumentation/smoke-test-servlet-$server:$version-jdk$jdk$vmSuffix$platformSuffix-$extraTag" val jdkImage = if (vm == "hotspot") { - if (jdk == "19") { + if (jdk == "20") { + // "The only tags which will continue to receive updates beyond July 2022 will be Early Access + // builds (which are sourced from jdk.java.net), as those are not published/supported by any + // of the above projects." + // (see https://hub.docker.com/_/openjdk) "openjdk:$jdk" } else { "eclipse-temurin:$jdk" diff --git a/smoke-tests/images/servlet/servlet-3.0/src/main/java/io/opentelemetry/smoketest/matrix/ExceptionRequestListener.java b/smoke-tests/images/servlet/servlet-3.0/src/main/java/io/opentelemetry/smoketest/matrix/ExceptionRequestListener.java deleted file mode 100644 index 3bcae8af0cb6..000000000000 --- a/smoke-tests/images/servlet/servlet-3.0/src/main/java/io/opentelemetry/smoketest/matrix/ExceptionRequestListener.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.smoketest.matrix; - -import javax.servlet.ServletRequestEvent; -import javax.servlet.ServletRequestListener; - -public class ExceptionRequestListener implements ServletRequestListener { - - @Override - public void requestDestroyed(ServletRequestEvent sre) { - if ("true".equals(sre.getServletRequest().getParameter("throwOnRequestDestroyed"))) { - throw new IllegalStateException("This is expected"); - } - } - - @Override - public void requestInitialized(ServletRequestEvent sre) { - if ("true".equals(sre.getServletRequest().getParameter("throwOnRequestInitialized"))) { - throw new IllegalStateException("This is expected"); - } - } -} diff --git a/smoke-tests/images/servlet/servlet-3.0/src/main/java/io/opentelemetry/smoketest/matrix/ForwardServlet.java b/smoke-tests/images/servlet/servlet-3.0/src/main/java/io/opentelemetry/smoketest/matrix/ForwardServlet.java deleted file mode 100644 index 21fdac7dada5..000000000000 --- a/smoke-tests/images/servlet/servlet-3.0/src/main/java/io/opentelemetry/smoketest/matrix/ForwardServlet.java +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.smoketest.matrix; - -import java.io.IOException; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -public class ForwardServlet extends HttpServlet { - private static final long serialVersionUID = 1L; - - @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { - req.getRequestDispatcher("/hello.txt").forward(req, resp); - } -} diff --git a/smoke-tests/images/servlet/servlet-3.0/src/main/java/io/opentelemetry/smoketest/matrix/IncludeServlet.java b/smoke-tests/images/servlet/servlet-3.0/src/main/java/io/opentelemetry/smoketest/matrix/IncludeServlet.java deleted file mode 100644 index 46ef54539599..000000000000 --- a/smoke-tests/images/servlet/servlet-3.0/src/main/java/io/opentelemetry/smoketest/matrix/IncludeServlet.java +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.smoketest.matrix; - -import java.io.IOException; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -public class IncludeServlet extends HttpServlet { - private static final long serialVersionUID = 1L; - - @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { - req.getRequestDispatcher("/hello.txt").include(req, resp); - } -} diff --git a/smoke-tests/images/servlet/servlet-3.0/src/main/webapp/WEB-INF/web.xml b/smoke-tests/images/servlet/servlet-3.0/src/main/webapp/WEB-INF/web.xml index 845e30374b6b..0575ce17dbe7 100644 --- a/smoke-tests/images/servlet/servlet-3.0/src/main/webapp/WEB-INF/web.xml +++ b/smoke-tests/images/servlet/servlet-3.0/src/main/webapp/WEB-INF/web.xml @@ -3,9 +3,7 @@ xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" version="3.1"> - - io.opentelemetry.smoketest.matrix.ExceptionRequestListener - + smoke-test-app Headers io.opentelemetry.smoketest.matrix.HeaderDumpingServlet @@ -23,14 +21,6 @@ Exception io.opentelemetry.smoketest.matrix.ExceptionServlet - - Forward - io.opentelemetry.smoketest.matrix.ForwardServlet - - - Include - io.opentelemetry.smoketest.matrix.IncludeServlet - Jsp io.opentelemetry.smoketest.matrix.JspServlet @@ -51,14 +41,6 @@ Exception /exception - - Forward - /forward - - - Include - /include - Jsp /jsp diff --git a/smoke-tests/images/servlet/servlet-5.0/src/main/java/io/opentelemetry/smoketest/matrix/ExceptionRequestListener.java b/smoke-tests/images/servlet/servlet-5.0/src/main/java/io/opentelemetry/smoketest/matrix/ExceptionRequestListener.java deleted file mode 100644 index b048ad79dd13..000000000000 --- a/smoke-tests/images/servlet/servlet-5.0/src/main/java/io/opentelemetry/smoketest/matrix/ExceptionRequestListener.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.smoketest.matrix; - -import jakarta.servlet.ServletRequestEvent; -import jakarta.servlet.ServletRequestListener; - -public class ExceptionRequestListener implements ServletRequestListener { - - @Override - public void requestDestroyed(ServletRequestEvent sre) { - if ("true".equals(sre.getServletRequest().getParameter("throwOnRequestDestroyed"))) { - throw new IllegalStateException("This is expected"); - } - } - - @Override - public void requestInitialized(ServletRequestEvent sre) { - if ("true".equals(sre.getServletRequest().getParameter("throwOnRequestInitialized"))) { - throw new IllegalStateException("This is expected"); - } - } -} diff --git a/smoke-tests/images/servlet/servlet-5.0/src/main/java/io/opentelemetry/smoketest/matrix/ForwardServlet.java b/smoke-tests/images/servlet/servlet-5.0/src/main/java/io/opentelemetry/smoketest/matrix/ForwardServlet.java deleted file mode 100644 index b3022f7d06c2..000000000000 --- a/smoke-tests/images/servlet/servlet-5.0/src/main/java/io/opentelemetry/smoketest/matrix/ForwardServlet.java +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.smoketest.matrix; - -import jakarta.servlet.ServletException; -import jakarta.servlet.http.HttpServlet; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import java.io.IOException; - -public class ForwardServlet extends HttpServlet { - private static final long serialVersionUID = 1L; - - @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { - req.getRequestDispatcher("/hello.txt").forward(req, resp); - } -} diff --git a/smoke-tests/images/servlet/servlet-5.0/src/main/java/io/opentelemetry/smoketest/matrix/IncludeServlet.java b/smoke-tests/images/servlet/servlet-5.0/src/main/java/io/opentelemetry/smoketest/matrix/IncludeServlet.java deleted file mode 100644 index b0b7a61e2e3a..000000000000 --- a/smoke-tests/images/servlet/servlet-5.0/src/main/java/io/opentelemetry/smoketest/matrix/IncludeServlet.java +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.smoketest.matrix; - -import jakarta.servlet.ServletException; -import jakarta.servlet.http.HttpServlet; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import java.io.IOException; - -public class IncludeServlet extends HttpServlet { - private static final long serialVersionUID = 1L; - - @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { - req.getRequestDispatcher("/hello.txt").include(req, resp); - } -} diff --git a/smoke-tests/images/servlet/servlet-5.0/src/main/webapp/WEB-INF/web.xml b/smoke-tests/images/servlet/servlet-5.0/src/main/webapp/WEB-INF/web.xml index 4ee1f5472fd0..28f6e1bde88b 100644 --- a/smoke-tests/images/servlet/servlet-5.0/src/main/webapp/WEB-INF/web.xml +++ b/smoke-tests/images/servlet/servlet-5.0/src/main/webapp/WEB-INF/web.xml @@ -3,9 +3,7 @@ xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd" version="5.0"> - - io.opentelemetry.smoketest.matrix.ExceptionRequestListener - + smoke-test-app Headers io.opentelemetry.smoketest.matrix.HeaderDumpingServlet @@ -23,14 +21,6 @@ Exception io.opentelemetry.smoketest.matrix.ExceptionServlet - - Forward - io.opentelemetry.smoketest.matrix.ForwardServlet - - - Include - io.opentelemetry.smoketest.matrix.IncludeServlet - Jsp io.opentelemetry.smoketest.matrix.JspServlet @@ -51,14 +41,6 @@ Exception /exception - - Forward - /forward - - - Include - /include - Jsp /jsp diff --git a/smoke-tests/images/spring-boot/build.gradle.kts b/smoke-tests/images/spring-boot/build.gradle.kts index 1fffaacd804b..5dc6792a319f 100644 --- a/smoke-tests/images/spring-boot/build.gradle.kts +++ b/smoke-tests/images/spring-boot/build.gradle.kts @@ -12,13 +12,14 @@ dependencies { implementation(platform("org.springframework.boot:spring-boot-dependencies:2.6.6")) implementation("io.opentelemetry:opentelemetry-api") - implementation("io.opentelemetry:opentelemetry-extension-annotations") + implementation(project(":instrumentation-annotations")) implementation("org.springframework.boot:spring-boot-starter-web") } val targetJDK = project.findProperty("targetJDK") ?: "11" -val tag = findProperty("tag") ?: DateTimeFormatter.ofPattern("yyyyMMdd.HHmmSS").format(LocalDateTime.now()) +val tag = findProperty("tag") + ?: DateTimeFormatter.ofPattern("yyyyMMdd.HHmmSS").format(LocalDateTime.now()) jib { from.image = "openjdk:$targetJDK" diff --git a/smoke-tests/images/spring-boot/src/main/java/io/opentelemetry/smoketest/springboot/SpringbootApplication.java b/smoke-tests/images/spring-boot/src/main/java/io/opentelemetry/smoketest/springboot/SpringbootApplication.java index ab848bc75fe8..cf526044b23f 100644 --- a/smoke-tests/images/spring-boot/src/main/java/io/opentelemetry/smoketest/springboot/SpringbootApplication.java +++ b/smoke-tests/images/spring-boot/src/main/java/io/opentelemetry/smoketest/springboot/SpringbootApplication.java @@ -14,4 +14,6 @@ public class SpringbootApplication { public static void main(String[] args) { SpringApplication.run(SpringbootApplication.class, args); } + + public SpringbootApplication() {} } diff --git a/smoke-tests/images/spring-boot/src/main/java/io/opentelemetry/smoketest/springboot/controller/WebController.java b/smoke-tests/images/spring-boot/src/main/java/io/opentelemetry/smoketest/springboot/controller/WebController.java index 891aa678ce26..a0376e378049 100644 --- a/smoke-tests/images/spring-boot/src/main/java/io/opentelemetry/smoketest/springboot/controller/WebController.java +++ b/smoke-tests/images/spring-boot/src/main/java/io/opentelemetry/smoketest/springboot/controller/WebController.java @@ -5,7 +5,7 @@ package io.opentelemetry.smoketest.springboot.controller; -import io.opentelemetry.extension.annotations.WithSpan; +import io.opentelemetry.instrumentation.annotations.WithSpan; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.bind.annotation.RequestMapping; diff --git a/smoke-tests/src/test/groovy/io/opentelemetry/smoketest/AppServerTest.groovy b/smoke-tests/src/test/groovy/io/opentelemetry/smoketest/AppServerTest.groovy index e9dd23dace75..938cf86c86c3 100644 --- a/smoke-tests/src/test/groovy/io/opentelemetry/smoketest/AppServerTest.groovy +++ b/smoke-tests/src/test/groovy/io/opentelemetry/smoketest/AppServerTest.groovy @@ -48,7 +48,7 @@ abstract class AppServerTest extends SmokeTest { @Override protected String getTargetImage(String jdk, String serverVersion, boolean windows) { String platformSuffix = windows ? "-windows" : "" - String extraTag = "20211216.1584506476" + String extraTag = "20220731.2770161172" String fullSuffix = "${serverVersion}-jdk$jdk$platformSuffix-$extraTag" return getTargetImagePrefix() + ":" + fullSuffix } diff --git a/smoke-tests/src/test/groovy/io/opentelemetry/smoketest/JettySmokeTest.groovy b/smoke-tests/src/test/groovy/io/opentelemetry/smoketest/JettySmokeTest.groovy index 4aaacbe32f9f..5e5b448b7422 100644 --- a/smoke-tests/src/test/groovy/io/opentelemetry/smoketest/JettySmokeTest.groovy +++ b/smoke-tests/src/test/groovy/io/opentelemetry/smoketest/JettySmokeTest.groovy @@ -31,10 +31,15 @@ class Jetty9Jdk11 extends JettySmokeTest { class Jetty9Jdk17 extends JettySmokeTest { } +// TODO (trask) remove Java 18 test once Java 19 is GA @AppServer(version = "9.4.39", jdk = "18") class Jetty9Jdk18 extends JettySmokeTest { } +@AppServer(version = "9.4.39", jdk = "19") +class Jetty9Jdk19 extends JettySmokeTest { +} + @AppServer(version = "9.4.39", jdk = "8-openj9") class Jetty9Jdk8Openj9 extends JettySmokeTest { } @@ -55,10 +60,15 @@ class Jetty10Jdk11 extends JettySmokeTest { class Jetty10Jdk17 extends JettySmokeTest { } +// TODO (trask) remove Java 18 test once Java 19 is GA @AppServer(version = "10.0.7", jdk = "18") class Jetty10Jdk18 extends JettySmokeTest { } +@AppServer(version = "10.0.7", jdk = "19") +class Jetty10Jdk19 extends JettySmokeTest { +} + @AppServer(version = "10.0.7", jdk = "11-openj9") class Jetty10Jdk11Openj9 extends JettySmokeTest { } @@ -75,10 +85,15 @@ class Jetty11Jdk11 extends JettySmokeTest { class Jetty11Jdk17 extends JettySmokeTest { } +// TODO (trask) remove Java 18 test once Java 19 is GA @AppServer(version = "11.0.7", jdk = "18") class Jetty11Jdk18 extends JettySmokeTest { } +@AppServer(version = "11.0.7", jdk = "19") +class Jetty11Jdk19 extends JettySmokeTest { +} + @AppServer(version = "11.0.7", jdk = "11-openj9") class Jetty11Jdk11Openj9 extends JettySmokeTest { } diff --git a/smoke-tests/src/test/groovy/io/opentelemetry/smoketest/PrometheusSmokeTest.groovy b/smoke-tests/src/test/groovy/io/opentelemetry/smoketest/PrometheusSmokeTest.groovy index ce86e16160db..7b3b375bdccb 100644 --- a/smoke-tests/src/test/groovy/io/opentelemetry/smoketest/PrometheusSmokeTest.groovy +++ b/smoke-tests/src/test/groovy/io/opentelemetry/smoketest/PrometheusSmokeTest.groovy @@ -6,6 +6,8 @@ package io.opentelemetry.smoketest import io.opentelemetry.testing.internal.armeria.client.WebClient +import spock.lang.Ignore + import java.time.Duration import spock.lang.IgnoreIf @@ -33,6 +35,7 @@ class PrometheusSmokeTest extends SmokeTest { return [PROMETHEUS_PORT] } + @Ignore // We've disabled the metrics provider def "Should export metrics"(int jdk) { setup: startTarget(jdk) diff --git a/smoke-tests/src/test/groovy/io/opentelemetry/smoketest/NoopApiSmokeTest.groovy b/smoke-tests/src/test/groovy/io/opentelemetry/smoketest/SdkDisabledSmokeTest.groovy similarity index 93% rename from smoke-tests/src/test/groovy/io/opentelemetry/smoketest/NoopApiSmokeTest.groovy rename to smoke-tests/src/test/groovy/io/opentelemetry/smoketest/SdkDisabledSmokeTest.groovy index 74d6a0dc4e00..beafb9b717a8 100644 --- a/smoke-tests/src/test/groovy/io/opentelemetry/smoketest/NoopApiSmokeTest.groovy +++ b/smoke-tests/src/test/groovy/io/opentelemetry/smoketest/SdkDisabledSmokeTest.groovy @@ -15,7 +15,7 @@ import java.util.jar.JarFile import static io.opentelemetry.smoketest.TestContainerManager.useWindowsContainers @IgnoreIf({ useWindowsContainers() }) -class NoopApiSmokeTest extends SmokeTest { +class SdkDisabledSmokeTest extends SmokeTest { protected String getTargetImage(String jdk) { "ghcr.io/open-telemetry/opentelemetry-java-instrumentation/smoke-test-spring-boot:jdk$jdk-20211213.1570880324" @@ -24,7 +24,7 @@ class NoopApiSmokeTest extends SmokeTest { @Override protected Map getExtraEnv() { return [ - "OTEL_JAVAAGENT_EXPERIMENTAL_USE_NOOP_API": "true" + "OTEL_EXPERIMENTAL_SDK_ENABLED": "false" ] } diff --git a/smoke-tests/src/test/groovy/io/opentelemetry/smoketest/SpringBootSmokeTest.groovy b/smoke-tests/src/test/groovy/io/opentelemetry/smoketest/SpringBootSmokeTest.groovy index b013a87a0a73..b03dc85dd709 100644 --- a/smoke-tests/src/test/groovy/io/opentelemetry/smoketest/SpringBootSmokeTest.groovy +++ b/smoke-tests/src/test/groovy/io/opentelemetry/smoketest/SpringBootSmokeTest.groovy @@ -75,12 +75,12 @@ class SpringBootSmokeTest extends SmokeTest { .collect(toSet()) loggedTraceIds == spanTraceIds - then: "JVM metrics are exported" - def metrics = new MetricsInspector(waitForMetrics()) - metrics.hasMetricsNamed("process.runtime.jvm.memory.init") - metrics.hasMetricsNamed("process.runtime.jvm.memory.usage") - metrics.hasMetricsNamed("process.runtime.jvm.memory.committed") - metrics.hasMetricsNamed("process.runtime.jvm.memory.limit") +// then: "JVM metrics are exported" +// def metrics = new MetricsInspector(waitForMetrics()) +// metrics.hasMetricsNamed("process.runtime.jvm.memory.init") +// metrics.hasMetricsNamed("process.runtime.jvm.memory.usage") +// metrics.hasMetricsNamed("process.runtime.jvm.memory.committed") +// metrics.hasMetricsNamed("process.runtime.jvm.memory.limit") cleanup: stopTarget() diff --git a/smoke-tests/src/test/groovy/io/opentelemetry/smoketest/TelemetryRetriever.groovy b/smoke-tests/src/test/groovy/io/opentelemetry/smoketest/TelemetryRetriever.groovy index 44dc4084aaec..04529cda2f9c 100644 --- a/smoke-tests/src/test/groovy/io/opentelemetry/smoketest/TelemetryRetriever.groovy +++ b/smoke-tests/src/test/groovy/io/opentelemetry/smoketest/TelemetryRetriever.groovy @@ -63,7 +63,7 @@ class TelemetryRetriever { break } previousSize = content.length() - println "Curent content size $previousSize" + println "Current content size $previousSize" TimeUnit.MILLISECONDS.sleep(500) } diff --git a/smoke-tests/src/test/groovy/io/opentelemetry/smoketest/TomcatSmokeTest.groovy b/smoke-tests/src/test/groovy/io/opentelemetry/smoketest/TomcatSmokeTest.groovy index b014092d5495..cfe7fc664a88 100644 --- a/smoke-tests/src/test/groovy/io/opentelemetry/smoketest/TomcatSmokeTest.groovy +++ b/smoke-tests/src/test/groovy/io/opentelemetry/smoketest/TomcatSmokeTest.groovy @@ -39,10 +39,15 @@ class Tomcat8Jdk11 extends TomcatSmokeTest { class Tomcat8Jdk17 extends TomcatSmokeTest { } +// TODO (trask) remove Java 18 test once Java 19 is GA @AppServer(version = "8.5.72", jdk = "18") class Tomcat8Jdk18 extends TomcatSmokeTest { } +@AppServer(version = "8.5.72", jdk = "19") +class Tomcat8Jdk19 extends TomcatSmokeTest { +} + @AppServer(version = "8.5.72", jdk = "8-openj9") class Tomcat8Jdk8Openj9 extends TomcatSmokeTest { } @@ -63,10 +68,15 @@ class Tomcat9Jdk11 extends TomcatSmokeTest { class Tomcat9Jdk17 extends TomcatSmokeTest { } +// TODO (trask) remove Java 18 test once Java 19 is GA @AppServer(version = "9.0.54", jdk = "18") class Tomcat9Jdk18 extends TomcatSmokeTest { } +@AppServer(version = "9.0.54", jdk = "19") +class Tomcat9Jdk19 extends TomcatSmokeTest { +} + @AppServer(version = "9.0.54", jdk = "8-openj9") class Tomcat9Jdk8Openj9 extends TomcatSmokeTest { } @@ -87,10 +97,15 @@ class Tomcat10Jdk11 extends TomcatSmokeTest { class Tomcat10Jdk17 extends TomcatSmokeTest { } +// TODO (trask) remove Java 18 test once Java 19 is GA @AppServer(version = "10.0.12", jdk = "18") class Tomcat10Jdk18 extends TomcatSmokeTest { } +@AppServer(version = "10.0.12", jdk = "19") +class Tomcat10Jdk19 extends TomcatSmokeTest { +} + @AppServer(version = "10.0.12", jdk = "8-openj9") class Tomcat10Jdk8Openj9 extends TomcatSmokeTest { } diff --git a/smoke-tests/src/test/groovy/io/opentelemetry/smoketest/TomeeSmokeTest.groovy b/smoke-tests/src/test/groovy/io/opentelemetry/smoketest/TomeeSmokeTest.groovy index b55c2c87d3d4..057ab056a12d 100644 --- a/smoke-tests/src/test/groovy/io/opentelemetry/smoketest/TomeeSmokeTest.groovy +++ b/smoke-tests/src/test/groovy/io/opentelemetry/smoketest/TomeeSmokeTest.groovy @@ -56,10 +56,15 @@ class Tomee8Jdk11 extends TomeeSmokeTest { class Tomee8Jdk17 extends TomeeSmokeTest { } +// TODO (trask) remove Java 18 test once Java 19 is GA @AppServer(version = "8.0.8", jdk = "18") class Tomee8Jdk18 extends TomeeSmokeTest { } +@AppServer(version = "8.0.8", jdk = "19") +class Tomee8Jdk19 extends TomeeSmokeTest { +} + @AppServer(version = "8.0.8", jdk = "8-openj9") class Tomee8Jdk8Openj9 extends TomeeSmokeTest { } @@ -84,10 +89,15 @@ class Tomee9Jdk11 extends TomeeSmokeTest { class Tomee9Jdk17 extends TomeeSmokeTest { } +// TODO (trask) remove Java 18 test once Java 19 is GA @AppServer(version = "9.0.0-M7", jdk = "18") class Tomee9Jdk18 extends TomeeSmokeTest { } +@AppServer(version = "9.0.0-M7", jdk = "19") +class Tomee9Jdk19 extends TomeeSmokeTest { +} + @AppServer(version = "9.0.0-M7", jdk = "8-openj9") class Tomee9Jdk8Openj9 extends TomeeSmokeTest { } diff --git a/smoke-tests/src/test/groovy/io/opentelemetry/smoketest/WildflySmokeTest.groovy b/smoke-tests/src/test/groovy/io/opentelemetry/smoketest/WildflySmokeTest.groovy index 4e1ae6a89d88..8e4a8c6eb416 100644 --- a/smoke-tests/src/test/groovy/io/opentelemetry/smoketest/WildflySmokeTest.groovy +++ b/smoke-tests/src/test/groovy/io/opentelemetry/smoketest/WildflySmokeTest.groovy @@ -41,8 +41,19 @@ abstract class WildflySmokeTest extends AppServerTest { } } +abstract class WildflyJdk8SmokeTest extends WildflySmokeTest { + @Override + protected Map getExtraEnv() { + // https://github.com/openjdk/jdk8u/commit/d72d28967d732ba32e02178b828255378c5a8938 + // introduces a changes that causes wildfly to throw java.io.FileNotFoundException: Invalid file + // path on windows + return Collections.singletonMap("JAVA_OPTS", "-Djdk.io.File.enableADS=true " + + "-Djava.net.preferIPv4Stack=true -Djava.awt.headless=true") + } +} + @AppServer(version = "13.0.0.Final", jdk = "8") -class Wildfly13Jdk8 extends WildflySmokeTest { +class Wildfly13Jdk8 extends WildflyJdk8SmokeTest { } @AppServer(version = "13.0.0.Final", jdk = "8-openj9") @@ -50,7 +61,7 @@ class Wildfly13Jdk8Openj9 extends WildflySmokeTest { } @AppServer(version = "17.0.1.Final", jdk = "8") -class Wildfly17Jdk8 extends WildflySmokeTest { +class Wildfly17Jdk8 extends WildflyJdk8SmokeTest { } @AppServer(version = "17.0.1.Final", jdk = "11") @@ -61,12 +72,17 @@ class Wildfly17Jdk11 extends WildflySmokeTest { class Wildfly17Jdk17 extends WildflySmokeTest { } +// TODO (trask) remove Java 18 test once Java 19 is GA @AppServer(version = "17.0.1.Final", jdk = "18") -class Wildfly17Jdk18 extends WildflySmokeTest { +class Wildfly17Jdk18 extends WildflyJdk8SmokeTest { +} + +@AppServer(version = "17.0.1.Final", jdk = "19") +class Wildfly17Jdk19 extends WildflySmokeTest { } @AppServer(version = "21.0.0.Final", jdk = "8") -class Wildfly21Jdk8 extends WildflySmokeTest { +class Wildfly21Jdk8 extends WildflyJdk8SmokeTest { } @AppServer(version = "21.0.0.Final", jdk = "11") @@ -77,12 +93,17 @@ class Wildfly21Jdk11 extends WildflySmokeTest { class Wildfly21Jdk17 extends WildflySmokeTest { } +// TODO (trask) remove Java 18 test once Java 19 is GA @AppServer(version = "21.0.0.Final", jdk = "18") -class Wildfly21Jdk18 extends WildflySmokeTest { +class Wildfly21Jdk18 extends WildflyJdk8SmokeTest { +} + +@AppServer(version = "21.0.0.Final", jdk = "19") +class Wildfly21Jdk19 extends WildflySmokeTest { } @AppServer(version = "25.0.1.Final", jdk = "8") -class Wildfly25Jdk8 extends WildflySmokeTest { +class Wildfly25Jdk8 extends WildflyJdk8SmokeTest { } @AppServer(version = "25.0.1.Final", jdk = "11") @@ -93,8 +114,13 @@ class Wildfly25Jdk11 extends WildflySmokeTest { class Wildfly25Jdk17 extends WildflySmokeTest { } +// TODO (trask) remove Java 18 test once Java 19 is GA @AppServer(version = "25.0.1.Final", jdk = "18") -class Wildfly25Jdk18 extends WildflySmokeTest { +class Wildfly25Jdk18 extends WildflyJdk8SmokeTest { +} + +@AppServer(version = "25.0.1.Final", jdk = "19") +class Wildfly25Jdk19 extends WildflySmokeTest { } @AppServer(version = "17.0.1.Final", jdk = "8-openj9") diff --git a/testing-common/build.gradle.kts b/testing-common/build.gradle.kts index 2b15c2dc6ab3..1191a7386c8b 100644 --- a/testing-common/build.gradle.kts +++ b/testing-common/build.gradle.kts @@ -37,6 +37,7 @@ dependencies { api("io.opentelemetry:opentelemetry-semconv") api("io.opentelemetry:opentelemetry-sdk") api("io.opentelemetry:opentelemetry-sdk-testing") + api("io.opentelemetry:opentelemetry-sdk-logs-testing") api("io.opentelemetry:opentelemetry-sdk-metrics") api("io.opentelemetry:opentelemetry-sdk-logs") api(project(":instrumentation-api")) @@ -46,18 +47,17 @@ dependencies { api("org.awaitility:awaitility") api("com.google.guava:guava") api("org.mockito:mockito-core") + api("org.slf4j:slf4j-api") compileOnly(project(":testing:armeria-shaded-for-testing", configuration = "shadow")) implementation("io.opentelemetry.proto:opentelemetry-proto") implementation("net.bytebuddy:byte-buddy") - implementation("org.slf4j:slf4j-api") implementation("ch.qos.logback:logback-classic") implementation("org.slf4j:log4j-over-slf4j") implementation("org.slf4j:jcl-over-slf4j") implementation("org.slf4j:jul-to-slf4j") - implementation("io.opentelemetry:opentelemetry-extension-annotations") implementation("io.opentelemetry:opentelemetry-exporter-logging") annotationProcessor("com.google.auto.service:auto-service") diff --git a/testing-common/integration-tests/build.gradle.kts b/testing-common/integration-tests/build.gradle.kts index eac4498d2a83..0107f04a4a02 100644 --- a/testing-common/integration-tests/build.gradle.kts +++ b/testing-common/integration-tests/build.gradle.kts @@ -14,7 +14,7 @@ dependencies { testImplementation("net.bytebuddy:byte-buddy-agent") testImplementation("com.google.guava:guava") - testImplementation("io.opentelemetry:opentelemetry-extension-annotations") + testImplementation(project(":instrumentation-annotations")) testImplementation("cglib:cglib:3.2.5") @@ -40,6 +40,9 @@ tasks { // this test uses reflection to access fields generated by FieldBackedProvider // internal-reflection needs to be disabled because it removes these fields from reflection results. jvmArgs("-Dotel.instrumentation.internal-reflection.enabled=false") + // required on jdk17 + jvmArgs("--add-opens=java.base/java.lang=ALL-UNNAMED") + jvmArgs("-XX:+IgnoreUnrecognizedVMOptions") } test { diff --git a/testing-common/integration-tests/src/test/java/config/SomeClass.java b/testing-common/integration-tests/src/test/java/config/SomeClass.java index 0b88f99f5c31..949cd4df2e90 100644 --- a/testing-common/integration-tests/src/test/java/config/SomeClass.java +++ b/testing-common/integration-tests/src/test/java/config/SomeClass.java @@ -5,7 +5,7 @@ package config; -import io.opentelemetry.extension.annotations.WithSpan; +import io.opentelemetry.instrumentation.annotations.WithSpan; public class SomeClass implements Runnable { diff --git a/testing-common/integration-tests/src/test/java/config/exclude/SomeClass.java b/testing-common/integration-tests/src/test/java/config/exclude/SomeClass.java index 7db69d1341b7..eb21692e8d12 100644 --- a/testing-common/integration-tests/src/test/java/config/exclude/SomeClass.java +++ b/testing-common/integration-tests/src/test/java/config/exclude/SomeClass.java @@ -5,7 +5,7 @@ package config.exclude; -import io.opentelemetry.extension.annotations.WithSpan; +import io.opentelemetry.instrumentation.annotations.WithSpan; public class SomeClass implements Runnable { diff --git a/testing-common/integration-tests/src/test/java/config/exclude/packagename/SomeClass.java b/testing-common/integration-tests/src/test/java/config/exclude/packagename/SomeClass.java index 7f9c2ac1634b..2af72763d6d6 100644 --- a/testing-common/integration-tests/src/test/java/config/exclude/packagename/SomeClass.java +++ b/testing-common/integration-tests/src/test/java/config/exclude/packagename/SomeClass.java @@ -5,7 +5,7 @@ package config.exclude.packagename; -import io.opentelemetry.extension.annotations.WithSpan; +import io.opentelemetry.instrumentation.annotations.WithSpan; public class SomeClass implements Runnable { diff --git a/testing-common/src/main/groovy/io/opentelemetry/instrumentation/test/InstrumentationSpecification.groovy b/testing-common/src/main/groovy/io/opentelemetry/instrumentation/test/InstrumentationSpecification.groovy index b0b8d5c7df1a..6bc478c6a47b 100644 --- a/testing-common/src/main/groovy/io/opentelemetry/instrumentation/test/InstrumentationSpecification.groovy +++ b/testing-common/src/main/groovy/io/opentelemetry/instrumentation/test/InstrumentationSpecification.groovy @@ -15,7 +15,7 @@ import io.opentelemetry.instrumentation.testing.InstrumentationTestRunner import io.opentelemetry.instrumentation.testing.util.ContextStorageCloser import io.opentelemetry.instrumentation.testing.util.TelemetryDataUtil import io.opentelemetry.instrumentation.testing.util.ThrowingSupplier -import io.opentelemetry.sdk.logs.data.LogData +import io.opentelemetry.sdk.logs.data.LogRecordData import io.opentelemetry.sdk.metrics.data.MetricData import io.opentelemetry.sdk.trace.data.SpanData import org.junit.Rule @@ -72,8 +72,8 @@ abstract class InstrumentationSpecification extends Specification { } /** Return a list of all captured logs. */ - List getLogs() { - testRunner().getExportedLogs() + List getLogRecords() { + testRunner().getExportedLogRecords() } /** diff --git a/testing-common/src/main/groovy/io/opentelemetry/instrumentation/test/base/HttpClientTest.groovy b/testing-common/src/main/groovy/io/opentelemetry/instrumentation/test/base/HttpClientTest.groovy index 6e0674d10e82..3749889afd07 100644 --- a/testing-common/src/main/groovy/io/opentelemetry/instrumentation/test/base/HttpClientTest.groovy +++ b/testing-common/src/main/groovy/io/opentelemetry/instrumentation/test/base/HttpClientTest.groovy @@ -5,8 +5,6 @@ package io.opentelemetry.instrumentation.test.base -import static org.junit.jupiter.api.Assumptions.assumeTrue - import io.opentelemetry.api.common.AttributeKey import io.opentelemetry.api.trace.SpanId import io.opentelemetry.instrumentation.test.InstrumentationSpecification @@ -21,6 +19,9 @@ import spock.lang.Requires import spock.lang.Shared import spock.lang.Unroll +import static org.junit.jupiter.api.Assumptions.assumeFalse +import static org.junit.jupiter.api.Assumptions.assumeTrue + @Unroll abstract class HttpClientTest extends InstrumentationSpecification { protected static final BODY_METHODS = ["POST", "PUT"] @@ -199,16 +200,6 @@ abstract class HttpClientTest extends InstrumentationSpecification { return HttpClientTest.this.testHttps() } - @Override - protected boolean testCausality() { - return HttpClientTest.this.testCausality() - } - - @Override - protected boolean testCausalityWithCallback() { - return HttpClientTest.this.testCausalityWithCallback() - } - @Override protected boolean testCallback() { return HttpClientTest.this.testCallback() @@ -221,6 +212,11 @@ abstract class HttpClientTest extends InstrumentationSpecification { return HttpClientTest.this.testCallbackWithParent() } + @Override + protected boolean testCallbackWithImplicitParent() { + return HttpClientTest.this.testCallbackWithImplicitParent() + } + @Override protected boolean testErrorWithCallback() { return HttpClientTest.this.testErrorWithCallback() @@ -294,10 +290,18 @@ abstract class HttpClientTest extends InstrumentationSpecification { def "trace request with callback and no parent"() { assumeTrue(testCallback()) + assumeFalse(testCallbackWithImplicitParent()) expect: junitTest.requestWithCallbackAndNoParent() } + def "trace request with callback and implicit parent"() { + assumeTrue(testCallback()) + assumeTrue(testCallbackWithImplicitParent()) + expect: + junitTest.requestWithCallbackAndImplicitParent() + } + def "basic request with 1 redirect"() { assumeTrue(testRedirects()) expect: @@ -388,14 +392,11 @@ abstract class HttpClientTest extends InstrumentationSpecification { * propagate trace context. */ def "high concurrency test"() { - assumeTrue(testCausality()) expect: junitTest.highConcurrency() } def "high concurrency test with callback"() { - assumeTrue(testCausality()) - assumeTrue(testCausalityWithCallback()) assumeTrue(testCallback()) assumeTrue(testCallbackWithParent()) expect: @@ -479,14 +480,6 @@ abstract class HttpClientTest extends InstrumentationSpecification { true } - boolean testCausality() { - true - } - - boolean testCausalityWithCallback() { - true - } - boolean testCallback() { return true } @@ -497,6 +490,13 @@ abstract class HttpClientTest extends InstrumentationSpecification { true } + boolean testCallbackWithImplicitParent() { + // depending on async behavior callback can be executed within + // parent span scope or outside of the scope, e.g. in reactor-netty or spring + // callback is correlated. + false + } + boolean testErrorWithCallback() { return true } diff --git a/testing-common/src/main/groovy/io/opentelemetry/instrumentation/test/base/HttpServerTest.groovy b/testing-common/src/main/groovy/io/opentelemetry/instrumentation/test/base/HttpServerTest.groovy index 7b3524a84c2a..83b484ebaa02 100644 --- a/testing-common/src/main/groovy/io/opentelemetry/instrumentation/test/base/HttpServerTest.groovy +++ b/testing-common/src/main/groovy/io/opentelemetry/instrumentation/test/base/HttpServerTest.groovy @@ -100,7 +100,7 @@ abstract class HttpServerTest extends InstrumentationSpecification imple false } - String peerIp(ServerEndpoint endpoint) { + String sockPeerAddr(ServerEndpoint endpoint) { "127.0.0.1" } @@ -116,10 +116,18 @@ abstract class HttpServerTest extends InstrumentationSpecification imple true } + boolean testCapturedHttpHeadersAsJson() { + false + } + boolean testCapturedRequestParameters() { false } + boolean testCapturedBody() { + false + } + boolean testErrorBody() { true } @@ -149,7 +157,7 @@ abstract class HttpServerTest extends InstrumentationSpecification imple [ SemanticAttributes.HTTP_ROUTE, SemanticAttributes.NET_TRANSPORT, - SemanticAttributes.NET_PEER_PORT + SemanticAttributes.NET_SOCK_PEER_PORT ] as Set } @@ -199,6 +207,9 @@ abstract class HttpServerTest extends InstrumentationSpecification imple options.hasExceptionOnServerSpan = { endpoint -> HttpServerTest.this.hasExceptionOnServerSpan(endpoint) } + options.sockPeerAddr = { endpoint -> + HttpServerTest.this.sockPeerAddr(endpoint) + } options.testRedirect = testRedirect() options.testError = testError() @@ -207,7 +218,9 @@ abstract class HttpServerTest extends InstrumentationSpecification imple options.testNotFound = testNotFound() options.testPathParam = testPathParam() options.testCaptureHttpHeaders = testCapturedHttpHeaders() + options.testCaptureHttpHeadersAsJson = testCapturedHttpHeadersAsJson() options.testCaptureRequestParameters = testCapturedRequestParameters() + options.testCaptureBody = testCapturedBody() } // Override trace assertion method. We can call java assertions from groovy but not the other @@ -215,12 +228,12 @@ abstract class HttpServerTest extends InstrumentationSpecification imple // the main trace assertion method to groovy to be able to call these assertions. @Override void assertTheTraces( - int size, - String traceId, - String parentId, - String method, - ServerEndpoint endpoint, - AggregatedHttpResponse response) { + int size, + String traceId, + String parentId, + String method, + ServerEndpoint endpoint, + AggregatedHttpResponse response) { HttpServerTest.this.assertTheTraces(size, traceId, parentId, method, endpoint, response) } @@ -293,12 +306,24 @@ abstract class HttpServerTest extends InstrumentationSpecification imple junitTest.captureHttpHeaders() } + def "test captured HTTP headers as Json"() { + assumeTrue(testCapturedHttpHeadersAsJson()) + expect: + junitTest.captureHttpHeadersAsJson() + } + def "test captured request parameters"() { assumeTrue(testCapturedRequestParameters()) expect: junitTest.captureRequestParameters() } + def "test captured body"() { + assumeTrue(testCapturedBody()) + expect: + junitTest.captureBody() + } + def "high concurrency test"() { expect: junitTest.highConcurrency() diff --git a/testing-common/src/main/java/io/opentelemetry/instrumentation/testing/AgentTestRunner.java b/testing-common/src/main/java/io/opentelemetry/instrumentation/testing/AgentTestRunner.java index 59f83b9c5977..dab38d0a8ddb 100644 --- a/testing-common/src/main/java/io/opentelemetry/instrumentation/testing/AgentTestRunner.java +++ b/testing-common/src/main/java/io/opentelemetry/instrumentation/testing/AgentTestRunner.java @@ -12,7 +12,7 @@ import io.opentelemetry.instrumentation.test.utils.LoggerUtils; import io.opentelemetry.javaagent.testing.common.AgentTestingExporterAccess; import io.opentelemetry.javaagent.testing.common.TestAgentListenerAccess; -import io.opentelemetry.sdk.logs.data.LogData; +import io.opentelemetry.sdk.logs.data.LogRecordData; import io.opentelemetry.sdk.metrics.data.MetricData; import io.opentelemetry.sdk.trace.data.SpanData; import java.util.List; @@ -84,8 +84,8 @@ public List getExportedMetrics() { } @Override - public List getExportedLogs() { - return AgentTestingExporterAccess.getExportedLogs(); + public List getExportedLogRecords() { + return AgentTestingExporterAccess.getExportedLogRecords(); } @Override diff --git a/testing-common/src/main/java/io/opentelemetry/instrumentation/testing/InstrumentationTestRunner.java b/testing-common/src/main/java/io/opentelemetry/instrumentation/testing/InstrumentationTestRunner.java index 064e57d834a4..8938f571cf26 100644 --- a/testing-common/src/main/java/io/opentelemetry/instrumentation/testing/InstrumentationTestRunner.java +++ b/testing-common/src/main/java/io/opentelemetry/instrumentation/testing/InstrumentationTestRunner.java @@ -11,7 +11,7 @@ import io.opentelemetry.instrumentation.testing.util.TelemetryDataUtil; import io.opentelemetry.instrumentation.testing.util.ThrowingRunnable; import io.opentelemetry.instrumentation.testing.util.ThrowingSupplier; -import io.opentelemetry.sdk.logs.data.LogData; +import io.opentelemetry.sdk.logs.data.LogRecordData; import io.opentelemetry.sdk.metrics.data.MetricData; import io.opentelemetry.sdk.testing.assertj.TraceAssert; import io.opentelemetry.sdk.testing.assertj.TracesAssert; @@ -53,7 +53,7 @@ protected InstrumentationTestRunner(OpenTelemetry openTelemetry) { public abstract List getExportedMetrics(); - public abstract List getExportedLogs(); + public abstract List getExportedLogRecords(); public abstract boolean forceFlushCalled(); diff --git a/testing-common/src/main/java/io/opentelemetry/instrumentation/testing/LibraryTestRunner.java b/testing-common/src/main/java/io/opentelemetry/instrumentation/testing/LibraryTestRunner.java index 940c2acf89a2..e22444cb99b0 100644 --- a/testing-common/src/main/java/io/opentelemetry/instrumentation/testing/LibraryTestRunner.java +++ b/testing-common/src/main/java/io/opentelemetry/instrumentation/testing/LibraryTestRunner.java @@ -13,10 +13,11 @@ import io.opentelemetry.exporter.logging.LoggingSpanExporter; import io.opentelemetry.sdk.OpenTelemetrySdk; import io.opentelemetry.sdk.common.CompletableResultCode; -import io.opentelemetry.sdk.logs.data.LogData; +import io.opentelemetry.sdk.logs.data.LogRecordData; import io.opentelemetry.sdk.metrics.SdkMeterProvider; import io.opentelemetry.sdk.metrics.data.AggregationTemporality; import io.opentelemetry.sdk.metrics.data.MetricData; +import io.opentelemetry.sdk.metrics.export.MetricReader; import io.opentelemetry.sdk.metrics.export.PeriodicMetricReader; import io.opentelemetry.sdk.testing.exporter.InMemoryMetricExporter; import io.opentelemetry.sdk.testing.exporter.InMemorySpanExporter; @@ -40,6 +41,7 @@ public final class LibraryTestRunner extends InstrumentationTestRunner { private static final OpenTelemetrySdk openTelemetry; private static final InMemorySpanExporter testSpanExporter; private static final InMemoryMetricExporter testMetricExporter; + private static final MetricReader metricReader; private static boolean forceFlushCalled; static { @@ -48,6 +50,13 @@ public final class LibraryTestRunner extends InstrumentationTestRunner { testSpanExporter = InMemorySpanExporter.create(); testMetricExporter = InMemoryMetricExporter.create(AggregationTemporality.DELTA); + metricReader = + PeriodicMetricReader.builder(testMetricExporter) + // Set really long interval. We'll call forceFlush when we need the metrics + // instead of collecting them periodically. + .setInterval(Duration.ofNanos(Long.MAX_VALUE)) + .build(); + openTelemetry = OpenTelemetrySdk.builder() .setTracerProvider( @@ -56,13 +65,7 @@ public final class LibraryTestRunner extends InstrumentationTestRunner { .addSpanProcessor(SimpleSpanProcessor.create(LoggingSpanExporter.create())) .addSpanProcessor(SimpleSpanProcessor.create(testSpanExporter)) .build()) - .setMeterProvider( - SdkMeterProvider.builder() - .registerMetricReader( - PeriodicMetricReader.builder(testMetricExporter) - .setInterval(Duration.ofMillis(100)) - .build()) - .build()) + .setMeterProvider(SdkMeterProvider.builder().registerMetricReader(metricReader).build()) .setPropagators(ContextPropagators.create(W3CTraceContextPropagator.getInstance())) .buildAndRegisterGlobal(); } @@ -114,11 +117,12 @@ public List getExportedSpans() { @Override public List getExportedMetrics() { + metricReader.forceFlush().join(10, TimeUnit.SECONDS); return testMetricExporter.getFinishedMetricItems(); } @Override - public List getExportedLogs() { + public List getExportedLogRecords() { // no logs support yet return Collections.emptyList(); } diff --git a/testing-common/src/main/java/io/opentelemetry/instrumentation/testing/TestInstrumenters.java b/testing-common/src/main/java/io/opentelemetry/instrumentation/testing/TestInstrumenters.java index 1474972bba58..203ae7b2e64f 100644 --- a/testing-common/src/main/java/io/opentelemetry/instrumentation/testing/TestInstrumenters.java +++ b/testing-common/src/main/java/io/opentelemetry/instrumentation/testing/TestInstrumenters.java @@ -31,19 +31,19 @@ final class TestInstrumenters { TestInstrumenters(OpenTelemetry openTelemetry) { instrumenter = Instrumenter.builder(openTelemetry, "test", name -> name) - .newInstrumenter(SpanKindExtractor.alwaysInternal()); + .buildInstrumenter(SpanKindExtractor.alwaysInternal()); httpClientInstrumenter = Instrumenter.builder(openTelemetry, "test", name -> name) // cover both semconv and span-kind strategies .addAttributesExtractor(new SpanKeyAttributesExtractor(SpanKey.HTTP_CLIENT)) .addAttributesExtractor(new SpanKeyAttributesExtractor(SpanKey.KIND_CLIENT)) - .newInstrumenter(SpanKindExtractor.alwaysClient()); + .buildInstrumenter(SpanKindExtractor.alwaysClient()); httpServerInstrumenter = Instrumenter.builder(openTelemetry, "test", name -> name) // cover both semconv and span-kind strategies .addAttributesExtractor(new SpanKeyAttributesExtractor(SpanKey.HTTP_SERVER)) .addAttributesExtractor(new SpanKeyAttributesExtractor(SpanKey.KIND_SERVER)) - .newInstrumenter(SpanKindExtractor.alwaysServer()); + .buildInstrumenter(SpanKindExtractor.alwaysServer()); } /** diff --git a/testing-common/src/main/java/io/opentelemetry/instrumentation/testing/junit/InstrumentationExtension.java b/testing-common/src/main/java/io/opentelemetry/instrumentation/testing/junit/InstrumentationExtension.java index e790da8f6c5b..9f0816ffb323 100644 --- a/testing-common/src/main/java/io/opentelemetry/instrumentation/testing/junit/InstrumentationExtension.java +++ b/testing-common/src/main/java/io/opentelemetry/instrumentation/testing/junit/InstrumentationExtension.java @@ -16,6 +16,7 @@ import io.opentelemetry.instrumentation.testing.util.ThrowingRunnable; import io.opentelemetry.instrumentation.testing.util.ThrowingSupplier; import io.opentelemetry.sdk.OpenTelemetrySdk; +import io.opentelemetry.sdk.logs.data.LogRecordData; import io.opentelemetry.sdk.metrics.data.MetricData; import io.opentelemetry.sdk.testing.assertj.TraceAssert; import io.opentelemetry.sdk.trace.data.SpanData; @@ -74,6 +75,11 @@ public List metrics() { return testRunner.getExportedMetrics(); } + /** Return a list of all captured logs. */ + public List logRecords() { + return testRunner.getExportedLogRecords(); + } + /** * Waits for the assertion applied to all metrics of the given instrumentation and metric name to * pass. @@ -186,7 +192,7 @@ public boolean forceFlushCalled() { return testRunner.forceFlushCalled(); } - /** Returns the {@link OpenTelemetrySdk} initialied for library tests. */ + /** Returns the {@link OpenTelemetrySdk} initialized for library tests. */ public OpenTelemetrySdk getOpenTelemetrySdk() { if (testRunner instanceof LibraryTestRunner) { return ((LibraryTestRunner) testRunner).getOpenTelemetrySdk(); diff --git a/testing-common/src/main/java/io/opentelemetry/instrumentation/testing/junit/db/DbConnectionPoolMetricsAssertions.java b/testing-common/src/main/java/io/opentelemetry/instrumentation/testing/junit/db/DbConnectionPoolMetricsAssertions.java index f16b197b3c70..1d1dfc9d504f 100644 --- a/testing-common/src/main/java/io/opentelemetry/instrumentation/testing/junit/db/DbConnectionPoolMetricsAssertions.java +++ b/testing-common/src/main/java/io/opentelemetry/instrumentation/testing/junit/db/DbConnectionPoolMetricsAssertions.java @@ -9,6 +9,7 @@ import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; +import com.google.errorprone.annotations.CanIgnoreReturnValue; import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.api.common.Attributes; import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; @@ -46,41 +47,49 @@ public static DbConnectionPoolMetricsAssertions create( this.poolName = poolName; } + @CanIgnoreReturnValue public DbConnectionPoolMetricsAssertions disableMinIdleConnections() { testMinIdleConnections = false; return this; } + @CanIgnoreReturnValue public DbConnectionPoolMetricsAssertions disableMaxIdleConnections() { testMaxIdleConnections = false; return this; } + @CanIgnoreReturnValue public DbConnectionPoolMetricsAssertions disableMaxConnections() { testMaxConnections = false; return this; } + @CanIgnoreReturnValue public DbConnectionPoolMetricsAssertions disablePendingRequests() { testPendingRequests = false; return this; } + @CanIgnoreReturnValue public DbConnectionPoolMetricsAssertions disableConnectionTimeouts() { testConnectionTimeouts = false; return this; } + @CanIgnoreReturnValue public DbConnectionPoolMetricsAssertions disableCreateTime() { testCreateTime = false; return this; } + @CanIgnoreReturnValue public DbConnectionPoolMetricsAssertions disableWaitTime() { testWaitTime = false; return this; } + @CanIgnoreReturnValue public DbConnectionPoolMetricsAssertions disableUseTime() { testUseTime = false; return this; @@ -123,7 +132,7 @@ private void verifyConnectionUsage() { private void verifyUsageMetric(MetricData metric) { assertThat(metric) - .hasUnit("connections") + .hasUnit("{connections}") .hasDescription( "The number of connections that are currently in state described by the state attribute.") .hasLongSumSatisfying( @@ -147,7 +156,7 @@ private void verifyMaxConnections() { private void verifyMaxConnectionsMetric(MetricData metric) { assertThat(metric) - .hasUnit("connections") + .hasUnit("{connections}") .hasDescription("The maximum number of open connections allowed.") .hasLongSumSatisfying(this::verifyPoolName); } @@ -161,7 +170,7 @@ private void verifyMinIdleConnections() { private void verifyMinIdleConnectionsMetric(MetricData metric) { assertThat(metric) - .hasUnit("connections") + .hasUnit("{connections}") .hasDescription("The minimum number of idle open connections allowed.") .hasLongSumSatisfying(this::verifyPoolName); } @@ -175,7 +184,7 @@ private void verifyMaxIdleConnections() { private void verifyMaxIdleConnectionsMetric(MetricData metric) { assertThat(metric) - .hasUnit("connections") + .hasUnit("{connections}") .hasDescription("The maximum number of idle open connections allowed.") .hasLongSumSatisfying(this::verifyPoolName); } @@ -194,7 +203,7 @@ private void verifyPendingRequests() { private void verifyPendingRequestsMetric(MetricData metric) { assertThat(metric) - .hasUnit("requests") + .hasUnit("{requests}") .hasDescription( "The number of pending requests for an open connection, cumulative for the entire pool.") .hasLongSumSatisfying(this::verifyPoolName); @@ -209,7 +218,7 @@ private void verifyTimeouts() { private void verifyTimeoutsMetric(MetricData metric) { assertThat(metric) - .hasUnit("timeouts") + .hasUnit("{timeouts}") .hasDescription( "The number of connection timeouts that have occurred trying to obtain a connection from the pool.") .hasLongSumSatisfying( 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 2e493720e680..6636918eb1e8 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 @@ -6,7 +6,9 @@ package io.opentelemetry.instrumentation.testing.junit.http; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; +import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.NetTransportValues.IP_TCP; import static org.assertj.core.api.Assertions.catchThrowable; +import static org.junit.Assume.assumeFalse; import static org.junit.jupiter.api.Assumptions.assumeTrue; import io.opentelemetry.api.common.AttributeKey; @@ -63,7 +65,8 @@ public abstract class AbstractHttpClientTest { * request a second time to verify that the traceparent header is not added multiple times to the * request, and that the last one wins. Tests will fail if the header shows multiple times. */ - protected abstract REQUEST buildRequest(String method, URI uri, Map headers); + protected abstract REQUEST buildRequest(String method, URI uri, Map headers) + throws Exception; /** * Helper class for capturing result of asynchronous request and running a callback when result is @@ -137,7 +140,7 @@ protected final Duration readTimeout() { return READ_TIMEOUT; } - private InstrumentationTestRunner testing; + protected InstrumentationTestRunner testing; private HttpClientTestServer server; private final HttpClientTestOptions options = new HttpClientTestOptions(); @@ -180,12 +183,6 @@ void setupOptions() { if (!testHttps()) { options.disableTestHttps(); } - if (!testCausality()) { - options.disableTestCausality(); - } - if (!testCausalityWithCallback()) { - options.disableTestCausalityWithCallback(); - } if (!testCallback()) { options.disableTestCallback(); } @@ -195,7 +192,9 @@ void setupOptions() { if (!testErrorWithCallback()) { options.disableTestErrorWithCallback(); } - + if (testCallbackWithImplicitParent()) { + options.enableTestCallbackWithImplicitParent(); + } configure(options); } @@ -304,6 +303,7 @@ void requestWithCallbackAndParent() throws Throwable { @Test void requestWithCallbackAndNoParent() throws Throwable { assumeTrue(options.testCallback); + assumeFalse(options.testCallbackWithImplicitParent); String method = "GET"; URI uri = resolveAddress("/success"); @@ -324,6 +324,29 @@ void requestWithCallbackAndNoParent() throws Throwable { span -> span.hasName("callback").hasKind(SpanKind.INTERNAL).hasNoParent())); } + @Test + void requestWithCallbackAndImplicitParent() throws Throwable { + assumeTrue(options.testCallbackWithImplicitParent); + + String method = "GET"; + URI uri = resolveAddress("/success"); + + RequestResult result = + doRequestWithCallback(method, uri, () -> testing.runWithSpan("callback", () -> {})); + + assertThat(result.get()).isEqualTo(200); + + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> assertClientSpan(span, uri, method, 200).hasNoParent(), + span -> assertServerSpan(span).hasParent(trace.getSpan(0)), + span -> + span.hasName("callback") + .hasKind(SpanKind.INTERNAL) + .hasParent(trace.getSpan(0)))); + } + @Test void basicRequestWith1Redirect() throws Exception { // TODO quite a few clients create an extra span for the redirect @@ -675,8 +698,6 @@ void httpsRequest() throws Exception { */ @Test void highConcurrency() { - assumeTrue(options.testCausality); - int count = 50; String method = "GET"; URI uri = resolveAddress("/success"); @@ -748,8 +769,6 @@ void highConcurrency() { @Test void highConcurrencyWithCallback() { - assumeTrue(options.testCausality); - assumeTrue(options.testCausalityWithCallback); assumeTrue(options.testCallback); assumeTrue(options.testCallbackWithParent); @@ -911,14 +930,18 @@ SpanDataAssert assertClientSpan( .hasKind(SpanKind.CLIENT) .hasAttributesSatisfying( attrs -> { + // TODO: Move to test knob rather than always treating as optional + if (attrs.get(SemanticAttributes.NET_TRANSPORT) != null) { + assertThat(attrs).containsEntry(SemanticAttributes.NET_TRANSPORT, IP_TCP); + } + if (uri.getPort() == PortUtils.UNUSABLE_PORT || uri.getHost().equals("192.0.2.1")) { - // TODO(anuraaga): For theses cases, there isn't actually a peer so we shouldn't be - // filling in peer information but some instrumentation does so based on the URL - // itself which is present in HTTP attributes. We should fix this. - if (attrs.asMap().containsKey(SemanticAttributes.NET_PEER_NAME)) { + // TODO: net.peer.name and net.peer.port should always be populated from the URI or + // the Host header, verify these assertions below + if (attrs.get(SemanticAttributes.NET_PEER_NAME) != null) { assertThat(attrs).containsEntry(SemanticAttributes.NET_PEER_NAME, uri.getHost()); } - if (attrs.asMap().containsKey(SemanticAttributes.NET_PEER_PORT)) { + if (attrs.get(SemanticAttributes.NET_PEER_PORT) != null) { if (uri.getPort() > 0) { assertThat(attrs) .containsEntry(SemanticAttributes.NET_PEER_PORT, (long) uri.getPort()); @@ -936,6 +959,16 @@ SpanDataAssert assertClientSpan( }); } } + + // In these cases the peer connection is not established, so the HTTP client should + // not report any socket-level attributes + assertThat(attrs) + .doesNotContainKey("net.sock.family") + // TODO netty sometimes reports net.sock.peer.addr in connection error test + // .doesNotContainKey("net.sock.peer.addr") + .doesNotContainKey("net.sock.peer.name") + .doesNotContainKey("net.sock.peer.port"); + } else { if (httpClientAttributes.contains(SemanticAttributes.NET_PEER_NAME)) { assertThat(attrs).containsEntry(SemanticAttributes.NET_PEER_NAME, uri.getHost()); @@ -943,16 +976,17 @@ SpanDataAssert assertClientSpan( if (httpClientAttributes.contains(SemanticAttributes.NET_PEER_PORT)) { assertThat(attrs).containsEntry(SemanticAttributes.NET_PEER_PORT, uri.getPort()); } - } - // TODO(anuraaga): Move to test knob rather than always treating as optional - if (attrs.asMap().containsKey(SemanticAttributes.NET_PEER_IP)) { - if (uri.getHost().equals("192.0.2.1")) { - // NB(anuraaga): This branch seems to currently only be exercised on Java 15. - // It would be good to understand how the JVM version is impacting this check. - assertThat(attrs).containsEntry(SemanticAttributes.NET_PEER_IP, "192.0.2.1"); - } else { - assertThat(attrs).containsEntry(SemanticAttributes.NET_PEER_IP, "127.0.0.1"); + // TODO: Move to test knob rather than always treating as optional + if (attrs.get(SemanticAttributes.NET_SOCK_PEER_ADDR) != null) { + assertThat(attrs) + .containsEntry(SemanticAttributes.NET_SOCK_PEER_ADDR, "127.0.0.1"); + } + if (attrs.get(SemanticAttributes.NET_SOCK_PEER_PORT) != null) { + assertThat(attrs) + .containsEntry( + SemanticAttributes.NET_SOCK_PEER_PORT, + "https".equals(uri.getScheme()) ? server.httpsPort() : server.httpPort()); } } @@ -978,13 +1012,13 @@ SpanDataAssert assertClientSpan( actual -> assertThat(actual).startsWith(userAgent)); } } - if (httpClientAttributes.contains(SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH)) { + if (attrs.get(SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH) != null) { assertThat(attrs) .hasEntrySatisfying( SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH, length -> assertThat(length).isNotNegative()); } - if (httpClientAttributes.contains(SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH)) { + if (attrs.get(SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH) != null) { assertThat(attrs) .hasEntrySatisfying( SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH, @@ -1077,14 +1111,6 @@ protected boolean testHttps() { return true; } - protected boolean testCausality() { - return true; - } - - protected boolean testCausalityWithCallback() { - return true; - } - protected boolean testCallback() { return true; } @@ -1095,6 +1121,13 @@ protected boolean testCallbackWithParent() { return true; } + protected boolean testCallbackWithImplicitParent() { + // depending on async behavior callback can be executed within + // parent span scope or outside of the scope, e.g. in reactor-netty or spring + // callback is correlated. + return false; + } + protected boolean testErrorWithCallback() { return true; } diff --git a/testing-common/src/main/java/io/opentelemetry/instrumentation/testing/junit/http/AbstractHttpServerTest.java b/testing-common/src/main/java/io/opentelemetry/instrumentation/testing/junit/http/AbstractHttpServerTest.java index 37e99a12cad5..ac74fb53034e 100644 --- a/testing-common/src/main/java/io/opentelemetry/instrumentation/testing/junit/http/AbstractHttpServerTest.java +++ b/testing-common/src/main/java/io/opentelemetry/instrumentation/testing/junit/http/AbstractHttpServerTest.java @@ -5,7 +5,9 @@ package io.opentelemetry.instrumentation.testing.junit.http; +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.CAPTURE_BODY; import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.CAPTURE_HEADERS; +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.CAPTURE_HEADERS_AS_JSON; import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.CAPTURE_PARAMETERS; import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.ERROR; import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.EXCEPTION; @@ -330,6 +332,40 @@ void captureHttpHeaders() { assertTheTraces(1, null, null, "GET", CAPTURE_HEADERS, response); } + @Test + void captureHttpHeadersAsJson() { + assumeTrue(options.testCaptureHttpHeadersAsJson); + + AggregatedHttpRequest request = + AggregatedHttpRequest.of( + request(CAPTURE_HEADERS_AS_JSON, "GET").headers().toBuilder().build()); + AggregatedHttpResponse response = client.execute(request).aggregate().join(); + + assertThat(response.status().code()).isEqualTo(CAPTURE_HEADERS_AS_JSON.getStatus()); + assertThat(response.contentUtf8()).isEqualTo(CAPTURE_HEADERS_AS_JSON.getBody()); + assertThat(response.headers()).isNotEmpty(); + + assertTheTraces(1, null, null, "GET", CAPTURE_HEADERS_AS_JSON, response); + } + + @Test + void captureBody() { + assumeTrue(options.testCaptureBody); + + AggregatedHttpRequest request = + AggregatedHttpRequest.of( + RequestHeaders.builder(HttpMethod.POST, resolveAddress(CAPTURE_BODY)) + .contentType(MediaType.JSON) + .build(), + HttpData.ofUtf8(CAPTURE_BODY.getBody())); + AggregatedHttpResponse response = client.execute(request).aggregate().join(); + + assertThat(response.status().code()).isEqualTo(CAPTURE_BODY.getStatus()); + assertThat(response.contentUtf8()).isEqualTo(CAPTURE_BODY.getBody()); + + assertTheTraces(1, null, null, "POST", CAPTURE_BODY, response); + } + @Test void captureRequestParameters() { assumeTrue(options.testCaptureRequestParameters); @@ -551,19 +587,28 @@ protected SpanDataAssert assertServerSpan( .containsEntry( SemanticAttributes.NET_TRANSPORT, SemanticAttributes.NetTransportValues.IP_TCP); } - if (httpAttributes.contains(SemanticAttributes.NET_PEER_PORT)) { + + assertThat(attrs).containsEntry(SemanticAttributes.NET_HOST_NAME, "localhost"); + // TODO: Move to test knob rather than always treating as optional + if (attrs.get(SemanticAttributes.NET_HOST_PORT) != null) { + assertThat(attrs).containsEntry(SemanticAttributes.NET_HOST_PORT, port); + } + if (attrs.get(SemanticAttributes.NET_SOCK_PEER_PORT) != null) { assertThat(attrs) .hasEntrySatisfying( - SemanticAttributes.NET_PEER_PORT, + SemanticAttributes.NET_SOCK_PEER_PORT, value -> assertThat(value) .isInstanceOf(Long.class) .isNotEqualTo(Long.valueOf(port))); } - if (httpAttributes.contains(SemanticAttributes.NET_PEER_IP) - || attrs.get(SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH) != null) { + if (attrs.get(SemanticAttributes.NET_SOCK_PEER_ADDR) != null) { assertThat(attrs) - .containsEntry(SemanticAttributes.NET_PEER_IP, options.peerIp.apply(endpoint)); + .containsEntry( + SemanticAttributes.NET_SOCK_PEER_ADDR, options.sockPeerAddr.apply(endpoint)); + } + if (attrs.get(SemanticAttributes.NET_SOCK_HOST_ADDR) != null) { + assertThat(attrs).containsEntry(SemanticAttributes.NET_SOCK_HOST_ADDR, "127.0.0.1"); } assertThat(attrs) @@ -583,10 +628,6 @@ protected SpanDataAssert assertServerSpan( assertThat(attrs).containsEntry(SemanticAttributes.HTTP_USER_AGENT, TEST_USER_AGENT); assertThat(attrs).containsEntry(SemanticAttributes.HTTP_SCHEME, "http"); - assertThat(attrs) - .hasEntrySatisfying( - SemanticAttributes.HTTP_HOST, - entry -> assertThat(entry).isIn("localhost", "localhost:" + port)); if (endpoint != INDEXED_CHILD) { assertThat(attrs) .containsEntry( @@ -595,30 +636,20 @@ protected SpanDataAssert assertServerSpan( + (endpoint == QUERY_PARAM ? "?" + endpoint.body : "")); } - if (httpAttributes.contains(SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH) - || attrs.get(SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH) != null) { + if (attrs.get(SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH) != null) { assertThat(attrs) .hasEntrySatisfying( SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH, - entry -> assertThat(entry).isInstanceOf(Long.class)); + entry -> assertThat(entry).isNotNegative()); } - if (httpAttributes.contains(SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH) - || attrs.get(SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH) != null) { + if (attrs.get(SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH) != null) { assertThat(attrs) .hasEntrySatisfying( SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH, - entry -> assertThat(entry).isInstanceOf(Long.class)); + entry -> assertThat(entry).isNotNegative()); } - if (httpAttributes.contains(SemanticAttributes.HTTP_SERVER_NAME)) { - assertThat(attrs) - .hasEntrySatisfying( - SemanticAttributes.HTTP_SERVER_NAME, - entry -> assertThat(entry).isInstanceOf(String.class)); - } - if (httpAttributes.contains(SemanticAttributes.HTTP_ROUTE)) { - if (expectedRoute != null) { - assertThat(attrs).containsEntry(SemanticAttributes.HTTP_ROUTE, expectedRoute); - } + if (httpAttributes.contains(SemanticAttributes.HTTP_ROUTE) && expectedRoute != null) { + assertThat(attrs).containsEntry(SemanticAttributes.HTTP_ROUTE, expectedRoute); } if (endpoint == CAPTURE_HEADERS) { @@ -627,11 +658,22 @@ protected SpanDataAssert assertServerSpan( assertThat(attrs) .containsEntry("http.response.header.x_test_response", new String[] {"test"}); } + + if (endpoint == CAPTURE_HEADERS_AS_JSON) { + assertThat(attrs).containsKey("http.request.headers"); + assertThat(attrs).containsKey("http.response.headers"); + } + if (endpoint == CAPTURE_PARAMETERS) { assertThat(attrs) .containsEntry( "servlet.request.parameter.test_parameter", new String[] {"test value õäöü"}); } + + if (endpoint == CAPTURE_BODY) { + assertThat(attrs).containsEntry("http.request.body", CAPTURE_BODY.body); + assertThat(attrs).containsEntry("http.response.body", CAPTURE_BODY.body); + } }); return span; 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 acad7093d97f..edee9178a010 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 @@ -5,6 +5,7 @@ package io.opentelemetry.instrumentation.testing.junit.http; +import com.google.errorprone.annotations.CanIgnoreReturnValue; import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; import java.net.URI; @@ -52,113 +53,126 @@ public final class HttpClientTestOptions { boolean testReadTimeout = false; boolean testRemoteConnection = true; boolean testHttps = true; - boolean testCausality = true; - boolean testCausalityWithCallback = true; boolean testCallback = true; boolean testCallbackWithParent = true; + boolean testCallbackWithImplicitParent = false; boolean testErrorWithCallback = true; HttpClientTestOptions() {} + @CanIgnoreReturnValue public HttpClientTestOptions setHttpAttributes( Function>> httpAttributes) { this.httpAttributes = httpAttributes; return this; } + @CanIgnoreReturnValue public HttpClientTestOptions setExpectedClientSpanNameMapper( BiFunction expectedClientSpanNameMapper) { this.expectedClientSpanNameMapper = expectedClientSpanNameMapper; return this; } + @CanIgnoreReturnValue public HttpClientTestOptions setResponseCodeOnRedirectError(int responseCodeOnRedirectError) { this.responseCodeOnRedirectError = responseCodeOnRedirectError; return this; } + @CanIgnoreReturnValue public HttpClientTestOptions setUserAgent(String userAgent) { this.userAgent = userAgent; return this; } + @CanIgnoreReturnValue public HttpClientTestOptions setClientSpanErrorMapper( BiFunction clientSpanErrorMapper) { this.clientSpanErrorMapper = clientSpanErrorMapper; return this; } + @CanIgnoreReturnValue public HttpClientTestOptions setSingleConnectionFactory( BiFunction singleConnectionFactory) { this.singleConnectionFactory = singleConnectionFactory; return this; } + @CanIgnoreReturnValue public HttpClientTestOptions setMaxRedirects(int maxRedirects) { this.maxRedirects = maxRedirects; return this; } + @CanIgnoreReturnValue public HttpClientTestOptions disableTestWithClientParent() { testWithClientParent = false; return this; } + @CanIgnoreReturnValue public HttpClientTestOptions disableTestRedirects() { testRedirects = false; return this; } + @CanIgnoreReturnValue public HttpClientTestOptions disableTestCircularRedirects() { testCircularRedirects = false; return this; } + @CanIgnoreReturnValue public HttpClientTestOptions disableTestReusedRequest() { testReusedRequest = false; return this; } + @CanIgnoreReturnValue public HttpClientTestOptions disableTestConnectionFailure() { testConnectionFailure = false; return this; } + @CanIgnoreReturnValue public HttpClientTestOptions enableTestReadTimeout() { testReadTimeout = true; return this; } + @CanIgnoreReturnValue public HttpClientTestOptions disableTestRemoteConnection() { testRemoteConnection = false; return this; } + @CanIgnoreReturnValue public HttpClientTestOptions disableTestHttps() { testHttps = false; return this; } - public HttpClientTestOptions disableTestCausality() { - testCausality = false; - return this; - } - - public HttpClientTestOptions disableTestCausalityWithCallback() { - testCausalityWithCallback = false; - return this; - } - + @CanIgnoreReturnValue public HttpClientTestOptions disableTestCallback() { testCallback = false; return this; } + @CanIgnoreReturnValue public HttpClientTestOptions disableTestCallbackWithParent() { testCallbackWithParent = false; return this; } + @CanIgnoreReturnValue + public HttpClientTestOptions enableTestCallbackWithImplicitParent() { + testCallbackWithImplicitParent = true; + return this; + } + + @CanIgnoreReturnValue public HttpClientTestOptions disableTestErrorWithCallback() { testErrorWithCallback = false; return this; diff --git a/testing-common/src/main/java/io/opentelemetry/instrumentation/testing/junit/http/HttpServerTestOptions.java b/testing-common/src/main/java/io/opentelemetry/instrumentation/testing/junit/http/HttpServerTestOptions.java index b93e1f4c3dce..70431ed23371 100644 --- a/testing-common/src/main/java/io/opentelemetry/instrumentation/testing/junit/http/HttpServerTestOptions.java +++ b/testing-common/src/main/java/io/opentelemetry/instrumentation/testing/junit/http/HttpServerTestOptions.java @@ -5,6 +5,7 @@ package io.opentelemetry.instrumentation.testing.junit.http; +import com.google.errorprone.annotations.CanIgnoreReturnValue; import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; import java.util.Arrays; @@ -32,7 +33,7 @@ public final class HttpServerTestOptions { BiFunction expectedServerSpanNameMapper = DEFAULT_EXPECTED_SERVER_SPAN_NAME_MAPPER; Function expectedHttpRoute = unused -> null; - Function peerIp = unused -> "127.0.0.1"; + Function sockPeerAddr = unused -> "127.0.0.1"; String contextPath = ""; Throwable expectedException = new Exception(ServerEndpoint.EXCEPTION.getBody()); @@ -49,102 +50,133 @@ public final class HttpServerTestOptions { boolean testNotFound = true; boolean testPathParam = false; boolean testCaptureHttpHeaders = true; + boolean testCaptureHttpHeadersAsJson = false; boolean testCaptureRequestParameters = false; + boolean testCaptureBody = false; HttpServerTestOptions() {} + @CanIgnoreReturnValue public HttpServerTestOptions setHttpAttributes( Function>> httpAttributes) { this.httpAttributes = httpAttributes; return this; } + @CanIgnoreReturnValue public HttpServerTestOptions setExpectedServerSpanNameMapper( BiFunction expectedServerSpanNameMapper) { this.expectedServerSpanNameMapper = expectedServerSpanNameMapper; return this; } + @CanIgnoreReturnValue public HttpServerTestOptions setExpectedHttpRoute( Function expectedHttpRoute) { this.expectedHttpRoute = expectedHttpRoute; return this; } - public HttpServerTestOptions setPeerIp(Function peerIp) { - this.peerIp = peerIp; + @CanIgnoreReturnValue + public HttpServerTestOptions setSockPeerAddr(Function sockPeerAddr) { + this.sockPeerAddr = sockPeerAddr; return this; } + @CanIgnoreReturnValue public HttpServerTestOptions setContextPath(String contextPath) { this.contextPath = contextPath; return this; } + @CanIgnoreReturnValue public HttpServerTestOptions setExpectedException(Throwable expectedException) { this.expectedException = expectedException; return this; } + @CanIgnoreReturnValue public HttpServerTestOptions setHasHandlerSpan(Predicate hasHandlerSpan) { this.hasHandlerSpan = hasHandlerSpan; return this; } + @CanIgnoreReturnValue public HttpServerTestOptions setHasResponseSpan(Predicate hasResponseSpan) { this.hasResponseSpan = hasResponseSpan; return this; } + @CanIgnoreReturnValue public HttpServerTestOptions setHasErrorPageSpans(Predicate hasErrorPageSpans) { this.hasErrorPageSpans = hasErrorPageSpans; return this; } + @CanIgnoreReturnValue public HttpServerTestOptions setHasExceptionOnServerSpan( Predicate hasExceptionOnServerSpan) { this.hasExceptionOnServerSpan = hasExceptionOnServerSpan; return this; } + @CanIgnoreReturnValue public HttpServerTestOptions setTestRedirect(boolean testRedirect) { this.testRedirect = testRedirect; return this; } + @CanIgnoreReturnValue public HttpServerTestOptions setTestError(boolean testError) { this.testError = testError; return this; } + @CanIgnoreReturnValue public HttpServerTestOptions setTestErrorBody(boolean testErrorBody) { this.testErrorBody = testErrorBody; return this; } + @CanIgnoreReturnValue public HttpServerTestOptions setTestException(boolean testException) { this.testException = testException; return this; } + @CanIgnoreReturnValue public HttpServerTestOptions setTestNotFound(boolean testNotFound) { this.testNotFound = testNotFound; return this; } + @CanIgnoreReturnValue public HttpServerTestOptions setTestPathParam(boolean testPathParam) { this.testPathParam = testPathParam; return this; } + @CanIgnoreReturnValue public HttpServerTestOptions setTestCaptureHttpHeaders(boolean testCaptureHttpHeaders) { this.testCaptureHttpHeaders = testCaptureHttpHeaders; return this; } + public HttpServerTestOptions setTestCaptureHttpHeadersAsJson( + boolean testCaptureHttpHeadersAsJson) { + this.testCaptureHttpHeadersAsJson = testCaptureHttpHeadersAsJson; + return this; + } + + @CanIgnoreReturnValue public HttpServerTestOptions setTestCaptureRequestParameters( boolean testCaptureRequestParameters) { this.testCaptureRequestParameters = testCaptureRequestParameters; return this; } + + public HttpServerTestOptions setTestCaptureBody(boolean testCaptureBody) { + this.testCaptureBody = testCaptureBody; + return this; + } } diff --git a/testing-common/src/main/java/io/opentelemetry/instrumentation/testing/junit/http/ServerEndpoint.java b/testing-common/src/main/java/io/opentelemetry/instrumentation/testing/junit/http/ServerEndpoint.java index bccb9b2e323f..f503acf01dc0 100644 --- a/testing-common/src/main/java/io/opentelemetry/instrumentation/testing/junit/http/ServerEndpoint.java +++ b/testing-common/src/main/java/io/opentelemetry/instrumentation/testing/junit/http/ServerEndpoint.java @@ -19,7 +19,9 @@ public enum ServerEndpoint { EXCEPTION("exception", 500, "controller exception"), NOT_FOUND("notFound", 404, "not found"), CAPTURE_HEADERS("captureHeaders", 200, "headers captured"), + CAPTURE_HEADERS_AS_JSON("captureHeadersAsJson", 200, "headers json captured"), CAPTURE_PARAMETERS("captureParameters", 200, "parameters captured"), + CAPTURE_BODY("captureBody", 200, "body captured"), // TODO: add tests for the following cases: QUERY_PARAM("query?some=query", 200, "some=query"), diff --git a/testing-common/src/main/java/io/opentelemetry/instrumentation/testing/util/TelemetryDataUtil.java b/testing-common/src/main/java/io/opentelemetry/instrumentation/testing/util/TelemetryDataUtil.java index 706ab8f9893b..29d7c9a5e284 100644 --- a/testing-common/src/main/java/io/opentelemetry/instrumentation/testing/util/TelemetryDataUtil.java +++ b/testing-common/src/main/java/io/opentelemetry/instrumentation/testing/util/TelemetryDataUtil.java @@ -6,6 +6,7 @@ package io.opentelemetry.instrumentation.testing.util; import static java.util.stream.Collectors.toList; +import static org.assertj.core.api.Assertions.assertThat; import io.opentelemetry.api.trace.SpanId; import io.opentelemetry.api.trace.SpanKind; @@ -73,6 +74,17 @@ public static List> waitForTraces( + " total trace(s): " + allTraces); } + // TODO (trask) is there a better location for this assertion? + for (List trace : completeTraces) { + for (SpanData span : trace) { + if (!span.getInstrumentationScopeInfo().getName().equals("test")) { + assertThat(span.getInstrumentationScopeInfo().getVersion()) + .as( + "Instrumentation version was empty; make sure that the instrumentation name matches the gradle module name") + .isNotNull(); + } + } + } return completeTraces; } diff --git a/testing-common/src/main/java/io/opentelemetry/javaagent/testing/common/AgentTestingExporterAccess.java b/testing-common/src/main/java/io/opentelemetry/javaagent/testing/common/AgentTestingExporterAccess.java index 6b6bf2227cf3..71ead85af910 100644 --- a/testing-common/src/main/java/io/opentelemetry/javaagent/testing/common/AgentTestingExporterAccess.java +++ b/testing-common/src/main/java/io/opentelemetry/javaagent/testing/common/AgentTestingExporterAccess.java @@ -14,6 +14,7 @@ import com.google.protobuf.InvalidProtocolBufferException; import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.common.AttributesBuilder; +import io.opentelemetry.api.logs.Severity; import io.opentelemetry.api.trace.SpanContext; import io.opentelemetry.api.trace.SpanKind; import io.opentelemetry.api.trace.StatusCode; @@ -44,9 +45,7 @@ import io.opentelemetry.proto.trace.v1.Span; import io.opentelemetry.proto.trace.v1.Status; import io.opentelemetry.sdk.common.InstrumentationScopeInfo; -import io.opentelemetry.sdk.logs.data.LogData; -import io.opentelemetry.sdk.logs.data.LogDataBuilder; -import io.opentelemetry.sdk.logs.data.Severity; +import io.opentelemetry.sdk.logs.data.LogRecordData; import io.opentelemetry.sdk.metrics.data.AggregationTemporality; import io.opentelemetry.sdk.metrics.data.DoublePointData; import io.opentelemetry.sdk.metrics.data.HistogramPointData; @@ -64,6 +63,7 @@ import io.opentelemetry.sdk.metrics.internal.data.ImmutableSummaryData; import io.opentelemetry.sdk.metrics.internal.data.ImmutableSummaryPointData; import io.opentelemetry.sdk.metrics.internal.data.ImmutableValueAtQuantile; +import io.opentelemetry.sdk.testing.logs.TestLogRecordData; import io.opentelemetry.sdk.testing.trace.TestSpanData; import io.opentelemetry.sdk.trace.data.EventData; import io.opentelemetry.sdk.trace.data.LinkData; @@ -188,10 +188,9 @@ public static List getExportedSpans() { io.opentelemetry.sdk.resources.Resource.create( fromProto(resource.getAttributesList()))) .setInstrumentationScopeInfo( - InstrumentationScopeInfo.create( - instrumentationScope.getName(), - instrumentationScope.getVersion(), - /* schemaUrl= */ null)) + InstrumentationScopeInfo.builder(instrumentationScope.getName()) + .setVersion(instrumentationScope.getVersion()) + .build()) .setName(span.getName()) .setStartEpochNanos(span.getStartTimeUnixNano()) .setEndEpochNanos(span.getEndTimeUnixNano()) @@ -267,10 +266,9 @@ public static List getExportedMetrics() { metric, io.opentelemetry.sdk.resources.Resource.create( fromProto(resource.getAttributesList())), - InstrumentationScopeInfo.create( - instrumentationScope.getName(), - instrumentationScope.getVersion(), - /* schemaUrl= */ null))); + InstrumentationScopeInfo.builder(instrumentationScope.getName()) + .setVersion(instrumentationScope.getVersion()) + .build())); } } } @@ -278,7 +276,7 @@ public static List getExportedMetrics() { } @SuppressWarnings("unchecked") - public static List getExportedLogs() { + public static List getExportedLogRecords() { List exportRequests; try { exportRequests = (List) getLogExportRequests.invokeExact(); @@ -298,7 +296,7 @@ public static List getExportedLogs() { }) .flatMap(request -> request.getResourceLogsList().stream()) .collect(toList()); - List logs = new ArrayList<>(); + List logs = new ArrayList<>(); for (ResourceLogs resourceLogs : allResourceLogs) { Resource resource = resourceLogs.getResource(); for (ScopeLogs ilLogs : resourceLogs.getScopeLogsList()) { @@ -309,10 +307,9 @@ public static List getExportedLogs() { logRecord, io.opentelemetry.sdk.resources.Resource.create( fromProto(resource.getAttributesList())), - InstrumentationScopeInfo.create( - instrumentationScope.getName(), - instrumentationScope.getVersion(), - /* schemaUrl= */ null))); + InstrumentationScopeInfo.builder(instrumentationScope.getName()) + .setVersion(instrumentationScope.getVersion()) + .build())); } } } @@ -394,11 +391,13 @@ private static MetricData createMetricData( } } - private static LogData createLogData( + private static LogRecordData createLogData( LogRecord logRecord, io.opentelemetry.sdk.resources.Resource resource, InstrumentationScopeInfo instrumentationScopeInfo) { - return LogDataBuilder.create(resource, instrumentationScopeInfo) + return TestLogRecordData.builder() + .setResource(resource) + .setInstrumentationScopeInfo(instrumentationScopeInfo) .setEpoch(logRecord.getTimeUnixNano(), TimeUnit.NANOSECONDS) .setSpanContext( SpanContext.create( diff --git a/testing/agent-exporter/build.gradle.kts b/testing/agent-exporter/build.gradle.kts index f4067f9991d8..063a7d3a01dc 100644 --- a/testing/agent-exporter/build.gradle.kts +++ b/testing/agent-exporter/build.gradle.kts @@ -16,9 +16,6 @@ dependencies { compileOnly(project(":javaagent-bootstrap")) compileOnly(project(":javaagent-tooling")) - compileOnly(project(":instrumentation-appender-api-internal")) - compileOnly(project(":instrumentation-appender-sdk-internal")) - implementation("io.opentelemetry:opentelemetry-exporter-otlp-common") compileOnly("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure") } diff --git a/testing/agent-exporter/src/main/java/io/opentelemetry/javaagent/testing/IgnoredTestTypesConfigurer.java b/testing/agent-exporter/src/main/java/io/opentelemetry/javaagent/testing/IgnoredTestTypesConfigurer.java index a36c983bc3fa..7947ea0112e4 100644 --- a/testing/agent-exporter/src/main/java/io/opentelemetry/javaagent/testing/IgnoredTestTypesConfigurer.java +++ b/testing/agent-exporter/src/main/java/io/opentelemetry/javaagent/testing/IgnoredTestTypesConfigurer.java @@ -14,7 +14,7 @@ public class IgnoredTestTypesConfigurer implements IgnoredTypesConfigurer { @Override - public void configure(ConfigProperties config, IgnoredTypesBuilder builder) { + public void configure(IgnoredTypesBuilder builder, ConfigProperties config) { // we don't want to instrument auto-generated mocks builder .ignoreClass("org.mockito") diff --git a/testing/agent-exporter/src/main/java/io/opentelemetry/javaagent/testing/bytebuddy/TestAgentExtension.java b/testing/agent-exporter/src/main/java/io/opentelemetry/javaagent/testing/bytebuddy/TestAgentExtension.java index f2a7463df333..39955c92d012 100644 --- a/testing/agent-exporter/src/main/java/io/opentelemetry/javaagent/testing/bytebuddy/TestAgentExtension.java +++ b/testing/agent-exporter/src/main/java/io/opentelemetry/javaagent/testing/bytebuddy/TestAgentExtension.java @@ -7,13 +7,14 @@ import com.google.auto.service.AutoService; import io.opentelemetry.javaagent.tooling.AgentExtension; +import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import net.bytebuddy.agent.builder.AgentBuilder; @AutoService(AgentExtension.class) public class TestAgentExtension implements AgentExtension { @Override - public AgentBuilder extend(AgentBuilder agentBuilder) { + public AgentBuilder extend(AgentBuilder agentBuilder, ConfigProperties config) { return agentBuilder.with(TestAgentListener.INSTANCE); } diff --git a/testing/agent-exporter/src/main/java/io/opentelemetry/javaagent/testing/bytebuddy/TestAgentListener.java b/testing/agent-exporter/src/main/java/io/opentelemetry/javaagent/testing/bytebuddy/TestAgentListener.java index 38a2050aa2ba..adedcb511c76 100644 --- a/testing/agent-exporter/src/main/java/io/opentelemetry/javaagent/testing/bytebuddy/TestAgentListener.java +++ b/testing/agent-exporter/src/main/java/io/opentelemetry/javaagent/testing/bytebuddy/TestAgentListener.java @@ -57,7 +57,7 @@ private static Trie buildOtherConfiguredIgnores() { || configurer instanceof GlobalIgnoredTypesConfigurer) { continue; } - configurer.configure(EmptyConfigProperties.INSTANCE, builder); + configurer.configure(builder, EmptyConfigProperties.INSTANCE); } return builder.buildIgnoredTypesTrie(); } diff --git a/testing/agent-exporter/src/main/java/io/opentelemetry/javaagent/testing/exporter/AgentTestingCustomizer.java b/testing/agent-exporter/src/main/java/io/opentelemetry/javaagent/testing/exporter/AgentTestingCustomizer.java index 357803f63d58..3c4d7aef6923 100644 --- a/testing/agent-exporter/src/main/java/io/opentelemetry/javaagent/testing/exporter/AgentTestingCustomizer.java +++ b/testing/agent-exporter/src/main/java/io/opentelemetry/javaagent/testing/exporter/AgentTestingCustomizer.java @@ -8,7 +8,7 @@ import com.google.auto.service.AutoService; import io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizer; import io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizerProvider; -import io.opentelemetry.sdk.logs.export.SimpleLogProcessor; +import io.opentelemetry.sdk.logs.export.SimpleLogRecordProcessor; import io.opentelemetry.sdk.metrics.export.MetricReader; import io.opentelemetry.sdk.metrics.export.PeriodicMetricReader; import io.opentelemetry.sdk.trace.export.SimpleSpanProcessor; @@ -40,9 +40,9 @@ public void customize(AutoConfigurationCustomizer autoConfigurationCustomizer) { autoConfigurationCustomizer.addMeterProviderCustomizer( (meterProvider, config) -> meterProvider.registerMetricReader(metricReader)); - autoConfigurationCustomizer.addLogEmitterProviderCustomizer( + autoConfigurationCustomizer.addLoggerProviderCustomizer( (logProvider, config) -> - logProvider.addLogProcessor( - SimpleLogProcessor.create(AgentTestingExporterFactory.logExporter))); + logProvider.addLogRecordProcessor( + SimpleLogRecordProcessor.create(AgentTestingExporterFactory.logExporter))); } } diff --git a/testing/agent-exporter/src/main/java/io/opentelemetry/javaagent/testing/exporter/AgentTestingExporterConfigSupplier.java b/testing/agent-exporter/src/main/java/io/opentelemetry/javaagent/testing/exporter/AgentTestingExporterConfigSupplier.java new file mode 100644 index 000000000000..ee40fe02a701 --- /dev/null +++ b/testing/agent-exporter/src/main/java/io/opentelemetry/javaagent/testing/exporter/AgentTestingExporterConfigSupplier.java @@ -0,0 +1,29 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.testing.exporter; + +import com.google.auto.service.AutoService; +import io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizer; +import io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizerProvider; +import java.util.HashMap; +import java.util.Map; + +@AutoService(AutoConfigurationCustomizerProvider.class) +public class AgentTestingExporterConfigSupplier implements AutoConfigurationCustomizerProvider { + + @Override + public void customize(AutoConfigurationCustomizer autoConfiguration) { + autoConfiguration.addPropertiesSupplier(AgentTestingExporterConfigSupplier::getTestProperties); + } + + private static Map getTestProperties() { + Map properties = new HashMap<>(); + properties.put("otel.logs.exporter", "none"); + properties.put("otel.metrics.exporter", "none"); + properties.put("otel.traces.exporter", "none"); + return properties; + } +} diff --git a/testing/agent-exporter/src/main/java/io/opentelemetry/javaagent/testing/exporter/AgentTestingExporterFactory.java b/testing/agent-exporter/src/main/java/io/opentelemetry/javaagent/testing/exporter/AgentTestingExporterFactory.java index b22cc695eddd..e9d88c3faf6a 100644 --- a/testing/agent-exporter/src/main/java/io/opentelemetry/javaagent/testing/exporter/AgentTestingExporterFactory.java +++ b/testing/agent-exporter/src/main/java/io/opentelemetry/javaagent/testing/exporter/AgentTestingExporterFactory.java @@ -8,11 +8,11 @@ import java.util.List; import java.util.concurrent.TimeUnit; -public class AgentTestingExporterFactory { +public final class AgentTestingExporterFactory { static final OtlpInMemorySpanExporter spanExporter = new OtlpInMemorySpanExporter(); static final OtlpInMemoryMetricExporter metricExporter = new OtlpInMemoryMetricExporter(); - static final OtlpInMemoryLogExporter logExporter = new OtlpInMemoryLogExporter(); + static final OtlpInMemoryLogRecordExporter logExporter = new OtlpInMemoryLogRecordExporter(); public static List getSpanExportRequests() { return spanExporter.getCollectedExportRequests(); @@ -38,4 +38,6 @@ public static void reset() { public static boolean forceFlushCalled() { return AgentTestingCustomizer.spanProcessor.forceFlushCalled; } + + private AgentTestingExporterFactory() {} } diff --git a/testing/agent-exporter/src/main/java/io/opentelemetry/javaagent/testing/exporter/AgentTestingExporterPropertySource.java b/testing/agent-exporter/src/main/java/io/opentelemetry/javaagent/testing/exporter/AgentTestingExporterPropertySource.java deleted file mode 100644 index 893bc90aff10..000000000000 --- a/testing/agent-exporter/src/main/java/io/opentelemetry/javaagent/testing/exporter/AgentTestingExporterPropertySource.java +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.javaagent.testing.exporter; - -import com.google.auto.service.AutoService; -import io.opentelemetry.javaagent.extension.config.ConfigPropertySource; -import java.util.HashMap; -import java.util.Map; - -@AutoService(ConfigPropertySource.class) -public class AgentTestingExporterPropertySource implements ConfigPropertySource { - - @Override - public Map getProperties() { - Map properties = new HashMap<>(); - properties.put("otel.logs.exporter", "none"); - properties.put("otel.metrics.exporter", "none"); - properties.put("otel.traces.exporter", "none"); - return properties; - } -} diff --git a/testing/agent-exporter/src/main/java/io/opentelemetry/javaagent/testing/exporter/OtlpInMemoryLogExporter.java b/testing/agent-exporter/src/main/java/io/opentelemetry/javaagent/testing/exporter/OtlpInMemoryLogRecordExporter.java similarity index 71% rename from testing/agent-exporter/src/main/java/io/opentelemetry/javaagent/testing/exporter/OtlpInMemoryLogExporter.java rename to testing/agent-exporter/src/main/java/io/opentelemetry/javaagent/testing/exporter/OtlpInMemoryLogRecordExporter.java index 29f753df11aa..0875fbfee99e 100644 --- a/testing/agent-exporter/src/main/java/io/opentelemetry/javaagent/testing/exporter/OtlpInMemoryLogExporter.java +++ b/testing/agent-exporter/src/main/java/io/opentelemetry/javaagent/testing/exporter/OtlpInMemoryLogRecordExporter.java @@ -9,8 +9,8 @@ import io.opentelemetry.exporter.internal.otlp.logs.LogsRequestMarshaler; import io.opentelemetry.sdk.common.CompletableResultCode; -import io.opentelemetry.sdk.logs.data.LogData; -import io.opentelemetry.sdk.logs.export.LogExporter; +import io.opentelemetry.sdk.logs.data.LogRecordData; +import io.opentelemetry.sdk.logs.export.LogRecordExporter; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.UncheckedIOException; @@ -21,9 +21,10 @@ import java.util.concurrent.ConcurrentLinkedQueue; import java.util.logging.Logger; -class OtlpInMemoryLogExporter implements LogExporter { +class OtlpInMemoryLogRecordExporter implements LogRecordExporter { - private static final Logger logger = Logger.getLogger(OtlpInMemoryLogExporter.class.getName()); + private static final Logger logger = + Logger.getLogger(OtlpInMemoryLogRecordExporter.class.getName()); private final Queue collectedRequests = new ConcurrentLinkedQueue<>(); @@ -36,13 +37,13 @@ void reset() { } @Override - public CompletableResultCode export(Collection logs) { - for (LogData log : logs) { - logger.log(INFO, "Exporting log {0}", log); + public CompletableResultCode export(Collection logRecords) { + for (LogRecordData logRecord : logRecords) { + logger.log(INFO, "Exporting log {0}", logRecord); } ByteArrayOutputStream bos = new ByteArrayOutputStream(); try { - LogsRequestMarshaler.create(logs).writeBinaryTo(bos); + LogsRequestMarshaler.create(logRecords).writeBinaryTo(bos); } catch (IOException e) { throw new UncheckedIOException(e); } diff --git a/testing/agent-exporter/src/main/java/io/opentelemetry/javaagent/testing/http/CapturedHttpHeadersTestConfigSource.java b/testing/agent-exporter/src/main/java/io/opentelemetry/javaagent/testing/http/CapturedHttpHeadersTestConfigSupplier.java similarity index 56% rename from testing/agent-exporter/src/main/java/io/opentelemetry/javaagent/testing/http/CapturedHttpHeadersTestConfigSource.java rename to testing/agent-exporter/src/main/java/io/opentelemetry/javaagent/testing/http/CapturedHttpHeadersTestConfigSupplier.java index eec0215d5404..2485e32f9506 100644 --- a/testing/agent-exporter/src/main/java/io/opentelemetry/javaagent/testing/http/CapturedHttpHeadersTestConfigSource.java +++ b/testing/agent-exporter/src/main/java/io/opentelemetry/javaagent/testing/http/CapturedHttpHeadersTestConfigSupplier.java @@ -6,15 +6,21 @@ package io.opentelemetry.javaagent.testing.http; import com.google.auto.service.AutoService; -import io.opentelemetry.javaagent.extension.config.ConfigPropertySource; +import io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizer; +import io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizerProvider; import java.util.HashMap; import java.util.Map; -@AutoService(ConfigPropertySource.class) -public class CapturedHttpHeadersTestConfigSource implements ConfigPropertySource { +@AutoService(AutoConfigurationCustomizerProvider.class) +public class CapturedHttpHeadersTestConfigSupplier implements AutoConfigurationCustomizerProvider { @Override - public Map getProperties() { + public void customize(AutoConfigurationCustomizer autoConfiguration) { + autoConfiguration.addPropertiesSupplier( + CapturedHttpHeadersTestConfigSupplier::getTestProperties); + } + + private static Map getTestProperties() { Map testConfig = new HashMap<>(); testConfig.put("otel.instrumentation.http.capture-headers.client.request", "X-Test-Request"); testConfig.put("otel.instrumentation.http.capture-headers.client.response", "X-Test-Response"); 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 new file mode 100644 index 000000000000..7c0865aee601 --- /dev/null +++ b/testing/agent-exporter/src/main/java/io/opentelemetry/javaagent/testing/messaging/CapturedMessagingHeadersTestConfigSupplier.java @@ -0,0 +1,34 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.testing.messaging; + +import com.google.auto.service.AutoService; +import io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizer; +import io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizerProvider; +import java.util.HashMap; +import java.util.Map; + +@AutoService(AutoConfigurationCustomizerProvider.class) +public class CapturedMessagingHeadersTestConfigSupplier + implements AutoConfigurationCustomizerProvider { + + @Override + public void customize(AutoConfigurationCustomizer autoConfiguration) { + autoConfiguration.addPropertiesSupplier( + CapturedMessagingHeadersTestConfigSupplier::getTestProperties); + } + + 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 + // '-' 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"); + return testConfig; + } +} diff --git a/testing/armeria-shaded-for-testing/build.gradle.kts b/testing/armeria-shaded-for-testing/build.gradle.kts index 49e06f260124..b053b9642801 100644 --- a/testing/armeria-shaded-for-testing/build.gradle.kts +++ b/testing/armeria-shaded-for-testing/build.gradle.kts @@ -5,11 +5,17 @@ plugins { } dependencies { - implementation("com.linecorp.armeria:armeria-junit5:1.13.3") + implementation("com.linecorp.armeria:armeria-junit5:1.18.0") } tasks { shadowJar { + dependencies { + exclude(dependency("org.slf4j:slf4j-api")) + exclude(dependency("org.junit.jupiter:junit-jupiter-api")) + exclude(dependency("org.junit.platform:junit-platform-commons")) + } + // Ensures tests are not affected by Armeria instrumentation relocate("com.linecorp.armeria", "io.opentelemetry.testing.internal.armeria") relocate("com.fasterxml.jackson", "io.opentelemetry.testing.internal.jackson") diff --git a/version.gradle.kts b/version.gradle.kts index c39500cf3b98..7cf5f9d049e6 100644 --- a/version.gradle.kts +++ b/version.gradle.kts @@ -1,5 +1,5 @@ -val stableVersion = "1.16.0-SNAPSHOT" -val alphaVersion = "1.16.0-alpha-SNAPSHOT" +val stableVersion = "1.0.4" +val alphaVersion = "1.0.4-alpha-SNAPSHOT" allprojects { if (findProperty("otel.stable") != "true") {