Compare commits
57 Commits
Author | SHA1 | Date |
---|---|---|
|
9c2bea5834 | |
|
5f062cb981 | |
|
aee5c486d6 | |
|
89e755feef | |
|
492a9acd96 | |
|
bf76c1fa1c | |
|
78b7327373 | |
|
7f1ba9b3ca | |
|
12d7fdde52 | |
|
887f9cdcbd | |
|
d9d3bebbb4 | |
|
8bcdefff70 | |
|
58afc90936 | |
|
1b3aa9da1d | |
|
81a464274d | |
|
afb322d77d | |
|
78ba9471d6 | |
|
cb27e562de | |
|
dc17154bf1 | |
|
68a17adfee | |
|
e2db7c1b2d | |
|
4251217436 | |
|
7689228d47 | |
|
236f2fba17 | |
|
f87ae0b728 | |
|
56067c413d | |
|
b1b2d735e8 | |
|
c7529e73de | |
|
7e839e2ad9 | |
|
168ff66d6e | |
|
9863e807b8 | |
|
fdee041ab3 | |
|
9410d8c2c9 | |
|
b7fe8070a2 | |
|
75898bf9ab | |
|
86f94fcbe6 | |
|
d8723a4d32 | |
|
c6b60c7d48 | |
|
91b4c429c0 | |
|
02235d5294 | |
|
6bb85bc743 | |
|
a4fea2ffdd | |
|
f26e4016dd | |
|
4ea8e9d889 | |
|
34579d59c9 | |
|
febe5c2bed | |
|
7b5f56ee0a | |
|
265449026a | |
|
3d8ed9f66e | |
|
a939b59cfc | |
|
cc28ab82ee | |
|
fa175d2b7f | |
|
fb91b7734c | |
|
c904a8f70e | |
|
a12e3817b1 | |
|
c3abfa15df | |
|
f46e4f14dd |
|
@ -0,0 +1,3 @@
|
|||
# this file exists so that Renovate can auto-update docker image versions that are then used elsewhere
|
||||
|
||||
FROM lycheeverse/lychee:sha-2aa22f8@sha256:2e3786630482c41f9f2dd081e06d7da1c36d66996e8cf6573409b8bc418d48c4 AS lychee
|
|
@ -0,0 +1,35 @@
|
|||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
export MSYS_NO_PATHCONV=1 # for Git Bash on Windows
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
LYCHEE_CONFIG="$SCRIPT_DIR/../../.lychee.toml"
|
||||
DEPENDENCIES_DOCKERFILE="$SCRIPT_DIR/dependencies.dockerfile"
|
||||
|
||||
# Extract lychee version from dependencies.dockerfile
|
||||
LYCHEE_VERSION=$(grep "FROM lycheeverse/lychee:" "$DEPENDENCIES_DOCKERFILE" | sed 's/.*FROM lycheeverse\/lychee:\([^ ]*\).*/\1/')
|
||||
|
||||
# Build the lychee command with optional GitHub token
|
||||
CMD="lycheeverse/lychee:$LYCHEE_VERSION --verbose --config $(basename "$LYCHEE_CONFIG")"
|
||||
|
||||
# Add GitHub token if available
|
||||
if [[ -n "$GITHUB_TOKEN" ]]; then
|
||||
CMD="$CMD --github-token $GITHUB_TOKEN"
|
||||
fi
|
||||
|
||||
# Add the target directory
|
||||
CMD="$CMD ."
|
||||
|
||||
# Determine if we should allocate a TTY
|
||||
DOCKER_FLAGS="--rm --init"
|
||||
if [[ -t 0 ]]; then
|
||||
DOCKER_FLAGS="$DOCKER_FLAGS -it"
|
||||
else
|
||||
DOCKER_FLAGS="$DOCKER_FLAGS -i"
|
||||
fi
|
||||
|
||||
# Run lychee with proper signal handling
|
||||
# shellcheck disable=SC2086
|
||||
exec docker run $DOCKER_FLAGS -v "$(dirname "$LYCHEE_CONFIG")":/data -w /data $CMD
|
|
@ -27,7 +27,7 @@ jobs:
|
|||
# muzzle is not included here because it doesn't use gradle cache anyway and so is already covered
|
||||
# by the normal daily build
|
||||
|
||||
# markdown-link-check and misspell-check are not included here because they don't use gradle cache
|
||||
# link-check and misspell-check are not included here because they don't use gradle cache
|
||||
# anyway and so are already covered by the normal daily build
|
||||
|
||||
workflow-notification:
|
||||
|
|
|
@ -26,8 +26,8 @@ jobs:
|
|||
shell-script-check:
|
||||
uses: ./.github/workflows/reusable-shell-script-check.yml
|
||||
|
||||
markdown-link-check:
|
||||
uses: ./.github/workflows/reusable-markdown-link-check.yml
|
||||
link-check:
|
||||
uses: ./.github/workflows/reusable-link-check.yml
|
||||
|
||||
markdown-lint-check:
|
||||
uses: ./.github/workflows/reusable-markdown-lint-check.yml
|
||||
|
@ -43,7 +43,7 @@ jobs:
|
|||
- common
|
||||
- test-latest-deps
|
||||
- muzzle
|
||||
- markdown-link-check
|
||||
- link-check
|
||||
- misspell-check
|
||||
if: always()
|
||||
uses: ./.github/workflows/reusable-workflow-notification.yml
|
||||
|
@ -53,6 +53,6 @@ jobs:
|
|||
needs.common.result == 'success' &&
|
||||
needs.test-latest-deps.result == 'success' &&
|
||||
needs.muzzle.result == 'success' &&
|
||||
needs.markdown-link-check.result == 'success' &&
|
||||
needs.link-check.result == 'success' &&
|
||||
needs.misspell-check.result == 'success'
|
||||
}}
|
||||
|
|
|
@ -43,11 +43,11 @@ jobs:
|
|||
uses: ./.github/workflows/reusable-shell-script-check.yml
|
||||
|
||||
# this is not a required check to avoid blocking pull requests if external links break
|
||||
markdown-link-check:
|
||||
markdown-check:
|
||||
# release branches are excluded because the README.md javaagent download link has to be updated
|
||||
# on release branches before the release download has been published
|
||||
if: "!startsWith(github.ref_name, 'release/') && !startsWith(github.base_ref, 'release/')"
|
||||
uses: ./.github/workflows/reusable-markdown-link-check.yml
|
||||
uses: ./.github/workflows/reusable-link-check.yml
|
||||
|
||||
markdown-lint-check:
|
||||
uses: ./.github/workflows/reusable-markdown-lint-check.yml
|
||||
|
|
|
@ -37,12 +37,12 @@ jobs:
|
|||
if: "!startsWith(github.ref_name, 'release/')"
|
||||
uses: ./.github/workflows/reusable-shell-script-check.yml
|
||||
|
||||
markdown-link-check:
|
||||
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
|
||||
uses: ./.github/workflows/reusable-link-check.yml
|
||||
|
||||
markdown-lint-check:
|
||||
# release branches are excluded
|
||||
|
@ -58,7 +58,7 @@ jobs:
|
|||
publish-snapshots:
|
||||
needs:
|
||||
# intentionally not blocking snapshot publishing on test-latest-deps, muzzle,
|
||||
# markdown-link-check, or misspell-check
|
||||
# link-check, or misspell-check
|
||||
- common
|
||||
runs-on: ubuntu-latest
|
||||
# skipping release branches because the versions in those branches are not snapshots
|
||||
|
|
|
@ -57,7 +57,7 @@ jobs:
|
|||
cache-read-only: ${{ github.event_name == 'pull_request' }}
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@ce28f5bb42b7a9f2c824e633a3f6ee835bab6858 # v3.29.0
|
||||
uses: github/codeql-action/init@181d5eefc20863364f96762470ba6f862bdef56b # v3.29.2
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
# using "latest" helps to keep up with the latest Kotlin support
|
||||
|
@ -73,6 +73,6 @@ jobs:
|
|||
run: ./gradlew assemble -x javadoc -x :instrumentation:quarkus-resteasy-reactive:quarkus3-testing:quarkusGenerateCodeDev -x :instrumentation:quarkus-resteasy-reactive:quarkus2-testing:quarkusGenerateCodeDev --no-build-cache --no-daemon
|
||||
|
||||
- name: Perform CodeQL analysis
|
||||
uses: github/codeql-action/analyze@ce28f5bb42b7a9f2c824e633a3f6ee835bab6858 # v3.29.0
|
||||
uses: github/codeql-action/analyze@181d5eefc20863364f96762470ba6f862bdef56b # v3.29.2
|
||||
with:
|
||||
category: "/language:${{matrix.language}}"
|
||||
|
|
|
@ -42,6 +42,6 @@ jobs:
|
|||
# Upload the results to GitHub's code scanning dashboard (optional).
|
||||
# Commenting out will disable upload of results to your repo's Code Scanning dashboard
|
||||
- name: "Upload to code-scanning"
|
||||
uses: github/codeql-action/upload-sarif@ce28f5bb42b7a9f2c824e633a3f6ee835bab6858 # v3.29.0
|
||||
uses: github/codeql-action/upload-sarif@181d5eefc20863364f96762470ba6f862bdef56b # v3.29.2
|
||||
with:
|
||||
sarif_file: results.sarif
|
||||
|
|
|
@ -19,7 +19,7 @@ jobs:
|
|||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
|
||||
- uses: docker/setup-buildx-action@18ce135bb5112fa8ce4ed6c17ab05699d7f3a5e0 # v3.11.0
|
||||
- uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1
|
||||
|
||||
- name: Login to GitHub container registry
|
||||
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
name: Reusable - Link check
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
link-check:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
|
||||
- name: Link check
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
run: ./.github/scripts/link-check.sh
|
|
@ -1,27 +0,0 @@
|
|||
name: Reusable - Markdown link check
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
markdown-link-check:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
|
||||
- uses: lycheeverse/lychee-action@82202e5e9c2f4ef1a55a3d02563e1cb6041e5332 # v2.4.1
|
||||
with:
|
||||
# excluding links to pull requests and issues is done for performance
|
||||
# stackexchange link fails with 403 when accessed by lychee
|
||||
args: >
|
||||
--include-fragments
|
||||
--exclude "^https://github.com/open-telemetry/opentelemetry-java-instrumentation/(issues|pull)/\\d+$"
|
||||
--exclude "^http://code.google.com/p/concurrentlinkedhashmap$"
|
||||
--exclude "^https://softwareengineering.stackexchange.com/questions/29727"
|
||||
--max-retries 6
|
||||
--max-concurrency 4
|
||||
--github-token ${{ github.token }}
|
||||
.
|
|
@ -14,9 +14,7 @@ jobs:
|
|||
|
||||
- name: Install misspell
|
||||
run: |
|
||||
curl -L -o install-misspell.sh \
|
||||
https://raw.githubusercontent.com/client9/misspell/master/install-misspell.sh
|
||||
sh ./install-misspell.sh
|
||||
curl -sfL https://raw.githubusercontent.com/golangci/misspell/master/install-misspell.sh | sh -s -- -b bin
|
||||
|
||||
- name: Run misspell
|
||||
run: |
|
||||
|
|
|
@ -27,7 +27,7 @@ jobs:
|
|||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
- id: read-java
|
||||
run: echo "version=$(cat .java-version)" >> "$GITHUB_OUTPUT"
|
||||
- uses: graalvm/setup-graalvm@01ed653ac833fe80569f1ef9f25585ba2811baab # v1.3.3.1
|
||||
- uses: graalvm/setup-graalvm@e1df20a713a4cc6ab5b0eb03f0e0dcdc0199b805 # v1.3.4.1
|
||||
with:
|
||||
version: "latest"
|
||||
java-version: ${{ matrix.test-java-version }}
|
||||
|
|
|
@ -58,6 +58,7 @@ hs_err_pid*
|
|||
replay_pid*
|
||||
.attach_pid*
|
||||
.telemetry*
|
||||
.lycheecache
|
||||
|
||||
!java-agent/benchmark/releases/*.jar
|
||||
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
timeout = 30
|
||||
retry_wait_time = 5
|
||||
max_retries = 6
|
||||
max_concurrency = 4
|
||||
|
||||
# Check link anchors
|
||||
include_fragments = true
|
||||
|
||||
# excluding links to pull requests and issues is done for performance
|
||||
# stackexchange link fails with 403 when accessed by lychee
|
||||
exclude = [
|
||||
'^https://github.com/open-telemetry/opentelemetry-java-instrumentation/(issues|pull)/\d+$',
|
||||
'^http://code.google.com/p/concurrentlinkedhashmap$',
|
||||
'^https://softwareengineering.stackexchange.com/questions/29727.*',
|
||||
'^https://central.sonatype.com/service/rest/repository/browse/maven-snapshots/io/opentelemetry/$',
|
||||
]
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
## Unreleased
|
||||
|
||||
## Version 2.17.0 (2025-06-20)
|
||||
|
||||
### Migration notes
|
||||
|
||||
- Changes have been made to Tomcat metric definitions provided by JMX Metric Insight component
|
||||
|
|
25
README.md
25
README.md
|
@ -129,36 +129,39 @@ Debug logging negatively impacts the performance of your application.
|
|||
|
||||
See [CONTRIBUTING.md](CONTRIBUTING.md).
|
||||
|
||||
Approvers ([@open-telemetry/java-instrumentation-approvers](https://github.com/orgs/open-telemetry/teams/java-instrumentation-approvers)):
|
||||
### Maintainers
|
||||
|
||||
- [Lauri Tulmin](https://github.com/laurit), Splunk
|
||||
- [Trask Stalnaker](https://github.com/trask), Microsoft
|
||||
|
||||
For more information about the maintainer role, see the [community repository](https://github.com/open-telemetry/community/blob/main/guides/contributor/membership.md#maintainer).
|
||||
|
||||
### Approvers
|
||||
|
||||
- [Gregor Zeitlinger](https://github.com/zeitlinger), Grafana
|
||||
- [Jack Berg](https://github.com/jack-berg), New Relic
|
||||
- [Jason Plumb](https://github.com/breedx-splk), Splunk
|
||||
- [Jay DeLuca](https://github.com/jaydeluca)
|
||||
- [Jay DeLuca](https://github.com/jaydeluca), Grafana
|
||||
- [Jean Bisutti](https://github.com/jeanbisutti), Microsoft
|
||||
- [John Watson](https://github.com/jkwatson), Cloudera
|
||||
- [Jonas Kunz](https://github.com/JonasKunz), Elastic
|
||||
- [Steve Rao](https://github.com/steverao), Alibaba
|
||||
- [Sylvain Juge](https://github.com/SylvainJuge), Elastic
|
||||
|
||||
Maintainers ([@open-telemetry/java-instrumentation-maintainers](https://github.com/orgs/open-telemetry/teams/java-instrumentation-maintainers)):
|
||||
For more information about the approver role, see the [community repository](https://github.com/open-telemetry/community/blob/main/guides/contributor/membership.md#approver).
|
||||
|
||||
- [Lauri Tulmin](https://github.com/laurit), Splunk
|
||||
- [Trask Stalnaker](https://github.com/trask), Microsoft
|
||||
|
||||
Emeritus maintainers:
|
||||
### Emeritus maintainers
|
||||
|
||||
- [Mateusz Rzeszutek](https://github.com/mateuszrzeszutek)
|
||||
- [Nikita Salnikov-Tarnovski](https://github.com/iNikem)
|
||||
- [Tyler Benson](https://github.com/tylerbenson)
|
||||
|
||||
Learn more about roles in
|
||||
the [community repository](https://github.com/open-telemetry/community/blob/main/guides/contributor/membership.md).
|
||||
For more information about the emeritus role, see the [community repository](https://github.com/open-telemetry/community/blob/main/guides/contributor/membership.md#emeritus-maintainerapprovertriager).
|
||||
|
||||
Thanks to all the people who have already contributed!
|
||||
### Thanks to all of our contributors!
|
||||
|
||||
<a href="https://github.com/open-telemetry/opentelemetry-java-instrumentation/graphs/contributors">
|
||||
<img src="https://contributors-img.web.app/image?repo=open-telemetry/opentelemetry-java-instrumentation" />
|
||||
<img alt="Repo contributors" src="https://contrib.rocks/image?repo=open-telemetry/opentelemetry-java-instrumentation" />
|
||||
</a>
|
||||
|
||||
[config-agent]: https://opentelemetry.io/docs/zero-code/java/agent/configuration/
|
||||
|
|
|
@ -13,7 +13,7 @@ otelJava {
|
|||
}
|
||||
|
||||
dependencies {
|
||||
jmhImplementation("org.springframework.boot:spring-boot-starter-web:3.5.0")
|
||||
jmhImplementation("org.springframework.boot:spring-boot-starter-web:3.5.3")
|
||||
}
|
||||
|
||||
tasks {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
FROM eclipse-temurin:11.0.27_6-jdk@sha256:cc77e8b834bfa18f4bf93d1649e27256213490168b35d7c72a2b92545763fab8 as app-build
|
||||
FROM eclipse-temurin:11.0.27_6-jdk@sha256:0296b46d1949f49054497db63a7630d4805ef587c2937eb23a582a2ffdde88da as app-build
|
||||
|
||||
# This is the base image that will contain a built version of the spring-petclinic-rest
|
||||
# application. Installing the dependencies and maven compiling the application is time
|
||||
|
|
|
@ -16,10 +16,10 @@ repositories {
|
|||
}
|
||||
|
||||
dependencies {
|
||||
implementation(enforcedPlatform("org.junit:junit-bom:5.13.1"))
|
||||
implementation(enforcedPlatform("org.junit:junit-bom:5.13.3"))
|
||||
|
||||
testImplementation("org.testcontainers:testcontainers:1.21.1")
|
||||
testImplementation("org.testcontainers:postgresql:1.21.1")
|
||||
testImplementation("org.testcontainers:testcontainers:1.21.3")
|
||||
testImplementation("org.testcontainers:postgresql:1.21.3")
|
||||
testImplementation("org.junit.jupiter:junit-jupiter-api")
|
||||
testImplementation("org.junit.jupiter:junit-jupiter-params")
|
||||
testImplementation("com.squareup.okhttp3:okhttp:4.12.0")
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionSha256Sum=7197a12f450794931532469d4ff21a59ea2c1cd59a3ec3f89c035c3c420a6999
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.2-bin.zip
|
||||
distributionSha256Sum=bd71102213493060956ec229d946beee57158dbd89d0e62b91bca0fa2c5f3531
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
|
|
@ -32,7 +32,7 @@ nexusPublishing {
|
|||
}
|
||||
|
||||
connectTimeout.set(Duration.ofMinutes(5))
|
||||
clientTimeout.set(Duration.ofMinutes(5))
|
||||
clientTimeout.set(Duration.ofMinutes(30))
|
||||
|
||||
transitionCheckOptions {
|
||||
// We have many artifacts so Maven Central takes a long time on its compliance checks. This sets
|
||||
|
|
|
@ -57,7 +57,7 @@ dependencies {
|
|||
implementation("com.diffplug.spotless:spotless-plugin-gradle:7.0.4")
|
||||
implementation("com.google.guava:guava:33.4.8-jre")
|
||||
implementation("gradle.plugin.com.google.protobuf:protobuf-gradle-plugin:0.8.18")
|
||||
implementation("com.gradleup.shadow:shadow-gradle-plugin:8.3.6")
|
||||
implementation("com.gradleup.shadow:shadow-gradle-plugin:8.3.8")
|
||||
implementation("org.apache.httpcomponents:httpclient:4.5.14")
|
||||
implementation("com.gradle.develocity:com.gradle.develocity.gradle.plugin:4.0.2")
|
||||
implementation("org.owasp:dependency-check-gradle:12.1.3")
|
||||
|
@ -67,11 +67,11 @@ dependencies {
|
|||
implementation("net.bytebuddy:byte-buddy-gradle-plugin:1.17.6")
|
||||
implementation("gradle.plugin.io.morethan.jmhreport:gradle-jmh-report:0.9.6")
|
||||
implementation("me.champeau.jmh:jmh-gradle-plugin:0.7.3")
|
||||
implementation("net.ltgt.gradle:gradle-errorprone-plugin:4.2.0")
|
||||
implementation("net.ltgt.gradle:gradle-errorprone-plugin:4.3.0")
|
||||
implementation("net.ltgt.gradle:gradle-nullaway-plugin:2.2.0")
|
||||
implementation("me.champeau.gradle:japicmp-gradle-plugin:0.4.6")
|
||||
|
||||
testImplementation(enforcedPlatform("org.junit:junit-bom:5.13.1"))
|
||||
testImplementation(enforcedPlatform("org.junit:junit-bom:5.13.3"))
|
||||
testImplementation("org.junit.jupiter:junit-jupiter-api")
|
||||
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine")
|
||||
testImplementation("org.assertj:assertj-core:3.27.3")
|
||||
|
|
|
@ -431,7 +431,7 @@ codenarc {
|
|||
checkstyle {
|
||||
configFile = rootProject.file("buildscripts/checkstyle.xml")
|
||||
// this version should match the version of google_checks.xml used as basis for above configuration
|
||||
toolVersion = "10.25.0"
|
||||
toolVersion = "10.26.1"
|
||||
maxWarnings = 0
|
||||
}
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ data class DependencySet(val group: String, val version: String, val modules: Li
|
|||
|
||||
// this line is managed by .github/scripts/update-sdk-version.sh
|
||||
val otelSdkVersion = "1.51.0"
|
||||
val otelContribVersion = "1.46.0-alpha"
|
||||
val otelContribVersion = "1.47.0-alpha"
|
||||
val otelSdkAlphaVersion = otelSdkVersion.replaceFirst("(-SNAPSHOT)?$".toRegex(), "-alpha$1")
|
||||
|
||||
// Need both BOM and groovy jars
|
||||
|
@ -28,17 +28,17 @@ val DEPENDENCY_BOMS = listOf(
|
|||
// even if they are only used by test dependencies, so not using junit bom since it is LGPL
|
||||
|
||||
"com.fasterxml.jackson:jackson-bom:2.19.1",
|
||||
"com.squareup.okio:okio-bom:3.13.0", // see https://github.com/open-telemetry/opentelemetry-java/issues/5637
|
||||
"com.squareup.okio:okio-bom:3.15.0", // see https://github.com/open-telemetry/opentelemetry-java/issues/5637
|
||||
"com.google.guava:guava-bom:33.4.8-jre",
|
||||
"org.apache.groovy:groovy-bom:${groovyVersion}",
|
||||
"io.opentelemetry:opentelemetry-bom:${otelSdkVersion}",
|
||||
"io.opentelemetry:opentelemetry-bom-alpha:${otelSdkAlphaVersion}",
|
||||
"org.testcontainers:testcontainers-bom:1.21.1"
|
||||
"org.testcontainers:testcontainers-bom:1.21.3"
|
||||
)
|
||||
|
||||
val autoServiceVersion = "1.1.1"
|
||||
val autoValueVersion = "1.11.0"
|
||||
val errorProneVersion = "2.38.0"
|
||||
val errorProneVersion = "2.39.0"
|
||||
val byteBuddyVersion = "1.17.6"
|
||||
val asmVersion = "9.8"
|
||||
val jmhVersion = "1.37"
|
||||
|
@ -81,7 +81,7 @@ val CORE_DEPENDENCIES = listOf(
|
|||
// There are dependencies included here that appear to have no usages, but are maintained at
|
||||
// this top level to help consistently satisfy large numbers of transitive dependencies.
|
||||
val DEPENDENCIES = listOf(
|
||||
"org.junit.jupiter:junit-jupiter-api:5.13.1",
|
||||
"org.junit.jupiter:junit-jupiter-api:5.13.3",
|
||||
"org.spockframework:spock-core:2.4-M6-groovy-4.0",
|
||||
"org.spockframework:spock-junit4:2.4-M6-groovy-4.0",
|
||||
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
Comparing source compatibility of opentelemetry-instrumentation-annotations-2.17.0.jar against opentelemetry-instrumentation-annotations-2.16.0.jar
|
||||
No changes.
|
|
@ -0,0 +1,2 @@
|
|||
Comparing source compatibility of opentelemetry-instrumentation-api-2.17.0.jar against opentelemetry-instrumentation-api-2.16.0.jar
|
||||
No changes.
|
|
@ -0,0 +1,2 @@
|
|||
Comparing source compatibility of opentelemetry-spring-boot-autoconfigure-2.17.0.jar against opentelemetry-spring-boot-autoconfigure-2.16.0.jar
|
||||
No changes.
|
|
@ -0,0 +1,2 @@
|
|||
Comparing source compatibility of opentelemetry-spring-boot-starter-2.17.0.jar against opentelemetry-spring-boot-starter-2.16.0.jar
|
||||
No changes.
|
|
@ -1,2 +1,2 @@
|
|||
Comparing source compatibility of opentelemetry-instrumentation-annotations-2.17.0-SNAPSHOT.jar against opentelemetry-instrumentation-annotations-2.16.0.jar
|
||||
Comparing source compatibility of opentelemetry-instrumentation-annotations-2.18.0-SNAPSHOT.jar against opentelemetry-instrumentation-annotations-2.17.0.jar
|
||||
No changes.
|
|
@ -1,2 +1,2 @@
|
|||
Comparing source compatibility of opentelemetry-instrumentation-api-2.17.0-SNAPSHOT.jar against opentelemetry-instrumentation-api-2.16.0.jar
|
||||
Comparing source compatibility of opentelemetry-instrumentation-api-2.18.0-SNAPSHOT.jar against opentelemetry-instrumentation-api-2.17.0.jar
|
||||
No changes.
|
|
@ -1,2 +1,2 @@
|
|||
Comparing source compatibility of opentelemetry-spring-boot-autoconfigure-2.17.0-SNAPSHOT.jar against opentelemetry-spring-boot-autoconfigure-2.16.0.jar
|
||||
Comparing source compatibility of opentelemetry-spring-boot-autoconfigure-2.18.0-SNAPSHOT.jar against opentelemetry-spring-boot-autoconfigure-2.17.0.jar
|
||||
No changes.
|
|
@ -1,2 +1,2 @@
|
|||
Comparing source compatibility of opentelemetry-spring-boot-starter-2.17.0-SNAPSHOT.jar against opentelemetry-spring-boot-starter-2.16.0.jar
|
||||
Comparing source compatibility of opentelemetry-spring-boot-starter-2.18.0-SNAPSHOT.jar against opentelemetry-spring-boot-starter-2.17.0.jar
|
||||
No changes.
|
File diff suppressed because it is too large
Load Diff
|
@ -13,8 +13,8 @@ buildscript {
|
|||
}
|
||||
dependencies {
|
||||
classpath "com.diffplug.spotless:spotless-plugin-gradle:7.0.4"
|
||||
classpath "com.gradleup.shadow:shadow-gradle-plugin:8.3.6"
|
||||
classpath "io.opentelemetry.instrumentation:gradle-plugins:2.17.0-alpha-SNAPSHOT"
|
||||
classpath "com.gradleup.shadow:shadow-gradle-plugin:8.3.8"
|
||||
classpath "io.opentelemetry.instrumentation:gradle-plugins:2.18.0-alpha-SNAPSHOT"
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -30,8 +30,8 @@ subprojects {
|
|||
opentelemetrySdk : "1.51.0",
|
||||
|
||||
// these lines are managed by .github/scripts/update-version.sh
|
||||
opentelemetryJavaagent : "2.17.0-SNAPSHOT",
|
||||
opentelemetryJavaagentAlpha: "2.17.0-alpha-SNAPSHOT",
|
||||
opentelemetryJavaagent : "2.18.0-SNAPSHOT",
|
||||
opentelemetryJavaagentAlpha: "2.18.0-alpha-SNAPSHOT",
|
||||
|
||||
autoservice : "1.1.1"
|
||||
]
|
||||
|
@ -69,7 +69,7 @@ subprojects {
|
|||
|
||||
testImplementation("org.mockito:mockito-core:5.18.0")
|
||||
|
||||
testImplementation(enforcedPlatform("org.junit:junit-bom:5.13.1"))
|
||||
testImplementation(enforcedPlatform("org.junit:junit-bom:5.13.3"))
|
||||
testImplementation("org.junit.jupiter:junit-jupiter-api")
|
||||
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine")
|
||||
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionSha256Sum=7197a12f450794931532469d4ff21a59ea2c1cd59a3ec3f89c035c3c420a6999
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.2-bin.zip
|
||||
distributionSha256Sum=bd71102213493060956ec229d946beee57158dbd89d0e62b91bca0fa2c5f3531
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
|
|
@ -3,7 +3,7 @@ plugins {
|
|||
}
|
||||
|
||||
dependencies {
|
||||
testImplementation("org.testcontainers:testcontainers:1.21.1")
|
||||
testImplementation("org.testcontainers:testcontainers:1.21.3")
|
||||
testImplementation("com.fasterxml.jackson.core:jackson-databind:2.19.1")
|
||||
testImplementation("com.google.protobuf:protobuf-java-util:4.31.1")
|
||||
testImplementation("com.squareup.okhttp3:okhttp:4.12.0")
|
||||
|
|
|
@ -10,11 +10,11 @@ plugins {
|
|||
into a single jar.
|
||||
See https://imperceptiblethoughts.com/shadow/ for more details about Shadow plugin.
|
||||
*/
|
||||
id "com.gradleup.shadow" version "8.3.6"
|
||||
id "com.gradleup.shadow" version "8.3.8"
|
||||
id "com.diffplug.spotless" version "7.0.4"
|
||||
|
||||
id "io.opentelemetry.instrumentation.muzzle-generation" version "2.17.0-alpha-SNAPSHOT"
|
||||
id "io.opentelemetry.instrumentation.muzzle-check" version "2.17.0-alpha-SNAPSHOT"
|
||||
id "io.opentelemetry.instrumentation.muzzle-generation" version "2.18.0-alpha-SNAPSHOT"
|
||||
id "io.opentelemetry.instrumentation.muzzle-check" version "2.18.0-alpha-SNAPSHOT"
|
||||
}
|
||||
|
||||
group 'io.opentelemetry.example'
|
||||
|
@ -26,8 +26,8 @@ ext {
|
|||
opentelemetrySdk : "1.51.0",
|
||||
|
||||
// these lines are managed by .github/scripts/update-version.sh
|
||||
opentelemetryJavaagent : "2.17.0-SNAPSHOT",
|
||||
opentelemetryJavaagentAlpha: "2.17.0-alpha-SNAPSHOT"
|
||||
opentelemetryJavaagent : "2.18.0-SNAPSHOT",
|
||||
opentelemetryJavaagentAlpha: "2.18.0-alpha-SNAPSHOT"
|
||||
]
|
||||
|
||||
deps = [
|
||||
|
@ -97,14 +97,14 @@ dependencies {
|
|||
implementation 'org.apache.commons:commons-lang3:3.17.0'
|
||||
|
||||
//All dependencies below are only for tests
|
||||
testImplementation("org.testcontainers:testcontainers:1.21.1")
|
||||
testImplementation("org.testcontainers:testcontainers:1.21.3")
|
||||
testImplementation("com.fasterxml.jackson.core:jackson-databind:2.19.1")
|
||||
testImplementation("com.google.protobuf:protobuf-java-util:4.31.1")
|
||||
testImplementation("com.squareup.okhttp3:okhttp:4.12.0")
|
||||
testImplementation("io.opentelemetry:opentelemetry-api")
|
||||
testImplementation("io.opentelemetry.proto:opentelemetry-proto:1.7.0-alpha")
|
||||
|
||||
testImplementation(enforcedPlatform("org.junit:junit-bom:5.13.1"))
|
||||
testImplementation(enforcedPlatform("org.junit:junit-bom:5.13.3"))
|
||||
testImplementation("org.junit.jupiter:junit-jupiter-api")
|
||||
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine")
|
||||
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionSha256Sum=7197a12f450794931532469d4ff21a59ea2c1cd59a3ec3f89c035c3c420a6999
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.2-bin.zip
|
||||
distributionSha256Sum=bd71102213493060956ec229d946beee57158dbd89d0e62b91bca0fa2c5f3531
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
|
|
@ -40,11 +40,11 @@ dependencies {
|
|||
implementation("org.eclipse.aether:aether-transport-http:${aetherVersion}")
|
||||
implementation("org.apache.maven:maven-aether-provider:3.3.9")
|
||||
|
||||
implementation("com.gradleup.shadow:shadow-gradle-plugin:8.3.6")
|
||||
implementation("com.gradleup.shadow:shadow-gradle-plugin:8.3.8")
|
||||
|
||||
testImplementation("org.assertj:assertj-core:3.27.3")
|
||||
|
||||
testImplementation(enforcedPlatform("org.junit:junit-bom:5.13.1"))
|
||||
testImplementation(enforcedPlatform("org.junit:junit-bom:5.13.3"))
|
||||
testImplementation("org.junit.jupiter:junit-jupiter-api")
|
||||
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine")
|
||||
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
|
||||
|
@ -107,7 +107,7 @@ nexusPublishing {
|
|||
}
|
||||
|
||||
connectTimeout.set(Duration.ofMinutes(5))
|
||||
clientTimeout.set(Duration.ofMinutes(5))
|
||||
clientTimeout.set(Duration.ofMinutes(30))
|
||||
}
|
||||
|
||||
tasks {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionSha256Sum=7197a12f450794931532469d4ff21a59ea2c1cd59a3ec3f89c035c3c420a6999
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.2-bin.zip
|
||||
distributionSha256Sum=bd71102213493060956ec229d946beee57158dbd89d0e62b91bca0fa2c5f3531
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionSha256Sum=7197a12f450794931532469d4ff21a59ea2c1cd59a3ec3f89c035c3c420a6999
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.2-bin.zip
|
||||
distributionSha256Sum=bd71102213493060956ec229d946beee57158dbd89d0e62b91bca0fa2c5f3531
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
|
|
@ -6,20 +6,20 @@
|
|||
package io.opentelemetry.javaagent.instrumentation.otelannotations;
|
||||
|
||||
import static io.opentelemetry.api.common.AttributeKey.booleanKey;
|
||||
import static io.opentelemetry.instrumentation.testing.junit.code.SemconvCodeStabilityUtil.codeFunctionAssertions;
|
||||
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo;
|
||||
import static io.opentelemetry.semconv.incubating.CodeIncubatingAttributes.CODE_FUNCTION;
|
||||
import static io.opentelemetry.semconv.incubating.CodeIncubatingAttributes.CODE_NAMESPACE;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.catchThrowable;
|
||||
|
||||
import io.opentelemetry.api.trace.SpanKind;
|
||||
import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension;
|
||||
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
|
||||
import io.opentelemetry.sdk.testing.assertj.AttributeAssertion;
|
||||
import io.opentelemetry.sdk.trace.data.StatusData;
|
||||
import java.util.List;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
|
||||
@SuppressWarnings("deprecation") // CodeIncubatingAttributes.CODE_FUNCTION is deprecated
|
||||
public abstract class AbstractWithSpanTest<T extends U, U> {
|
||||
|
||||
@RegisterExtension
|
||||
|
@ -59,8 +59,7 @@ public abstract class AbstractWithSpanTest<T extends U, U> {
|
|||
.hasKind(SpanKind.INTERNAL)
|
||||
.hasNoParent()
|
||||
.hasAttributesSatisfyingExactly(
|
||||
equalTo(CODE_NAMESPACE, traced.getClass().getName()),
|
||||
equalTo(CODE_FUNCTION, "completable"))));
|
||||
codeFunctionAssertions(traced.getClass(), "completable"))));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -82,8 +81,7 @@ public abstract class AbstractWithSpanTest<T extends U, U> {
|
|||
.hasStatus(StatusData.error())
|
||||
.hasException(AbstractTraced.FAILURE)
|
||||
.hasAttributesSatisfyingExactly(
|
||||
equalTo(CODE_NAMESPACE, traced.getClass().getName()),
|
||||
equalTo(CODE_FUNCTION, "completable"))));
|
||||
codeFunctionAssertions(traced.getClass(), "completable"))));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -92,6 +90,10 @@ public abstract class AbstractWithSpanTest<T extends U, U> {
|
|||
T future = traced.completable();
|
||||
cancel(future);
|
||||
|
||||
List<AttributeAssertion> attributeAssertions =
|
||||
codeFunctionAssertions(traced.getClass(), "completable");
|
||||
attributeAssertions.add(equalTo(booleanKey(canceledKey()), true));
|
||||
|
||||
testing.waitAndAssertTraces(
|
||||
trace ->
|
||||
trace.hasSpansSatisfyingExactly(
|
||||
|
@ -99,10 +101,7 @@ public abstract class AbstractWithSpanTest<T extends U, U> {
|
|||
span.hasName("Traced.completable")
|
||||
.hasKind(SpanKind.INTERNAL)
|
||||
.hasNoParent()
|
||||
.hasAttributesSatisfyingExactly(
|
||||
equalTo(CODE_NAMESPACE, traced.getClass().getName()),
|
||||
equalTo(CODE_FUNCTION, "completable"),
|
||||
equalTo(booleanKey(canceledKey()), true))));
|
||||
.hasAttributesSatisfyingExactly(attributeAssertions)));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -118,8 +117,7 @@ public abstract class AbstractWithSpanTest<T extends U, U> {
|
|||
.hasKind(SpanKind.INTERNAL)
|
||||
.hasNoParent()
|
||||
.hasAttributesSatisfyingExactly(
|
||||
equalTo(CODE_NAMESPACE, traced.getClass().getName()),
|
||||
equalTo(CODE_FUNCTION, "alreadySucceeded"))));
|
||||
codeFunctionAssertions(traced.getClass(), "alreadySucceeded"))));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -138,7 +136,6 @@ public abstract class AbstractWithSpanTest<T extends U, U> {
|
|||
.hasStatus(StatusData.error())
|
||||
.hasException(AbstractTraced.FAILURE)
|
||||
.hasAttributesSatisfyingExactly(
|
||||
equalTo(CODE_NAMESPACE, traced.getClass().getName()),
|
||||
equalTo(CODE_FUNCTION, "alreadyFailed"))));
|
||||
codeFunctionAssertions(traced.getClass(), "alreadyFailed"))));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ group = "io.opentelemetry.instrumentation"
|
|||
dependencies {
|
||||
api("io.opentelemetry.semconv:opentelemetry-semconv")
|
||||
api(project(":instrumentation-api"))
|
||||
implementation("io.opentelemetry:opentelemetry-api-incubator")
|
||||
api("io.opentelemetry:opentelemetry-api-incubator")
|
||||
|
||||
compileOnly("com.google.auto.value:auto-value-annotations")
|
||||
annotationProcessor("com.google.auto.value:auto-value")
|
||||
|
@ -42,11 +42,11 @@ tasks {
|
|||
}
|
||||
|
||||
val testStableSemconv by registering(Test::class) {
|
||||
jvmArgs("-Dotel.semconv-stability.opt-in=database")
|
||||
jvmArgs("-Dotel.semconv-stability.opt-in=database,code")
|
||||
}
|
||||
|
||||
val testBothSemconv by registering(Test::class) {
|
||||
jvmArgs("-Dotel.semconv-stability.opt-in=database/dup")
|
||||
jvmArgs("-Dotel.semconv-stability.opt-in=database/dup,code/dup")
|
||||
}
|
||||
|
||||
check {
|
||||
|
|
|
@ -7,6 +7,7 @@ package io.opentelemetry.instrumentation.api.incubator.config.internal;
|
|||
|
||||
import static java.util.Collections.emptyList;
|
||||
|
||||
import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties;
|
||||
import java.time.Duration;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
@ -107,4 +108,18 @@ public interface InstrumentationConfig {
|
|||
* {@code key=value,anotherKey=anotherValue}. The returned map is unmodifiable.
|
||||
*/
|
||||
Map<String, String> getMap(String name, Map<String, String> defaultValue);
|
||||
|
||||
/**
|
||||
* Returns a {@link DeclarativeConfigProperties} for the given instrumentation name, or {@code
|
||||
* null} if no declarative configuration is available for that instrumentation.
|
||||
*
|
||||
* <p>Declarative configuration is used to configure instrumentation properties in a declarative
|
||||
* way, such as through YAML or JSON files.
|
||||
*
|
||||
* @param instrumentationName the name of the instrumentation
|
||||
* @return the declarative configuration properties for the given instrumentation name, or {@code
|
||||
* null} if not available
|
||||
*/
|
||||
@Nullable
|
||||
DeclarativeConfigProperties getDeclarativeConfig(String instrumentationName);
|
||||
}
|
||||
|
|
|
@ -11,6 +11,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.instrumentation.api.internal.SemconvStability;
|
||||
import io.opentelemetry.semconv.CodeAttributes;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
|
@ -22,9 +24,9 @@ public final class CodeAttributesExtractor<REQUEST, RESPONSE>
|
|||
implements AttributesExtractor<REQUEST, RESPONSE> {
|
||||
|
||||
// copied from CodeIncubatingAttributes
|
||||
private static final AttributeKey<String> CODE_FUNCTION = AttributeKey.stringKey("code.function");
|
||||
private static final AttributeKey<String> CODE_NAMESPACE =
|
||||
AttributeKey.stringKey("code.namespace");
|
||||
private static final AttributeKey<String> CODE_FUNCTION = AttributeKey.stringKey("code.function");
|
||||
|
||||
/** Creates the code attributes extractor. */
|
||||
public static <REQUEST, RESPONSE> AttributesExtractor<REQUEST, RESPONSE> create(
|
||||
|
@ -40,11 +42,28 @@ public final class CodeAttributesExtractor<REQUEST, RESPONSE>
|
|||
|
||||
@Override
|
||||
public void onStart(AttributesBuilder attributes, Context parentContext, REQUEST request) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
Class<?> cls = getter.getCodeClass(request);
|
||||
if (cls != null) {
|
||||
internalSet(attributes, CODE_NAMESPACE, cls.getName());
|
||||
sb.append(cls.getName());
|
||||
|
||||
if (SemconvStability.isEmitOldCodeSemconv()) {
|
||||
internalSet(attributes, CODE_NAMESPACE, cls.getName());
|
||||
}
|
||||
}
|
||||
String methodName = getter.getMethodName(request);
|
||||
if (methodName != null) {
|
||||
if (sb.length() > 0) {
|
||||
sb.append(".");
|
||||
}
|
||||
sb.append(methodName);
|
||||
if (SemconvStability.isEmitOldCodeSemconv()) {
|
||||
internalSet(attributes, CODE_FUNCTION, methodName);
|
||||
}
|
||||
}
|
||||
if (SemconvStability.isEmitStableCodeSemconv() && sb.length() > 0) {
|
||||
internalSet(attributes, CodeAttributes.CODE_FUNCTION_NAME, sb.toString());
|
||||
}
|
||||
internalSet(attributes, CODE_FUNCTION, getter.getMethodName(request));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -6,12 +6,14 @@
|
|||
package io.opentelemetry.instrumentation.api.incubator.semconv.code;
|
||||
|
||||
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.entry;
|
||||
|
||||
import io.opentelemetry.api.common.Attributes;
|
||||
import io.opentelemetry.api.common.AttributesBuilder;
|
||||
import io.opentelemetry.context.Context;
|
||||
import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor;
|
||||
import io.opentelemetry.instrumentation.api.internal.SemconvStability;
|
||||
import io.opentelemetry.instrumentation.testing.junit.code.SemconvCodeStabilityUtil;
|
||||
import io.opentelemetry.semconv.CodeAttributes;
|
||||
import io.opentelemetry.semconv.incubating.CodeIncubatingAttributes;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
|
@ -58,11 +60,19 @@ class CodeAttributesExtractorTest {
|
|||
underTest.onEnd(endAttributes, context, request, null, null);
|
||||
|
||||
// then
|
||||
assertThat(startAttributes.build())
|
||||
.containsOnly(
|
||||
entry(CodeIncubatingAttributes.CODE_NAMESPACE, TestClass.class.getName()),
|
||||
entry(CodeIncubatingAttributes.CODE_FUNCTION, "doSomething"));
|
||||
Attributes attributes = startAttributes.build();
|
||||
SemconvCodeStabilityUtil.codeFunctionAssertions(TestClass.class, "doSomething");
|
||||
|
||||
if (SemconvStability.isEmitStableCodeSemconv()) {
|
||||
assertThat(attributes)
|
||||
.containsEntry(
|
||||
CodeAttributes.CODE_FUNCTION_NAME, TestClass.class.getName() + ".doSomething");
|
||||
}
|
||||
if (SemconvStability.isEmitOldCodeSemconv()) {
|
||||
assertThat(attributes)
|
||||
.containsEntry(CodeIncubatingAttributes.CODE_NAMESPACE, TestClass.class.getName())
|
||||
.containsEntry(CodeIncubatingAttributes.CODE_FUNCTION, "doSomething");
|
||||
}
|
||||
assertThat(endAttributes.build().isEmpty()).isTrue();
|
||||
}
|
||||
|
||||
|
|
|
@ -21,27 +21,47 @@ public final class SemconvStability {
|
|||
private static final boolean emitOldDatabaseSemconv;
|
||||
private static final boolean emitStableDatabaseSemconv;
|
||||
|
||||
private static final boolean emitOldCodeSemconv;
|
||||
private static final boolean emitStableCodeSemconv;
|
||||
|
||||
static {
|
||||
boolean oldDatabase = true;
|
||||
boolean stableDatabase = false;
|
||||
|
||||
boolean oldCode = true;
|
||||
boolean stableCode = false;
|
||||
|
||||
String value = ConfigPropertiesUtil.getString("otel.semconv-stability.opt-in");
|
||||
if (value != null) {
|
||||
Set<String> values = new HashSet<>(asList(value.split(",")));
|
||||
|
||||
// no else -- technically it's possible to set "XXX,XXX/dup", in which case we
|
||||
// should emit both sets of attributes for XXX
|
||||
|
||||
if (values.contains("database")) {
|
||||
oldDatabase = false;
|
||||
stableDatabase = true;
|
||||
}
|
||||
// no else -- technically it's possible to set "database,database/dup", in which case we
|
||||
// should emit both sets of attributes
|
||||
if (values.contains("database/dup")) {
|
||||
oldDatabase = true;
|
||||
stableDatabase = true;
|
||||
}
|
||||
|
||||
if (values.contains("code")) {
|
||||
oldCode = false;
|
||||
stableCode = true;
|
||||
}
|
||||
if (values.contains("code/dup")) {
|
||||
oldCode = true;
|
||||
stableCode = true;
|
||||
}
|
||||
}
|
||||
|
||||
emitOldDatabaseSemconv = oldDatabase;
|
||||
emitStableDatabaseSemconv = stableDatabase;
|
||||
|
||||
emitOldCodeSemconv = oldCode;
|
||||
emitStableCodeSemconv = stableCode;
|
||||
}
|
||||
|
||||
public static boolean emitOldDatabaseSemconv() {
|
||||
|
@ -77,5 +97,13 @@ public final class SemconvStability {
|
|||
return dbSystemName != null ? dbSystemName : oldDbSystem;
|
||||
}
|
||||
|
||||
public static boolean isEmitOldCodeSemconv() {
|
||||
return emitOldCodeSemconv;
|
||||
}
|
||||
|
||||
public static boolean isEmitStableCodeSemconv() {
|
||||
return emitStableCodeSemconv;
|
||||
}
|
||||
|
||||
private SemconvStability() {}
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ dependencies {
|
|||
implementation("com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.19.1")
|
||||
implementation("io.opentelemetry:opentelemetry-sdk-common")
|
||||
|
||||
testImplementation(enforcedPlatform("org.junit:junit-bom:5.13.1"))
|
||||
testImplementation(enforcedPlatform("org.junit:junit-bom:5.13.3"))
|
||||
testImplementation("org.assertj:assertj-core:3.27.3")
|
||||
testImplementation("org.junit.jupiter:junit-jupiter-api")
|
||||
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine")
|
||||
|
|
|
@ -14,13 +14,18 @@ fi
|
|||
|
||||
readonly INSTRUMENTATIONS=(
|
||||
# <module path (colon-separated)> : <javaagent|library> : [ gradle-task-suffix ]
|
||||
"activej-http-6.0:javaagent:test"
|
||||
"akka:akka-http-10.0:javaagent:test"
|
||||
"apache-httpasyncclient-4.1:javaagent:test"
|
||||
"alibaba-druid-1.0:javaagent:test"
|
||||
"alibaba-druid-1.0:javaagent:testStableSemconv"
|
||||
"apache-dbcp-2.0:javaagent:test"
|
||||
"apache-dbcp-2.0:javaagent:testStableSemconv"
|
||||
"apache-httpclient:apache-httpclient-2.0:javaagent:test"
|
||||
"apache-httpclient:apache-httpclient-4.0:javaagent:test"
|
||||
"apache-httpclient:apache-httpclient-4.3:library:test"
|
||||
"apache-httpclient:apache-httpclient-5.0:javaagent:test"
|
||||
"apache-dubbo-2.7:javaagent:testDubbo"
|
||||
"c3p0-0.9:javaagent:test"
|
||||
"c3p0-0.9:javaagent:testStableSemconv"
|
||||
"clickhouse-client-0.5:javaagent:test"
|
||||
|
@ -169,7 +174,7 @@ echo
|
|||
|
||||
./gradlew "${gradle_tasks[@]}" \
|
||||
-PcollectMetadata=true \
|
||||
--rerun-tasks
|
||||
--rerun-tasks --continue
|
||||
|
||||
# uncomment the next line to remove all .telemetry directories
|
||||
#find_and_remove_all_telemetry
|
||||
|
|
|
@ -8,10 +8,11 @@ Run the analysis to update the instrumentation-list.yaml:
|
|||
|
||||
`./gradlew :instrumentation-docs:runAnalysis`
|
||||
|
||||
### Metric collection
|
||||
### Telemetry collection
|
||||
|
||||
Until this process is ready for all instrumentations, each module will be modified to include a
|
||||
system property feature flag configured for when the tests run:
|
||||
system property feature flag configured for when the tests run. By enabling the following flag you
|
||||
will enable metric collection:
|
||||
|
||||
```kotlin
|
||||
tasks {
|
||||
|
@ -22,6 +23,17 @@ tasks {
|
|||
}
|
||||
```
|
||||
|
||||
In order to collect spans, add the `collectSpans` property (along with `collectMetadata`):
|
||||
|
||||
```kotlin
|
||||
tasks {
|
||||
test {
|
||||
systemProperty("collectMetadata", collectMetadata)
|
||||
systemProperty("collectSpans", true)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Sometimes instrumentation will behave differently based on configuration options, and we can
|
||||
differentiate between these configurations by using the `metaDataConfig` system property. When the
|
||||
telemetry is written to a file, the value of this property will be included, or it will default to
|
||||
|
@ -130,6 +142,9 @@ public class SpringWebInstrumentationModule extends InstrumentationModule
|
|||
* metrics
|
||||
* List of metrics that the instrumentation module collects, including the metric name, description, type, and attributes.
|
||||
* Separate lists for the metrics emitted by default vs via configuration options.
|
||||
* spans
|
||||
* List of spans kinds the instrumentation module generates, including the attributes and their types.
|
||||
* Separate lists for the spans emitted by default vs via configuration options.
|
||||
|
||||
## Methodology
|
||||
|
||||
|
@ -168,16 +183,16 @@ name is determined by the instrumentation module name: `io.opentelemetry.{instr
|
|||
We will implement gatherers for the schemaUrl and scope attributes when instrumentations start
|
||||
implementing them.
|
||||
|
||||
### Metrics
|
||||
### Spans and Metrics
|
||||
|
||||
In order to identify what metrics are emitted from instrumentations, we can hook into the
|
||||
`InstrumentationTestRunner` class and collect the metrics generated during runs. We can then
|
||||
In order to identify what telemetry is emitted from instrumentations, we can hook into the
|
||||
`InstrumentationTestRunner` class and collect the metrics and spans generated during runs. We can then
|
||||
leverage the `afterTestClass()` in the Agent and library test runners to then write this information
|
||||
into temporary files. When we analyze the instrumentation modules, we can read these files and
|
||||
generate the metrics section of the instrumentation-list.yaml file.
|
||||
generate the telemetry section of the instrumentation-list.yaml file.
|
||||
|
||||
The data is written into a `.telemetry` directory in the root of each instrumentation module. This
|
||||
data will be excluded from git and just generated on demand.
|
||||
|
||||
Each file has a `when` value along with the list of metrics that indicates whether the telemetry is emitted by default or via a
|
||||
configuration option.
|
||||
Each file has a `when` value along with the list of metrics that indicates whether the telemetry is
|
||||
emitted by default or via a configuration option.
|
||||
|
|
|
@ -14,6 +14,7 @@ import java.io.BufferedWriter;
|
|||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.TreeMap;
|
||||
|
@ -107,5 +108,48 @@ public class DocGeneratorApplication {
|
|||
+ "%)";
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused") // temporary helper method used for project tracking
|
||||
private static String listAllModules(List<InstrumentationModule> modules) {
|
||||
return modules.stream()
|
||||
.map(InstrumentationModule::getInstrumentationName)
|
||||
.sorted()
|
||||
.map(name -> "- [ ] " + name)
|
||||
.collect(Collectors.joining("\n"));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused") // temporary helper method used for project tracking
|
||||
private static String modulesWithDescriptions(List<InstrumentationModule> modules) {
|
||||
// checklist of all modules sorted by name, with a check if description is set
|
||||
return modules.stream()
|
||||
.sorted(Comparator.comparing(InstrumentationModule::getInstrumentationName))
|
||||
.map(
|
||||
module -> {
|
||||
boolean hasDescription =
|
||||
module.getMetadata() != null
|
||||
&& module.getMetadata().getDescription() != null
|
||||
&& !module.getMetadata().getDescription().isEmpty();
|
||||
String checkbox = hasDescription ? "- [x] " : "- [ ] ";
|
||||
return checkbox + module.getInstrumentationName();
|
||||
})
|
||||
.collect(Collectors.joining("\n"));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused") // temporary helper method used for project tracking
|
||||
private static String modulesWithConfigs(List<InstrumentationModule> modules) {
|
||||
// checklist of all modules sorted by name, with a check if config is set
|
||||
return modules.stream()
|
||||
.sorted(Comparator.comparing(InstrumentationModule::getInstrumentationName))
|
||||
.map(
|
||||
module -> {
|
||||
boolean hasDescription =
|
||||
module.getMetadata() != null
|
||||
&& module.getMetadata().getConfigurations() != null
|
||||
&& !module.getMetadata().getConfigurations().isEmpty();
|
||||
String checkbox = hasDescription ? "- [x] " : "- [ ] ";
|
||||
return checkbox + module.getInstrumentationName();
|
||||
})
|
||||
.collect(Collectors.joining("\n"));
|
||||
}
|
||||
|
||||
private DocGeneratorApplication() {}
|
||||
}
|
||||
|
|
|
@ -5,26 +5,29 @@
|
|||
|
||||
package io.opentelemetry.instrumentation.docs;
|
||||
|
||||
import static io.opentelemetry.instrumentation.docs.parsers.GradleParser.parseGradleFile;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.exc.ValueInstantiationException;
|
||||
import io.opentelemetry.instrumentation.docs.internal.DependencyInfo;
|
||||
import io.opentelemetry.instrumentation.docs.internal.EmittedMetrics;
|
||||
import io.opentelemetry.instrumentation.docs.internal.InstrumentationMetaData;
|
||||
import io.opentelemetry.instrumentation.docs.internal.InstrumentationModule;
|
||||
import io.opentelemetry.instrumentation.docs.internal.InstrumentationType;
|
||||
import io.opentelemetry.instrumentation.docs.parsers.GradleParser;
|
||||
import io.opentelemetry.instrumentation.docs.parsers.MetricParser;
|
||||
import io.opentelemetry.instrumentation.docs.parsers.ModuleParser;
|
||||
import io.opentelemetry.instrumentation.docs.parsers.SpanParser;
|
||||
import io.opentelemetry.instrumentation.docs.utils.FileManager;
|
||||
import io.opentelemetry.instrumentation.docs.utils.InstrumentationPath;
|
||||
import io.opentelemetry.instrumentation.docs.utils.YamlHelper;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Logger;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Analyzes instrumentation modules by extracting version information, metrics, spans, and metadata
|
||||
* from various source files.
|
||||
*/
|
||||
class InstrumentationAnalyzer {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(InstrumentationAnalyzer.class.getName());
|
||||
|
@ -36,100 +39,52 @@ class InstrumentationAnalyzer {
|
|||
}
|
||||
|
||||
/**
|
||||
* Converts a list of {@link InstrumentationPath} into a list of {@link InstrumentationModule},
|
||||
* Analyzes all instrumentation modules found in the root directory.
|
||||
*
|
||||
* @param paths the list of {@link InstrumentationPath} objects to be converted
|
||||
* @return a list of {@link InstrumentationModule} objects with aggregated types
|
||||
* @return a list of analyzed {@link InstrumentationModule}
|
||||
* @throws IOException if file operations fail
|
||||
*/
|
||||
public static List<InstrumentationModule> convertToInstrumentationModules(
|
||||
String rootPath, List<InstrumentationPath> paths) {
|
||||
Map<String, InstrumentationModule> moduleMap = new HashMap<>();
|
||||
|
||||
for (InstrumentationPath path : paths) {
|
||||
String key = path.group() + ":" + path.namespace() + ":" + path.instrumentationName();
|
||||
if (!moduleMap.containsKey(key)) {
|
||||
moduleMap.put(
|
||||
key,
|
||||
new InstrumentationModule.Builder()
|
||||
.srcPath(sanitizePathName(rootPath, path.srcPath()))
|
||||
.instrumentationName(path.instrumentationName())
|
||||
.namespace(path.namespace())
|
||||
.group(path.group())
|
||||
.build());
|
||||
}
|
||||
}
|
||||
|
||||
return new ArrayList<>(moduleMap.values());
|
||||
}
|
||||
|
||||
private static String sanitizePathName(String rootPath, String path) {
|
||||
return path.replace(rootPath, "").replace("/javaagent", "").replace("/library", "");
|
||||
}
|
||||
|
||||
/**
|
||||
* Traverses the given root directory to find all instrumentation paths and then analyzes them.
|
||||
* Extracts version information from each instrumentation's build.gradle file, metric data from
|
||||
* files in the .telemetry directories, and other information from metadata.yaml files.
|
||||
*
|
||||
* @return a list of {@link InstrumentationModule}
|
||||
*/
|
||||
List<InstrumentationModule> analyze() throws IOException {
|
||||
public List<InstrumentationModule> analyze() throws IOException {
|
||||
List<InstrumentationPath> paths = fileManager.getInstrumentationPaths();
|
||||
List<InstrumentationModule> modules =
|
||||
convertToInstrumentationModules(fileManager.rootDir(), paths);
|
||||
ModuleParser.convertToModules(fileManager.rootDir(), paths);
|
||||
|
||||
for (InstrumentationModule module : modules) {
|
||||
List<String> gradleFiles = fileManager.findBuildGradleFiles(module.getSrcPath());
|
||||
analyzeVersions(gradleFiles, module);
|
||||
|
||||
String metadataFile = fileManager.getMetaDataFile(module.getSrcPath());
|
||||
if (metadataFile != null) {
|
||||
try {
|
||||
module.setMetadata(YamlHelper.metaDataParser(metadataFile));
|
||||
} catch (ValueInstantiationException e) {
|
||||
logger.severe("Error parsing metadata file for " + module.getInstrumentationName());
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, EmittedMetrics> metrics =
|
||||
MetricParser.getMetricsFromFiles(fileManager.rootDir(), module.getSrcPath());
|
||||
|
||||
for (Map.Entry<String, EmittedMetrics> entry : metrics.entrySet()) {
|
||||
if (entry.getValue() == null || entry.getValue().getMetrics() == null) {
|
||||
continue;
|
||||
}
|
||||
module.getMetrics().put(entry.getKey(), entry.getValue().getMetrics());
|
||||
}
|
||||
enrichModule(module);
|
||||
}
|
||||
|
||||
return modules;
|
||||
}
|
||||
|
||||
void analyzeVersions(List<String> files, InstrumentationModule module) {
|
||||
Map<InstrumentationType, Set<String>> versions = new HashMap<>();
|
||||
for (String file : files) {
|
||||
String fileContents = FileManager.readFileToString(file);
|
||||
if (fileContents == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
DependencyInfo results = null;
|
||||
|
||||
if (file.contains("/javaagent/")) {
|
||||
results = parseGradleFile(fileContents, InstrumentationType.JAVAAGENT);
|
||||
versions
|
||||
.computeIfAbsent(InstrumentationType.JAVAAGENT, k -> new HashSet<>())
|
||||
.addAll(results.versions());
|
||||
} else if (file.contains("/library/")) {
|
||||
results = parseGradleFile(fileContents, InstrumentationType.LIBRARY);
|
||||
versions
|
||||
.computeIfAbsent(InstrumentationType.LIBRARY, k -> new HashSet<>())
|
||||
.addAll(results.versions());
|
||||
}
|
||||
if (results != null && results.minJavaVersionSupported() != null) {
|
||||
module.setMinJavaVersion(results.minJavaVersionSupported());
|
||||
}
|
||||
private void enrichModule(InstrumentationModule module) throws IOException {
|
||||
InstrumentationMetaData metaData = getMetadata(module);
|
||||
if (metaData != null) {
|
||||
module.setMetadata(metaData);
|
||||
}
|
||||
module.setTargetVersions(versions);
|
||||
|
||||
module.setTargetVersions(getVersionInformation(module));
|
||||
module.setMetrics(MetricParser.getMetrics(module, fileManager));
|
||||
module.setSpans(SpanParser.getSpans(module, fileManager));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private InstrumentationMetaData getMetadata(InstrumentationModule module)
|
||||
throws JsonProcessingException {
|
||||
String metadataFile = fileManager.getMetaDataFile(module.getSrcPath());
|
||||
if (metadataFile == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return YamlHelper.metaDataParser(metadataFile);
|
||||
} catch (ValueInstantiationException e) {
|
||||
logger.severe("Error parsing metadata file for " + module.getInstrumentationName());
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
private Map<InstrumentationType, Set<String>> getVersionInformation(
|
||||
InstrumentationModule module) {
|
||||
List<String> gradleFiles = fileManager.findBuildGradleFiles(module.getSrcPath());
|
||||
return GradleParser.extractVersions(gradleFiles, module);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,9 @@
|
|||
|
||||
package io.opentelemetry.instrumentation.docs.internal;
|
||||
|
||||
import static java.util.Collections.emptyList;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
|
@ -16,16 +19,18 @@ import java.util.List;
|
|||
public class EmittedMetrics {
|
||||
// Condition in which the metrics are emitted (ex: default, or configuration option names).
|
||||
private String when;
|
||||
private List<Metric> metrics;
|
||||
|
||||
@JsonProperty("metrics_by_scope")
|
||||
private List<MetricsByScope> metricsByScope;
|
||||
|
||||
public EmittedMetrics() {
|
||||
this.when = "";
|
||||
this.metrics = new ArrayList<>();
|
||||
this.metricsByScope = emptyList();
|
||||
}
|
||||
|
||||
public EmittedMetrics(String when, List<Metric> metrics) {
|
||||
this.when = "";
|
||||
this.metrics = metrics;
|
||||
public EmittedMetrics(String when, List<MetricsByScope> metricsByScope) {
|
||||
this.when = when;
|
||||
this.metricsByScope = metricsByScope;
|
||||
}
|
||||
|
||||
public String getWhen() {
|
||||
|
@ -36,12 +41,49 @@ public class EmittedMetrics {
|
|||
this.when = when;
|
||||
}
|
||||
|
||||
public List<Metric> getMetrics() {
|
||||
return metrics;
|
||||
@JsonProperty("metrics_by_scope")
|
||||
public List<MetricsByScope> getMetricsByScope() {
|
||||
return metricsByScope;
|
||||
}
|
||||
|
||||
public void setMetrics(List<Metric> metrics) {
|
||||
this.metrics = metrics;
|
||||
@JsonProperty("metrics_by_scope")
|
||||
public void setMetricsByScope(List<MetricsByScope> metricsByScope) {
|
||||
this.metricsByScope = metricsByScope;
|
||||
}
|
||||
|
||||
/**
|
||||
* This class is internal and is hence not for public use. Its APIs are unstable and can change at
|
||||
* any time.
|
||||
*/
|
||||
public static class MetricsByScope {
|
||||
private String scope;
|
||||
private List<Metric> metrics;
|
||||
|
||||
public MetricsByScope(String scope, List<Metric> metrics) {
|
||||
this.scope = scope;
|
||||
this.metrics = metrics;
|
||||
}
|
||||
|
||||
public MetricsByScope() {
|
||||
this.scope = "";
|
||||
this.metrics = new ArrayList<>();
|
||||
}
|
||||
|
||||
public String getScope() {
|
||||
return scope;
|
||||
}
|
||||
|
||||
public void setScope(String scope) {
|
||||
this.scope = scope;
|
||||
}
|
||||
|
||||
public List<Metric> getMetrics() {
|
||||
return metrics;
|
||||
}
|
||||
|
||||
public void setMetrics(List<Metric> metrics) {
|
||||
this.metrics = metrics;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -53,10 +95,14 @@ public class EmittedMetrics {
|
|||
private String description;
|
||||
private String type;
|
||||
private String unit;
|
||||
private List<Attribute> attributes;
|
||||
private List<TelemetryAttribute> attributes;
|
||||
|
||||
public Metric(
|
||||
String name, String description, String type, String unit, List<Attribute> attributes) {
|
||||
String name,
|
||||
String description,
|
||||
String type,
|
||||
String unit,
|
||||
List<TelemetryAttribute> attributes) {
|
||||
this.name = name;
|
||||
this.description = description;
|
||||
this.type = type;
|
||||
|
@ -104,47 +150,12 @@ public class EmittedMetrics {
|
|||
this.unit = unit;
|
||||
}
|
||||
|
||||
public List<Attribute> getAttributes() {
|
||||
public List<TelemetryAttribute> getAttributes() {
|
||||
return attributes;
|
||||
}
|
||||
|
||||
public void setAttributes(List<Attribute> attributes) {
|
||||
public void setAttributes(List<TelemetryAttribute> attributes) {
|
||||
this.attributes = attributes;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This class is internal and is hence not for public use. Its APIs are unstable and can change at
|
||||
* any time.
|
||||
*/
|
||||
public static class Attribute {
|
||||
private String name;
|
||||
private String type;
|
||||
|
||||
public Attribute() {
|
||||
this.name = "";
|
||||
this.type = "";
|
||||
}
|
||||
|
||||
public Attribute(String name, String type) {
|
||||
this.name = name;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,127 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.instrumentation.docs.internal;
|
||||
|
||||
import static java.util.Collections.emptyList;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Representation of spans emitted by an instrumentation. Includes context about whether emitted by
|
||||
* default or via a configuration option. This class is internal and is hence not for public use.
|
||||
* Its APIs are unstable and can change at any time.
|
||||
*/
|
||||
public class EmittedSpans {
|
||||
// Condition in which the telemetry is emitted (ex: default, or configuration option names).
|
||||
private String when;
|
||||
|
||||
@JsonProperty("spans_by_scope")
|
||||
private List<SpansByScope> spansByScope;
|
||||
|
||||
public EmittedSpans() {
|
||||
this.when = "";
|
||||
this.spansByScope = emptyList();
|
||||
}
|
||||
|
||||
public EmittedSpans(String when, List<SpansByScope> spansByScope) {
|
||||
this.when = when;
|
||||
this.spansByScope = spansByScope;
|
||||
}
|
||||
|
||||
public String getWhen() {
|
||||
return when;
|
||||
}
|
||||
|
||||
public void setWhen(String when) {
|
||||
this.when = when;
|
||||
}
|
||||
|
||||
@JsonProperty("spans_by_scope")
|
||||
public List<SpansByScope> getSpansByScope() {
|
||||
return spansByScope;
|
||||
}
|
||||
|
||||
@JsonProperty("spans_by_scope")
|
||||
public void setSpansByScope(List<SpansByScope> spans) {
|
||||
this.spansByScope = spans;
|
||||
}
|
||||
|
||||
/**
|
||||
* This class is internal and is hence not for public use. Its APIs are unstable and can change at
|
||||
* any time.
|
||||
*/
|
||||
public static class SpansByScope {
|
||||
private String scope;
|
||||
private List<Span> spans;
|
||||
|
||||
public SpansByScope(String scopeName, List<Span> spans) {
|
||||
this.scope = scopeName;
|
||||
this.spans = spans;
|
||||
}
|
||||
|
||||
public SpansByScope() {
|
||||
this.scope = "";
|
||||
this.spans = emptyList();
|
||||
}
|
||||
|
||||
public String getScope() {
|
||||
return scope;
|
||||
}
|
||||
|
||||
public void setScope(String scope) {
|
||||
this.scope = scope;
|
||||
}
|
||||
|
||||
public List<Span> getSpans() {
|
||||
return spans;
|
||||
}
|
||||
|
||||
public void setSpans(List<Span> spans) {
|
||||
this.spans = spans;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This class is internal and is hence not for public use. Its APIs are unstable and can change at
|
||||
* any time.
|
||||
*/
|
||||
public static class Span {
|
||||
@JsonProperty("span_kind")
|
||||
private String spanKind;
|
||||
|
||||
private List<TelemetryAttribute> attributes;
|
||||
|
||||
public Span(String spanKind, List<TelemetryAttribute> attributes) {
|
||||
this.spanKind = spanKind;
|
||||
this.attributes = attributes;
|
||||
}
|
||||
|
||||
public Span() {
|
||||
this.spanKind = "";
|
||||
this.attributes = new ArrayList<>();
|
||||
}
|
||||
|
||||
@JsonProperty("span_kind")
|
||||
public String getSpanKind() {
|
||||
return spanKind;
|
||||
}
|
||||
|
||||
@JsonProperty("span_kind")
|
||||
public void setSpanKind(String spanKind) {
|
||||
this.spanKind = spanKind;
|
||||
}
|
||||
|
||||
public List<TelemetryAttribute> getAttributes() {
|
||||
return attributes;
|
||||
}
|
||||
|
||||
public void setAttributes(List<TelemetryAttribute> attributes) {
|
||||
this.attributes = attributes;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -29,6 +29,7 @@ public class InstrumentationModule {
|
|||
private final String group;
|
||||
private final InstrumentationScopeInfo scopeInfo;
|
||||
private Map<String, List<EmittedMetrics.Metric>> metrics;
|
||||
private Map<String, List<EmittedSpans.Span>> spans;
|
||||
|
||||
@Nullable private Map<InstrumentationType, Set<String>> targetVersions;
|
||||
|
||||
|
@ -47,6 +48,7 @@ public class InstrumentationModule {
|
|||
requireNonNull(builder.group, "group required");
|
||||
|
||||
this.metrics = Objects.requireNonNullElseGet(builder.metrics, HashMap::new);
|
||||
this.spans = Objects.requireNonNullElseGet(builder.spans, HashMap::new);
|
||||
this.srcPath = builder.srcPath;
|
||||
this.instrumentationName = builder.instrumentationName;
|
||||
this.namespace = builder.namespace;
|
||||
|
@ -99,6 +101,10 @@ public class InstrumentationModule {
|
|||
return metrics;
|
||||
}
|
||||
|
||||
public Map<String, List<EmittedSpans.Span>> getSpans() {
|
||||
return spans;
|
||||
}
|
||||
|
||||
public void setTargetVersions(Map<InstrumentationType, Set<String>> targetVersions) {
|
||||
this.targetVersions = targetVersions;
|
||||
}
|
||||
|
@ -115,6 +121,10 @@ public class InstrumentationModule {
|
|||
this.metrics = metrics;
|
||||
}
|
||||
|
||||
public void setSpans(Map<String, List<EmittedSpans.Span>> spans) {
|
||||
this.spans = spans;
|
||||
}
|
||||
|
||||
/**
|
||||
* This class is internal and is hence not for public use. Its APIs are unstable and can change at
|
||||
* any time.
|
||||
|
@ -128,6 +138,7 @@ public class InstrumentationModule {
|
|||
@Nullable private InstrumentationMetaData metadata;
|
||||
@Nullable private Map<InstrumentationType, Set<String>> targetVersions;
|
||||
@Nullable private Map<String, List<EmittedMetrics.Metric>> metrics;
|
||||
@Nullable private Map<String, List<EmittedSpans.Span>> spans;
|
||||
|
||||
@CanIgnoreReturnValue
|
||||
public Builder srcPath(String srcPath) {
|
||||
|
@ -177,6 +188,12 @@ public class InstrumentationModule {
|
|||
return this;
|
||||
}
|
||||
|
||||
@CanIgnoreReturnValue
|
||||
public Builder spans(Map<String, List<EmittedSpans.Span>> spans) {
|
||||
this.spans = spans;
|
||||
return this;
|
||||
}
|
||||
|
||||
public InstrumentationModule build() {
|
||||
return new InstrumentationModule(this);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.instrumentation.docs.internal;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* This class is internal and is hence not for public use. Its APIs are unstable and can change at
|
||||
* any time.
|
||||
*/
|
||||
public class TelemetryAttribute {
|
||||
private String name;
|
||||
private String type;
|
||||
|
||||
public TelemetryAttribute() {
|
||||
this.name = "";
|
||||
this.type = "";
|
||||
}
|
||||
|
||||
public TelemetryAttribute(String name, String type) {
|
||||
this.name = name;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
// Overriding equals and hashCode to compare TelemetryAttribute objects based on name and type
|
||||
// This is important for our deduplication logic in the documentation generation process.
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (!(o instanceof TelemetryAttribute that)) {
|
||||
return false;
|
||||
}
|
||||
return Objects.equals(name, that.name) && Objects.equals(type, that.type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(name, type);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,169 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.instrumentation.docs.parsers;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import io.opentelemetry.instrumentation.docs.internal.EmittedMetrics;
|
||||
import io.opentelemetry.instrumentation.docs.utils.FileManager;
|
||||
import io.opentelemetry.instrumentation.docs.utils.YamlHelper;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* This class is responsible for parsing metric-* files from the `.telemetry` directory of an
|
||||
* instrumentation module and converting them into the {@link EmittedMetrics} format.
|
||||
*/
|
||||
public class EmittedMetricsParser {
|
||||
private static final Logger logger = Logger.getLogger(EmittedMetricsParser.class.getName());
|
||||
|
||||
/**
|
||||
* Looks for metric files in the .telemetry directory, and combines them into a map where the key
|
||||
* represents the "when", and the value is a list of metrics emitted under that condition.
|
||||
*
|
||||
* @param instrumentationDirectory the directory to traverse
|
||||
* @return contents of aggregated files
|
||||
*/
|
||||
public static Map<String, EmittedMetrics> getMetricsFromFiles(
|
||||
String rootDir, String instrumentationDirectory) {
|
||||
Path telemetryDir = Paths.get(rootDir + "/" + instrumentationDirectory, ".telemetry");
|
||||
|
||||
Map<String, List<EmittedMetrics.MetricsByScope>> metricsByWhen =
|
||||
parseAllMetricFiles(telemetryDir);
|
||||
|
||||
return aggregateMetricsByScope(metricsByWhen);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses all metric files in the given .telemetry directory and returns a map where the key is
|
||||
* the 'when' condition and the value is a list of metrics grouped by scope.
|
||||
*
|
||||
* @param telemetryDir the path to the .telemetry directory
|
||||
* @return a map of 'when' to list of metrics by scope
|
||||
*/
|
||||
private static Map<String, List<EmittedMetrics.MetricsByScope>> parseAllMetricFiles(
|
||||
Path telemetryDir) {
|
||||
Map<String, List<EmittedMetrics.MetricsByScope>> metricsByWhen = new HashMap<>();
|
||||
if (Files.exists(telemetryDir) && Files.isDirectory(telemetryDir)) {
|
||||
try (Stream<Path> files = Files.list(telemetryDir)) {
|
||||
files
|
||||
.filter(path -> path.getFileName().toString().startsWith("metrics-"))
|
||||
.forEach(
|
||||
path -> {
|
||||
String content = FileManager.readFileToString(path.toString());
|
||||
if (content != null) {
|
||||
String when = content.substring(0, content.indexOf('\n'));
|
||||
String whenKey = when.replace("when: ", "");
|
||||
|
||||
int metricsIndex = content.indexOf("metrics_by_scope:");
|
||||
if (metricsIndex != -1) {
|
||||
String yaml = "when: " + whenKey + "\n" + content.substring(metricsIndex);
|
||||
EmittedMetrics parsed;
|
||||
try {
|
||||
parsed = YamlHelper.emittedMetricsParser(yaml);
|
||||
} catch (Exception e) {
|
||||
logger.severe(
|
||||
"Error parsing metrics file (" + path + "): " + e.getMessage());
|
||||
return;
|
||||
}
|
||||
if (parsed.getMetricsByScope() != null) {
|
||||
metricsByWhen.putIfAbsent(whenKey, new ArrayList<>());
|
||||
metricsByWhen.get(whenKey).addAll(parsed.getMetricsByScope());
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (IOException e) {
|
||||
logger.severe("Error reading metrics files: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
return metricsByWhen;
|
||||
}
|
||||
|
||||
/**
|
||||
* Aggregates metrics under the same scope for each 'when' condition, deduplicating metrics by
|
||||
* name.
|
||||
*
|
||||
* @param metricsByWhen map of 'when' to list of metrics by scope
|
||||
* @return a map of 'when' to aggregated EmittedMetrics
|
||||
*/
|
||||
private static Map<String, EmittedMetrics> aggregateMetricsByScope(
|
||||
Map<String, List<EmittedMetrics.MetricsByScope>> metricsByWhen) {
|
||||
Map<String, EmittedMetrics> result = new HashMap<>();
|
||||
for (Map.Entry<String, List<EmittedMetrics.MetricsByScope>> entry : metricsByWhen.entrySet()) {
|
||||
String when = entry.getKey();
|
||||
List<EmittedMetrics.MetricsByScope> allScopes = entry.getValue();
|
||||
Map<String, Map<String, EmittedMetrics.Metric>> metricsByScopeName = new HashMap<>();
|
||||
|
||||
for (EmittedMetrics.MetricsByScope scopeEntry : allScopes) {
|
||||
String scope = scopeEntry.getScope();
|
||||
metricsByScopeName.putIfAbsent(scope, new HashMap<>());
|
||||
Map<String, EmittedMetrics.Metric> metricMap = metricsByScopeName.get(scope);
|
||||
|
||||
for (EmittedMetrics.Metric metric : scopeEntry.getMetrics()) {
|
||||
metricMap.put(metric.getName(), metric); // deduplicate by name
|
||||
}
|
||||
}
|
||||
|
||||
List<EmittedMetrics.MetricsByScope> mergedScopes = new ArrayList<>();
|
||||
for (Map.Entry<String, Map<String, EmittedMetrics.Metric>> scopeEntry :
|
||||
metricsByScopeName.entrySet()) {
|
||||
mergedScopes.add(
|
||||
new EmittedMetrics.MetricsByScope(
|
||||
scopeEntry.getKey(), new ArrayList<>(scopeEntry.getValue().values())));
|
||||
}
|
||||
result.put(when, new EmittedMetrics(when, mergedScopes));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes in a raw string representation of the aggregated EmittedMetrics yaml map, separated by
|
||||
* the {@code when}, indicating the conditions under which the metrics are emitted. Deduplicates
|
||||
* the metrics by name and then returns a new map of EmittedMetrics objects.
|
||||
*
|
||||
* @param input raw string representation of EmittedMetrics yaml
|
||||
* @return map where the key is the {@code when} condition and the value is the corresponding
|
||||
* EmittedMetrics
|
||||
* @throws JsonProcessingException if parsing fails
|
||||
*/
|
||||
// visible for testing
|
||||
public static Map<String, EmittedMetrics> parseMetrics(Map<String, StringBuilder> input)
|
||||
throws JsonProcessingException {
|
||||
Map<String, EmittedMetrics> metricsMap = new HashMap<>();
|
||||
for (Map.Entry<String, StringBuilder> entry : input.entrySet()) {
|
||||
String when = entry.getKey();
|
||||
StringBuilder content = entry.getValue();
|
||||
|
||||
EmittedMetrics metrics = YamlHelper.emittedMetricsParser(content.toString());
|
||||
if (metrics.getMetricsByScope() == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
List<EmittedMetrics.MetricsByScope> deduplicatedScopes = new ArrayList<>();
|
||||
for (EmittedMetrics.MetricsByScope scopeEntry : metrics.getMetricsByScope()) {
|
||||
String scope = scopeEntry.getScope();
|
||||
Map<String, EmittedMetrics.Metric> dedupedMetrics = new HashMap<>();
|
||||
for (EmittedMetrics.Metric metric : scopeEntry.getMetrics()) {
|
||||
dedupedMetrics.put(metric.getName(), metric);
|
||||
}
|
||||
deduplicatedScopes.add(
|
||||
new EmittedMetrics.MetricsByScope(scope, new ArrayList<>(dedupedMetrics.values())));
|
||||
}
|
||||
metricsMap.put(when, new EmittedMetrics(when, deduplicatedScopes));
|
||||
}
|
||||
return metricsMap;
|
||||
}
|
||||
|
||||
private EmittedMetricsParser() {}
|
||||
}
|
|
@ -0,0 +1,175 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.instrumentation.docs.parsers;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import io.opentelemetry.instrumentation.docs.internal.EmittedSpans;
|
||||
import io.opentelemetry.instrumentation.docs.internal.TelemetryAttribute;
|
||||
import io.opentelemetry.instrumentation.docs.utils.FileManager;
|
||||
import io.opentelemetry.instrumentation.docs.utils.YamlHelper;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* This class is responsible for parsing span-* files from the `.telemetry` directory of an
|
||||
* instrumentation module and converting them into the {@link EmittedSpans} format.
|
||||
*/
|
||||
public class EmittedSpanParser {
|
||||
private static final Logger logger = Logger.getLogger(EmittedSpanParser.class.getName());
|
||||
|
||||
/**
|
||||
* Looks for span files in the .telemetry directory, and combines them into a single map.
|
||||
*
|
||||
* @param instrumentationDirectory the directory to traverse
|
||||
* @return contents of aggregated files
|
||||
*/
|
||||
public static Map<String, EmittedSpans> getSpansByScopeFromFiles(
|
||||
String rootDir, String instrumentationDirectory) throws JsonProcessingException {
|
||||
Map<String, StringBuilder> spansByScope = new HashMap<>();
|
||||
Path telemetryDir = Paths.get(rootDir + "/" + instrumentationDirectory, ".telemetry");
|
||||
|
||||
if (Files.exists(telemetryDir) && Files.isDirectory(telemetryDir)) {
|
||||
try (Stream<Path> files = Files.list(telemetryDir)) {
|
||||
files
|
||||
.filter(path -> path.getFileName().toString().startsWith("spans-"))
|
||||
.forEach(
|
||||
path -> {
|
||||
String content = FileManager.readFileToString(path.toString());
|
||||
if (content != null) {
|
||||
String when = content.substring(0, content.indexOf('\n'));
|
||||
String whenKey = when.replace("when: ", "");
|
||||
|
||||
spansByScope.putIfAbsent(whenKey, new StringBuilder("spans_by_scope:\n"));
|
||||
|
||||
// Skip the spans_by_scope line so we can aggregate into one list
|
||||
int spanIndex = content.indexOf("spans_by_scope:\n");
|
||||
if (spanIndex != -1) {
|
||||
String contentAfter =
|
||||
content.substring(spanIndex + "spans_by_scope:\n".length());
|
||||
spansByScope.get(whenKey).append(contentAfter);
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (IOException e) {
|
||||
logger.severe("Error reading span files: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
return parseSpans(spansByScope);
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes in a raw string representation of the aggregated EmittedSpan yaml map, separated by the
|
||||
* `when`, indicating the conditions under which the telemetry is emitted. deduplicates by name
|
||||
* and then returns a new map.
|
||||
*
|
||||
* @param input raw string representation of EmittedSpans yaml
|
||||
* @return {@code Map<String, EmittedSpans>} where the key is the `when` condition
|
||||
*/
|
||||
private static Map<String, EmittedSpans> parseSpans(Map<String, StringBuilder> input)
|
||||
throws JsonProcessingException {
|
||||
Map<String, EmittedSpans> result = new HashMap<>();
|
||||
|
||||
for (Map.Entry<String, StringBuilder> entry : input.entrySet()) {
|
||||
String when = entry.getKey().strip();
|
||||
StringBuilder content = entry.getValue();
|
||||
|
||||
EmittedSpans spans = YamlHelper.emittedSpansParser(content.toString());
|
||||
if (spans.getSpansByScope().isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Map<String, Map<String, Set<TelemetryAttribute>>> attributesByScopeAndSpanKind =
|
||||
new HashMap<>();
|
||||
|
||||
for (EmittedSpans.SpansByScope spansByScopeEntry : spans.getSpansByScope()) {
|
||||
String scope = spansByScopeEntry.getScope();
|
||||
|
||||
attributesByScopeAndSpanKind.putIfAbsent(scope, new HashMap<>());
|
||||
Map<String, Set<TelemetryAttribute>> attributesBySpanKind =
|
||||
attributesByScopeAndSpanKind.get(scope);
|
||||
|
||||
for (EmittedSpans.Span span : spansByScopeEntry.getSpans()) {
|
||||
String spanKind = span.getSpanKind();
|
||||
|
||||
attributesBySpanKind.putIfAbsent(spanKind, new HashSet<>());
|
||||
Set<TelemetryAttribute> attributeSet = attributesBySpanKind.get(spanKind);
|
||||
|
||||
if (span.getAttributes() != null) {
|
||||
for (TelemetryAttribute attr : span.getAttributes()) {
|
||||
attributeSet.add(new TelemetryAttribute(attr.getName(), attr.getType()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EmittedSpans deduplicatedEmittedSpans = getEmittedSpans(attributesByScopeAndSpanKind, when);
|
||||
result.put(when, deduplicatedEmittedSpans);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes in a map of attributes by scope and span kind, and returns an {@link EmittedSpans} object
|
||||
* with deduplicated spans.
|
||||
*
|
||||
* @param attributesByScopeAndSpanKind the map of attributes by scope and span kind
|
||||
* @param when the condition under which the telemetry is emitted
|
||||
* @return an {@link EmittedSpans} object with deduplicated spans
|
||||
*/
|
||||
private static EmittedSpans getEmittedSpans(
|
||||
Map<String, Map<String, Set<TelemetryAttribute>>> attributesByScopeAndSpanKind, String when) {
|
||||
List<EmittedSpans.SpansByScope> deduplicatedSpansByScope = new ArrayList<>();
|
||||
|
||||
for (Map.Entry<String, Map<String, Set<TelemetryAttribute>>> scopeEntry :
|
||||
attributesByScopeAndSpanKind.entrySet()) {
|
||||
String scope = scopeEntry.getKey();
|
||||
Map<String, Set<TelemetryAttribute>> spanKindMap = scopeEntry.getValue();
|
||||
EmittedSpans.SpansByScope deduplicatedScope = getSpansByScope(scope, spanKindMap);
|
||||
deduplicatedSpansByScope.add(deduplicatedScope);
|
||||
}
|
||||
|
||||
return new EmittedSpans(when, deduplicatedSpansByScope);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a map of attributes by spanKind into an {@link EmittedSpans.SpansByScope} object.
|
||||
* Deduplicates spans by their kind and collects their attributes.
|
||||
*
|
||||
* @param scope the name of the scope
|
||||
* @param spanKindMap a map where the key is the span kind and the value is set of attributes
|
||||
* @return an {@link EmittedSpans.SpansByScope} object with deduplicated spans
|
||||
*/
|
||||
private static EmittedSpans.SpansByScope getSpansByScope(
|
||||
String scope, Map<String, Set<TelemetryAttribute>> spanKindMap) {
|
||||
|
||||
List<EmittedSpans.Span> deduplicatedSpans = new ArrayList<>();
|
||||
|
||||
for (Map.Entry<String, Set<TelemetryAttribute>> spanKindEntry : spanKindMap.entrySet()) {
|
||||
String spanKind = spanKindEntry.getKey();
|
||||
Set<TelemetryAttribute> attributes = spanKindEntry.getValue();
|
||||
|
||||
List<TelemetryAttribute> attributeList = new ArrayList<>(attributes);
|
||||
EmittedSpans.Span deduplicatedSpan = new EmittedSpans.Span(spanKind, attributeList);
|
||||
deduplicatedSpans.add(deduplicatedSpan);
|
||||
}
|
||||
|
||||
return new EmittedSpans.SpansByScope(scope, deduplicatedSpans);
|
||||
}
|
||||
|
||||
private EmittedSpanParser() {}
|
||||
}
|
|
@ -6,17 +6,21 @@
|
|||
package io.opentelemetry.instrumentation.docs.parsers;
|
||||
|
||||
import io.opentelemetry.instrumentation.docs.internal.DependencyInfo;
|
||||
import io.opentelemetry.instrumentation.docs.internal.InstrumentationModule;
|
||||
import io.opentelemetry.instrumentation.docs.internal.InstrumentationType;
|
||||
import io.opentelemetry.instrumentation.docs.utils.FileManager;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/** Handles parsing of Gradle build files to extract muzzle and dependency information. */
|
||||
public class GradleParser {
|
||||
|
||||
private static final Pattern variablePattern =
|
||||
|
@ -241,5 +245,58 @@ public class GradleParser {
|
|||
return null;
|
||||
}
|
||||
|
||||
public static Map<InstrumentationType, Set<String>> extractVersions(
|
||||
List<String> gradleFiles, InstrumentationModule module) {
|
||||
Map<InstrumentationType, Set<String>> versionsByType = new HashMap<>();
|
||||
gradleFiles.forEach(file -> processGradleFile(file, versionsByType, module));
|
||||
return versionsByType;
|
||||
}
|
||||
|
||||
private static void processGradleFile(
|
||||
String filePath,
|
||||
Map<InstrumentationType, Set<String>> versionsByType,
|
||||
InstrumentationModule module) {
|
||||
String fileContents = FileManager.readFileToString(filePath);
|
||||
if (fileContents == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Optional<InstrumentationType> type = determineInstrumentationType(filePath);
|
||||
if (type.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
DependencyInfo dependencyInfo = parseGradleFile(fileContents, type.get());
|
||||
if (dependencyInfo == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
addVersions(versionsByType, type.get(), dependencyInfo.versions());
|
||||
setMinJavaVersionIfPresent(module, dependencyInfo);
|
||||
}
|
||||
|
||||
private static Optional<InstrumentationType> determineInstrumentationType(String filePath) {
|
||||
if (filePath.contains("/javaagent/")) {
|
||||
return Optional.of(InstrumentationType.JAVAAGENT);
|
||||
} else if (filePath.contains("/library/")) {
|
||||
return Optional.of(InstrumentationType.LIBRARY);
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
private static void addVersions(
|
||||
Map<InstrumentationType, Set<String>> versionsByType,
|
||||
InstrumentationType type,
|
||||
Set<String> versions) {
|
||||
versionsByType.computeIfAbsent(type, k -> new HashSet<>()).addAll(versions);
|
||||
}
|
||||
|
||||
private static void setMinJavaVersionIfPresent(
|
||||
InstrumentationModule module, DependencyInfo dependencyInfo) {
|
||||
if (dependencyInfo.minJavaVersionSupported() != null) {
|
||||
module.setMinJavaVersion(dependencyInfo.minJavaVersionSupported());
|
||||
}
|
||||
}
|
||||
|
||||
private GradleParser() {}
|
||||
}
|
||||
|
|
|
@ -6,93 +6,174 @@
|
|||
package io.opentelemetry.instrumentation.docs.parsers;
|
||||
|
||||
import io.opentelemetry.instrumentation.docs.internal.EmittedMetrics;
|
||||
import io.opentelemetry.instrumentation.docs.internal.InstrumentationModule;
|
||||
import io.opentelemetry.instrumentation.docs.internal.TelemetryAttribute;
|
||||
import io.opentelemetry.instrumentation.docs.utils.FileManager;
|
||||
import io.opentelemetry.instrumentation.docs.utils.YamlHelper;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.stream.Stream;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* This class is responsible for parsing metric files from the `.telemetry` directory of an
|
||||
* instrumentation module and filtering them by scope.
|
||||
*/
|
||||
public class MetricParser {
|
||||
private static final Logger logger = Logger.getLogger(MetricParser.class.getName());
|
||||
|
||||
/**
|
||||
* Looks for metric files in the .telemetry directory, and combines them into a single list of
|
||||
* metrics.
|
||||
* Retrieves metrics for a given instrumentation module, filtered by scope.
|
||||
*
|
||||
* @param instrumentationDirectory the directory to traverse
|
||||
* @return contents of aggregated files
|
||||
* @param module the instrumentation module
|
||||
* @param fileManager the file manager to use for file operations
|
||||
* @return a map where the key is the 'when' condition and the value is a list of metrics
|
||||
*/
|
||||
public static Map<String, EmittedMetrics> getMetricsFromFiles(
|
||||
String rootDir, String instrumentationDirectory) {
|
||||
Map<String, StringBuilder> metricsByWhen = new HashMap<>();
|
||||
Path telemetryDir = Paths.get(rootDir + "/" + instrumentationDirectory, ".telemetry");
|
||||
public static Map<String, List<EmittedMetrics.Metric>> getMetrics(
|
||||
InstrumentationModule module, FileManager fileManager) {
|
||||
Map<String, EmittedMetrics> metrics =
|
||||
EmittedMetricsParser.getMetricsFromFiles(fileManager.rootDir(), module.getSrcPath());
|
||||
|
||||
if (Files.exists(telemetryDir) && Files.isDirectory(telemetryDir)) {
|
||||
try (Stream<Path> files = Files.list(telemetryDir)) {
|
||||
files
|
||||
.filter(path -> path.getFileName().toString().startsWith("metrics-"))
|
||||
.forEach(
|
||||
path -> {
|
||||
String content = FileManager.readFileToString(path.toString());
|
||||
if (content != null) {
|
||||
String when = content.substring(0, content.indexOf('\n'));
|
||||
String whenKey = when.replace("when: ", "");
|
||||
|
||||
metricsByWhen.putIfAbsent(whenKey, new StringBuilder("metrics:\n"));
|
||||
|
||||
// Skip the metric label ("metrics:") so we can aggregate into one list
|
||||
int metricsIndex = content.indexOf("metrics:\n");
|
||||
if (metricsIndex != -1) {
|
||||
String contentAfterMetrics =
|
||||
content.substring(metricsIndex + "metrics:\n".length());
|
||||
metricsByWhen.get(whenKey).append(contentAfterMetrics);
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (IOException e) {
|
||||
logger.severe("Error reading metrics files: " + e.getMessage());
|
||||
}
|
||||
if (metrics.isEmpty()) {
|
||||
return new HashMap<>();
|
||||
}
|
||||
|
||||
return parseMetrics(metricsByWhen);
|
||||
String scopeName = module.getScopeInfo().getName();
|
||||
return filterMetricsByScope(metrics, scopeName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes in a raw string representation of the aggregated EmittedMetrics yaml map, separated by
|
||||
* the `when`, indicating the conditions under which the metrics are emitted. deduplicates the
|
||||
* metrics by name and then returns a new map EmittedMetrics objects.
|
||||
* Filters metrics by scope and aggregates attributes for each metric kind.
|
||||
*
|
||||
* @param input raw string representation of EmittedMetrics yaml
|
||||
* @return {@code Map<String, EmittedMetrics>} where the key is the `when` condition
|
||||
* @param metricsByScope the map of metrics by scope
|
||||
* @param scopeName the name of the scope to filter metrics for
|
||||
* @return a map of filtered metrics by 'when'
|
||||
*/
|
||||
// visible for testing
|
||||
public static Map<String, EmittedMetrics> parseMetrics(Map<String, StringBuilder> input) {
|
||||
Map<String, EmittedMetrics> metricsMap = new HashMap<>();
|
||||
for (Map.Entry<String, StringBuilder> entry : input.entrySet()) {
|
||||
String when = entry.getKey();
|
||||
StringBuilder content = entry.getValue();
|
||||
private static Map<String, List<EmittedMetrics.Metric>> filterMetricsByScope(
|
||||
Map<String, EmittedMetrics> metricsByScope, String scopeName) {
|
||||
|
||||
EmittedMetrics metrics = YamlHelper.emittedMetricsParser(content.toString());
|
||||
if (metrics.getMetrics() == null) {
|
||||
Map<String, Map<String, MetricAggregator.AggregatedMetricInfo>> aggregatedMetrics =
|
||||
new HashMap<>();
|
||||
|
||||
for (Map.Entry<String, EmittedMetrics> entry : metricsByScope.entrySet()) {
|
||||
if (!hasValidMetrics(entry.getValue())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Map<String, EmittedMetrics.Metric> deduplicatedMetrics = new HashMap<>();
|
||||
for (EmittedMetrics.Metric metric : metrics.getMetrics()) {
|
||||
deduplicatedMetrics.put(metric.getName(), metric);
|
||||
}
|
||||
String when = entry.getValue().getWhen();
|
||||
Map<String, Map<String, MetricAggregator.AggregatedMetricInfo>> result =
|
||||
MetricAggregator.aggregateMetrics(when, entry.getValue(), scopeName);
|
||||
|
||||
List<EmittedMetrics.Metric> uniqueMetrics = new ArrayList<>(deduplicatedMetrics.values());
|
||||
metricsMap.put(when, new EmittedMetrics(when, uniqueMetrics));
|
||||
// Merge result into aggregatedMetrics
|
||||
for (Map.Entry<String, Map<String, MetricAggregator.AggregatedMetricInfo>> e :
|
||||
result.entrySet()) {
|
||||
String whenKey = e.getKey();
|
||||
Map<String, MetricAggregator.AggregatedMetricInfo> metricMap =
|
||||
aggregatedMetrics.computeIfAbsent(whenKey, k -> new HashMap<>());
|
||||
|
||||
for (Map.Entry<String, MetricAggregator.AggregatedMetricInfo> metricEntry :
|
||||
e.getValue().entrySet()) {
|
||||
String metricName = metricEntry.getKey();
|
||||
MetricAggregator.AggregatedMetricInfo newInfo = metricEntry.getValue();
|
||||
MetricAggregator.AggregatedMetricInfo existingInfo = metricMap.get(metricName);
|
||||
if (existingInfo == null) {
|
||||
metricMap.put(metricName, newInfo);
|
||||
} else {
|
||||
existingInfo.attributes.addAll(newInfo.attributes);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return metricsMap;
|
||||
|
||||
return MetricAggregator.buildFilteredMetrics(aggregatedMetrics);
|
||||
}
|
||||
|
||||
private static boolean hasValidMetrics(EmittedMetrics metrics) {
|
||||
return metrics != null && metrics.getMetricsByScope() != null;
|
||||
}
|
||||
|
||||
/** Helper class to aggregate metrics by scope and name. */
|
||||
static class MetricAggregator {
|
||||
/**
|
||||
* Aggregates metrics for a given 'when' condition, metrics object, and target scope name.
|
||||
*
|
||||
* @param when the 'when' condition
|
||||
* @param metrics the EmittedMetrics object
|
||||
* @param targetScopeName the scope name to filter by
|
||||
* @return a map of aggregated metrics by 'when' and metric name
|
||||
*/
|
||||
public static Map<String, Map<String, AggregatedMetricInfo>> aggregateMetrics(
|
||||
String when, EmittedMetrics metrics, String targetScopeName) {
|
||||
Map<String, Map<String, AggregatedMetricInfo>> aggregatedMetrics = new HashMap<>();
|
||||
Map<String, AggregatedMetricInfo> metricKindMap =
|
||||
aggregatedMetrics.computeIfAbsent(when, k -> new HashMap<>());
|
||||
|
||||
for (EmittedMetrics.MetricsByScope metricsByScope : metrics.getMetricsByScope()) {
|
||||
if (metricsByScope.getScope().equals(targetScopeName)) {
|
||||
for (EmittedMetrics.Metric metric : metricsByScope.getMetrics()) {
|
||||
AggregatedMetricInfo aggInfo =
|
||||
metricKindMap.computeIfAbsent(
|
||||
metric.getName(),
|
||||
k ->
|
||||
new AggregatedMetricInfo(
|
||||
metric.getName(),
|
||||
metric.getDescription(),
|
||||
metric.getType(),
|
||||
metric.getUnit()));
|
||||
if (metric.getAttributes() != null) {
|
||||
for (TelemetryAttribute attr : metric.getAttributes()) {
|
||||
aggInfo.attributes.add(new TelemetryAttribute(attr.getName(), attr.getType()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return aggregatedMetrics;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a filtered metrics map from aggregated metrics.
|
||||
*
|
||||
* @param aggregatedMetrics the aggregated metrics map
|
||||
* @return a map where the key is the 'when' condition and the value is a list of metrics
|
||||
*/
|
||||
public static Map<String, List<EmittedMetrics.Metric>> buildFilteredMetrics(
|
||||
Map<String, Map<String, AggregatedMetricInfo>> aggregatedMetrics) {
|
||||
Map<String, List<EmittedMetrics.Metric>> result = new HashMap<>();
|
||||
for (Map.Entry<String, Map<String, AggregatedMetricInfo>> entry :
|
||||
aggregatedMetrics.entrySet()) {
|
||||
String when = entry.getKey();
|
||||
List<EmittedMetrics.Metric> metrics = result.computeIfAbsent(when, k -> new ArrayList<>());
|
||||
for (AggregatedMetricInfo aggInfo : entry.getValue().values()) {
|
||||
metrics.add(
|
||||
new EmittedMetrics.Metric(
|
||||
aggInfo.name,
|
||||
aggInfo.description,
|
||||
aggInfo.type,
|
||||
aggInfo.unit,
|
||||
new ArrayList<>(aggInfo.attributes)));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Data class to hold aggregated metric information. */
|
||||
static class AggregatedMetricInfo {
|
||||
final String name;
|
||||
final String description;
|
||||
final String type;
|
||||
final String unit;
|
||||
final Set<TelemetryAttribute> attributes = new HashSet<>();
|
||||
|
||||
AggregatedMetricInfo(String name, String description, String type, String unit) {
|
||||
this.name = name;
|
||||
this.description = description;
|
||||
this.type = type;
|
||||
this.unit = unit;
|
||||
}
|
||||
}
|
||||
|
||||
private MetricAggregator() {}
|
||||
}
|
||||
|
||||
private MetricParser() {}
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.instrumentation.docs.parsers;
|
||||
|
||||
import io.opentelemetry.instrumentation.docs.internal.InstrumentationModule;
|
||||
import io.opentelemetry.instrumentation.docs.utils.InstrumentationPath;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/** Handles conversion of InstrumentationPath objects to InstrumentationModule objects. */
|
||||
public class ModuleParser {
|
||||
|
||||
/**
|
||||
* Converts a list of {@link InstrumentationPath} into a list of {@link InstrumentationModule}.
|
||||
*
|
||||
* @param rootPath the root path for sanitization
|
||||
* @param paths the list of {@link InstrumentationPath} objects to be converted
|
||||
* @return a list of {@link InstrumentationModule} objects
|
||||
*/
|
||||
public static List<InstrumentationModule> convertToModules(
|
||||
String rootPath, List<InstrumentationPath> paths) {
|
||||
Map<String, InstrumentationModule> moduleMap = new HashMap<>();
|
||||
|
||||
for (InstrumentationPath path : paths) {
|
||||
String moduleKey = createModuleKey(path);
|
||||
moduleMap.computeIfAbsent(moduleKey, k -> createModule(rootPath, path));
|
||||
}
|
||||
|
||||
return new ArrayList<>(moduleMap.values());
|
||||
}
|
||||
|
||||
private static String createModuleKey(InstrumentationPath path) {
|
||||
return String.join(":", path.group(), path.namespace(), path.instrumentationName());
|
||||
}
|
||||
|
||||
private static InstrumentationModule createModule(String rootPath, InstrumentationPath path) {
|
||||
return new InstrumentationModule.Builder()
|
||||
.srcPath(sanitizePathName(rootPath, path.srcPath()))
|
||||
.instrumentationName(path.instrumentationName())
|
||||
.namespace(path.namespace())
|
||||
.group(path.group())
|
||||
.build();
|
||||
}
|
||||
|
||||
// visible for testing
|
||||
public static String sanitizePathName(String rootPath, String path) {
|
||||
return path.replace(rootPath, "").replace("/javaagent", "").replace("/library", "");
|
||||
}
|
||||
|
||||
private ModuleParser() {}
|
||||
}
|
|
@ -0,0 +1,143 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.instrumentation.docs.parsers;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import io.opentelemetry.instrumentation.docs.internal.EmittedSpans;
|
||||
import io.opentelemetry.instrumentation.docs.internal.InstrumentationModule;
|
||||
import io.opentelemetry.instrumentation.docs.internal.TelemetryAttribute;
|
||||
import io.opentelemetry.instrumentation.docs.utils.FileManager;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* This class is responsible for parsing span files from the `.telemetry` directory of an
|
||||
* instrumentation module and filtering them by scope.
|
||||
*/
|
||||
public class SpanParser {
|
||||
|
||||
// We want to ignore test related attributes
|
||||
private static final List<String> EXCLUDED_ATTRIBUTES = List.of("x-test-", "test-baggage-");
|
||||
|
||||
/**
|
||||
* Pull spans from the `.telemetry` directory, filter them by scope, and set them in the module.
|
||||
*
|
||||
* @param module the instrumentation module to extract spans for
|
||||
* @param fileManager the file manager to access the filesystem
|
||||
* @throws JsonProcessingException if there is an error processing the JSON from the span files
|
||||
*/
|
||||
public static Map<String, List<EmittedSpans.Span>> getSpans(
|
||||
InstrumentationModule module, FileManager fileManager) throws JsonProcessingException {
|
||||
Map<String, EmittedSpans> spans =
|
||||
EmittedSpanParser.getSpansByScopeFromFiles(fileManager.rootDir(), module.getSrcPath());
|
||||
|
||||
if (spans.isEmpty()) {
|
||||
return new HashMap<>();
|
||||
}
|
||||
|
||||
String scopeName = module.getScopeInfo().getName();
|
||||
return filterSpansByScope(spans, scopeName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters spans by scope and aggregates attributes for each span kind.
|
||||
*
|
||||
* @param spansByScope the map of spans by scope
|
||||
* @param scopeName the name of the scope to filter spans for
|
||||
* @return a map of filtered spans by `when`
|
||||
*/
|
||||
private static Map<String, List<EmittedSpans.Span>> filterSpansByScope(
|
||||
Map<String, EmittedSpans> spansByScope, String scopeName) {
|
||||
|
||||
Map<String, Map<String, Set<TelemetryAttribute>>> aggregatedAttributes = new HashMap<>();
|
||||
|
||||
for (Map.Entry<String, EmittedSpans> entry : spansByScope.entrySet()) {
|
||||
if (!hasValidSpans(entry.getValue())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String when = entry.getValue().getWhen();
|
||||
Map<String, Map<String, Set<TelemetryAttribute>>> result =
|
||||
SpanAggregator.aggregateSpans(when, entry.getValue(), scopeName);
|
||||
aggregatedAttributes.putAll(result);
|
||||
}
|
||||
|
||||
return SpanAggregator.buildFilteredSpans(aggregatedAttributes);
|
||||
}
|
||||
|
||||
private static boolean hasValidSpans(EmittedSpans spans) {
|
||||
return spans != null && spans.getSpansByScope() != null;
|
||||
}
|
||||
|
||||
/** Helper class to aggregate span attributes by scope and kind. */
|
||||
static class SpanAggregator {
|
||||
|
||||
public static Map<String, Map<String, Set<TelemetryAttribute>>> aggregateSpans(
|
||||
String when, EmittedSpans spans, String targetScopeName) {
|
||||
Map<String, Map<String, Set<TelemetryAttribute>>> aggregatedAttributes = new HashMap<>();
|
||||
Map<String, Set<TelemetryAttribute>> spanKindMap =
|
||||
aggregatedAttributes.computeIfAbsent(when, k -> new HashMap<>());
|
||||
|
||||
for (EmittedSpans.SpansByScope spansByScope : spans.getSpansByScope()) {
|
||||
if (spansByScope.getScope().equals(targetScopeName)) {
|
||||
processSpansForScope(spansByScope, spanKindMap);
|
||||
}
|
||||
}
|
||||
return aggregatedAttributes;
|
||||
}
|
||||
|
||||
private static void processSpansForScope(
|
||||
EmittedSpans.SpansByScope spansByScope, Map<String, Set<TelemetryAttribute>> spanKindMap) {
|
||||
for (EmittedSpans.Span span : spansByScope.getSpans()) {
|
||||
Set<TelemetryAttribute> attributes =
|
||||
spanKindMap.computeIfAbsent(span.getSpanKind(), k -> new HashSet<>());
|
||||
|
||||
addSpanAttributes(span, attributes);
|
||||
}
|
||||
}
|
||||
|
||||
private static void addSpanAttributes(
|
||||
EmittedSpans.Span span, Set<TelemetryAttribute> attributes) {
|
||||
if (span.getAttributes() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (TelemetryAttribute attr : span.getAttributes()) {
|
||||
boolean excluded = EXCLUDED_ATTRIBUTES.stream().anyMatch(ex -> attr.getName().contains(ex));
|
||||
if (!excluded) {
|
||||
attributes.add(new TelemetryAttribute(attr.getName(), attr.getType()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static Map<String, List<EmittedSpans.Span>> buildFilteredSpans(
|
||||
Map<String, Map<String, Set<TelemetryAttribute>>> aggregatedAttributes) {
|
||||
Map<String, List<EmittedSpans.Span>> result = new HashMap<>();
|
||||
|
||||
for (Map.Entry<String, Map<String, Set<TelemetryAttribute>>> entry :
|
||||
aggregatedAttributes.entrySet()) {
|
||||
String when = entry.getKey();
|
||||
List<EmittedSpans.Span> spans = result.computeIfAbsent(when, k -> new ArrayList<>());
|
||||
|
||||
for (Map.Entry<String, Set<TelemetryAttribute>> kindEntry : entry.getValue().entrySet()) {
|
||||
String spanKind = kindEntry.getKey();
|
||||
Set<TelemetryAttribute> attributes = kindEntry.getValue();
|
||||
spans.add(new EmittedSpans.Span(spanKind, new ArrayList<>(attributes)));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private SpanAggregator() {}
|
||||
}
|
||||
|
||||
private SpanParser() {}
|
||||
}
|
|
@ -11,9 +11,11 @@ import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
|
|||
import io.opentelemetry.instrumentation.docs.internal.ConfigurationOption;
|
||||
import io.opentelemetry.instrumentation.docs.internal.ConfigurationType;
|
||||
import io.opentelemetry.instrumentation.docs.internal.EmittedMetrics;
|
||||
import io.opentelemetry.instrumentation.docs.internal.EmittedSpans;
|
||||
import io.opentelemetry.instrumentation.docs.internal.InstrumentationClassification;
|
||||
import io.opentelemetry.instrumentation.docs.internal.InstrumentationMetaData;
|
||||
import io.opentelemetry.instrumentation.docs.internal.InstrumentationModule;
|
||||
import io.opentelemetry.instrumentation.docs.internal.TelemetryAttribute;
|
||||
import java.io.BufferedWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
|
@ -75,7 +77,16 @@ public class YamlHelper {
|
|||
.equals(InstrumentationClassification.LIBRARY))
|
||||
.collect(
|
||||
Collectors.groupingBy(
|
||||
InstrumentationModule::getGroup, TreeMap::new, Collectors.toList()));
|
||||
InstrumentationModule::getGroup,
|
||||
TreeMap::new,
|
||||
Collectors.collectingAndThen(
|
||||
Collectors.toList(),
|
||||
modules ->
|
||||
modules.stream()
|
||||
.sorted(
|
||||
Comparator.comparing(
|
||||
InstrumentationModule::getInstrumentationName))
|
||||
.collect(Collectors.toList()))));
|
||||
|
||||
Map<String, Object> output = new TreeMap<>();
|
||||
libraryInstrumentations.forEach(
|
||||
|
@ -131,8 +142,9 @@ public class YamlHelper {
|
|||
addTargetVersions(module, moduleMap);
|
||||
addConfigurations(module, moduleMap);
|
||||
|
||||
// Get telemetry grouping list
|
||||
Set<String> telemetryGroups = module.getMetrics().keySet();
|
||||
// Get telemetry grouping lists
|
||||
Set<String> telemetryGroups = new java.util.HashSet<>(module.getMetrics().keySet());
|
||||
telemetryGroups.addAll(module.getSpans().keySet());
|
||||
|
||||
if (!telemetryGroups.isEmpty()) {
|
||||
List<Map<String, Object>> telemetryList = new ArrayList<>();
|
||||
|
@ -143,16 +155,38 @@ public class YamlHelper {
|
|||
new ArrayList<>(module.getMetrics().getOrDefault(group, Collections.emptyList()));
|
||||
List<Map<String, Object>> metricsList = new ArrayList<>();
|
||||
|
||||
// sort metrics by name for some determinism in the order
|
||||
// sort by name for determinism in the order
|
||||
metrics.sort(Comparator.comparing(EmittedMetrics.Metric::getName));
|
||||
|
||||
for (EmittedMetrics.Metric metric : metrics) {
|
||||
metricsList.add(getMetricsMap(metric));
|
||||
}
|
||||
telemetryEntry.put("metrics", metricsList);
|
||||
telemetryList.add(telemetryEntry);
|
||||
if (!metricsList.isEmpty()) {
|
||||
telemetryEntry.put("metrics", metricsList);
|
||||
}
|
||||
|
||||
List<EmittedSpans.Span> spans =
|
||||
new ArrayList<>(module.getSpans().getOrDefault(group, Collections.emptyList()));
|
||||
List<Map<String, Object>> spanList = new ArrayList<>();
|
||||
|
||||
// sort by name for determinism in the order
|
||||
spans.sort(Comparator.comparing(EmittedSpans.Span::getSpanKind));
|
||||
|
||||
for (EmittedSpans.Span span : spans) {
|
||||
spanList.add(getSpanMap(span));
|
||||
}
|
||||
if (!spanList.isEmpty()) {
|
||||
telemetryEntry.put("spans", spanList);
|
||||
}
|
||||
|
||||
if (!spanList.isEmpty() || !metricsList.isEmpty()) {
|
||||
telemetryList.add(telemetryEntry);
|
||||
}
|
||||
}
|
||||
|
||||
if (!telemetryList.isEmpty()) {
|
||||
moduleMap.put("telemetry", telemetryList);
|
||||
}
|
||||
moduleMap.put("telemetry", telemetryList);
|
||||
}
|
||||
return moduleMap;
|
||||
}
|
||||
|
@ -221,21 +255,34 @@ public class YamlHelper {
|
|||
return conf;
|
||||
}
|
||||
|
||||
private static List<Map<String, Object>> getSortedAttributeMaps(
|
||||
List<TelemetryAttribute> attributes) {
|
||||
List<TelemetryAttribute> sortedAttributes = new ArrayList<>(attributes);
|
||||
sortedAttributes.sort(Comparator.comparing(TelemetryAttribute::getName));
|
||||
List<Map<String, Object>> attributeMaps = new ArrayList<>();
|
||||
for (TelemetryAttribute attribute : sortedAttributes) {
|
||||
Map<String, Object> attributeMap = new LinkedHashMap<>();
|
||||
attributeMap.put("name", attribute.getName());
|
||||
attributeMap.put("type", attribute.getType());
|
||||
attributeMaps.add(attributeMap);
|
||||
}
|
||||
return attributeMaps;
|
||||
}
|
||||
|
||||
private static Map<String, Object> getMetricsMap(EmittedMetrics.Metric metric) {
|
||||
Map<String, Object> innerMetricMap = new LinkedHashMap<>();
|
||||
innerMetricMap.put("name", metric.getName());
|
||||
innerMetricMap.put("description", metric.getDescription());
|
||||
innerMetricMap.put("type", metric.getType());
|
||||
innerMetricMap.put("unit", metric.getUnit());
|
||||
innerMetricMap.put("attributes", getSortedAttributeMaps(metric.getAttributes()));
|
||||
return innerMetricMap;
|
||||
}
|
||||
|
||||
List<Map<String, Object>> attributes = new ArrayList<>();
|
||||
for (EmittedMetrics.Attribute attribute : metric.getAttributes()) {
|
||||
Map<String, Object> attributeMap = new LinkedHashMap<>();
|
||||
attributeMap.put("name", attribute.getName());
|
||||
attributeMap.put("type", attribute.getType());
|
||||
attributes.add(attributeMap);
|
||||
}
|
||||
innerMetricMap.put("attributes", attributes);
|
||||
private static Map<String, Object> getSpanMap(EmittedSpans.Span span) {
|
||||
Map<String, Object> innerMetricMap = new LinkedHashMap<>();
|
||||
innerMetricMap.put("span_kind", span.getSpanKind());
|
||||
innerMetricMap.put("attributes", getSortedAttributeMaps(span.getAttributes()));
|
||||
return innerMetricMap;
|
||||
}
|
||||
|
||||
|
@ -244,8 +291,12 @@ public class YamlHelper {
|
|||
return mapper.readValue(input, InstrumentationMetaData.class);
|
||||
}
|
||||
|
||||
public static EmittedMetrics emittedMetricsParser(String input) {
|
||||
return new Yaml().loadAs(input, EmittedMetrics.class);
|
||||
public static EmittedMetrics emittedMetricsParser(String input) throws JsonProcessingException {
|
||||
return mapper.readValue(input, EmittedMetrics.class);
|
||||
}
|
||||
|
||||
public static EmittedSpans emittedSpansParser(String input) throws JsonProcessingException {
|
||||
return mapper.readValue(input, EmittedSpans.class);
|
||||
}
|
||||
|
||||
private YamlHelper() {}
|
||||
|
|
|
@ -9,6 +9,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
|||
|
||||
import io.opentelemetry.instrumentation.docs.internal.InstrumentationModule;
|
||||
import io.opentelemetry.instrumentation.docs.internal.InstrumentationType;
|
||||
import io.opentelemetry.instrumentation.docs.parsers.ModuleParser;
|
||||
import io.opentelemetry.instrumentation.docs.utils.InstrumentationPath;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
@ -40,8 +41,7 @@ class InstrumentationAnalyzerTest {
|
|||
"spring",
|
||||
InstrumentationType.LIBRARY));
|
||||
|
||||
List<InstrumentationModule> modules =
|
||||
InstrumentationAnalyzer.convertToInstrumentationModules("test", paths);
|
||||
List<InstrumentationModule> modules = ModuleParser.convertToModules("test", paths);
|
||||
|
||||
assertThat(modules.size()).isEqualTo(2);
|
||||
|
||||
|
@ -70,4 +70,38 @@ class InstrumentationAnalyzerTest {
|
|||
assertThat(springModule.getSrcPath()).isEqualTo("instrumentation/spring/spring-web");
|
||||
assertThat(springModule.getScopeInfo().getName()).isEqualTo("io.opentelemetry.spring-web");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testModuleConverterCreatesUniqueModules() {
|
||||
List<InstrumentationPath> paths =
|
||||
Arrays.asList(
|
||||
new InstrumentationPath(
|
||||
"same-name",
|
||||
"instrumentation/test1/same-name/library",
|
||||
"namespace1",
|
||||
"group1",
|
||||
InstrumentationType.LIBRARY),
|
||||
new InstrumentationPath(
|
||||
"same-name",
|
||||
"instrumentation/test2/same-name/library",
|
||||
"namespace2",
|
||||
"group2",
|
||||
InstrumentationType.LIBRARY));
|
||||
|
||||
List<InstrumentationModule> modules = ModuleParser.convertToModules("test", paths);
|
||||
|
||||
// Should create 2 separate modules because they have different group/namespace combinations
|
||||
assertThat(modules.size()).isEqualTo(2);
|
||||
|
||||
assertThat(
|
||||
modules.stream()
|
||||
.anyMatch(
|
||||
m -> m.getGroup().equals("group1") && m.getNamespace().equals("namespace1")))
|
||||
.isTrue();
|
||||
assertThat(
|
||||
modules.stream()
|
||||
.anyMatch(
|
||||
m -> m.getGroup().equals("group2") && m.getNamespace().equals("namespace2")))
|
||||
.isTrue();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.instrumentation.docs;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import io.opentelemetry.instrumentation.docs.internal.InstrumentationModule;
|
||||
import io.opentelemetry.instrumentation.docs.parsers.ModuleParser;
|
||||
import io.opentelemetry.instrumentation.docs.utils.InstrumentationPath;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class ModuleConverterTest {
|
||||
|
||||
@Test
|
||||
void testConvertToModulesCreatesUniqueModules() {
|
||||
InstrumentationPath path1 = mock(InstrumentationPath.class);
|
||||
when(path1.group()).thenReturn("g1");
|
||||
when(path1.namespace()).thenReturn("n1");
|
||||
when(path1.instrumentationName()).thenReturn("i1");
|
||||
when(path1.srcPath()).thenReturn("/root/javaagent/foo");
|
||||
|
||||
InstrumentationPath path2 = mock(InstrumentationPath.class);
|
||||
when(path2.group()).thenReturn("g1");
|
||||
when(path2.namespace()).thenReturn("n1");
|
||||
when(path2.instrumentationName()).thenReturn("i1");
|
||||
when(path2.srcPath()).thenReturn("/root/library/bar");
|
||||
|
||||
InstrumentationPath path3 = mock(InstrumentationPath.class);
|
||||
when(path3.group()).thenReturn("g2");
|
||||
when(path3.namespace()).thenReturn("n2");
|
||||
when(path3.instrumentationName()).thenReturn("i2");
|
||||
when(path3.srcPath()).thenReturn("/root/javaagent/baz");
|
||||
|
||||
List<InstrumentationModule> modules =
|
||||
ModuleParser.convertToModules("/root", Arrays.asList(path1, path2, path3));
|
||||
|
||||
assertThat(modules.size()).isEqualTo(2);
|
||||
assertThat(modules)
|
||||
.extracting(InstrumentationModule::getGroup)
|
||||
.containsExactlyInAnyOrder("g1", "g2");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSanitizePathNameRemovesRootAndKnownFolders() throws Exception {
|
||||
String sanitized = ModuleParser.sanitizePathName("/root", "/root/javaagent/foo/bar");
|
||||
assertThat(sanitized).isEqualTo("/foo/bar");
|
||||
|
||||
sanitized = ModuleParser.sanitizePathName("/root", "/root/library/baz");
|
||||
assertThat(sanitized).isEqualTo("/baz");
|
||||
|
||||
sanitized = ModuleParser.sanitizePathName("/root", "/root/other");
|
||||
assertThat(sanitized).isEqualTo("/other");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,128 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.instrumentation.docs.parsers;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.mockStatic;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import io.opentelemetry.instrumentation.docs.internal.EmittedMetrics;
|
||||
import io.opentelemetry.instrumentation.docs.utils.FileManager;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
import org.mockito.MockedStatic;
|
||||
|
||||
@SuppressWarnings("NullAway")
|
||||
class EmittedMetricsParserTest {
|
||||
|
||||
@Test
|
||||
void parseMetricsDeduplicatesMetricsByName() throws JsonProcessingException {
|
||||
String input =
|
||||
"""
|
||||
metrics_by_scope:
|
||||
- scope: io.opentelemetry.alibaba-druid-1.0
|
||||
metrics:
|
||||
- name: metric1
|
||||
type: counter
|
||||
- name: metric1
|
||||
type: counter
|
||||
- name: metric2
|
||||
type: gauge
|
||||
""";
|
||||
|
||||
Map<String, StringBuilder> metricMap = new HashMap<>();
|
||||
metricMap.put("default", new StringBuilder(input));
|
||||
|
||||
Map<String, EmittedMetrics> result = EmittedMetricsParser.parseMetrics(metricMap);
|
||||
List<String> metricNames =
|
||||
result.get("default").getMetricsByScope().get(0).getMetrics().stream()
|
||||
.map(EmittedMetrics.Metric::getName)
|
||||
.sorted()
|
||||
.toList();
|
||||
|
||||
assertThat(metricNames).hasSize(2);
|
||||
assertThat(metricNames).containsExactly("metric1", "metric2");
|
||||
}
|
||||
|
||||
@Test
|
||||
void parseMetricsHandlesEmptyInput() throws JsonProcessingException {
|
||||
String input = "metrics_by_scope:\n";
|
||||
Map<String, StringBuilder> metricMap = new HashMap<>();
|
||||
metricMap.put("default", new StringBuilder(input));
|
||||
|
||||
Map<String, EmittedMetrics> result = EmittedMetricsParser.parseMetrics(metricMap);
|
||||
assertThat(result).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void getMetricsFromFilesCombinesFilesCorrectly(@TempDir Path tempDir) throws IOException {
|
||||
Path telemetryDir = Files.createDirectories(tempDir.resolve(".telemetry"));
|
||||
|
||||
String file1Content =
|
||||
"""
|
||||
when: default
|
||||
metrics_by_scope:
|
||||
- scope: io.opentelemetry.MetricParserTest
|
||||
metrics:
|
||||
- name: metric1
|
||||
type: counter
|
||||
""";
|
||||
|
||||
String file2Content =
|
||||
"""
|
||||
when: default
|
||||
metrics_by_scope:
|
||||
- scope: io.opentelemetry.MetricParserTest
|
||||
metrics:
|
||||
- name: metric2
|
||||
type: gauge
|
||||
""";
|
||||
|
||||
Files.writeString(telemetryDir.resolve("metrics-1.yaml"), file1Content);
|
||||
Files.writeString(telemetryDir.resolve("metrics-2.yaml"), file2Content);
|
||||
|
||||
// Create a non-metrics file that should be ignored
|
||||
Files.writeString(telemetryDir.resolve("other-file.yaml"), "some content");
|
||||
|
||||
try (MockedStatic<FileManager> fileManagerMock = mockStatic(FileManager.class)) {
|
||||
fileManagerMock
|
||||
.when(
|
||||
() -> FileManager.readFileToString(telemetryDir.resolve("metrics-1.yaml").toString()))
|
||||
.thenReturn(file1Content);
|
||||
fileManagerMock
|
||||
.when(
|
||||
() -> FileManager.readFileToString(telemetryDir.resolve("metrics-2.yaml").toString()))
|
||||
.thenReturn(file2Content);
|
||||
|
||||
Map<String, EmittedMetrics> result =
|
||||
EmittedMetricsParser.getMetricsFromFiles(tempDir.toString(), "");
|
||||
|
||||
EmittedMetrics.MetricsByScope metrics =
|
||||
result.get("default").getMetricsByScope().stream()
|
||||
.filter(scope -> scope.getScope().equals("io.opentelemetry.MetricParserTest"))
|
||||
.findFirst()
|
||||
.orElseThrow();
|
||||
|
||||
assertThat(metrics.getMetrics()).hasSize(2);
|
||||
List<String> metricNames =
|
||||
metrics.getMetrics().stream().map(EmittedMetrics.Metric::getName).sorted().toList();
|
||||
assertThat(metricNames).containsExactly("metric1", "metric2");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void getMetricsFromFilesHandlesNonexistentDirectory() throws JsonProcessingException {
|
||||
Map<String, EmittedMetrics> result =
|
||||
EmittedMetricsParser.getMetricsFromFiles("/nonexistent", "path");
|
||||
assertThat(result).isEmpty();
|
||||
}
|
||||
}
|
|
@ -6,97 +6,110 @@
|
|||
package io.opentelemetry.instrumentation.docs.parsers;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.mockStatic;
|
||||
|
||||
import io.opentelemetry.instrumentation.docs.internal.EmittedMetrics;
|
||||
import io.opentelemetry.instrumentation.docs.utils.FileManager;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.HashMap;
|
||||
import io.opentelemetry.instrumentation.docs.internal.TelemetryAttribute;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
import org.mockito.MockedStatic;
|
||||
|
||||
@SuppressWarnings("NullAway")
|
||||
class MetricParserTest {
|
||||
|
||||
@Test
|
||||
void parseMetricsDeduplicatesMetricsByName() {
|
||||
String input =
|
||||
"""
|
||||
metrics:
|
||||
- name: metric1
|
||||
type: counter
|
||||
- name: metric1
|
||||
type: counter
|
||||
- name: metric2
|
||||
type: gauge
|
||||
""";
|
||||
void testFiltersMetricsByScope() {
|
||||
String targetScopeName = "my-instrumentation-scope";
|
||||
|
||||
Map<String, StringBuilder> metricMap = new HashMap<>();
|
||||
metricMap.put("default", new StringBuilder(input));
|
||||
EmittedMetrics.Metric metric1 = createMetric("my.metric1", "desc1", "attr1");
|
||||
EmittedMetrics.Metric otherMetric = createMetric("other.metric", "desc2", "other.attr");
|
||||
|
||||
Map<String, EmittedMetrics> result = MetricParser.parseMetrics(metricMap);
|
||||
List<String> metricNames =
|
||||
result.get("default").getMetrics().stream()
|
||||
.map(EmittedMetrics.Metric::getName)
|
||||
.sorted()
|
||||
.toList();
|
||||
EmittedMetrics.MetricsByScope targetMetricsByScope =
|
||||
new EmittedMetrics.MetricsByScope(targetScopeName, List.of(metric1));
|
||||
|
||||
assertThat(metricNames).hasSize(2);
|
||||
assertThat(metricNames).containsExactly("metric1", "metric2");
|
||||
EmittedMetrics.MetricsByScope otherMetricsByScope =
|
||||
new EmittedMetrics.MetricsByScope("other-scope", List.of(otherMetric));
|
||||
|
||||
EmittedMetrics emittedMetrics =
|
||||
new EmittedMetrics("default", List.of(targetMetricsByScope, otherMetricsByScope));
|
||||
|
||||
Map<String, Map<String, MetricParser.MetricAggregator.AggregatedMetricInfo>> metrics =
|
||||
MetricParser.MetricAggregator.aggregateMetrics("default", emittedMetrics, targetScopeName);
|
||||
|
||||
Map<String, List<EmittedMetrics.Metric>> result =
|
||||
MetricParser.MetricAggregator.buildFilteredMetrics(metrics);
|
||||
|
||||
assertThat(result.get("default")).hasSize(1);
|
||||
assertThat(result.get("default").get(0).getName()).isEqualTo("my.metric1");
|
||||
}
|
||||
|
||||
@Test
|
||||
void parseMetricsHandlesEmptyInput() {
|
||||
String input = "metrics:\n";
|
||||
Map<String, StringBuilder> metricMap = new HashMap<>();
|
||||
metricMap.put("default", new StringBuilder(input));
|
||||
void testAggregatesAndDeduplicatesAttributes() {
|
||||
String targetScopeName = "my-instrumentation-scope";
|
||||
|
||||
Map<String, EmittedMetrics> result = MetricParser.parseMetrics(metricMap);
|
||||
assertThat(result).isEmpty();
|
||||
EmittedMetrics.Metric metric1 =
|
||||
new EmittedMetrics.Metric(
|
||||
"my.metric1",
|
||||
"desc1",
|
||||
"gauge",
|
||||
"unit",
|
||||
List.of(
|
||||
new TelemetryAttribute("attr1", "STRING"),
|
||||
new TelemetryAttribute("attr2", "STRING")));
|
||||
|
||||
EmittedMetrics.Metric metric1Dup =
|
||||
new EmittedMetrics.Metric(
|
||||
"my.metric1",
|
||||
"desc1",
|
||||
"gauge",
|
||||
"unit",
|
||||
List.of(
|
||||
new TelemetryAttribute("attr1", "STRING"),
|
||||
new TelemetryAttribute("attr3", "STRING")));
|
||||
|
||||
EmittedMetrics.MetricsByScope targetMetricsByScope =
|
||||
new EmittedMetrics.MetricsByScope(targetScopeName, List.of(metric1, metric1Dup));
|
||||
|
||||
EmittedMetrics emittedMetrics = new EmittedMetrics("default", List.of(targetMetricsByScope));
|
||||
|
||||
Map<String, Map<String, MetricParser.MetricAggregator.AggregatedMetricInfo>> metrics =
|
||||
MetricParser.MetricAggregator.aggregateMetrics("default", emittedMetrics, targetScopeName);
|
||||
|
||||
Map<String, List<EmittedMetrics.Metric>> result =
|
||||
MetricParser.MetricAggregator.buildFilteredMetrics(metrics);
|
||||
List<TelemetryAttribute> attrs = result.get("default").get(0).getAttributes();
|
||||
|
||||
assertThat(attrs).hasSize(3);
|
||||
assertThat(attrs.stream().map(TelemetryAttribute::getName))
|
||||
.containsExactlyInAnyOrder("attr1", "attr2", "attr3");
|
||||
}
|
||||
|
||||
@Test
|
||||
void getMetricsFromFilesCombinesFilesCorrectly(@TempDir Path tempDir) throws IOException {
|
||||
Path telemetryDir = Files.createDirectories(tempDir.resolve(".telemetry"));
|
||||
void testPreservesMetricMetadata() {
|
||||
String targetScopeName = "my-instrumentation-scope";
|
||||
|
||||
String file1Content = "when: default\n metrics:\n - name: metric1\n type: counter\n";
|
||||
String file2Content = "when: default\n metrics:\n - name: metric2\n type: gauge\n";
|
||||
EmittedMetrics.Metric metric1 =
|
||||
createMetric("my.metric1", "description of my.metric1", "attr1");
|
||||
|
||||
Files.writeString(telemetryDir.resolve("metrics-1.yaml"), file1Content);
|
||||
Files.writeString(telemetryDir.resolve("metrics-2.yaml"), file2Content);
|
||||
EmittedMetrics.MetricsByScope targetMetricsByScope =
|
||||
new EmittedMetrics.MetricsByScope(targetScopeName, List.of(metric1));
|
||||
|
||||
// Create a non-metrics file that should be ignored
|
||||
Files.writeString(telemetryDir.resolve("other-file.yaml"), "some content");
|
||||
EmittedMetrics emittedMetrics = new EmittedMetrics("default", List.of(targetMetricsByScope));
|
||||
|
||||
try (MockedStatic<FileManager> fileManagerMock = mockStatic(FileManager.class)) {
|
||||
fileManagerMock
|
||||
.when(
|
||||
() -> FileManager.readFileToString(telemetryDir.resolve("metrics-1.yaml").toString()))
|
||||
.thenReturn(file1Content);
|
||||
fileManagerMock
|
||||
.when(
|
||||
() -> FileManager.readFileToString(telemetryDir.resolve("metrics-2.yaml").toString()))
|
||||
.thenReturn(file2Content);
|
||||
Map<String, Map<String, MetricParser.MetricAggregator.AggregatedMetricInfo>> metrics =
|
||||
MetricParser.MetricAggregator.aggregateMetrics("default", emittedMetrics, targetScopeName);
|
||||
|
||||
Map<String, EmittedMetrics> result = MetricParser.getMetricsFromFiles(tempDir.toString(), "");
|
||||
Map<String, List<EmittedMetrics.Metric>> result =
|
||||
MetricParser.MetricAggregator.buildFilteredMetrics(metrics);
|
||||
|
||||
EmittedMetrics metrics = result.get("default");
|
||||
|
||||
assertThat(metrics.getMetrics()).hasSize(2);
|
||||
List<String> metricNames =
|
||||
metrics.getMetrics().stream().map(EmittedMetrics.Metric::getName).sorted().toList();
|
||||
assertThat(metricNames).containsExactly("metric1", "metric2");
|
||||
}
|
||||
EmittedMetrics.Metric foundMetric = result.get("default").get(0);
|
||||
assertThat(foundMetric.getName()).isEqualTo("my.metric1");
|
||||
assertThat(foundMetric.getDescription()).isEqualTo("description of my.metric1");
|
||||
assertThat(foundMetric.getType()).isEqualTo("gauge");
|
||||
assertThat(foundMetric.getUnit()).isEqualTo("unit");
|
||||
}
|
||||
|
||||
@Test
|
||||
void getMetricsFromFilesHandlesNonexistentDirectory() {
|
||||
Map<String, EmittedMetrics> result = MetricParser.getMetricsFromFiles("/nonexistent", "path");
|
||||
assertThat(result).isEmpty();
|
||||
private static EmittedMetrics.Metric createMetric(
|
||||
String name, String description, String attrName) {
|
||||
return new EmittedMetrics.Metric(
|
||||
name, description, "gauge", "unit", List.of(new TelemetryAttribute(attrName, "STRING")));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,198 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.instrumentation.docs.parsers;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.mockStatic;
|
||||
|
||||
import io.opentelemetry.instrumentation.docs.internal.EmittedSpans;
|
||||
import io.opentelemetry.instrumentation.docs.internal.TelemetryAttribute;
|
||||
import io.opentelemetry.instrumentation.docs.utils.FileManager;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
import org.mockito.MockedStatic;
|
||||
|
||||
@SuppressWarnings("NullAway")
|
||||
class SpanParserTest {
|
||||
|
||||
@Test
|
||||
void getSpansFromFilesCombinesFilesCorrectly(@TempDir Path tempDir) throws IOException {
|
||||
Path telemetryDir = Files.createDirectories(tempDir.resolve(".telemetry"));
|
||||
|
||||
String file1Content =
|
||||
"""
|
||||
when: default
|
||||
spans_by_scope:
|
||||
- scope: test
|
||||
spans:
|
||||
- span_kind: INTERNAL
|
||||
attributes:
|
||||
- scope: io.opentelemetry.clickhouse-client-0.5
|
||||
spans:
|
||||
- span_kind: SERVER
|
||||
attributes:
|
||||
- name: server.address
|
||||
type: STRING
|
||||
""";
|
||||
|
||||
String file2Content =
|
||||
"""
|
||||
when: default
|
||||
spans_by_scope:
|
||||
- scope: test
|
||||
spans:
|
||||
- span_kind: INTERNAL
|
||||
attributes:
|
||||
- scope: io.opentelemetry.clickhouse-client-0.5
|
||||
spans:
|
||||
- span_kind: CLIENT
|
||||
attributes:
|
||||
- name: db.statement
|
||||
type: STRING
|
||||
- name: server.port
|
||||
type: LONG
|
||||
- name: db.system
|
||||
type: STRING
|
||||
- name: server.address
|
||||
type: STRING
|
||||
- name: db.name
|
||||
type: STRING
|
||||
- name: db.operation
|
||||
type: STRING
|
||||
""";
|
||||
|
||||
Files.writeString(telemetryDir.resolve("spans-1.yaml"), file1Content);
|
||||
Files.writeString(telemetryDir.resolve("spans-2.yaml"), file2Content);
|
||||
// duplicate span contents to test deduplication
|
||||
Files.writeString(telemetryDir.resolve("spans-3.yaml"), file2Content);
|
||||
|
||||
try (MockedStatic<FileManager> fileManagerMock = mockStatic(FileManager.class)) {
|
||||
fileManagerMock
|
||||
.when(() -> FileManager.readFileToString(telemetryDir.resolve("spans-1.yaml").toString()))
|
||||
.thenReturn(file1Content);
|
||||
fileManagerMock
|
||||
.when(() -> FileManager.readFileToString(telemetryDir.resolve("spans-2.yaml").toString()))
|
||||
.thenReturn(file2Content);
|
||||
fileManagerMock
|
||||
.when(() -> FileManager.readFileToString(telemetryDir.resolve("spans-3.yaml").toString()))
|
||||
.thenReturn(file2Content);
|
||||
|
||||
Map<String, EmittedSpans> result =
|
||||
EmittedSpanParser.getSpansByScopeFromFiles(tempDir.toString(), "");
|
||||
|
||||
EmittedSpans spans = result.get("default");
|
||||
assertThat(spans.getSpansByScope()).hasSize(2);
|
||||
|
||||
List<EmittedSpans.Span> clickHouseSpans =
|
||||
spans.getSpansByScope().stream()
|
||||
.filter(item -> item.getScope().equals("io.opentelemetry.clickhouse-client-0.5"))
|
||||
.map(EmittedSpans.SpansByScope::getSpans)
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
assertThat(clickHouseSpans).hasSize(2);
|
||||
|
||||
EmittedSpans.Span serverSpan =
|
||||
clickHouseSpans.stream()
|
||||
.filter(item -> item.getSpanKind().equals("SERVER"))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
EmittedSpans.Span clientSpan =
|
||||
clickHouseSpans.stream()
|
||||
.filter(item -> item.getSpanKind().equals("CLIENT"))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
|
||||
assertThat(serverSpan.getAttributes()).hasSize(1);
|
||||
assertThat(clientSpan.getAttributes()).hasSize(6);
|
||||
|
||||
List<EmittedSpans.Span> testSpans =
|
||||
spans.getSpansByScope().stream()
|
||||
.filter(item -> item.getScope().equals("test"))
|
||||
.map(EmittedSpans.SpansByScope::getSpans)
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
// deduped should have only one span
|
||||
assertThat(testSpans).hasSize(1);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSpanAggregatorFiltersAndAggregatesCorrectly() {
|
||||
String targetScopeName = "my-instrumentation-scope";
|
||||
|
||||
EmittedSpans.Span span1 =
|
||||
new EmittedSpans.Span(
|
||||
"CLIENT",
|
||||
List.of(
|
||||
new TelemetryAttribute("my.operation", "STRING"),
|
||||
new TelemetryAttribute("http.request.header.x-test-request", "STRING_ARRAY"),
|
||||
new TelemetryAttribute("http.response.header.x-test-response", "STRING_ARRAY")));
|
||||
EmittedSpans.Span span2 =
|
||||
new EmittedSpans.Span(
|
||||
"SERVER",
|
||||
List.of(
|
||||
new TelemetryAttribute("my.operation", "STRING"),
|
||||
new TelemetryAttribute("test-baggage-key-1", "STRING"),
|
||||
new TelemetryAttribute("test-baggage-key-2", "STRING")));
|
||||
|
||||
// Create test span for a different scope (should be filtered out)
|
||||
EmittedSpans.Span testSpan =
|
||||
new EmittedSpans.Span(
|
||||
"INTERNAL", List.of(new TelemetryAttribute("my.operation", "STRING")));
|
||||
|
||||
EmittedSpans.SpansByScope targetSpansByScope =
|
||||
new EmittedSpans.SpansByScope(targetScopeName, List.of(span1, span2));
|
||||
EmittedSpans.SpansByScope otherSpansByScope =
|
||||
new EmittedSpans.SpansByScope("other-scope", List.of(testSpan));
|
||||
|
||||
EmittedSpans emittedSpans =
|
||||
new EmittedSpans("default", List.of(targetSpansByScope, otherSpansByScope));
|
||||
|
||||
// Aggregate spans - only target scope should be included
|
||||
Map<String, Map<String, Set<TelemetryAttribute>>> spans =
|
||||
SpanParser.SpanAggregator.aggregateSpans("default", emittedSpans, targetScopeName);
|
||||
|
||||
Map<String, List<EmittedSpans.Span>> result =
|
||||
SpanParser.SpanAggregator.buildFilteredSpans(spans);
|
||||
|
||||
assertThat(result.size()).isEqualTo(1);
|
||||
assertThat(result.get("default")).isNotNull();
|
||||
assertThat(result.get("default").size()).isEqualTo(2); // CLIENT and SERVER spans
|
||||
|
||||
// Verify span kinds are preserved
|
||||
List<String> spanKinds =
|
||||
result.get("default").stream().map(EmittedSpans.Span::getSpanKind).toList();
|
||||
assertThat(spanKinds).containsExactlyInAnyOrder("CLIENT", "SERVER");
|
||||
|
||||
// Verify test attributes are filtered out
|
||||
List<TelemetryAttribute> clientAttributes =
|
||||
result.get("default").stream()
|
||||
.filter(span -> span.getSpanKind().equals("CLIENT"))
|
||||
.flatMap(span -> span.getAttributes().stream())
|
||||
.toList();
|
||||
assertThat(clientAttributes)
|
||||
.hasSize(1)
|
||||
.extracting(TelemetryAttribute::getName)
|
||||
.containsExactly("my.operation");
|
||||
|
||||
List<TelemetryAttribute> serverAttributes =
|
||||
result.get("default").stream()
|
||||
.filter(span -> span.getSpanKind().equals("SERVER"))
|
||||
.flatMap(span -> span.getAttributes().stream())
|
||||
.toList();
|
||||
|
||||
assertThat(serverAttributes)
|
||||
.hasSize(1)
|
||||
.extracting(TelemetryAttribute::getName)
|
||||
.containsExactly("my.operation");
|
||||
}
|
||||
}
|
|
@ -11,10 +11,12 @@ import com.fasterxml.jackson.core.JsonProcessingException;
|
|||
import io.opentelemetry.instrumentation.docs.internal.ConfigurationOption;
|
||||
import io.opentelemetry.instrumentation.docs.internal.ConfigurationType;
|
||||
import io.opentelemetry.instrumentation.docs.internal.EmittedMetrics;
|
||||
import io.opentelemetry.instrumentation.docs.internal.EmittedSpans;
|
||||
import io.opentelemetry.instrumentation.docs.internal.InstrumentationClassification;
|
||||
import io.opentelemetry.instrumentation.docs.internal.InstrumentationMetaData;
|
||||
import io.opentelemetry.instrumentation.docs.internal.InstrumentationModule;
|
||||
import io.opentelemetry.instrumentation.docs.internal.InstrumentationType;
|
||||
import io.opentelemetry.instrumentation.docs.internal.TelemetryAttribute;
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.util.ArrayList;
|
||||
|
@ -297,11 +299,11 @@ class YamlHelperTest {
|
|||
"HISTOGRAM",
|
||||
"s",
|
||||
List.of(
|
||||
new EmittedMetrics.Attribute("db.namespace", "STRING"),
|
||||
new EmittedMetrics.Attribute("db.operation.name", "STRING"),
|
||||
new EmittedMetrics.Attribute("db.system.name", "STRING"),
|
||||
new EmittedMetrics.Attribute("server.address", "STRING"),
|
||||
new EmittedMetrics.Attribute("server.port", "LONG")));
|
||||
new TelemetryAttribute("db.namespace", "STRING"),
|
||||
new TelemetryAttribute("db.operation.name", "STRING"),
|
||||
new TelemetryAttribute("db.system.name", "STRING"),
|
||||
new TelemetryAttribute("server.address", "STRING"),
|
||||
new TelemetryAttribute("server.port", "LONG")));
|
||||
|
||||
targetVersions.put(
|
||||
InstrumentationType.LIBRARY, new HashSet<>(List.of("org.apache.mylib:mylib-core:2.3.0")));
|
||||
|
@ -354,4 +356,68 @@ class YamlHelperTest {
|
|||
|
||||
assertThat(expectedYaml).isEqualTo(stringWriter.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSpanParsing() throws Exception {
|
||||
List<InstrumentationModule> modules = new ArrayList<>();
|
||||
Map<InstrumentationType, Set<String>> targetVersions = new HashMap<>();
|
||||
|
||||
EmittedSpans.Span span =
|
||||
new EmittedSpans.Span(
|
||||
"CLIENT",
|
||||
List.of(
|
||||
new TelemetryAttribute("db.namespace", "STRING"),
|
||||
new TelemetryAttribute("db.operation.name", "STRING"),
|
||||
new TelemetryAttribute("db.system.name", "STRING"),
|
||||
new TelemetryAttribute("server.address", "STRING"),
|
||||
new TelemetryAttribute("server.port", "LONG")));
|
||||
|
||||
targetVersions.put(
|
||||
InstrumentationType.LIBRARY, new HashSet<>(List.of("org.apache.mylib:mylib-core:2.3.0")));
|
||||
modules.add(
|
||||
new InstrumentationModule.Builder()
|
||||
.srcPath("instrumentation/mylib/mylib-core-2.3")
|
||||
.instrumentationName("mylib-2.3")
|
||||
.namespace("mylib")
|
||||
.group("mylib")
|
||||
.targetVersions(targetVersions)
|
||||
.spans(Map.of("default", List.of(span)))
|
||||
.build());
|
||||
|
||||
StringWriter stringWriter = new StringWriter();
|
||||
BufferedWriter writer = new BufferedWriter(stringWriter);
|
||||
|
||||
YamlHelper.generateInstrumentationYaml(modules, writer);
|
||||
writer.flush();
|
||||
|
||||
String expectedYaml =
|
||||
"""
|
||||
libraries:
|
||||
mylib:
|
||||
- name: mylib-2.3
|
||||
source_path: instrumentation/mylib/mylib-core-2.3
|
||||
scope:
|
||||
name: io.opentelemetry.mylib-2.3
|
||||
target_versions:
|
||||
library:
|
||||
- org.apache.mylib:mylib-core:2.3.0
|
||||
telemetry:
|
||||
- when: default
|
||||
spans:
|
||||
- span_kind: CLIENT
|
||||
attributes:
|
||||
- name: db.namespace
|
||||
type: STRING
|
||||
- name: db.operation.name
|
||||
type: STRING
|
||||
- name: db.system.name
|
||||
type: STRING
|
||||
- name: server.address
|
||||
type: STRING
|
||||
- name: server.port
|
||||
type: LONG
|
||||
""";
|
||||
|
||||
assertThat(expectedYaml).isEqualTo(stringWriter.toString());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,3 +19,10 @@ dependencies {
|
|||
otelJava {
|
||||
minJavaVersionSupported.set(JavaVersion.VERSION_17)
|
||||
}
|
||||
|
||||
tasks {
|
||||
test {
|
||||
systemProperty("collectMetadata", findProperty("collectMetadata")?.toString() ?: "false")
|
||||
systemProperty("collectSpans", true)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,11 +5,6 @@
|
|||
|
||||
package io.opentelemetry.instrumentation.akkaactor
|
||||
|
||||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import akka.actor.{Actor, ActorLogging, ActorRef, ActorSystem, Props}
|
||||
import akka.pattern.ask
|
||||
import akka.util.Timeout
|
||||
|
|
|
@ -66,6 +66,7 @@ tasks {
|
|||
|
||||
systemProperty("testLatestDeps", findProperty("testLatestDeps") as Boolean)
|
||||
systemProperty("collectMetadata", findProperty("collectMetadata")?.toString() ?: "false")
|
||||
systemProperty("collectSpans", true)
|
||||
}
|
||||
|
||||
check {
|
||||
|
|
|
@ -27,10 +27,12 @@ tasks {
|
|||
|
||||
systemProperty("collectMetadata", collectMetadata)
|
||||
systemProperty("metaDataConfig", "otel.semconv-stability.opt-in=database")
|
||||
systemProperty("collectSpans", true)
|
||||
}
|
||||
|
||||
test {
|
||||
systemProperty("collectMetadata", collectMetadata)
|
||||
systemProperty("collectSpans", true)
|
||||
}
|
||||
|
||||
check {
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
description: >
|
||||
This instrumentation provides database connection pools metrics for Apache DBCP.
|
||||
|
||||
The instrumentation uses `MBeanRegistration` methods for lifecycle detection, therefore it
|
||||
only activates if the `BasicDataSource` is registered to an `MBeanServer`. If using Spring Boot,
|
||||
this happens automatically as all Spring beans that support JMX registration are automatically
|
||||
registered by default.
|
|
@ -45,6 +45,9 @@ tasks.withType<Test>().configureEach {
|
|||
jvmArgs("--add-opens=java.base/java.math=ALL-UNNAMED")
|
||||
// required on jdk17
|
||||
jvmArgs("--add-opens=java.base/java.lang=ALL-UNNAMED")
|
||||
|
||||
systemProperty("collectMetadata", findProperty("collectMetadata")?.toString() ?: "false")
|
||||
systemProperty("collectSpans", true)
|
||||
}
|
||||
|
||||
tasks {
|
||||
|
|
|
@ -20,5 +20,6 @@ dependencies {
|
|||
tasks {
|
||||
test {
|
||||
systemProperty("collectMetadata", findProperty("collectMetadata")?.toString() ?: "false")
|
||||
systemProperty("collectSpans", true)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
description: This instrumentation provides CLIENT spans and metrics for the Apache HttpAsyncClient.
|
|
@ -19,5 +19,7 @@ dependencies {
|
|||
tasks {
|
||||
withType<Test>().configureEach {
|
||||
systemProperty("testLatestDeps", findProperty("testLatestDeps") as Boolean)
|
||||
systemProperty("collectMetadata", findProperty("collectMetadata")?.toString() ?: "false")
|
||||
systemProperty("collectSpans", true)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
description: This instrumentation provides CLIENT spans and metrics for versions 2 and 3 of the Apache HttpClient.
|
|
@ -29,3 +29,10 @@ dependencies {
|
|||
library("org.apache.httpcomponents:httpclient:4.0")
|
||||
testCompileOnly("net.jcip:jcip-annotations:1.0")
|
||||
}
|
||||
|
||||
tasks {
|
||||
test {
|
||||
systemProperty("collectMetadata", findProperty("collectMetadata")?.toString() ?: "false")
|
||||
systemProperty("collectSpans", true)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
description: This instrumentation provides CLIENT spans and metrics for version 4 of the Apache HttpClient.
|
|
@ -11,3 +11,10 @@ dependencies {
|
|||
|
||||
latestDepTestLibrary("org.apache.httpcomponents:httpclient:4.+") // see apache-httpclient-5.0 module
|
||||
}
|
||||
|
||||
tasks {
|
||||
test {
|
||||
systemProperty("collectMetadata", findProperty("collectMetadata")?.toString() ?: "false")
|
||||
systemProperty("collectSpans", true)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
description: This instrumentation provides a library integration that enables CLIENT spans and metrics for the Apache HttpClient.
|
|
@ -99,33 +99,39 @@ class AwsConnector {
|
|||
}
|
||||
|
||||
void setTopicPublishingPolicy(String topicArn) {
|
||||
String snsPolicy =
|
||||
"{"
|
||||
+ " \"Statement\": ["
|
||||
+ " {"
|
||||
+ " \"Effect\": \"Allow\","
|
||||
+ " \"Principal\": \"*\","
|
||||
+ " \"Action\": \"sns:Publish\","
|
||||
+ " \"Resource\": \"%s\""
|
||||
+ " }]"
|
||||
+ "}";
|
||||
snsClient.setTopicAttributes(
|
||||
new SetTopicAttributesRequest(topicArn, "Policy", String.format(snsPolicy, topicArn)));
|
||||
new SetTopicAttributesRequest(
|
||||
topicArn,
|
||||
"Policy",
|
||||
String.format(
|
||||
"{"
|
||||
+ " \"Statement\": ["
|
||||
+ " {"
|
||||
+ " \"Effect\": \"Allow\","
|
||||
+ " \"Principal\": \"*\","
|
||||
+ " \"Action\": \"sns:Publish\","
|
||||
+ " \"Resource\": \"%s\""
|
||||
+ " }]"
|
||||
+ "}",
|
||||
topicArn)));
|
||||
}
|
||||
|
||||
void setQueuePublishingPolicy(String queueUrl, String queueArn) {
|
||||
String sqsPolicy =
|
||||
"{"
|
||||
+ " \"Statement\": ["
|
||||
+ " {"
|
||||
+ " \"Effect\": \"Allow\","
|
||||
+ " \"Principal\": \"*\","
|
||||
+ " \"Action\": \"sqs:SendMessage\","
|
||||
+ " \"Resource\": \"%s\""
|
||||
+ " }]"
|
||||
+ "}";
|
||||
sqsClient.setQueueAttributes(
|
||||
queueUrl, Collections.singletonMap("Policy", String.format(sqsPolicy, queueArn)));
|
||||
queueUrl,
|
||||
Collections.singletonMap(
|
||||
"Policy",
|
||||
String.format(
|
||||
"{"
|
||||
+ " \"Statement\": ["
|
||||
+ " {"
|
||||
+ " \"Effect\": \"Allow\","
|
||||
+ " \"Principal\": \"*\","
|
||||
+ " \"Action\": \"sqs:SendMessage\","
|
||||
+ " \"Resource\": \"%s\""
|
||||
+ " }]"
|
||||
+ "}",
|
||||
queueArn)));
|
||||
}
|
||||
|
||||
void createBucket(String bucketName) {
|
||||
|
|
|
@ -23,7 +23,8 @@ dependencies {
|
|||
latestDepTestLibrary("com.amazonaws:aws-java-sdk-sqs:1.12.583") // documented limitation
|
||||
}
|
||||
|
||||
if (!(findProperty("testLatestDeps") as Boolean)) {
|
||||
val testLatestDeps = findProperty("testLatestDeps") as Boolean
|
||||
if (!testLatestDeps) {
|
||||
configurations.testRuntimeClasspath {
|
||||
resolutionStrategy {
|
||||
eachDependency {
|
||||
|
@ -36,12 +37,26 @@ if (!(findProperty("testLatestDeps") as Boolean)) {
|
|||
}
|
||||
}
|
||||
|
||||
testing {
|
||||
suites {
|
||||
val testSecretsManager by registering(JvmTestSuite::class) {
|
||||
dependencies {
|
||||
implementation(project())
|
||||
implementation(project(":instrumentation:aws-sdk:aws-sdk-1.11:testing"))
|
||||
val version = if (testLatestDeps) "latest.release" else "1.12.80"
|
||||
implementation("com.amazonaws:aws-java-sdk-secretsmanager:$version")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tasks {
|
||||
val testStableSemconv by registering(Test::class) {
|
||||
jvmArgs("-Dotel.semconv-stability.opt-in=database")
|
||||
}
|
||||
|
||||
check {
|
||||
dependsOn(testing.suites)
|
||||
dependsOn(testStableSemconv)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,6 +24,8 @@ class AwsSdkAttributesExtractor implements AttributesExtractor<Request<?>, Respo
|
|||
private static final AttributeKey<String> AWS_REQUEST_ID = stringKey("aws.request_id");
|
||||
|
||||
// Copied from AwsIncubatingAttributes
|
||||
private static final AttributeKey<String> AWS_SECRETSMANAGER_SECRET_ARN =
|
||||
stringKey("aws.secretsmanager.secret.arn");
|
||||
private static final AttributeKey<String> AWS_STEP_FUNCTIONS_ACTIVITY_ARN =
|
||||
stringKey("aws.step_functions.activity.arn");
|
||||
private static final AttributeKey<String> AWS_STEP_FUNCTIONS_STATE_MACHINE_ARN =
|
||||
|
@ -62,8 +64,9 @@ class AwsSdkAttributesExtractor implements AttributesExtractor<Request<?>, Respo
|
|||
Request<?> request,
|
||||
@Nullable Response<?> response,
|
||||
@Nullable Throwable error) {
|
||||
if (response != null) {
|
||||
Object awsResp = response.getAwsResponse();
|
||||
Object awsResp = getAwsResponse(response);
|
||||
if (awsResp != null) {
|
||||
setAttribute(attributes, AWS_SECRETSMANAGER_SECRET_ARN, awsResp, RequestAccess::getSecretArn);
|
||||
setAttribute(
|
||||
attributes,
|
||||
AWS_STEP_FUNCTIONS_STATE_MACHINE_ARN,
|
||||
|
@ -106,4 +109,11 @@ class AwsSdkAttributesExtractor implements AttributesExtractor<Request<?>, Respo
|
|||
attributes.put(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
private static Object getAwsResponse(Response<?> response) {
|
||||
if (response == null) {
|
||||
return null;
|
||||
}
|
||||
return response.getAwsResponse();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,8 @@ import java.lang.invoke.MethodType;
|
|||
import javax.annotation.Nullable;
|
||||
|
||||
final class RequestAccess {
|
||||
private static final String SECRETS_MANAGER_REQUEST_CLASS_PREFIX =
|
||||
"com.amazonaws.services.secretsmanager.model.";
|
||||
private static final String STEP_FUNCTIONS_REQUEST_CLASS_PREFIX =
|
||||
"com.amazonaws.services.stepfunctions.model.";
|
||||
|
||||
|
@ -22,20 +24,20 @@ final class RequestAccess {
|
|||
}
|
||||
};
|
||||
|
||||
@Nullable
|
||||
static String getSecretArn(Object request) {
|
||||
RequestAccess access = REQUEST_ACCESSORS.get(request.getClass());
|
||||
return invokeOrNull(access.getSecretArn, request);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
static String getStepFunctionsActivityArn(Object request) {
|
||||
if (request == null) {
|
||||
return null;
|
||||
}
|
||||
RequestAccess access = REQUEST_ACCESSORS.get(request.getClass());
|
||||
return invokeOrNull(access.getStepFunctionsActivityArn, request);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
static String getStateMachineArn(Object request) {
|
||||
if (request == null) {
|
||||
return null;
|
||||
}
|
||||
RequestAccess access = REQUEST_ACCESSORS.get(request.getClass());
|
||||
return invokeOrNull(access.getStateMachineArn, request);
|
||||
}
|
||||
|
@ -97,6 +99,7 @@ final class RequestAccess {
|
|||
@Nullable private final MethodHandle getBucketName;
|
||||
@Nullable private final MethodHandle getQueueUrl;
|
||||
@Nullable private final MethodHandle getQueueName;
|
||||
@Nullable private final MethodHandle getSecretArn;
|
||||
@Nullable private final MethodHandle getStreamName;
|
||||
@Nullable private final MethodHandle getTableName;
|
||||
@Nullable private final MethodHandle getTopicArn;
|
||||
|
@ -112,6 +115,9 @@ final class RequestAccess {
|
|||
getTableName = findAccessorOrNull(clz, "getTableName");
|
||||
getTopicArn = findAccessorOrNull(clz, "getTopicArn");
|
||||
getTargetArn = findAccessorOrNull(clz, "getTargetArn");
|
||||
|
||||
boolean isSecretsManager = clz.getName().startsWith(SECRETS_MANAGER_REQUEST_CLASS_PREFIX);
|
||||
getSecretArn = isSecretsManager ? findAccessorOrNull(clz, "getARN") : null;
|
||||
boolean isStepFunction = clz.getName().startsWith(STEP_FUNCTIONS_REQUEST_CLASS_PREFIX);
|
||||
getStateMachineArn = isStepFunction ? findAccessorOrNull(clz, "getStateMachineArn") : null;
|
||||
getStepFunctionsActivityArn = isStepFunction ? findAccessorOrNull(clz, "getActivityArn") : null;
|
||||
|
|
|
@ -81,7 +81,7 @@ final class TracingRequestHandler extends RequestHandler2 {
|
|||
Context context = InstrumenterUtil.suppressSpan(instrumenter, parentContext, request);
|
||||
context = context.with(REQUEST_TIMER_KEY, Timer.start());
|
||||
context = context.with(PARENT_CONTEXT_KEY, parentContext);
|
||||
context = context.with(REQUEST_SPAN_SUPPRESSED_KEY, Boolean.TRUE);
|
||||
context = context.with(REQUEST_SPAN_SUPPRESSED_KEY, true);
|
||||
request.addHandlerContext(CONTEXT, context);
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.instrumentation.awssdk.v1_11;
|
||||
|
||||
import com.amazonaws.services.secretsmanager.AWSSecretsManagerClientBuilder;
|
||||
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
|
||||
import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
|
||||
class SecretsManagerClientTest extends AbstractSecretsManagerClientTest {
|
||||
@RegisterExtension
|
||||
private static final InstrumentationExtension testing = LibraryInstrumentationExtension.create();
|
||||
|
||||
@Override
|
||||
protected InstrumentationExtension testing() {
|
||||
return testing;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AWSSecretsManagerClientBuilder configureClient(
|
||||
AWSSecretsManagerClientBuilder clientBuilder) {
|
||||
|
||||
return clientBuilder.withRequestHandlers(
|
||||
AwsSdkTelemetry.builder(testing().getOpenTelemetry())
|
||||
.setCaptureExperimentalSpanAttributes(true)
|
||||
.build()
|
||||
.newRequestHandler());
|
||||
}
|
||||
}
|
|
@ -12,6 +12,7 @@ dependencies {
|
|||
compileOnly("com.amazonaws:aws-java-sdk-kinesis:1.11.106")
|
||||
compileOnly("com.amazonaws:aws-java-sdk-rds:1.11.106")
|
||||
compileOnly("com.amazonaws:aws-java-sdk-s3:1.11.106")
|
||||
compileOnly("com.amazonaws:aws-java-sdk-secretsmanager:1.12.80")
|
||||
compileOnly("com.amazonaws:aws-java-sdk-sns:1.11.106")
|
||||
compileOnly("com.amazonaws:aws-java-sdk-sqs:1.11.106")
|
||||
compileOnly("com.amazonaws:aws-java-sdk-stepfunctions:1.11.106")
|
||||
|
|
|
@ -0,0 +1,93 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.instrumentation.awssdk.v1_11;
|
||||
|
||||
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo;
|
||||
import static io.opentelemetry.semconv.incubating.AwsIncubatingAttributes.AWS_SECRETSMANAGER_SECRET_ARN;
|
||||
import static java.util.Collections.singletonList;
|
||||
|
||||
import com.amazonaws.services.secretsmanager.AWSSecretsManager;
|
||||
import com.amazonaws.services.secretsmanager.AWSSecretsManagerClientBuilder;
|
||||
import com.amazonaws.services.secretsmanager.model.CreateSecretRequest;
|
||||
import com.amazonaws.services.secretsmanager.model.DescribeSecretRequest;
|
||||
import io.opentelemetry.sdk.testing.assertj.AttributeAssertion;
|
||||
import io.opentelemetry.testing.internal.armeria.common.HttpResponse;
|
||||
import io.opentelemetry.testing.internal.armeria.common.HttpStatus;
|
||||
import io.opentelemetry.testing.internal.armeria.common.MediaType;
|
||||
import java.util.List;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public abstract class AbstractSecretsManagerClientTest extends AbstractBaseAwsClientTest {
|
||||
|
||||
public abstract AWSSecretsManagerClientBuilder configureClient(
|
||||
AWSSecretsManagerClientBuilder client);
|
||||
|
||||
@Override
|
||||
protected boolean hasRequestId() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void sendCreateSecretRequestWithMockedResponse() throws Exception {
|
||||
AWSSecretsManagerClientBuilder clientBuilder = AWSSecretsManagerClientBuilder.standard();
|
||||
AWSSecretsManager client =
|
||||
configureClient(clientBuilder)
|
||||
.withEndpointConfiguration(endpoint)
|
||||
.withCredentials(credentialsProvider)
|
||||
.build();
|
||||
|
||||
String body =
|
||||
"{"
|
||||
+ "\"ARN\": \"arn:aws:secretsmanager:us-west-2:123456789012:secret:MyTestDatabaseSecret-a1b2c3\","
|
||||
+ "\"Name\": \"MyTestDatabaseSecret\","
|
||||
+ "\"VersionId\": \"EXAMPLE1-90ab-cdef-fedc-ba987SECRET1\""
|
||||
+ "}";
|
||||
server.enqueue(HttpResponse.of(HttpStatus.OK, MediaType.PLAIN_TEXT_UTF_8, body));
|
||||
|
||||
Object response =
|
||||
client.createSecret(
|
||||
new CreateSecretRequest().withName("secretName").withSecretString("secretValue"));
|
||||
|
||||
List<AttributeAssertion> additionalAttributes =
|
||||
singletonList(
|
||||
equalTo(
|
||||
AWS_SECRETSMANAGER_SECRET_ARN,
|
||||
"arn:aws:secretsmanager:us-west-2:123456789012:secret:MyTestDatabaseSecret-a1b2c3"));
|
||||
|
||||
assertRequestWithMockedResponse(
|
||||
response, client, "AWSSecretsManager", "CreateSecret", "POST", additionalAttributes);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void sendDescribeSecretRequestWithMockedResponse() throws Exception {
|
||||
AWSSecretsManagerClientBuilder clientBuilder = AWSSecretsManagerClientBuilder.standard();
|
||||
AWSSecretsManager client =
|
||||
configureClient(clientBuilder)
|
||||
.withEndpointConfiguration(endpoint)
|
||||
.withCredentials(credentialsProvider)
|
||||
.build();
|
||||
|
||||
String body =
|
||||
"{"
|
||||
+ "\"ARN\": \"arn:aws:secretsmanager:us-east-1:123456789012:secret:My-Secret-Id-WzAXar\","
|
||||
+ "\"Name\": \"My-Secret-Id\","
|
||||
+ "\"VersionId\": \"EXAMPLE1-90ab-cdef-fedc-ba987SECRET1\""
|
||||
+ "}";
|
||||
|
||||
server.enqueue(HttpResponse.of(HttpStatus.OK, MediaType.PLAIN_TEXT_UTF_8, body));
|
||||
Object response =
|
||||
client.describeSecret(new DescribeSecretRequest().withSecretId("My-Secret-Id"));
|
||||
|
||||
List<AttributeAssertion> additionalAttributes =
|
||||
singletonList(
|
||||
equalTo(
|
||||
AWS_SECRETSMANAGER_SECRET_ARN,
|
||||
"arn:aws:secretsmanager:us-east-1:123456789012:secret:My-Secret-Id-WzAXar"));
|
||||
|
||||
assertRequestWithMockedResponse(
|
||||
response, client, "AWSSecretsManager", "DescribeSecret", "POST", additionalAttributes);
|
||||
}
|
||||
}
|
|
@ -28,12 +28,15 @@ tasks {
|
|||
test {
|
||||
usesService(gradle.sharedServices.registrations["testcontainersBuildService"].service)
|
||||
systemProperty("collectMetadata", collectMetadata)
|
||||
systemProperty("collectSpans", true)
|
||||
}
|
||||
|
||||
val testStableSemconv by registering(Test::class) {
|
||||
jvmArgs("-Dotel.semconv-stability.opt-in=database")
|
||||
systemProperty("collectMetadata", collectMetadata)
|
||||
|
||||
systemProperty("metaDataConfig", "otel.semconv-stability.opt-in=database")
|
||||
systemProperty("collectMetadata", collectMetadata)
|
||||
systemProperty("collectSpans", true)
|
||||
}
|
||||
|
||||
check {
|
||||
|
|
|
@ -78,7 +78,7 @@ tasks {
|
|||
}
|
||||
|
||||
val testStableSemconv by registering(Test::class) {
|
||||
jvmArgs("-Dotel.semconv-stability.opt-in=database")
|
||||
jvmArgs("-Dotel.semconv-stability.opt-in=database,code")
|
||||
}
|
||||
|
||||
check {
|
||||
|
|
|
@ -74,7 +74,7 @@ class Elasticsearch53NodeClientTest extends AbstractElasticsearchNodeClientTest
|
|||
new ClusterUpdateSettingsRequest()
|
||||
.transientSettings(
|
||||
Collections.singletonMap(
|
||||
"cluster.routing.allocation.disk.threshold_enabled", Boolean.FALSE)));
|
||||
"cluster.routing.allocation.disk.threshold_enabled", false)));
|
||||
});
|
||||
testing.waitForTraces(1);
|
||||
testing.clearData();
|
||||
|
|
|
@ -87,7 +87,7 @@ class Elasticsearch53TransportClientTest extends AbstractElasticsearchTransportC
|
|||
new ClusterUpdateSettingsRequest()
|
||||
.transientSettings(
|
||||
Collections.singletonMap(
|
||||
"cluster.routing.allocation.disk.threshold_enabled", Boolean.FALSE)));
|
||||
"cluster.routing.allocation.disk.threshold_enabled", false)));
|
||||
});
|
||||
testing.waitForTraces(1);
|
||||
testing.clearData();
|
||||
|
|
|
@ -71,7 +71,7 @@ class Config {
|
|||
new ClusterUpdateSettingsRequest()
|
||||
.transientSettings(
|
||||
Collections.singletonMap(
|
||||
"cluster.routing.allocation.disk.threshold_enabled", Boolean.FALSE)));
|
||||
"cluster.routing.allocation.disk.threshold_enabled", false)));
|
||||
|
||||
return testNode;
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ package io.opentelemetry.javaagent.instrumentation.elasticsearch.transport.v5_3.
|
|||
|
||||
import static io.opentelemetry.api.common.AttributeKey.longKey;
|
||||
import static io.opentelemetry.api.common.AttributeKey.stringKey;
|
||||
import static io.opentelemetry.instrumentation.testing.junit.code.SemconvCodeStabilityUtil.codeFunctionAssertions;
|
||||
import static io.opentelemetry.instrumentation.testing.junit.db.SemconvStabilityUtil.maybeStable;
|
||||
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat;
|
||||
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo;
|
||||
|
@ -17,9 +18,10 @@ import io.opentelemetry.api.trace.SpanKind;
|
|||
import io.opentelemetry.instrumentation.testing.internal.AutoCleanupExtension;
|
||||
import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension;
|
||||
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
|
||||
import io.opentelemetry.semconv.incubating.CodeIncubatingAttributes;
|
||||
import io.opentelemetry.sdk.testing.assertj.AttributeAssertion;
|
||||
import io.opentelemetry.semconv.incubating.DbIncubatingAttributes;
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
import org.junit.jupiter.api.Assumptions;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
@ -79,11 +81,7 @@ class Elasticsearch53SpringRepositoryTest {
|
|||
span.hasName("DocRepository.findAll")
|
||||
.hasKind(SpanKind.INTERNAL)
|
||||
.hasNoParent()
|
||||
.hasAttributesSatisfyingExactly(
|
||||
equalTo(
|
||||
CodeIncubatingAttributes.CODE_NAMESPACE,
|
||||
DocRepository.class.getName()),
|
||||
equalTo(CodeIncubatingAttributes.CODE_FUNCTION, "findAll")),
|
||||
.hasAttributesSatisfyingExactly(assertFunctionName("findAll")),
|
||||
span ->
|
||||
span.hasName("SearchAction")
|
||||
.hasKind(SpanKind.CLIENT)
|
||||
|
@ -113,11 +111,7 @@ class Elasticsearch53SpringRepositoryTest {
|
|||
span.hasName("DocRepository.index")
|
||||
.hasKind(SpanKind.INTERNAL)
|
||||
.hasNoParent()
|
||||
.hasAttributesSatisfyingExactly(
|
||||
equalTo(
|
||||
CodeIncubatingAttributes.CODE_NAMESPACE,
|
||||
DocRepository.class.getName()),
|
||||
equalTo(CodeIncubatingAttributes.CODE_FUNCTION, "index")),
|
||||
.hasAttributesSatisfyingExactly(assertFunctionName("index")),
|
||||
span ->
|
||||
span.hasName("IndexAction")
|
||||
.hasKind(SpanKind.CLIENT)
|
||||
|
@ -162,11 +156,7 @@ class Elasticsearch53SpringRepositoryTest {
|
|||
span.hasName("DocRepository.findById")
|
||||
.hasKind(SpanKind.INTERNAL)
|
||||
.hasNoParent()
|
||||
.hasAttributesSatisfyingExactly(
|
||||
equalTo(
|
||||
CodeIncubatingAttributes.CODE_NAMESPACE,
|
||||
DocRepository.class.getName()),
|
||||
equalTo(CodeIncubatingAttributes.CODE_FUNCTION, "findById")),
|
||||
.hasAttributesSatisfyingExactly(assertFunctionName("findById")),
|
||||
span ->
|
||||
span.hasName("GetAction")
|
||||
.hasKind(SpanKind.CLIENT)
|
||||
|
@ -196,11 +186,7 @@ class Elasticsearch53SpringRepositoryTest {
|
|||
span.hasName("DocRepository.index")
|
||||
.hasKind(SpanKind.INTERNAL)
|
||||
.hasNoParent()
|
||||
.hasAttributesSatisfyingExactly(
|
||||
equalTo(
|
||||
CodeIncubatingAttributes.CODE_NAMESPACE,
|
||||
DocRepository.class.getName()),
|
||||
equalTo(CodeIncubatingAttributes.CODE_FUNCTION, "index")),
|
||||
.hasAttributesSatisfyingExactly(assertFunctionName("index")),
|
||||
span ->
|
||||
span.hasName("IndexAction")
|
||||
.hasKind(SpanKind.CLIENT)
|
||||
|
@ -240,11 +226,7 @@ class Elasticsearch53SpringRepositoryTest {
|
|||
span.hasName("DocRepository.findById")
|
||||
.hasKind(SpanKind.INTERNAL)
|
||||
.hasNoParent()
|
||||
.hasAttributesSatisfyingExactly(
|
||||
equalTo(
|
||||
CodeIncubatingAttributes.CODE_NAMESPACE,
|
||||
DocRepository.class.getName()),
|
||||
equalTo(CodeIncubatingAttributes.CODE_FUNCTION, "findById")),
|
||||
.hasAttributesSatisfyingExactly(assertFunctionName("findById")),
|
||||
span ->
|
||||
span.hasName("GetAction")
|
||||
.hasKind(SpanKind.CLIENT)
|
||||
|
@ -272,11 +254,7 @@ class Elasticsearch53SpringRepositoryTest {
|
|||
span.hasName("DocRepository.deleteById")
|
||||
.hasKind(SpanKind.INTERNAL)
|
||||
.hasNoParent()
|
||||
.hasAttributesSatisfyingExactly(
|
||||
equalTo(
|
||||
CodeIncubatingAttributes.CODE_NAMESPACE,
|
||||
DocRepository.class.getName()),
|
||||
equalTo(CodeIncubatingAttributes.CODE_FUNCTION, "deleteById")),
|
||||
.hasAttributesSatisfyingExactly(assertFunctionName("deleteById")),
|
||||
span ->
|
||||
span.hasName("DeleteAction")
|
||||
.hasKind(SpanKind.CLIENT)
|
||||
|
@ -315,11 +293,7 @@ class Elasticsearch53SpringRepositoryTest {
|
|||
span.hasName("DocRepository.findAll")
|
||||
.hasKind(SpanKind.INTERNAL)
|
||||
.hasNoParent()
|
||||
.hasAttributesSatisfyingExactly(
|
||||
equalTo(
|
||||
CodeIncubatingAttributes.CODE_NAMESPACE,
|
||||
DocRepository.class.getName()),
|
||||
equalTo(CodeIncubatingAttributes.CODE_FUNCTION, "findAll")),
|
||||
.hasAttributesSatisfyingExactly(assertFunctionName("findAll")),
|
||||
span ->
|
||||
span.hasName("SearchAction")
|
||||
.hasKind(SpanKind.CLIENT)
|
||||
|
@ -334,4 +308,8 @@ class Elasticsearch53SpringRepositoryTest {
|
|||
equalTo(stringKey("elasticsearch.request.indices"), "test-index"),
|
||||
equalTo(stringKey("elasticsearch.request.search.types"), "doc"))));
|
||||
}
|
||||
|
||||
private static List<AttributeAssertion> assertFunctionName(String methodName) {
|
||||
return codeFunctionAssertions(DocRepository.class, methodName);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -140,7 +140,7 @@ class Elasticsearch53SpringTemplateTest {
|
|||
new ClusterUpdateSettingsRequest()
|
||||
.transientSettings(
|
||||
Collections.singletonMap(
|
||||
"cluster.routing.allocation.disk.threshold_enabled", Boolean.FALSE)));
|
||||
"cluster.routing.allocation.disk.threshold_enabled", false)));
|
||||
});
|
||||
testing.waitForTraces(1);
|
||||
testing.clearData();
|
||||
|
|
|
@ -68,7 +68,7 @@ public abstract class AbstractElasticsearch6NodeClientTest
|
|||
new ClusterUpdateSettingsRequest()
|
||||
.transientSettings(
|
||||
Collections.singletonMap(
|
||||
"cluster.routing.allocation.disk.threshold_enabled", Boolean.FALSE)));
|
||||
"cluster.routing.allocation.disk.threshold_enabled", false)));
|
||||
});
|
||||
testing.waitForTraces(1);
|
||||
testing.clearData();
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue