diff --git a/.dlc.json b/.dlc.json index 1ae0883a40..483a8c1255 100644 --- a/.dlc.json +++ b/.dlc.json @@ -26,16 +26,23 @@ }, { "pattern": "^https://twitter.com*" + }, + { + "pattern": "^https://blogs.oracle.com/javamagazine/post/a-peek-into-java-17-continuing-the-drive-to-encapsulate-the-java-runtime-internals" + }, + { + "pattern": "^https://www.oceanbase.com/" } ], "timeout": "10s", "retryOn429": true, "retryCount": 10, - "fallbackRetryDelay": "1000s", + "fallbackRetryDelay": "60s", "aliveStatusCodes": [ 200, 301, 302, - 401 + 401, + 403 ] } diff --git a/.github/actions/build/action.yml b/.github/actions/build/action.yml index 2b63165d0d..80e4344366 100644 --- a/.github/actions/build/action.yml +++ b/.github/actions/build/action.yml @@ -39,7 +39,7 @@ runs: sed -i "/<\/sourceDirectories>/i scenarios\/""${{ inputs.test_case }}""<\/sourceDirectory>" test/plugin/pom.xml echo "::endgroup::" - name: Cache local Maven repository - uses: actions/cache@v2 + uses: actions/cache@v4 with: path: ~/.m2/repository key: ${{ runner.os }}-maven-agent-test-${{ hashFiles('**/pom.xml') }} @@ -56,7 +56,7 @@ runs: ./mvnw -q --batch-mode clean package -Dmaven.test.skip || \ ./mvnw -q --batch-mode clean package -Dmaven.test.skip echo "::endgroup::" - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@v4 name: Upload Agent with: name: skywalking-agent @@ -88,8 +88,9 @@ runs: mkdir -p test-containers docker save -o test-containers/skywalking-agent-test-jvm-1.0.0.tgz skywalking/agent-test-jvm:1.0.0 docker save -o test-containers/skywalking-agent-test-tomcat-1.0.0.tgz skywalking/agent-test-tomcat:1.0.0 + ls -la test-containers/ echo "::endgroup::" - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@v4 name: Upload Test Containers with: name: test-tools diff --git a/.github/actions/run/action.yml b/.github/actions/run/action.yml index 02fdb8503c..bb9b3c6143 100644 --- a/.github/actions/run/action.yml +++ b/.github/actions/run/action.yml @@ -25,13 +25,29 @@ inputs: runs: using: "composite" steps: - - uses: actions/download-artifact@v2 + - uses: actions/download-artifact@v4 with: name: skywalking-agent path: skywalking-agent - - uses: actions/download-artifact@v2 + - uses: actions/download-artifact@v4 with: name: test-tools + - name: Disable containerd image store + shell: bash + run: | + DAEMON_JSON="/etc/docker/daemon.json" + if [ -f "$DAEMON_JSON" ]; then + sudo jq '. + {"features": {"containerd-snapshotter": false}}' "$DAEMON_JSON" \ + | sudo tee "${DAEMON_JSON}.tmp" > /dev/null + sudo mv "${DAEMON_JSON}.tmp" "$DAEMON_JSON" + else + echo '{"features": {"containerd-snapshotter": false}}' \ + | sudo tee "$DAEMON_JSON" > /dev/null + fi + sudo systemctl restart docker + docker version + docker info + echo "DOCKER_API_VERSION=$(docker version --format '{{.Server.APIVersion}}')" >> "$GITHUB_ENV" - name: Load Test Containers shell: bash run: | @@ -41,8 +57,9 @@ runs: if ls test-containers/skywalking-agent-test-tomcat-1.0.0.tgz; then docker load -i test-containers/skywalking-agent-test-tomcat-1.0.0.tgz fi + docker images - name: Cache local Maven repository - uses: actions/cache@v2 + uses: actions/cache@v4 with: path: ~/.m2/repository key: ${{ runner.os }}-maven-agent-test-run-${{ hashFiles('**/pom.xml') }} @@ -51,9 +68,9 @@ runs: shell: bash run: | echo "::group::Run Plugin Test ${{ inputs.test_case }}" - bash test/plugin/run.sh ${{ inputs.test_case }} + bash test/plugin/run.sh --debug ${{ inputs.test_case }} echo "::endgroup::" - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@v4 name: Upload Agent if: always() with: diff --git a/.github/workflows/Dockerfile-tomcat-jdk25-withCurl b/.github/workflows/Dockerfile-tomcat-jdk25-withCurl new file mode 100644 index 0000000000..dc510ffb4c --- /dev/null +++ b/.github/workflows/Dockerfile-tomcat-jdk25-withCurl @@ -0,0 +1,21 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +FROM tomcat:10.1.50-jdk25-temurin + +RUN apt-get update \ + && apt-get install -y --no-install-recommends curl \ + && rm -rf /var/lib/apt/lists/* \ No newline at end of file diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index f286166990..0bc7a9c0e9 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -42,7 +42,7 @@ jobs: build: name: Java ${{ matrix.java-version }} / ${{ matrix.os }} runs-on: ${{ matrix.os }}-latest - timeout-minutes: 60 + timeout-minutes: 90 needs: [ license ] strategy: fail-fast: true @@ -52,12 +52,17 @@ jobs: include: - os: ubuntu java-version: 21 + - os: ubuntu + java-version: 25 steps: + - if: matrix.os == 'windows' + name: Support longpaths + run: git config --system core.longpaths true - uses: actions/checkout@v2 with: submodules: true - name: Cache local Maven repository - uses: actions/cache@v2 + uses: actions/cache@v4 with: path: ~/.m2/repository key: ${{ runner.os }}-maven-ci-${{ hashFiles('**/pom.xml') }} diff --git a/.github/workflows/codeql.yaml b/.github/workflows/codeql.yaml index 0932d63cee..1abc057ad0 100644 --- a/.github/workflows/codeql.yaml +++ b/.github/workflows/codeql.yaml @@ -54,11 +54,11 @@ jobs: java-version: 17 - name: Initialize CodeQL - uses: github/codeql-action/init@v1 + uses: github/codeql-action/init@v3 with: languages: ${{ matrix.language }} - run: ./mvnw -q -Dmaven.test.skip=true clean install || ./mvnw -q -Dmaven.test.skip=true clean install - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v1 + uses: github/codeql-action/analyze@v3 diff --git a/.github/workflows/e2e.yaml b/.github/workflows/e2e.yaml index ad1536c3d9..a2393b4942 100644 --- a/.github/workflows/e2e.yaml +++ b/.github/workflows/e2e.yaml @@ -58,8 +58,14 @@ jobs: - name: Setup go uses: actions/setup-go@v2 with: - go-version: '1.18' + go-version: '1.24' - name: Run E2E Tests - uses: apache/skywalking-infra-e2e@c1558dba921a36320f9fdc3d774b66592243589d + uses: apache/skywalking-infra-e2e@8c21e43e241a32a54bdf8eeceb9099eb27e5e9b4 with: e2e-file: ${{ matrix.case.path }} + - uses: actions/upload-artifact@v4 + if: ${{ failure() }} + name: Upload Logs + with: + name: test-logs-${{ matrix.case.name }} + path: "${{ env.SW_INFRA_E2E_LOG_DIR }}" diff --git a/.github/workflows/plugins-jdk11-test.3.yaml b/.github/workflows/plugins-jdk11-test.3.yaml index a1f90fff14..238fd4047c 100644 --- a/.github/workflows/plugins-jdk11-test.3.yaml +++ b/.github/workflows/plugins-jdk11-test.3.yaml @@ -41,6 +41,21 @@ jobs: - uses: actions/checkout@v2 with: submodules: true + - name: Disable containerd image store + run: | + DAEMON_JSON="/etc/docker/daemon.json" + if [ -f "$DAEMON_JSON" ]; then + sudo jq '. + {"features": {"containerd-snapshotter": false}}' "$DAEMON_JSON" \ + | sudo tee "${DAEMON_JSON}.tmp" > /dev/null + sudo mv "${DAEMON_JSON}.tmp" "$DAEMON_JSON" + else + echo '{"features": {"containerd-snapshotter": false}}' \ + | sudo tee "$DAEMON_JSON" > /dev/null + fi + sudo systemctl restart docker + docker version + docker info + echo "DOCKER_API_VERSION=$(docker version --format '{{.Server.APIVersion}}')" >> "$GITHUB_ENV" - name: Build uses: ./.github/actions/build with: @@ -55,6 +70,7 @@ jobs: matrix: case: - jdk11-forkjoinpool-scenario + - jdk-httpclient-scenario steps: - uses: actions/checkout@v2 with: diff --git a/.github/workflows/plugins-jdk17-test.0.yaml b/.github/workflows/plugins-jdk17-test.0.yaml index 6d2ad4c2b6..74ae79f0fe 100644 --- a/.github/workflows/plugins-jdk17-test.0.yaml +++ b/.github/workflows/plugins-jdk17-test.0.yaml @@ -41,6 +41,21 @@ jobs: - uses: actions/checkout@v2 with: submodules: true + - name: Disable containerd image store + run: | + DAEMON_JSON="/etc/docker/daemon.json" + if [ -f "$DAEMON_JSON" ]; then + sudo jq '. + {"features": {"containerd-snapshotter": false}}' "$DAEMON_JSON" \ + | sudo tee "${DAEMON_JSON}.tmp" > /dev/null + sudo mv "${DAEMON_JSON}.tmp" "$DAEMON_JSON" + else + echo '{"features": {"containerd-snapshotter": false}}' \ + | sudo tee "$DAEMON_JSON" > /dev/null + fi + sudo systemctl restart docker + docker version + docker info + echo "DOCKER_API_VERSION=$(docker version --format '{{.Server.APIVersion}}')" >> "$GITHUB_ENV" - name: Build uses: ./.github/actions/build with: @@ -64,6 +79,8 @@ jobs: - grizzly-2.3.x-4.x-workthreadpool-scenario - jetty-11.x-scenario - jetty-10.x-scenario + - spring-ai-1.x-scenario + - spring-rabbitmq-scenario steps: - uses: actions/checkout@v2 with: diff --git a/.github/workflows/plugins-jdk17-test.1.yaml b/.github/workflows/plugins-jdk17-test.1.yaml index d469d4c2ef..939393713b 100644 --- a/.github/workflows/plugins-jdk17-test.1.yaml +++ b/.github/workflows/plugins-jdk17-test.1.yaml @@ -41,6 +41,21 @@ jobs: - uses: actions/checkout@v2 with: submodules: true + - name: Disable containerd image store + run: | + DAEMON_JSON="/etc/docker/daemon.json" + if [ -f "$DAEMON_JSON" ]; then + sudo jq '. + {"features": {"containerd-snapshotter": false}}' "$DAEMON_JSON" \ + | sudo tee "${DAEMON_JSON}.tmp" > /dev/null + sudo mv "${DAEMON_JSON}.tmp" "$DAEMON_JSON" + else + echo '{"features": {"containerd-snapshotter": false}}' \ + | sudo tee "$DAEMON_JSON" > /dev/null + fi + sudo systemctl restart docker + docker version + docker info + echo "DOCKER_API_VERSION=$(docker version --format '{{.Server.APIVersion}}')" >> "$GITHUB_ENV" - name: Build uses: ./.github/actions/build with: @@ -58,8 +73,14 @@ jobs: - spring-6.x-scenario - resteasy-6.x-scenario - gateway-4.x-scenario + - gateway-4.1.2.x-scenario - httpexchange-scenario - activemq-artemis-2.x-scenario + - c3p0-0.9.0.x-0.9.1.x-scenario + - c3p0-0.9.2.x-0.10.x-scenario + - spring-scheduled-6.x-scenario + - caffeine-3.x-scenario + - lettuce-webflux-6x-scenario steps: - uses: actions/checkout@v2 with: diff --git a/.github/workflows/plugins-jdk21-test.0.yaml b/.github/workflows/plugins-jdk21-test.0.yaml index 2851619f12..9e6805515f 100644 --- a/.github/workflows/plugins-jdk21-test.0.yaml +++ b/.github/workflows/plugins-jdk21-test.0.yaml @@ -41,6 +41,21 @@ jobs: - uses: actions/checkout@v2 with: submodules: true + - name: Disable containerd image store + run: | + DAEMON_JSON="/etc/docker/daemon.json" + if [ -f "$DAEMON_JSON" ]; then + sudo jq '. + {"features": {"containerd-snapshotter": false}}' "$DAEMON_JSON" \ + | sudo tee "${DAEMON_JSON}.tmp" > /dev/null + sudo mv "${DAEMON_JSON}.tmp" "$DAEMON_JSON" + else + echo '{"features": {"containerd-snapshotter": false}}' \ + | sudo tee "$DAEMON_JSON" > /dev/null + fi + sudo systemctl restart docker + docker version + docker info + echo "DOCKER_API_VERSION=$(docker version --format '{{.Server.APIVersion}}')" >> "$GITHUB_ENV" - name: Build uses: ./.github/actions/build with: @@ -56,6 +71,7 @@ jobs: matrix: case: - spring-6.x-scenario + - jdk-virtual-thread-executor-scenario steps: - uses: actions/checkout@v2 with: diff --git a/.github/workflows/plugins-jdk25-test.0.yaml b/.github/workflows/plugins-jdk25-test.0.yaml new file mode 100644 index 0000000000..06526df29f --- /dev/null +++ b/.github/workflows/plugins-jdk25-test.0.yaml @@ -0,0 +1,89 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +name: Test + +on: + pull_request: + paths: + - '.github/workflows/plugins-*.yaml' + - 'apm-application-toolkit/**' + - 'apm-commons/**' + - 'apm-protocol/**' + - 'apm-sniffer/**' + - 'test/plugin/**' + - '**/pom.xml' + - '!**.md' + +concurrency: + group: plugins-jdk25-1-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + build: + name: Build + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - uses: actions/checkout@v2 + with: + submodules: true + - name: Disable containerd image store + run: | + DAEMON_JSON="/etc/docker/daemon.json" + if [ -f "$DAEMON_JSON" ]; then + sudo jq '. + {"features": {"containerd-snapshotter": false}}' "$DAEMON_JSON" \ + | sudo tee "${DAEMON_JSON}.tmp" > /dev/null + sudo mv "${DAEMON_JSON}.tmp" "$DAEMON_JSON" + else + echo '{"features": {"containerd-snapshotter": false}}' \ + | sudo tee "$DAEMON_JSON" > /dev/null + fi + sudo systemctl restart docker + docker version + docker info + echo "DOCKER_API_VERSION=$(docker version --format '{{.Server.APIVersion}}')" >> "$GITHUB_ENV" + - name: Build local tomcat-curl image + run: | + docker build -t tomcat-curl:10.1.50-jdk25-temurin \ + -f ./.github/workflows/Dockerfile-tomcat-jdk25-withCurl . + - name: Build + uses: ./.github/actions/build + with: + base_image_java: eclipse-temurin:25-jdk + base_image_tomcat: tomcat-curl:10.1.50-jdk25-temurin + + test: + needs: [ build ] + name: ${{ matrix.case }}-jdk25 + runs-on: ubuntu-latest + timeout-minutes: 90 + strategy: + matrix: + case: + - spring-6.x-scenario + steps: + - uses: actions/checkout@v2 + with: + submodules: true + - uses: actions/setup-java@v2 + with: + distribution: 'temurin' + java-version: '25' + - name: Run Test + uses: ./.github/actions/run + with: + test_case: ${{ matrix.case }} diff --git a/.github/workflows/plugins-test.0.yaml b/.github/workflows/plugins-test.0.yaml index 57cec68cf8..0c86b2e8ad 100644 --- a/.github/workflows/plugins-test.0.yaml +++ b/.github/workflows/plugins-test.0.yaml @@ -41,6 +41,21 @@ jobs: - uses: actions/checkout@v2 with: submodules: true + - name: Disable containerd image store + run: | + DAEMON_JSON="/etc/docker/daemon.json" + if [ -f "$DAEMON_JSON" ]; then + sudo jq '. + {"features": {"containerd-snapshotter": false}}' "$DAEMON_JSON" \ + | sudo tee "${DAEMON_JSON}.tmp" > /dev/null + sudo mv "${DAEMON_JSON}.tmp" "$DAEMON_JSON" + else + echo '{"features": {"containerd-snapshotter": false}}' \ + | sudo tee "$DAEMON_JSON" > /dev/null + fi + sudo systemctl restart docker + docker version + docker info + echo "DOCKER_API_VERSION=$(docker version --format '{{.Server.APIVersion}}')" >> "$GITHUB_ENV" - name: Build uses: ./.github/actions/build @@ -53,6 +68,7 @@ jobs: matrix: case: - activemq-scenario + - agent-so11y-scenario - apm-toolkit-trace-scenario - apm-toolkit-tracer-scenario - armeria-0.96minus-scenario @@ -79,6 +95,8 @@ jobs: - gateway-2.1.x-scenario - gateway-2.0.x-scenario - grpc-scenario + - grpc-1.59.x-1.70.x-scenario + - grpc-1.30.x-1.39.x-scenario - gson-scenario - guava-cache-scenario - elasticjob-3.x-scenario diff --git a/.github/workflows/plugins-test.1.yaml b/.github/workflows/plugins-test.1.yaml index e271ad915b..21d5f04eab 100644 --- a/.github/workflows/plugins-test.1.yaml +++ b/.github/workflows/plugins-test.1.yaml @@ -41,6 +41,21 @@ jobs: - uses: actions/checkout@v2 with: submodules: true + - name: Disable containerd image store + run: | + DAEMON_JSON="/etc/docker/daemon.json" + if [ -f "$DAEMON_JSON" ]; then + sudo jq '. + {"features": {"containerd-snapshotter": false}}' "$DAEMON_JSON" \ + | sudo tee "${DAEMON_JSON}.tmp" > /dev/null + sudo mv "${DAEMON_JSON}.tmp" "$DAEMON_JSON" + else + echo '{"features": {"containerd-snapshotter": false}}' \ + | sudo tee "$DAEMON_JSON" > /dev/null + fi + sudo systemctl restart docker + docker version + docker info + echo "DOCKER_API_VERSION=$(docker version --format '{{.Server.APIVersion}}')" >> "$GITHUB_ENV" - name: Build uses: ./.github/actions/build @@ -72,6 +87,8 @@ jobs: - kafka-scenario - kotlin-coroutine-scenario - lettuce-scenario + - lettuce-6.5.x-scenario + - lettuce-webflux-5x-scenario - mongodb-3.x-scenario - mongodb-4.x-scenario - netty-socketio-scenario diff --git a/.github/workflows/plugins-test.2.yaml b/.github/workflows/plugins-test.2.yaml index ccb6a7865d..5fe28c244f 100644 --- a/.github/workflows/plugins-test.2.yaml +++ b/.github/workflows/plugins-test.2.yaml @@ -41,6 +41,21 @@ jobs: - uses: actions/checkout@v2 with: submodules: true + - name: Disable containerd image store + run: | + DAEMON_JSON="/etc/docker/daemon.json" + if [ -f "$DAEMON_JSON" ]; then + sudo jq '. + {"features": {"containerd-snapshotter": false}}' "$DAEMON_JSON" \ + | sudo tee "${DAEMON_JSON}.tmp" > /dev/null + sudo mv "${DAEMON_JSON}.tmp" "$DAEMON_JSON" + else + echo '{"features": {"containerd-snapshotter": false}}' \ + | sudo tee "$DAEMON_JSON" > /dev/null + fi + sudo systemctl restart docker + docker version + docker info + echo "DOCKER_API_VERSION=$(docker version --format '{{.Server.APIVersion}}')" >> "$GITHUB_ENV" - name: Build uses: ./.github/actions/build @@ -76,11 +91,11 @@ jobs: - struts2.5-scenario - cxf-scenario - okhttp2-scenario - - rocketmq-scenario - jersey-2.0.x-2.25.x-scenario - jersey-2.26.x-2.39.x-scenario - websphere-liberty-23.x-scenario - nacos-client-2.x-scenario + - rocketmq-scenario - rocketmq-5-grpc-scenario steps: - uses: actions/checkout@v2 diff --git a/.github/workflows/plugins-test.3.yaml b/.github/workflows/plugins-test.3.yaml index a4cfe3cb54..3d5880fd9f 100644 --- a/.github/workflows/plugins-test.3.yaml +++ b/.github/workflows/plugins-test.3.yaml @@ -41,6 +41,21 @@ jobs: - uses: actions/checkout@v2 with: submodules: true + - name: Disable containerd image store + run: | + DAEMON_JSON="/etc/docker/daemon.json" + if [ -f "$DAEMON_JSON" ]; then + sudo jq '. + {"features": {"containerd-snapshotter": false}}' "$DAEMON_JSON" \ + | sudo tee "${DAEMON_JSON}.tmp" > /dev/null + sudo mv "${DAEMON_JSON}.tmp" "$DAEMON_JSON" + else + echo '{"features": {"containerd-snapshotter": false}}' \ + | sudo tee "$DAEMON_JSON" > /dev/null + fi + sudo systemctl restart docker + docker version + docker info + echo "DOCKER_API_VERSION=$(docker version --format '{{.Server.APIVersion}}')" >> "$GITHUB_ENV" - name: Build uses: ./.github/actions/build @@ -67,7 +82,7 @@ jobs: - vertx-web-3.6plus-scenario - mariadb-scenario - micronaut-http-scenario - - nats-2.14.x-2.15.x-scenario + - nats-2.14.x-2.16.5-scenario - quasar-scenario - baidu-brpc-scenario - baidu-brpc-3.x-scenario @@ -81,7 +96,7 @@ jobs: - spring-kafka-1.3.x-scenario - spring-kafka-2.2.x-scenario - spring-kafka-2.3.x-scenario - - spring-scheduled-scenario + - spring-scheduled-3.x-5.x-scenario - elasticjob-2.x-scenario - quartz-scheduler-2.x-scenario - xxl-job-2.2.0-scenario @@ -92,7 +107,6 @@ jobs: - gateway-3.x-scenario - gateway-3.x-filter-context-scenario - neo4j-4.x-scenario - - oracle-scenario - druid-1.x-scenario - hikaricp-scenario - clickhouse-0.3.1-scenario @@ -108,7 +122,7 @@ jobs: - grpc-generic-call-scenario - shenyu-2.4.x-grpc-scenario - shenyu-2.4.x-sofarpc-scenario - - impala-jdbc-2.6.x-scenario + - solon-2.x-scenario steps: - uses: actions/checkout@v2 with: diff --git a/.github/workflows/plugins-tomcat10-test.0.yaml b/.github/workflows/plugins-tomcat10-test.0.yaml index 6ba26a754c..9bac4133d9 100644 --- a/.github/workflows/plugins-tomcat10-test.0.yaml +++ b/.github/workflows/plugins-tomcat10-test.0.yaml @@ -41,6 +41,21 @@ jobs: - uses: actions/checkout@v2 with: submodules: true + - name: Disable containerd image store + run: | + DAEMON_JSON="/etc/docker/daemon.json" + if [ -f "$DAEMON_JSON" ]; then + sudo jq '. + {"features": {"containerd-snapshotter": false}}' "$DAEMON_JSON" \ + | sudo tee "${DAEMON_JSON}.tmp" > /dev/null + sudo mv "${DAEMON_JSON}.tmp" "$DAEMON_JSON" + else + echo '{"features": {"containerd-snapshotter": false}}' \ + | sudo tee "$DAEMON_JSON" > /dev/null + fi + sudo systemctl restart docker + docker version + docker info + echo "DOCKER_API_VERSION=$(docker version --format '{{.Server.APIVersion}}')" >> "$GITHUB_ENV" - name: Build uses: ./.github/actions/build with: diff --git a/.github/workflows/plugins-tomcat9-test.0.yaml b/.github/workflows/plugins-tomcat9-test.0.yaml index 8f54080254..400e8a0ff6 100644 --- a/.github/workflows/plugins-tomcat9-test.0.yaml +++ b/.github/workflows/plugins-tomcat9-test.0.yaml @@ -41,6 +41,21 @@ jobs: - uses: actions/checkout@v2 with: submodules: true + - name: Disable containerd image store + run: | + DAEMON_JSON="/etc/docker/daemon.json" + if [ -f "$DAEMON_JSON" ]; then + sudo jq '. + {"features": {"containerd-snapshotter": false}}' "$DAEMON_JSON" \ + | sudo tee "${DAEMON_JSON}.tmp" > /dev/null + sudo mv "${DAEMON_JSON}.tmp" "$DAEMON_JSON" + else + echo '{"features": {"containerd-snapshotter": false}}' \ + | sudo tee "$DAEMON_JSON" > /dev/null + fi + sudo systemctl restart docker + docker version + docker info + echo "DOCKER_API_VERSION=$(docker version --format '{{.Server.APIVersion}}')" >> "$GITHUB_ENV" - name: Build uses: ./.github/actions/build with: diff --git a/.github/workflows/publish-docker.yaml b/.github/workflows/publish-docker.yaml index be696af980..1eb75de778 100644 --- a/.github/workflows/publish-docker.yaml +++ b/.github/workflows/publish-docker.yaml @@ -36,7 +36,7 @@ jobs: with: submodules: true - name: Cache local Maven repository - uses: actions/cache@v2 + uses: actions/cache@v4 with: path: ~/.m2/repository key: ${{ runner.os }}-maven-publish-docker-${{ hashFiles('**/pom.xml') }} @@ -47,7 +47,7 @@ jobs: java-version: 17 - name: Build Agent run: make build - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@v4 name: Upload Agent with: name: skywalking-agent @@ -64,17 +64,32 @@ jobs: timeout-minutes: 60 strategy: matrix: - java-version: [ 8, 11, 17, 21 ] + java-version: [ 8, 11, 17, 21, 25 ] env: TAG: ${{ github.sha }} steps: - uses: actions/checkout@v2 with: submodules: true - - uses: actions/download-artifact@v2 + - uses: actions/download-artifact@v4 with: name: skywalking-agent path: skywalking-agent + - name: Disable containerd image store + run: | + DAEMON_JSON="/etc/docker/daemon.json" + if [ -f "$DAEMON_JSON" ]; then + sudo jq '. + {"features": {"containerd-snapshotter": false}}' "$DAEMON_JSON" \ + | sudo tee "${DAEMON_JSON}.tmp" > /dev/null + sudo mv "${DAEMON_JSON}.tmp" "$DAEMON_JSON" + else + echo '{"features": {"containerd-snapshotter": false}}' \ + | sudo tee "$DAEMON_JSON" > /dev/null + fi + sudo systemctl restart docker + docker version + docker info + echo "DOCKER_API_VERSION=$(docker version --format '{{.Server.APIVersion}}')" >> "$GITHUB_ENV" - name: Log in to the Container registry uses: docker/login-action@v1.10.0 with: diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties index 54ab0bc2f6..94a275913a 100644 --- a/.mvn/wrapper/maven-wrapper.properties +++ b/.mvn/wrapper/maven-wrapper.properties @@ -1,3 +1,3 @@ -distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.1/apache-maven-3.6.1-bin.zip +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.5/maven-wrapper-0.5.5.jar diff --git a/CHANGES.md b/CHANGES.md index 14032134e8..e49c314aab 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -2,24 +2,15 @@ Changes by Version ================== Release Notes. -9.2.0 +9.7.0 ------------------ -* Fix NoSuchMethodError in mvc-annotation-commons and change deprecated method. -* Fix forkjoinpool plugin in JDK11. -* Support for tracing spring-cloud-gateway 4.x in gateway-4.x-plugin. -* Fix re-transform bug when plugin enhanced class proxy parent method. -* Fix error HTTP status codes not recording as SLA failures in Vert.x plugins. -* Support for HttpExchange request tracing. -* Support tracing for async producing, batch sync consuming, and batch async consuming in rocketMQ-client-java-5.x-plugin. -* Convert the Redisson span into an async span. -* Rename system env name from `sw_plugin_kafka_producer_config` to `SW_PLUGIN_KAFKA_PRODUCER_CONFIG`. -* Support for ActiveMQ-Artemis messaging tracing. +* Added support for Lettuce reactive Redis commands. +* Add Spring AI 1.x plugin and GenAI layer. +* Fix httpclient-5.x plugin injecting sw8 propagation headers into ClickHouse HTTP requests (port 8123), causing HTTP 400. Add `PROPAGATION_EXCLUDE_PORTS` config to skip tracing (including header injection) for specified ports in the classic client interceptor. +* Add Spring RabbitMQ 2.x - 4.x plugin. -#### Documentation - - -All issues and pull requests are [here](https://github.com/apache/skywalking/milestone/204?closed=1) +All issues and pull requests are [here](https://github.com/apache/skywalking/milestone/249?closed=1) ------------------ Find change logs of all versions [here](changes). diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000000..02c69c17e3 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,296 @@ +# CLAUDE.md - AI Assistant Guide for Apache SkyWalking Java Agent + +This file provides guidance for AI assistants working with the Apache SkyWalking Java Agent codebase. + +## Project Overview + +Apache SkyWalking Java Agent is a Java-based APM (Application Performance Monitoring) agent designed for microservices, cloud-native, and container-based architectures. It provides automatic instrumentation for distributed tracing, performance metrics collection, and context propagation across service boundaries using bytecode manipulation via ByteBuddy. + +## Repository Structure + +``` +skywalking-java/ +├── apm-commons/ # Shared utilities and libraries +│ ├── apm-datacarrier/ # Data buffering and transport +│ └── apm-util/ # Common utilities +├── apm-protocol/ # Protocol definitions +│ └── apm-network/ # gRPC protocol (submodule: skywalking-data-collect-protocol) +├── apm-sniffer/ # Core agent and plugins (MAIN MODULE) +│ ├── apm-agent/ # Main agent bootstrap and premain entry +│ ├── apm-agent-core/ # Core agent logic, instrumentation engine +│ ├── apm-sdk-plugin/ # Standard SDK plugins (70+ plugins) +│ ├── bootstrap-plugins/ # Bootstrap-level plugins (JDK-level) +│ ├── optional-plugins/ # Optional framework plugins +│ ├── optional-reporter-plugins/ # Reporter plugins (Kafka, etc.) +│ ├── apm-toolkit-activation/ # Toolkit activations +│ ├── apm-test-tools/ # Testing utilities +│ ├── bytebuddy-patch/ # ByteBuddy patches +│ └── config/ # Default agent configurations +├── apm-application-toolkit/ # Public API for applications +│ ├── apm-toolkit-trace/ # Tracing API +│ ├── apm-toolkit-log4j-1.x/ # Log4j 1.x integration +│ ├── apm-toolkit-log4j-2.x/ # Log4j 2.x integration +│ ├── apm-toolkit-logback-1.x/ # Logback integration +│ ├── apm-toolkit-meter/ # Meter API +│ └── apm-toolkit-opentracing/ # OpenTracing API +├── apm-checkstyle/ # Code style configuration +│ ├── checkStyle.xml # Checkstyle rules +│ └── importControl.xml # Import control rules +├── test/ # Testing infrastructure +│ ├── plugin/ # Plugin E2E tests +│ │ ├── scenarios/ # Test scenarios (100+ scenarios) +│ │ ├── agent-test-tools/ # Mock collector, test utilities +│ │ ├── runner-helper/ # Test runner +│ │ └── containers/ # Docker test containers +│ └── e2e/ # End-to-end tests +├── docs/ # Documentation +├── tools/ # Build and utility tools +├── skywalking-agent/ # Built agent distribution output +├── changes/ # Changelog +└── dist-material/ # Distribution materials +``` + +## Build System + +### Prerequisites +- JDK 8, 11, 17, 21, or 25 +- Maven 3.6+ +- Git (with submodule support) + +### Common Build Commands + +```bash +# Clone with submodules +git clone --recurse-submodules https://github.com/apache/skywalking-java.git + +# Or initialize submodules after clone +git submodule init && git submodule update + +# Full build with tests +./mvnw clean install + +# Build without tests (recommended for development) +./mvnw clean package -Dmaven.test.skip=true + +# CI build with javadoc verification +./mvnw clean verify install javadoc:javadoc + +# Run checkstyle only +./mvnw checkstyle:check + +# Build with submodule update +./mvnw clean package -Pall + +# Docker build +make build +make docker +``` + +### Maven Profiles +- `all`: Includes git submodule update for protocol definitions + +### Key Build Properties +- ByteBuddy: 1.17.6 (bytecode manipulation) +- gRPC: 1.74.0 (communication protocol) +- Netty: 4.1.124.Final (network framework) +- Protobuf: 3.25.5 (protocol buffers) +- Lombok: 1.18.42 (annotation processing) + +## Architecture & Key Concepts + +### Agent Architecture +The agent uses ByteBuddy for bytecode manipulation at runtime: + +1. **Premain Entry**: `apm-agent/` contains the agent bootstrap via Java's `-javaagent` mechanism +2. **Instrumentation Engine**: `apm-agent-core/` handles class transformation and plugin loading +3. **Plugins**: Define which classes/methods to intercept and how to collect telemetry + +### Plugin Categories + +**1. SDK Plugins** (`apm-sniffer/apm-sdk-plugin/`) +- Framework-specific instrumentations (70+ plugins) +- Examples: grpc-1.x, spring, dubbo, mybatis, mongodb, redis, etc. +- See `apm-sniffer/apm-sdk-plugin/CLAUDE.md` for plugin development guide + +**2. Bootstrap Plugins** (`apm-sniffer/bootstrap-plugins/`) +- Load at JVM bootstrap phase for JDK-level instrumentation +- Examples: jdk-threading, jdk-http, jdk-httpclient, jdk-virtual-thread-executor +- See `apm-sniffer/bootstrap-plugins/CLAUDE.md` for bootstrap plugin guide + +**3. Optional Plugins** (`apm-sniffer/optional-plugins/`) +- Not included by default, user must copy to plugins directory + +**4. Optional Reporter Plugins** (`apm-sniffer/optional-reporter-plugins/`) +- Alternative data collection backends (e.g., Kafka) + +### Data Flow +1. Agent attaches to JVM via `-javaagent` flag +2. ByteBuddy transforms target classes at load time +3. Interceptors collect span/trace data on method entry/exit +4. Data is buffered via DataCarrier +5. gRPC reporter sends data to OAP backend + +## Code Style & Conventions + +### Checkstyle Rules (enforced via `apm-checkstyle/checkStyle.xml`) + +**Prohibited patterns:** +- No `System.out.println` - use proper logging +- No `@author` tags - ASF projects don't use author annotations +- No Chinese characters in source files +- No tab characters (use 4 spaces) +- No star imports (`import xxx.*`) +- No unused or redundant imports + +**Required patterns:** +- `@Override` annotation required for overridden methods +- `equals()` and `hashCode()` must be overridden together +- Apache 2.0 license header on all source files + +**Naming conventions:** +- Constants/static variables: `UPPER_CASE_WITH_UNDERSCORES` +- Package names: `org.apache.skywalking.apm.*` or `test.apache.skywalking.apm.*` +- Type names: `PascalCase` +- Local variables/parameters/members: `camelCase` +- Plugin directories: `{framework}-{version}-plugin` +- Instrumentation classes: `*Instrumentation.java` +- Interceptor classes: `*Interceptor.java` + +**File limits:** +- Max file length: 3000 lines + +### Lombok Usage +Use Lombok annotations for boilerplate code: +- `@Getter`, `@Setter`, `@Data` +- `@Builder` +- `@Slf4j` for logging + +## Testing + +### Test Frameworks +- JUnit 4.12 for unit tests +- Mockito 5.0.0 for mocking + +### Test Categories + +**Unit Tests** (in each module's `src/test/java`) +- Standard JUnit tests +- Pattern: `*Test.java` + +**Plugin E2E Tests** (`test/plugin/scenarios/`) +- 100+ test scenarios for plugin validation +- Docker-based testing with actual frameworks +- Pattern: `{framework}-{version}-scenario` +- See `apm-sniffer/apm-sdk-plugin/CLAUDE.md` for full test framework documentation + +**End-to-End Tests** (`test/e2e/`) +- Full system integration testing + +### Running Tests +```bash +# Unit tests +./mvnw test + +# Full verification including checkstyle +./mvnw clean verify + +# Skip tests during build +./mvnw package -Dmaven.test.skip=true +``` + +## Git Submodules + +The project uses submodules for protocol definitions: +- `apm-protocol/apm-network/src/main/proto` - skywalking-data-collect-protocol + +Always use `--recurse-submodules` when cloning or update submodules manually: +```bash +git submodule init && git submodule update +``` + +## IDE Setup (IntelliJ IDEA) + +1. Import as Maven project +2. Run `./mvnw compile -Dmaven.test.skip=true` to generate protobuf sources +3. Mark generated source folders: + - `*/target/generated-sources/protobuf/java` + - `*/target/generated-sources/protobuf/grpc-java` +4. Enable annotation processing for Lombok + +## Key Files for Understanding the Codebase + +- `apm-sniffer/apm-agent/` - Agent entry point (premain) +- `apm-sniffer/apm-agent-core/src/main/java/.../enhance/` - Instrumentation engine +- `apm-sniffer/apm-agent-core/src/main/java/.../plugin/` - Plugin loading system +- `apm-sniffer/apm-sdk-plugin/` - All standard plugins (reference implementations) +- `apm-sniffer/config/agent.config` - Default agent configuration + +## Common Development Tasks + +### Adding a New Plugin +See `apm-sniffer/apm-sdk-plugin/CLAUDE.md` for detailed guide. + +### Adding an Optional Plugin +1. Create in `apm-sniffer/optional-plugins/` +2. Update documentation in `docs/en/setup/service-agent/java-agent/Optional-plugins.md` + +### Modifying Agent Configuration +1. Edit `apm-sniffer/config/agent.config` +2. Update documentation if adding new options + +## Documentation + +- `docs/en/setup/service-agent/java-agent/` - Main agent documentation +- `docs/en/setup/service-agent/java-agent/Plugin-list.md` - Complete plugin list +- `docs/en/setup/service-agent/java-agent/Optional-plugins.md` - Optional plugins guide +- `CHANGES.md` - Changelog (update when making changes) + +## Community + +- GitHub Issues: https://github.com/apache/skywalking-java/issues +- Mailing List: dev@skywalking.apache.org +- Slack: #skywalking channel at Apache Slack + +## Submitting Pull Requests + +### Branch Strategy +- **Never work directly on main branch** +- Create a new branch for your changes + +### PR Template +Follow `.github/PULL_REQUEST_TEMPLATE` based on change type: +- **Bug fix**: Add unit test, explain bug cause and fix +- **New plugin**: Add test case, component ID in OAP, logo in UI repo +- **Performance improvement**: Add benchmark with results, link to theory/discussion +- **New feature**: Link design doc if non-trivial, update docs, add tests + +### PR Requirements +- Follow Apache Code of Conduct +- Include updated documentation for new features +- Include tests for new functionality +- Reference original issue (e.g., "Resolves #123") +- Update `CHANGES.md` for user-facing changes +- Pass all CI checks (checkstyle, tests, license headers) + +### PR Description +- Bug fixes: Explain the bug and how it's fixed, add regression test +- New features: Link to design doc if non-trivial, update docs, add tests +- Do NOT add AI assistant as co-author + +## CI/CD + +GitHub Actions workflows: +- **CI**: Multi-OS (Ubuntu, macOS, Windows), Multi-Java (8, 11, 17, 21, 25) +- **Plugin Tests**: Parallel E2E tests for all plugins +- **E2E Tests**: Full system integration +- **Docker Publishing**: Multi-variant images + +## Tips for AI Assistants + +1. **Always check submodules**: Protocol changes may require submodule updates +2. **Generate sources first**: Run `mvnw compile` before analyzing generated code +3. **Respect checkstyle**: No System.out, no @author, no Chinese characters +4. **Use Lombok**: Prefer annotations over boilerplate code +5. **Test both unit and E2E**: Different test patterns for different scopes +6. **Java version compatibility**: Agent core must maintain Java 8 compatibility, but individual plugins may target higher JDK versions (e.g., jdk-httpclient-plugin for JDK 11+, virtual-thread plugins for JDK 21+) +7. **For plugin development**: See `apm-sniffer/apm-sdk-plugin/CLAUDE.md` and `apm-sniffer/bootstrap-plugins/CLAUDE.md` diff --git a/Makefile b/Makefile index b0b6adde5e..965c6572bc 100644 --- a/Makefile +++ b/Makefile @@ -34,7 +34,7 @@ dist: build # Docker build -base.all := alpine java8 java11 java17 java21 +base.all := alpine java8 java11 java17 java21 java25 base.each = $(word 1, $@) base.image.alpine := alpine:3 @@ -42,6 +42,7 @@ base.image.java8 := eclipse-temurin:8-jre base.image.java11 := eclipse-temurin:11-jre base.image.java17 := eclipse-temurin:17-jre base.image.java21 := eclipse-temurin:21-jre +base.image.java25 := eclipse-temurin:25-jre docker.%: PLATFORMS = docker.%: LOAD_OR_PUSH = --load diff --git a/apm-application-toolkit/apm-toolkit-kafka/pom.xml b/apm-application-toolkit/apm-toolkit-kafka/pom.xml index 9f612c228b..695985162e 100644 --- a/apm-application-toolkit/apm-toolkit-kafka/pom.xml +++ b/apm-application-toolkit/apm-toolkit-kafka/pom.xml @@ -21,7 +21,7 @@ apm-application-toolkit org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-application-toolkit/apm-toolkit-log4j-1.x/pom.xml b/apm-application-toolkit/apm-toolkit-log4j-1.x/pom.xml index 893f062e28..6983c0d2e9 100644 --- a/apm-application-toolkit/apm-toolkit-log4j-1.x/pom.xml +++ b/apm-application-toolkit/apm-toolkit-log4j-1.x/pom.xml @@ -21,7 +21,7 @@ apm-application-toolkit org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-application-toolkit/apm-toolkit-log4j-2.x/pom.xml b/apm-application-toolkit/apm-toolkit-log4j-2.x/pom.xml index 09c604acd7..393ea94496 100644 --- a/apm-application-toolkit/apm-toolkit-log4j-2.x/pom.xml +++ b/apm-application-toolkit/apm-toolkit-log4j-2.x/pom.xml @@ -21,7 +21,7 @@ apm-application-toolkit org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-application-toolkit/apm-toolkit-logback-1.x/pom.xml b/apm-application-toolkit/apm-toolkit-logback-1.x/pom.xml index 1ee1242ba0..f5b9d63625 100644 --- a/apm-application-toolkit/apm-toolkit-logback-1.x/pom.xml +++ b/apm-application-toolkit/apm-toolkit-logback-1.x/pom.xml @@ -21,7 +21,7 @@ apm-application-toolkit org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-application-toolkit/apm-toolkit-meter/pom.xml b/apm-application-toolkit/apm-toolkit-meter/pom.xml index 91ed42b70e..9c2af46e51 100644 --- a/apm-application-toolkit/apm-toolkit-meter/pom.xml +++ b/apm-application-toolkit/apm-toolkit-meter/pom.xml @@ -20,7 +20,7 @@ apm-application-toolkit org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-application-toolkit/apm-toolkit-micrometer-1.10/pom.xml b/apm-application-toolkit/apm-toolkit-micrometer-1.10/pom.xml index 98b6dec2f9..70e3a957d6 100644 --- a/apm-application-toolkit/apm-toolkit-micrometer-1.10/pom.xml +++ b/apm-application-toolkit/apm-toolkit-micrometer-1.10/pom.xml @@ -20,7 +20,7 @@ apm-application-toolkit org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-application-toolkit/apm-toolkit-micrometer-registry/pom.xml b/apm-application-toolkit/apm-toolkit-micrometer-registry/pom.xml index d2882e4c1d..5658bea135 100644 --- a/apm-application-toolkit/apm-toolkit-micrometer-registry/pom.xml +++ b/apm-application-toolkit/apm-toolkit-micrometer-registry/pom.xml @@ -20,7 +20,7 @@ apm-application-toolkit org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-application-toolkit/apm-toolkit-opentracing/pom.xml b/apm-application-toolkit/apm-toolkit-opentracing/pom.xml index d517cf9ba0..a7d9aefd2a 100644 --- a/apm-application-toolkit/apm-toolkit-opentracing/pom.xml +++ b/apm-application-toolkit/apm-toolkit-opentracing/pom.xml @@ -21,7 +21,7 @@ apm-application-toolkit org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-application-toolkit/apm-toolkit-opentracing/src/main/resources/META-INF.services/io.opentracing.Tracer b/apm-application-toolkit/apm-toolkit-opentracing/src/main/resources/META-INF/services/io.opentracing.Tracer similarity index 92% rename from apm-application-toolkit/apm-toolkit-opentracing/src/main/resources/META-INF.services/io.opentracing.Tracer rename to apm-application-toolkit/apm-toolkit-opentracing/src/main/resources/META-INF/services/io.opentracing.Tracer index 894da6a64f..4cb914e314 100644 --- a/apm-application-toolkit/apm-toolkit-opentracing/src/main/resources/META-INF.services/io.opentracing.Tracer +++ b/apm-application-toolkit/apm-toolkit-opentracing/src/main/resources/META-INF/services/io.opentracing.Tracer @@ -16,4 +16,4 @@ # # -org.apache.skywalking.apm.toolkit.opentracing.SkyWalkingTracer +org.apache.skywalking.apm.toolkit.opentracing.SkywalkingTracer diff --git a/apm-application-toolkit/apm-toolkit-trace/pom.xml b/apm-application-toolkit/apm-toolkit-trace/pom.xml index a97add2ba6..642a551645 100644 --- a/apm-application-toolkit/apm-toolkit-trace/pom.xml +++ b/apm-application-toolkit/apm-toolkit-trace/pom.xml @@ -20,7 +20,7 @@ apm-application-toolkit org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-application-toolkit/apm-toolkit-webflux/pom.xml b/apm-application-toolkit/apm-toolkit-webflux/pom.xml index 1e0d76778f..8397b9193f 100644 --- a/apm-application-toolkit/apm-toolkit-webflux/pom.xml +++ b/apm-application-toolkit/apm-toolkit-webflux/pom.xml @@ -20,7 +20,7 @@ apm-application-toolkit org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-application-toolkit/pom.xml b/apm-application-toolkit/pom.xml index 3610a77e88..cc173543d0 100644 --- a/apm-application-toolkit/pom.xml +++ b/apm-application-toolkit/pom.xml @@ -20,7 +20,7 @@ java-agent org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 apm-application-toolkit diff --git a/apm-commons/apm-datacarrier/pom.xml b/apm-commons/apm-datacarrier/pom.xml index 8167793d82..a3c91d10ba 100644 --- a/apm-commons/apm-datacarrier/pom.xml +++ b/apm-commons/apm-datacarrier/pom.xml @@ -21,7 +21,7 @@ java-agent-commons org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-commons/apm-datacarrier/src/main/java/org/apache/skywalking/apm/commons/datacarrier/consumer/ConsumeDriver.java b/apm-commons/apm-datacarrier/src/main/java/org/apache/skywalking/apm/commons/datacarrier/consumer/ConsumeDriver.java index a6d808fead..853c7fc3bc 100644 --- a/apm-commons/apm-datacarrier/src/main/java/org/apache/skywalking/apm/commons/datacarrier/consumer/ConsumeDriver.java +++ b/apm-commons/apm-datacarrier/src/main/java/org/apache/skywalking/apm/commons/datacarrier/consumer/ConsumeDriver.java @@ -27,7 +27,7 @@ * Pool of consumers

Created by wusheng on 2016/10/25. */ public class ConsumeDriver implements IDriver { - private boolean running; + private volatile boolean running; private ConsumerThread[] consumerThreads; private Channels channels; private ReentrantLock lock; @@ -88,6 +88,9 @@ public void begin(Channels channels) { } lock.lock(); try { + if (running) { + return; + } this.allocateBuffer2Thread(); for (ConsumerThread consumerThread : consumerThreads) { consumerThread.start(); @@ -124,8 +127,14 @@ private void allocateBuffer2Thread() { @Override public void close(Channels channels) { + if (!running) { + return; + } lock.lock(); try { + if (!running) { + return; + } this.running = false; for (ConsumerThread consumerThread : consumerThreads) { consumerThread.shutdown(); diff --git a/apm-commons/apm-util/pom.xml b/apm-commons/apm-util/pom.xml index f0c0f422d6..1bbb7009a4 100644 --- a/apm-commons/apm-util/pom.xml +++ b/apm-commons/apm-util/pom.xml @@ -20,7 +20,7 @@ java-agent-commons org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-commons/pom.xml b/apm-commons/pom.xml index 930dbef34e..79e66796ca 100644 --- a/apm-commons/pom.xml +++ b/apm-commons/pom.xml @@ -20,7 +20,7 @@ java-agent org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-protocol/apm-network/pom.xml b/apm-protocol/apm-network/pom.xml index 78086418f6..a1531c3ca0 100644 --- a/apm-protocol/apm-network/pom.xml +++ b/apm-protocol/apm-network/pom.xml @@ -21,7 +21,7 @@ java-agent-protocol org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 @@ -35,12 +35,15 @@ io.netty netty-handler-proxy - ${netty.version} io.netty netty-codec-http2 - ${netty.version} + + + io.grpc + grpc-core + ${grpc.version} io.grpc @@ -70,7 +73,6 @@ io.netty netty-tcnative-boringssl-static - ${netty-tcnative-boringssl-static.version} org.apache.tomcat diff --git a/apm-protocol/apm-network/src/main/java/org/apache/skywalking/apm/network/trace/component/ComponentsDefine.java b/apm-protocol/apm-network/src/main/java/org/apache/skywalking/apm/network/trace/component/ComponentsDefine.java index 2334cb58e0..a8b57ba7f8 100755 --- a/apm-protocol/apm-network/src/main/java/org/apache/skywalking/apm/network/trace/component/ComponentsDefine.java +++ b/apm-protocol/apm-network/src/main/java/org/apache/skywalking/apm/network/trace/component/ComponentsDefine.java @@ -244,4 +244,55 @@ public class ComponentsDefine { public static final OfficialComponent NACOS = new OfficialComponent(150, "Nacos"); public static final OfficialComponent NETTY_HTTP = new OfficialComponent(151, "Netty-http"); + + public static final OfficialComponent C3P0 = new OfficialComponent(152, "c3p0"); + + public static final OfficialComponent DERBY_JDBC_DRIVER = new OfficialComponent(153, "Derby-jdbc-driver"); + + public static final OfficialComponent SQLITE_JDBC_DRIVER = new OfficialComponent(154, "Sqlite-jdbc-driver"); + + public static final OfficialComponent DB2_JDBC_DRIVER = new OfficialComponent(155, "Db2-jdbc-driver"); + + public static final OfficialComponent SYBASE_JDBC_DRIVER = new OfficialComponent(156, "Sybase-jdbc-driver"); + + public static final OfficialComponent OCEANBASE_JDBC_DRIVER = new OfficialComponent(157, "OceanBase-jdbc-driver"); + + public static final OfficialComponent SOLON_MVC = new OfficialComponent(158, "SolonMVC"); + + public static final OfficialComponent CAFFEINE = new OfficialComponent(160, "Caffeine"); + + public static final OfficialComponent THREAD_PER_TASK_EXECUTOR = new OfficialComponent(161, "ThreadPerTask-executor"); + + public static final OfficialComponent DMDB_JDBC_DRIVER = new OfficialComponent(163, "Dmdb-jdbc-driver"); + + public static final OfficialComponent SPRING_AI_UNKNOWN = new OfficialComponent(164, "spring-ai-unknown"); + + public static final OfficialComponent SPRING_AI_ANTHROPIC = new OfficialComponent(165, "spring-ai-anthropic"); + + public static final OfficialComponent SPRING_AI_BEDROCK = new OfficialComponent(166, "spring-ai-aws-bedrock"); + + public static final OfficialComponent SPRING_AI_AZURE_OPENAI = new OfficialComponent(167, "spring-ai-azure-openai"); + + public static final OfficialComponent SPRING_AI_COHERE = new OfficialComponent(168, "spring-ai-cohere"); + + public static final OfficialComponent SPRING_AI_DEEPSEEK = new OfficialComponent(169, "spring-ai-deepseek"); + + public static final OfficialComponent SPRING_AI_GOOGLE_GENAI = new OfficialComponent(170, "spring-ai-gcp-genai"); + + public static final OfficialComponent SPRING_AI_VERTEXAI = new OfficialComponent(171, "spring-ai-gcp-vertex-ai"); + + public static final OfficialComponent SPRING_AI_MISTRAL_AI = new OfficialComponent(172, "spring-ai-mistral-ai"); + + public static final OfficialComponent SPRING_AI_OPENAI = new OfficialComponent(173, "spring-ai-openai"); + + public static final OfficialComponent SPRING_AI_HUGGINGFACE = new OfficialComponent(174, "spring-ai-huggingface"); + + public static final OfficialComponent SPRING_AI_MINIMAX = new OfficialComponent(175, "spring-ai-minimax"); + + public static final OfficialComponent SPRING_AI_OLLAMA = new OfficialComponent(176, "spring-ai-ollama"); + + public static final OfficialComponent SPRING_AI_ZHIPU_AI = new OfficialComponent(177, "spring-ai-zhipu-ai"); + + public static final OfficialComponent SPRING_AI = new OfficialComponent(178, "spring-ai"); + } diff --git a/apm-protocol/apm-network/src/main/java/org/apache/skywalking/apm/network/trace/component/command/AsyncProfilerTaskCommand.java b/apm-protocol/apm-network/src/main/java/org/apache/skywalking/apm/network/trace/component/command/AsyncProfilerTaskCommand.java new file mode 100644 index 0000000000..41d6043dee --- /dev/null +++ b/apm-protocol/apm-network/src/main/java/org/apache/skywalking/apm/network/trace/component/command/AsyncProfilerTaskCommand.java @@ -0,0 +1,133 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.network.trace.component.command; + +import org.apache.skywalking.apm.network.common.v3.Command; +import org.apache.skywalking.apm.network.common.v3.KeyStringValuePair; + +import java.util.List; +import java.util.Objects; + +public class AsyncProfilerTaskCommand extends BaseCommand implements Serializable, Deserializable { + public static final Deserializable DESERIALIZER = new AsyncProfilerTaskCommand("", "", 0, null, "", 0); + public static final String NAME = "AsyncProfilerTaskQuery"; + + /** + * async-profiler taskId + */ + private final String taskId; + /** + * run profiling for duration (second) + */ + private final int duration; + /** + * async profiler extended parameters. Here is a table of optional parameters. + * + *

lock[=DURATION] - profile contended locks overflowing the DURATION ns bucket (default: 10us)

+ *

alloc[=BYTES] - profile allocations with BYTES interval

+ *

interval=N - sampling interval in ns (default: 10'000'000, i.e. 10 ms)

+ *

jstackdepth=N - maximum Java stack depth (default: 2048)

+ *

chunksize=N - approximate size of JFR chunk in bytes (default: 100 MB)

+ *

chunktime=N - duration of JFR chunk in seconds (default: 1 hour)

+ * details @see async-profiler argument + */ + private final String execArgs; + /** + * task create time + */ + private final long createTime; + + public AsyncProfilerTaskCommand(String serialNumber, String taskId, int duration, + List events, String execArgs, long createTime) { + super(NAME, serialNumber); + this.taskId = taskId; + this.duration = duration; + this.createTime = createTime; + String comma = ","; + StringBuilder sb = new StringBuilder(); + if (Objects.nonNull(events) && !events.isEmpty()) { + sb.append("event=") + .append(String.join(comma, events)) + .append(comma); + } + if (execArgs != null && !execArgs.isEmpty()) { + sb.append(execArgs); + } + this.execArgs = sb.toString(); + } + + public AsyncProfilerTaskCommand(String serialNumber, String taskId, int duration, + String execArgs, long createTime) { + super(NAME, serialNumber); + this.taskId = taskId; + this.duration = duration; + this.execArgs = execArgs; + this.createTime = createTime; + } + + @Override + public AsyncProfilerTaskCommand deserialize(Command command) { + final List argsList = command.getArgsList(); + String taskId = null; + int duration = 0; + String execArgs = null; + long createTime = 0; + String serialNumber = null; + for (final KeyStringValuePair pair : argsList) { + if ("SerialNumber".equals(pair.getKey())) { + serialNumber = pair.getValue(); + } else if ("TaskId".equals(pair.getKey())) { + taskId = pair.getValue(); + } else if ("Duration".equals(pair.getKey())) { + duration = Integer.parseInt(pair.getValue()); + } else if ("ExecArgs".equals(pair.getKey())) { + execArgs = pair.getValue(); + } else if ("CreateTime".equals(pair.getKey())) { + createTime = Long.parseLong(pair.getValue()); + } + } + return new AsyncProfilerTaskCommand(serialNumber, taskId, duration, execArgs, createTime); + } + + @Override + public Command.Builder serialize() { + final Command.Builder builder = commandBuilder(); + builder.addArgs(KeyStringValuePair.newBuilder().setKey("TaskId").setValue(taskId)) + .addArgs(KeyStringValuePair.newBuilder().setKey("Duration").setValue(String.valueOf(duration))) + .addArgs(KeyStringValuePair.newBuilder().setKey("ExecArgs").setValue(execArgs)) + .addArgs(KeyStringValuePair.newBuilder().setKey("CreateTime").setValue(String.valueOf(createTime))); + return builder; + } + + public String getTaskId() { + return taskId; + } + + public int getDuration() { + return duration; + } + + public String getExecArgs() { + return execArgs; + } + + public long getCreateTime() { + return createTime; + } +} diff --git a/apm-protocol/apm-network/src/main/java/org/apache/skywalking/apm/network/trace/component/command/CommandDeserializer.java b/apm-protocol/apm-network/src/main/java/org/apache/skywalking/apm/network/trace/component/command/CommandDeserializer.java index ff8680bcb3..4fd737ff98 100644 --- a/apm-protocol/apm-network/src/main/java/org/apache/skywalking/apm/network/trace/component/command/CommandDeserializer.java +++ b/apm-protocol/apm-network/src/main/java/org/apache/skywalking/apm/network/trace/component/command/CommandDeserializer.java @@ -27,7 +27,10 @@ public static BaseCommand deserialize(final Command command) { return ProfileTaskCommand.DESERIALIZER.deserialize(command); } else if (ConfigurationDiscoveryCommand.NAME.equals(commandName)) { return ConfigurationDiscoveryCommand.DESERIALIZER.deserialize(command); + } else if (AsyncProfilerTaskCommand.NAME.equals(commandName)) { + return AsyncProfilerTaskCommand.DESERIALIZER.deserialize(command); } + throw new UnsupportedCommandException(command); } diff --git a/apm-protocol/apm-network/src/main/proto b/apm-protocol/apm-network/src/main/proto index d4da569991..07882d57be 160000 --- a/apm-protocol/apm-network/src/main/proto +++ b/apm-protocol/apm-network/src/main/proto @@ -1 +1 @@ -Subproject commit d4da5699915ee52288f8ff1c954decf6363485bc +Subproject commit 07882d57becb37e341f7fc492c11f9f5a5f311cf diff --git a/apm-protocol/pom.xml b/apm-protocol/pom.xml index c8eb4d2759..3cfb939955 100644 --- a/apm-protocol/pom.xml +++ b/apm-protocol/pom.xml @@ -21,7 +21,7 @@ java-agent org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-agent-core/pom.xml b/apm-sniffer/apm-agent-core/pom.xml index ebea951ca8..43670f03ba 100644 --- a/apm-sniffer/apm-agent-core/pom.xml +++ b/apm-sniffer/apm-agent-core/pom.xml @@ -22,7 +22,7 @@ org.apache.skywalking java-agent-sniffer - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT apm-agent-core @@ -34,10 +34,7 @@ UTF-8 ${project.build.outputDirectory}/skywalking-agent-version.properties - 32.0.1-jre 2.6.0 - 2.0.7.Final - 1.4.1.Final 4.9.10 com.google ${shade.package}.${shade.com.google.source} @@ -53,6 +50,8 @@ ${shade.package}.${shade.org.slf4j.source} 1.18.0 1.7.25 + one.profiler + ${shade.package}.${shade.one.profiler.source} @@ -79,7 +78,6 @@ net.bytebuddy byte-buddy-agent - ${bytebuddy.version} test @@ -138,6 +136,10 @@ org.slf4j slf4j-api + + tools.profiler + async-profiler + @@ -244,6 +246,10 @@ ${shade.org.slf4j.source} ${shade.org.slf4j.target} + + ${shade.one.profiler.source} + ${shade.one.profiler.target} + diff --git a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/asyncprofiler/AsyncProfilerDataSender.java b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/asyncprofiler/AsyncProfilerDataSender.java new file mode 100644 index 0000000000..be5af4c360 --- /dev/null +++ b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/asyncprofiler/AsyncProfilerDataSender.java @@ -0,0 +1,196 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.agent.core.asyncprofiler; + +import com.google.protobuf.ByteString; +import io.grpc.Channel; +import io.grpc.stub.ClientCallStreamObserver; +import io.grpc.stub.ClientResponseObserver; +import io.grpc.stub.StreamObserver; +import org.apache.skywalking.apm.agent.core.boot.BootService; +import org.apache.skywalking.apm.agent.core.boot.DefaultImplementor; +import org.apache.skywalking.apm.agent.core.boot.ServiceManager; +import org.apache.skywalking.apm.agent.core.conf.Config; +import org.apache.skywalking.apm.agent.core.logging.api.ILog; +import org.apache.skywalking.apm.agent.core.logging.api.LogManager; +import org.apache.skywalking.apm.agent.core.remote.GRPCChannelListener; +import org.apache.skywalking.apm.agent.core.remote.GRPCChannelManager; +import org.apache.skywalking.apm.agent.core.remote.GRPCChannelStatus; +import org.apache.skywalking.apm.agent.core.remote.GRPCStreamServiceStatus; +import org.apache.skywalking.apm.network.language.asyncprofiler.v10.AsyncProfilerCollectionResponse; +import org.apache.skywalking.apm.network.language.asyncprofiler.v10.AsyncProfilerData; +import org.apache.skywalking.apm.network.language.asyncprofiler.v10.AsyncProfilerMetaData; +import org.apache.skywalking.apm.network.language.asyncprofiler.v10.AsyncProfilerTaskGrpc; +import org.apache.skywalking.apm.network.language.asyncprofiler.v10.AsyncProfilingStatus; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.nio.file.Files; +import java.util.concurrent.TimeUnit; + +import static org.apache.skywalking.apm.agent.core.conf.Config.AsyncProfiler.DATA_CHUNK_SIZE; +import static org.apache.skywalking.apm.agent.core.conf.Config.Collector.GRPC_UPSTREAM_TIMEOUT; + +@DefaultImplementor +public class AsyncProfilerDataSender implements BootService, GRPCChannelListener { + private static final ILog LOGGER = LogManager.getLogger(AsyncProfilerDataSender.class); + + private volatile GRPCChannelStatus status = GRPCChannelStatus.DISCONNECT; + + private volatile AsyncProfilerTaskGrpc.AsyncProfilerTaskStub asyncProfilerTaskStub; + + @Override + public void prepare() throws Throwable { + ServiceManager.INSTANCE.findService(GRPCChannelManager.class).addChannelListener(this); + } + + @Override + public void boot() throws Throwable { + + } + + @Override + public void onComplete() throws Throwable { + + } + + @Override + public void shutdown() throws Throwable { + + } + + @Override + public void statusChanged(GRPCChannelStatus status) { + if (GRPCChannelStatus.CONNECTED.equals(status)) { + Channel channel = ServiceManager.INSTANCE.findService(GRPCChannelManager.class).getChannel(); + asyncProfilerTaskStub = AsyncProfilerTaskGrpc.newStub(channel); + } else { + asyncProfilerTaskStub = null; + } + this.status = status; + } + + public void sendData(AsyncProfilerTask task, File dumpFile) throws IOException, InterruptedException { + if (status != GRPCChannelStatus.CONNECTED) { + return; + } + + try (FileInputStream fileInputStream = new FileInputStream(dumpFile)) { + long fileSize = Files.size(dumpFile.toPath()); + int size = Math.toIntExact(fileSize); + final GRPCStreamServiceStatus status = new GRPCStreamServiceStatus(false); + StreamObserver dataStreamObserver = asyncProfilerTaskStub.withDeadlineAfter( + GRPC_UPSTREAM_TIMEOUT, TimeUnit.SECONDS + ).collect(new ClientResponseObserver() { + ClientCallStreamObserver requestStream; + final byte[] buf = new byte[DATA_CHUNK_SIZE]; + + @Override + public void beforeStart(ClientCallStreamObserver requestStream) { + this.requestStream = requestStream; + } + + @Override + public void onNext(AsyncProfilerCollectionResponse value) { + if (AsyncProfilingStatus.TERMINATED_BY_OVERSIZE.equals(value.getType())) { + LOGGER.warn("JFR is too large to be received by the oap server"); + } else { + try { + int bytesRead; + while ((bytesRead = fileInputStream.read(buf)) != -1) { + AsyncProfilerData asyncProfilerData = AsyncProfilerData.newBuilder() + .setContent(ByteString.copyFrom(buf, 0, bytesRead)) + .build(); + requestStream.onNext(asyncProfilerData); + } + } catch (IOException e) { + LOGGER.error("Failed to read JFR file and failed to upload to oap", e); + } + } + + requestStream.onCompleted(); + } + + @Override + public void onError(Throwable t) { + status.finished(); + LOGGER.error(t, "Send async profiler task data to collector fail with a grpc internal exception."); + ServiceManager.INSTANCE.findService(GRPCChannelManager.class).reportError(t); + } + + @Override + public void onCompleted() { + status.finished(); + } + }); + AsyncProfilerMetaData metaData = AsyncProfilerMetaData.newBuilder() + .setService(Config.Agent.SERVICE_NAME) + .setServiceInstance(Config.Agent.INSTANCE_NAME) + .setType(AsyncProfilingStatus.PROFILING_SUCCESS) + .setContentSize(size) + .setTaskId(task.getTaskId()) + .build(); + AsyncProfilerData asyncProfilerData = AsyncProfilerData.newBuilder().setMetaData(metaData).build(); + dataStreamObserver.onNext(asyncProfilerData); + + status.wait4Finish(); + } + } + + public void sendError(AsyncProfilerTask task, String errorMessage) { + if (status != GRPCChannelStatus.CONNECTED) { + return; + } + final GRPCStreamServiceStatus status = new GRPCStreamServiceStatus(false); + StreamObserver dataStreamObserver = asyncProfilerTaskStub.withDeadlineAfter( + GRPC_UPSTREAM_TIMEOUT, TimeUnit.SECONDS + ).collect(new StreamObserver() { + @Override + public void onNext(AsyncProfilerCollectionResponse value) { + } + + @Override + public void onError(Throwable t) { + status.finished(); + LOGGER.error(t, "Send async profiler task execute error fail with a grpc internal exception."); + ServiceManager.INSTANCE.findService(GRPCChannelManager.class).reportError(t); + } + + @Override + public void onCompleted() { + status.finished(); + } + }); + AsyncProfilerMetaData metaData = AsyncProfilerMetaData.newBuilder() + .setService(Config.Agent.SERVICE_NAME) + .setServiceInstance(Config.Agent.INSTANCE_NAME) + .setTaskId(task.getTaskId()) + .setType(AsyncProfilingStatus.EXECUTION_TASK_ERROR) + .setContentSize(-1) + .build(); + AsyncProfilerData asyncProfilerData = AsyncProfilerData.newBuilder() + .setMetaData(metaData) + .setErrorMessage(errorMessage) + .build(); + dataStreamObserver.onNext(asyncProfilerData); + dataStreamObserver.onCompleted(); + status.wait4Finish(); + } +} diff --git a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/asyncprofiler/AsyncProfilerTask.java b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/asyncprofiler/AsyncProfilerTask.java new file mode 100644 index 0000000000..95948762b2 --- /dev/null +++ b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/asyncprofiler/AsyncProfilerTask.java @@ -0,0 +1,146 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.agent.core.asyncprofiler; + +import one.profiler.AsyncProfiler; +import org.apache.skywalking.apm.agent.core.conf.Config; +import org.apache.skywalking.apm.agent.core.logging.api.ILog; +import org.apache.skywalking.apm.agent.core.logging.api.LogManager; +import org.apache.skywalking.apm.util.StringUtil; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + +public class AsyncProfilerTask { + private static final ILog LOGGER = LogManager.getLogger(AsyncProfilerTask.class); + private static final String COMMA = ","; + /** + * task id + */ + private String taskId; + /** + * async profiler optional extended parameters + * + * @see org.apache.skywalking.apm.network.trace.component.command.AsyncProfilerTaskCommand + */ + private String execArgs; + /** + * run profiling for duration (second) + */ + private int duration; + /** + * The time when oap server created this task + */ + private long createTime; + /** + * tempFile generated by async-profiler execution + */ + private Path tempFile; + + private static String execute(AsyncProfiler asyncProfiler, String args) + throws IllegalArgumentException, IOException { + LOGGER.info("async profiler execute args:{}", args); + return asyncProfiler.execute(args); + } + + /** + * start async profiler + */ + public String start(AsyncProfiler asyncProfiler) throws IOException { + tempFile = getProfilerFilePath(); + StringBuilder startArgs = new StringBuilder(); + startArgs.append("start").append(COMMA); + if (StringUtil.isNotEmpty(execArgs)) { + startArgs.append(execArgs).append(COMMA); + } + startArgs.append("file=").append(tempFile.toString()); + + return execute(asyncProfiler, startArgs.toString()); + } + + /** + * stop async profiler and get file + */ + public File stop(AsyncProfiler asyncProfiler) throws IOException { + LOGGER.info("async profiler process stop and dump file"); + String stopArgs = "stop" + COMMA + "file=" + tempFile.toAbsolutePath(); + execute(asyncProfiler, stopArgs); + return tempFile.toFile(); + } + + /** + * if outputPath is configured, the JFR file will be generated at outputPath, + * otherwise createTemp will be used to create the file + */ + public Path getProfilerFilePath() throws IOException { + if (StringUtil.isNotEmpty(Config.AsyncProfiler.OUTPUT_PATH)) { + Path tempFilePath = Paths.get(Config.AsyncProfiler.OUTPUT_PATH, taskId + getFileExtension()); + return Files.createFile(tempFilePath.toAbsolutePath()); + } else { + return Files.createTempFile(taskId, getFileExtension()); + } + } + + private String getFileExtension() { + return ".jfr"; + } + + public void setExecArgs(String execArgs) { + this.execArgs = execArgs; + } + + public void setDuration(int duration) { + this.duration = duration; + } + + public void setTempFile(Path tempFile) { + this.tempFile = tempFile; + } + + public void setTaskId(String taskId) { + this.taskId = taskId; + } + + public void setCreateTime(long createTime) { + this.createTime = createTime; + } + + public String getExecArgs() { + return execArgs; + } + + public int getDuration() { + return duration; + } + + public Path getTempFile() { + return tempFile; + } + + public String getTaskId() { + return taskId; + } + + public long getCreateTime() { + return createTime; + } +} diff --git a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/asyncprofiler/AsyncProfilerTaskChannelService.java b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/asyncprofiler/AsyncProfilerTaskChannelService.java new file mode 100644 index 0000000000..7a2b26a46a --- /dev/null +++ b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/asyncprofiler/AsyncProfilerTaskChannelService.java @@ -0,0 +1,131 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.agent.core.asyncprofiler; + +import io.grpc.Channel; +import io.grpc.Status; +import io.grpc.StatusRuntimeException; +import org.apache.skywalking.apm.agent.core.boot.BootService; +import org.apache.skywalking.apm.agent.core.boot.DefaultImplementor; +import org.apache.skywalking.apm.agent.core.boot.DefaultNamedThreadFactory; +import org.apache.skywalking.apm.agent.core.boot.ServiceManager; +import org.apache.skywalking.apm.agent.core.commands.CommandService; +import org.apache.skywalking.apm.agent.core.conf.Config; +import org.apache.skywalking.apm.agent.core.logging.api.ILog; +import org.apache.skywalking.apm.agent.core.logging.api.LogManager; +import org.apache.skywalking.apm.agent.core.remote.GRPCChannelListener; +import org.apache.skywalking.apm.agent.core.remote.GRPCChannelManager; +import org.apache.skywalking.apm.agent.core.remote.GRPCChannelStatus; +import org.apache.skywalking.apm.network.common.v3.Commands; +import org.apache.skywalking.apm.network.language.asyncprofiler.v10.AsyncProfilerTaskCommandQuery; +import org.apache.skywalking.apm.network.language.asyncprofiler.v10.AsyncProfilerTaskGrpc; +import org.apache.skywalking.apm.util.RunnableWithExceptionProtection; + +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; + +import static org.apache.skywalking.apm.agent.core.conf.Config.Collector.GRPC_UPSTREAM_TIMEOUT; + +@DefaultImplementor +public class AsyncProfilerTaskChannelService implements BootService, Runnable, GRPCChannelListener { + private static final ILog LOGGER = LogManager.getLogger(AsyncProfilerTaskChannelService.class); + + // channel status + private volatile GRPCChannelStatus status = GRPCChannelStatus.DISCONNECT; + private volatile AsyncProfilerTaskGrpc.AsyncProfilerTaskBlockingStub asyncProfilerTaskBlockingStub; + + // query task schedule + private volatile ScheduledFuture getTaskFuture; + + @Override + public void run() { + if (status == GRPCChannelStatus.CONNECTED) { + try { + // test start command and 10s after put stop command + long lastCommandCreateTime = ServiceManager.INSTANCE + .findService(AsyncProfilerTaskExecutionService.class).getLastCommandCreateTime(); + + AsyncProfilerTaskCommandQuery query = AsyncProfilerTaskCommandQuery.newBuilder() + .setServiceInstance(Config.Agent.INSTANCE_NAME) + .setService(Config.Agent.SERVICE_NAME) + .setLastCommandTime(lastCommandCreateTime) + .build(); + Commands commands = asyncProfilerTaskBlockingStub.withDeadlineAfter(GRPC_UPSTREAM_TIMEOUT, TimeUnit.SECONDS) + .getAsyncProfilerTaskCommands(query); + ServiceManager.INSTANCE.findService(CommandService.class).receiveCommand(commands); + } catch (Throwable t) { + if (!(t instanceof StatusRuntimeException)) { + LOGGER.error(t, "fail to query async-profiler task from backend"); + return; + } + final StatusRuntimeException statusRuntimeException = (StatusRuntimeException) t; + if (Status.Code.UNIMPLEMENTED.equals(statusRuntimeException.getStatus().getCode())) { + LOGGER.warn("Backend doesn't support async-profiler, async-profiler will be disabled"); + if (getTaskFuture != null) { + getTaskFuture.cancel(true); + } + } + } + } + } + + @Override + public void statusChanged(GRPCChannelStatus status) { + if (GRPCChannelStatus.CONNECTED.equals(status)) { + Channel channel = ServiceManager.INSTANCE.findService(GRPCChannelManager.class).getChannel(); + asyncProfilerTaskBlockingStub = AsyncProfilerTaskGrpc.newBlockingStub(channel); + } else { + asyncProfilerTaskBlockingStub = null; + } + this.status = status; + } + + @Override + public void prepare() throws Throwable { + ServiceManager.INSTANCE.findService(GRPCChannelManager.class).addChannelListener(this); + } + + @Override + public void boot() throws Throwable { + + if (Config.AsyncProfiler.ACTIVE) { + getTaskFuture = Executors.newSingleThreadScheduledExecutor( + new DefaultNamedThreadFactory("AsyncProfilerGetTaskService") + ).scheduleWithFixedDelay( + new RunnableWithExceptionProtection( + this, + t -> LOGGER.error("Query async profiler task list failure.", t) + ), 0, Config.Collector.GET_PROFILE_TASK_INTERVAL, TimeUnit.SECONDS + ); + } + } + + @Override + public void onComplete() throws Throwable { + + } + + @Override + public void shutdown() throws Throwable { + if (getTaskFuture != null) { + getTaskFuture.cancel(true); + } + } +} diff --git a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/asyncprofiler/AsyncProfilerTaskExecutionService.java b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/asyncprofiler/AsyncProfilerTaskExecutionService.java new file mode 100644 index 0000000000..b5d7a5b643 --- /dev/null +++ b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/asyncprofiler/AsyncProfilerTaskExecutionService.java @@ -0,0 +1,153 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.agent.core.asyncprofiler; + +import one.profiler.AsyncProfiler; +import org.apache.skywalking.apm.agent.core.boot.BootService; +import org.apache.skywalking.apm.agent.core.boot.DefaultImplementor; +import org.apache.skywalking.apm.agent.core.boot.DefaultNamedThreadFactory; +import org.apache.skywalking.apm.agent.core.boot.ServiceManager; +import org.apache.skywalking.apm.agent.core.logging.api.ILog; +import org.apache.skywalking.apm.agent.core.logging.api.LogManager; + +import java.io.File; +import java.io.IOException; +import java.util.Objects; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; + +@DefaultImplementor +public class AsyncProfilerTaskExecutionService implements BootService { + + private static final ILog LOGGER = LogManager.getLogger(AsyncProfilerTaskChannelService.class); + + private AsyncProfiler asyncProfilerInstance; + + private static final String SUCCESS_RESULT = "Profiling started\n"; + + // profile executor thread pool, only running one thread + private volatile ScheduledExecutorService asyncProfilerExecutor; + + // last command create time, use to next query task list + private volatile long lastCommandCreateTime = -1; + + // task schedule future + private volatile ScheduledFuture scheduledFuture; + + public void processAsyncProfilerTask(AsyncProfilerTask task) { + if (task.getCreateTime() <= lastCommandCreateTime) { + LOGGER.warn("get repeat task because createTime is less than lastCommandCreateTime"); + return; + } + lastCommandCreateTime = task.getCreateTime(); + LOGGER.info("add async profiler task: {}", task.getTaskId()); + // add task to list + getAsyncProfilerExecutor().execute(() -> { + try { + if (Objects.nonNull(scheduledFuture) && !scheduledFuture.isDone()) { + LOGGER.info("AsyncProfilerTask already running"); + return; + } + + String result = task.start(getAsyncProfiler()); + if (!SUCCESS_RESULT.equals(result)) { + stopWhenError(task, result); + return; + } + scheduledFuture = getAsyncProfilerExecutor().schedule( + () -> stopWhenSuccess(task), task.getDuration(), TimeUnit.SECONDS + ); + } catch (IOException e) { + LOGGER.error("AsyncProfilerTask executor error:" + e.getMessage(), e); + } + }); + } + + private void stopWhenError(AsyncProfilerTask task, String errorMessage) { + LOGGER.error("AsyncProfilerTask fails to start: " + errorMessage); + AsyncProfilerDataSender dataSender = ServiceManager.INSTANCE.findService(AsyncProfilerDataSender.class); + dataSender.sendError(task, errorMessage); + } + + private void stopWhenSuccess(AsyncProfilerTask task) { + // stop task and send data + try { + File dumpFile = task.stop(getAsyncProfiler()); + if (dumpFile != null && dumpFile.exists()) { + AsyncProfilerDataSender dataSender = ServiceManager.INSTANCE.findService(AsyncProfilerDataSender.class); + dataSender.sendData(task, dumpFile); + if (!dumpFile.delete()) { + LOGGER.warn("Fail to delete the dump file of async profiler."); + } + } + } catch (Exception e) { + LOGGER.error("stop async profiler task error", e); + return; + } + } + + public long getLastCommandCreateTime() { + return lastCommandCreateTime; + } + + @Override + public void prepare() throws Throwable { + + } + + @Override + public void boot() throws Throwable { + + } + + @Override + public void onComplete() throws Throwable { + + } + + @Override + public void shutdown() throws Throwable { + getAsyncProfilerExecutor().shutdown(); + if (Objects.nonNull(scheduledFuture)) { + scheduledFuture.cancel(true); + scheduledFuture = null; + } + } + + private AsyncProfiler getAsyncProfiler() { + if (asyncProfilerInstance == null) { + asyncProfilerInstance = AsyncProfiler.getInstance(); + } + return asyncProfilerInstance; + } + + private ScheduledExecutorService getAsyncProfilerExecutor() { + if (asyncProfilerExecutor == null) { + synchronized (this) { + if (asyncProfilerExecutor == null) { + asyncProfilerExecutor = Executors.newSingleThreadScheduledExecutor( + new DefaultNamedThreadFactory("ASYNC-PROFILING-TASK")); + } + } + } + return asyncProfilerExecutor; + } +} diff --git a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/boot/ServiceManager.java b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/boot/ServiceManager.java index ab3042646d..015596ad26 100644 --- a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/boot/ServiceManager.java +++ b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/boot/ServiceManager.java @@ -25,6 +25,7 @@ import java.util.List; import java.util.Map; import java.util.ServiceLoader; +import lombok.Getter; import org.apache.skywalking.apm.agent.core.logging.api.ILog; import org.apache.skywalking.apm.agent.core.logging.api.LogManager; import org.apache.skywalking.apm.agent.core.plugin.loader.AgentClassLoader; @@ -37,6 +38,8 @@ public enum ServiceManager { private static final ILog LOGGER = LogManager.getLogger(ServiceManager.class); private Map bootedServices = Collections.emptyMap(); + @Getter + private volatile boolean isBooted = false; public void boot() { bootedServices = loadAllServices(); @@ -127,6 +130,7 @@ private void onComplete() { LOGGER.error(e, "Service [{}] AfterBoot process fails.", service.getClass().getName()); } } + isBooted = true; } /** diff --git a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/commands/CommandExecutorService.java b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/commands/CommandExecutorService.java index 819b0b9ff1..c619051c9b 100755 --- a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/commands/CommandExecutorService.java +++ b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/commands/CommandExecutorService.java @@ -21,9 +21,11 @@ import java.util.Map; import org.apache.skywalking.apm.agent.core.boot.BootService; import org.apache.skywalking.apm.agent.core.boot.DefaultImplementor; +import org.apache.skywalking.apm.agent.core.commands.executor.AsyncProfilerCommandExecutor; import org.apache.skywalking.apm.agent.core.commands.executor.ConfigurationDiscoveryCommandExecutor; import org.apache.skywalking.apm.agent.core.commands.executor.NoopCommandExecutor; import org.apache.skywalking.apm.agent.core.commands.executor.ProfileTaskCommandExecutor; +import org.apache.skywalking.apm.network.trace.component.command.AsyncProfilerTaskCommand; import org.apache.skywalking.apm.network.trace.component.command.BaseCommand; import org.apache.skywalking.apm.network.trace.component.command.ConfigurationDiscoveryCommand; import org.apache.skywalking.apm.network.trace.component.command.ProfileTaskCommand; @@ -48,6 +50,9 @@ public void prepare() throws Throwable { //Get ConfigurationDiscoveryCommand executor. commandExecutorMap.put(ConfigurationDiscoveryCommand.NAME, new ConfigurationDiscoveryCommandExecutor()); + + // AsyncProfiler task executor + commandExecutorMap.put(AsyncProfilerTaskCommand.NAME, new AsyncProfilerCommandExecutor()); } @Override diff --git a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/commands/executor/AsyncProfilerCommandExecutor.java b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/commands/executor/AsyncProfilerCommandExecutor.java new file mode 100644 index 0000000000..530b655f84 --- /dev/null +++ b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/commands/executor/AsyncProfilerCommandExecutor.java @@ -0,0 +1,44 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.agent.core.commands.executor; + +import org.apache.skywalking.apm.agent.core.asyncprofiler.AsyncProfilerTask; +import org.apache.skywalking.apm.agent.core.asyncprofiler.AsyncProfilerTaskExecutionService; +import org.apache.skywalking.apm.agent.core.boot.ServiceManager; +import org.apache.skywalking.apm.agent.core.commands.CommandExecutionException; +import org.apache.skywalking.apm.agent.core.commands.CommandExecutor; +import org.apache.skywalking.apm.agent.core.conf.Config; +import org.apache.skywalking.apm.network.trace.component.command.AsyncProfilerTaskCommand; +import org.apache.skywalking.apm.network.trace.component.command.BaseCommand; + +public class AsyncProfilerCommandExecutor implements CommandExecutor { + @Override + public void execute(BaseCommand command) throws CommandExecutionException { + AsyncProfilerTaskCommand asyncProfilerTaskCommand = (AsyncProfilerTaskCommand) command; + + AsyncProfilerTask asyncProfilerTask = new AsyncProfilerTask(); + asyncProfilerTask.setTaskId(asyncProfilerTaskCommand.getTaskId()); + int duration = Math.min(Config.AsyncProfiler.MAX_DURATION, asyncProfilerTaskCommand.getDuration()); + asyncProfilerTask.setDuration(duration); + asyncProfilerTask.setExecArgs(asyncProfilerTaskCommand.getExecArgs()); + asyncProfilerTask.setCreateTime(asyncProfilerTaskCommand.getCreateTime()); + ServiceManager.INSTANCE.findService(AsyncProfilerTaskExecutionService.class) + .processAsyncProfilerTask(asyncProfilerTask); + } +} diff --git a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/conf/Config.java b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/conf/Config.java index be7d54a4e4..87cf57955b 100755 --- a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/conf/Config.java +++ b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/conf/Config.java @@ -97,6 +97,11 @@ public static class Agent { */ public static int TRACE_SEGMENT_REF_LIMIT_PER_SPAN = 500; + /** + * The max number of logs in a single span to keep memory cost estimatable. + */ + public static int LOG_LIMIT_PER_SPAN = 300; + /** * The max number of spans in a single segment. Through this config item, SkyWalking keep your application * memory cost estimated. @@ -252,6 +257,33 @@ public static class Profile { public static int SNAPSHOT_TRANSPORT_BUFFER_SIZE = 500; } + public static class AsyncProfiler { + /** + * If true, async profiler will be enabled when user creates a new async profiler task. + * If false, it will be disabled. + * The default value is true. + */ + public static boolean ACTIVE = true; + + /** + * Max execution time(second) for the Async Profiler. The task will be stopped even if a longer time is specified. + * default 20min. + */ + public static int MAX_DURATION = 1200; + + /** + * Path for the JFR outputs from the Async Profiler. + * If the parameter is not empty, the file will be created in the specified directory, + * otherwise the Files.createTemp method will be used to create the file. + */ + public static String OUTPUT_PATH = ""; + + /** + * The size of the chunk when uploading jfr + */ + public static final int DATA_CHUNK_SIZE = 1024 * 1024; + } + public static class Meter { /** * If true, skywalking agent will enable sending meters. Otherwise disable meter report. diff --git a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/context/AbstractTracerContext.java b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/context/AbstractTracerContext.java index a212e126e9..01d8ba99d2 100644 --- a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/context/AbstractTracerContext.java +++ b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/context/AbstractTracerContext.java @@ -138,4 +138,10 @@ public interface AbstractTracerContext { * Get current primary endpoint name */ String getPrimaryEndpointName(); + + /** + * Change the current context to be in ignoring status. + */ + AbstractTracerContext forceIgnoring(); + } diff --git a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/context/ContextManager.java b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/context/ContextManager.java index c1ed28fd9d..1da79e9cce 100644 --- a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/context/ContextManager.java +++ b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/context/ContextManager.java @@ -26,6 +26,7 @@ import org.apache.skywalking.apm.agent.core.logging.api.ILog; import org.apache.skywalking.apm.agent.core.logging.api.LogManager; import org.apache.skywalking.apm.agent.core.sampling.SamplingService; +import org.apache.skywalking.apm.agent.core.so11y.AgentSo11y; import org.apache.skywalking.apm.util.StringUtil; import static org.apache.skywalking.apm.agent.core.conf.Config.Agent.OPERATION_NAME_THRESHOLD; @@ -52,6 +53,7 @@ private static AbstractTracerContext getOrCreate(String operationName, boolean f if (LOGGER.isDebugEnable()) { LOGGER.debug("No operation name, ignore this trace."); } + AgentSo11y.measureTracingContextCreation(forceSampling, true); context = new IgnoredTracerContext(); } else { if (EXTEND_SERVICE == null) { @@ -163,7 +165,14 @@ public static void continued(ContextSnapshot snapshot) { throw new IllegalArgumentException("ContextSnapshot can't be null."); } if (!snapshot.isFromCurrent()) { - get().continued(snapshot); + // Invalid snapshot is only created by {@link IgnoredTracerContext#capture()}. + // When the snapshot is not valid, need to force ignoring the current tracing context. + if (snapshot.isValid()) { + get().continued(snapshot); + } else { + AbstractTracerContext context = get().forceIgnoring(); + CONTEXT.set(context); + } } } diff --git a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/context/ContextManagerExtendService.java b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/context/ContextManagerExtendService.java index 1e408df81b..560d57c660 100644 --- a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/context/ContextManagerExtendService.java +++ b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/context/ContextManagerExtendService.java @@ -18,7 +18,10 @@ package org.apache.skywalking.apm.agent.core.context; -import java.util.Arrays; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + import org.apache.skywalking.apm.agent.core.boot.BootService; import org.apache.skywalking.apm.agent.core.boot.DefaultImplementor; import org.apache.skywalking.apm.agent.core.boot.ServiceManager; @@ -30,12 +33,13 @@ import org.apache.skywalking.apm.agent.core.remote.GRPCChannelManager; import org.apache.skywalking.apm.agent.core.remote.GRPCChannelStatus; import org.apache.skywalking.apm.agent.core.sampling.SamplingService; +import org.apache.skywalking.apm.agent.core.so11y.AgentSo11y; import org.apache.skywalking.apm.util.StringUtil; @DefaultImplementor public class ContextManagerExtendService implements BootService, GRPCChannelListener { - private volatile String[] ignoreSuffixArray = new String[0]; + private volatile Set ignoreSuffixSet; private volatile GRPCChannelStatus status = GRPCChannelStatus.DISCONNECT; @@ -50,7 +54,7 @@ public void prepare() { @Override public void boot() { - ignoreSuffixArray = Config.Agent.IGNORE_SUFFIX.split(","); + ignoreSuffixSet = Stream.of(Config.Agent.IGNORE_SUFFIX.split(",")).collect(Collectors.toSet()); ignoreSuffixPatternsWatcher = new IgnoreSuffixPatternsWatcher("agent.ignore_suffix", this); spanLimitWatcher = new SpanLimitWatcher("agent.span_limit_per_segment"); @@ -78,18 +82,22 @@ public AbstractTracerContext createTraceContext(String operationName, boolean fo * Don't trace anything if the backend is not available. */ if (!Config.Agent.KEEP_TRACING && GRPCChannelStatus.DISCONNECT.equals(status)) { + AgentSo11y.measureTracingContextCreation(forceSampling, true); return new IgnoredTracerContext(); } int suffixIdx = operationName.lastIndexOf("."); - if (suffixIdx > -1 && Arrays.stream(ignoreSuffixArray) - .anyMatch(a -> a.equals(operationName.substring(suffixIdx)))) { + if (suffixIdx > -1 && ignoreSuffixSet.contains(operationName.substring(suffixIdx))) { + AgentSo11y.measureTracingContextCreation(forceSampling, true); context = new IgnoredTracerContext(); } else { SamplingService samplingService = ServiceManager.INSTANCE.findService(SamplingService.class); if (forceSampling || samplingService.trySampling(operationName)) { + AgentSo11y.measureTracingContextCreation(forceSampling, false); context = new TracingContext(operationName, spanLimitWatcher); } else { + AgentSo11y.measureTracingContextCreation(false, true); + AgentSo11y.measureLeakedTracingContext(true); context = new IgnoredTracerContext(); } } @@ -104,7 +112,7 @@ public void statusChanged(final GRPCChannelStatus status) { public void handleIgnoreSuffixPatternsChanged() { if (StringUtil.isNotBlank(ignoreSuffixPatternsWatcher.getIgnoreSuffixPatterns())) { - ignoreSuffixArray = ignoreSuffixPatternsWatcher.getIgnoreSuffixPatterns().split(","); + ignoreSuffixSet = Stream.of(ignoreSuffixPatternsWatcher.getIgnoreSuffixPatterns().split(",")).collect(Collectors.toSet()); } } } diff --git a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/context/IgnoredTracerContext.java b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/context/IgnoredTracerContext.java index 67fabd3e0a..8bf57ee064 100644 --- a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/context/IgnoredTracerContext.java +++ b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/context/IgnoredTracerContext.java @@ -23,6 +23,7 @@ import org.apache.skywalking.apm.agent.core.context.trace.AbstractSpan; import org.apache.skywalking.apm.agent.core.context.trace.NoopSpan; import org.apache.skywalking.apm.agent.core.profile.ProfileStatusContext; +import org.apache.skywalking.apm.agent.core.so11y.AgentSo11y; /** * The IgnoredTracerContext represent a context should be ignored. So it just maintains the stack with an @@ -47,6 +48,13 @@ public IgnoredTracerContext() { this.profileStatusContext = ProfileStatusContext.createWithNone(); } + public IgnoredTracerContext(int stackDepth) { + this.stackDepth = stackDepth; + this.correlationContext = new CorrelationContext(); + this.extensionContext = new ExtensionContext(); + this.profileStatusContext = ProfileStatusContext.createWithNone(); + } + @Override public void inject(ContextCarrier carrier) { this.correlationContext.inject(carrier); @@ -109,6 +117,7 @@ public AbstractSpan activeSpan() { public boolean stopSpan(AbstractSpan span) { stackDepth--; if (stackDepth == 0) { + AgentSo11y.measureTracingContextCompletion(true); ListenerManager.notifyFinish(this); } return stackDepth == 0; @@ -134,6 +143,11 @@ public String getPrimaryEndpointName() { return null; } + @Override + public AbstractTracerContext forceIgnoring() { + return this; + } + public static class ListenerManager { private static List LISTENERS = new LinkedList<>(); diff --git a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/context/TracingContext.java b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/context/TracingContext.java index 305672bb2a..cc1faa9af7 100644 --- a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/context/TracingContext.java +++ b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/context/TracingContext.java @@ -43,6 +43,7 @@ import org.apache.skywalking.apm.agent.core.logging.api.LogManager; import org.apache.skywalking.apm.agent.core.profile.ProfileStatusContext; import org.apache.skywalking.apm.agent.core.profile.ProfileTaskExecutionService; +import org.apache.skywalking.apm.agent.core.so11y.AgentSo11y; import org.apache.skywalking.apm.util.StringUtil; import static org.apache.skywalking.apm.agent.core.conf.Config.Agent.CLUSTER; @@ -419,6 +420,14 @@ public String getPrimaryEndpointName() { return primaryEndpoint.getName(); } + @Override + public AbstractTracerContext forceIgnoring() { + for (AbstractSpan span: activeSpanStack) { + span.forceIgnoring(); + } + return new IgnoredTracerContext(activeSpanStack.size()); + } + /** * Re-check current trace need profiling, encase third part plugin change the operation name. * @@ -452,7 +461,12 @@ private void finish() { } if (isFinishedInMainThread && (!isRunningInAsyncMode || asyncSpanCounter == 0)) { - TraceSegment finishedSegment = segment.finish(isLimitMechanismWorking()); + boolean limitMechanismWorking = isLimitMechanismWorking(); + if (limitMechanismWorking) { + AgentSo11y.measureLeakedTracingContext(false); + } + AgentSo11y.measureTracingContextCompletion(false); + TraceSegment finishedSegment = segment.finish(limitMechanismWorking); TracingContext.ListenerManager.notifyFinish(finishedSegment); running = false; } diff --git a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/context/tag/Tags.java b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/context/tag/Tags.java index e538241368..3d0b9f37cb 100644 --- a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/context/tag/Tags.java +++ b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/context/tag/Tags.java @@ -155,6 +155,101 @@ public static final class HTTP { */ public static final StringTag THREAD_ID = new StringTag(23, "thread.id"); + /** + * THREAD_CARRIER represents the actual operating system thread that carries out the execution of the virtual thread. + */ + public static final StringTag THREAD_CARRIER = new StringTag(24, "thread.carrier"); + + /** + * GEN_AI_OPERATION_NAME represents the name of the operation being performed + */ + public static final StringTag GEN_AI_OPERATION_NAME = new StringTag(25, "gen_ai.operation.name"); + + /** + * GEN_AI_PROVIDER_NAME represents the Generative AI provider as identified by the client or server instrumentation. + */ + public static final StringTag GEN_AI_PROVIDER_NAME = new StringTag(26, "gen_ai.provider.name"); + + /** + * GEN_AI_REQUEST_MODEL represents the name of the GenAI model a request is being made to. + */ + public static final StringTag GEN_AI_REQUEST_MODEL = new StringTag(27, "gen_ai.request.model"); + + /** + * GEN_AI_TOP_K represents the top_k sampling setting for the GenAI request. + */ + public static final StringTag GEN_AI_TOP_K = new StringTag(28, "gen_ai.request.top_k"); + + /** + * GEN_AI_TOP_P represents the top_p sampling setting for the GenAI request. + */ + public static final StringTag GEN_AI_TOP_P = new StringTag(29, "gen_ai.request.top_p"); + + /** + * GEN_AI_TEMPERATURE represents the temperature setting for the GenAI request. + */ + public static final StringTag GEN_AI_TEMPERATURE = new StringTag(30, "gen_ai.request.temperature"); + + /** + * GEN_AI_TOOL_NAME represents the name of the tool utilized by the agent. + */ + public static final StringTag GEN_AI_TOOL_NAME = new StringTag(31, "gen_ai.tool.name"); + + /** + * GEN_AI_TOOL_CALL_ARGUMENTS represents the parameters passed to the tool call. + */ + public static final StringTag GEN_AI_TOOL_CALL_ARGUMENTS = new StringTag(32, "gen_ai.tool.call.arguments"); + + /** + * GEN_AI_TOOL_CALL_RESULT represents the result returned by the tool call (if any and if execution was successful). + */ + public static final StringTag GEN_AI_TOOL_CALL_RESULT = new StringTag(33, "gen_ai.tool.call.result"); + + /** + * GEN_AI_RESPONSE_MODEL represents the name of the model that generated the response. + */ + public static final StringTag GEN_AI_RESPONSE_MODEL = new StringTag(34, "gen_ai.response.model"); + + /** + * GEN_AI_RESPONSE_ID represents the unique identifier for the completion. + */ + public static final StringTag GEN_AI_RESPONSE_ID = new StringTag(35, "gen_ai.response.id"); + + /** + * GEN_AI_USAGE_INPUT_TOKENS represents the number of tokens used in the GenAI input (prompt). + */ + public static final StringTag GEN_AI_USAGE_INPUT_TOKENS = new StringTag(36, "gen_ai.usage.input_tokens"); + + /** + * GEN_AI_USAGE_OUTPUT_TOKENS represents the number of tokens used in the GenAI response (completion). + */ + public static final StringTag GEN_AI_USAGE_OUTPUT_TOKENS = new StringTag(37, "gen_ai.usage.output_tokens"); + + /** + * GEN_AI_USAGE_TOTAL_TOKENS represents the total number of tokens used in the GenAI exchange. + */ + public static final StringTag GEN_AI_CLIENT_TOKEN_USAGE = new StringTag(38, "gen_ai.client.token.usage"); + + /** + * GEN_AI_RESPONSE_FINISH_REASONS represents the array of reasons the model stopped generating tokens. + */ + public static final StringTag GEN_AI_RESPONSE_FINISH_REASONS = new StringTag(39, "gen_ai.response.finish_reasons"); + + /** + * GEN_AI_STREAM_TTFR represents the time to first response (TTFR) for streaming operations. + */ + public static final StringTag GEN_AI_SERVER_TIME_TO_FIRST_TOKEN = new StringTag(40, "gen_ai.server.time_to_first_token"); + + /** + * GEN_AI_INPUT_MESSAGES represents the chat history provided to the model as an input. + */ + public static final StringTag GEN_AI_INPUT_MESSAGES = new StringTag(41, "gen_ai.input.messages"); + + /** + * GEN_AI_OUTPUT_MESSAGES represents the messages returned by the model where each message represents a specific model response (choice, candidate). + */ + public static final StringTag GEN_AI_OUTPUT_MESSAGES = new StringTag(42, "gen_ai.output.messages"); + /** * Creates a {@code StringTag} with the given key and cache it, if it's created before, simply return it without * creating a new one. diff --git a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/context/trace/AbstractSpan.java b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/context/trace/AbstractSpan.java index 91a8c79432..9a1b6c93eb 100644 --- a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/context/trace/AbstractSpan.java +++ b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/context/trace/AbstractSpan.java @@ -124,4 +124,9 @@ public interface AbstractSpan extends AsyncSpan { * Should skip analysis in the backend. */ void skipAnalysis(); + + /** + * Set to ignored status. + */ + void forceIgnoring(); } diff --git a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/context/trace/AbstractTracingSpan.java b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/context/trace/AbstractTracingSpan.java index 83736bd5d8..a8ca96e07a 100644 --- a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/context/trace/AbstractTracingSpan.java +++ b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/context/trace/AbstractTracingSpan.java @@ -63,7 +63,8 @@ public abstract class AbstractTracingSpan implements AbstractSpan { private volatile boolean isAsyncStopped = false; /** - * The context to which the span belongs + * The context to which the span belongs. + * This should not be called when {@link #ignored} is true. */ protected final TracingContext owner; @@ -99,6 +100,11 @@ public abstract class AbstractTracingSpan implements AbstractSpan { */ protected boolean skipAnalysis; + /** + * The ignore flag of this span. + */ + protected boolean ignored; + protected AbstractTracingSpan(int spanId, int parentSpanId, String operationName, TracingContext owner) { this.operationName = operationName; this.spanId = spanId; @@ -169,6 +175,9 @@ public AbstractTracingSpan log(Throwable t) { if (!errorOccurred && ServiceManager.INSTANCE.findService(StatusCheckService.class).isError(t)) { errorOccurred(); } + if (logs.size() >= Config.Agent.LOG_LIMIT_PER_SPAN) { + return this; + } logs.add(new LogDataEntity.Builder().add(new KeyValuePair("event", "error")) .add(new KeyValuePair("error.kind", t.getClass().getName())) .add(new KeyValuePair("message", t.getMessage())) @@ -190,6 +199,9 @@ public AbstractTracingSpan log(long timestampMicroseconds, Map fields if (logs == null) { logs = new LinkedList<>(); } + if (logs.size() >= Config.Agent.LOG_LIMIT_PER_SPAN) { + return this; + } LogDataEntity.Builder builder = new LogDataEntity.Builder(); for (Map.Entry entry : fields.entrySet()) { builder.add(new KeyValuePair(entry.getKey(), entry.getValue().toString())); @@ -318,7 +330,9 @@ public AbstractSpan prepareForAsync() { if (isInAsyncMode) { throw new RuntimeException("Prepare for async repeatedly. Span is already in async mode."); } - ContextManager.awaitFinishAsync(this); + if (!ignored) { + ContextManager.awaitFinishAsync(this); + } isInAsyncMode = true; return this; } @@ -332,13 +346,18 @@ public AbstractSpan asyncFinish() { throw new RuntimeException("Can not do async finish for the span repeatedly."); } this.endTime = System.currentTimeMillis(); - owner.asyncStop(this); + if (!ignored) { + owner.asyncStop(this); + } isAsyncStopped = true; return this; } @Override public boolean isProfiling() { + if (ignored) { + return false; + } return this.owner.profileStatus().isProfiling(); } @@ -346,4 +365,9 @@ public boolean isProfiling() { public void skipAnalysis() { this.skipAnalysis = true; } + + @Override + public void forceIgnoring() { + this.ignored = true; + } } diff --git a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/context/trace/ExitSpan.java b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/context/trace/ExitSpan.java index e61a8d1cab..18e391be4e 100644 --- a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/context/trace/ExitSpan.java +++ b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/context/trace/ExitSpan.java @@ -111,7 +111,9 @@ public String getPeer() { @Override public ExitSpan inject(final ContextCarrier carrier) { - this.owner.inject(this, carrier); + if (!ignored) { + this.owner.inject(this, carrier); + } return this; } diff --git a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/context/trace/NoopSpan.java b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/context/trace/NoopSpan.java index c67148abea..59dfb1a78a 100644 --- a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/context/trace/NoopSpan.java +++ b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/context/trace/NoopSpan.java @@ -123,6 +123,10 @@ public boolean isProfiling() { public void skipAnalysis() { } + @Override + public void forceIgnoring() { + } + @Override public AbstractSpan prepareForAsync() { return this; diff --git a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/context/trace/SpanLayer.java b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/context/trace/SpanLayer.java index 4ee9395ac9..c31fdc4cc1 100644 --- a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/context/trace/SpanLayer.java +++ b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/context/trace/SpanLayer.java @@ -19,7 +19,8 @@ package org.apache.skywalking.apm.agent.core.context.trace; public enum SpanLayer { - DB(1), RPC_FRAMEWORK(2), HTTP(3), MQ(4), CACHE(5); + DB(1), RPC_FRAMEWORK(2), HTTP(3), MQ(4), CACHE(5), + GEN_AI(7); private int code; @@ -50,4 +51,8 @@ public static void asHttp(AbstractSpan span) { public static void asMQ(AbstractSpan span) { span.setLayer(SpanLayer.MQ); } + + public static void asGenAI(AbstractSpan span) { + span.setLayer(SpanLayer.GEN_AI); + } } diff --git a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/logging/core/AbstractLogger.java b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/logging/core/AbstractLogger.java index 20b55589f9..4020359d0a 100644 --- a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/logging/core/AbstractLogger.java +++ b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/logging/core/AbstractLogger.java @@ -32,7 +32,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.regex.Matcher; /** * An abstract class to simplify the real implementation of the loggers. @@ -189,18 +188,17 @@ protected String replaceParam(String message, Object... parameters) { int startSize = 0; int parametersIndex = 0; int index; - String tmpMessage = message; - while ((index = message.indexOf("{}", startSize)) != -1) { + StringBuilder sb = new StringBuilder(message); + while ((index = sb.indexOf("{}", startSize)) != -1) { if (parametersIndex >= parameters.length) { break; } - /** - * @Fix the Illegal group reference issue - */ - tmpMessage = tmpMessage.replaceFirst("\\{\\}", Matcher.quoteReplacement(String.valueOf(parameters[parametersIndex++]))); - startSize = index + 2; + + String replaced = String.valueOf(parameters[parametersIndex++]); + sb.replace(index, index + 2, replaced); + startSize = index + replaced.length(); } - return tmpMessage; + return sb.toString(); } protected void logger(LogLevel level, String message, Throwable e) { diff --git a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/logging/core/FileWriter.java b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/logging/core/FileWriter.java index 4020b7a98a..adc7194e45 100644 --- a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/logging/core/FileWriter.java +++ b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/logging/core/FileWriter.java @@ -97,6 +97,7 @@ public void run() { public void handle(Throwable t) { } }), "SkywalkingAgent-LogFileWriter"); + logFlusherThread.setDaemon(true); logFlusherThread.start(); } diff --git a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/AbstractClassEnhancePluginDefine.java b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/AbstractClassEnhancePluginDefine.java index 949f8d5813..f0ab14a75b 100644 --- a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/AbstractClassEnhancePluginDefine.java +++ b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/AbstractClassEnhancePluginDefine.java @@ -43,10 +43,22 @@ public abstract class AbstractClassEnhancePluginDefine { private static final ILog LOGGER = LogManager.getLogger(AbstractClassEnhancePluginDefine.class); + /** + * plugin name defined in skywalking-plugin.def + */ + private String pluginName; /** * New field name. */ public static final String CONTEXT_ATTR_NAME = "_$EnhancedClassField_ws"; + /** + * Getter method name. + */ + public static final String CONTEXT_GETTER_NAME = "getSkyWalkingDynamicField"; + /** + * Setter method name. + */ + public static final String CONTEXT_SETTER_NAME = "setSkyWalkingDynamicField"; /** * Main entrance of enhancing the class. @@ -199,4 +211,17 @@ public boolean isBootstrapInstrumentation() { * @return collections of {@link InstanceMethodsInterceptV2Point} */ public abstract StaticMethodsInterceptV2Point[] getStaticMethodsInterceptV2Points(); + + /** + * plugin name should be set after create PluginDefine instance + * + * @param pluginName key defined in skywalking-plugin.def + */ + protected void setPluginName(final String pluginName) { + this.pluginName = pluginName; + } + + public String getPluginName() { + return pluginName; + } } diff --git a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/PluginBootstrap.java b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/PluginBootstrap.java index 7d54eebd73..bea0c3a7fa 100644 --- a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/PluginBootstrap.java +++ b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/PluginBootstrap.java @@ -33,6 +33,15 @@ public class PluginBootstrap { private static final ILog LOGGER = LogManager.getLogger(PluginBootstrap.class); + // Preload ThreadLocalRandom in case of intermittent ClassCircularityError since ByteBuddy 1.12.11 + static { + try { + Class.forName("java.util.concurrent.ThreadLocalRandom"); + } catch (Exception e) { + LOGGER.warn(e, "Preload ThreadLocalRandom failure."); + } + } + /** * load all plugins. * @@ -65,6 +74,7 @@ public List loadPlugins() throws AgentPackageN LOGGER.debug("loading plugin class {}.", pluginDefine.getDefineClass()); AbstractClassEnhancePluginDefine plugin = (AbstractClassEnhancePluginDefine) Class.forName(pluginDefine.getDefineClass(), true, AgentClassLoader .getDefault()).newInstance(); + plugin.setPluginName(pluginDefine.getName()); plugins.add(plugin); } catch (Throwable t) { LOGGER.error(t, "load plugin [{}] failure.", pluginDefine.getDefineClass()); diff --git a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/PluginFinder.java b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/PluginFinder.java index 4ef608dccc..5a7404e729 100644 --- a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/PluginFinder.java +++ b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/PluginFinder.java @@ -95,13 +95,14 @@ public boolean matches(NamedElement target) { return nameMatchDefine.containsKey(target.getActualName()); } }; - judge = judge.and(not(isInterface())); for (AbstractClassEnhancePluginDefine define : signatureMatchDefine) { ClassMatch match = define.enhanceClass(); if (match instanceof IndirectMatch) { judge = judge.or(((IndirectMatch) match).buildJunction()); } } + // Filter out all matchers returns to exclude pure interface types. + judge = not(isInterface()).and(judge); return new ProtectiveShieldMatcher(judge); } diff --git a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/bootstrap/BootstrapInstrumentBoost.java b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/bootstrap/BootstrapInstrumentBoost.java index 523340a401..f5875a7616 100644 --- a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/bootstrap/BootstrapInstrumentBoost.java +++ b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/bootstrap/BootstrapInstrumentBoost.java @@ -71,6 +71,9 @@ public class BootstrapInstrumentBoost { "org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.v2.InstanceMethodsAroundInterceptorV2", "org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.v2.StaticMethodsAroundInterceptorV2", "org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.v2.MethodInvocationContext", + + //SO11Y + "org.apache.skywalking.apm.agent.core.so11y.bootstrap.BootstrapPluginSo11y" }; private static String INSTANCE_METHOD_DELEGATE_TEMPLATE = "org.apache.skywalking.apm.agent.core.plugin.bootstrap.template.InstanceMethodInterTemplate"; @@ -162,11 +165,14 @@ private static boolean prepareJREInstrumentation(PluginFinder pluginFinder, for (InstanceMethodsInterceptPoint point : define.getInstanceMethodsInterceptPoints()) { if (point.isOverrideArgs()) { generateDelegator( - classesTypeMap, typePool, INSTANCE_METHOD_WITH_OVERRIDE_ARGS_DELEGATE_TEMPLATE, point - .getMethodsInterceptor()); + classesTypeMap, typePool, define.getPluginName(), + INSTANCE_METHOD_WITH_OVERRIDE_ARGS_DELEGATE_TEMPLATE, point.getMethodsInterceptor() + ); } else { generateDelegator( - classesTypeMap, typePool, INSTANCE_METHOD_DELEGATE_TEMPLATE, point.getMethodsInterceptor()); + classesTypeMap, typePool, define.getPluginName(), + INSTANCE_METHOD_DELEGATE_TEMPLATE, point.getMethodsInterceptor() + ); } } } @@ -174,7 +180,9 @@ private static boolean prepareJREInstrumentation(PluginFinder pluginFinder, if (Objects.nonNull(define.getConstructorsInterceptPoints())) { for (ConstructorInterceptPoint point : define.getConstructorsInterceptPoints()) { generateDelegator( - classesTypeMap, typePool, CONSTRUCTOR_DELEGATE_TEMPLATE, point.getConstructorInterceptor()); + classesTypeMap, typePool, define.getPluginName(), + CONSTRUCTOR_DELEGATE_TEMPLATE, point.getConstructorInterceptor() + ); } } @@ -182,11 +190,14 @@ private static boolean prepareJREInstrumentation(PluginFinder pluginFinder, for (StaticMethodsInterceptPoint point : define.getStaticMethodsInterceptPoints()) { if (point.isOverrideArgs()) { generateDelegator( - classesTypeMap, typePool, STATIC_METHOD_WITH_OVERRIDE_ARGS_DELEGATE_TEMPLATE, point - .getMethodsInterceptor()); + classesTypeMap, typePool, define.getPluginName(), + STATIC_METHOD_WITH_OVERRIDE_ARGS_DELEGATE_TEMPLATE, point.getMethodsInterceptor() + ); } else { generateDelegator( - classesTypeMap, typePool, STATIC_METHOD_DELEGATE_TEMPLATE, point.getMethodsInterceptor()); + classesTypeMap, typePool, define.getPluginName(), + STATIC_METHOD_DELEGATE_TEMPLATE, point.getMethodsInterceptor() + ); } } } @@ -202,14 +213,14 @@ private static boolean prepareJREInstrumentationV2(PluginFinder pluginFinder, if (Objects.nonNull(define.getInstanceMethodsInterceptV2Points())) { for (InstanceMethodsInterceptV2Point point : define.getInstanceMethodsInterceptV2Points()) { if (point.isOverrideArgs()) { - generateDelegator(classesTypeMap, typePool, - INSTANCE_METHOD_V2_WITH_OVERRIDE_ARGS_DELEGATE_TEMPLATE, - point.getMethodsInterceptorV2() + generateDelegator( + classesTypeMap, typePool, define.getPluginName(), + INSTANCE_METHOD_V2_WITH_OVERRIDE_ARGS_DELEGATE_TEMPLATE, point.getMethodsInterceptorV2() ); } else { generateDelegator( - classesTypeMap, typePool, INSTANCE_METHOD_V2_DELEGATE_TEMPLATE, - point.getMethodsInterceptorV2() + classesTypeMap, typePool, define.getPluginName(), + INSTANCE_METHOD_V2_DELEGATE_TEMPLATE, point.getMethodsInterceptorV2() ); } } @@ -218,14 +229,14 @@ private static boolean prepareJREInstrumentationV2(PluginFinder pluginFinder, if (Objects.nonNull(define.getStaticMethodsInterceptV2Points())) { for (StaticMethodsInterceptV2Point point : define.getStaticMethodsInterceptV2Points()) { if (point.isOverrideArgs()) { - generateDelegator(classesTypeMap, typePool, - STATIC_METHOD_V2_WITH_OVERRIDE_ARGS_DELEGATE_TEMPLATE, - point.getMethodsInterceptorV2() + generateDelegator( + classesTypeMap, typePool, define.getPluginName(), + STATIC_METHOD_V2_WITH_OVERRIDE_ARGS_DELEGATE_TEMPLATE, point.getMethodsInterceptorV2() ); } else { generateDelegator( - classesTypeMap, typePool, STATIC_METHOD_V2_DELEGATE_TEMPLATE, - point.getMethodsInterceptorV2() + classesTypeMap, typePool, define.getPluginName(), + STATIC_METHOD_V2_DELEGATE_TEMPLATE, point.getMethodsInterceptorV2() ); } } @@ -245,7 +256,7 @@ private static boolean prepareJREInstrumentationV2(PluginFinder pluginFinder, * pre-defined in SkyWalking agent core. */ private static void generateDelegator(Map classesTypeMap, TypePool typePool, - String templateClassName, String methodsInterceptor) { + String pluginName, String templateClassName, String methodsInterceptor) { String internalInterceptorName = internalDelegate(methodsInterceptor); try { TypeDescription templateTypeDescription = typePool.describe(templateClassName).resolve(); @@ -253,6 +264,8 @@ private static void generateDelegator(Map classesTypeMap, TypePo DynamicType.Unloaded interceptorType = new ByteBuddy().redefine(templateTypeDescription, ClassFileLocator.ForClassLoader .of(BootstrapInstrumentBoost.class.getClassLoader())) .name(internalInterceptorName) + .field(named("PLUGIN_NAME")) + .value(pluginName) .field(named("TARGET_INTERCEPTOR")) .value(methodsInterceptor) .make(); diff --git a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/bootstrap/template/ConstructorInterTemplate.java b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/bootstrap/template/ConstructorInterTemplate.java index 756d03f299..06b7e8132e 100644 --- a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/bootstrap/template/ConstructorInterTemplate.java +++ b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/bootstrap/template/ConstructorInterTemplate.java @@ -25,6 +25,7 @@ import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.BootstrapInterRuntimeAssist; import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceConstructorInterceptor; +import org.apache.skywalking.apm.agent.core.so11y.bootstrap.BootstrapPluginSo11y; /** * --------CLASS TEMPLATE--------- @@ -37,6 +38,12 @@ * This class wouldn't be loaded in real env. This is a class template for dynamic class generation. */ public class ConstructorInterTemplate { + + private static final String INTERCEPTOR_TYPE = "constructor"; + /** + * This field is never set in the template, but has value in the runtime. + */ + private static String PLUGIN_NAME; /** * This field is never set in the template, but has value in the runtime. */ @@ -44,6 +51,7 @@ public class ConstructorInterTemplate { private static InstanceConstructorInterceptor INTERCEPTOR; private static IBootstrapLog LOGGER; + private static BootstrapPluginSo11y PLUGIN_SO11Y; /** * Intercept the target constructor. @@ -53,6 +61,8 @@ public class ConstructorInterTemplate { */ @RuntimeType public static void intercept(@This Object obj, @AllArguments Object[] allArguments) { + long interceptorTimeCost = 0L; + long startTime = System.nanoTime(); try { prepare(); @@ -64,7 +74,10 @@ public static void intercept(@This Object obj, @AllArguments Object[] allArgumen INTERCEPTOR.onConstruct(targetObject, allArguments); } catch (Throwable t) { LOGGER.error("ConstructorInter failure.", t); + PLUGIN_SO11Y.error(PLUGIN_NAME, INTERCEPTOR_TYPE); } + interceptorTimeCost += System.nanoTime() - startTime; + PLUGIN_SO11Y.duration(interceptorTimeCost); } /** @@ -79,6 +92,7 @@ private static void prepare() { if (logger != null) { LOGGER = logger; + PLUGIN_SO11Y = BootstrapInterRuntimeAssist.getSO11Y(loader); INTERCEPTOR = BootstrapInterRuntimeAssist.createInterceptor(loader, TARGET_INTERCEPTOR, LOGGER); } } else { diff --git a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/bootstrap/template/InstanceMethodInterTemplate.java b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/bootstrap/template/InstanceMethodInterTemplate.java index ef70a5722b..62a0c8fac9 100644 --- a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/bootstrap/template/InstanceMethodInterTemplate.java +++ b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/bootstrap/template/InstanceMethodInterTemplate.java @@ -30,6 +30,7 @@ import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceMethodsAroundInterceptor; import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult; +import org.apache.skywalking.apm.agent.core.so11y.bootstrap.BootstrapPluginSo11y; /** * --------CLASS TEMPLATE--------- @@ -42,6 +43,12 @@ * This class wouldn't be loaded in real env. This is a class template for dynamic class generation. */ public class InstanceMethodInterTemplate { + + private static final String INTERCEPTOR_TYPE = "inst"; + /** + * This field is never set in the template, but has value in the runtime. + */ + private static String PLUGIN_NAME; /** * This field is never set in the template, but has value in the runtime. */ @@ -49,6 +56,7 @@ public class InstanceMethodInterTemplate { private static InstanceMethodsAroundInterceptor INTERCEPTOR; private static IBootstrapLog LOGGER; + private static BootstrapPluginSo11y PLUGIN_SO11Y; /** * Intercept the target instance method. @@ -68,6 +76,8 @@ public static Object intercept(@This Object obj, @AllArguments Object[] allArgum prepare(); + long interceptorTimeCost = 0L; + long startTimeOfMethodBeforeInter = System.nanoTime(); MethodInterceptResult result = new MethodInterceptResult(); try { if (INTERCEPTOR != null) { @@ -77,7 +87,9 @@ public static Object intercept(@This Object obj, @AllArguments Object[] allArgum if (LOGGER != null) { LOGGER.error(t, "class[{}] before method[{}] intercept failure", obj.getClass(), method.getName()); } + PLUGIN_SO11Y.error(PLUGIN_NAME, INTERCEPTOR_TYPE); } + interceptorTimeCost += System.nanoTime() - startTimeOfMethodBeforeInter; Object ret = null; try { @@ -87,6 +99,7 @@ public static Object intercept(@This Object obj, @AllArguments Object[] allArgum ret = zuper.call(); } } catch (Throwable t) { + long startTimeOfMethodHandleExceptionInter = System.nanoTime(); try { if (INTERCEPTOR != null) { INTERCEPTOR.handleMethodException(targetObject, method, allArguments, method.getParameterTypes(), t); @@ -95,9 +108,12 @@ public static Object intercept(@This Object obj, @AllArguments Object[] allArgum if (LOGGER != null) { LOGGER.error(t2, "class[{}] handle method[{}] exception failure", obj.getClass(), method.getName()); } + PLUGIN_SO11Y.error(PLUGIN_NAME, INTERCEPTOR_TYPE); } + interceptorTimeCost += System.nanoTime() - startTimeOfMethodHandleExceptionInter; throw t; } finally { + long startTimeOfMethodAfterInter = System.nanoTime(); try { if (INTERCEPTOR != null) { ret = INTERCEPTOR.afterMethod(targetObject, method, allArguments, method.getParameterTypes(), ret); @@ -106,8 +122,11 @@ public static Object intercept(@This Object obj, @AllArguments Object[] allArgum if (LOGGER != null) { LOGGER.error(t, "class[{}] after method[{}] intercept failure", obj.getClass(), method.getName()); } + PLUGIN_SO11Y.error(PLUGIN_NAME, INTERCEPTOR_TYPE); } + interceptorTimeCost += System.nanoTime() - startTimeOfMethodAfterInter; } + PLUGIN_SO11Y.duration(interceptorTimeCost); return ret; } @@ -124,6 +143,7 @@ private static void prepare() { if (logger != null) { LOGGER = logger; + PLUGIN_SO11Y = BootstrapInterRuntimeAssist.getSO11Y(loader); INTERCEPTOR = BootstrapInterRuntimeAssist.createInterceptor(loader, TARGET_INTERCEPTOR, LOGGER); } } else { diff --git a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/bootstrap/template/InstanceMethodInterWithOverrideArgsTemplate.java b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/bootstrap/template/InstanceMethodInterWithOverrideArgsTemplate.java index 1ba0464de0..16053055d3 100644 --- a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/bootstrap/template/InstanceMethodInterWithOverrideArgsTemplate.java +++ b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/bootstrap/template/InstanceMethodInterWithOverrideArgsTemplate.java @@ -30,6 +30,7 @@ import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceMethodsAroundInterceptor; import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult; import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.OverrideCallable; +import org.apache.skywalking.apm.agent.core.so11y.bootstrap.BootstrapPluginSo11y; /** * --------CLASS TEMPLATE--------- @@ -42,6 +43,12 @@ * This class wouldn't be loaded in real env. This is a class template for dynamic class generation. */ public class InstanceMethodInterWithOverrideArgsTemplate { + + private static final String INTERCEPTOR_TYPE = "inst"; + /** + * This field is never set in the template, but has value in the runtime. + */ + private static String PLUGIN_NAME; /** * This field is never set in the template, but has value in the runtime. */ @@ -49,6 +56,7 @@ public class InstanceMethodInterWithOverrideArgsTemplate { private static InstanceMethodsAroundInterceptor INTERCEPTOR; private static IBootstrapLog LOGGER; + private static BootstrapPluginSo11y PLUGIN_SO11Y; /** * Intercept the target instance method. @@ -68,6 +76,8 @@ public static Object intercept(@This Object obj, @AllArguments Object[] allArgum prepare(); + long interceptorTimeCost = 0L; + long startTimeOfMethodBeforeInter = System.nanoTime(); MethodInterceptResult result = new MethodInterceptResult(); try { if (INTERCEPTOR != null) { @@ -77,7 +87,9 @@ public static Object intercept(@This Object obj, @AllArguments Object[] allArgum if (LOGGER != null) { LOGGER.error(t, "class[{}] before method[{}] intercept failure", obj.getClass(), method.getName()); } + PLUGIN_SO11Y.error(PLUGIN_NAME, INTERCEPTOR_TYPE); } + interceptorTimeCost += System.nanoTime() - startTimeOfMethodBeforeInter; Object ret = null; try { @@ -87,6 +99,7 @@ public static Object intercept(@This Object obj, @AllArguments Object[] allArgum ret = zuper.call(allArguments); } } catch (Throwable t) { + long startTimeOfMethodHandleExceptionInter = System.nanoTime(); try { if (INTERCEPTOR != null) { INTERCEPTOR.handleMethodException(targetObject, method, allArguments, method.getParameterTypes(), t); @@ -95,9 +108,12 @@ public static Object intercept(@This Object obj, @AllArguments Object[] allArgum if (LOGGER != null) { LOGGER.error(t2, "class[{}] handle method[{}] exception failure", obj.getClass(), method.getName()); } + PLUGIN_SO11Y.error(PLUGIN_NAME, INTERCEPTOR_TYPE); } + interceptorTimeCost += System.nanoTime() - startTimeOfMethodHandleExceptionInter; throw t; } finally { + long startTimeOfMethodAfterInter = System.nanoTime(); try { if (INTERCEPTOR != null) { ret = INTERCEPTOR.afterMethod(targetObject, method, allArguments, method.getParameterTypes(), ret); @@ -106,8 +122,11 @@ public static Object intercept(@This Object obj, @AllArguments Object[] allArgum if (LOGGER != null) { LOGGER.error(t, "class[{}] after method[{}] intercept failure", obj.getClass(), method.getName()); } + PLUGIN_SO11Y.error(PLUGIN_NAME, INTERCEPTOR_TYPE); } + interceptorTimeCost += System.nanoTime() - startTimeOfMethodAfterInter; } + PLUGIN_SO11Y.duration(interceptorTimeCost); return ret; } @@ -124,6 +143,7 @@ private static void prepare() { if (logger != null) { LOGGER = logger; + PLUGIN_SO11Y = BootstrapInterRuntimeAssist.getSO11Y(loader); INTERCEPTOR = BootstrapInterRuntimeAssist.createInterceptor(loader, TARGET_INTERCEPTOR, LOGGER); } } else { diff --git a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/bootstrap/template/StaticMethodInterTemplate.java b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/bootstrap/template/StaticMethodInterTemplate.java index a4caf96528..e32b1fc554 100644 --- a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/bootstrap/template/StaticMethodInterTemplate.java +++ b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/bootstrap/template/StaticMethodInterTemplate.java @@ -28,6 +28,7 @@ import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.BootstrapInterRuntimeAssist; import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult; import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.StaticMethodsAroundInterceptor; +import org.apache.skywalking.apm.agent.core.so11y.bootstrap.BootstrapPluginSo11y; /** * --------CLASS TEMPLATE--------- @@ -40,6 +41,12 @@ * This class wouldn't be loaded in real env. This is a class template for dynamic class generation. */ public class StaticMethodInterTemplate { + + private static final String INTERCEPTOR_TYPE = "static"; + /** + * This field is never set in the template, but has value in the runtime. + */ + private static String PLUGIN_NAME; /** * This field is never set in the template, but has value in the runtime. */ @@ -47,6 +54,7 @@ public class StaticMethodInterTemplate { private static StaticMethodsAroundInterceptor INTERCEPTOR; private static IBootstrapLog LOGGER; + private static BootstrapPluginSo11y PLUGIN_SO11Y; /** * Intercept the target static method. @@ -64,6 +72,8 @@ public static Object intercept(@Origin Class clazz, @AllArguments Object[] al @SuperCall Callable zuper) throws Throwable { prepare(); + long interceptorTimeCost = 0L; + long startTimeOfMethodBeforeInter = System.nanoTime(); MethodInterceptResult result = new MethodInterceptResult(); try { if (INTERCEPTOR != null) { @@ -71,7 +81,9 @@ public static Object intercept(@Origin Class clazz, @AllArguments Object[] al } } catch (Throwable t) { LOGGER.error(t, "class[{}] before static method[{}] intercept failure", clazz, method.getName()); + PLUGIN_SO11Y.error(PLUGIN_NAME, INTERCEPTOR_TYPE); } + interceptorTimeCost += System.nanoTime() - startTimeOfMethodBeforeInter; Object ret = null; try { @@ -81,23 +93,31 @@ public static Object intercept(@Origin Class clazz, @AllArguments Object[] al ret = zuper.call(); } } catch (Throwable t) { + long startTimeOfMethodHandleExceptionInter = System.nanoTime(); try { if (INTERCEPTOR != null) { INTERCEPTOR.handleMethodException(clazz, method, allArguments, method.getParameterTypes(), t); } } catch (Throwable t2) { LOGGER.error(t2, "class[{}] handle static method[{}] exception failure", clazz, method.getName(), t2.getMessage()); + PLUGIN_SO11Y.error(PLUGIN_NAME, INTERCEPTOR_TYPE); } + interceptorTimeCost += System.nanoTime() - startTimeOfMethodHandleExceptionInter; throw t; } finally { + long startTimeOfMethodAfterInter = System.nanoTime(); try { if (INTERCEPTOR != null) { ret = INTERCEPTOR.afterMethod(clazz, method, allArguments, method.getParameterTypes(), ret); } } catch (Throwable t) { LOGGER.error(t, "class[{}] after static method[{}] intercept failure:{}", clazz, method.getName(), t.getMessage()); + PLUGIN_SO11Y.error(PLUGIN_NAME, INTERCEPTOR_TYPE); } + interceptorTimeCost += System.nanoTime() - startTimeOfMethodAfterInter; } + PLUGIN_SO11Y.duration(interceptorTimeCost); + return ret; } @@ -113,6 +133,7 @@ private static void prepare() { if (logger != null) { LOGGER = logger; + PLUGIN_SO11Y = BootstrapInterRuntimeAssist.getSO11Y(loader); INTERCEPTOR = BootstrapInterRuntimeAssist.createInterceptor(loader, TARGET_INTERCEPTOR, LOGGER); } } else { diff --git a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/bootstrap/template/StaticMethodInterWithOverrideArgsTemplate.java b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/bootstrap/template/StaticMethodInterWithOverrideArgsTemplate.java index 6dd7ac0b00..c182878148 100644 --- a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/bootstrap/template/StaticMethodInterWithOverrideArgsTemplate.java +++ b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/bootstrap/template/StaticMethodInterWithOverrideArgsTemplate.java @@ -28,6 +28,7 @@ import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult; import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.OverrideCallable; import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.StaticMethodsAroundInterceptor; +import org.apache.skywalking.apm.agent.core.so11y.bootstrap.BootstrapPluginSo11y; /** * --------CLASS TEMPLATE--------- @@ -40,6 +41,12 @@ * This class wouldn't be loaded in real env. This is a class template for dynamic class generation. */ public class StaticMethodInterWithOverrideArgsTemplate { + + private static final String INTERCEPTOR_TYPE = "static"; + /** + * This field is never set in the template, but has value in the runtime. + */ + private static String PLUGIN_NAME; /** * This field is never set in the template, but has value in the runtime. */ @@ -47,6 +54,7 @@ public class StaticMethodInterWithOverrideArgsTemplate { private static StaticMethodsAroundInterceptor INTERCEPTOR; private static IBootstrapLog LOGGER; + private static BootstrapPluginSo11y PLUGIN_SO11Y; /** * Intercept the target static method. @@ -64,6 +72,8 @@ public static Object intercept(@Origin Class clazz, @AllArguments Object[] al @Morph OverrideCallable zuper) throws Throwable { prepare(); + long interceptorTimeCost = 0L; + long startTimeOfMethodBeforeInter = System.nanoTime(); MethodInterceptResult result = new MethodInterceptResult(); try { if (INTERCEPTOR != null) { @@ -71,7 +81,9 @@ public static Object intercept(@Origin Class clazz, @AllArguments Object[] al } } catch (Throwable t) { LOGGER.error(t, "class[{}] before static method[{}] intercept failure", clazz, method.getName()); + PLUGIN_SO11Y.error(PLUGIN_NAME, INTERCEPTOR_TYPE); } + interceptorTimeCost += System.nanoTime() - startTimeOfMethodBeforeInter; Object ret = null; try { @@ -81,23 +93,31 @@ public static Object intercept(@Origin Class clazz, @AllArguments Object[] al ret = zuper.call(allArguments); } } catch (Throwable t) { + long startTimeOfMethodHandleExceptionInter = System.nanoTime(); try { if (INTERCEPTOR != null) { INTERCEPTOR.handleMethodException(clazz, method, allArguments, method.getParameterTypes(), t); } } catch (Throwable t2) { LOGGER.error(t2, "class[{}] handle static method[{}] exception failure", clazz, method.getName(), t2.getMessage()); + PLUGIN_SO11Y.error(PLUGIN_NAME, INTERCEPTOR_TYPE); } + interceptorTimeCost += System.nanoTime() - startTimeOfMethodHandleExceptionInter; throw t; } finally { + long startTimeOfMethodAfterInter = System.nanoTime(); try { if (INTERCEPTOR != null) { ret = INTERCEPTOR.afterMethod(clazz, method, allArguments, method.getParameterTypes(), ret); } } catch (Throwable t) { LOGGER.error(t, "class[{}] after static method[{}] intercept failure:{}", clazz, method.getName(), t.getMessage()); + PLUGIN_SO11Y.error(PLUGIN_NAME, INTERCEPTOR_TYPE); } + interceptorTimeCost += System.nanoTime() - startTimeOfMethodAfterInter; } + PLUGIN_SO11Y.duration(interceptorTimeCost); + return ret; } @@ -113,6 +133,7 @@ private static void prepare() { if (logger != null) { LOGGER = logger; + PLUGIN_SO11Y = BootstrapInterRuntimeAssist.getSO11Y(loader); INTERCEPTOR = BootstrapInterRuntimeAssist.createInterceptor(loader, TARGET_INTERCEPTOR, LOGGER); } } else { diff --git a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/bootstrap/template/v2/InstanceMethodInterV2Template.java b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/bootstrap/template/v2/InstanceMethodInterV2Template.java index 88be482f5f..92be67f5f8 100644 --- a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/bootstrap/template/v2/InstanceMethodInterV2Template.java +++ b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/bootstrap/template/v2/InstanceMethodInterV2Template.java @@ -29,12 +29,18 @@ import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.v2.InstanceMethodsAroundInterceptorV2; import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.v2.MethodInvocationContext; +import org.apache.skywalking.apm.agent.core.so11y.bootstrap.BootstrapPluginSo11y; /** * This class wouldn't be loaded in real env. This is a class template for dynamic class generation. */ public class InstanceMethodInterV2Template { + private static final String INTERCEPTOR_TYPE = "inst"; + /** + * This field is never set in the template, but has value in the runtime. + */ + private static String PLUGIN_NAME; /** * This field is never set in the template, but has value in the runtime. */ @@ -42,6 +48,7 @@ public class InstanceMethodInterV2Template { private static InstanceMethodsAroundInterceptorV2 INTERCEPTOR; private static IBootstrapLog LOGGER; + private static BootstrapPluginSo11y PLUGIN_SO11Y; /** * Intercept the target instance method. @@ -61,6 +68,8 @@ public static Object intercept(@This Object obj, @AllArguments Object[] allArgum prepare(); + long interceptorTimeCost = 0L; + long startTimeOfMethodBeforeInter = System.nanoTime(); MethodInvocationContext context = new MethodInvocationContext(); try { if (INTERCEPTOR != null) { @@ -70,7 +79,9 @@ public static Object intercept(@This Object obj, @AllArguments Object[] allArgum if (LOGGER != null) { LOGGER.error(t, "class[{}] before method[{}] intercept failure", obj.getClass(), method.getName()); } + PLUGIN_SO11Y.error(PLUGIN_NAME, INTERCEPTOR_TYPE); } + interceptorTimeCost += System.nanoTime() - startTimeOfMethodBeforeInter; Object ret = null; try { @@ -80,6 +91,7 @@ public static Object intercept(@This Object obj, @AllArguments Object[] allArgum ret = zuper.call(); } } catch (Throwable t) { + long startTimeOfMethodHandleExceptionInter = System.nanoTime(); try { if (INTERCEPTOR != null) { INTERCEPTOR.handleMethodException(targetObject, method, allArguments, method.getParameterTypes(), t, context); @@ -88,9 +100,12 @@ public static Object intercept(@This Object obj, @AllArguments Object[] allArgum if (LOGGER != null) { LOGGER.error(t2, "class[{}] handle method[{}] exception failure", obj.getClass(), method.getName()); } + PLUGIN_SO11Y.error(PLUGIN_NAME, INTERCEPTOR_TYPE); } + interceptorTimeCost += System.nanoTime() - startTimeOfMethodHandleExceptionInter; throw t; } finally { + long startTimeOfMethodAfterInter = System.nanoTime(); try { if (INTERCEPTOR != null) { ret = INTERCEPTOR.afterMethod(targetObject, method, allArguments, method.getParameterTypes(), ret, context); @@ -99,8 +114,11 @@ public static Object intercept(@This Object obj, @AllArguments Object[] allArgum if (LOGGER != null) { LOGGER.error(t, "class[{}] after method[{}] intercept failure", obj.getClass(), method.getName()); } + PLUGIN_SO11Y.error(PLUGIN_NAME, INTERCEPTOR_TYPE); } + interceptorTimeCost += System.nanoTime() - startTimeOfMethodAfterInter; } + PLUGIN_SO11Y.duration(interceptorTimeCost); return ret; } @@ -117,6 +135,7 @@ private static void prepare() { if (logger != null) { LOGGER = logger; + PLUGIN_SO11Y = BootstrapInterRuntimeAssist.getSO11Y(loader); INTERCEPTOR = BootstrapInterRuntimeAssist.createInterceptor(loader, TARGET_INTERCEPTOR, LOGGER); } } else { diff --git a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/bootstrap/template/v2/InstanceMethodInterV2WithOverrideArgsTemplate.java b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/bootstrap/template/v2/InstanceMethodInterV2WithOverrideArgsTemplate.java index 83fc8a9711..b027f086d3 100644 --- a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/bootstrap/template/v2/InstanceMethodInterV2WithOverrideArgsTemplate.java +++ b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/bootstrap/template/v2/InstanceMethodInterV2WithOverrideArgsTemplate.java @@ -30,11 +30,18 @@ import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.OverrideCallable; import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.v2.InstanceMethodsAroundInterceptorV2; import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.v2.MethodInvocationContext; +import org.apache.skywalking.apm.agent.core.so11y.bootstrap.BootstrapPluginSo11y; /** * This class wouldn't be loaded in real env. This is a class template for dynamic class generation. */ public class InstanceMethodInterV2WithOverrideArgsTemplate { + + private static final String INTERCEPTOR_TYPE = "inst"; + /** + * This field is never set in the template, but has value in the runtime. + */ + private static String PLUGIN_NAME; /** * This field is never set in the template, but has value in the runtime. */ @@ -42,6 +49,7 @@ public class InstanceMethodInterV2WithOverrideArgsTemplate { private static InstanceMethodsAroundInterceptorV2 INTERCEPTOR; private static IBootstrapLog LOGGER; + private static BootstrapPluginSo11y PLUGIN_SO11Y; /** * Intercept the target instance method. @@ -61,6 +69,8 @@ public static Object intercept(@This Object obj, @AllArguments Object[] allArgum prepare(); + long interceptorTimeCost = 0L; + long startTimeOfMethodBeforeInter = System.nanoTime(); MethodInvocationContext context = new MethodInvocationContext(); try { if (INTERCEPTOR != null) { @@ -70,7 +80,9 @@ public static Object intercept(@This Object obj, @AllArguments Object[] allArgum if (LOGGER != null) { LOGGER.error(t, "class[{}] before method[{}] intercept failure", obj.getClass(), method.getName()); } + PLUGIN_SO11Y.error(PLUGIN_NAME, INTERCEPTOR_TYPE); } + interceptorTimeCost += System.nanoTime() - startTimeOfMethodBeforeInter; Object ret = null; try { @@ -80,6 +92,7 @@ public static Object intercept(@This Object obj, @AllArguments Object[] allArgum ret = zuper.call(allArguments); } } catch (Throwable t) { + long startTimeOfMethodHandleExceptionInter = System.nanoTime(); try { if (INTERCEPTOR != null) { INTERCEPTOR.handleMethodException(targetObject, method, allArguments, method.getParameterTypes(), t, context); @@ -88,9 +101,12 @@ public static Object intercept(@This Object obj, @AllArguments Object[] allArgum if (LOGGER != null) { LOGGER.error(t2, "class[{}] handle method[{}] exception failure", obj.getClass(), method.getName()); } + PLUGIN_SO11Y.error(PLUGIN_NAME, INTERCEPTOR_TYPE); } + interceptorTimeCost += System.nanoTime() - startTimeOfMethodHandleExceptionInter; throw t; } finally { + long startTimeOfMethodAfterInter = System.nanoTime(); try { if (INTERCEPTOR != null) { ret = INTERCEPTOR.afterMethod(targetObject, method, allArguments, method.getParameterTypes(), ret, context); @@ -99,8 +115,11 @@ public static Object intercept(@This Object obj, @AllArguments Object[] allArgum if (LOGGER != null) { LOGGER.error(t, "class[{}] after method[{}] intercept failure", obj.getClass(), method.getName()); } + PLUGIN_SO11Y.error(PLUGIN_NAME, INTERCEPTOR_TYPE); } + interceptorTimeCost += System.nanoTime() - startTimeOfMethodAfterInter; } + PLUGIN_SO11Y.duration(interceptorTimeCost); return ret; } @@ -117,6 +136,7 @@ private static void prepare() { if (logger != null) { LOGGER = logger; + PLUGIN_SO11Y = BootstrapInterRuntimeAssist.getSO11Y(loader); INTERCEPTOR = BootstrapInterRuntimeAssist.createInterceptor(loader, TARGET_INTERCEPTOR, LOGGER); } } else { diff --git a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/bootstrap/template/v2/StaticMethodInterV2Template.java b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/bootstrap/template/v2/StaticMethodInterV2Template.java index 74cfb1e26a..4541716c2f 100644 --- a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/bootstrap/template/v2/StaticMethodInterV2Template.java +++ b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/bootstrap/template/v2/StaticMethodInterV2Template.java @@ -28,11 +28,18 @@ import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.BootstrapInterRuntimeAssist; import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.v2.MethodInvocationContext; import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.v2.StaticMethodsAroundInterceptorV2; +import org.apache.skywalking.apm.agent.core.so11y.bootstrap.BootstrapPluginSo11y; /** * This class wouldn't be loaded in real env. This is a class template for dynamic class generation. */ public class StaticMethodInterV2Template { + + private static final String INTERCEPTOR_TYPE = "static"; + /** + * This field is never set in the template, but has value in the runtime. + */ + private static String PLUGIN_NAME; /** * This field is never set in the template, but has value in the runtime. */ @@ -40,6 +47,7 @@ public class StaticMethodInterV2Template { private static StaticMethodsAroundInterceptorV2 INTERCEPTOR; private static IBootstrapLog LOGGER; + private static BootstrapPluginSo11y PLUGIN_SO11Y; /** * Intercept the target static method. @@ -57,6 +65,8 @@ public static Object intercept(@Origin Class clazz, @AllArguments Object[] al @SuperCall Callable zuper) throws Throwable { prepare(); + long interceptorTimeCost = 0L; + long startTimeOfMethodBeforeInter = System.nanoTime(); MethodInvocationContext context = new MethodInvocationContext(); try { if (INTERCEPTOR != null) { @@ -64,7 +74,9 @@ public static Object intercept(@Origin Class clazz, @AllArguments Object[] al } } catch (Throwable t) { LOGGER.error(t, "class[{}] before static method[{}] intercept failure", clazz, method.getName()); + PLUGIN_SO11Y.error(PLUGIN_NAME, INTERCEPTOR_TYPE); } + interceptorTimeCost += System.nanoTime() - startTimeOfMethodBeforeInter; Object ret = null; try { @@ -74,23 +86,31 @@ public static Object intercept(@Origin Class clazz, @AllArguments Object[] al ret = zuper.call(); } } catch (Throwable t) { + long startTimeOfMethodHandleExceptionInter = System.nanoTime(); try { if (INTERCEPTOR != null) { INTERCEPTOR.handleMethodException(clazz, method, allArguments, method.getParameterTypes(), t, context); } } catch (Throwable t2) { LOGGER.error(t2, "class[{}] handle static method[{}] exception failure", clazz, method.getName(), t2.getMessage()); + PLUGIN_SO11Y.error(PLUGIN_NAME, INTERCEPTOR_TYPE); } + interceptorTimeCost += System.nanoTime() - startTimeOfMethodHandleExceptionInter; throw t; } finally { + long startTimeOfMethodAfterInter = System.nanoTime(); try { if (INTERCEPTOR != null) { ret = INTERCEPTOR.afterMethod(clazz, method, allArguments, method.getParameterTypes(), ret, context); } } catch (Throwable t) { LOGGER.error(t, "class[{}] after static method[{}] intercept failure:{}", clazz, method.getName(), t.getMessage()); + PLUGIN_SO11Y.error(PLUGIN_NAME, INTERCEPTOR_TYPE); } + interceptorTimeCost += System.nanoTime() - startTimeOfMethodAfterInter; } + PLUGIN_SO11Y.duration(interceptorTimeCost); + return ret; } @@ -106,6 +126,7 @@ private static void prepare() { if (logger != null) { LOGGER = logger; + PLUGIN_SO11Y = BootstrapInterRuntimeAssist.getSO11Y(loader); INTERCEPTOR = BootstrapInterRuntimeAssist.createInterceptor(loader, TARGET_INTERCEPTOR, LOGGER); } } else { diff --git a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/bootstrap/template/v2/StaticMethodInterV2WithOverrideArgsTemplate.java b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/bootstrap/template/v2/StaticMethodInterV2WithOverrideArgsTemplate.java index 09607882d7..9297f36582 100644 --- a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/bootstrap/template/v2/StaticMethodInterV2WithOverrideArgsTemplate.java +++ b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/bootstrap/template/v2/StaticMethodInterV2WithOverrideArgsTemplate.java @@ -28,11 +28,18 @@ import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.OverrideCallable; import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.v2.MethodInvocationContext; import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.v2.StaticMethodsAroundInterceptorV2; +import org.apache.skywalking.apm.agent.core.so11y.bootstrap.BootstrapPluginSo11y; /** * This class wouldn't be loaded in real env. This is a class template for dynamic class generation. */ public class StaticMethodInterV2WithOverrideArgsTemplate { + + private static final String INTERCEPTOR_TYPE = "static"; + /** + * This field is never set in the template, but has value in the runtime. + */ + private static String PLUGIN_NAME; /** * This field is never set in the template, but has value in the runtime. */ @@ -40,6 +47,7 @@ public class StaticMethodInterV2WithOverrideArgsTemplate { private static StaticMethodsAroundInterceptorV2 INTERCEPTOR; private static IBootstrapLog LOGGER; + private static BootstrapPluginSo11y PLUGIN_SO11Y; /** * Intercept the target static method. @@ -57,6 +65,8 @@ public static Object intercept(@Origin Class clazz, @AllArguments Object[] al @Morph OverrideCallable zuper) throws Throwable { prepare(); + long interceptorTimeCost = 0L; + long startTimeOfMethodBeforeInter = System.nanoTime(); MethodInvocationContext context = new MethodInvocationContext(); try { if (INTERCEPTOR != null) { @@ -64,7 +74,9 @@ public static Object intercept(@Origin Class clazz, @AllArguments Object[] al } } catch (Throwable t) { LOGGER.error(t, "class[{}] before static method[{}] intercept failure", clazz, method.getName()); + PLUGIN_SO11Y.error(PLUGIN_NAME, INTERCEPTOR_TYPE); } + interceptorTimeCost += System.nanoTime() - startTimeOfMethodBeforeInter; Object ret = null; try { @@ -74,23 +86,31 @@ public static Object intercept(@Origin Class clazz, @AllArguments Object[] al ret = zuper.call(allArguments); } } catch (Throwable t) { + long startTimeOfMethodHandleExceptionInter = System.nanoTime(); try { if (INTERCEPTOR != null) { INTERCEPTOR.handleMethodException(clazz, method, allArguments, method.getParameterTypes(), t, context); } } catch (Throwable t2) { LOGGER.error(t2, "class[{}] handle static method[{}] exception failure", clazz, method.getName(), t2.getMessage()); + PLUGIN_SO11Y.error(PLUGIN_NAME, INTERCEPTOR_TYPE); } + interceptorTimeCost += System.nanoTime() - startTimeOfMethodHandleExceptionInter; throw t; } finally { + long startTimeOfMethodAfterInter = System.nanoTime(); try { if (INTERCEPTOR != null) { ret = INTERCEPTOR.afterMethod(clazz, method, allArguments, method.getParameterTypes(), ret, context); } } catch (Throwable t) { LOGGER.error(t, "class[{}] after static method[{}] intercept failure:{}", clazz, method.getName(), t.getMessage()); + PLUGIN_SO11Y.error(PLUGIN_NAME, INTERCEPTOR_TYPE); } + interceptorTimeCost += System.nanoTime() - startTimeOfMethodAfterInter; } + PLUGIN_SO11Y.duration(interceptorTimeCost); + return ret; } @@ -106,6 +126,7 @@ private static void prepare() { if (logger != null) { LOGGER = logger; + PLUGIN_SO11Y = BootstrapInterRuntimeAssist.getSO11Y(loader); INTERCEPTOR = BootstrapInterRuntimeAssist.createInterceptor(loader, TARGET_INTERCEPTOR, LOGGER); } } else { diff --git a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/interceptor/enhance/BootstrapInterRuntimeAssist.java b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/interceptor/enhance/BootstrapInterRuntimeAssist.java index f667667a3e..9289391ccb 100644 --- a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/interceptor/enhance/BootstrapInterRuntimeAssist.java +++ b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/interceptor/enhance/BootstrapInterRuntimeAssist.java @@ -22,6 +22,7 @@ import java.lang.reflect.Field; import java.lang.reflect.Method; import org.apache.skywalking.apm.agent.core.plugin.bootstrap.IBootstrapLog; +import org.apache.skywalking.apm.agent.core.so11y.bootstrap.BootstrapPluginSo11y; /** * This assist help all bootstrap class core interceptor. @@ -29,6 +30,8 @@ public class BootstrapInterRuntimeAssist { private static final String AGENT_CLASSLOADER_DEFAULT = "org.apache.skywalking.apm.agent.core.plugin.loader.AgentClassLoader"; private static final String DEFAULT_AGENT_CLASSLOADER_INSTANCE = "DEFAULT_LOADER"; + private static final String SO11Y_BRIDGE_CLASS = "org.apache.skywalking.apm.agent.core.so11y.bootstrap.BootstrapPluginSo11yBridge"; + private static final String SO11Y_BRIDGE_GET_SO11Y_METHOD = "getSo11y"; private static final String LOG_MANAGER_CLASS = "org.apache.skywalking.apm.agent.core.plugin.bootstrap.BootstrapPluginLogBridge"; private static final String LOG_MANAGER_GET_LOGGER_METHOD = "getLogger"; private static final PrintStream OUT = System.out; @@ -62,6 +65,17 @@ public static IBootstrapLog getLogger(ClassLoader defaultAgentClassLoader, Strin } } + public static BootstrapPluginSo11y getSO11Y(ClassLoader defaultAgentClassLoader) { + try { + Class logManagerClass = Class.forName(SO11Y_BRIDGE_CLASS, true, defaultAgentClassLoader); + Method getLogger = logManagerClass.getMethod(SO11Y_BRIDGE_GET_SO11Y_METHOD); + return (BootstrapPluginSo11y) getLogger.invoke(null); + } catch (Exception e) { + e.printStackTrace(OUT); + return null; + } + } + public static T createInterceptor(ClassLoader defaultAgentClassLoader, String className, IBootstrapLog log) { try { Class interceptor = Class.forName(className, true, defaultAgentClassLoader); diff --git a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/interceptor/enhance/ClassEnhancePluginDefine.java b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/interceptor/enhance/ClassEnhancePluginDefine.java index e1a1fa4261..ec6628fe95 100644 --- a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/interceptor/enhance/ClassEnhancePluginDefine.java +++ b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/interceptor/enhance/ClassEnhancePluginDefine.java @@ -19,6 +19,7 @@ package org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance; import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.description.modifier.Visibility; import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.dynamic.DynamicType; import net.bytebuddy.implementation.FieldAccessor; @@ -102,6 +103,9 @@ protected DynamicType.Builder enhanceInstance(TypeDescription typeDescription newClassBuilder = newClassBuilder.defineField( CONTEXT_ATTR_NAME, Object.class, ACC_PRIVATE | ACC_VOLATILE) .implement(EnhancedInstance.class) + .defineMethod(CONTEXT_GETTER_NAME, Object.class, Visibility.PUBLIC) + .intercept(FieldAccessor.ofField(CONTEXT_ATTR_NAME)) + .defineMethod(CONTEXT_SETTER_NAME, void.class, Visibility.PUBLIC).withParameters(Object.class) .intercept(FieldAccessor.ofField(CONTEXT_ATTR_NAME)); context.extendObjectCompleted(); } @@ -112,17 +116,20 @@ protected DynamicType.Builder enhanceInstance(TypeDescription typeDescription */ if (existedConstructorInterceptPoint) { for (ConstructorInterceptPoint constructorInterceptPoint : constructorInterceptPoints) { + String constructorInterceptor = constructorInterceptPoint.getConstructorInterceptor(); + if (StringUtil.isEmpty(constructorInterceptor)) { + throw new EnhanceException("no InstanceConstructorInterceptor define to enhance class " + enhanceOriginClassName); + } if (isBootstrapInstrumentation()) { newClassBuilder = newClassBuilder.constructor(constructorInterceptPoint.getConstructorMatcher()) - .intercept(SuperMethodCall.INSTANCE.andThen(MethodDelegation.withDefaultConfiguration() - .to(BootstrapInstrumentBoost - .forInternalDelegateClass(constructorInterceptPoint - .getConstructorInterceptor())))); + .intercept(SuperMethodCall.INSTANCE.andThen(MethodDelegation.withDefaultConfiguration() + .to(BootstrapInstrumentBoost + .forInternalDelegateClass(constructorInterceptor)))); } else { newClassBuilder = newClassBuilder.constructor(constructorInterceptPoint.getConstructorMatcher()) - .intercept(SuperMethodCall.INSTANCE.andThen(MethodDelegation.withDefaultConfiguration() - .to(new ConstructorInter(constructorInterceptPoint - .getConstructorInterceptor(), classLoader), delegateNamingResolver.resolve(constructorInterceptPoint)))); + .intercept(SuperMethodCall.INSTANCE.andThen(MethodDelegation.withDefaultConfiguration() + .to(new ConstructorInter(getPluginName(), constructorInterceptor, classLoader), + delegateNamingResolver.resolve(constructorInterceptPoint)))); } } } @@ -150,7 +157,7 @@ protected DynamicType.Builder enhanceInstance(TypeDescription typeDescription newClassBuilder = newClassBuilder.method(junction) .intercept(MethodDelegation.withDefaultConfiguration() .withBinders(Morph.Binder.install(OverrideCallable.class)) - .to(new InstMethodsInterWithOverrideArgs(interceptor, classLoader), delegateNamingResolver.resolve(instanceMethodsInterceptPoint))); + .to(new InstMethodsInterWithOverrideArgs(getPluginName(), interceptor, classLoader), delegateNamingResolver.resolve(instanceMethodsInterceptPoint))); } } else { if (isBootstrapInstrumentation()) { @@ -160,7 +167,7 @@ protected DynamicType.Builder enhanceInstance(TypeDescription typeDescription } else { newClassBuilder = newClassBuilder.method(junction) .intercept(MethodDelegation.withDefaultConfiguration() - .to(new InstMethodsInter(interceptor, classLoader), delegateNamingResolver.resolve(instanceMethodsInterceptPoint))); + .to(new InstMethodsInter(getPluginName(), interceptor, classLoader), delegateNamingResolver.resolve(instanceMethodsInterceptPoint))); } } } @@ -202,7 +209,7 @@ protected DynamicType.Builder enhanceClass(TypeDescription typeDescription, D newClassBuilder = newClassBuilder.method(isStatic().and(staticMethodsInterceptPoint.getMethodsMatcher())) .intercept(MethodDelegation.withDefaultConfiguration() .withBinders(Morph.Binder.install(OverrideCallable.class)) - .to(new StaticMethodsInterWithOverrideArgs(interceptor), delegateNamingResolver.resolve(staticMethodsInterceptPoint))); + .to(new StaticMethodsInterWithOverrideArgs(getPluginName(), interceptor), delegateNamingResolver.resolve(staticMethodsInterceptPoint))); } } else { if (isBootstrapInstrumentation()) { @@ -212,7 +219,7 @@ protected DynamicType.Builder enhanceClass(TypeDescription typeDescription, D } else { newClassBuilder = newClassBuilder.method(isStatic().and(staticMethodsInterceptPoint.getMethodsMatcher())) .intercept(MethodDelegation.withDefaultConfiguration() - .to(new StaticMethodsInter(interceptor), delegateNamingResolver.resolve(staticMethodsInterceptPoint))); + .to(new StaticMethodsInter(getPluginName(), interceptor), delegateNamingResolver.resolve(staticMethodsInterceptPoint))); } } diff --git a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/interceptor/enhance/ConstructorInter.java b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/interceptor/enhance/ConstructorInter.java index 8a057df259..3a1063da03 100644 --- a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/interceptor/enhance/ConstructorInter.java +++ b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/interceptor/enhance/ConstructorInter.java @@ -25,6 +25,7 @@ import org.apache.skywalking.apm.agent.core.plugin.PluginException; import org.apache.skywalking.apm.agent.core.plugin.loader.InterceptorInstanceLoader; import org.apache.skywalking.apm.agent.core.logging.api.ILog; +import org.apache.skywalking.apm.agent.core.so11y.AgentSo11y; /** * The actual byte-buddy's interceptor to intercept constructor methods. In this class, it provides a bridge between @@ -33,6 +34,9 @@ public class ConstructorInter { private static final ILog LOGGER = LogManager.getLogger(ConstructorInter.class); + private static final String INTERCEPTOR_TYPE = "constructor"; + + private String pluginName; /** * An {@link InstanceConstructorInterceptor} This name should only stay in {@link String}, the real {@link Class} * type will trigger classloader failure. If you want to know more, please check on books about Classloader or @@ -43,7 +47,8 @@ public class ConstructorInter { /** * @param constructorInterceptorClassName class full name. */ - public ConstructorInter(String constructorInterceptorClassName, ClassLoader classLoader) throws PluginException { + public ConstructorInter(String pluginName, String constructorInterceptorClassName, ClassLoader classLoader) throws PluginException { + this.pluginName = pluginName; try { interceptor = InterceptorInstanceLoader.load(constructorInterceptorClassName, classLoader); } catch (Throwable t) { @@ -59,13 +64,17 @@ public ConstructorInter(String constructorInterceptorClassName, ClassLoader clas */ @RuntimeType public void intercept(@This Object obj, @AllArguments Object[] allArguments) { + long interceptorTimeCost = 0L; + long startTime = System.nanoTime(); try { EnhancedInstance targetObject = (EnhancedInstance) obj; interceptor.onConstruct(targetObject, allArguments); } catch (Throwable t) { LOGGER.error("ConstructorInter failure.", t); + AgentSo11y.errorOfPlugin(pluginName, INTERCEPTOR_TYPE); } - + interceptorTimeCost += System.nanoTime() - startTime; + AgentSo11y.durationOfInterceptor(interceptorTimeCost); } } diff --git a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/interceptor/enhance/InstMethodsInter.java b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/interceptor/enhance/InstMethodsInter.java index 81ebfb1b78..581b854de3 100644 --- a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/interceptor/enhance/InstMethodsInter.java +++ b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/interceptor/enhance/InstMethodsInter.java @@ -29,6 +29,7 @@ import org.apache.skywalking.apm.agent.core.plugin.PluginException; import org.apache.skywalking.apm.agent.core.plugin.loader.InterceptorInstanceLoader; import org.apache.skywalking.apm.agent.core.logging.api.ILog; +import org.apache.skywalking.apm.agent.core.so11y.AgentSo11y; /** * The actual byte-buddy's interceptor to intercept class instance methods. In this class, it provides a bridge between @@ -37,6 +38,9 @@ public class InstMethodsInter { private static final ILog LOGGER = LogManager.getLogger(InstMethodsInter.class); + private static final String INTERCEPTOR_TYPE = "inst"; + + private String pluginName; /** * An {@link InstanceMethodsAroundInterceptor} This name should only stay in {@link String}, the real {@link Class} * type will trigger classloader failure. If you want to know more, please check on books about Classloader or @@ -47,7 +51,8 @@ public class InstMethodsInter { /** * @param instanceMethodsAroundInterceptorClassName class full name. */ - public InstMethodsInter(String instanceMethodsAroundInterceptorClassName, ClassLoader classLoader) { + public InstMethodsInter(String pluginName, String instanceMethodsAroundInterceptorClassName, ClassLoader classLoader) { + this.pluginName = pluginName; try { interceptor = InterceptorInstanceLoader.load(instanceMethodsAroundInterceptorClassName, classLoader); } catch (Throwable t) { @@ -71,12 +76,16 @@ public Object intercept(@This Object obj, @AllArguments Object[] allArguments, @ @Origin Method method) throws Throwable { EnhancedInstance targetObject = (EnhancedInstance) obj; + long interceptorTimeCost = 0L; + long startTimeOfMethodBeforeInter = System.nanoTime(); MethodInterceptResult result = new MethodInterceptResult(); try { interceptor.beforeMethod(targetObject, method, allArguments, method.getParameterTypes(), result); } catch (Throwable t) { LOGGER.error(t, "class[{}] before method[{}] intercept failure", obj.getClass(), method.getName()); + AgentSo11y.errorOfPlugin(pluginName, INTERCEPTOR_TYPE); } + interceptorTimeCost += System.nanoTime() - startTimeOfMethodBeforeInter; Object ret = null; try { @@ -86,19 +95,27 @@ public Object intercept(@This Object obj, @AllArguments Object[] allArguments, @ ret = zuper.call(); } } catch (Throwable t) { + long startTimeOfMethodHandleExceptionInter = System.nanoTime(); try { interceptor.handleMethodException(targetObject, method, allArguments, method.getParameterTypes(), t); } catch (Throwable t2) { LOGGER.error(t2, "class[{}] handle method[{}] exception failure", obj.getClass(), method.getName()); + AgentSo11y.errorOfPlugin(pluginName, INTERCEPTOR_TYPE); } + interceptorTimeCost += System.nanoTime() - startTimeOfMethodHandleExceptionInter; throw t; } finally { + long startTimeOfMethodAfterInter = System.nanoTime(); try { ret = interceptor.afterMethod(targetObject, method, allArguments, method.getParameterTypes(), ret); } catch (Throwable t) { LOGGER.error(t, "class[{}] after method[{}] intercept failure", obj.getClass(), method.getName()); + AgentSo11y.errorOfPlugin(pluginName, INTERCEPTOR_TYPE); } + interceptorTimeCost += System.nanoTime() - startTimeOfMethodAfterInter; } + AgentSo11y.durationOfInterceptor(interceptorTimeCost); + return ret; } } diff --git a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/interceptor/enhance/InstMethodsInterWithOverrideArgs.java b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/interceptor/enhance/InstMethodsInterWithOverrideArgs.java index 1bba49c177..fabe5094e3 100644 --- a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/interceptor/enhance/InstMethodsInterWithOverrideArgs.java +++ b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/interceptor/enhance/InstMethodsInterWithOverrideArgs.java @@ -28,6 +28,7 @@ import org.apache.skywalking.apm.agent.core.plugin.loader.InterceptorInstanceLoader; import org.apache.skywalking.apm.agent.core.logging.api.ILog; import org.apache.skywalking.apm.agent.core.logging.api.LogManager; +import org.apache.skywalking.apm.agent.core.so11y.AgentSo11y; /** * The actual byte-buddy's interceptor to intercept class instance methods. In this class, it provides a bridge between @@ -36,6 +37,9 @@ public class InstMethodsInterWithOverrideArgs { private static final ILog LOGGER = LogManager.getLogger(InstMethodsInterWithOverrideArgs.class); + private static final String INTERCEPTOR_TYPE = "inst"; + + private String pluginName; /** * An {@link InstanceMethodsAroundInterceptor} This name should only stay in {@link String}, the real {@link Class} * type will trigger classloader failure. If you want to know more, please check on books about Classloader or @@ -46,7 +50,8 @@ public class InstMethodsInterWithOverrideArgs { /** * @param instanceMethodsAroundInterceptorClassName class full name. */ - public InstMethodsInterWithOverrideArgs(String instanceMethodsAroundInterceptorClassName, ClassLoader classLoader) { + public InstMethodsInterWithOverrideArgs(String pluginName, String instanceMethodsAroundInterceptorClassName, ClassLoader classLoader) { + this.pluginName = pluginName; try { interceptor = InterceptorInstanceLoader.load(instanceMethodsAroundInterceptorClassName, classLoader); } catch (Throwable t) { @@ -70,12 +75,16 @@ public Object intercept(@This Object obj, @AllArguments Object[] allArguments, @ @Morph OverrideCallable zuper) throws Throwable { EnhancedInstance targetObject = (EnhancedInstance) obj; + long interceptorTimeCost = 0L; + long startTimeOfMethodBeforeInter = System.nanoTime(); MethodInterceptResult result = new MethodInterceptResult(); try { interceptor.beforeMethod(targetObject, method, allArguments, method.getParameterTypes(), result); } catch (Throwable t) { LOGGER.error(t, "class[{}] before method[{}] intercept failure", obj.getClass(), method.getName()); + AgentSo11y.errorOfPlugin(pluginName, INTERCEPTOR_TYPE); } + interceptorTimeCost += System.nanoTime() - startTimeOfMethodBeforeInter; Object ret = null; try { @@ -85,19 +94,26 @@ public Object intercept(@This Object obj, @AllArguments Object[] allArguments, @ ret = zuper.call(allArguments); } } catch (Throwable t) { + long startTimeOfMethodHandleExceptionInter = System.nanoTime(); try { interceptor.handleMethodException(targetObject, method, allArguments, method.getParameterTypes(), t); } catch (Throwable t2) { LOGGER.error(t2, "class[{}] handle method[{}] exception failure", obj.getClass(), method.getName()); + AgentSo11y.errorOfPlugin(pluginName, INTERCEPTOR_TYPE); } + interceptorTimeCost += System.nanoTime() - startTimeOfMethodHandleExceptionInter; throw t; } finally { + long startTimeOfMethodAfterInter = System.nanoTime(); try { ret = interceptor.afterMethod(targetObject, method, allArguments, method.getParameterTypes(), ret); } catch (Throwable t) { LOGGER.error(t, "class[{}] after method[{}] intercept failure", obj.getClass(), method.getName()); + AgentSo11y.errorOfPlugin(pluginName, INTERCEPTOR_TYPE); } + interceptorTimeCost += System.nanoTime() - startTimeOfMethodAfterInter; } + AgentSo11y.durationOfInterceptor(interceptorTimeCost); return ret; } } diff --git a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/interceptor/enhance/StaticMethodsInter.java b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/interceptor/enhance/StaticMethodsInter.java index 6507118aaf..c1f3564dfe 100644 --- a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/interceptor/enhance/StaticMethodsInter.java +++ b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/interceptor/enhance/StaticMethodsInter.java @@ -27,6 +27,7 @@ import org.apache.skywalking.apm.agent.core.plugin.loader.InterceptorInstanceLoader; import org.apache.skywalking.apm.agent.core.logging.api.ILog; import org.apache.skywalking.apm.agent.core.logging.api.LogManager; +import org.apache.skywalking.apm.agent.core.so11y.AgentSo11y; /** * The actual byte-buddy's interceptor to intercept class static methods. In this class, it provides a bridge between @@ -35,6 +36,9 @@ public class StaticMethodsInter { private static final ILog LOGGER = LogManager.getLogger(StaticMethodsInter.class); + private static final String INTERCEPTOR_TYPE = "static"; + + private String pluginName; /** * A class full name, and instanceof {@link StaticMethodsAroundInterceptor} This name should only stay in {@link * String}, the real {@link Class} type will trigger classloader failure. If you want to know more, please check on @@ -47,7 +51,8 @@ public class StaticMethodsInter { * * @param staticMethodsAroundInterceptorClassName class full name. */ - public StaticMethodsInter(String staticMethodsAroundInterceptorClassName) { + public StaticMethodsInter(String pluginName, String staticMethodsAroundInterceptorClassName) { + this.pluginName = pluginName; this.staticMethodsAroundInterceptorClassName = staticMethodsAroundInterceptorClassName; } @@ -68,12 +73,16 @@ public Object intercept(@Origin Class clazz, @AllArguments Object[] allArgume StaticMethodsAroundInterceptor interceptor = InterceptorInstanceLoader.load(staticMethodsAroundInterceptorClassName, clazz .getClassLoader()); + long interceptorTimeCost = 0L; + long startTimeOfMethodBeforeInter = System.nanoTime(); MethodInterceptResult result = new MethodInterceptResult(); try { interceptor.beforeMethod(clazz, method, allArguments, method.getParameterTypes(), result); } catch (Throwable t) { LOGGER.error(t, "class[{}] before static method[{}] intercept failure", clazz, method.getName()); + AgentSo11y.errorOfPlugin(pluginName, INTERCEPTOR_TYPE); } + interceptorTimeCost += System.nanoTime() - startTimeOfMethodBeforeInter; Object ret = null; try { @@ -83,19 +92,27 @@ public Object intercept(@Origin Class clazz, @AllArguments Object[] allArgume ret = zuper.call(); } } catch (Throwable t) { + long startTimeOfMethodHandleExceptionInter = System.nanoTime(); try { interceptor.handleMethodException(clazz, method, allArguments, method.getParameterTypes(), t); } catch (Throwable t2) { LOGGER.error(t2, "class[{}] handle static method[{}] exception failure", clazz, method.getName(), t2.getMessage()); + AgentSo11y.errorOfPlugin(pluginName, INTERCEPTOR_TYPE); } + interceptorTimeCost += System.nanoTime() - startTimeOfMethodHandleExceptionInter; throw t; } finally { + long startTimeOfMethodAfterInter = System.nanoTime(); try { ret = interceptor.afterMethod(clazz, method, allArguments, method.getParameterTypes(), ret); } catch (Throwable t) { LOGGER.error(t, "class[{}] after static method[{}] intercept failure:{}", clazz, method.getName(), t.getMessage()); + AgentSo11y.errorOfPlugin(pluginName, INTERCEPTOR_TYPE); } + interceptorTimeCost += System.nanoTime() - startTimeOfMethodAfterInter; } + AgentSo11y.durationOfInterceptor(interceptorTimeCost); + return ret; } } diff --git a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/interceptor/enhance/StaticMethodsInterWithOverrideArgs.java b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/interceptor/enhance/StaticMethodsInterWithOverrideArgs.java index 83e66e4bcd..23b136fdcf 100644 --- a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/interceptor/enhance/StaticMethodsInterWithOverrideArgs.java +++ b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/interceptor/enhance/StaticMethodsInterWithOverrideArgs.java @@ -26,6 +26,7 @@ import org.apache.skywalking.apm.agent.core.plugin.loader.InterceptorInstanceLoader; import org.apache.skywalking.apm.agent.core.logging.api.ILog; import org.apache.skywalking.apm.agent.core.logging.api.LogManager; +import org.apache.skywalking.apm.agent.core.so11y.AgentSo11y; /** * The actual byte-buddy's interceptor to intercept class static methods. In this class, it provides a bridge between @@ -34,6 +35,9 @@ public class StaticMethodsInterWithOverrideArgs { private static final ILog LOGGER = LogManager.getLogger(StaticMethodsInterWithOverrideArgs.class); + private static final String INTERCEPTOR_TYPE = "static"; + + private String pluginName; /** * A class full name, and instanceof {@link StaticMethodsAroundInterceptor} This name should only stay in {@link * String}, the real {@link Class} type will trigger classloader failure. If you want to know more, please check on @@ -44,9 +48,11 @@ public class StaticMethodsInterWithOverrideArgs { /** * Set the name of {@link StaticMethodsInterWithOverrideArgs#staticMethodsAroundInterceptorClassName} * + * @param pluginName name of interceptor plugin * @param staticMethodsAroundInterceptorClassName class full name. */ - public StaticMethodsInterWithOverrideArgs(String staticMethodsAroundInterceptorClassName) { + public StaticMethodsInterWithOverrideArgs(String pluginName, String staticMethodsAroundInterceptorClassName) { + this.pluginName = pluginName; this.staticMethodsAroundInterceptorClassName = staticMethodsAroundInterceptorClassName; } @@ -67,12 +73,16 @@ public Object intercept(@Origin Class clazz, @AllArguments Object[] allArgume StaticMethodsAroundInterceptor interceptor = InterceptorInstanceLoader.load(staticMethodsAroundInterceptorClassName, clazz .getClassLoader()); + long interceptorTimeCost = 0L; + long startTimeOfMethodBeforeInter = System.nanoTime(); MethodInterceptResult result = new MethodInterceptResult(); try { interceptor.beforeMethod(clazz, method, allArguments, method.getParameterTypes(), result); } catch (Throwable t) { LOGGER.error(t, "class[{}] before static method[{}] intercept failure", clazz, method.getName()); + AgentSo11y.errorOfPlugin(pluginName, INTERCEPTOR_TYPE); } + interceptorTimeCost += System.nanoTime() - startTimeOfMethodBeforeInter; Object ret = null; try { @@ -82,19 +92,27 @@ public Object intercept(@Origin Class clazz, @AllArguments Object[] allArgume ret = zuper.call(allArguments); } } catch (Throwable t) { + long startTimeOfMethodHandleExceptionInter = System.nanoTime(); try { interceptor.handleMethodException(clazz, method, allArguments, method.getParameterTypes(), t); } catch (Throwable t2) { LOGGER.error(t2, "class[{}] handle static method[{}] exception failure", clazz, method.getName(), t2.getMessage()); + AgentSo11y.errorOfPlugin(pluginName, INTERCEPTOR_TYPE); } + interceptorTimeCost += System.nanoTime() - startTimeOfMethodHandleExceptionInter; throw t; } finally { + long startTimeOfMethodAfterInter = System.nanoTime(); try { ret = interceptor.afterMethod(clazz, method, allArguments, method.getParameterTypes(), ret); } catch (Throwable t) { LOGGER.error(t, "class[{}] after static method[{}] intercept failure:{}", clazz, method.getName(), t.getMessage()); + AgentSo11y.errorOfPlugin(pluginName, INTERCEPTOR_TYPE); } + interceptorTimeCost += System.nanoTime() - startTimeOfMethodAfterInter; } + AgentSo11y.durationOfInterceptor(interceptorTimeCost); + return ret; } } diff --git a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/interceptor/enhance/v2/ClassEnhancePluginDefineV2.java b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/interceptor/enhance/v2/ClassEnhancePluginDefineV2.java index 2436c2994b..3423ac1392 100644 --- a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/interceptor/enhance/v2/ClassEnhancePluginDefineV2.java +++ b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/interceptor/enhance/v2/ClassEnhancePluginDefineV2.java @@ -18,6 +18,7 @@ package org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.v2; import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.description.modifier.Visibility; import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.dynamic.DynamicType; import net.bytebuddy.implementation.FieldAccessor; @@ -87,7 +88,7 @@ protected DynamicType.Builder enhanceClass(TypeDescription typeDescription, isStatic().and(staticMethodsInterceptV2Point.getMethodsMatcher())) .intercept(MethodDelegation.withDefaultConfiguration() .withBinders(Morph.Binder.install(OverrideCallable.class)) - .to(new StaticMethodsInterV2WithOverrideArgs(interceptor), delegateNamingResolver.resolve(staticMethodsInterceptV2Point))); + .to(new StaticMethodsInterV2WithOverrideArgs(getPluginName(), interceptor), delegateNamingResolver.resolve(staticMethodsInterceptV2Point))); } } else { if (isBootstrapInstrumentation()) { @@ -99,7 +100,7 @@ protected DynamicType.Builder enhanceClass(TypeDescription typeDescription, newClassBuilder = newClassBuilder.method( isStatic().and(staticMethodsInterceptV2Point.getMethodsMatcher())) .intercept(MethodDelegation.withDefaultConfiguration() - .to(new StaticMethodsInterV2(interceptor), delegateNamingResolver.resolve(staticMethodsInterceptV2Point))); + .to(new StaticMethodsInterV2(getPluginName(), interceptor), delegateNamingResolver.resolve(staticMethodsInterceptV2Point))); } } @@ -135,6 +136,9 @@ protected DynamicType.Builder enhanceInstance(TypeDescription typeDescription newClassBuilder = newClassBuilder.defineField( CONTEXT_ATTR_NAME, Object.class, ACC_PRIVATE | ACC_VOLATILE) .implement(EnhancedInstance.class) + .defineMethod(CONTEXT_GETTER_NAME, Object.class, Visibility.PUBLIC) + .intercept(FieldAccessor.ofField(CONTEXT_ATTR_NAME)) + .defineMethod(CONTEXT_SETTER_NAME, void.class, Visibility.PUBLIC).withParameters(Object.class) .intercept(FieldAccessor.ofField(CONTEXT_ATTR_NAME)); context.extendObjectCompleted(); } @@ -151,7 +155,7 @@ protected DynamicType.Builder enhanceInstance(TypeDescription typeDescription } else { newClassBuilder = newClassBuilder.constructor(constructorInterceptPoint.getConstructorMatcher()) .intercept(SuperMethodCall.INSTANCE.andThen(MethodDelegation.withDefaultConfiguration() - .to(new ConstructorInter(constructorInterceptPoint + .to(new ConstructorInter(getPluginName(), constructorInterceptPoint .getConstructorInterceptor(), classLoader), fieldNamingResolver.resolve(constructorInterceptPoint)))); } } @@ -179,7 +183,7 @@ protected DynamicType.Builder enhanceInstance(TypeDescription typeDescription newClassBuilder = newClassBuilder.method(junction) .intercept(MethodDelegation.withDefaultConfiguration() .withBinders(Morph.Binder.install(OverrideCallable.class)) - .to(new InstMethodsInterV2WithOverrideArgs(interceptor, classLoader), fieldNamingResolver.resolve(instanceMethodsInterceptV2Point))); + .to(new InstMethodsInterV2WithOverrideArgs(getPluginName(), interceptor, classLoader), fieldNamingResolver.resolve(instanceMethodsInterceptV2Point))); } } else { if (isBootstrapInstrumentation()) { @@ -189,7 +193,7 @@ protected DynamicType.Builder enhanceInstance(TypeDescription typeDescription } else { newClassBuilder = newClassBuilder.method(junction) .intercept(MethodDelegation.withDefaultConfiguration() - .to(new InstMethodsInterV2(interceptor, classLoader), fieldNamingResolver.resolve(instanceMethodsInterceptV2Point))); + .to(new InstMethodsInterV2(getPluginName(), interceptor, classLoader), fieldNamingResolver.resolve(instanceMethodsInterceptV2Point))); } } } diff --git a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/interceptor/enhance/v2/InstMethodsInterV2.java b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/interceptor/enhance/v2/InstMethodsInterV2.java index 8638980931..a46092ad92 100644 --- a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/interceptor/enhance/v2/InstMethodsInterV2.java +++ b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/interceptor/enhance/v2/InstMethodsInterV2.java @@ -29,6 +29,7 @@ import org.apache.skywalking.apm.agent.core.plugin.PluginException; import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; import org.apache.skywalking.apm.agent.core.plugin.loader.InterceptorInstanceLoader; +import org.apache.skywalking.apm.agent.core.so11y.AgentSo11y; /** * The actual byte-buddy's interceptor to intercept class instance methods. In this class, it provides a bridge between @@ -37,9 +38,13 @@ public class InstMethodsInterV2 { private static final ILog LOGGER = LogManager.getLogger(InstMethodsInterV2.class); + private static final String INTERCEPTOR_TYPE = "inst"; + + private String pluginName; private InstanceMethodsAroundInterceptorV2 interceptor; - public InstMethodsInterV2(String instanceMethodsAroundInterceptorClassName, ClassLoader classLoader) { + public InstMethodsInterV2(String pluginName, String instanceMethodsAroundInterceptorClassName, ClassLoader classLoader) { + this.pluginName = pluginName; try { interceptor = InterceptorInstanceLoader.load(instanceMethodsAroundInterceptorClassName, classLoader); } catch (Throwable t) { @@ -52,12 +57,16 @@ public Object intercept(@This Object obj, @AllArguments Object[] allArguments, @ @Origin Method method) throws Throwable { EnhancedInstance targetObject = (EnhancedInstance) obj; + long interceptorTimeCost = 0L; + long startTimeOfMethodBeforeInter = System.nanoTime(); MethodInvocationContext context = new MethodInvocationContext(); try { interceptor.beforeMethod(targetObject, method, allArguments, method.getParameterTypes(), context); } catch (Throwable t) { LOGGER.error(t, "class[{}] before method[{}] intercept failure", obj.getClass(), method.getName()); + AgentSo11y.errorOfPlugin(pluginName, INTERCEPTOR_TYPE); } + interceptorTimeCost += System.nanoTime() - startTimeOfMethodBeforeInter; Object ret = null; try { @@ -67,19 +76,27 @@ public Object intercept(@This Object obj, @AllArguments Object[] allArguments, @ ret = zuper.call(); } } catch (Throwable t) { + long startTimeOfMethodHandleExceptionInter = System.nanoTime(); try { interceptor.handleMethodException(targetObject, method, allArguments, method.getParameterTypes(), t, context); } catch (Throwable t2) { LOGGER.error(t2, "class[{}] handle method[{}] exception failure", obj.getClass(), method.getName()); + AgentSo11y.errorOfPlugin(pluginName, INTERCEPTOR_TYPE); } + interceptorTimeCost += System.nanoTime() - startTimeOfMethodHandleExceptionInter; throw t; } finally { + long startTimeOfMethodAfterInter = System.nanoTime(); try { ret = interceptor.afterMethod(targetObject, method, allArguments, method.getParameterTypes(), ret, context); } catch (Throwable t) { LOGGER.error(t, "class[{}] after method[{}] intercept failure", obj.getClass(), method.getName()); + AgentSo11y.errorOfPlugin(pluginName, INTERCEPTOR_TYPE); } + interceptorTimeCost += System.nanoTime() - startTimeOfMethodAfterInter; } + AgentSo11y.durationOfInterceptor(interceptorTimeCost); + return ret; } } diff --git a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/interceptor/enhance/v2/InstMethodsInterV2WithOverrideArgs.java b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/interceptor/enhance/v2/InstMethodsInterV2WithOverrideArgs.java index 5779a386f1..2a937c961a 100644 --- a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/interceptor/enhance/v2/InstMethodsInterV2WithOverrideArgs.java +++ b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/interceptor/enhance/v2/InstMethodsInterV2WithOverrideArgs.java @@ -30,6 +30,7 @@ import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.OverrideCallable; import org.apache.skywalking.apm.agent.core.plugin.loader.InterceptorInstanceLoader; +import org.apache.skywalking.apm.agent.core.so11y.AgentSo11y; /** * The actual byte-buddy's interceptor to intercept class instance methods. In this class, it provides a bridge between @@ -38,6 +39,9 @@ public class InstMethodsInterV2WithOverrideArgs { private static final ILog LOGGER = LogManager.getLogger(InstMethodsInterV2WithOverrideArgs.class); + private static final String INTERCEPTOR_TYPE = "inst"; + + private String pluginName; /** * An {@link InstanceMethodsAroundInterceptorV2} This name should only stay in {@link String}, the real {@link Class} * type will trigger classloader failure. If you want to know more, please check on books about Classloader or @@ -48,7 +52,8 @@ public class InstMethodsInterV2WithOverrideArgs { /** * @param instanceMethodsAroundInterceptorClassName class full name. */ - public InstMethodsInterV2WithOverrideArgs(String instanceMethodsAroundInterceptorClassName, ClassLoader classLoader) { + public InstMethodsInterV2WithOverrideArgs(String pluginName, String instanceMethodsAroundInterceptorClassName, ClassLoader classLoader) { + this.pluginName = pluginName; try { interceptor = InterceptorInstanceLoader.load(instanceMethodsAroundInterceptorClassName, classLoader); } catch (Throwable t) { @@ -72,12 +77,16 @@ public Object intercept(@This Object obj, @AllArguments Object[] allArguments, @ @Morph OverrideCallable zuper) throws Throwable { EnhancedInstance targetObject = (EnhancedInstance) obj; + long interceptorTimeCost = 0L; + long startTimeOfMethodBeforeInter = System.nanoTime(); MethodInvocationContext context = new MethodInvocationContext(); try { interceptor.beforeMethod(targetObject, method, allArguments, method.getParameterTypes(), context); } catch (Throwable t) { LOGGER.error(t, "class[{}] before method[{}] intercept failure", obj.getClass(), method.getName()); + AgentSo11y.errorOfPlugin(pluginName, INTERCEPTOR_TYPE); } + interceptorTimeCost += System.nanoTime() - startTimeOfMethodBeforeInter; Object ret = null; try { @@ -87,19 +96,27 @@ public Object intercept(@This Object obj, @AllArguments Object[] allArguments, @ ret = zuper.call(allArguments); } } catch (Throwable t) { + long startTimeOfMethodHandleExceptionInter = System.nanoTime(); try { interceptor.handleMethodException(targetObject, method, allArguments, method.getParameterTypes(), t, context); } catch (Throwable t2) { LOGGER.error(t2, "class[{}] handle method[{}] exception failure", obj.getClass(), method.getName()); + AgentSo11y.errorOfPlugin(pluginName, INTERCEPTOR_TYPE); } + interceptorTimeCost += System.nanoTime() - startTimeOfMethodHandleExceptionInter; throw t; } finally { + long startTimeOfMethodAfterInter = System.nanoTime(); try { ret = interceptor.afterMethod(targetObject, method, allArguments, method.getParameterTypes(), ret, context); } catch (Throwable t) { LOGGER.error(t, "class[{}] after method[{}] intercept failure", obj.getClass(), method.getName()); + AgentSo11y.errorOfPlugin(pluginName, INTERCEPTOR_TYPE); } + interceptorTimeCost += System.nanoTime() - startTimeOfMethodAfterInter; } + AgentSo11y.durationOfInterceptor(interceptorTimeCost); + return ret; } } diff --git a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/interceptor/enhance/v2/StaticMethodsInterV2.java b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/interceptor/enhance/v2/StaticMethodsInterV2.java index 677ffa3697..add92b4fe1 100644 --- a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/interceptor/enhance/v2/StaticMethodsInterV2.java +++ b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/interceptor/enhance/v2/StaticMethodsInterV2.java @@ -27,6 +27,7 @@ import org.apache.skywalking.apm.agent.core.logging.api.ILog; import org.apache.skywalking.apm.agent.core.logging.api.LogManager; import org.apache.skywalking.apm.agent.core.plugin.loader.InterceptorInstanceLoader; +import org.apache.skywalking.apm.agent.core.so11y.AgentSo11y; /** * The actual byte-buddy's interceptor to intercept class instance methods. In this class, it provides a bridge between @@ -35,6 +36,9 @@ public class StaticMethodsInterV2 { private static final ILog LOGGER = LogManager.getLogger(StaticMethodsInterV2.class); + private static final String INTERCEPTOR_TYPE = "static"; + + private String pluginName; /** * A class full name, and instanceof {@link StaticMethodsAroundInterceptorV2} This name should only stay in {@link * String}, the real {@link Class} type will trigger classloader failure. If you want to know more, please check on @@ -47,7 +51,8 @@ public class StaticMethodsInterV2 { * * @param staticMethodsAroundInterceptorClassName class full name. */ - public StaticMethodsInterV2(String staticMethodsAroundInterceptorClassName) { + public StaticMethodsInterV2(String pluginName, String staticMethodsAroundInterceptorClassName) { + this.pluginName = pluginName; this.staticMethodsAroundInterceptorClassName = staticMethodsAroundInterceptorClassName; } @@ -68,12 +73,16 @@ public Object intercept(@Origin Class clazz, @AllArguments Object[] allArgume StaticMethodsAroundInterceptorV2 interceptor = InterceptorInstanceLoader.load(staticMethodsAroundInterceptorClassName, clazz.getClassLoader()); + long interceptorTimeCost = 0L; + long startTimeOfMethodBeforeInter = System.nanoTime(); MethodInvocationContext context = new MethodInvocationContext(); try { interceptor.beforeMethod(clazz, method, allArguments, method.getParameterTypes(), context); } catch (Throwable t) { LOGGER.error(t, "class[{}] before static method[{}] intercept failure", clazz, method.getName()); + AgentSo11y.errorOfPlugin(pluginName, INTERCEPTOR_TYPE); } + interceptorTimeCost += System.nanoTime() - startTimeOfMethodBeforeInter; Object ret = null; try { @@ -83,19 +92,27 @@ public Object intercept(@Origin Class clazz, @AllArguments Object[] allArgume ret = zuper.call(); } } catch (Throwable t) { + long startTimeOfMethodHandleExceptionInter = System.nanoTime(); try { interceptor.handleMethodException(clazz, method, allArguments, method.getParameterTypes(), t, context); } catch (Throwable t2) { LOGGER.error(t2, "class[{}] handle static method[{}] exception failure", clazz, method.getName(), t2.getMessage()); + AgentSo11y.errorOfPlugin(pluginName, INTERCEPTOR_TYPE); } + interceptorTimeCost += System.nanoTime() - startTimeOfMethodHandleExceptionInter; throw t; } finally { + long startTimeOfMethodAfterInter = System.nanoTime(); try { ret = interceptor.afterMethod(clazz, method, allArguments, method.getParameterTypes(), ret, context); } catch (Throwable t) { LOGGER.error(t, "class[{}] after static method[{}] intercept failure:{}", clazz, method.getName(), t.getMessage()); + AgentSo11y.errorOfPlugin(pluginName, INTERCEPTOR_TYPE); } + interceptorTimeCost += System.nanoTime() - startTimeOfMethodAfterInter; } + AgentSo11y.durationOfInterceptor(interceptorTimeCost); + return ret; } } diff --git a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/interceptor/enhance/v2/StaticMethodsInterV2WithOverrideArgs.java b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/interceptor/enhance/v2/StaticMethodsInterV2WithOverrideArgs.java index e7d326b43a..0341e67d0d 100644 --- a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/interceptor/enhance/v2/StaticMethodsInterV2WithOverrideArgs.java +++ b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/interceptor/enhance/v2/StaticMethodsInterV2WithOverrideArgs.java @@ -27,6 +27,7 @@ import org.apache.skywalking.apm.agent.core.logging.api.LogManager; import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.OverrideCallable; import org.apache.skywalking.apm.agent.core.plugin.loader.InterceptorInstanceLoader; +import org.apache.skywalking.apm.agent.core.so11y.AgentSo11y; /** * The actual byte-buddy's interceptor to intercept class instance methods. In this class, it provides a bridge between @@ -35,6 +36,9 @@ public class StaticMethodsInterV2WithOverrideArgs { private static final ILog LOGGER = LogManager.getLogger(StaticMethodsInterV2WithOverrideArgs.class); + private static final String INTERCEPTOR_TYPE = "static"; + + private String pluginName; /** * A class full name, and instanceof {@link StaticMethodsAroundInterceptorV2} This name should only stay in {@link * String}, the real {@link Class} type will trigger classloader failure. If you want to know more, please check on @@ -47,7 +51,8 @@ public class StaticMethodsInterV2WithOverrideArgs { * * @param staticMethodsAroundInterceptorClassName class full name. */ - public StaticMethodsInterV2WithOverrideArgs(String staticMethodsAroundInterceptorClassName) { + public StaticMethodsInterV2WithOverrideArgs(String pluginName, String staticMethodsAroundInterceptorClassName) { + this.pluginName = pluginName; this.staticMethodsAroundInterceptorClassName = staticMethodsAroundInterceptorClassName; } @@ -68,12 +73,16 @@ public Object intercept(@Origin Class clazz, @AllArguments Object[] allArgume StaticMethodsAroundInterceptorV2 interceptor = InterceptorInstanceLoader.load(staticMethodsAroundInterceptorClassName, clazz.getClassLoader()); + long interceptorTimeCost = 0L; + long startTimeOfMethodBeforeInter = System.nanoTime(); MethodInvocationContext context = new MethodInvocationContext(); try { interceptor.beforeMethod(clazz, method, allArguments, method.getParameterTypes(), context); } catch (Throwable t) { LOGGER.error(t, "class[{}] before static method[{}] intercept failure", clazz, method.getName()); + AgentSo11y.errorOfPlugin(pluginName, INTERCEPTOR_TYPE); } + interceptorTimeCost += System.nanoTime() - startTimeOfMethodBeforeInter; Object ret = null; try { @@ -83,19 +92,27 @@ public Object intercept(@Origin Class clazz, @AllArguments Object[] allArgume ret = zuper.call(allArguments); } } catch (Throwable t) { + long startTimeOfMethodHandleExceptionInter = System.nanoTime(); try { interceptor.handleMethodException(clazz, method, allArguments, method.getParameterTypes(), t, context); } catch (Throwable t2) { LOGGER.error(t2, "class[{}] handle static method[{}] exception failure", clazz, method.getName(), t2.getMessage()); + AgentSo11y.errorOfPlugin(pluginName, INTERCEPTOR_TYPE); } + interceptorTimeCost += System.nanoTime() - startTimeOfMethodHandleExceptionInter; throw t; } finally { + long startTimeOfMethodAfterInter = System.nanoTime(); try { ret = interceptor.afterMethod(clazz, method, allArguments, method.getParameterTypes(), ret, context); } catch (Throwable t) { LOGGER.error(t, "class[{}] after static method[{}] intercept failure:{}", clazz, method.getName(), t.getMessage()); + AgentSo11y.errorOfPlugin(pluginName, INTERCEPTOR_TYPE); } + interceptorTimeCost += System.nanoTime() - startTimeOfMethodAfterInter; } + AgentSo11y.durationOfInterceptor(interceptorTimeCost); + return ret; } } diff --git a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/so11y/AgentSo11y.java b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/so11y/AgentSo11y.java new file mode 100644 index 0000000000..c8da7ee262 --- /dev/null +++ b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/so11y/AgentSo11y.java @@ -0,0 +1,184 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.agent.core.so11y; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import org.apache.skywalking.apm.agent.core.boot.ServiceManager; +import org.apache.skywalking.apm.agent.core.meter.Counter; +import org.apache.skywalking.apm.agent.core.meter.Histogram; +import org.apache.skywalking.apm.agent.core.meter.MeterFactory; + +/** + * Agent self-observability meters collect through skywalking native protocols + */ +public class AgentSo11y { + + // A map to cache meter obj(s) for plugins. The key is the plugin name. + private static final Map ERROR_COUNTER_CACHE = new ConcurrentHashMap<>(); + + // Steps of interceptor time cost histogram + private static final List TIME_COST_HISTOGRAM_STEPS = Arrays.asList( + 1000d, 10000d, 50000d, 100000d, 300000d, 500000d, + 1000000d, 5000000d, 10000000d, 20000000d, 50000000d, 100000000d + ); + + // context counter + private static Counter PROPAGATED_CONTEXT_COUNTER; + private static Counter SAMPLER_CONTEXT_COUNTER; + private static Counter FINISH_CONTEXT_COUNTER; + + // ignore context counter + private static Counter PROPAGATED_IGNORE_CONTEXT_COUNTER; + private static Counter SAMPLER_IGNORE_CONTEXT_COUNTER; + private static Counter FINISH_IGNORE_CONTEXT_COUNTER; + + // leaked context counter + private static Counter LEAKED_CONTEXT_COUNTER; + private static Counter LEAKED_IGNORE_CONTEXT_COUNTER; + + // context perf histogram + private static Histogram INTERCEPTOR_TIME_COST; + + public static void measureTracingContextCreation(boolean forceSampling, boolean ignoredTracingContext) { + if (!ServiceManager.INSTANCE.isBooted()) { + // Agent kernel services could be not-booted-yet as ServiceManager#INSTANCE#boot executed after agent + // transfer initialization. + // Skip when the services are not ready to avoid MeterService status is not initialized. + return; + } + if (forceSampling) { + if (ignoredTracingContext) { + if (PROPAGATED_IGNORE_CONTEXT_COUNTER == null) { + PROPAGATED_IGNORE_CONTEXT_COUNTER = MeterFactory + .counter("created_ignored_context_counter") + .tag("created_by", "propagated") + .build(); + } + PROPAGATED_IGNORE_CONTEXT_COUNTER.increment(1); + } else { + if (PROPAGATED_CONTEXT_COUNTER == null) { + PROPAGATED_CONTEXT_COUNTER = MeterFactory + .counter("created_tracing_context_counter") + .tag("created_by", "propagated") + .build(); + } + PROPAGATED_CONTEXT_COUNTER.increment(1); + } + } else { + if (ignoredTracingContext) { + if (SAMPLER_IGNORE_CONTEXT_COUNTER == null) { + SAMPLER_IGNORE_CONTEXT_COUNTER = MeterFactory + .counter("created_ignored_context_counter") + .tag("created_by", "sampler") + .build(); + } + SAMPLER_IGNORE_CONTEXT_COUNTER.increment(1); + } else { + if (SAMPLER_CONTEXT_COUNTER == null) { + SAMPLER_CONTEXT_COUNTER = MeterFactory + .counter("created_tracing_context_counter") + .tag("created_by", "sampler") + .build(); + } + SAMPLER_CONTEXT_COUNTER.increment(1); + } + } + } + + public static void measureTracingContextCompletion(boolean ignoredTracingContext) { + if (!ServiceManager.INSTANCE.isBooted()) { + // Agent kernel services could be not-booted-yet as ServiceManager#INSTANCE#boot executed after agent + // transfer initialization. + // Skip when the services are not ready to avoid MeterService status is not initialized. + return; + } + if (ignoredTracingContext) { + if (FINISH_IGNORE_CONTEXT_COUNTER == null) { + FINISH_IGNORE_CONTEXT_COUNTER = MeterFactory.counter("finished_ignored_context_counter").build(); + } + FINISH_IGNORE_CONTEXT_COUNTER.increment(1); + } else { + if (FINISH_CONTEXT_COUNTER == null) { + FINISH_CONTEXT_COUNTER = MeterFactory.counter("finished_tracing_context_counter").build(); + } + FINISH_CONTEXT_COUNTER.increment(1); + } + } + + public static void measureLeakedTracingContext(boolean ignoredTracingContext) { + if (!ServiceManager.INSTANCE.isBooted()) { + // Agent kernel services could be not-booted-yet as ServiceManager#INSTANCE#boot executed after agent + // transfer initialization. + // Skip when the services are not ready to avoid MeterService status is not initialized. + return; + } + if (ignoredTracingContext) { + if (LEAKED_IGNORE_CONTEXT_COUNTER == null) { + LEAKED_IGNORE_CONTEXT_COUNTER = MeterFactory + .counter("possible_leaked_context_counter") + .tag("source", "ignore") + .build(); + } + LEAKED_IGNORE_CONTEXT_COUNTER.increment(1); + } else { + if (LEAKED_CONTEXT_COUNTER == null) { + LEAKED_CONTEXT_COUNTER = MeterFactory + .counter("possible_leaked_context_counter") + .tag("source", "tracing") + .build(); + } + LEAKED_CONTEXT_COUNTER.increment(1); + } + } + + public static void durationOfInterceptor(double timeCostInNanos) { + if (!ServiceManager.INSTANCE.isBooted()) { + // Agent kernel services could be not-booted-yet as ServiceManager#INSTANCE#boot executed after agent + // transfer initialization. + // Skip when the services are not ready to avoid MeterService status is not initialized. + return; + } + if (INTERCEPTOR_TIME_COST == null) { + INTERCEPTOR_TIME_COST = MeterFactory + .histogram("tracing_context_performance") + .steps(TIME_COST_HISTOGRAM_STEPS) + .build(); + } + INTERCEPTOR_TIME_COST.addValue(timeCostInNanos); + } + + public static void errorOfPlugin(String pluginName, String interType) { + if (!ServiceManager.INSTANCE.isBooted()) { + // Agent kernel services could be not-booted-yet as ServiceManager#INSTANCE#boot executed after agent + // transfer initialization. + // Skip when the services are not ready to avoid MeterService status is not initialized. + return; + } + Counter counter = ERROR_COUNTER_CACHE.computeIfAbsent(pluginName + interType, key -> MeterFactory + .counter("interceptor_error_counter") + .tag("plugin_name", pluginName) + .tag("inter_type", interType) + .build() + ); + counter.increment(1); + } +} diff --git a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/so11y/bootstrap/BootstrapPluginSo11y.java b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/so11y/bootstrap/BootstrapPluginSo11y.java new file mode 100644 index 0000000000..e0032ac7d3 --- /dev/null +++ b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/so11y/bootstrap/BootstrapPluginSo11y.java @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.agent.core.so11y.bootstrap; + +public interface BootstrapPluginSo11y { + void duration(double timeCostInNanos); + + void error(String pluginName, String interType); +} diff --git a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/so11y/bootstrap/BootstrapPluginSo11yBridge.java b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/so11y/bootstrap/BootstrapPluginSo11yBridge.java new file mode 100644 index 0000000000..2c9ac37600 --- /dev/null +++ b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/so11y/bootstrap/BootstrapPluginSo11yBridge.java @@ -0,0 +1,46 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.agent.core.so11y.bootstrap; + +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.BootstrapInterRuntimeAssist; +import org.apache.skywalking.apm.agent.core.so11y.AgentSo11y; + +/** + * used by {@link BootstrapInterRuntimeAssist} + */ +@SuppressWarnings("unused") +public class BootstrapPluginSo11yBridge implements BootstrapPluginSo11y { + + public static BootstrapPluginSo11y getSo11y() { + return new BootstrapPluginSo11yBridge(); + } + + private BootstrapPluginSo11yBridge() { + } + + @Override + public void duration(final double timeCostInNanos) { + AgentSo11y.durationOfInterceptor(timeCostInNanos); + } + + @Override + public void error(final String pluginName, final String interType) { + AgentSo11y.errorOfPlugin(pluginName, interType); + } +} diff --git a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/util/GsonUtil.java b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/util/GsonUtil.java new file mode 100644 index 0000000000..fb710e4a61 --- /dev/null +++ b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/util/GsonUtil.java @@ -0,0 +1,36 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.agent.core.util; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + +public class GsonUtil { + + private static final Gson GSON = new GsonBuilder() + .disableHtmlEscaping() + .create(); + + public static String toJson(Object src) { + if (src == null) { + return null; + } + return GSON.toJson(src); + } +} diff --git a/apm-sniffer/apm-agent-core/src/main/resources/META-INF/services/org.apache.skywalking.apm.agent.core.boot.BootService b/apm-sniffer/apm-agent-core/src/main/resources/META-INF/services/org.apache.skywalking.apm.agent.core.boot.BootService index cfda93521c..f75d28cd78 100644 --- a/apm-sniffer/apm-agent-core/src/main/resources/META-INF/services/org.apache.skywalking.apm.agent.core.boot.BootService +++ b/apm-sniffer/apm-agent-core/src/main/resources/META-INF/services/org.apache.skywalking.apm.agent.core.boot.BootService @@ -36,3 +36,6 @@ org.apache.skywalking.apm.agent.core.remote.LogReportServiceClient org.apache.skywalking.apm.agent.core.conf.dynamic.ConfigurationDiscoveryService org.apache.skywalking.apm.agent.core.remote.EventReportServiceClient org.apache.skywalking.apm.agent.core.ServiceInstanceGenerator +org.apache.skywalking.apm.agent.core.asyncprofiler.AsyncProfilerTaskExecutionService +org.apache.skywalking.apm.agent.core.asyncprofiler.AsyncProfilerTaskChannelService +org.apache.skywalking.apm.agent.core.asyncprofiler.AsyncProfilerDataSender \ No newline at end of file diff --git a/apm-sniffer/apm-agent-core/src/test/java/org/apache/skywalking/apm/agent/core/boot/ServiceManagerTest.java b/apm-sniffer/apm-agent-core/src/test/java/org/apache/skywalking/apm/agent/core/boot/ServiceManagerTest.java index 48af242a43..e177920cfe 100644 --- a/apm-sniffer/apm-agent-core/src/test/java/org/apache/skywalking/apm/agent/core/boot/ServiceManagerTest.java +++ b/apm-sniffer/apm-agent-core/src/test/java/org/apache/skywalking/apm/agent/core/boot/ServiceManagerTest.java @@ -59,7 +59,7 @@ public static void afterClass() { public void testServiceDependencies() throws Exception { HashMap registryService = getFieldValue(ServiceManager.INSTANCE, "bootedServices"); - assertThat(registryService.size(), is(20)); + assertThat(registryService.size(), is(23)); assertTraceSegmentServiceClient(ServiceManager.INSTANCE.findService(TraceSegmentServiceClient.class)); assertContextManager(ServiceManager.INSTANCE.findService(ContextManager.class)); @@ -109,7 +109,7 @@ private void assertGRPCChannelManager(GRPCChannelManager service) throws Excepti assertNotNull(service); List listeners = getFieldValue(service, "listeners"); - assertEquals(listeners.size(), 10); + assertEquals(listeners.size(), 12); } private void assertSamplingService(SamplingService service) { diff --git a/apm-sniffer/apm-agent-core/src/test/java/org/apache/skywalking/apm/agent/core/context/ContinuedContextTest.java b/apm-sniffer/apm-agent-core/src/test/java/org/apache/skywalking/apm/agent/core/context/ContinuedContextTest.java new file mode 100644 index 0000000000..55bfadada2 --- /dev/null +++ b/apm-sniffer/apm-agent-core/src/test/java/org/apache/skywalking/apm/agent/core/context/ContinuedContextTest.java @@ -0,0 +1,98 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.agent.core.context; + +import org.apache.skywalking.apm.agent.core.boot.ServiceManager; +import org.apache.skywalking.apm.agent.core.conf.Config; +import org.apache.skywalking.apm.agent.core.context.ids.NewDistributedTraceId; +import org.apache.skywalking.apm.agent.core.context.trace.AbstractSpan; +import org.apache.skywalking.apm.agent.core.context.trace.LocalSpan; +import org.apache.skywalking.apm.agent.core.context.trace.NoopSpan; +import org.apache.skywalking.apm.agent.core.profile.ProfileStatusContext; +import org.apache.skywalking.apm.agent.core.test.tools.AgentServiceRule; +import org.apache.skywalking.apm.agent.core.test.tools.SegmentStorage; +import org.apache.skywalking.apm.agent.core.test.tools.SegmentStoragePoint; +import org.apache.skywalking.apm.agent.core.test.tools.TracingSegmentRunner; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.Assert; + +@RunWith(TracingSegmentRunner.class) +public class ContinuedContextTest { + + @SegmentStoragePoint + private SegmentStorage tracingData; + + @Rule + public AgentServiceRule agentServiceRule = new AgentServiceRule(); + + @BeforeClass + public static void beforeClass() { + Config.Agent.KEEP_TRACING = true; + } + + @AfterClass + public static void afterClass() { + Config.Agent.KEEP_TRACING = false; + ServiceManager.INSTANCE.shutdown(); + } + + @Test + public void testContinued() { + + NewDistributedTraceId distributedTraceId = new NewDistributedTraceId(); + ContextSnapshot snapshot = new ContextSnapshot( + "1, 2, 3", + 1, + distributedTraceId, + "/for-test-continued", + new CorrelationContext(), + new ExtensionContext(), + ProfileStatusContext.createWithNone() + ); + + AbstractSpan span = ContextManager.createLocalSpan("test-span"); + ContextManager.continued(snapshot); + + Assert.assertEquals(distributedTraceId.getId(), ContextManager.getGlobalTraceId()); + ContextManager.stopSpan(); + } + + @Test + public void testContinuedWithIgnoredSnapshot() { + + ContextSnapshot snapshot = + new ContextSnapshot(null, -1, null, null, new CorrelationContext(), new ExtensionContext(), ProfileStatusContext.createWithNone()); + + AbstractSpan span = ContextManager.createLocalSpan("test-span"); + ContextManager.continued(snapshot); + + Assert.assertTrue(span instanceof LocalSpan); + + AbstractSpan span2 = ContextManager.createLocalSpan("test-span2"); + Assert.assertTrue(span2 instanceof NoopSpan); + + ContextManager.stopSpan(); + ContextManager.stopSpan(); + } + +} diff --git a/apm-sniffer/apm-agent-core/src/test/java/org/apache/skywalking/apm/agent/core/context/IgnoreSuffixBenchmark.java b/apm-sniffer/apm-agent-core/src/test/java/org/apache/skywalking/apm/agent/core/context/IgnoreSuffixBenchmark.java new file mode 100644 index 0000000000..623d674d06 --- /dev/null +++ b/apm-sniffer/apm-agent-core/src/test/java/org/apache/skywalking/apm/agent/core/context/IgnoreSuffixBenchmark.java @@ -0,0 +1,117 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.agent.core.context; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; +import org.openjdk.jmh.runner.options.TimeValue; + +import java.util.Arrays; +import java.util.Set; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * ISSUE-12355 + */ +@State(Scope.Benchmark) +@BenchmarkMode({Mode.Throughput, Mode.SampleTime}) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +public class IgnoreSuffixBenchmark { + public static String IGNORE_SUFFIX = ".jpg,.jpeg,.js,.css,.png,.bmp,.gif,.ico,.mp3,.mp4,.html,.svg"; + private String[] ignoreSuffixArray; + private Set ignoreSuffixSet; + + private String operationName = "test.api"; + + @Setup + public void setup() { + ignoreSuffixArray = IGNORE_SUFFIX.split(","); + ignoreSuffixSet = Stream.of(ignoreSuffixArray).collect(Collectors.toSet()); + } + + @Benchmark + public boolean testArray() { + int suffixIdx = operationName.lastIndexOf("."); + return Arrays.stream(ignoreSuffixArray) + .anyMatch(a -> a.equals(operationName.substring(suffixIdx))); + } + + @Benchmark + public boolean testHashSet() { + int suffixIdx = operationName.lastIndexOf("."); + return ignoreSuffixSet.contains(operationName.substring(suffixIdx)); + } + + public static void main(String[] args) throws Exception { + Options opt = new OptionsBuilder() + .include(IgnoreSuffixBenchmark.class.getName()) + .warmupIterations(3) + .warmupTime(TimeValue.seconds(10)) + .measurementIterations(3) + .measurementTime(TimeValue.seconds(10)) + .forks(1) + .build(); + new Runner(opt).run(); + } + /** + * # JMH version: 1.33 + * # VM version: JDK 11.0.21, Java HotSpot(TM) 64-Bit Server VM, 11.0.21+9-LTS-193 + * # VM invoker: /Library/Java/JavaVirtualMachines/jdk-11.jdk/Contents/Home/bin/java + * # VM options: -javaagent:/Applications/IntelliJ IDEA CE.app/Contents/lib/idea_rt.jar=60939:/Applications/IntelliJ IDEA CE.app/Contents/bin -Dfile.encoding=UTF-8 + * # Blackhole mode: full + dont-inline hint (default, use -Djmh.blackhole.autoDetect=true to auto-detect) + * # Warmup: 3 iterations, 10 s each + * # Measurement: 3 iterations, 10 s each + * # Timeout: 10 min per iteration + * # Threads: 1 thread, will synchronize iterations + * # Benchmark mode: Sampling time + * # Benchmark: org.apache.skywalking.apm.agent.core.context.IgnoreSuffixBenchmark.testHashSet + * + * Benchmark Mode Cnt Score Error Units + * IgnoreSuffixBenchmark.testArray thrpt 3 0.007 ± 0.003 ops/ns + * IgnoreSuffixBenchmark.testHashSet thrpt 3 0.084 ± 0.035 ops/ns + * IgnoreSuffixBenchmark.testArray sample 823984 183.234 ± 11.201 ns/op + * IgnoreSuffixBenchmark.testArray:testArray·p0.00 sample 41.000 ns/op + * IgnoreSuffixBenchmark.testArray:testArray·p0.50 sample 166.000 ns/op + * IgnoreSuffixBenchmark.testArray:testArray·p0.90 sample 167.000 ns/op + * IgnoreSuffixBenchmark.testArray:testArray·p0.95 sample 209.000 ns/op + * IgnoreSuffixBenchmark.testArray:testArray·p0.99 sample 375.000 ns/op + * IgnoreSuffixBenchmark.testArray:testArray·p0.999 sample 1124.630 ns/op + * IgnoreSuffixBenchmark.testArray:testArray·p0.9999 sample 29971.248 ns/op + * IgnoreSuffixBenchmark.testArray:testArray·p1.00 sample 1130496.000 ns/op + * IgnoreSuffixBenchmark.testHashSet sample 972621 27.117 ± 1.788 ns/op + * IgnoreSuffixBenchmark.testHashSet:testHashSet·p0.00 sample ≈ 0 ns/op + * IgnoreSuffixBenchmark.testHashSet:testHashSet·p0.50 sample 41.000 ns/op + * IgnoreSuffixBenchmark.testHashSet:testHashSet·p0.90 sample 42.000 ns/op + * IgnoreSuffixBenchmark.testHashSet:testHashSet·p0.95 sample 42.000 ns/op + * IgnoreSuffixBenchmark.testHashSet:testHashSet·p0.99 sample 83.000 ns/op + * IgnoreSuffixBenchmark.testHashSet:testHashSet·p0.999 sample 167.000 ns/op + * IgnoreSuffixBenchmark.testHashSet:testHashSet·p0.9999 sample 6827.950 ns/op + * IgnoreSuffixBenchmark.testHashSet:testHashSet·p1.00 sample 478208.000 ns/op + */ +} diff --git a/apm-sniffer/apm-agent-core/src/test/java/org/apache/skywalking/apm/agent/core/logging/core/AbstractLoggerTest.java b/apm-sniffer/apm-agent-core/src/test/java/org/apache/skywalking/apm/agent/core/logging/core/AbstractLoggerTest.java new file mode 100644 index 0000000000..1d56d7700b --- /dev/null +++ b/apm-sniffer/apm-agent-core/src/test/java/org/apache/skywalking/apm/agent/core/logging/core/AbstractLoggerTest.java @@ -0,0 +1,56 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.agent.core.logging.core; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +public class AbstractLoggerTest { + + @Test + public void replaceParamNormal() { + TestLogger logger = new TestLogger("AbstractLoggerTest"); + String result = logger.testReplaceParam("from {} to {}", "a", "b"); + assertEquals("from a to b", result); + } + + @Test + public void replaceParamWithReplaceMark() { + TestLogger logger = new TestLogger("AbstractLoggerTest"); + String result = logger.testReplaceParam("from {} to {}", "a={}", "b={}"); + assertEquals("from a={} to b={}", result); + } + + private static class TestLogger extends AbstractLogger { + + public TestLogger(String targetClass) { + super(targetClass); + } + + @Override + protected String format(LogLevel level, String message, Throwable e) { + return message; + } + + String testReplaceParam(String message, Object... parameters) { + return replaceParam(message, parameters); + } + } +} \ No newline at end of file diff --git a/apm-sniffer/apm-agent-core/src/test/java/org/apache/skywalking/apm/agent/core/test/tools/TracingSegmentRunner.java b/apm-sniffer/apm-agent-core/src/test/java/org/apache/skywalking/apm/agent/core/test/tools/TracingSegmentRunner.java index 1b88070fda..a9fa848e21 100644 --- a/apm-sniffer/apm-agent-core/src/test/java/org/apache/skywalking/apm/agent/core/test/tools/TracingSegmentRunner.java +++ b/apm-sniffer/apm-agent-core/src/test/java/org/apache/skywalking/apm/agent/core/test/tools/TracingSegmentRunner.java @@ -55,7 +55,7 @@ protected Object createTest() throws Exception { @Override protected Statement withAfters(FrameworkMethod method, Object target, final Statement statement) { - return new Statement() { + Statement st = new Statement() { @Override public void evaluate() throws Throwable { if (field != null) { @@ -89,5 +89,7 @@ public void afterFinished(IgnoredTracerContext tracerContext) { } } }; + + return super.withAfters(method, target, st); } } diff --git a/apm-sniffer/apm-agent/pom.xml b/apm-sniffer/apm-agent/pom.xml index 2d8f73214b..78a3b733ac 100644 --- a/apm-sniffer/apm-agent/pom.xml +++ b/apm-sniffer/apm-agent/pom.xml @@ -22,7 +22,7 @@ org.apache.skywalking java-agent-sniffer - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT apm-agent @@ -92,6 +92,7 @@ org.codehaus.mojo:animal-sniffer-annotations io.perfmark:* org.slf4j:* + tools.profiler:async-profiler @@ -105,6 +106,7 @@ net.bytebuddy:byte-buddy META-INF/versions/9/module-info.class + META-INF/versions/24/** diff --git a/apm-sniffer/apm-agent/src/main/java/org/apache/skywalking/apm/agent/SkyWalkingAgent.java b/apm-sniffer/apm-agent/src/main/java/org/apache/skywalking/apm/agent/SkyWalkingAgent.java index 2dd3fe8b38..b0dc55913f 100644 --- a/apm-sniffer/apm-agent/src/main/java/org/apache/skywalking/apm/agent/SkyWalkingAgent.java +++ b/apm-sniffer/apm-agent/src/main/java/org/apache/skywalking/apm/agent/SkyWalkingAgent.java @@ -128,6 +128,7 @@ static void installClassTransformer(Instrumentation instrumentation, PluginFinde .or(nameContains(".asm.")) .or(nameContains(".reflectasm.")) .or(nameStartsWith("sun.reflect")) + .or(nameStartsWith("sun.nio.cs")) .or(allSkyWalkingAgentExcludeToolkit()) .or(ElementMatchers.isSynthetic())); diff --git a/apm-sniffer/apm-sdk-plugin/CLAUDE.md b/apm-sniffer/apm-sdk-plugin/CLAUDE.md new file mode 100644 index 0000000000..a0c73a2a04 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/CLAUDE.md @@ -0,0 +1,401 @@ +# CLAUDE.md - SDK Plugin Development Guide + +This guide covers developing standard SDK plugins in `apm-sniffer/apm-sdk-plugin/`. + +## Plugin Instrumentation APIs (v1 vs v2) + +The agent provides two instrumentation APIs. **V2 is recommended** for all new plugins; v1 is legacy and should only be used for maintaining existing plugins. + +### V2 API (Recommended) + +V2 provides a `MethodInvocationContext` that is shared across all interception phases (`beforeMethod`, `afterMethod`, `handleMethodException`), allowing you to pass data (e.g., spans) between phases. + +**Instrumentation class (extends `ClassEnhancePluginDefineV2`):** +```java +public class XxxInstrumentation extends ClassInstanceMethodsEnhancePluginDefineV2 { + @Override + protected ClassMatch enhanceClass() { + return NameMatch.byName("target.class.Name"); + } + + @Override + public ConstructorInterceptPoint[] getConstructorsInterceptPoints() { + return new ConstructorInterceptPoint[] { ... }; + } + + @Override + public InstanceMethodsInterceptV2Point[] getInstanceMethodsInterceptV2Points() { + return new InstanceMethodsInterceptV2Point[] { + new InstanceMethodsInterceptV2Point() { + @Override + public ElementMatcher getMethodsMatcher() { + return named("targetMethod"); + } + + @Override + public String getMethodsInterceptorV2() { + return "org.apache.skywalking.apm.plugin.xxx.XxxInterceptor"; + } + + @Override + public boolean isOverrideArgs() { + return false; + } + } + }; + } +} +``` + +**Interceptor class (implements `InstanceMethodsAroundInterceptorV2`):** +```java +public class XxxInterceptor implements InstanceMethodsAroundInterceptorV2 { + @Override + public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, + Class[] argumentsTypes, MethodInvocationContext context) { + AbstractSpan span = ContextManager.createLocalSpan("operationName"); + context.setContext(span); // Pass to afterMethod/handleMethodException + } + + @Override + public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, + Class[] argumentsTypes, Object ret, MethodInvocationContext context) { + AbstractSpan span = (AbstractSpan) context.getContext(); + span.asyncFinish(); + return ret; + } + + @Override + public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments, + Class[] argumentsTypes, Throwable t, MethodInvocationContext context) { + AbstractSpan span = (AbstractSpan) context.getContext(); + span.log(t); + } +} +``` + +**Key V2 classes:** +- `ClassEnhancePluginDefineV2` - Base class for plugins with both instance and static methods +- `ClassInstanceMethodsEnhancePluginDefineV2` - For instance methods only +- `ClassStaticMethodsEnhancePluginDefineV2` - For static methods only +- `InstanceMethodsAroundInterceptorV2` - Interceptor interface with `MethodInvocationContext` +- `StaticMethodsAroundInterceptorV2` - Static method interceptor with context + +### V1 API (Legacy) + +V1 uses `MethodInterceptResult` only in `beforeMethod` and has no shared context between phases. **Only use for maintaining existing legacy plugins.** + +**Key V1 classes (legacy):** +- `ClassEnhancePluginDefine` +- `ClassInstanceMethodsEnhancePluginDefine` +- `ClassStaticMethodsEnhancePluginDefine` +- `InstanceMethodsAroundInterceptor` +- `StaticMethodsAroundInterceptor` + +## Plugin Development Rules + +### Class Matching Restrictions + +**CRITICAL: Never use `.class` references in instrumentation definitions:** +```java +// WRONG - will break the agent if ThirdPartyClass doesn't exist +takesArguments(ThirdPartyClass.class) +byName(ThirdPartyClass.class.getName()) + +// CORRECT - use string literals +takesArguments("com.example.ThirdPartyClass") +byName("com.example.ThirdPartyClass") +``` + +**ClassMatch options:** +- `byName(String)`: Match by full class name (package + class name) - **preferred** +- `byClassAnnotationMatch`: Match classes with specific annotations (does NOT support inherited annotations) +- `byMethodAnnotationMatch`: Match classes with methods having specific annotations +- `byHierarchyMatch`: Match by parent class/interface - **avoid unless necessary** (performance impact) + +### Witness Classes/Methods + +Use witness classes/methods to activate plugins only for specific library versions: +```java +@Override +protected String[] witnessClasses() { + return new String[] { "com.example.VersionSpecificClass" }; +} + +@Override +protected List witnessMethods() { + return Collections.singletonList( + new WitnessMethod("com.example.SomeClass", ElementMatchers.named("specificMethod")) + ); +} +``` + +### Plugin Configuration + +Use `@PluginConfig` annotation for custom plugin settings: +```java +public class MyPluginConfig { + public static class Plugin { + @PluginConfig(root = MyPluginConfig.class) + public static class MyPlugin { + public static boolean SOME_SETTING = false; + } + } +} +``` +Config key becomes: `plugin.myplugin.some_setting` + +### Dependency Management + +**Plugin dependencies must use `provided` scope:** +```xml + + com.example + target-library + ${version} + provided + +``` + +**Agent core dependency policy:** +- New dependencies in agent core are treated with extreme caution +- Prefer using existing imported libraries already in the project +- Prefer JDK standard libraries over third-party libraries +- Plugins should rely on the target application's libraries (provided scope), not bundle them + +## Tracing Concepts + +### Span Types +- **EntrySpan**: Service provider/endpoint (HTTP server, MQ consumer) +- **LocalSpan**: Internal method (no remote calls) +- **ExitSpan**: Client call (HTTP client, DB access, MQ producer) + +### SpanLayer (required for EntrySpan/ExitSpan) +- `DB`: Database access +- `RPC_FRAMEWORK`: RPC calls (not ordinary HTTP) +- `HTTP`: HTTP calls +- `MQ`: Message queue +- `UNKNOWN`: Default + +### Context Propagation +- **ContextCarrier**: Cross-process propagation (serialize to headers/attachments) +- **ContextSnapshot**: Cross-thread propagation (in-memory, no serialization) + +### Required Span Attributes +For EntrySpan and ExitSpan, always set: +```java +span.setComponent(ComponentsDefine.YOUR_COMPONENT); +span.setLayer(SpanLayer.HTTP); // or DB, MQ, RPC_FRAMEWORK +``` + +### Special Tags for OAP Analysis +| Tag | Purpose | +|-----|---------| +| `http.status_code` | HTTP response code (integer) | +| `db.type` | Database type (e.g., "sql", "redis") | +| `db.statement` | SQL/query statement (enables slow query analysis) | +| `cache.type`, `cache.op`, `cache.cmd`, `cache.key` | Cache metrics | +| `mq.queue`, `mq.topic` | MQ metrics | + +## Meter Plugin APIs + +For collecting numeric metrics (alternative to tracing): +```java +// Counter +Counter counter = MeterFactory.counter("metric_name") + .tag("key", "value") + .mode(Counter.Mode.INCREMENT) + .build(); +counter.increment(1d); + +// Gauge +Gauge gauge = MeterFactory.gauge("metric_name", () -> getValue()) + .tag("key", "value") + .build(); + +// Histogram +Histogram histogram = MeterFactory.histogram("metric_name") + .steps(Arrays.asList(1, 5, 10)) + .build(); +histogram.addValue(3); +``` + +## Adding a New SDK Plugin + +1. Create directory: `apm-sniffer/apm-sdk-plugin/{framework}-{version}-plugin/` +2. Implement instrumentation class using **V2 API** (extend `ClassInstanceMethodsEnhancePluginDefineV2`) +3. Implement interceptor class using **V2 API** (implement `InstanceMethodsAroundInterceptorV2`) +4. Register plugin in `skywalking-plugin.def` file +5. Add test scenario in `test/plugin/scenarios/` + +## Plugin Test Framework + +The plugin test framework verifies plugin functionality using Docker containers with real services and a mock OAP backend. + +### Environment Requirements +- MacOS/Linux +- JDK 8+ +- Docker & Docker Compose + +### Test Case Structure + +**JVM-container (preferred):** +``` +{scenario}-scenario/ +├── bin/ +│ └── startup.sh # JVM startup script (required) +├── config/ +│ └── expectedData.yaml # Expected trace/meter/log data +├── src/main/java/... # Test application code +├── pom.xml +├── configuration.yml # Test case configuration +└── support-version.list # Supported versions (one per line) +``` + +**Tomcat-container:** +``` +{scenario}-scenario/ +├── config/ +│ └── expectedData.yaml +├── src/main/ +│ ├── java/... +│ └── webapp/WEB-INF/web.xml +├── pom.xml +├── configuration.yml +└── support-version.list +``` + +### Key Configuration Files + +**configuration.yml:** +```yaml +type: jvm # or tomcat +entryService: http://localhost:8080/case # Entry endpoint (GET) +healthCheck: http://localhost:8080/health # Health check endpoint (HEAD) +startScript: ./bin/startup.sh # JVM-container only +runningMode: default # default|with_optional|with_bootstrap +withPlugins: apm-spring-annotation-plugin-*.jar # For optional/bootstrap modes +environment: + - KEY=value +dependencies: # External services (docker-compose style) + mysql: + image: mysql:8.0 + hostname: mysql + environment: + - MYSQL_ROOT_PASSWORD=root +``` + +**support-version.list:** +``` +# One version per line, use # for comments +# Only include ONE version per minor version (not all patch versions) +4.3.6 +4.4.1 +4.5.0 +``` + +**expectedData.yaml:** + +Trace and meter expectations are typically in separate scenarios. + +*For tracing plugins:* +```yaml +segmentItems: + - serviceName: your-scenario + segmentSize: ge 1 # Operators: eq, ge, gt, nq + segments: + - segmentId: not null + spans: + - operationName: /your/endpoint + parentSpanId: -1 # -1 for root span + spanId: 0 + spanLayer: Http # Http, DB, RPC_FRAMEWORK, MQ, CACHE, Unknown + spanType: Entry # Entry, Exit, Local + startTime: nq 0 + endTime: nq 0 + componentId: 1 + isError: false + peer: '' # Empty string for Entry/Local, required for Exit + skipAnalysis: false + tags: + - {key: url, value: not null} + - {key: http.method, value: GET} + - {key: http.status_code, value: '200'} + logs: [] + refs: [] # SegmentRefs for cross-process/cross-thread +``` + +*For meter plugins:* +```yaml +meterItems: + - serviceName: your-scenario + meterSize: ge 1 + meters: + - meterId: + name: test_counter + tags: + - {name: key1, value: value1} # Note: uses 'name' not 'key' + singleValue: gt 0 # For counter/gauge + - meterId: + name: test_histogram + tags: + - {name: key1, value: value1} + histogramBuckets: # For histogram + - 0.0 + - 1.0 + - 5.0 + - 10.0 +``` + +**startup.sh (JVM-container):** +```bash +#!/bin/bash +home="$(cd "$(dirname $0)"; pwd)" +# ${agent_opts} is REQUIRED - contains agent parameters +java -jar ${agent_opts} ${home}/../libs/your-scenario.jar & +``` + +### Running Plugin Tests Locally + +```bash +# Run a specific scenario +bash ./test/plugin/run.sh -f {scenario_name} + +# IMPORTANT: Rebuild agent if apm-sniffer code changed +./mvnw clean package -DskipTests -pl apm-sniffer + +# Use generator to create new test case +bash ./test/plugin/generator.sh +``` + +### Adding Tests to CI + +Add scenario to the appropriate `.github/workflows/` file: +- Use `python3 tools/select-group.py` to find the file with fewest cases +- **JDK 8 tests**: `plugins-test..yaml` +- **JDK 17 tests**: `plugins-jdk17-test..yaml` +- **JDK 21 tests**: `plugins-jdk21-test..yaml` +- **JDK 25 tests**: `plugins-jdk25-test..yaml` + +```yaml +matrix: + case: + - your-scenario-scenario +``` + +### Test Code Package Naming +- Test code: `org.apache.skywalking.apm.testcase.*` +- Code to be instrumented: `test.org.apache.skywalking.apm.testcase.*` + +## Tips for AI Assistants + +1. **Use V2 instrumentation API**: Always use V2 classes for new plugins; V1 is legacy +2. **NEVER use `.class` references**: Always use string literals for class names +3. **Always set component and layer**: For EntrySpan and ExitSpan, always call `setComponent()` and `setLayer()` +4. **Prefer `byName` for class matching**: Avoid `byHierarchyMatch` unless necessary (performance impact) +5. **Use witness classes for version-specific plugins**: Implement `witnessClasses()` or `witnessMethods()` +6. **Follow plugin patterns**: Use existing V2 plugins as templates +7. **Plugin naming**: Follow `{framework}-{version}-plugin` convention +8. **Register plugins**: Always add plugin definition to `skywalking-plugin.def` file +9. **Java version compatibility**: Agent core must maintain Java 8 compatibility, but individual plugins may target higher JDK versions +10. **Shaded dependencies**: Core dependencies are shaded to avoid classpath conflicts diff --git a/apm-sniffer/apm-sdk-plugin/activemq-5.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/activemq-5.x-plugin/pom.xml index 0956d91d4f..00abc003dc 100644 --- a/apm-sniffer/apm-sdk-plugin/activemq-5.x-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/activemq-5.x-plugin/pom.xml @@ -21,7 +21,7 @@ apm-sdk-plugin org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-sdk-plugin/activemq-artemis-jakarta-client-2.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/activemq-artemis-jakarta-client-2.x-plugin/pom.xml index cc6a9f1ae2..17f0dc4442 100644 --- a/apm-sniffer/apm-sdk-plugin/activemq-artemis-jakarta-client-2.x-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/activemq-artemis-jakarta-client-2.x-plugin/pom.xml @@ -21,7 +21,7 @@ apm-sdk-plugin org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-sdk-plugin/aerospike-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/aerospike-plugin/pom.xml index 7f9148573f..f2c5ac64f4 100644 --- a/apm-sniffer/apm-sdk-plugin/aerospike-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/aerospike-plugin/pom.xml @@ -21,7 +21,7 @@ apm-sdk-plugin org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-sdk-plugin/apm-armeria-plugins/apm-armeria-0.84.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/apm-armeria-plugins/apm-armeria-0.84.x-plugin/pom.xml index 1a31ced10b..81ea378be9 100644 --- a/apm-sniffer/apm-sdk-plugin/apm-armeria-plugins/apm-armeria-0.84.x-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/apm-armeria-plugins/apm-armeria-0.84.x-plugin/pom.xml @@ -20,7 +20,7 @@ apm-armeria-plugins org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-sdk-plugin/apm-armeria-plugins/apm-armeria-0.85.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/apm-armeria-plugins/apm-armeria-0.85.x-plugin/pom.xml index b987bf0852..d4897e1325 100644 --- a/apm-sniffer/apm-sdk-plugin/apm-armeria-plugins/apm-armeria-0.85.x-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/apm-armeria-plugins/apm-armeria-0.85.x-plugin/pom.xml @@ -20,7 +20,7 @@ apm-armeria-plugins org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-sdk-plugin/apm-armeria-plugins/apm-armeria-1.0.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/apm-armeria-plugins/apm-armeria-1.0.x-plugin/pom.xml index 3209d63d6c..6a32acee05 100644 --- a/apm-sniffer/apm-sdk-plugin/apm-armeria-plugins/apm-armeria-1.0.x-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/apm-armeria-plugins/apm-armeria-1.0.x-plugin/pom.xml @@ -20,7 +20,7 @@ apm-armeria-plugins org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-sdk-plugin/apm-armeria-plugins/pom.xml b/apm-sniffer/apm-sdk-plugin/apm-armeria-plugins/pom.xml index 24f68f1cbb..f55ffcb8fc 100644 --- a/apm-sniffer/apm-sdk-plugin/apm-armeria-plugins/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/apm-armeria-plugins/pom.xml @@ -20,7 +20,7 @@ apm-sdk-plugin org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 pom @@ -35,7 +35,6 @@ UTF-8 - /.. \ No newline at end of file diff --git a/apm-sniffer/apm-sdk-plugin/asynchttpclient-2.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/asynchttpclient-2.x-plugin/pom.xml index 7a3dcaf36e..fe491b9ee2 100644 --- a/apm-sniffer/apm-sdk-plugin/asynchttpclient-2.x-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/asynchttpclient-2.x-plugin/pom.xml @@ -21,7 +21,7 @@ org.apache.skywalking apm-sdk-plugin - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-sdk-plugin/avro-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/avro-plugin/pom.xml index ed69c782a8..cf5039fc31 100644 --- a/apm-sniffer/apm-sdk-plugin/avro-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/avro-plugin/pom.xml @@ -21,7 +21,7 @@ apm-sdk-plugin org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-sdk-plugin/baidu-brpc-3.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/baidu-brpc-3.x-plugin/pom.xml index 2de9d1e726..6716583e00 100644 --- a/apm-sniffer/apm-sdk-plugin/baidu-brpc-3.x-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/baidu-brpc-3.x-plugin/pom.xml @@ -21,7 +21,7 @@ apm-sdk-plugin org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 jar diff --git a/apm-sniffer/apm-sdk-plugin/baidu-brpc-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/baidu-brpc-plugin/pom.xml index a793335f55..7f31b50046 100644 --- a/apm-sniffer/apm-sdk-plugin/baidu-brpc-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/baidu-brpc-plugin/pom.xml @@ -21,7 +21,7 @@ apm-sdk-plugin org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 jar diff --git a/apm-sniffer/apm-sdk-plugin/c3p0-0.9.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/c3p0-0.9.x-plugin/pom.xml new file mode 100644 index 0000000000..7b7a82628a --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/c3p0-0.9.x-plugin/pom.xml @@ -0,0 +1,49 @@ + + + + + + apm-sdk-plugin + org.apache.skywalking + 9.7.0-SNAPSHOT + + 4.0.0 + + apm-c3p0-0.9.x-plugin + jar + c3p0-0.9.x-plugin + + + 0.9.5 + + + + + com.mchange + c3p0 + ${c3p0.version} + provided + + + org.apache.skywalking + apm-jdbc-commons + ${project.version} + provided + + + diff --git a/apm-sniffer/apm-sdk-plugin/c3p0-0.9.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/c3p0/PoolConstants.java b/apm-sniffer/apm-sdk-plugin/c3p0-0.9.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/c3p0/PoolConstants.java new file mode 100644 index 0000000000..4536cd5112 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/c3p0-0.9.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/c3p0/PoolConstants.java @@ -0,0 +1,34 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.skywalking.apm.plugin.c3p0; + +public class PoolConstants { + public static final String POOL_CONNECTION = "C3P0/Connection/"; + public static final String METER_NAME = "datasource"; + public static final String METER_TAG_NAME = "name"; + public static final String METER_TAG_STATUS = "status"; + + //==================== METRICS ========================// + public static final String NUM_TOTAL_CONNECTIONS = "numTotalConnections"; + public static final String NUM_BUSY_CONNECTIONS = "numBusyConnections"; + public static final String NUM_IDLE_CONNECTIONS = "numIdleConnections"; + public static final String MAX_IDLE_TIME = "maxIdleTime"; + public static final String MIN_POOL_SIZE = "minPoolSize"; + public static final String MAX_POOL_SIZE = "maxPoolSize"; + public static final String INITIAL_POOL_SIZE = "initialPoolSize"; +} diff --git a/apm-sniffer/apm-sdk-plugin/c3p0-0.9.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/c3p0/PoolingConnectionCloseInterceptor.java b/apm-sniffer/apm-sdk-plugin/c3p0-0.9.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/c3p0/PoolingConnectionCloseInterceptor.java new file mode 100644 index 0000000000..aa3a41fa33 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/c3p0-0.9.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/c3p0/PoolingConnectionCloseInterceptor.java @@ -0,0 +1,62 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.skywalking.apm.plugin.c3p0; + +import org.apache.skywalking.apm.agent.core.context.ContextManager; +import org.apache.skywalking.apm.agent.core.context.trace.AbstractSpan; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceMethodsAroundInterceptor; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult; +import org.apache.skywalking.apm.network.trace.component.ComponentsDefine; + +import java.lang.reflect.Method; + +/** + * {@link PoolingConnectionCloseInterceptor} intercepted the method of C3P0 closing connection. + */ +public class PoolingConnectionCloseInterceptor implements InstanceMethodsAroundInterceptor { + + @Override + public void beforeMethod(EnhancedInstance objInst, + Method method, + Object[] allArguments, + Class[] argumentsTypes, + MethodInterceptResult result) throws Throwable { + AbstractSpan span = ContextManager.createLocalSpan(PoolConstants.POOL_CONNECTION + method.getName()); + span.setComponent(ComponentsDefine.C3P0); + } + + @Override + public Object afterMethod(EnhancedInstance objInst, + Method method, + Object[] allArguments, + Class[] argumentsTypes, + Object ret) throws Throwable { + ContextManager.stopSpan(); + return ret; + } + + @Override + public void handleMethodException(EnhancedInstance objInst, + Method method, + Object[] allArguments, + Class[] argumentsTypes, + Throwable t) { + ContextManager.activeSpan().errorOccurred().log(t); + } +} diff --git a/apm-sniffer/apm-sdk-plugin/c3p0-0.9.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/c3p0/PoolingConnectionGetInterceptor.java b/apm-sniffer/apm-sdk-plugin/c3p0-0.9.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/c3p0/PoolingConnectionGetInterceptor.java new file mode 100644 index 0000000000..aff1e69e6c --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/c3p0-0.9.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/c3p0/PoolingConnectionGetInterceptor.java @@ -0,0 +1,61 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.skywalking.apm.plugin.c3p0; + +import java.lang.reflect.Method; +import org.apache.skywalking.apm.agent.core.context.ContextManager; +import org.apache.skywalking.apm.agent.core.context.trace.AbstractSpan; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceMethodsAroundInterceptor; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult; +import org.apache.skywalking.apm.network.trace.component.ComponentsDefine; + +/** + * {@link PoolingConnectionGetInterceptor} intercepted the method of C3P0 getting connection. + */ +public class PoolingConnectionGetInterceptor implements InstanceMethodsAroundInterceptor { + + @Override + public void beforeMethod(EnhancedInstance objInst, + Method method, + Object[] allArguments, + Class[] argumentsTypes, + MethodInterceptResult result) throws Throwable { + AbstractSpan span = ContextManager.createLocalSpan(PoolConstants.POOL_CONNECTION + method.getName()); + span.setComponent(ComponentsDefine.C3P0); + } + + @Override + public Object afterMethod(EnhancedInstance objInst, + Method method, + Object[] allArguments, + Class[] argumentsTypes, + Object ret) throws Throwable { + ContextManager.stopSpan(); + return ret; + } + + @Override + public void handleMethodException(EnhancedInstance objInst, + Method method, + Object[] allArguments, + Class[] argumentsTypes, + Throwable t) { + ContextManager.activeSpan().errorOccurred().log(t); + } +} diff --git a/apm-sniffer/apm-sdk-plugin/c3p0-0.9.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/c3p0/PoolingCreationInterceptor.java b/apm-sniffer/apm-sdk-plugin/c3p0-0.9.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/c3p0/PoolingCreationInterceptor.java new file mode 100644 index 0000000000..6a5514e8c5 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/c3p0-0.9.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/c3p0/PoolingCreationInterceptor.java @@ -0,0 +1,132 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.skywalking.apm.plugin.c3p0; + +import com.mchange.v2.c3p0.C3P0Registry; +import com.mchange.v2.c3p0.ComboPooledDataSource; +import java.lang.reflect.Method; +import java.sql.SQLException; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.function.Function; +import java.util.function.Supplier; +import org.apache.skywalking.apm.agent.core.context.ContextManager; +import org.apache.skywalking.apm.agent.core.meter.MeterFactory; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceMethodsAroundInterceptor; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult; +import org.apache.skywalking.apm.plugin.jdbc.connectionurl.parser.URLParser; +import org.apache.skywalking.apm.plugin.jdbc.trace.ConnectionInfo; + +/** + * {@link PoolingCreationInterceptor} intercepted the method of pool creation. + */ +public class PoolingCreationInterceptor implements InstanceMethodsAroundInterceptor { + private static final Set TOKEN_MAP = new HashSet<>(16); + + @Override + public void beforeMethod(final EnhancedInstance enhancedInstance, + final Method method, + final Object[] objects, + final Class[] classes, + final MethodInterceptResult methodInterceptResult) throws Throwable { + + } + + @Override + public Object afterMethod(final EnhancedInstance enhancedInstance, + final Method method, + final Object[] objects, + final Class[] classes, + final Object ret) throws Throwable { + C3P0Registry.getPooledDataSources().forEach(obj -> { + ComboPooledDataSource pooledDataSource = (ComboPooledDataSource) obj; + if (!TOKEN_MAP.contains(pooledDataSource.getIdentityToken())) { + ConnectionInfo connectionInfo = URLParser.parser(pooledDataSource.getJdbcUrl()); + String tagValue = connectionInfo.getDatabaseName() + "_" + connectionInfo.getDatabasePeer(); + Map>> metricMap = getMetrics(); + metricMap.forEach( + (key, value) -> MeterFactory.gauge(PoolConstants.METER_NAME, value.apply(pooledDataSource)) + .tag(PoolConstants.METER_TAG_NAME, tagValue) + .tag(PoolConstants.METER_TAG_STATUS, key) + .build()); + TOKEN_MAP.add(pooledDataSource.getIdentityToken()); + } + }); + return ret; + } + + @Override + public void handleMethodException(final EnhancedInstance enhancedInstance, + final Method method, + final Object[] objects, + final Class[] classes, + final Throwable t) { + ContextManager.activeSpan().errorOccurred().log(t); + } + + private Map>> getMetrics() { + Map>> metricMap = new HashMap<>(); + metricMap.put(PoolConstants.NUM_TOTAL_CONNECTIONS, (ComboPooledDataSource pooledDataSource) -> () -> { + double numConnections = 0; + try { + numConnections = pooledDataSource.getNumConnections(); + } catch (SQLException e) { + ContextManager.activeSpan().errorOccurred().log(e); + } + return numConnections; + }); + metricMap.put(PoolConstants.NUM_BUSY_CONNECTIONS, (ComboPooledDataSource pooledDataSource) -> () -> { + double numBusyConnections = 0; + try { + numBusyConnections = pooledDataSource.getNumBusyConnections(); + } catch (SQLException e) { + ContextManager.activeSpan().errorOccurred().log(e); + } + return numBusyConnections; + }); + metricMap.put(PoolConstants.NUM_IDLE_CONNECTIONS, (ComboPooledDataSource pooledDataSource) -> () -> { + double numIdleConnections = 0; + try { + numIdleConnections = pooledDataSource.getNumIdleConnections(); + } catch (SQLException e) { + ContextManager.activeSpan().errorOccurred().log(e); + } + return numIdleConnections; + }); + metricMap.put( + PoolConstants.MAX_IDLE_TIME, + (ComboPooledDataSource pooledDataSource) -> () -> (double) pooledDataSource.getMaxIdleTime() + ); + metricMap.put( + PoolConstants.MIN_POOL_SIZE, + (ComboPooledDataSource pooledDataSource) -> () -> (double) pooledDataSource.getMinPoolSize() + ); + metricMap.put( + PoolConstants.MAX_POOL_SIZE, + (ComboPooledDataSource pooledDataSource) -> () -> (double) pooledDataSource.getMaxPoolSize() + ); + metricMap.put( + PoolConstants.INITIAL_POOL_SIZE, + (ComboPooledDataSource pooledDataSource) -> () -> (double) pooledDataSource.getInitialPoolSize() + ); + return metricMap; + } +} diff --git a/apm-sniffer/apm-sdk-plugin/c3p0-0.9.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/c3p0/define/C3P0NewProxyConnectionInstrumentation.java b/apm-sniffer/apm-sdk-plugin/c3p0-0.9.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/c3p0/define/C3P0NewProxyConnectionInstrumentation.java new file mode 100644 index 0000000000..e049023a23 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/c3p0-0.9.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/c3p0/define/C3P0NewProxyConnectionInstrumentation.java @@ -0,0 +1,72 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.skywalking.apm.plugin.c3p0.define; + +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.matcher.ElementMatcher; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine; +import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch; + +import static net.bytebuddy.matcher.ElementMatchers.named; +import static org.apache.skywalking.apm.agent.core.plugin.match.NameMatch.byName; + +/** + * C3p0 is a mature, highly concurrent JDBC Connection pooling library, with support for caching and reuse of + * PreparedStatement objects. Defined by the jdbc3 spec and the optional extensions to jdbc2. c3p0 now also fully + * supports the jdbc4. {@link C3P0NewProxyConnectionInstrumentation} defines the instance methods enhance plugin for + * connection closed + */ +public class C3P0NewProxyConnectionInstrumentation extends ClassInstanceMethodsEnhancePluginDefine { + private static final String ENHANCE_CLASS = "com.mchange.v2.c3p0.impl.NewProxyConnection"; + private static final String ENHANCE_METHOD = "close"; + private static final String INTERCEPTOR_CLASS = "org.apache.skywalking.apm.plugin.c3p0.PoolingConnectionCloseInterceptor"; + + @Override + protected ClassMatch enhanceClass() { + return byName(ENHANCE_CLASS); + } + + @Override + public ConstructorInterceptPoint[] getConstructorsInterceptPoints() { + return new ConstructorInterceptPoint[0]; + } + + @Override + public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() { + return new InstanceMethodsInterceptPoint[] { + new InstanceMethodsInterceptPoint() { + @Override + public ElementMatcher getMethodsMatcher() { + return named(ENHANCE_METHOD); + } + + @Override + public String getMethodsInterceptor() { + return INTERCEPTOR_CLASS; + } + + @Override + public boolean isOverrideArgs() { + return false; + } + } + }; + } +} \ No newline at end of file diff --git a/apm-sniffer/apm-sdk-plugin/c3p0-0.9.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/c3p0/define/C3P0PoolManagerInstrumentation.java b/apm-sniffer/apm-sdk-plugin/c3p0-0.9.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/c3p0/define/C3P0PoolManagerInstrumentation.java new file mode 100644 index 0000000000..57510c2362 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/c3p0-0.9.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/c3p0/define/C3P0PoolManagerInstrumentation.java @@ -0,0 +1,72 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.skywalking.apm.plugin.c3p0.define; + +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.matcher.ElementMatcher; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine; +import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch; + +import static net.bytebuddy.matcher.ElementMatchers.named; +import static org.apache.skywalking.apm.agent.core.plugin.match.NameMatch.byName; + +/** + * C3p0 is a mature, highly concurrent JDBC Connection pooling library, with support for caching and reuse of + * PreparedStatement objects. Defined by the jdbc3 spec and the optional extensions to jdbc2. c3p0 now also fully + * supports the jdbc4. { @link C3P0PoolManagerInstrumentation} defines the instance methods enhance plugin for pool + * creation. + */ +public class C3P0PoolManagerInstrumentation extends ClassInstanceMethodsEnhancePluginDefine { + private static final String ENHANCE_CLASS = "com.mchange.v2.c3p0.impl.C3P0PooledConnectionPoolManager"; + private static final String ENHANCE_METHOD = "createPooledConnectionPool"; + private static final String INTERCEPTOR_CLASS = "org.apache.skywalking.apm.plugin.c3p0.PoolingCreationInterceptor"; + + @Override + protected ClassMatch enhanceClass() { + return byName(ENHANCE_CLASS); + } + + @Override + public ConstructorInterceptPoint[] getConstructorsInterceptPoints() { + return new ConstructorInterceptPoint[0]; + } + + @Override + public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() { + return new InstanceMethodsInterceptPoint[] { + new InstanceMethodsInterceptPoint() { + @Override + public ElementMatcher getMethodsMatcher() { + return named(ENHANCE_METHOD); + } + + @Override + public String getMethodsInterceptor() { + return INTERCEPTOR_CLASS; + } + + @Override + public boolean isOverrideArgs() { + return false; + } + } + }; + } +} diff --git a/apm-sniffer/apm-sdk-plugin/c3p0-0.9.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/c3p0/define/C3P0PooledDataSourceInstrumentation.java b/apm-sniffer/apm-sdk-plugin/c3p0-0.9.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/c3p0/define/C3P0PooledDataSourceInstrumentation.java new file mode 100644 index 0000000000..e2ae84731b --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/c3p0-0.9.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/c3p0/define/C3P0PooledDataSourceInstrumentation.java @@ -0,0 +1,72 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.skywalking.apm.plugin.c3p0.define; + +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.matcher.ElementMatcher; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine; +import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch; + +import static net.bytebuddy.matcher.ElementMatchers.named; +import static org.apache.skywalking.apm.agent.core.plugin.match.NameMatch.byName; + +/** + * C3p0 is a mature, highly concurrent JDBC Connection pooling library, with support for caching and reuse of + * PreparedStatement objects. Defined by the jdbc3 spec and the optional extensions to jdbc2. c3p0 now also fully + * supports the jdbc4. { @link C3P0PooledDataSourceInstrumentation} defines the instance methods enhance plugin for + * connection init + */ +public class C3P0PooledDataSourceInstrumentation extends ClassInstanceMethodsEnhancePluginDefine { + private static final String ENHANCE_CLASS = "com.mchange.v2.c3p0.ComboPooledDataSource"; + private static final String ENHANCE_METHOD = "getConnection"; + private static final String INTERCEPTOR_CLASS = "org.apache.skywalking.apm.plugin.c3p0.PoolingConnectionGetInterceptor"; + + @Override + protected ClassMatch enhanceClass() { + return byName(ENHANCE_CLASS); + } + + @Override + public ConstructorInterceptPoint[] getConstructorsInterceptPoints() { + return new ConstructorInterceptPoint[0]; + } + + @Override + public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() { + return new InstanceMethodsInterceptPoint[] { + new InstanceMethodsInterceptPoint() { + @Override + public ElementMatcher getMethodsMatcher() { + return named(ENHANCE_METHOD); + } + + @Override + public String getMethodsInterceptor() { + return INTERCEPTOR_CLASS; + } + + @Override + public boolean isOverrideArgs() { + return false; + } + } + }; + } +} diff --git a/apm-sniffer/apm-sdk-plugin/c3p0-0.9.x-plugin/src/main/resources/skywalking-plugin.def b/apm-sniffer/apm-sdk-plugin/c3p0-0.9.x-plugin/src/main/resources/skywalking-plugin.def new file mode 100644 index 0000000000..bbb99e5908 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/c3p0-0.9.x-plugin/src/main/resources/skywalking-plugin.def @@ -0,0 +1,19 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +c3p0-0.9.x=org.apache.skywalking.apm.plugin.c3p0.define.C3P0NewProxyConnectionInstrumentation +c3p0-0.9.x=org.apache.skywalking.apm.plugin.c3p0.define.C3P0PoolManagerInstrumentation +c3p0-0.9.x=org.apache.skywalking.apm.plugin.c3p0.define.C3P0PooledDataSourceInstrumentation \ No newline at end of file diff --git a/apm-sniffer/apm-sdk-plugin/canal-1.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/canal-1.x-plugin/pom.xml index c5f85346a3..fd4b295a51 100644 --- a/apm-sniffer/apm-sdk-plugin/canal-1.x-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/canal-1.x-plugin/pom.xml @@ -21,7 +21,7 @@ apm-sdk-plugin org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-sdk-plugin/cassandra-java-driver-3.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/cassandra-java-driver-3.x-plugin/pom.xml index 043ca5f1d1..ecc5d3535d 100644 --- a/apm-sniffer/apm-sdk-plugin/cassandra-java-driver-3.x-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/cassandra-java-driver-3.x-plugin/pom.xml @@ -20,7 +20,7 @@ apm-sdk-plugin org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-sdk-plugin/clickhouse-0.3.1-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/clickhouse-0.3.1-plugin/pom.xml index dcc05354b9..c80e82160b 100755 --- a/apm-sniffer/apm-sdk-plugin/clickhouse-0.3.1-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/clickhouse-0.3.1-plugin/pom.xml @@ -20,7 +20,7 @@ apm-sdk-plugin org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-sdk-plugin/clickhouse-0.3.2.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/clickhouse-0.3.2.x-plugin/pom.xml index 00bc941f41..e50763327d 100755 --- a/apm-sniffer/apm-sdk-plugin/clickhouse-0.3.2.x-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/clickhouse-0.3.2.x-plugin/pom.xml @@ -20,7 +20,7 @@ apm-sdk-plugin org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-sdk-plugin/cxf-3.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/cxf-3.x-plugin/pom.xml index 612482786a..9744820eca 100644 --- a/apm-sniffer/apm-sdk-plugin/cxf-3.x-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/cxf-3.x-plugin/pom.xml @@ -20,7 +20,7 @@ apm-sdk-plugin org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-sdk-plugin/dbcp-2.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/dbcp-2.x-plugin/pom.xml index a1c9770f77..2ae4ae4b3f 100644 --- a/apm-sniffer/apm-sdk-plugin/dbcp-2.x-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/dbcp-2.x-plugin/pom.xml @@ -20,7 +20,7 @@ apm-sdk-plugin org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-sdk-plugin/druid-1.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/druid-1.x-plugin/pom.xml index d3fc28a30d..bd8f9d6e39 100644 --- a/apm-sniffer/apm-sdk-plugin/druid-1.x-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/druid-1.x-plugin/pom.xml @@ -20,7 +20,7 @@ apm-sdk-plugin org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-sdk-plugin/druid-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/druid/v1/PoolingAddDruidDataSourceInterceptor.java b/apm-sniffer/apm-sdk-plugin/druid-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/druid/v1/PoolingAddDruidDataSourceInterceptor.java index 63a85409de..03416103b2 100644 --- a/apm-sniffer/apm-sdk-plugin/druid-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/druid/v1/PoolingAddDruidDataSourceInterceptor.java +++ b/apm-sniffer/apm-sdk-plugin/druid-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/druid/v1/PoolingAddDruidDataSourceInterceptor.java @@ -58,10 +58,9 @@ public void handleMethodException(Class clazz, Method method, Object[] allArgume } private Map>> getMetrics() { - Map>> metricMap = new HashMap(); + Map>> metricMap = new HashMap<>(); metricMap.put("activeCount", (DruidDataSourceMBean druidDataSource) -> () -> (double) druidDataSource.getActiveCount()); metricMap.put("poolingCount", (DruidDataSourceMBean druidDataSource) -> () -> (double) druidDataSource.getPoolingCount()); - metricMap.put("idleCount", (DruidDataSourceMBean druidDataSource) -> () -> (double) (druidDataSource.getPoolingCount() - druidDataSource.getActiveCount())); metricMap.put("lockQueueLength", (DruidDataSourceMBean druidDataSource) -> () -> (double) druidDataSource.getLockQueueLength()); metricMap.put("maxWaitThreadCount", (DruidDataSourceMBean druidDataSource) -> () -> (double) druidDataSource.getMaxWaitThreadCount()); metricMap.put("commitCount", (DruidDataSourceMBean druidDataSource) -> () -> (double) druidDataSource.getCommitCount()); diff --git a/apm-sniffer/apm-sdk-plugin/dubbo-2.7.x-conflict-patch/pom.xml b/apm-sniffer/apm-sdk-plugin/dubbo-2.7.x-conflict-patch/pom.xml index 6a04864b83..4880f503ff 100644 --- a/apm-sniffer/apm-sdk-plugin/dubbo-2.7.x-conflict-patch/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/dubbo-2.7.x-conflict-patch/pom.xml @@ -20,7 +20,7 @@ apm-sdk-plugin org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-sdk-plugin/dubbo-2.7.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/dubbo-2.7.x-plugin/pom.xml index d42f327a5b..4608dc3006 100644 --- a/apm-sniffer/apm-sdk-plugin/dubbo-2.7.x-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/dubbo-2.7.x-plugin/pom.xml @@ -20,7 +20,7 @@ apm-sdk-plugin org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-sdk-plugin/dubbo-3.x-conflict-patch/pom.xml b/apm-sniffer/apm-sdk-plugin/dubbo-3.x-conflict-patch/pom.xml index 6c2fabb877..f1eb7380c9 100644 --- a/apm-sniffer/apm-sdk-plugin/dubbo-3.x-conflict-patch/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/dubbo-3.x-conflict-patch/pom.xml @@ -20,7 +20,7 @@ apm-sdk-plugin org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-sdk-plugin/dubbo-3.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/dubbo-3.x-plugin/pom.xml index 806c76fa2d..48e35f1950 100644 --- a/apm-sniffer/apm-sdk-plugin/dubbo-3.x-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/dubbo-3.x-plugin/pom.xml @@ -20,7 +20,7 @@ apm-sdk-plugin org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-sdk-plugin/dubbo-conflict-patch/pom.xml b/apm-sniffer/apm-sdk-plugin/dubbo-conflict-patch/pom.xml index c94396034b..70adb55678 100644 --- a/apm-sniffer/apm-sdk-plugin/dubbo-conflict-patch/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/dubbo-conflict-patch/pom.xml @@ -20,7 +20,7 @@ apm-sdk-plugin org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-sdk-plugin/dubbo-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/dubbo-plugin/pom.xml index f99b4ad833..de7bd3752b 100644 --- a/apm-sniffer/apm-sdk-plugin/dubbo-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/dubbo-plugin/pom.xml @@ -20,7 +20,7 @@ apm-sdk-plugin org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-sdk-plugin/elastic-job-2.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/elastic-job-2.x-plugin/pom.xml index 6a2863e923..01e5f81dfe 100644 --- a/apm-sniffer/apm-sdk-plugin/elastic-job-2.x-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/elastic-job-2.x-plugin/pom.xml @@ -21,7 +21,7 @@ apm-sdk-plugin org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-sdk-plugin/elasticjob-3.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/elasticjob-3.x-plugin/pom.xml index 048f5ce71e..a965673abb 100644 --- a/apm-sniffer/apm-sdk-plugin/elasticjob-3.x-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/elasticjob-3.x-plugin/pom.xml @@ -20,7 +20,7 @@ org.apache.skywalking apm-sdk-plugin - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-sdk-plugin/elasticsearch-5.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/elasticsearch-5.x-plugin/pom.xml index 292485a085..6f048e9caf 100644 --- a/apm-sniffer/apm-sdk-plugin/elasticsearch-5.x-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/elasticsearch-5.x-plugin/pom.xml @@ -20,7 +20,7 @@ apm-sdk-plugin org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 @@ -32,9 +32,6 @@ UTF-8 - 1.8 - ${java.version} - ${java.version} 5.6.6 diff --git a/apm-sniffer/apm-sdk-plugin/elasticsearch-6.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/elasticsearch-6.x-plugin/pom.xml index 9f11bfdb3c..e1a226cc08 100644 --- a/apm-sniffer/apm-sdk-plugin/elasticsearch-6.x-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/elasticsearch-6.x-plugin/pom.xml @@ -20,7 +20,7 @@ apm-sdk-plugin org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-sdk-plugin/elasticsearch-6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/elasticsearch/v6/define/IndicesClientInstrumentation.java b/apm-sniffer/apm-sdk-plugin/elasticsearch-6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/elasticsearch/v6/define/IndicesClientInstrumentation.java index 0031efb96a..75b494b249 100644 --- a/apm-sniffer/apm-sdk-plugin/elasticsearch-6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/elasticsearch/v6/define/IndicesClientInstrumentation.java +++ b/apm-sniffer/apm-sdk-plugin/elasticsearch-6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/elasticsearch/v6/define/IndicesClientInstrumentation.java @@ -68,7 +68,7 @@ public String getMethodsInterceptor() { @Override public boolean isOverrideArgs() { - return false; + return true; } }, new InstanceMethodsInterceptPoint() { @@ -85,7 +85,7 @@ public String getMethodsInterceptor() { @Override public boolean isOverrideArgs() { - return false; + return true; } }, new InstanceMethodsInterceptPoint() { @@ -102,7 +102,7 @@ public String getMethodsInterceptor() { @Override public boolean isOverrideArgs() { - return false; + return true; } }, new InstanceMethodsInterceptPoint() { @@ -119,7 +119,7 @@ public String getMethodsInterceptor() { @Override public boolean isOverrideArgs() { - return false; + return true; } } }; diff --git a/apm-sniffer/apm-sdk-plugin/elasticsearch-6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/elasticsearch/v6/define/RestHighLevelClientInstrumentation.java b/apm-sniffer/apm-sdk-plugin/elasticsearch-6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/elasticsearch/v6/define/RestHighLevelClientInstrumentation.java index 8185926181..68b434740a 100644 --- a/apm-sniffer/apm-sdk-plugin/elasticsearch-6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/elasticsearch/v6/define/RestHighLevelClientInstrumentation.java +++ b/apm-sniffer/apm-sdk-plugin/elasticsearch-6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/elasticsearch/v6/define/RestHighLevelClientInstrumentation.java @@ -86,7 +86,7 @@ public String getMethodsInterceptor() { @Override public boolean isOverrideArgs() { - return false; + return true; } }, new InstanceMethodsInterceptPoint() { @@ -102,7 +102,7 @@ public String getMethodsInterceptor() { @Override public boolean isOverrideArgs() { - return false; + return true; } }, new InstanceMethodsInterceptPoint() { @@ -118,7 +118,7 @@ public String getMethodsInterceptor() { @Override public boolean isOverrideArgs() { - return false; + return true; } }, new InstanceMethodsInterceptPoint() { @@ -134,7 +134,7 @@ public String getMethodsInterceptor() { @Override public boolean isOverrideArgs() { - return false; + return true; } }, new InstanceMethodsInterceptPoint() { @@ -182,7 +182,7 @@ public String getMethodsInterceptor() { @Override public boolean isOverrideArgs() { - return false; + return true; } }, new InstanceMethodsInterceptPoint() { @@ -198,7 +198,7 @@ public String getMethodsInterceptor() { @Override public boolean isOverrideArgs() { - return false; + return true; } }, new InstanceMethodsInterceptPoint() { @@ -214,7 +214,7 @@ public String getMethodsInterceptor() { @Override public boolean isOverrideArgs() { - return false; + return true; } }, new InstanceMethodsInterceptPoint() { @@ -230,7 +230,7 @@ public String getMethodsInterceptor() { @Override public boolean isOverrideArgs() { - return false; + return true; } } }; diff --git a/apm-sniffer/apm-sdk-plugin/elasticsearch-6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/elasticsearch/v6/interceptor/Constants.java b/apm-sniffer/apm-sdk-plugin/elasticsearch-6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/elasticsearch/v6/interceptor/Constants.java index bf9322ed7f..6d61006e9f 100644 --- a/apm-sniffer/apm-sdk-plugin/elasticsearch-6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/elasticsearch/v6/interceptor/Constants.java +++ b/apm-sniffer/apm-sdk-plugin/elasticsearch-6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/elasticsearch/v6/interceptor/Constants.java @@ -79,4 +79,7 @@ public class Constants { public static final AbstractTag ES_TOOK_MILLIS = Tags.ofKey("es.took_millis"); public static final AbstractTag ES_TOTAL_HITS = Tags.ofKey("es.total_hits"); public static final AbstractTag ES_INGEST_TOOK_MILLIS = Tags.ofKey("es.ingest_took_millis"); + + public static final String ON_RESPONSE_SUFFIX = "/onResponse"; + public static final String ON_FAILURE_SUFFIX = "/onFailure"; } diff --git a/apm-sniffer/apm-sdk-plugin/elasticsearch-6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/elasticsearch/v6/interceptor/IndicesClientAnalyzeMethodsInterceptor.java b/apm-sniffer/apm-sdk-plugin/elasticsearch-6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/elasticsearch/v6/interceptor/IndicesClientAnalyzeMethodsInterceptor.java index 8ab77d4e86..6d8338e497 100644 --- a/apm-sniffer/apm-sdk-plugin/elasticsearch-6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/elasticsearch/v6/interceptor/IndicesClientAnalyzeMethodsInterceptor.java +++ b/apm-sniffer/apm-sdk-plugin/elasticsearch-6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/elasticsearch/v6/interceptor/IndicesClientAnalyzeMethodsInterceptor.java @@ -30,6 +30,8 @@ import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult; import org.apache.skywalking.apm.network.trace.component.ComponentsDefine; import org.apache.skywalking.apm.plugin.elasticsearch.common.RestClientEnhanceInfo; +import org.apache.skywalking.apm.plugin.elasticsearch.v6.support.AdapterUtil; +import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.admin.indices.analyze.AnalyzeRequest; import java.lang.reflect.Method; @@ -58,6 +60,10 @@ public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allAr Tags.DB_STATEMENT.set(span, analyzeRequest.text()[0]); } SpanLayer.asDB(span); + if (allArguments.length > 2 && allArguments[2] != null && allArguments[2] instanceof ActionListener) { + allArguments[2] = AdapterUtil.wrapActionListener(restClientEnhanceInfo, Constants.ANALYZE_OPERATOR_NAME, + (ActionListener) allArguments[2]); + } } } diff --git a/apm-sniffer/apm-sdk-plugin/elasticsearch-6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/elasticsearch/v6/interceptor/IndicesClientCreateMethodsInterceptor.java b/apm-sniffer/apm-sdk-plugin/elasticsearch-6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/elasticsearch/v6/interceptor/IndicesClientCreateMethodsInterceptor.java index ae17f85d75..b1b95481ec 100644 --- a/apm-sniffer/apm-sdk-plugin/elasticsearch-6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/elasticsearch/v6/interceptor/IndicesClientCreateMethodsInterceptor.java +++ b/apm-sniffer/apm-sdk-plugin/elasticsearch-6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/elasticsearch/v6/interceptor/IndicesClientCreateMethodsInterceptor.java @@ -28,6 +28,8 @@ import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult; import org.apache.skywalking.apm.network.trace.component.ComponentsDefine; import org.apache.skywalking.apm.plugin.elasticsearch.common.RestClientEnhanceInfo; +import org.apache.skywalking.apm.plugin.elasticsearch.v6.support.AdapterUtil; +import org.elasticsearch.action.ActionListener; import org.elasticsearch.client.indices.CreateIndexRequest; import static org.apache.skywalking.apm.plugin.elasticsearch.v6.ElasticsearchPluginConfig.Plugin.Elasticsearch.TRACE_DSL; @@ -52,6 +54,10 @@ public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allAr Tags.DB_STATEMENT.set(span, createIndexRequest.mappings().utf8ToString()); } SpanLayer.asDB(span); + if (allArguments.length > 2 && allArguments[2] != null && allArguments[2] instanceof ActionListener) { + allArguments[2] = AdapterUtil.wrapActionListener(restClientEnhanceInfo, Constants.CREATE_OPERATOR_NAME, + (ActionListener) allArguments[2]); + } } } diff --git a/apm-sniffer/apm-sdk-plugin/elasticsearch-6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/elasticsearch/v6/interceptor/IndicesClientDeleteMethodsInterceptor.java b/apm-sniffer/apm-sdk-plugin/elasticsearch-6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/elasticsearch/v6/interceptor/IndicesClientDeleteMethodsInterceptor.java index 454afa08b6..68c7377dfe 100644 --- a/apm-sniffer/apm-sdk-plugin/elasticsearch-6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/elasticsearch/v6/interceptor/IndicesClientDeleteMethodsInterceptor.java +++ b/apm-sniffer/apm-sdk-plugin/elasticsearch-6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/elasticsearch/v6/interceptor/IndicesClientDeleteMethodsInterceptor.java @@ -29,6 +29,8 @@ import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult; import org.apache.skywalking.apm.network.trace.component.ComponentsDefine; import org.apache.skywalking.apm.plugin.elasticsearch.common.RestClientEnhanceInfo; +import org.apache.skywalking.apm.plugin.elasticsearch.v6.support.AdapterUtil; +import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest; import static org.apache.skywalking.apm.plugin.elasticsearch.v6.interceptor.Constants.DB_TYPE; @@ -48,6 +50,10 @@ public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allAr Tags.DB_TYPE.set(span, DB_TYPE); Tags.DB_INSTANCE.set(span, Arrays.asList(deleteIndexRequest.indices()).toString()); SpanLayer.asDB(span); + if (allArguments.length > 2 && allArguments[2] != null && allArguments[2] instanceof ActionListener) { + allArguments[2] = AdapterUtil.wrapActionListener(restClientEnhanceInfo, Constants.DELETE_OPERATOR_NAME, + (ActionListener) allArguments[2]); + } } } diff --git a/apm-sniffer/apm-sdk-plugin/elasticsearch-6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/elasticsearch/v6/interceptor/IndicesClientRefreshMethodsInterceptor.java b/apm-sniffer/apm-sdk-plugin/elasticsearch-6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/elasticsearch/v6/interceptor/IndicesClientRefreshMethodsInterceptor.java index 9e3cb6fc17..cb9dfbb384 100644 --- a/apm-sniffer/apm-sdk-plugin/elasticsearch-6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/elasticsearch/v6/interceptor/IndicesClientRefreshMethodsInterceptor.java +++ b/apm-sniffer/apm-sdk-plugin/elasticsearch-6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/elasticsearch/v6/interceptor/IndicesClientRefreshMethodsInterceptor.java @@ -27,6 +27,8 @@ import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult; import org.apache.skywalking.apm.network.trace.component.ComponentsDefine; import org.apache.skywalking.apm.plugin.elasticsearch.common.RestClientEnhanceInfo; +import org.apache.skywalking.apm.plugin.elasticsearch.v6.support.AdapterUtil; +import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.admin.indices.refresh.RefreshRequest; import java.lang.reflect.Method; @@ -48,6 +50,10 @@ public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allAr Tags.DB_TYPE.set(span, DB_TYPE); Tags.DB_INSTANCE.set(span, buildIndicesString(request.indices())); SpanLayer.asDB(span); + if (allArguments.length > 2 && allArguments[2] != null && allArguments[2] instanceof ActionListener) { + allArguments[2] = AdapterUtil.wrapActionListener(restClientEnhanceInfo, Constants.REFRESH_OPERATOR_NAME, + (ActionListener) allArguments[2]); + } } } diff --git a/apm-sniffer/apm-sdk-plugin/elasticsearch-6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/elasticsearch/v6/interceptor/RestHighLevelClientClearScrollMethodsInterceptor.java b/apm-sniffer/apm-sdk-plugin/elasticsearch-6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/elasticsearch/v6/interceptor/RestHighLevelClientClearScrollMethodsInterceptor.java index d1099f8ebc..2a65908af4 100644 --- a/apm-sniffer/apm-sdk-plugin/elasticsearch-6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/elasticsearch/v6/interceptor/RestHighLevelClientClearScrollMethodsInterceptor.java +++ b/apm-sniffer/apm-sdk-plugin/elasticsearch-6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/elasticsearch/v6/interceptor/RestHighLevelClientClearScrollMethodsInterceptor.java @@ -27,6 +27,8 @@ import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult; import org.apache.skywalking.apm.network.trace.component.ComponentsDefine; import org.apache.skywalking.apm.plugin.elasticsearch.common.RestClientEnhanceInfo; +import org.apache.skywalking.apm.plugin.elasticsearch.v6.support.AdapterUtil; +import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.search.ClearScrollRequest; import java.lang.reflect.Method; @@ -52,6 +54,10 @@ public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allAr } SpanLayer.asDB(span); + if (allArguments.length > 2 && allArguments[2] != null && allArguments[2] instanceof ActionListener) { + allArguments[2] = AdapterUtil.wrapActionListener(restClientEnhanceInfo, Constants.CLEAR_SCROLL_OPERATOR_NAME, + (ActionListener) allArguments[2]); + } } @Override diff --git a/apm-sniffer/apm-sdk-plugin/elasticsearch-6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/elasticsearch/v6/interceptor/RestHighLevelClientDeleteByQueryMethodsInterceptor.java b/apm-sniffer/apm-sdk-plugin/elasticsearch-6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/elasticsearch/v6/interceptor/RestHighLevelClientDeleteByQueryMethodsInterceptor.java index 654c7df7b9..aba54fe54e 100644 --- a/apm-sniffer/apm-sdk-plugin/elasticsearch-6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/elasticsearch/v6/interceptor/RestHighLevelClientDeleteByQueryMethodsInterceptor.java +++ b/apm-sniffer/apm-sdk-plugin/elasticsearch-6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/elasticsearch/v6/interceptor/RestHighLevelClientDeleteByQueryMethodsInterceptor.java @@ -27,6 +27,8 @@ import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult; import org.apache.skywalking.apm.network.trace.component.ComponentsDefine; import org.apache.skywalking.apm.plugin.elasticsearch.common.RestClientEnhanceInfo; +import org.apache.skywalking.apm.plugin.elasticsearch.v6.support.AdapterUtil; +import org.elasticsearch.action.ActionListener; import org.elasticsearch.index.reindex.DeleteByQueryRequest; import java.lang.reflect.Method; @@ -58,6 +60,10 @@ public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allAr } SpanLayer.asDB(span); + if (allArguments.length > 2 && allArguments[2] != null && allArguments[2] instanceof ActionListener) { + allArguments[2] = AdapterUtil.wrapActionListener(restClientEnhanceInfo, Constants.DELETE_BY_QUERY_OPERATOR_NAME, + (ActionListener) allArguments[2]); + } } @Override diff --git a/apm-sniffer/apm-sdk-plugin/elasticsearch-6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/elasticsearch/v6/interceptor/RestHighLevelClientGetMethodsInterceptor.java b/apm-sniffer/apm-sdk-plugin/elasticsearch-6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/elasticsearch/v6/interceptor/RestHighLevelClientGetMethodsInterceptor.java index 171bd29802..32c9c24c73 100644 --- a/apm-sniffer/apm-sdk-plugin/elasticsearch-6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/elasticsearch/v6/interceptor/RestHighLevelClientGetMethodsInterceptor.java +++ b/apm-sniffer/apm-sdk-plugin/elasticsearch-6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/elasticsearch/v6/interceptor/RestHighLevelClientGetMethodsInterceptor.java @@ -28,6 +28,8 @@ import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult; import org.apache.skywalking.apm.network.trace.component.ComponentsDefine; import org.apache.skywalking.apm.plugin.elasticsearch.common.RestClientEnhanceInfo; +import org.apache.skywalking.apm.plugin.elasticsearch.v6.support.AdapterUtil; +import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.get.GetRequest; import static org.apache.skywalking.apm.plugin.elasticsearch.v6.ElasticsearchPluginConfig.Plugin.Elasticsearch.TRACE_DSL; @@ -51,6 +53,10 @@ public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allAr } SpanLayer.asDB(span); + if (allArguments.length > 2 && allArguments[2] != null && allArguments[2] instanceof ActionListener) { + allArguments[2] = AdapterUtil.wrapActionListener(restClientEnhanceInfo, Constants.GET_OPERATOR_NAME, + (ActionListener) allArguments[2]); + } } @Override diff --git a/apm-sniffer/apm-sdk-plugin/elasticsearch-6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/elasticsearch/v6/interceptor/RestHighLevelClientIndexMethodsInterceptor.java b/apm-sniffer/apm-sdk-plugin/elasticsearch-6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/elasticsearch/v6/interceptor/RestHighLevelClientIndexMethodsInterceptor.java index c2075207d6..0e7744f7c4 100644 --- a/apm-sniffer/apm-sdk-plugin/elasticsearch-6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/elasticsearch/v6/interceptor/RestHighLevelClientIndexMethodsInterceptor.java +++ b/apm-sniffer/apm-sdk-plugin/elasticsearch-6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/elasticsearch/v6/interceptor/RestHighLevelClientIndexMethodsInterceptor.java @@ -28,6 +28,8 @@ import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult; import org.apache.skywalking.apm.network.trace.component.ComponentsDefine; import org.apache.skywalking.apm.plugin.elasticsearch.common.RestClientEnhanceInfo; +import org.apache.skywalking.apm.plugin.elasticsearch.v6.support.AdapterUtil; +import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.index.IndexRequest; import static org.apache.skywalking.apm.plugin.elasticsearch.v6.ElasticsearchPluginConfig.Plugin.Elasticsearch.TRACE_DSL; @@ -51,6 +53,10 @@ public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allAr } SpanLayer.asDB(span); + if (allArguments.length > 2 && allArguments[2] != null && allArguments[2] instanceof ActionListener) { + allArguments[2] = AdapterUtil.wrapActionListener(restClientEnhanceInfo, Constants.INDEX_OPERATOR_NAME, + (ActionListener) allArguments[2]); + } } @Override diff --git a/apm-sniffer/apm-sdk-plugin/elasticsearch-6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/elasticsearch/v6/interceptor/RestHighLevelClientSearchMethodsInterceptor.java b/apm-sniffer/apm-sdk-plugin/elasticsearch-6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/elasticsearch/v6/interceptor/RestHighLevelClientSearchMethodsInterceptor.java index cc709d6273..fb941490ba 100644 --- a/apm-sniffer/apm-sdk-plugin/elasticsearch-6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/elasticsearch/v6/interceptor/RestHighLevelClientSearchMethodsInterceptor.java +++ b/apm-sniffer/apm-sdk-plugin/elasticsearch-6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/elasticsearch/v6/interceptor/RestHighLevelClientSearchMethodsInterceptor.java @@ -29,6 +29,8 @@ import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult; import org.apache.skywalking.apm.network.trace.component.ComponentsDefine; import org.apache.skywalking.apm.plugin.elasticsearch.common.RestClientEnhanceInfo; +import org.apache.skywalking.apm.plugin.elasticsearch.v6.support.AdapterUtil; +import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.search.SearchRequest; import static org.apache.skywalking.apm.plugin.elasticsearch.v6.ElasticsearchPluginConfig.Plugin.Elasticsearch.TRACE_DSL; @@ -52,6 +54,10 @@ public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allAr } SpanLayer.asDB(span); + if (allArguments.length > 2 && allArguments[2] != null && allArguments[2] instanceof ActionListener) { + allArguments[2] = AdapterUtil.wrapActionListener(restClientEnhanceInfo, Constants.SEARCH_OPERATOR_NAME, + (ActionListener) allArguments[2]); + } } @Override diff --git a/apm-sniffer/apm-sdk-plugin/elasticsearch-6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/elasticsearch/v6/interceptor/RestHighLevelClientSearchScrollMethodsInterceptor.java b/apm-sniffer/apm-sdk-plugin/elasticsearch-6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/elasticsearch/v6/interceptor/RestHighLevelClientSearchScrollMethodsInterceptor.java index 64d503618e..1b4be55ea6 100644 --- a/apm-sniffer/apm-sdk-plugin/elasticsearch-6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/elasticsearch/v6/interceptor/RestHighLevelClientSearchScrollMethodsInterceptor.java +++ b/apm-sniffer/apm-sdk-plugin/elasticsearch-6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/elasticsearch/v6/interceptor/RestHighLevelClientSearchScrollMethodsInterceptor.java @@ -27,6 +27,8 @@ import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult; import org.apache.skywalking.apm.network.trace.component.ComponentsDefine; import org.apache.skywalking.apm.plugin.elasticsearch.common.RestClientEnhanceInfo; +import org.apache.skywalking.apm.plugin.elasticsearch.v6.support.AdapterUtil; +import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.search.SearchScrollRequest; import java.lang.reflect.Method; @@ -51,6 +53,10 @@ public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allAr } SpanLayer.asDB(span); + if (allArguments.length > 2 && allArguments[2] != null && allArguments[2] instanceof ActionListener) { + allArguments[2] = AdapterUtil.wrapActionListener(restClientEnhanceInfo, Constants.SEARCH_SCROLL_OPERATOR_NAME, + (ActionListener) allArguments[2]); + } } @Override diff --git a/apm-sniffer/apm-sdk-plugin/elasticsearch-6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/elasticsearch/v6/interceptor/RestHighLevelClientSearchTemplateMethodsInterceptor.java b/apm-sniffer/apm-sdk-plugin/elasticsearch-6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/elasticsearch/v6/interceptor/RestHighLevelClientSearchTemplateMethodsInterceptor.java index f57a652b15..b4d945fdb2 100644 --- a/apm-sniffer/apm-sdk-plugin/elasticsearch-6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/elasticsearch/v6/interceptor/RestHighLevelClientSearchTemplateMethodsInterceptor.java +++ b/apm-sniffer/apm-sdk-plugin/elasticsearch-6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/elasticsearch/v6/interceptor/RestHighLevelClientSearchTemplateMethodsInterceptor.java @@ -27,6 +27,8 @@ import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult; import org.apache.skywalking.apm.network.trace.component.ComponentsDefine; import org.apache.skywalking.apm.plugin.elasticsearch.common.RestClientEnhanceInfo; +import org.apache.skywalking.apm.plugin.elasticsearch.v6.support.AdapterUtil; +import org.elasticsearch.action.ActionListener; import org.elasticsearch.script.mustache.SearchTemplateRequest; import java.lang.reflect.Method; @@ -55,6 +57,10 @@ public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allAr } SpanLayer.asDB(span); + if (allArguments.length > 2 && allArguments[2] != null && allArguments[2] instanceof ActionListener) { + allArguments[2] = AdapterUtil.wrapActionListener(restClientEnhanceInfo, Constants.SEARCH_TEMPLATE_OPERATOR_NAME, + (ActionListener) allArguments[2]); + } } @Override diff --git a/apm-sniffer/apm-sdk-plugin/elasticsearch-6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/elasticsearch/v6/interceptor/RestHighLevelClientUpdateMethodsInterceptor.java b/apm-sniffer/apm-sdk-plugin/elasticsearch-6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/elasticsearch/v6/interceptor/RestHighLevelClientUpdateMethodsInterceptor.java index b3a34e1aa1..7d76894ade 100644 --- a/apm-sniffer/apm-sdk-plugin/elasticsearch-6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/elasticsearch/v6/interceptor/RestHighLevelClientUpdateMethodsInterceptor.java +++ b/apm-sniffer/apm-sdk-plugin/elasticsearch-6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/elasticsearch/v6/interceptor/RestHighLevelClientUpdateMethodsInterceptor.java @@ -28,6 +28,8 @@ import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult; import org.apache.skywalking.apm.network.trace.component.ComponentsDefine; import org.apache.skywalking.apm.plugin.elasticsearch.common.RestClientEnhanceInfo; +import org.apache.skywalking.apm.plugin.elasticsearch.v6.support.AdapterUtil; +import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.update.UpdateRequest; import static org.apache.skywalking.apm.plugin.elasticsearch.v6.ElasticsearchPluginConfig.Plugin.Elasticsearch.TRACE_DSL; @@ -51,6 +53,10 @@ public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allAr } SpanLayer.asDB(span); + if (allArguments.length > 2 && allArguments[2] != null && allArguments[2] instanceof ActionListener) { + allArguments[2] = AdapterUtil.wrapActionListener(restClientEnhanceInfo, Constants.UPDATE_OPERATOR_NAME, + (ActionListener) allArguments[2]); + } } @Override diff --git a/apm-sniffer/apm-sdk-plugin/elasticsearch-6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/elasticsearch/v6/support/ActionListenerAdapter.java b/apm-sniffer/apm-sdk-plugin/elasticsearch-6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/elasticsearch/v6/support/ActionListenerAdapter.java new file mode 100644 index 0000000000..8bcb7d23ea --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/elasticsearch-6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/elasticsearch/v6/support/ActionListenerAdapter.java @@ -0,0 +1,92 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.elasticsearch.v6.support; + +import org.apache.skywalking.apm.agent.core.context.ContextManager; +import org.apache.skywalking.apm.agent.core.context.tag.Tags; +import org.apache.skywalking.apm.agent.core.context.trace.AbstractSpan; +import org.apache.skywalking.apm.agent.core.context.trace.SpanLayer; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; +import org.apache.skywalking.apm.network.trace.component.ComponentsDefine; +import org.apache.skywalking.apm.plugin.elasticsearch.v6.interceptor.Constants; +import org.elasticsearch.action.ActionListener; + +import static org.apache.skywalking.apm.plugin.elasticsearch.v6.interceptor.Constants.DB_TYPE; + +public class ActionListenerAdapter implements ActionListener, EnhancedInstance { + + private final RestClientCache restClientCache; + + public ActionListenerAdapter(RestClientCache restClientCache) { + this.restClientCache = restClientCache; + } + + @Override + public void onResponse(final T o) { + try { + if (restClientCache.getContextSnapshot() != null) { + continueContext(Constants.ON_RESPONSE_SUFFIX); + } + if (restClientCache.getActionListener() != null) { + restClientCache.getActionListener().onResponse(o); + } + } catch (Throwable t) { + ContextManager.activeSpan().log(t); + throw t; + } finally { + restClientCache.setContextSnapshot(null); + ContextManager.stopSpan(); + } + } + + @Override + public void onFailure(final Exception e) { + try { + if (restClientCache.getContextSnapshot() != null) { + continueContext(Constants.ON_FAILURE_SUFFIX); + } + if (restClientCache.getActionListener() != null) { + restClientCache.getActionListener().onFailure(e); + } + } catch (Throwable t) { + ContextManager.activeSpan().log(t); + throw t; + } finally { + ContextManager.stopSpan(); + } + } + + @Override + public Object getSkyWalkingDynamicField() { + return restClientCache; + } + + @Override + public void setSkyWalkingDynamicField(final Object enhanceInfo) { + + } + + private void continueContext(String action) { + AbstractSpan activeSpan = ContextManager.createLocalSpan(restClientCache.getOperationName() + action); + activeSpan.setComponent(ComponentsDefine.REST_HIGH_LEVEL_CLIENT); + Tags.DB_TYPE.set(activeSpan, DB_TYPE); + SpanLayer.asDB(activeSpan); + ContextManager.continued(restClientCache.getContextSnapshot()); + } +} diff --git a/apm-sniffer/apm-sdk-plugin/elasticsearch-6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/elasticsearch/v6/support/AdapterUtil.java b/apm-sniffer/apm-sdk-plugin/elasticsearch-6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/elasticsearch/v6/support/AdapterUtil.java new file mode 100644 index 0000000000..f275fc98c8 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/elasticsearch-6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/elasticsearch/v6/support/AdapterUtil.java @@ -0,0 +1,40 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.elasticsearch.v6.support; + +import org.apache.skywalking.apm.agent.core.context.ContextManager; +import org.apache.skywalking.apm.agent.core.context.ContextSnapshot; +import org.apache.skywalking.apm.plugin.elasticsearch.common.RestClientEnhanceInfo; +import org.elasticsearch.action.ActionListener; + +public class AdapterUtil { + + public static ActionListener wrapActionListener(RestClientEnhanceInfo restClientEnhanceInfo, + String operation, + ActionListener listener) { + ContextSnapshot capture = ContextManager.capture(); + capture.getExtensionContext().setSendingTimestamp(System.currentTimeMillis()); + RestClientCache cache = new RestClientCache(); + cache.setEnhanceInfo(restClientEnhanceInfo); + cache.setContextSnapshot(capture); + cache.setOperationName(operation); + cache.setActionListener(listener); + return new ActionListenerAdapter(cache); + } +} diff --git a/apm-sniffer/apm-sdk-plugin/elasticsearch-6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/elasticsearch/v6/support/RestClientCache.java b/apm-sniffer/apm-sdk-plugin/elasticsearch-6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/elasticsearch/v6/support/RestClientCache.java new file mode 100644 index 0000000000..2c81df0024 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/elasticsearch-6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/elasticsearch/v6/support/RestClientCache.java @@ -0,0 +1,62 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.elasticsearch.v6.support; + +import org.apache.skywalking.apm.agent.core.context.ContextSnapshot; +import org.apache.skywalking.apm.plugin.elasticsearch.common.RestClientEnhanceInfo; +import org.elasticsearch.action.ActionListener; + +public class RestClientCache { + private RestClientEnhanceInfo enhanceInfo; + private ActionListener actionListener; + private ContextSnapshot contextSnapshot; + private String operationName; + + public ContextSnapshot getContextSnapshot() { + return contextSnapshot; + } + + public void setContextSnapshot(final ContextSnapshot contextSnapshot) { + this.contextSnapshot = contextSnapshot; + } + + public String getOperationName() { + return operationName; + } + + public void setOperationName(final String operationName) { + this.operationName = operationName; + } + + public ActionListener getActionListener() { + return actionListener; + } + + public void setActionListener(final ActionListener actionListener) { + this.actionListener = actionListener; + } + + public RestClientEnhanceInfo getEnhanceInfo() { + return enhanceInfo; + } + + public void setEnhanceInfo(final RestClientEnhanceInfo enhanceInfo) { + this.enhanceInfo = enhanceInfo; + } +} diff --git a/apm-sniffer/apm-sdk-plugin/elasticsearch-7.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/elasticsearch-7.x-plugin/pom.xml index c51efcdcfa..89a90b55f2 100644 --- a/apm-sniffer/apm-sdk-plugin/elasticsearch-7.x-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/elasticsearch-7.x-plugin/pom.xml @@ -20,7 +20,7 @@ apm-sdk-plugin org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-sdk-plugin/elasticsearch-7.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/elasticsearch/v7/Constants.java b/apm-sniffer/apm-sdk-plugin/elasticsearch-7.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/elasticsearch/v7/Constants.java index b7408b7c94..f9f72eb21d 100644 --- a/apm-sniffer/apm-sdk-plugin/elasticsearch-7.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/elasticsearch/v7/Constants.java +++ b/apm-sniffer/apm-sdk-plugin/elasticsearch-7.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/elasticsearch/v7/Constants.java @@ -48,4 +48,8 @@ public class Constants { public static final AbstractTag ES_TOOK_MILLIS = Tags.ofKey("es.took_millis"); public static final AbstractTag ES_TOTAL_HITS = Tags.ofKey("es.total_hits"); public static final AbstractTag ES_INGEST_TOOK_MILLIS = Tags.ofKey("es.ingest_took_millis"); + + public static final String ON_RESPONSE_SUFFIX = "/onResponse"; + public static final String ON_FAILURE_SUFFIX = "/onFailure"; + } diff --git a/apm-sniffer/apm-sdk-plugin/elasticsearch-7.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/elasticsearch/v7/define/IndicesClientInstrumentation.java b/apm-sniffer/apm-sdk-plugin/elasticsearch-7.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/elasticsearch/v7/define/IndicesClientInstrumentation.java index 158a3b1ce6..5ce24546ee 100644 --- a/apm-sniffer/apm-sdk-plugin/elasticsearch-7.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/elasticsearch/v7/define/IndicesClientInstrumentation.java +++ b/apm-sniffer/apm-sdk-plugin/elasticsearch-7.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/elasticsearch/v7/define/IndicesClientInstrumentation.java @@ -62,7 +62,7 @@ public String getMethodsInterceptor() { @Override public boolean isOverrideArgs() { - return false; + return true; } }, new InstanceMethodsInterceptPoint() { @@ -79,7 +79,7 @@ public String getMethodsInterceptor() { @Override public boolean isOverrideArgs() { - return false; + return true; } }, new InstanceMethodsInterceptPoint() { @@ -96,7 +96,7 @@ public String getMethodsInterceptor() { @Override public boolean isOverrideArgs() { - return false; + return true; } }, new InstanceMethodsInterceptPoint() { @@ -113,7 +113,7 @@ public String getMethodsInterceptor() { @Override public boolean isOverrideArgs() { - return false; + return true; } } }; diff --git a/apm-sniffer/apm-sdk-plugin/elasticsearch-7.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/elasticsearch/v7/interceptor/IndicesClientAnalyzeMethodsInterceptor.java b/apm-sniffer/apm-sdk-plugin/elasticsearch-7.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/elasticsearch/v7/interceptor/IndicesClientAnalyzeMethodsInterceptor.java index 15d3992066..3a1ff1e7f1 100644 --- a/apm-sniffer/apm-sdk-plugin/elasticsearch-7.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/elasticsearch/v7/interceptor/IndicesClientAnalyzeMethodsInterceptor.java +++ b/apm-sniffer/apm-sdk-plugin/elasticsearch-7.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/elasticsearch/v7/interceptor/IndicesClientAnalyzeMethodsInterceptor.java @@ -31,6 +31,8 @@ import org.apache.skywalking.apm.network.trace.component.ComponentsDefine; import org.apache.skywalking.apm.plugin.elasticsearch.common.RestClientEnhanceInfo; import org.apache.skywalking.apm.plugin.elasticsearch.v7.Constants; +import org.apache.skywalking.apm.plugin.elasticsearch.v7.support.AdapterUtil; +import org.elasticsearch.action.ActionListener; import org.elasticsearch.client.indices.AnalyzeRequest; import java.lang.reflect.Method; @@ -59,6 +61,10 @@ public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allAr Tags.DB_STATEMENT.set(span, analyzeRequest.text()[0]); } SpanLayer.asDB(span); + if (allArguments.length > 2 && allArguments[2] != null && allArguments[2] instanceof ActionListener) { + allArguments[2] = AdapterUtil.wrapActionListener(restClientEnhanceInfo, Constants.ANALYZE_OPERATOR_NAME, + (ActionListener) allArguments[2]); + } } } diff --git a/apm-sniffer/apm-sdk-plugin/elasticsearch-7.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/elasticsearch/v7/interceptor/IndicesClientCreateMethodsInterceptor.java b/apm-sniffer/apm-sdk-plugin/elasticsearch-7.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/elasticsearch/v7/interceptor/IndicesClientCreateMethodsInterceptor.java index e4ef7379fa..56b2af778a 100644 --- a/apm-sniffer/apm-sdk-plugin/elasticsearch-7.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/elasticsearch/v7/interceptor/IndicesClientCreateMethodsInterceptor.java +++ b/apm-sniffer/apm-sdk-plugin/elasticsearch-7.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/elasticsearch/v7/interceptor/IndicesClientCreateMethodsInterceptor.java @@ -28,6 +28,8 @@ import org.apache.skywalking.apm.network.trace.component.ComponentsDefine; import org.apache.skywalking.apm.plugin.elasticsearch.common.RestClientEnhanceInfo; import org.apache.skywalking.apm.plugin.elasticsearch.v7.Constants; +import org.apache.skywalking.apm.plugin.elasticsearch.v7.support.AdapterUtil; +import org.elasticsearch.action.ActionListener; import org.elasticsearch.client.indices.CreateIndexRequest; import java.lang.reflect.Method; @@ -49,6 +51,11 @@ public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allAr Tags.DB_TYPE.set(span, DB_TYPE); Tags.DB_INSTANCE.set(span, createIndexRequest.index()); SpanLayer.asDB(span); + + if (allArguments.length > 2 && allArguments[2] != null && allArguments[2] instanceof ActionListener) { + allArguments[2] = AdapterUtil.wrapActionListener(restClientEnhanceInfo, Constants.CREATE_OPERATOR_NAME, + (ActionListener) allArguments[2]); + } } } diff --git a/apm-sniffer/apm-sdk-plugin/elasticsearch-7.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/elasticsearch/v7/interceptor/IndicesClientDeleteMethodsInterceptor.java b/apm-sniffer/apm-sdk-plugin/elasticsearch-7.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/elasticsearch/v7/interceptor/IndicesClientDeleteMethodsInterceptor.java index 37ee7658fd..e327688d91 100644 --- a/apm-sniffer/apm-sdk-plugin/elasticsearch-7.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/elasticsearch/v7/interceptor/IndicesClientDeleteMethodsInterceptor.java +++ b/apm-sniffer/apm-sdk-plugin/elasticsearch-7.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/elasticsearch/v7/interceptor/IndicesClientDeleteMethodsInterceptor.java @@ -28,6 +28,8 @@ import org.apache.skywalking.apm.network.trace.component.ComponentsDefine; import org.apache.skywalking.apm.plugin.elasticsearch.common.RestClientEnhanceInfo; import org.apache.skywalking.apm.plugin.elasticsearch.v7.Constants; +import org.apache.skywalking.apm.plugin.elasticsearch.v7.support.AdapterUtil; +import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest; import java.lang.reflect.Method; @@ -50,6 +52,11 @@ public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allAr Tags.DB_TYPE.set(span, DB_TYPE); Tags.DB_INSTANCE.set(span, Arrays.asList(deleteIndexRequest.indices()).toString()); SpanLayer.asDB(span); + + if (allArguments.length > 2 && allArguments[2] != null && allArguments[2] instanceof ActionListener) { + allArguments[2] = AdapterUtil.wrapActionListener(restClientEnhanceInfo, Constants.DELETE_OPERATOR_NAME, + (ActionListener) allArguments[2]); + } } } diff --git a/apm-sniffer/apm-sdk-plugin/elasticsearch-7.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/elasticsearch/v7/interceptor/IndicesClientRefreshMethodsInterceptor.java b/apm-sniffer/apm-sdk-plugin/elasticsearch-7.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/elasticsearch/v7/interceptor/IndicesClientRefreshMethodsInterceptor.java index 8602be4272..95886ed82b 100644 --- a/apm-sniffer/apm-sdk-plugin/elasticsearch-7.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/elasticsearch/v7/interceptor/IndicesClientRefreshMethodsInterceptor.java +++ b/apm-sniffer/apm-sdk-plugin/elasticsearch-7.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/elasticsearch/v7/interceptor/IndicesClientRefreshMethodsInterceptor.java @@ -28,6 +28,8 @@ import org.apache.skywalking.apm.network.trace.component.ComponentsDefine; import org.apache.skywalking.apm.plugin.elasticsearch.common.RestClientEnhanceInfo; import org.apache.skywalking.apm.plugin.elasticsearch.v7.Constants; +import org.apache.skywalking.apm.plugin.elasticsearch.v7.support.AdapterUtil; +import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.admin.indices.refresh.RefreshRequest; import java.lang.reflect.Method; @@ -49,6 +51,11 @@ public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allAr Tags.DB_TYPE.set(span, DB_TYPE); Tags.DB_INSTANCE.set(span, buildIndicesString(request.indices())); SpanLayer.asDB(span); + + if (allArguments.length > 2 && allArguments[2] != null && allArguments[2] instanceof ActionListener) { + allArguments[2] = AdapterUtil.wrapActionListener(restClientEnhanceInfo, Constants.REFRESH_OPERATOR_NAME, + (ActionListener) allArguments[2]); + } } } diff --git a/apm-sniffer/apm-sdk-plugin/elasticsearch-7.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/elasticsearch/v7/support/ActionListenerAdapter.java b/apm-sniffer/apm-sdk-plugin/elasticsearch-7.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/elasticsearch/v7/support/ActionListenerAdapter.java new file mode 100644 index 0000000000..f99178412b --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/elasticsearch-7.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/elasticsearch/v7/support/ActionListenerAdapter.java @@ -0,0 +1,92 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.elasticsearch.v7.support; + +import org.apache.skywalking.apm.agent.core.context.ContextManager; +import org.apache.skywalking.apm.agent.core.context.tag.Tags; +import org.apache.skywalking.apm.agent.core.context.trace.AbstractSpan; +import org.apache.skywalking.apm.agent.core.context.trace.SpanLayer; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; +import org.apache.skywalking.apm.network.trace.component.ComponentsDefine; +import org.apache.skywalking.apm.plugin.elasticsearch.v7.Constants; +import org.elasticsearch.action.ActionListener; + +import static org.apache.skywalking.apm.plugin.elasticsearch.v7.Constants.DB_TYPE; + +public class ActionListenerAdapter implements ActionListener, EnhancedInstance { + + private final RestClientCache restClientCache; + + public ActionListenerAdapter(RestClientCache restClientCache) { + this.restClientCache = restClientCache; + } + + @Override + public void onResponse(final T o) { + try { + if (restClientCache.getContextSnapshot() != null) { + continueContext(Constants.ON_RESPONSE_SUFFIX); + } + if (restClientCache.getActionListener() != null) { + restClientCache.getActionListener().onResponse(o); + } + } catch (Throwable t) { + ContextManager.activeSpan().log(t); + throw t; + } finally { + restClientCache.setContextSnapshot(null); + ContextManager.stopSpan(); + } + } + + @Override + public void onFailure(final Exception e) { + try { + if (restClientCache.getContextSnapshot() != null) { + continueContext(Constants.ON_FAILURE_SUFFIX); + } + if (restClientCache.getActionListener() != null) { + restClientCache.getActionListener().onFailure(e); + } + } catch (Throwable t) { + ContextManager.activeSpan().log(t); + throw t; + } finally { + ContextManager.stopSpan(); + } + } + + @Override + public Object getSkyWalkingDynamicField() { + return restClientCache; + } + + @Override + public void setSkyWalkingDynamicField(final Object enhanceInfo) { + + } + + private void continueContext(String action) { + AbstractSpan activeSpan = ContextManager.createLocalSpan(restClientCache.getOperationName() + action); + activeSpan.setComponent(ComponentsDefine.REST_HIGH_LEVEL_CLIENT); + Tags.DB_TYPE.set(activeSpan, DB_TYPE); + SpanLayer.asDB(activeSpan); + ContextManager.continued(restClientCache.getContextSnapshot()); + } +} diff --git a/apm-sniffer/apm-sdk-plugin/elasticsearch-7.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/elasticsearch/v7/support/AdapterUtil.java b/apm-sniffer/apm-sdk-plugin/elasticsearch-7.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/elasticsearch/v7/support/AdapterUtil.java new file mode 100644 index 0000000000..093b6c92ea --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/elasticsearch-7.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/elasticsearch/v7/support/AdapterUtil.java @@ -0,0 +1,40 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.elasticsearch.v7.support; + +import org.apache.skywalking.apm.agent.core.context.ContextManager; +import org.apache.skywalking.apm.agent.core.context.ContextSnapshot; +import org.apache.skywalking.apm.plugin.elasticsearch.common.RestClientEnhanceInfo; +import org.elasticsearch.action.ActionListener; + +public class AdapterUtil { + + public static ActionListener wrapActionListener(RestClientEnhanceInfo restClientEnhanceInfo, + String operation, + ActionListener listener) { + ContextSnapshot capture = ContextManager.capture(); + capture.getExtensionContext().setSendingTimestamp(System.currentTimeMillis()); + RestClientCache cache = new RestClientCache(); + cache.setEnhanceInfo(restClientEnhanceInfo); + cache.setContextSnapshot(capture); + cache.setOperationName(operation); + cache.setActionListener(listener); + return new ActionListenerAdapter(cache); + } +} diff --git a/apm-sniffer/apm-sdk-plugin/elasticsearch-7.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/elasticsearch/v7/support/RestClientCache.java b/apm-sniffer/apm-sdk-plugin/elasticsearch-7.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/elasticsearch/v7/support/RestClientCache.java new file mode 100644 index 0000000000..18f895578a --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/elasticsearch-7.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/elasticsearch/v7/support/RestClientCache.java @@ -0,0 +1,62 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.elasticsearch.v7.support; + +import org.apache.skywalking.apm.agent.core.context.ContextSnapshot; +import org.apache.skywalking.apm.plugin.elasticsearch.common.RestClientEnhanceInfo; +import org.elasticsearch.action.ActionListener; + +public class RestClientCache { + private RestClientEnhanceInfo enhanceInfo; + private ActionListener actionListener; + private ContextSnapshot contextSnapshot; + private String operationName; + + public ContextSnapshot getContextSnapshot() { + return contextSnapshot; + } + + public void setContextSnapshot(final ContextSnapshot contextSnapshot) { + this.contextSnapshot = contextSnapshot; + } + + public String getOperationName() { + return operationName; + } + + public void setOperationName(final String operationName) { + this.operationName = operationName; + } + + public ActionListener getActionListener() { + return actionListener; + } + + public void setActionListener(final ActionListener actionListener) { + this.actionListener = actionListener; + } + + public RestClientEnhanceInfo getEnhanceInfo() { + return enhanceInfo; + } + + public void setEnhanceInfo(final RestClientEnhanceInfo enhanceInfo) { + this.enhanceInfo = enhanceInfo; + } +} diff --git a/apm-sniffer/apm-sdk-plugin/elasticsearch-common/pom.xml b/apm-sniffer/apm-sdk-plugin/elasticsearch-common/pom.xml index b4dee8ad78..51acbee581 100644 --- a/apm-sniffer/apm-sdk-plugin/elasticsearch-common/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/elasticsearch-common/pom.xml @@ -20,7 +20,7 @@ apm-sdk-plugin org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-sdk-plugin/feign-default-http-9.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/feign-default-http-9.x-plugin/pom.xml index 74d05bfdc3..a97bc44739 100644 --- a/apm-sniffer/apm-sdk-plugin/feign-default-http-9.x-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/feign-default-http-9.x-plugin/pom.xml @@ -21,7 +21,7 @@ org.apache.skywalking apm-sdk-plugin - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-sdk-plugin/finagle-6.25.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/finagle-6.25.x-plugin/pom.xml index 3ff991b40d..89f906c289 100644 --- a/apm-sniffer/apm-sdk-plugin/finagle-6.25.x-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/finagle-6.25.x-plugin/pom.xml @@ -21,7 +21,7 @@ apm-sdk-plugin org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 @@ -35,8 +35,6 @@ UTF-8 6.34.0 2.11.12 - 1.8 - 1.8 diff --git a/apm-sniffer/apm-sdk-plugin/graphql-plugin/graphql-12.x-15.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/graphql-plugin/graphql-12.x-15.x-plugin/pom.xml index a2804dafcf..170586098e 100644 --- a/apm-sniffer/apm-sdk-plugin/graphql-plugin/graphql-12.x-15.x-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/graphql-plugin/graphql-12.x-15.x-plugin/pom.xml @@ -21,7 +21,7 @@ graphql-plugin org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-sdk-plugin/graphql-plugin/graphql-16plus-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/graphql-plugin/graphql-16plus-plugin/pom.xml index 6c4684c0cd..474b404f95 100644 --- a/apm-sniffer/apm-sdk-plugin/graphql-plugin/graphql-16plus-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/graphql-plugin/graphql-16plus-plugin/pom.xml @@ -21,7 +21,7 @@ graphql-plugin org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-sdk-plugin/graphql-plugin/graphql-8.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/graphql-plugin/graphql-8.x-plugin/pom.xml index 3025ff06fe..cb6d9e0e02 100644 --- a/apm-sniffer/apm-sdk-plugin/graphql-plugin/graphql-8.x-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/graphql-plugin/graphql-8.x-plugin/pom.xml @@ -21,7 +21,7 @@ graphql-plugin org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-sdk-plugin/graphql-plugin/graphql-9.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/graphql-plugin/graphql-9.x-plugin/pom.xml index c1f67a749c..d1e081d15c 100644 --- a/apm-sniffer/apm-sdk-plugin/graphql-plugin/graphql-9.x-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/graphql-plugin/graphql-9.x-plugin/pom.xml @@ -21,7 +21,7 @@ graphql-plugin org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-sdk-plugin/graphql-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/graphql-plugin/pom.xml index 40d94ac707..74e296d1bd 100644 --- a/apm-sniffer/apm-sdk-plugin/graphql-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/graphql-plugin/pom.xml @@ -21,7 +21,7 @@ apm-sdk-plugin org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 pom @@ -37,6 +37,5 @@ UTF-8 - /.. \ No newline at end of file diff --git a/apm-sniffer/apm-sdk-plugin/grizzly-2.3.x-4.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/grizzly-2.3.x-4.x-plugin/pom.xml index c1f9b7129e..191aa45db8 100644 --- a/apm-sniffer/apm-sdk-plugin/grizzly-2.3.x-4.x-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/grizzly-2.3.x-4.x-plugin/pom.xml @@ -20,7 +20,7 @@ org.apache.skywalking apm-sdk-plugin - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-sdk-plugin/grizzly-2.3.x-4.x-work-threadpool-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/grizzly-2.3.x-4.x-work-threadpool-plugin/pom.xml index c5fb2521d4..5db8506875 100644 --- a/apm-sniffer/apm-sdk-plugin/grizzly-2.3.x-4.x-work-threadpool-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/grizzly-2.3.x-4.x-work-threadpool-plugin/pom.xml @@ -21,7 +21,7 @@ org.apache.skywalking apm-sdk-plugin - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT apm-grizzly-2.x-4.x-work-threadpool-plugin diff --git a/apm-sniffer/apm-sdk-plugin/grpc-1.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/grpc-1.x-plugin/pom.xml index 1b188f16a9..72884e2d48 100644 --- a/apm-sniffer/apm-sdk-plugin/grpc-1.x-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/grpc-1.x-plugin/pom.xml @@ -22,7 +22,7 @@ org.apache.skywalking apm-sdk-plugin - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT apm-grpc-1.x-plugin diff --git a/apm-sniffer/apm-sdk-plugin/grpc-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/grpc/v1/define/AbstractServerImplBuilderInstrumentation.java b/apm-sniffer/apm-sdk-plugin/grpc-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/grpc/v1/define/AbstractServerImplBuilderInstrumentation.java index f32987f4bc..bd71445d7c 100644 --- a/apm-sniffer/apm-sdk-plugin/grpc-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/grpc/v1/define/AbstractServerImplBuilderInstrumentation.java +++ b/apm-sniffer/apm-sdk-plugin/grpc-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/grpc/v1/define/AbstractServerImplBuilderInstrumentation.java @@ -24,14 +24,13 @@ import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint; import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine; import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch; +import org.apache.skywalking.apm.agent.core.plugin.match.MultiClassNameMatch; import static net.bytebuddy.matcher.ElementMatchers.named; import static net.bytebuddy.matcher.ElementMatchers.takesNoArguments; -import static org.apache.skywalking.apm.agent.core.plugin.match.NameMatch.byName; public class AbstractServerImplBuilderInstrumentation extends ClassInstanceMethodsEnhancePluginDefine { - public static final String ENHANCE_CLASS = "io.grpc.internal.AbstractServerImplBuilder"; public static final String ENHANCE_METHOD = "build"; public static final String INTERCEPT_CLASS = "org.apache.skywalking.apm.plugin.grpc.v1.server.AbstractServerImplBuilderInterceptor"; @@ -64,6 +63,9 @@ public boolean isOverrideArgs() { @Override protected ClassMatch enhanceClass() { - return byName(ENHANCE_CLASS); + return MultiClassNameMatch.byMultiClassMatch( + "io.grpc.internal.AbstractServerImplBuilder", //grpc version <= 1.58.1 + "io.grpc.internal.ServerImplBuilder" //grpc version >= 1.59.0 + ); } } diff --git a/apm-sniffer/apm-sdk-plugin/grpc-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/grpc/v1/server/AbstractServerImplBuilderInterceptor.java b/apm-sniffer/apm-sdk-plugin/grpc-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/grpc/v1/server/AbstractServerImplBuilderInterceptor.java index c02cf566de..cf1bcf4f76 100644 --- a/apm-sniffer/apm-sdk-plugin/grpc-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/grpc/v1/server/AbstractServerImplBuilderInterceptor.java +++ b/apm-sniffer/apm-sdk-plugin/grpc-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/grpc/v1/server/AbstractServerImplBuilderInterceptor.java @@ -20,7 +20,11 @@ import io.grpc.ServerBuilder; +import java.lang.reflect.Field; import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceMethodsAroundInterceptor; @@ -30,14 +34,26 @@ * {@link AbstractServerImplBuilderInterceptor} add the {@link ServerInterceptor} interceptor for every ServerService. */ public class AbstractServerImplBuilderInterceptor implements InstanceMethodsAroundInterceptor { + private final static Map, Field> FIELD_CACHE = new HashMap<>(); + @Override public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, - MethodInterceptResult result) { + MethodInterceptResult result) throws Throwable { if (objInst.getSkyWalkingDynamicField() == null) { ServerBuilder builder = (ServerBuilder) objInst; - ServerInterceptor interceptor = new ServerInterceptor(); - builder.intercept(interceptor); - objInst.setSkyWalkingDynamicField(interceptor); + Field field = findField(builder.getClass()); + if (field != null) { + List interceptors = (List) field.get(builder); + boolean hasCustomInterceptor = interceptors.stream() + .anyMatch(i -> i.getClass() == ServerInterceptor.class); + + if (!hasCustomInterceptor) { + ServerInterceptor interceptor = new ServerInterceptor(); + builder.intercept(interceptor); + objInst.setSkyWalkingDynamicField(interceptor); + } + + } } } @@ -52,4 +68,31 @@ public void handleMethodException(EnhancedInstance objInst, Method method, Objec Class[] argumentsTypes, Throwable t) { } + + private static Field findField(Class clazz) { + if (FIELD_CACHE.containsKey(clazz)) { + return FIELD_CACHE.get(clazz); + } + synchronized (AbstractServerImplBuilderInterceptor.class) { + if (FIELD_CACHE.containsKey(clazz)) { + return FIELD_CACHE.get(clazz); + } + Field field = doFindField(clazz); + FIELD_CACHE.put(clazz, field); + return field; + } + } + + private static Field doFindField(Class clazz) { + while (clazz != null) { + for (Field f : clazz.getDeclaredFields()) { + if (f.getName().equals("interceptors")) { + f.setAccessible(true); + return f; + } + } + clazz = clazz.getSuperclass(); + } + return null; + } } diff --git a/apm-sniffer/apm-sdk-plugin/grpc-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/grpc/v1/server/ServerInterceptor.java b/apm-sniffer/apm-sdk-plugin/grpc-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/grpc/v1/server/ServerInterceptor.java index f970d813f0..b285ff169d 100644 --- a/apm-sniffer/apm-sdk-plugin/grpc-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/grpc/v1/server/ServerInterceptor.java +++ b/apm-sniffer/apm-sdk-plugin/grpc-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/grpc/v1/server/ServerInterceptor.java @@ -36,9 +36,6 @@ public class ServerInterceptor implements io.grpc.ServerInterceptor { - static final Context.Key CONTEXT_SNAPSHOT_KEY = Context.key("skywalking-grpc-context-snapshot"); - static final Context.Key ACTIVE_SPAN_KEY = Context.key("skywalking-grpc-active-span"); - @Override public ServerCall.Listener interceptCall(ServerCall call, Metadata headers, ServerCallHandler handler) { @@ -59,15 +56,15 @@ public ServerCall.Listener interceptCall(ServerCall ContextSnapshot contextSnapshot = ContextManager.capture(); AbstractSpan asyncSpan = span.prepareForAsync(); - Context context = Context.current().withValues(CONTEXT_SNAPSHOT_KEY, contextSnapshot, ACTIVE_SPAN_KEY, asyncSpan); - ServerCall.Listener listener = Contexts.interceptCall( - context, - new TracingServerCall<>(call), + Context.current(), + new TracingServerCall<>(call, contextSnapshot, asyncSpan), headers, (serverCall, metadata) -> new TracingServerCallListener<>( handler.startCall(serverCall, metadata), - serverCall.getMethodDescriptor() + serverCall.getMethodDescriptor(), + contextSnapshot, + asyncSpan ) ); ContextManager.stopSpan(asyncSpan); diff --git a/apm-sniffer/apm-sdk-plugin/grpc-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/grpc/v1/server/TracingServerCall.java b/apm-sniffer/apm-sdk-plugin/grpc-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/grpc/v1/server/TracingServerCall.java index 3361c4b7db..c674b5e16b 100644 --- a/apm-sniffer/apm-sdk-plugin/grpc-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/grpc/v1/server/TracingServerCall.java +++ b/apm-sniffer/apm-sdk-plugin/grpc-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/grpc/v1/server/TracingServerCall.java @@ -18,38 +18,46 @@ package org.apache.skywalking.apm.plugin.grpc.v1.server; -import static org.apache.skywalking.apm.plugin.grpc.v1.Constants.RESPONSE_ON_CLOSE_OPERATION_NAME; -import static org.apache.skywalking.apm.plugin.grpc.v1.Constants.RESPONSE_ON_MESSAGE_OPERATION_NAME; -import static org.apache.skywalking.apm.plugin.grpc.v1.Constants.SERVER; - import io.grpc.ForwardingServerCall; import io.grpc.Metadata; import io.grpc.ServerCall; import io.grpc.Status; import org.apache.skywalking.apm.agent.core.context.ContextManager; +import org.apache.skywalking.apm.agent.core.context.ContextSnapshot; import org.apache.skywalking.apm.agent.core.context.tag.Tags; import org.apache.skywalking.apm.agent.core.context.trace.AbstractSpan; import org.apache.skywalking.apm.agent.core.context.trace.SpanLayer; import org.apache.skywalking.apm.network.trace.component.ComponentsDefine; import org.apache.skywalking.apm.plugin.grpc.v1.OperationNameFormatUtil; +import static org.apache.skywalking.apm.plugin.grpc.v1.Constants.RESPONSE_ON_CLOSE_OPERATION_NAME; +import static org.apache.skywalking.apm.plugin.grpc.v1.Constants.RESPONSE_ON_MESSAGE_OPERATION_NAME; +import static org.apache.skywalking.apm.plugin.grpc.v1.Constants.SERVER; + public class TracingServerCall extends ForwardingServerCall.SimpleForwardingServerCall { private final String operationPrefix; + private final ContextSnapshot contextSnapshot; + private final AbstractSpan parentEntrySpan; - protected TracingServerCall(ServerCall delegate) { + protected TracingServerCall(ServerCall delegate, + final ContextSnapshot contextSnapshot, + final AbstractSpan parentEntrySpan) { super(delegate); this.operationPrefix = OperationNameFormatUtil.formatOperationName(delegate.getMethodDescriptor()) + SERVER; + this.contextSnapshot = contextSnapshot; + this.parentEntrySpan = parentEntrySpan; } @Override public void sendMessage(RESPONSE message) { // We just create the request on message span for server stream calls. if (!getMethodDescriptor().getType().serverSendsOneMessage()) { - final AbstractSpan span = ContextManager.createLocalSpan(operationPrefix + RESPONSE_ON_MESSAGE_OPERATION_NAME); + final AbstractSpan span = ContextManager.createLocalSpan( + operationPrefix + RESPONSE_ON_MESSAGE_OPERATION_NAME); span.setComponent(ComponentsDefine.GRPC); span.setLayer(SpanLayer.RPC_FRAMEWORK); - ContextManager.continued(ServerInterceptor.CONTEXT_SNAPSHOT_KEY.get()); + ContextManager.continued(contextSnapshot); try { super.sendMessage(message); } catch (Throwable t) { @@ -68,7 +76,7 @@ public void close(Status status, Metadata trailers) { final AbstractSpan span = ContextManager.createLocalSpan(operationPrefix + RESPONSE_ON_CLOSE_OPERATION_NAME); span.setComponent(ComponentsDefine.GRPC); span.setLayer(SpanLayer.RPC_FRAMEWORK); - ContextManager.continued(ServerInterceptor.CONTEXT_SNAPSHOT_KEY.get()); + ContextManager.continued(contextSnapshot); switch (status.getCode()) { case OK: break; @@ -94,7 +102,7 @@ public void close(Status status, Metadata trailers) { break; } Tags.RPC_RESPONSE_STATUS_CODE.set(span, status.getCode().name()); - ServerInterceptor.ACTIVE_SPAN_KEY.get().asyncFinish(); + parentEntrySpan.asyncFinish(); try { super.close(status, trailers); diff --git a/apm-sniffer/apm-sdk-plugin/grpc-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/grpc/v1/server/TracingServerCallListener.java b/apm-sniffer/apm-sdk-plugin/grpc-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/grpc/v1/server/TracingServerCallListener.java index cb19e86c6c..65910f003f 100644 --- a/apm-sniffer/apm-sdk-plugin/grpc-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/grpc/v1/server/TracingServerCallListener.java +++ b/apm-sniffer/apm-sdk-plugin/grpc-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/grpc/v1/server/TracingServerCallListener.java @@ -22,6 +22,7 @@ import io.grpc.MethodDescriptor; import io.grpc.ServerCall; import org.apache.skywalking.apm.agent.core.context.ContextManager; +import org.apache.skywalking.apm.agent.core.context.ContextSnapshot; import org.apache.skywalking.apm.agent.core.context.trace.AbstractSpan; import org.apache.skywalking.apm.agent.core.context.trace.SpanLayer; import org.apache.skywalking.apm.network.trace.component.ComponentsDefine; @@ -34,11 +35,17 @@ public class TracingServerCallListener extends ForwardingServerCallListener.SimpleForwardingServerCallListener { private final MethodDescriptor.MethodType methodType; private final String operationPrefix; + private final ContextSnapshot contextSnapshot; + private final AbstractSpan parentEntrySpan; - protected TracingServerCallListener(ServerCall.Listener delegate, MethodDescriptor descriptor) { + protected TracingServerCallListener(ServerCall.Listener delegate, MethodDescriptor descriptor, + final ContextSnapshot contextSnapshot, + final AbstractSpan parentEntrySpan) { super(delegate); this.methodType = descriptor.getType(); this.operationPrefix = OperationNameFormatUtil.formatOperationName(descriptor) + SERVER; + this.contextSnapshot = contextSnapshot; + this.parentEntrySpan = parentEntrySpan; } @Override @@ -48,7 +55,7 @@ public void onMessage(REQUEST message) { final AbstractSpan span = ContextManager.createLocalSpan(operationPrefix + REQUEST_ON_MESSAGE_OPERATION_NAME); span.setComponent(ComponentsDefine.GRPC); span.setLayer(SpanLayer.RPC_FRAMEWORK); - ContextManager.continued(ServerInterceptor.CONTEXT_SNAPSHOT_KEY.get()); + ContextManager.continued(contextSnapshot); try { super.onMessage(message); } catch (Throwable t) { @@ -67,7 +74,7 @@ public void onCancel() { final AbstractSpan span = ContextManager.createLocalSpan(operationPrefix + REQUEST_ON_CANCEL_OPERATION_NAME); span.setComponent(ComponentsDefine.GRPC); span.setLayer(SpanLayer.RPC_FRAMEWORK); - ContextManager.continued(ServerInterceptor.CONTEXT_SNAPSHOT_KEY.get()); + ContextManager.continued(contextSnapshot); try { super.onCancel(); } catch (Throwable t) { @@ -75,7 +82,7 @@ public void onCancel() { throw t; } finally { ContextManager.stopSpan(); - ServerInterceptor.ACTIVE_SPAN_KEY.get().asyncFinish(); + parentEntrySpan.asyncFinish(); } } @@ -84,7 +91,7 @@ public void onHalfClose() { final AbstractSpan span = ContextManager.createLocalSpan(operationPrefix + REQUEST_ON_HALF_CLOSE_OPERATION_NAME); span.setComponent(ComponentsDefine.GRPC); span.setLayer(SpanLayer.RPC_FRAMEWORK); - ContextManager.continued(ServerInterceptor.CONTEXT_SNAPSHOT_KEY.get()); + ContextManager.continued(contextSnapshot); try { super.onHalfClose(); } catch (Throwable t) { diff --git a/apm-sniffer/apm-sdk-plugin/guava-eventbus-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/guava-eventbus-plugin/pom.xml index 20d42e9f91..d2ad8e9292 100644 --- a/apm-sniffer/apm-sdk-plugin/guava-eventbus-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/guava-eventbus-plugin/pom.xml @@ -20,7 +20,7 @@ apm-sdk-plugin org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-sdk-plugin/h2-1.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/h2-1.x-plugin/pom.xml index 1195786969..4c359960a0 100755 --- a/apm-sniffer/apm-sdk-plugin/h2-1.x-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/h2-1.x-plugin/pom.xml @@ -20,7 +20,7 @@ apm-sdk-plugin org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-sdk-plugin/hbase-1.x-2.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/hbase-1.x-2.x-plugin/pom.xml index a947f6940d..d256acb1c5 100644 --- a/apm-sniffer/apm-sdk-plugin/hbase-1.x-2.x-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/hbase-1.x-2.x-plugin/pom.xml @@ -23,7 +23,7 @@ org.apache.skywalking apm-sdk-plugin - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT apm-hbase-1.x-2.x-plugin diff --git a/apm-sniffer/apm-sdk-plugin/hikaricp-3.x-4.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/hikaricp-3.x-4.x-plugin/pom.xml index 903716083c..864b91b125 100644 --- a/apm-sniffer/apm-sdk-plugin/hikaricp-3.x-4.x-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/hikaricp-3.x-4.x-plugin/pom.xml @@ -20,7 +20,7 @@ apm-sdk-plugin org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-sdk-plugin/hikaricp-3.x-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/hikaricp/PoolingSealInterceptor.java b/apm-sniffer/apm-sdk-plugin/hikaricp-3.x-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/hikaricp/PoolingSealInterceptor.java index 7be4b562c2..a2673afbf9 100644 --- a/apm-sniffer/apm-sdk-plugin/hikaricp-3.x-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/hikaricp/PoolingSealInterceptor.java +++ b/apm-sniffer/apm-sdk-plugin/hikaricp-3.x-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/hikaricp/PoolingSealInterceptor.java @@ -49,8 +49,13 @@ public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allAr public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, Object ret) throws Throwable { HikariDataSource hikariDataSource = (HikariDataSource) objInst; - ConnectionInfo connectionInfo = URLParser.parser(hikariDataSource.getJdbcUrl()); - String tagValue = connectionInfo.getDatabaseName() + "_" + connectionInfo.getDatabasePeer(); + String tagValue; + if (hikariDataSource.getJdbcUrl() != null) { + ConnectionInfo connectionInfo = URLParser.parser(hikariDataSource.getJdbcUrl()); + tagValue = connectionInfo.getDatabaseName() + "_" + connectionInfo.getDatabasePeer(); + } else { + tagValue = hikariDataSource.getPoolName(); + } final Map>> poolMetricMap = getPoolMetrics(); final Map>> metricConfigMap = getConfigMetrics(); poolMetricMap.forEach((key, value) -> MeterFactory.gauge(METER_NAME, value.apply(hikariDataSource.getHikariPoolMXBean())) diff --git a/apm-sniffer/apm-sdk-plugin/httpClient-4.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/httpClient-4.x-plugin/pom.xml index 3a10202ebd..0fcdd24c95 100644 --- a/apm-sniffer/apm-sdk-plugin/httpClient-4.x-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/httpClient-4.x-plugin/pom.xml @@ -22,7 +22,7 @@ org.apache.skywalking apm-sdk-plugin - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT apm-httpClient-4.x-plugin @@ -55,7 +55,6 @@ junit junit - ${junit.version} test diff --git a/apm-sniffer/apm-sdk-plugin/httpasyncclient-4.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/httpasyncclient-4.x-plugin/pom.xml index 8497d83457..2cd5025cd6 100644 --- a/apm-sniffer/apm-sdk-plugin/httpasyncclient-4.x-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/httpasyncclient-4.x-plugin/pom.xml @@ -22,7 +22,7 @@ org.apache.skywalking apm-sdk-plugin - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT apm-httpasyncclient-4.x-plugin diff --git a/apm-sniffer/apm-sdk-plugin/httpclient-3.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/httpclient-3.x-plugin/pom.xml index 477e9028a5..2127da9e0d 100644 --- a/apm-sniffer/apm-sdk-plugin/httpclient-3.x-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/httpclient-3.x-plugin/pom.xml @@ -23,7 +23,7 @@ org.apache.skywalking apm-sdk-plugin - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT apm-httpclient-3.x-plugin diff --git a/apm-sniffer/apm-sdk-plugin/httpclient-5.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/httpclient-5.x-plugin/pom.xml index d5da174f16..ba159639a4 100644 --- a/apm-sniffer/apm-sdk-plugin/httpclient-5.x-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/httpclient-5.x-plugin/pom.xml @@ -22,7 +22,7 @@ org.apache.skywalking apm-sdk-plugin - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT apm-httpclient-5.x-plugin @@ -46,7 +46,6 @@ junit junit - ${junit.version} test diff --git a/apm-sniffer/apm-sdk-plugin/httpclient-5.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/httpclient/v5/HttpClient5PluginConfig.java b/apm-sniffer/apm-sdk-plugin/httpclient-5.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/httpclient/v5/HttpClient5PluginConfig.java new file mode 100644 index 0000000000..3b6aaf6aa9 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/httpclient-5.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/httpclient/v5/HttpClient5PluginConfig.java @@ -0,0 +1,47 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.httpclient.v5; + +import org.apache.skywalking.apm.agent.core.boot.PluginConfig; + +public class HttpClient5PluginConfig { + public static class Plugin { + @PluginConfig(root = HttpClient5PluginConfig.class) + public static class HttpClient5 { + /** + * Comma-separated list of destination ports whose outbound HTTP requests + * will be completely skipped by the classic client interceptor: no exit + * span is created and no SkyWalking propagation headers are injected. + * + *

Some HTTP-based database protocols (e.g. ClickHouse on port 8123) + * reject requests that contain unknown HTTP headers, returning HTTP 400. + * Adding such ports here prevents the agent from creating exit spans + * and from injecting the {@code sw8} tracing headers into those outbound + * requests, meaning these requests are completely untraced by SkyWalking, + * while leaving all other HTTP calls fully traced. + * + *

Default: {@code "8123"} (ClickHouse HTTP interface). + * + *

Example – also exclude port 9200 (Elasticsearch): + * {@code plugin.httpclient5.PROPAGATION_EXCLUDE_PORTS=8123,9200} + */ + public static String PROPAGATION_EXCLUDE_PORTS = "8123"; + } + } +} diff --git a/apm-sniffer/apm-sdk-plugin/httpclient-5.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/httpclient/v5/HttpClientDoExecuteInterceptor.java b/apm-sniffer/apm-sdk-plugin/httpclient-5.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/httpclient/v5/HttpClientDoExecuteInterceptor.java index e31b5cae1e..238a745b69 100644 --- a/apm-sniffer/apm-sdk-plugin/httpclient-5.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/httpclient/v5/HttpClientDoExecuteInterceptor.java +++ b/apm-sniffer/apm-sdk-plugin/httpclient-5.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/httpclient/v5/HttpClientDoExecuteInterceptor.java @@ -37,20 +37,31 @@ import java.lang.reflect.Method; import java.net.MalformedURLException; import java.net.URL; +import java.util.Arrays; +import java.util.Collections; +import java.util.Set; +import java.util.stream.Collectors; -public class HttpClientDoExecuteInterceptor implements InstanceMethodsAroundInterceptor { +public abstract class HttpClientDoExecuteInterceptor implements InstanceMethodsAroundInterceptor { private static final String ERROR_URI = "/_blank"; private static final ILog LOGGER = LogManager.getLogger(HttpClientDoExecuteInterceptor.class); + /** + * Lazily-resolved set of ports that must not receive SkyWalking + * propagation headers. Built once from + * {@link HttpClient5PluginConfig.Plugin.HttpClient5#PROPAGATION_EXCLUDE_PORTS}. + */ + private volatile Set excludePortsCache; + @Override public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, MethodInterceptResult result) throws Throwable { - if (allArguments[0] == null || allArguments[1] == null) { + if (skipIntercept(objInst, method, allArguments, argumentsTypes)) { // illegal args, can't trace. ignore. return; } - final HttpHost httpHost = (HttpHost) allArguments[0]; + final HttpHost httpHost = getHttpHost(objInst, method, allArguments, argumentsTypes); ClassicHttpRequest httpRequest = (ClassicHttpRequest) allArguments[1]; final ContextCarrier contextCarrier = new ContextCarrier(); @@ -75,10 +86,66 @@ public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allAr } } + protected boolean skipIntercept(EnhancedInstance objInst, Method method, Object[] allArguments, + Class[] argumentsTypes) { + if (allArguments[1] == null) { + return true; + } + HttpHost host = getHttpHost(objInst, method, allArguments, argumentsTypes); + if (host == null) { + return true; + } + return isExcludedPort(port(host)); + } + + /** + * Returns {@code true} when {@code port} is listed in + * {@link HttpClient5PluginConfig.Plugin.HttpClient5#PROPAGATION_EXCLUDE_PORTS}. + * + *

The config value is parsed lazily and cached so that it is read after + * the agent has fully initialised its configuration subsystem. + */ + private boolean isExcludedPort(int port) { + if (port <= 0) { + return false; + } + if (excludePortsCache == null) { + synchronized (this) { + if (excludePortsCache == null) { + excludePortsCache = parseExcludePorts( + HttpClient5PluginConfig.Plugin.HttpClient5.PROPAGATION_EXCLUDE_PORTS); + } + } + } + return excludePortsCache.contains(port); + } + + private static Set parseExcludePorts(String raw) { + if (raw == null || raw.trim().isEmpty()) { + return Collections.emptySet(); + } + return Arrays.stream(raw.split(",")) + .map(String::trim) + .filter(s -> !s.isEmpty()) + .map(s -> { + try { + return Integer.parseInt(s); + } catch (NumberFormatException e) { + LOGGER.warn("Ignoring invalid port in PROPAGATION_EXCLUDE_PORTS: {}", s); + return -1; + } + }) + .filter(p -> p > 0) + .collect(Collectors.toSet()); + } + + protected abstract HttpHost getHttpHost(EnhancedInstance objInst, Method method, Object[] allArguments, + Class[] argumentsTypes) ; + @Override public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, Object ret) throws Throwable { - if (allArguments[0] == null || allArguments[1] == null) { + if (skipIntercept(objInst, method, allArguments, argumentsTypes)) { return ret; } @@ -100,6 +167,9 @@ public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allA @Override public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, Throwable t) { + if (skipIntercept(objInst, method, allArguments, argumentsTypes)) { + return; + } AbstractSpan activeSpan = ContextManager.activeSpan(); activeSpan.log(t); } diff --git a/apm-sniffer/apm-sdk-plugin/httpclient-5.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/httpclient/v5/InternalClientDoExecuteInterceptor.java b/apm-sniffer/apm-sdk-plugin/httpclient-5.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/httpclient/v5/InternalClientDoExecuteInterceptor.java new file mode 100644 index 0000000000..c487452473 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/httpclient-5.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/httpclient/v5/InternalClientDoExecuteInterceptor.java @@ -0,0 +1,44 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.httpclient.v5; + +import org.apache.hc.client5.http.routing.RoutingSupport; +import org.apache.hc.core5.http.HttpHost; +import org.apache.hc.core5.http.HttpRequest; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; + +import java.lang.reflect.Method; + +public class InternalClientDoExecuteInterceptor extends HttpClientDoExecuteInterceptor { + + @Override + protected HttpHost getHttpHost(EnhancedInstance objInst, Method method, Object[] allArguments, + Class[] argumentsTypes) { + HttpHost httpHost = (HttpHost) allArguments[0]; + if (httpHost != null) { + return httpHost; + } + try { + return RoutingSupport.determineHost((HttpRequest) allArguments[1]); + } catch (Exception ignore) { + // ignore + return null; + } + } +} diff --git a/apm-sniffer/apm-sdk-plugin/httpclient-5.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/httpclient/v5/MinimalClientDoExecuteInterceptor.java b/apm-sniffer/apm-sdk-plugin/httpclient-5.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/httpclient/v5/MinimalClientDoExecuteInterceptor.java new file mode 100644 index 0000000000..4c1dc38c4b --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/httpclient-5.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/httpclient/v5/MinimalClientDoExecuteInterceptor.java @@ -0,0 +1,33 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.httpclient.v5; + +import org.apache.hc.core5.http.HttpHost; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; + +import java.lang.reflect.Method; + +public class MinimalClientDoExecuteInterceptor extends HttpClientDoExecuteInterceptor { + + @Override + protected HttpHost getHttpHost(EnhancedInstance objInst, Method method, Object[] allArguments, + Class[] argumentsTypes) { + return (HttpHost) allArguments[0]; + } +} diff --git a/apm-sniffer/apm-sdk-plugin/httpclient-5.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/httpclient/v5/define/InternalHttpClientInstrumentation.java b/apm-sniffer/apm-sdk-plugin/httpclient-5.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/httpclient/v5/define/InternalHttpClientInstrumentation.java new file mode 100644 index 0000000000..431a4c1225 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/httpclient-5.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/httpclient/v5/define/InternalHttpClientInstrumentation.java @@ -0,0 +1,68 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.httpclient.v5.define; + +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.matcher.ElementMatcher; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine; +import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch; +import org.apache.skywalking.apm.agent.core.plugin.match.MultiClassNameMatch; + +import static net.bytebuddy.matcher.ElementMatchers.named; + +public class InternalHttpClientInstrumentation extends ClassInstanceMethodsEnhancePluginDefine { + + private static final String ENHANCE_CLASS_MINIMAL = "org.apache.hc.client5.http.impl.classic.InternalHttpClient"; + private static final String METHOD_NAME = "doExecute"; + private static final String INTERCEPT_CLASS = "org.apache.skywalking.apm.plugin.httpclient.v5.InternalClientDoExecuteInterceptor"; + + @Override + public ClassMatch enhanceClass() { + return MultiClassNameMatch.byMultiClassMatch(ENHANCE_CLASS_MINIMAL); + } + + @Override + public ConstructorInterceptPoint[] getConstructorsInterceptPoints() { + return new ConstructorInterceptPoint[0]; + } + + @Override + public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() { + return new InstanceMethodsInterceptPoint[]{ + new InstanceMethodsInterceptPoint() { + @Override + public ElementMatcher getMethodsMatcher() { + return named(METHOD_NAME); + } + + @Override + public String getMethodsInterceptor() { + return INTERCEPT_CLASS; + } + + @Override + public boolean isOverrideArgs() { + return false; + } + } + }; + } +} diff --git a/apm-sniffer/apm-sdk-plugin/httpclient-5.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/httpclient/v5/define/HttpClientInstrumentation.java b/apm-sniffer/apm-sdk-plugin/httpclient-5.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/httpclient/v5/define/MinimalHttpClientInstrumentation.java similarity index 88% rename from apm-sniffer/apm-sdk-plugin/httpclient-5.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/httpclient/v5/define/HttpClientInstrumentation.java rename to apm-sniffer/apm-sdk-plugin/httpclient-5.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/httpclient/v5/define/MinimalHttpClientInstrumentation.java index 79593f1ada..f078cc04e8 100644 --- a/apm-sniffer/apm-sdk-plugin/httpclient-5.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/httpclient/v5/define/HttpClientInstrumentation.java +++ b/apm-sniffer/apm-sdk-plugin/httpclient-5.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/httpclient/v5/define/MinimalHttpClientInstrumentation.java @@ -28,21 +28,20 @@ import static net.bytebuddy.matcher.ElementMatchers.named; -public class HttpClientInstrumentation extends ClassInstanceMethodsEnhancePluginDefine { +public class MinimalHttpClientInstrumentation extends ClassInstanceMethodsEnhancePluginDefine { private static final String ENHANCE_CLASS_MINIMAL = "org.apache.hc.client5.http.impl.classic.MinimalHttpClient"; - private static final String ENHANCE_CLASS_INTERNAL = "org.apache.hc.client5.http.impl.classic.InternalHttpClient"; private static final String METHOD_NAME = "doExecute"; - private static final String INTERCEPT_CLASS = "org.apache.skywalking.apm.plugin.httpclient.v5.HttpClientDoExecuteInterceptor"; + private static final String INTERCEPT_CLASS = "org.apache.skywalking.apm.plugin.httpclient.v5.MinimalClientDoExecuteInterceptor"; @Override public ClassMatch enhanceClass() { - return MultiClassNameMatch.byMultiClassMatch(ENHANCE_CLASS_MINIMAL, ENHANCE_CLASS_INTERNAL); + return MultiClassNameMatch.byMultiClassMatch(ENHANCE_CLASS_MINIMAL); } @Override public ConstructorInterceptPoint[] getConstructorsInterceptPoints() { - return null; + return new ConstructorInterceptPoint[0]; } @Override diff --git a/apm-sniffer/apm-sdk-plugin/httpclient-5.x-plugin/src/main/resources/skywalking-plugin.def b/apm-sniffer/apm-sdk-plugin/httpclient-5.x-plugin/src/main/resources/skywalking-plugin.def index 7f04889ca2..dc6622a88f 100644 --- a/apm-sniffer/apm-sdk-plugin/httpclient-5.x-plugin/src/main/resources/skywalking-plugin.def +++ b/apm-sniffer/apm-sdk-plugin/httpclient-5.x-plugin/src/main/resources/skywalking-plugin.def @@ -14,6 +14,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -httpclient-5.x=org.apache.skywalking.apm.plugin.httpclient.v5.define.HttpClientInstrumentation +httpclient-5.x=org.apache.skywalking.apm.plugin.httpclient.v5.define.MinimalHttpClientInstrumentation +httpclient-5.x=org.apache.skywalking.apm.plugin.httpclient.v5.define.InternalHttpClientInstrumentation httpclient-5.x=org.apache.skywalking.apm.plugin.httpclient.v5.define.HttpAsyncClientInstrumentation httpclient-5.x=org.apache.skywalking.apm.plugin.httpclient.v5.define.IOSessionImplInstrumentation diff --git a/apm-sniffer/apm-sdk-plugin/httpclient-5.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/httpclient/v5/HttpClientPropagationExcludePortTest.java b/apm-sniffer/apm-sdk-plugin/httpclient-5.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/httpclient/v5/HttpClientPropagationExcludePortTest.java new file mode 100644 index 0000000000..717eb61a3e --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/httpclient-5.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/httpclient/v5/HttpClientPropagationExcludePortTest.java @@ -0,0 +1,244 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.httpclient.v5; + +import org.apache.hc.core5.http.ClassicHttpRequest; +import org.apache.hc.core5.http.ClassicHttpResponse; +import org.apache.hc.core5.http.HttpHost; +import org.apache.skywalking.apm.agent.core.boot.ServiceManager; +import org.apache.skywalking.apm.agent.core.context.trace.TraceSegment; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; +import org.apache.skywalking.apm.agent.test.tools.AgentServiceRule; +import org.apache.skywalking.apm.agent.test.tools.SegmentStorage; +import org.apache.skywalking.apm.agent.test.tools.SegmentStoragePoint; +import org.apache.skywalking.apm.agent.test.tools.TracingSegmentRunner; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +import java.net.URI; +import java.util.List; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +/** + * Verifies that requests to ports listed in + * {@link HttpClient5PluginConfig.Plugin.HttpClient5#PROPAGATION_EXCLUDE_PORTS} + * are silently skipped (no span created, no {@code sw8} header injected). + * + *

This regression-covers the ClickHouse HTTP-interface issue: ClickHouse + * listens on port 8123 and rejects HTTP requests that carry unknown headers + * (such as the SkyWalking {@code sw8} propagation header), responding with + * HTTP 400 Bad Request. By excluding port 8123 the agent leaves those + * requests untouched while continuing to trace all other HTTP calls. + */ +@RunWith(TracingSegmentRunner.class) +public class HttpClientPropagationExcludePortTest { + + @SegmentStoragePoint + private SegmentStorage segmentStorage; + + @Rule + public AgentServiceRule agentServiceRule = new AgentServiceRule(); + @Rule + public MockitoRule rule = MockitoJUnit.rule(); + + @Mock + private HttpHost clickHouseHost; // port 8123 – should be excluded + @Mock + private HttpHost regularHost; // port 8080 – should be traced + @Mock + private ClassicHttpRequest request; + @Mock + private ClassicHttpResponse httpResponse; + @Mock + private EnhancedInstance enhancedInstance; + + private HttpClientDoExecuteInterceptor internalInterceptor; + private HttpClientDoExecuteInterceptor minimalInterceptor; + + private Object[] clickHouseArgs; + private Object[] regularArgs; + private Class[] argumentsType; + + @Before + public void setUp() throws Exception { + ServiceManager.INSTANCE.boot(); + + // Set the exclusion list to the default (includes 8123) + HttpClient5PluginConfig.Plugin.HttpClient5.PROPAGATION_EXCLUDE_PORTS = "8123"; + + internalInterceptor = new InternalClientDoExecuteInterceptor(); + minimalInterceptor = new MinimalClientDoExecuteInterceptor(); + + when(httpResponse.getCode()).thenReturn(200); + + // ClickHouse-like host on port 8123 + when(clickHouseHost.getHostName()).thenReturn("clickhouse-server"); + when(clickHouseHost.getSchemeName()).thenReturn("http"); + when(clickHouseHost.getPort()).thenReturn(8123); + + // Regular application host on port 8080 + when(regularHost.getHostName()).thenReturn("my-service"); + when(regularHost.getSchemeName()).thenReturn("http"); + when(regularHost.getPort()).thenReturn(8080); + + when(request.getUri()).thenReturn(new URI("http://my-service:8080/api/ping")); + when(request.getMethod()).thenReturn("GET"); + + clickHouseArgs = new Object[]{clickHouseHost, request}; + regularArgs = new Object[]{regularHost, request}; + argumentsType = new Class[]{HttpHost.class, ClassicHttpRequest.class}; + } + + // ----------------------------------------------------------------------- + // InternalHttpClient path + // ----------------------------------------------------------------------- + + /** + * Requests to port 8123 via {@code InternalHttpClient} must not produce a + * trace segment and must NOT set any propagation header on the request. + * + *

Before this fix the agent injected {@code sw8} (and two companion + * headers) into every outbound request regardless of the destination port. + * ClickHouse interprets unknown headers as malformed requests and returns + * HTTP 400, making all JDBC queries fail while the SkyWalking agent is + * attached. + */ + @Test + public void internalClient_requestToExcludedPort_noSpanAndNoHeaderInjected() throws Throwable { + internalInterceptor.beforeMethod(enhancedInstance, null, clickHouseArgs, argumentsType, null); + internalInterceptor.afterMethod(enhancedInstance, null, clickHouseArgs, argumentsType, httpResponse); + + List segments = segmentStorage.getTraceSegments(); + assertThat("No trace segment should be created for excluded port", segments.size(), is(0)); + verify(request, never()).setHeader(anyString(), anyString()); + } + + /** + * Requests to a non-excluded port via {@code InternalHttpClient} must still + * be traced and have propagation headers injected. + */ + @Test + public void internalClient_requestToRegularPort_spanCreatedAndHeadersInjected() throws Throwable { + internalInterceptor.beforeMethod(enhancedInstance, null, regularArgs, argumentsType, null); + internalInterceptor.afterMethod(enhancedInstance, null, regularArgs, argumentsType, httpResponse); + + List segments = segmentStorage.getTraceSegments(); + assertThat("A trace segment must be created for a non-excluded port", segments.size(), is(1)); + // sw8, sw8-correlation, sw8-x – exactly 3 propagation headers, consistent with existing tests + verify(request, org.mockito.Mockito.times(3)).setHeader(anyString(), anyString()); + } + + // ----------------------------------------------------------------------- + // MinimalHttpClient path + // ----------------------------------------------------------------------- + + /** + * Same assertion for the {@code MinimalHttpClient} code path. + */ + @Test + public void minimalClient_requestToExcludedPort_noSpanAndNoHeaderInjected() throws Throwable { + minimalInterceptor.beforeMethod(enhancedInstance, null, clickHouseArgs, argumentsType, null); + minimalInterceptor.afterMethod(enhancedInstance, null, clickHouseArgs, argumentsType, httpResponse); + + List segments = segmentStorage.getTraceSegments(); + assertThat("No trace segment should be created for excluded port", segments.size(), is(0)); + verify(request, never()).setHeader(anyString(), anyString()); + } + + /** + * Normal (non-excluded) port via {@code MinimalHttpClient} must still be + * traced. + */ + @Test + public void minimalClient_requestToRegularPort_spanCreatedAndHeadersInjected() throws Throwable { + minimalInterceptor.beforeMethod(enhancedInstance, null, regularArgs, argumentsType, null); + minimalInterceptor.afterMethod(enhancedInstance, null, regularArgs, argumentsType, httpResponse); + + List segments = segmentStorage.getTraceSegments(); + assertThat("A trace segment must be created for a non-excluded port", segments.size(), is(1)); + verify(request, org.mockito.Mockito.times(3)).setHeader(anyString(), anyString()); + } + + // ----------------------------------------------------------------------- + // Configuration edge cases + // ----------------------------------------------------------------------- + + /** + * When {@code PROPAGATION_EXCLUDE_PORTS} is cleared (empty string), every + * port – including 8123 – must be traced normally. + */ + @Test + public void whenExcludePortsEmpty_allPortsAreTraced() throws Throwable { + HttpClient5PluginConfig.Plugin.HttpClient5.PROPAGATION_EXCLUDE_PORTS = ""; + + // Use a fresh interceptor so the cache is not pre-populated + HttpClientDoExecuteInterceptor freshInterceptor = new MinimalClientDoExecuteInterceptor(); + freshInterceptor.beforeMethod(enhancedInstance, null, clickHouseArgs, argumentsType, null); + freshInterceptor.afterMethod(enhancedInstance, null, clickHouseArgs, argumentsType, httpResponse); + + List segments = segmentStorage.getTraceSegments(); + assertThat("Port 8123 should be traced when exclusion list is empty", segments.size(), is(1)); + } + + /** + * Multiple ports can be listed: verify that both excluded ports are silently + * skipped while a non-excluded port is still traced under the same config. + */ + @Test + public void multipleExcludedPorts_allSkippedAndNonExcludedStillTraced() throws Throwable { + HttpClient5PluginConfig.Plugin.HttpClient5.PROPAGATION_EXCLUDE_PORTS = "8123,9200"; + + HttpClientDoExecuteInterceptor freshInterceptor = new MinimalClientDoExecuteInterceptor(); + + // 8123 – must be excluded + freshInterceptor.beforeMethod(enhancedInstance, null, clickHouseArgs, argumentsType, null); + freshInterceptor.afterMethod(enhancedInstance, null, clickHouseArgs, argumentsType, httpResponse); + assertThat("Port 8123 should be excluded", segmentStorage.getTraceSegments().size(), is(0)); + + // 9200 (Elasticsearch) – must also be excluded + HttpHost esHost = org.mockito.Mockito.mock(HttpHost.class); + when(esHost.getHostName()).thenReturn("es-server"); + when(esHost.getSchemeName()).thenReturn("http"); + when(esHost.getPort()).thenReturn(9200); + Object[] esArgs = new Object[]{esHost, request}; + + freshInterceptor = new MinimalClientDoExecuteInterceptor(); + freshInterceptor.beforeMethod(enhancedInstance, null, esArgs, argumentsType, null); + freshInterceptor.afterMethod(enhancedInstance, null, esArgs, argumentsType, httpResponse); + assertThat("Port 9200 should also be excluded", segmentStorage.getTraceSegments().size(), is(0)); + + // 8080 (regular service) – must still be traced under the same multi-port config + freshInterceptor = new MinimalClientDoExecuteInterceptor(); + freshInterceptor.beforeMethod(enhancedInstance, null, regularArgs, argumentsType, null); + freshInterceptor.afterMethod(enhancedInstance, null, regularArgs, argumentsType, httpResponse); + assertThat("Non-excluded port 8080 must still produce a trace segment", + segmentStorage.getTraceSegments().size(), is(1)); + } +} diff --git a/apm-sniffer/apm-sdk-plugin/httpclient-5.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/httpclient/v5/InternalHttpClientExecuteInterceptorTest.java b/apm-sniffer/apm-sdk-plugin/httpclient-5.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/httpclient/v5/InternalHttpClientExecuteInterceptorTest.java new file mode 100644 index 0000000000..f20bf6254a --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/httpclient-5.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/httpclient/v5/InternalHttpClientExecuteInterceptorTest.java @@ -0,0 +1,262 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.httpclient.v5; + +import org.apache.hc.core5.http.ClassicHttpRequest; +import org.apache.hc.core5.http.ClassicHttpResponse; +import org.apache.hc.core5.http.HttpHost; +import org.apache.skywalking.apm.agent.core.boot.ServiceManager; +import org.apache.skywalking.apm.agent.core.context.trace.AbstractTracingSpan; +import org.apache.skywalking.apm.agent.core.context.trace.LogDataEntity; +import org.apache.skywalking.apm.agent.core.context.trace.TraceSegment; +import org.apache.skywalking.apm.agent.core.context.util.TagValuePair; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; +import org.apache.skywalking.apm.agent.test.helper.SegmentHelper; +import org.apache.skywalking.apm.agent.test.helper.SpanHelper; +import org.apache.skywalking.apm.agent.test.tools.AgentServiceRule; +import org.apache.skywalking.apm.agent.test.tools.SegmentStorage; +import org.apache.skywalking.apm.agent.test.tools.SegmentStoragePoint; +import org.apache.skywalking.apm.agent.test.tools.TracingSegmentRunner; +import org.hamcrest.CoreMatchers; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +import java.net.URI; +import java.util.List; + +import static junit.framework.TestCase.assertNotNull; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@RunWith(TracingSegmentRunner.class) +public class InternalHttpClientExecuteInterceptorTest { + + @SegmentStoragePoint + private SegmentStorage segmentStorage; + + @Rule + public AgentServiceRule agentServiceRule = new AgentServiceRule(); + @Rule + public MockitoRule rule = MockitoJUnit.rule(); + + private HttpClientDoExecuteInterceptor httpClientDoExecuteInterceptor; + + @Mock + private HttpHost httpHost; + @Mock + private ClassicHttpRequest request; + @Mock + private ClassicHttpResponse httpResponse; + + private Object[] allArguments; + private Object[] allArgumentsWithNullHttpHost; + private Class[] argumentsType; + + @Mock + private EnhancedInstance enhancedInstance; + + @Before + public void setUp() throws Exception { + + ServiceManager.INSTANCE.boot(); + httpClientDoExecuteInterceptor = new InternalClientDoExecuteInterceptor(); + + when(httpResponse.getCode()).thenReturn(200); + when(httpHost.getHostName()).thenReturn("127.0.0.1"); + when(httpHost.getSchemeName()).thenReturn("http"); + when(request.getUri()).thenReturn(new URI("http://127.0.0.1:8080/test-web/test")); + when(request.getMethod()).thenReturn("GET"); + when(httpHost.getPort()).thenReturn(8080); + + allArguments = new Object[]{ + httpHost, + request + }; + allArgumentsWithNullHttpHost = new Object[]{ + null, + request + }; + argumentsType = new Class[]{ + httpHost.getClass(), + request.getClass() + }; + } + + @Test + public void testHttpClient() throws Throwable { + httpClientDoExecuteInterceptor.beforeMethod(enhancedInstance, null, allArguments, argumentsType, null); + httpClientDoExecuteInterceptor.afterMethod(enhancedInstance, null, allArguments, argumentsType, httpResponse); + + Assert.assertThat(segmentStorage.getTraceSegments().size(), is(1)); + TraceSegment traceSegment = segmentStorage.getTraceSegments().get(0); + + List spans = SegmentHelper.getSpans(traceSegment); + assertHttpSpan(spans.get(0)); + verify(request, times(3)).setHeader(anyString(), anyString()); + + } + + @Test + public void testNullHttpHostHttpClient() throws Throwable { + httpClientDoExecuteInterceptor.beforeMethod(enhancedInstance, null, allArgumentsWithNullHttpHost, argumentsType, null); + httpClientDoExecuteInterceptor.afterMethod(enhancedInstance, null, allArgumentsWithNullHttpHost, argumentsType, httpResponse); + + Assert.assertThat(segmentStorage.getTraceSegments().size(), is(1)); + TraceSegment traceSegment = segmentStorage.getTraceSegments().get(0); + + List spans = SegmentHelper.getSpans(traceSegment); + assertHttpSpan(spans.get(0)); + verify(request, times(3)).setHeader(anyString(), anyString()); + } + + @Test + public void testStatusCodeNotEquals200() throws Throwable { + when(httpResponse.getCode()).thenReturn(500); + httpClientDoExecuteInterceptor.beforeMethod(enhancedInstance, null, allArguments, argumentsType, null); + httpClientDoExecuteInterceptor.afterMethod(enhancedInstance, null, allArguments, argumentsType, httpResponse); + + Assert.assertThat(segmentStorage.getTraceSegments().size(), is(1)); + TraceSegment traceSegment = segmentStorage.getTraceSegments().get(0); + List spans = SegmentHelper.getSpans(traceSegment); + + assertThat(spans.size(), is(1)); + + List tags = SpanHelper.getTags(spans.get(0)); + assertThat(tags.size(), is(3)); + assertThat(tags.get(2).getValue(), is("500")); + + assertHttpSpan(spans.get(0)); + assertThat(SpanHelper.getErrorOccurred(spans.get(0)), is(true)); + verify(request, times(3)).setHeader(anyString(), anyString()); + } + + @Test + public void testNullHttpHostStatusCodeNotEquals200() throws Throwable { + when(httpResponse.getCode()).thenReturn(500); + httpClientDoExecuteInterceptor.beforeMethod(enhancedInstance, null, allArgumentsWithNullHttpHost, argumentsType, null); + httpClientDoExecuteInterceptor.afterMethod(enhancedInstance, null, allArgumentsWithNullHttpHost, argumentsType, httpResponse); + + Assert.assertThat(segmentStorage.getTraceSegments().size(), is(1)); + TraceSegment traceSegment = segmentStorage.getTraceSegments().get(0); + List spans = SegmentHelper.getSpans(traceSegment); + + assertThat(spans.size(), is(1)); + + List tags = SpanHelper.getTags(spans.get(0)); + assertThat(tags.size(), is(3)); + assertThat(tags.get(2).getValue(), is("500")); + + assertHttpSpan(spans.get(0)); + assertThat(SpanHelper.getErrorOccurred(spans.get(0)), is(true)); + verify(request, times(3)).setHeader(anyString(), anyString()); + } + + @Test + public void testHttpClientWithException() throws Throwable { + httpClientDoExecuteInterceptor.beforeMethod(enhancedInstance, null, allArguments, argumentsType, null); + httpClientDoExecuteInterceptor.handleMethodException(enhancedInstance, null, allArguments, argumentsType, + new RuntimeException("testException")); + httpClientDoExecuteInterceptor.afterMethod(enhancedInstance, null, allArguments, argumentsType, httpResponse); + + Assert.assertThat(segmentStorage.getTraceSegments().size(), is(1)); + TraceSegment traceSegment = segmentStorage.getTraceSegments().get(0); + List spans = SegmentHelper.getSpans(traceSegment); + + assertThat(spans.size(), is(1)); + AbstractTracingSpan span = spans.get(0); + assertHttpSpan(span); + assertThat(SpanHelper.getErrorOccurred(span), is(true)); + assertHttpSpanErrorLog(SpanHelper.getLogs(span)); + verify(request, times(3)).setHeader(anyString(), anyString()); + } + + @Test + public void testNullHttpHostHttpClientWithException() throws Throwable { + httpClientDoExecuteInterceptor.beforeMethod(enhancedInstance, null, allArgumentsWithNullHttpHost, argumentsType, null); + httpClientDoExecuteInterceptor.handleMethodException(enhancedInstance, null, allArgumentsWithNullHttpHost, argumentsType, + new RuntimeException("testException")); + httpClientDoExecuteInterceptor.afterMethod(enhancedInstance, null, allArgumentsWithNullHttpHost, argumentsType, httpResponse); + + Assert.assertThat(segmentStorage.getTraceSegments().size(), is(1)); + TraceSegment traceSegment = segmentStorage.getTraceSegments().get(0); + List spans = SegmentHelper.getSpans(traceSegment); + + assertThat(spans.size(), is(1)); + AbstractTracingSpan span = spans.get(0); + assertHttpSpan(span); + assertThat(SpanHelper.getErrorOccurred(span), is(true)); + assertHttpSpanErrorLog(SpanHelper.getLogs(span)); + verify(request, times(3)).setHeader(anyString(), anyString()); + } + + @Test + public void testUriNotProtocol() throws Throwable { + when(request.getUri()).thenReturn(new URI("/test-web/test")); + httpClientDoExecuteInterceptor.beforeMethod(enhancedInstance, null, allArguments, argumentsType, null); + httpClientDoExecuteInterceptor.afterMethod(enhancedInstance, null, allArguments, argumentsType, httpResponse); + + Assert.assertThat(segmentStorage.getTraceSegments().size(), is(1)); + TraceSegment traceSegment = segmentStorage.getTraceSegments().get(0); + + List spans = SegmentHelper.getSpans(traceSegment); + assertHttpSpan(spans.get(0)); + verify(request, times(3)).setHeader(anyString(), anyString()); + } + + @Test + public void testNullHttpHostUriNotProtocol() throws Throwable { + when(request.getUri()).thenReturn(new URI("/test-web/test")); + httpClientDoExecuteInterceptor.beforeMethod(enhancedInstance, null, allArgumentsWithNullHttpHost, argumentsType, null); + httpClientDoExecuteInterceptor.afterMethod(enhancedInstance, null, allArgumentsWithNullHttpHost, argumentsType, httpResponse); + + Assert.assertThat(segmentStorage.getTraceSegments().size(), is(0)); + } + + private void assertHttpSpanErrorLog(List logs) { + assertThat(logs.size(), is(1)); + LogDataEntity logData = logs.get(0); + Assert.assertThat(logData.getLogs().size(), is(4)); + Assert.assertThat(logData.getLogs().get(0).getValue(), CoreMatchers.is("error")); + Assert.assertThat(logData.getLogs() + .get(1) + .getValue(), CoreMatchers.is(RuntimeException.class.getName())); + Assert.assertThat(logData.getLogs().get(2).getValue(), is("testException")); + assertNotNull(logData.getLogs().get(3).getValue()); + } + + private void assertHttpSpan(AbstractTracingSpan span) { + assertThat(span.getOperationName(), is("/test-web/test")); + assertThat(SpanHelper.getComponentId(span), is(2)); + List tags = SpanHelper.getTags(span); + assertThat(tags.get(0).getValue(), is("http://127.0.0.1:8080/test-web/test")); + assertThat(tags.get(1).getValue(), is("GET")); + assertThat(span.isExit(), is(true)); + } + +} diff --git a/apm-sniffer/apm-sdk-plugin/httpclient-5.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/httpclient/v5/HttpClientExecuteInterceptorTest.java b/apm-sniffer/apm-sdk-plugin/httpclient-5.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/httpclient/v5/MinimalHttpClientExecuteInterceptorTest.java similarity index 98% rename from apm-sniffer/apm-sdk-plugin/httpclient-5.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/httpclient/v5/HttpClientExecuteInterceptorTest.java rename to apm-sniffer/apm-sdk-plugin/httpclient-5.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/httpclient/v5/MinimalHttpClientExecuteInterceptorTest.java index b4c1972d1e..57b9d90553 100644 --- a/apm-sniffer/apm-sdk-plugin/httpclient-5.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/httpclient/v5/HttpClientExecuteInterceptorTest.java +++ b/apm-sniffer/apm-sdk-plugin/httpclient-5.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/httpclient/v5/MinimalHttpClientExecuteInterceptorTest.java @@ -53,7 +53,7 @@ import org.mockito.junit.MockitoRule; @RunWith(TracingSegmentRunner.class) -public class HttpClientExecuteInterceptorTest { +public class MinimalHttpClientExecuteInterceptorTest { @SegmentStoragePoint private SegmentStorage segmentStorage; @@ -82,7 +82,7 @@ public class HttpClientExecuteInterceptorTest { public void setUp() throws Exception { ServiceManager.INSTANCE.boot(); - httpClientDoExecuteInterceptor = new HttpClientDoExecuteInterceptor(); + httpClientDoExecuteInterceptor = new MinimalClientDoExecuteInterceptor(); when(httpResponse.getCode()).thenReturn(200); when(httpHost.getHostName()).thenReturn("127.0.0.1"); diff --git a/apm-sniffer/apm-sdk-plugin/httpclient-commons/pom.xml b/apm-sniffer/apm-sdk-plugin/httpclient-commons/pom.xml index 69b2a387a6..c9976aadf2 100644 --- a/apm-sniffer/apm-sdk-plugin/httpclient-commons/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/httpclient-commons/pom.xml @@ -22,7 +22,7 @@ apm-sdk-plugin org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT apm-httpclient-commons diff --git a/apm-sniffer/apm-sdk-plugin/hutool-plugins/hutool-http-5.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/hutool-plugins/hutool-http-5.x-plugin/pom.xml index 042be7b679..b31ba12cf4 100644 --- a/apm-sniffer/apm-sdk-plugin/hutool-plugins/hutool-http-5.x-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/hutool-plugins/hutool-http-5.x-plugin/pom.xml @@ -20,7 +20,7 @@ hutool-plugins org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-sdk-plugin/hutool-plugins/pom.xml b/apm-sniffer/apm-sdk-plugin/hutool-plugins/pom.xml index 257e44f41d..6c41996fb0 100644 --- a/apm-sniffer/apm-sdk-plugin/hutool-plugins/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/hutool-plugins/pom.xml @@ -20,7 +20,7 @@ apm-sdk-plugin org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 @@ -32,7 +32,6 @@ UTF-8 - /.. diff --git a/apm-sniffer/apm-sdk-plugin/hystrix-1.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/hystrix-1.x-plugin/pom.xml index 72b0b525a6..83f8023afe 100644 --- a/apm-sniffer/apm-sdk-plugin/hystrix-1.x-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/hystrix-1.x-plugin/pom.xml @@ -21,7 +21,7 @@ apm-sdk-plugin org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-sdk-plugin/influxdb-2.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/influxdb-2.x-plugin/pom.xml index f7aa70e074..e360c9ac4b 100644 --- a/apm-sniffer/apm-sdk-plugin/influxdb-2.x-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/influxdb-2.x-plugin/pom.xml @@ -21,7 +21,7 @@ apm-sdk-plugin org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-sdk-plugin/jdbc-commons/pom.xml b/apm-sniffer/apm-sdk-plugin/jdbc-commons/pom.xml index 3cef466f69..1eecc0d35c 100755 --- a/apm-sniffer/apm-sdk-plugin/jdbc-commons/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/jdbc-commons/pom.xml @@ -20,7 +20,7 @@ apm-sdk-plugin org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-sdk-plugin/jdbc-commons/src/main/java/org/apache/skywalking/apm/plugin/jdbc/connectionurl/parser/DMURLParser.java b/apm-sniffer/apm-sdk-plugin/jdbc-commons/src/main/java/org/apache/skywalking/apm/plugin/jdbc/connectionurl/parser/DMURLParser.java new file mode 100644 index 0000000000..2cb4c1c4c0 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/jdbc-commons/src/main/java/org/apache/skywalking/apm/plugin/jdbc/connectionurl/parser/DMURLParser.java @@ -0,0 +1,112 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.jdbc.connectionurl.parser; + +import org.apache.skywalking.apm.network.trace.component.ComponentsDefine; +import org.apache.skywalking.apm.plugin.jdbc.trace.ConnectionInfo; + +public class DMURLParser extends AbstractURLParser { + private static final int DEFAULT_PORT = 5236; + private static final String DB_TYPE = "DM"; + private static final String URL_PARAMS_HOST_KEY = "host"; + private static final String URL_PARAMS_PORT_KEY = "port"; + private static final String URL_PARAMS_SCHEMA_KEY = "schema"; + + public DMURLParser(String url) { + super(url); + } + + @Override + protected URLLocation fetchDatabaseHostsIndexRange() { + int hostLabelStartIndex = url.indexOf("//"); + if (hostLabelStartIndex == -1) { + return new URLLocation(0, 0); + } + int hostLabelEndIndex = url.indexOf("?", hostLabelStartIndex + 2); + if (hostLabelEndIndex == -1) { + hostLabelEndIndex = url.length(); + } + return new URLLocation(hostLabelStartIndex + 2, hostLabelEndIndex); + } + + @Override + protected URLLocation fetchDatabaseNameIndexRange() { + return new URLLocation(0, 0); + } + + @Override + public ConnectionInfo parse() { + URLLocation location = fetchDatabaseHostsIndexRange(); + String hostPortSegment = ""; + if (location.endIndex() > location.startIndex()) { + hostPortSegment = url.substring(location.startIndex(), location.endIndex()); + } + + String host = ""; + String port = ""; + + if (!hostPortSegment.isEmpty()) { + String[] parts = hostPortSegment.split(":"); + if (parts.length >= 1) { + host = parts[0]; + } + if (parts.length == 2) { + port = parts[1]; + } + } + + if (host.isEmpty()) { + host = fetchFromUrlParams(URL_PARAMS_HOST_KEY); + } + if (port == null || port.isEmpty()) { + port = fetchFromUrlParams(URL_PARAMS_PORT_KEY); + } + + if (port == null || port.isEmpty()) { + port = String.valueOf(DEFAULT_PORT); + } + + String schema = fetchFromUrlParams(URL_PARAMS_SCHEMA_KEY); + + return new ConnectionInfo( + ComponentsDefine.DMDB_JDBC_DRIVER, + DB_TYPE, + host, + Integer.parseInt(port), + schema == null ? "" : schema + ); + } + + private String fetchFromUrlParams(String key) { + int paramIndex = url.indexOf("?"); + if (paramIndex == -1) { + return null; + } + String[] params = url.substring(paramIndex + 1).split("&"); + for (String pair : params) { + if (pair.startsWith(key + "=")) { + String[] segments = pair.split("=", 2); + if (segments.length == 2) { + return segments[1]; + } + } + } + return null; + } +} diff --git a/apm-sniffer/apm-sdk-plugin/jdbc-commons/src/main/java/org/apache/skywalking/apm/plugin/jdbc/connectionurl/parser/Db2URLParser.java b/apm-sniffer/apm-sdk-plugin/jdbc-commons/src/main/java/org/apache/skywalking/apm/plugin/jdbc/connectionurl/parser/Db2URLParser.java new file mode 100644 index 0000000000..8fe2b36515 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/jdbc-commons/src/main/java/org/apache/skywalking/apm/plugin/jdbc/connectionurl/parser/Db2URLParser.java @@ -0,0 +1,129 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.jdbc.connectionurl.parser; + +import org.apache.skywalking.apm.network.trace.component.ComponentsDefine; +import org.apache.skywalking.apm.plugin.jdbc.trace.ConnectionInfo; + +public class Db2URLParser extends AbstractURLParser { + private static final String DEFAULT_HOST = "localhost"; + private static final int DEFAULT_PORT = 50000; + private static final String DB_TYPE = "DB2"; + private static final String JDBC_PREFIX = "jdbc:db2:"; + + public Db2URLParser(String url) { + super(url); + } + + @Override + protected URLLocation fetchDatabaseHostsIndexRange() { + int hostLabelStartIndex = url.indexOf("//"); + if (hostLabelStartIndex == -1) { + return null; + } + int hostLabelEndIndex = url.indexOf("/", hostLabelStartIndex + 2); + int hostLabelEndIndexWithParameter = url.indexOf(":", hostLabelEndIndex + 1); + if (hostLabelEndIndex == -1) { + hostLabelEndIndex = hostLabelEndIndexWithParameter; + } + if (hostLabelEndIndexWithParameter < hostLabelEndIndex && hostLabelEndIndexWithParameter != -1) { + hostLabelEndIndex = hostLabelEndIndexWithParameter; + } + if (hostLabelEndIndex == -1) { + hostLabelEndIndex = url.length(); + } + return new URLLocation(hostLabelStartIndex + 2, hostLabelEndIndex); + } + + protected String fetchDatabaseNameFromURL(int startSize) { + URLLocation hostsLocation = fetchDatabaseNameIndexRange(startSize); + if (hostsLocation == null) { + return ""; + } + return url.substring(hostsLocation.startIndex(), hostsLocation.endIndex()); + } + + protected URLLocation fetchDatabaseNameIndexRange(int startSize) { + int databaseStartTag = url.indexOf("/", startSize); + int parameterStartTag = url.indexOf(":", startSize); + if (parameterStartTag < databaseStartTag && parameterStartTag != -1) { + return null; + } + if (databaseStartTag == -1) { + databaseStartTag = startSize - 1; + } + int databaseEndTag = url.indexOf(":", startSize); + if (databaseEndTag == -1) { + databaseEndTag = url.length(); + } + return new URLLocation(databaseStartTag + 1, databaseEndTag); + } + + @Override + protected URLLocation fetchDatabaseNameIndexRange() { + int databaseStartTag = url.lastIndexOf("/"); + int databaseEndTag = url.indexOf(":", databaseStartTag); + if (databaseEndTag == -1) { + databaseEndTag = url.length(); + } + return new URLLocation(databaseStartTag + 1, databaseEndTag); + } + + @Override + public ConnectionInfo parse() { + URLLocation location = fetchDatabaseHostsIndexRange(); + if (location == null) { + return new ConnectionInfo( + ComponentsDefine.DB2_JDBC_DRIVER, DB_TYPE, DEFAULT_HOST, DEFAULT_PORT, + fetchDatabaseNameFromURL(JDBC_PREFIX.length()) + ); + } + String hosts = url.substring(location.startIndex(), location.endIndex()); + String[] hostSegment = hosts.split(","); + if (hostSegment.length > 1) { + StringBuilder sb = new StringBuilder(); + for (String host : hostSegment) { + if (host.split(":").length == 1) { + sb.append(host).append(":").append(DEFAULT_PORT).append(","); + } else { + sb.append(host).append(","); + } + } + return new ConnectionInfo( + ComponentsDefine.DB2_JDBC_DRIVER, DB_TYPE, sb.substring(0, sb.length() - 1), + fetchDatabaseNameFromURL() + ); + } else { + String[] hostAndPort = hostSegment[0].split(":"); + if (hostAndPort.length != 1) { + return new ConnectionInfo( + ComponentsDefine.DB2_JDBC_DRIVER, DB_TYPE, hostAndPort[0], Integer.valueOf(hostAndPort[1]), + fetchDatabaseNameFromURL(location + .endIndex()) + ); + } else { + return new ConnectionInfo( + ComponentsDefine.DB2_JDBC_DRIVER, DB_TYPE, hostAndPort[0], DEFAULT_PORT, + fetchDatabaseNameFromURL(location + .endIndex()) + ); + } + } + } +} diff --git a/apm-sniffer/apm-sdk-plugin/jdbc-commons/src/main/java/org/apache/skywalking/apm/plugin/jdbc/connectionurl/parser/DerbyURLParser.java b/apm-sniffer/apm-sdk-plugin/jdbc-commons/src/main/java/org/apache/skywalking/apm/plugin/jdbc/connectionurl/parser/DerbyURLParser.java new file mode 100644 index 0000000000..dd96b8e06a --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/jdbc-commons/src/main/java/org/apache/skywalking/apm/plugin/jdbc/connectionurl/parser/DerbyURLParser.java @@ -0,0 +1,197 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.jdbc.connectionurl.parser; + +import org.apache.skywalking.apm.network.trace.component.ComponentsDefine; +import org.apache.skywalking.apm.plugin.jdbc.trace.ConnectionInfo; + +public class DerbyURLParser extends AbstractURLParser { + + private static final String DEFAULT_HOST = "localhost"; + private static final int DEFAULT_PORT = 1527; + private static final String DB_TYPE = "Derby"; + private static final String DERBY_JDBC_URL_PREFIX = "jdbc:derby"; + /** + * Flag that running with directory mode. + */ + private static final String DIRECTORY_MODE_FLAG = "derby:directory"; + /** + * Flag that running with memory mode. + */ + private static final String MEMORY_MODE_FLAG = "derby:memory"; + /** + * Flag that running with classpath mode. + */ + private static final String CLASSPATH_MODE_FLAG = "derby:classpath"; + /** + * Flag that running with jar mode. + */ + private static final String JAR_MODE_FLAG = "derby:jar"; + + public DerbyURLParser(String url) { + super(url); + } + + /** + * Fetch range index that the database name from connection url if Derby database running in client/server + * environment. eg: jdbc:derby://host[:port]/[databaseName][;attribute=value]* + * + * @return range index that the database name. + */ + @Override + protected URLLocation fetchDatabaseHostsIndexRange() { + int hostLabelStartIndex = url.indexOf("//"); + int hostLabelEndIndex = url.indexOf("/", hostLabelStartIndex + 2); + return new URLLocation(hostLabelStartIndex + 2, hostLabelEndIndex); + } + + @Override + protected URLLocation fetchDatabaseNameIndexRange() { + int databaseEndTag = url.indexOf(";"); + if (databaseEndTag == -1) { + databaseEndTag = url.length(); + } + int databaseStartTag = url.lastIndexOf("\\"); + if (databaseStartTag == -1) { + databaseStartTag = url.lastIndexOf("/"); + } + if (url.indexOf(":", databaseStartTag) != -1) { + databaseStartTag = url.indexOf(":", databaseStartTag); + } + return new URLLocation(databaseStartTag + 1, databaseEndTag); + } + + @Override + public ConnectionInfo parse() { + int[] databaseNameRangeIndex = fetchDatabaseNameRangeIndexForSubProtocol(DIRECTORY_MODE_FLAG); + if (databaseNameRangeIndex != null) { + return defaultConnection(databaseNameRangeIndex); + } + databaseNameRangeIndex = fetchDatabaseNameRangeIndexForSubProtocol(MEMORY_MODE_FLAG); + if (databaseNameRangeIndex != null) { + return defaultConnection(databaseNameRangeIndex); + } + databaseNameRangeIndex = fetchDatabaseNameRangeIndexForSubProtocol(CLASSPATH_MODE_FLAG); + if (databaseNameRangeIndex != null) { + return defaultConnection(databaseNameRangeIndex); + } + databaseNameRangeIndex = fetchDatabaseNameRangeIndexForSubProtocol(JAR_MODE_FLAG); + if (databaseNameRangeIndex != null) { + return defaultConnection(databaseNameRangeIndex); + } + databaseNameRangeIndex = fetchDatabaseNameRangeIndexWithoutHosts(); + if (databaseNameRangeIndex != null) { + return defaultConnection(databaseNameRangeIndex); + } + String[] hostAndPort = fetchDatabaseHostsFromURL().split(":"); + if (hostAndPort.length == 1) { + return new ConnectionInfo( + ComponentsDefine.DERBY_JDBC_DRIVER, DB_TYPE, hostAndPort[0], DEFAULT_PORT, fetchDatabaseNameFromURL()); + } else { + return new ConnectionInfo( + ComponentsDefine.DERBY_JDBC_DRIVER, DB_TYPE, hostAndPort[0], Integer.valueOf(hostAndPort[1]), + fetchDatabaseNameFromURL() + ); + } + } + + /** + * Fetch range index that the database name from connection url if Derby database running in embedded environment. + * eg: jdbc:derby:[databaseName][;attribute=value]* + * + * @return range index that the database name. + */ + private int[] fetchDatabaseNameRangeIndexWithoutHosts() { + if (url.contains("//")) { + return null; + } + int fileLabelIndex = url.indexOf(DERBY_JDBC_URL_PREFIX); + int parameterLabelIndex = url.indexOf(";"); + if (parameterLabelIndex == -1) { + parameterLabelIndex = url.length(); + } + + if (fileLabelIndex != -1) { + int pathLabelIndexForLinux = url.lastIndexOf("/"); + if (pathLabelIndexForLinux != -1 && pathLabelIndexForLinux > fileLabelIndex) { + return new int[] { + pathLabelIndexForLinux + 1, + parameterLabelIndex + }; + } + int pathLabelIndexForWin = url.lastIndexOf("\\"); + if (pathLabelIndexForWin != -1 && pathLabelIndexForWin > fileLabelIndex) { + return new int[] { + pathLabelIndexForWin + 1, + parameterLabelIndex + }; + } + return new int[] { + fileLabelIndex + DERBY_JDBC_URL_PREFIX.length() + 1, + parameterLabelIndex + }; + } else { + return null; + } + } + + /** + * Fetch range index that the database name from connection url if Derby database running with subprotocol. eg: + * jdbc:derby:subprotocol:[databaseName][;attribute=value]* + * + * @return range index that the database name. + */ + private int[] fetchDatabaseNameRangeIndexForSubProtocol(String mode) { + int fileLabelIndex = url.indexOf(mode); + int parameterLabelIndex = url.indexOf(";", fileLabelIndex); + if (parameterLabelIndex == -1) { + parameterLabelIndex = url.length(); + } + + if (fileLabelIndex != -1) { + int pathLabelIndexForLinux = url.lastIndexOf("/"); + if (pathLabelIndexForLinux != -1 && pathLabelIndexForLinux > fileLabelIndex) { + return new int[] { + pathLabelIndexForLinux + 1, + parameterLabelIndex + }; + } + int pathLabelIndexForWin = url.lastIndexOf("\\"); + if (pathLabelIndexForWin != -1 && pathLabelIndexForWin > fileLabelIndex) { + return new int[] { + pathLabelIndexForWin + 1, + parameterLabelIndex + }; + } + return new int[] { + fileLabelIndex + mode.length() + 1, + parameterLabelIndex + }; + } else { + return null; + } + } + + private ConnectionInfo defaultConnection(int[] databaseNameRangeIndex) { + return new ConnectionInfo( + ComponentsDefine.DERBY_JDBC_DRIVER, DB_TYPE, DEFAULT_HOST, -1, + fetchDatabaseNameFromURL(databaseNameRangeIndex) + ); + } +} diff --git a/apm-sniffer/apm-sdk-plugin/jdbc-commons/src/main/java/org/apache/skywalking/apm/plugin/jdbc/connectionurl/parser/OceanBaseURLParser.java b/apm-sniffer/apm-sdk-plugin/jdbc-commons/src/main/java/org/apache/skywalking/apm/plugin/jdbc/connectionurl/parser/OceanBaseURLParser.java new file mode 100644 index 0000000000..f459c268e8 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/jdbc-commons/src/main/java/org/apache/skywalking/apm/plugin/jdbc/connectionurl/parser/OceanBaseURLParser.java @@ -0,0 +1,27 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.jdbc.connectionurl.parser; + +import org.apache.skywalking.apm.network.trace.component.ComponentsDefine; + +public class OceanBaseURLParser extends MysqlURLParser { + public OceanBaseURLParser(String url) { + super(url, "OceanBase", ComponentsDefine.OCEANBASE_JDBC_DRIVER, 2881); + } +} diff --git a/apm-sniffer/apm-sdk-plugin/jdbc-commons/src/main/java/org/apache/skywalking/apm/plugin/jdbc/connectionurl/parser/SqliteURLParser.java b/apm-sniffer/apm-sdk-plugin/jdbc-commons/src/main/java/org/apache/skywalking/apm/plugin/jdbc/connectionurl/parser/SqliteURLParser.java new file mode 100644 index 0000000000..21f44492b3 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/jdbc-commons/src/main/java/org/apache/skywalking/apm/plugin/jdbc/connectionurl/parser/SqliteURLParser.java @@ -0,0 +1,57 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.jdbc.connectionurl.parser; + +import org.apache.skywalking.apm.network.trace.component.ComponentsDefine; +import org.apache.skywalking.apm.plugin.jdbc.trace.ConnectionInfo; + +public class SqliteURLParser extends AbstractURLParser { + private static final String DEFAULT_HOST = "localhost"; + private static final int DEFAULT_PORT = -1; + private static final String DB_TYPE = "Sqlite"; + + public SqliteURLParser(String url) { + super(url); + } + + @Override + protected URLLocation fetchDatabaseHostsIndexRange() { + return null; + } + + @Override + protected URLLocation fetchDatabaseNameIndexRange() { + int databaseStartTag = url.lastIndexOf("/"); + if (databaseStartTag == -1) { + databaseStartTag = url.lastIndexOf(":"); + } + int databaseEndTag = url.indexOf("?"); + if (databaseEndTag == -1) { + databaseEndTag = url.length(); + } + + return new URLLocation(databaseStartTag + 1, databaseEndTag); + } + + @Override + public ConnectionInfo parse() { + return new ConnectionInfo( + ComponentsDefine.SQLITE_JDBC_DRIVER, DB_TYPE, DEFAULT_HOST, DEFAULT_PORT, fetchDatabaseNameFromURL()); + } +} diff --git a/apm-sniffer/apm-sdk-plugin/jdbc-commons/src/main/java/org/apache/skywalking/apm/plugin/jdbc/connectionurl/parser/SybaseURLParser.java b/apm-sniffer/apm-sdk-plugin/jdbc-commons/src/main/java/org/apache/skywalking/apm/plugin/jdbc/connectionurl/parser/SybaseURLParser.java new file mode 100644 index 0000000000..dfc8a57519 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/jdbc-commons/src/main/java/org/apache/skywalking/apm/plugin/jdbc/connectionurl/parser/SybaseURLParser.java @@ -0,0 +1,114 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.jdbc.connectionurl.parser; + +import org.apache.skywalking.apm.network.trace.component.ComponentsDefine; +import org.apache.skywalking.apm.plugin.jdbc.trace.ConnectionInfo; + +public class SybaseURLParser extends AbstractURLParser { + private static final int DEFAULT_PORT = 5000; + private static final String DB_TYPE = "Sybase"; + private static final String JDBC_PREFIX = "jdbc:sybase:Tds:"; + + public SybaseURLParser(String url) { + super(url); + } + + @Override + protected URLLocation fetchDatabaseHostsIndexRange() { + int hostLabelStartIndex = JDBC_PREFIX.length(); + int hostLabelEndIndex = url.indexOf("/", hostLabelStartIndex); + int hostLabelEndIndexWithParameter = url.indexOf("?", hostLabelStartIndex); + if (hostLabelEndIndex == -1) { + hostLabelEndIndex = hostLabelEndIndexWithParameter; + } + if (hostLabelEndIndexWithParameter < hostLabelEndIndex && hostLabelEndIndexWithParameter != -1) { + hostLabelEndIndex = hostLabelEndIndexWithParameter; + } + if (hostLabelEndIndex == -1) { + hostLabelEndIndex = url.length(); + } + return new URLLocation(hostLabelStartIndex, hostLabelEndIndex); + } + + protected String fetchDatabaseNameFromURL(int startSize) { + URLLocation hostsLocation = fetchDatabaseNameIndexRange(startSize); + if (hostsLocation == null) { + return ""; + } + return url.substring(hostsLocation.startIndex(), hostsLocation.endIndex()); + } + + protected URLLocation fetchDatabaseNameIndexRange(int startSize) { + int databaseStartTag = url.indexOf("/", startSize); + int parameterStartTag = url.indexOf("?", startSize); + if (parameterStartTag < databaseStartTag && parameterStartTag != -1) { + return null; + } + if (databaseStartTag == -1) { + return null; + } + int databaseEndTag = url.indexOf("?", databaseStartTag); + if (databaseEndTag == -1) { + databaseEndTag = url.length(); + } + return new URLLocation(databaseStartTag + 1, databaseEndTag); + } + + @Override + protected URLLocation fetchDatabaseNameIndexRange() { + int databaseStartTag = url.lastIndexOf("/"); + int databaseEndTag = url.indexOf("?", databaseStartTag); + if (databaseEndTag == -1) { + databaseEndTag = url.length(); + } + return new URLLocation(databaseStartTag + 1, databaseEndTag); + } + + @Override + public ConnectionInfo parse() { + URLLocation location = fetchDatabaseHostsIndexRange(); + String hosts = url.substring(location.startIndex(), location.endIndex()); + String[] hostSegment = hosts.split(","); + if (hostSegment.length > 1) { + StringBuilder sb = new StringBuilder(); + for (String host : hostSegment) { + if (host.split(":").length == 1) { + sb.append(host).append(":").append(DEFAULT_PORT).append(","); + } else { + sb.append(host).append(","); + } + } + return new ConnectionInfo(ComponentsDefine.SYBASE_JDBC_DRIVER, DB_TYPE, sb.substring(0, sb.length() - 1), fetchDatabaseNameFromURL()); + } else { + String[] hostAndPort = hostSegment[0].split(":"); + if (hostAndPort.length != 1) { + return new ConnectionInfo( + ComponentsDefine.SYBASE_JDBC_DRIVER, DB_TYPE, hostAndPort[0], Integer.valueOf(hostAndPort[1]), + fetchDatabaseNameFromURL(location + .endIndex()) + ); + } else { + return new ConnectionInfo( + ComponentsDefine.SYBASE_JDBC_DRIVER, DB_TYPE, hostAndPort[0], DEFAULT_PORT, fetchDatabaseNameFromURL(location + .endIndex())); + } + } + } +} diff --git a/apm-sniffer/apm-sdk-plugin/jdbc-commons/src/main/java/org/apache/skywalking/apm/plugin/jdbc/connectionurl/parser/URLParser.java b/apm-sniffer/apm-sdk-plugin/jdbc-commons/src/main/java/org/apache/skywalking/apm/plugin/jdbc/connectionurl/parser/URLParser.java index ddf2f20fc7..83afcf9efa 100644 --- a/apm-sniffer/apm-sdk-plugin/jdbc-commons/src/main/java/org/apache/skywalking/apm/plugin/jdbc/connectionurl/parser/URLParser.java +++ b/apm-sniffer/apm-sdk-plugin/jdbc-commons/src/main/java/org/apache/skywalking/apm/plugin/jdbc/connectionurl/parser/URLParser.java @@ -33,9 +33,15 @@ public class URLParser { private static final String MARIADB_JDBC_URL_PREFIX = "jdbc:mariadb"; private static final String MSSQL_JTDS_URL_PREFIX = "jdbc:jtds:sqlserver:"; private static final String MSSQL_JDBC_URL_PREFIX = "jdbc:sqlserver:"; - private static final String KYLIN_JDBC_URK_PREFIX = "jdbc:kylin"; - private static final String IMPALA_JDBC_URK_PREFIX = "jdbc:impala"; - private static final String CLICKHOUSE_JDBC_URK_PREFIX = "jdbc:clickhouse"; + private static final String KYLIN_JDBC_URL_PREFIX = "jdbc:kylin"; + private static final String IMPALA_JDBC_URL_PREFIX = "jdbc:impala"; + private static final String CLICKHOUSE_JDBC_URL_PREFIX = "jdbc:clickhouse"; + private static final String DERBY_JDBC_URL_PREFIX = "jdbc:derby:"; + private static final String SQLITE_JDBC_URL_PREFIX = "jdbc:sqlite:"; + private static final String DB2_JDBC_URL_PREFIIX = "jdbc:db2:"; + private static final String SYBASE_JDBC_URL_PREFIX = "jdbc:sybase:tds:"; + private static final String OCEANBASE_JDBC_URL_PREFIX = "jdbc:oceanbase:"; + private static final String DM_JDBC_URL_PREFIX = "jdbc:dm:"; public static ConnectionInfo parser(String url) { ConnectionURLParser parser = null; @@ -54,13 +60,26 @@ public static ConnectionInfo parser(String url) { parser = new MssqlJtdsURLParser(url); } else if (lowerCaseUrl.startsWith(MSSQL_JDBC_URL_PREFIX)) { parser = new MssqlJdbcURLParser(url); - } else if (lowerCaseUrl.startsWith(KYLIN_JDBC_URK_PREFIX)) { + } else if (lowerCaseUrl.startsWith(KYLIN_JDBC_URL_PREFIX)) { parser = new KylinJdbcURLParser(url); - } else if (lowerCaseUrl.startsWith(IMPALA_JDBC_URK_PREFIX)) { + } else if (lowerCaseUrl.startsWith(IMPALA_JDBC_URL_PREFIX)) { parser = new ImpalaJdbcURLParser(url); - } else if (lowerCaseUrl.startsWith(CLICKHOUSE_JDBC_URK_PREFIX)) { + } else if (lowerCaseUrl.startsWith(CLICKHOUSE_JDBC_URL_PREFIX)) { parser = new ClickHouseURLParser(url); + } else if (lowerCaseUrl.startsWith(DERBY_JDBC_URL_PREFIX)) { + parser = new DerbyURLParser(url); + } else if (lowerCaseUrl.startsWith(SQLITE_JDBC_URL_PREFIX)) { + parser = new SqliteURLParser(url); + } else if (lowerCaseUrl.startsWith(DB2_JDBC_URL_PREFIIX)) { + parser = new Db2URLParser(url); + } else if (lowerCaseUrl.startsWith(SYBASE_JDBC_URL_PREFIX)) { + parser = new SybaseURLParser(url); + } else if (lowerCaseUrl.startsWith(OCEANBASE_JDBC_URL_PREFIX)) { + parser = new OceanBaseURLParser(url); + } else if (lowerCaseUrl.startsWith(DM_JDBC_URL_PREFIX)) { + parser = new DMURLParser(url); } + return parser.parse(); } } diff --git a/apm-sniffer/apm-sdk-plugin/jdbc-commons/src/main/java/org/apache/skywalking/apm/plugin/jdbc/trace/CallableStatementTracing.java b/apm-sniffer/apm-sdk-plugin/jdbc-commons/src/main/java/org/apache/skywalking/apm/plugin/jdbc/trace/CallableStatementTracing.java index 58bea0fd22..2a3a1f33b0 100644 --- a/apm-sniffer/apm-sdk-plugin/jdbc-commons/src/main/java/org/apache/skywalking/apm/plugin/jdbc/trace/CallableStatementTracing.java +++ b/apm-sniffer/apm-sdk-plugin/jdbc-commons/src/main/java/org/apache/skywalking/apm/plugin/jdbc/trace/CallableStatementTracing.java @@ -23,6 +23,7 @@ import org.apache.skywalking.apm.agent.core.context.trace.AbstractSpan; import org.apache.skywalking.apm.agent.core.context.trace.SpanLayer; import org.apache.skywalking.apm.agent.core.context.ContextManager; +import org.apache.skywalking.apm.plugin.jdbc.SqlBodyUtil; /** * {@link CallableStatementTracing} create an exit span when the client call the method in the class that extend {@link @@ -38,7 +39,7 @@ public static R execute(java.sql.CallableStatement realStatement, Connection Tags.DB_TYPE.set(span, connectInfo.getDBType()); SpanLayer.asDB(span); Tags.DB_INSTANCE.set(span, connectInfo.getDatabaseName()); - Tags.DB_STATEMENT.set(span, sql); + Tags.DB_STATEMENT.set(span, SqlBodyUtil.limitSqlBodySize(sql)); span.setComponent(connectInfo.getComponent()); return exec.exe(realStatement, sql); } catch (SQLException e) { diff --git a/apm-sniffer/apm-sdk-plugin/jdbc-commons/src/main/java/org/apache/skywalking/apm/plugin/jdbc/trace/PreparedStatementTracing.java b/apm-sniffer/apm-sdk-plugin/jdbc-commons/src/main/java/org/apache/skywalking/apm/plugin/jdbc/trace/PreparedStatementTracing.java index 86989b7b3d..7c70cb65b0 100644 --- a/apm-sniffer/apm-sdk-plugin/jdbc-commons/src/main/java/org/apache/skywalking/apm/plugin/jdbc/trace/PreparedStatementTracing.java +++ b/apm-sniffer/apm-sdk-plugin/jdbc-commons/src/main/java/org/apache/skywalking/apm/plugin/jdbc/trace/PreparedStatementTracing.java @@ -26,6 +26,7 @@ import org.apache.skywalking.apm.agent.core.context.trace.SpanLayer; import org.apache.skywalking.apm.plugin.jdbc.JDBCPluginConfig; import org.apache.skywalking.apm.plugin.jdbc.PreparedStatementParameterBuilder; +import org.apache.skywalking.apm.plugin.jdbc.SqlBodyUtil; import org.apache.skywalking.apm.plugin.jdbc.define.StatementEnhanceInfos; /** @@ -42,7 +43,7 @@ public static R execute(java.sql.PreparedStatement realStatement, Connection try { Tags.DB_TYPE.set(span, connectInfo.getDBType()); Tags.DB_INSTANCE.set(span, connectInfo.getDatabaseName()); - Tags.DB_STATEMENT.set(span, sql); + Tags.DB_STATEMENT.set(span, SqlBodyUtil.limitSqlBodySize(sql)); span.setComponent(connectInfo.getComponent()); SpanLayer.asDB(span); if (JDBCPluginConfig.Plugin.JDBC.TRACE_SQL_PARAMETERS && Objects.nonNull(statementEnhanceInfos)) { diff --git a/apm-sniffer/apm-sdk-plugin/jdbc-commons/src/main/java/org/apache/skywalking/apm/plugin/jdbc/trace/StatementTracing.java b/apm-sniffer/apm-sdk-plugin/jdbc-commons/src/main/java/org/apache/skywalking/apm/plugin/jdbc/trace/StatementTracing.java index c94b777441..cbac44a6fd 100644 --- a/apm-sniffer/apm-sdk-plugin/jdbc-commons/src/main/java/org/apache/skywalking/apm/plugin/jdbc/trace/StatementTracing.java +++ b/apm-sniffer/apm-sdk-plugin/jdbc-commons/src/main/java/org/apache/skywalking/apm/plugin/jdbc/trace/StatementTracing.java @@ -23,6 +23,7 @@ import org.apache.skywalking.apm.agent.core.context.tag.Tags; import org.apache.skywalking.apm.agent.core.context.trace.AbstractSpan; import org.apache.skywalking.apm.agent.core.context.trace.SpanLayer; +import org.apache.skywalking.apm.plugin.jdbc.SqlBodyUtil; /** * {@link PreparedStatementTracing} create an exit span when the client call the method in the class that extend {@link @@ -36,7 +37,7 @@ public static R execute(java.sql.Statement realStatement, ConnectionInfo con .getDatabasePeer()); Tags.DB_TYPE.set(span, connectInfo.getDBType()); Tags.DB_INSTANCE.set(span, connectInfo.getDatabaseName()); - Tags.DB_STATEMENT.set(span, sql); + Tags.DB_STATEMENT.set(span, SqlBodyUtil.limitSqlBodySize(sql)); span.setComponent(connectInfo.getComponent()); SpanLayer.asDB(span); return exec.exe(realStatement, sql); diff --git a/apm-sniffer/apm-sdk-plugin/jdbc-commons/src/test/java/org/apache/skywalking/apm/plugin/jdbc/connectionurl/parser/URLParserTest.java b/apm-sniffer/apm-sdk-plugin/jdbc-commons/src/test/java/org/apache/skywalking/apm/plugin/jdbc/connectionurl/parser/URLParserTest.java index f679f05cdf..a9a38450e0 100644 --- a/apm-sniffer/apm-sdk-plugin/jdbc-commons/src/test/java/org/apache/skywalking/apm/plugin/jdbc/connectionurl/parser/URLParserTest.java +++ b/apm-sniffer/apm-sdk-plugin/jdbc-commons/src/test/java/org/apache/skywalking/apm/plugin/jdbc/connectionurl/parser/URLParserTest.java @@ -27,7 +27,7 @@ public class URLParserTest { @Test public void testParseMysqlJDBCURLWithHost() { - ConnectionInfo connectionInfo = new URLParser().parser("jdbc:mysql//primaryhost/test"); + ConnectionInfo connectionInfo = URLParser.parser("jdbc:mysql//primaryhost/test"); assertThat(connectionInfo.getDBType(), is("Mysql")); assertThat(connectionInfo.getDatabaseName(), is("test")); assertThat(connectionInfo.getDatabasePeer(), is("primaryhost:3306")); @@ -35,7 +35,7 @@ public void testParseMysqlJDBCURLWithHost() { @Test public void testParseMysqlJDBCURLWithoutDB() { - ConnectionInfo connectionInfo = new URLParser().parser("jdbc:mysql//primaryhost?profileSQL=true"); + ConnectionInfo connectionInfo = URLParser.parser("jdbc:mysql//primaryhost?profileSQL=true"); assertThat(connectionInfo.getDBType(), is("Mysql")); assertThat(connectionInfo.getDatabaseName(), is("")); assertThat(connectionInfo.getDatabasePeer(), is("primaryhost:3306")); @@ -43,7 +43,7 @@ public void testParseMysqlJDBCURLWithoutDB() { @Test public void testParseMysqlJDBCURLWithHostAndPort() { - ConnectionInfo connectionInfo = new URLParser().parser("jdbc:mysql//primaryhost:3307/test?profileSQL=true"); + ConnectionInfo connectionInfo = URLParser.parser("jdbc:mysql//primaryhost:3307/test?profileSQL=true"); assertThat(connectionInfo.getDBType(), is("Mysql")); assertThat(connectionInfo.getDatabaseName(), is("test")); assertThat(connectionInfo.getDatabasePeer(), is("primaryhost:3307")); @@ -51,7 +51,7 @@ public void testParseMysqlJDBCURLWithHostAndPort() { @Test public void testParseMysqlJDBCURLWithMultiHost() { - ConnectionInfo connectionInfo = new URLParser().parser("jdbc:mysql//primaryhost:3307,secondaryhost1,secondaryhost2/test?profileSQL=true"); + ConnectionInfo connectionInfo = URLParser.parser("jdbc:mysql//primaryhost:3307,secondaryhost1,secondaryhost2/test?profileSQL=true"); assertThat(connectionInfo.getDBType(), is("Mysql")); assertThat(connectionInfo.getDatabaseName(), is("test")); assertThat(connectionInfo.getDatabasePeer(), is("primaryhost:3307,secondaryhost1:3306,secondaryhost2:3306")); @@ -59,7 +59,7 @@ public void testParseMysqlJDBCURLWithMultiHost() { @Test public void testParseMysqlJDBCURLWitOutDatabase() { - ConnectionInfo connectionInfo = new URLParser().parser("jdbc:mysql//primaryhost:3307?profileSQL=true"); + ConnectionInfo connectionInfo = URLParser.parser("jdbc:mysql//primaryhost:3307?profileSQL=true"); assertThat(connectionInfo.getDBType(), is("Mysql")); assertThat(connectionInfo.getDatabaseName(), is("")); assertThat(connectionInfo.getDatabasePeer(), is("primaryhost:3307")); @@ -67,7 +67,7 @@ public void testParseMysqlJDBCURLWitOutDatabase() { @Test public void testParseMysqlJDBCURLWithConnectorJs() { - ConnectionInfo connectionInfo = new URLParser().parser("jdbc:mysql:replication://master,slave1,slave2,slave3/test"); + ConnectionInfo connectionInfo = URLParser.parser("jdbc:mysql:replication://master,slave1,slave2,slave3/test"); assertThat(connectionInfo.getDBType(), is("Mysql")); assertThat(connectionInfo.getDatabaseName(), is("test")); assertThat(connectionInfo.getDatabasePeer(), is("master:3306,slave1:3306,slave2:3306,slave3:3306")); @@ -75,7 +75,7 @@ public void testParseMysqlJDBCURLWithConnectorJs() { @Test public void testParseOracleJDBCURLWithHost() { - ConnectionInfo connectionInfo = new URLParser().parser("jdbc:oracle:thin:@localhost:orcl"); + ConnectionInfo connectionInfo = URLParser.parser("jdbc:oracle:thin:@localhost:orcl"); assertThat(connectionInfo.getDBType(), is("Oracle")); assertThat(connectionInfo.getDatabaseName(), is("orcl")); assertThat(connectionInfo.getDatabasePeer(), is("localhost:1521")); @@ -83,7 +83,7 @@ public void testParseOracleJDBCURLWithHost() { @Test public void testParseOracleJDBCURLWithHostAndPort() { - ConnectionInfo connectionInfo = new URLParser().parser("jdbc:oracle:thin:@localhost:1522:orcl"); + ConnectionInfo connectionInfo = URLParser.parser("jdbc:oracle:thin:@localhost:1522:orcl"); assertThat(connectionInfo.getDBType(), is("Oracle")); assertThat(connectionInfo.getDatabaseName(), is("orcl")); assertThat(connectionInfo.getDatabasePeer(), is("localhost:1522")); @@ -91,7 +91,7 @@ public void testParseOracleJDBCURLWithHostAndPort() { @Test public void testParseOracleSID() { - ConnectionInfo connectionInfo = new URLParser().parser("jdbc:oracle:thin:@localhost:1522/orcl"); + ConnectionInfo connectionInfo = URLParser.parser("jdbc:oracle:thin:@localhost:1522/orcl"); assertThat(connectionInfo.getDBType(), is("Oracle")); assertThat(connectionInfo.getDatabaseName(), is("orcl")); assertThat(connectionInfo.getDatabasePeer(), is("localhost:1522")); @@ -99,7 +99,7 @@ public void testParseOracleSID() { @Test public void testParseOracleServiceName() { - ConnectionInfo connectionInfo = new URLParser().parser("jdbc:oracle:thin:@//localhost:1531/orcl"); + ConnectionInfo connectionInfo = URLParser.parser("jdbc:oracle:thin:@//localhost:1531/orcl"); assertThat(connectionInfo.getDBType(), is("Oracle")); assertThat(connectionInfo.getDatabaseName(), is("orcl")); assertThat(connectionInfo.getDatabasePeer(), is("localhost:1531")); @@ -107,7 +107,7 @@ public void testParseOracleServiceName() { @Test public void testParseOracleTNSName() { - ConnectionInfo connectionInfo = new URLParser().parser("jdbc:oracle:thin:@(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST= localhost )(PORT= 1521))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=orcl)))"); + ConnectionInfo connectionInfo = URLParser.parser("jdbc:oracle:thin:@(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST= localhost )(PORT= 1521))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=orcl)))"); assertThat(connectionInfo.getDBType(), is("Oracle")); assertThat(connectionInfo.getDatabaseName(), is("orcl")); assertThat(connectionInfo.getDatabasePeer(), is("localhost:1521")); @@ -115,7 +115,7 @@ public void testParseOracleTNSName() { @Test public void testParseOracleLowerTNSName() { - ConnectionInfo connectionInfo = new URLParser().parser("jdbc:oracle:thin:@(description=(address=(protocol=tcp)(host= localhost )(port= 1521))(connect_data=(server=dedicated)(service_name=orcl)))"); + ConnectionInfo connectionInfo = URLParser.parser("jdbc:oracle:thin:@(description=(address=(protocol=tcp)(host= localhost )(port= 1521))(connect_data=(server=dedicated)(service_name=orcl)))"); assertThat(connectionInfo.getDBType(), is("Oracle")); assertThat(connectionInfo.getDatabaseName(), is("orcl")); assertThat(connectionInfo.getDatabasePeer(), is("localhost:1521")); @@ -123,7 +123,7 @@ public void testParseOracleLowerTNSName() { @Test public void testParseOracleTNSNameWithMultiAddress() { - ConnectionInfo connectionInfo = new URLParser().parser("jdbc:oracle:thin:@(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL= TCP)(HOST=hostA)(PORT= 1523 ))(ADDRESS=(PROTOCOL=TCP)(HOST=hostB)(PORT= 1521 )))(SOURCE_ROUTE=yes)(CONNECT_DATA=(SERVICE_NAME=orcl)))"); + ConnectionInfo connectionInfo = URLParser.parser("jdbc:oracle:thin:@(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL= TCP)(HOST=hostA)(PORT= 1523 ))(ADDRESS=(PROTOCOL=TCP)(HOST=hostB)(PORT= 1521 )))(SOURCE_ROUTE=yes)(CONNECT_DATA=(SERVICE_NAME=orcl)))"); assertThat(connectionInfo.getDBType(), is("Oracle")); assertThat(connectionInfo.getDatabaseName(), is("orcl")); assertThat(connectionInfo.getDatabasePeer(), is("hostA:1523,hostB:1521")); @@ -131,7 +131,7 @@ public void testParseOracleTNSNameWithMultiAddress() { @Test public void testParseOracleJDBCURLWithUserNameAndPassword() { - ConnectionInfo connectionInfo = new URLParser().parser("jdbc:oracle:thin:scott/tiger@myhost:1521:orcl"); + ConnectionInfo connectionInfo = URLParser.parser("jdbc:oracle:thin:scott/tiger@myhost:1521:orcl"); assertThat(connectionInfo.getDBType(), is("Oracle")); assertThat(connectionInfo.getDatabaseName(), is("orcl")); assertThat(connectionInfo.getDatabasePeer(), is("myhost:1521")); @@ -139,7 +139,7 @@ public void testParseOracleJDBCURLWithUserNameAndPassword() { @Test public void testParseH2JDBCURLWithEmbedded() { - ConnectionInfo connectionInfo = new URLParser().parser("jdbc:h2:file:/data/sample"); + ConnectionInfo connectionInfo = URLParser.parser("jdbc:h2:file:/data/sample"); assertThat(connectionInfo.getDBType(), is("H2")); assertThat(connectionInfo.getDatabaseName(), is("/data/sample")); assertThat(connectionInfo.getDatabasePeer(), is("localhost:-1")); @@ -147,7 +147,7 @@ public void testParseH2JDBCURLWithEmbedded() { @Test public void testParseH2JDBCURLWithEmbeddedRunningInWindows() { - ConnectionInfo connectionInfo = new URLParser().parser("jdbc:h2:file:C:/data/sample"); + ConnectionInfo connectionInfo = URLParser.parser("jdbc:h2:file:C:/data/sample"); assertThat(connectionInfo.getDBType(), is("H2")); assertThat(connectionInfo.getDatabaseName(), is("C:/data/sample")); assertThat(connectionInfo.getDatabasePeer(), is("localhost:-1")); @@ -155,7 +155,7 @@ public void testParseH2JDBCURLWithEmbeddedRunningInWindows() { @Test public void testParseH2JDBCURLWithMemoryMode() { - ConnectionInfo connectionInfo = new URLParser().parser("jdbc:h2:mem:test_mem"); + ConnectionInfo connectionInfo = URLParser.parser("jdbc:h2:mem:test_mem"); assertThat(connectionInfo.getDBType(), is("H2")); assertThat(connectionInfo.getDatabaseName(), is("test_mem")); assertThat(connectionInfo.getDatabasePeer(), is("localhost:-1")); @@ -163,7 +163,7 @@ public void testParseH2JDBCURLWithMemoryMode() { @Test public void testParseH2JDBCURL() { - ConnectionInfo connectionInfo = new URLParser().parser("jdbc:h2:tcp://localhost:8084/~/sample"); + ConnectionInfo connectionInfo = URLParser.parser("jdbc:h2:tcp://localhost:8084/~/sample"); assertThat(connectionInfo.getDBType(), is("H2")); assertThat(connectionInfo.getDatabaseName(), is("sample")); assertThat(connectionInfo.getDatabasePeer(), is("localhost:8084")); @@ -171,7 +171,7 @@ public void testParseH2JDBCURL() { @Test public void testParseMariadbJDBCURLWithHost() { - ConnectionInfo connectionInfo = new URLParser().parser("jdbc:mariadb//primaryhost/test"); + ConnectionInfo connectionInfo = URLParser.parser("jdbc:mariadb//primaryhost/test"); assertThat(connectionInfo.getDBType(), is("Mariadb")); assertThat(connectionInfo.getDatabaseName(), is("test")); assertThat(connectionInfo.getDatabasePeer(), is("primaryhost:3306")); @@ -179,7 +179,7 @@ public void testParseMariadbJDBCURLWithHost() { @Test public void testParseClickhouseJDBCURL() { - ConnectionInfo connectionInfo = new URLParser().parser("jdbc:clickhouse://localhost:8123/test"); + ConnectionInfo connectionInfo = URLParser.parser("jdbc:clickhouse://localhost:8123/test"); assertThat(connectionInfo.getDBType(), is("ClickHouse")); assertThat(connectionInfo.getDatabaseName(), is("test")); assertThat(connectionInfo.getDatabasePeer(), is("localhost:8123")); @@ -187,7 +187,7 @@ public void testParseClickhouseJDBCURL() { @Test public void testParseImpalaJDBCURL() { - ConnectionInfo connectionInfo = new URLParser().parser("jdbc:impala://localhost:21050/test;AuthMech=3;UID=UserName;PWD=Password"); + ConnectionInfo connectionInfo = URLParser.parser("jdbc:impala://localhost:21050/test;AuthMech=3;UID=UserName;PWD=Password"); assertThat(connectionInfo.getDBType(), is("Impala")); assertThat(connectionInfo.getDatabaseName(), is("test")); assertThat(connectionInfo.getDatabasePeer(), is("localhost:21050")); @@ -195,7 +195,7 @@ public void testParseImpalaJDBCURL() { @Test public void testParseImpalaJDBCURLWithSchema() { - ConnectionInfo connectionInfo = new URLParser().parser("jdbc:impala://localhost:21050/test"); + ConnectionInfo connectionInfo = URLParser.parser("jdbc:impala://localhost:21050/test"); assertThat(connectionInfo.getDBType(), is("Impala")); assertThat(connectionInfo.getDatabaseName(), is("test")); assertThat(connectionInfo.getDatabasePeer(), is("localhost:21050")); @@ -203,14 +203,14 @@ public void testParseImpalaJDBCURLWithSchema() { @Test public void testParseImpalaJDBCURLWithoutSchema() { - ConnectionInfo connectionInfo = new URLParser().parser("jdbc:impala://localhost:21050"); + ConnectionInfo connectionInfo = URLParser.parser("jdbc:impala://localhost:21050"); assertThat(connectionInfo.getDBType(), is("Impala")); assertThat(connectionInfo.getDatabasePeer(), is("localhost:21050")); } @Test public void testParsePostgresqlJDBCURLWithHostAndParams() { - ConnectionInfo connectionInfo = new URLParser().parser("jdbc:postgresql://localhost:5432/testdb?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&characterSetResults=utf8&useSSL=false&allowMultiQueries=true"); + ConnectionInfo connectionInfo = URLParser.parser("jdbc:postgresql://localhost:5432/testdb?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&characterSetResults=utf8&useSSL=false&allowMultiQueries=true"); assertThat(connectionInfo.getDBType(), is("PostgreSQL")); assertThat(connectionInfo.getDatabaseName(), is("testdb")); assertThat(connectionInfo.getDatabasePeer(), is("localhost:5432")); @@ -218,7 +218,7 @@ public void testParsePostgresqlJDBCURLWithHostAndParams() { @Test public void testParsePostgresqlJDBCURLWithMultiHost() { - ConnectionInfo connectionInfo = new URLParser().parser("jdbc:postgresql://localhost1:5432,localhost2:5433/testdb?target_session_attrs=any&application_name=myapp"); + ConnectionInfo connectionInfo = URLParser.parser("jdbc:postgresql://localhost1:5432,localhost2:5433/testdb?target_session_attrs=any&application_name=myapp"); assertThat(connectionInfo.getDBType(), is("PostgreSQL")); assertThat(connectionInfo.getDatabaseName(), is("testdb")); assertThat(connectionInfo.getDatabasePeer(), is("localhost1:5432,localhost2:5433")); @@ -226,7 +226,7 @@ public void testParsePostgresqlJDBCURLWithMultiHost() { @Test public void testParsePostgresqlJDBCURLWithHostNoPort() { - ConnectionInfo connectionInfo = new URLParser().parser("jdbc:postgresql://localhost/testdb"); + ConnectionInfo connectionInfo = URLParser.parser("jdbc:postgresql://localhost/testdb"); assertThat(connectionInfo.getDBType(), is("PostgreSQL")); assertThat(connectionInfo.getDatabaseName(), is("testdb")); assertThat(connectionInfo.getDatabasePeer(), is("localhost:5432")); @@ -234,7 +234,7 @@ public void testParsePostgresqlJDBCURLWithHostNoPort() { @Test public void testParsePostgresqlJDBCURLWithHostNoDb() { - ConnectionInfo connectionInfo = new URLParser().parser("jdbc:postgresql://localhost:5432"); + ConnectionInfo connectionInfo = URLParser.parser("jdbc:postgresql://localhost:5432"); assertThat(connectionInfo.getDBType(), is("PostgreSQL")); assertThat(connectionInfo.getDatabaseName(), is("")); assertThat(connectionInfo.getDatabasePeer(), is("localhost:5432")); @@ -242,7 +242,7 @@ public void testParsePostgresqlJDBCURLWithHostNoDb() { @Test public void testParsePostgresqlJDBCURLWithHostNoPortAndDb() { - ConnectionInfo connectionInfo = new URLParser().parser("jdbc:postgresql://localhost"); + ConnectionInfo connectionInfo = URLParser.parser("jdbc:postgresql://localhost"); assertThat(connectionInfo.getDBType(), is("PostgreSQL")); assertThat(connectionInfo.getDatabaseName(), is("")); assertThat(connectionInfo.getDatabasePeer(), is("localhost:5432")); @@ -250,7 +250,7 @@ public void testParsePostgresqlJDBCURLWithHostNoPortAndDb() { @Test public void testParsePostgresqlJDBCURLEmpty() { - ConnectionInfo connectionInfo = new URLParser().parser("jdbc:postgresql://"); + ConnectionInfo connectionInfo = URLParser.parser("jdbc:postgresql://"); assertThat(connectionInfo.getDBType(), is("PostgreSQL")); assertThat(connectionInfo.getDatabaseName(), is("")); assertThat(connectionInfo.getDatabasePeer(), is(":5432")); @@ -258,7 +258,7 @@ public void testParsePostgresqlJDBCURLEmpty() { @Test public void testParsePostgresqlJDBCURLWithNamedParams() { - ConnectionInfo connectionInfo = new URLParser().parser("jdbc:postgresql:///testdb?host=localhost&port=5433"); + ConnectionInfo connectionInfo = URLParser.parser("jdbc:postgresql:///testdb?host=localhost&port=5433"); assertThat(connectionInfo.getDBType(), is("PostgreSQL")); assertThat(connectionInfo.getDatabaseName(), is("testdb")); assertThat(connectionInfo.getDatabasePeer(), is("localhost:5433")); @@ -266,7 +266,7 @@ public void testParsePostgresqlJDBCURLWithNamedParams() { @Test public void testParsePostgresqlJDBCURLWithSingleIpv6() { - ConnectionInfo connectionInfo = new URLParser().parser("jdbc:postgresql://[2001:db8::1234]/testdb"); + ConnectionInfo connectionInfo = URLParser.parser("jdbc:postgresql://[2001:db8::1234]/testdb"); assertThat(connectionInfo.getDBType(), is("PostgreSQL")); assertThat(connectionInfo.getDatabaseName(), is("testdb")); assertThat(connectionInfo.getDatabasePeer(), is("[2001:db8::1234]:5432")); @@ -274,10 +274,190 @@ public void testParsePostgresqlJDBCURLWithSingleIpv6() { @Test public void testParsePostgresqlJDBCURLWithMultiIpv6() { - ConnectionInfo connectionInfo = new URLParser().parser( + ConnectionInfo connectionInfo = URLParser.parser( "jdbc:postgresql://[2001:db8::1234],[2001:db8::1235]/testdb"); assertThat(connectionInfo.getDBType(), is("PostgreSQL")); assertThat(connectionInfo.getDatabaseName(), is("testdb")); assertThat(connectionInfo.getDatabasePeer(), is("[2001:db8::1234]:5432,[2001:db8::1235]:5432")); } + + @Test + public void testParseOceanBaseJDBCURL() { + ConnectionInfo connectionInfo = URLParser.parser( + "jdbc:oceanbase://localhost:2881/mydb?user=root@sys&password=pass&pool=false&useBulkStmts=true&rewriteBatchedStatements=false&useServerPrepStmts=true"); + assertThat(connectionInfo.getDBType(), is("OceanBase")); + assertThat(connectionInfo.getDatabaseName(), is("mydb")); + assertThat(connectionInfo.getDatabasePeer(), is("localhost:2881")); + } + + @Test + public void testParseOceanBaseJDBCURLWithMultiHosts() { + ConnectionInfo connectionInfo = URLParser.parser( + "jdbc:oceanbase://primaryhost:2888,secondaryhost1,secondaryhost2/mydb?user=root@sys&password=pass&pool=false&useBulkStmts=true&rewriteBatchedStatements=false&useServerPrepStmts=true"); + assertThat(connectionInfo.getDBType(), is("OceanBase")); + assertThat(connectionInfo.getDatabaseName(), is("mydb")); + assertThat(connectionInfo.getDatabasePeer(), is("primaryhost:2888,secondaryhost1:2881,secondaryhost2:2881")); + } + + @Test + public void testParseDerbyJDBCURLWithDirMode() { + ConnectionInfo connectionInfo = URLParser.parser( + "jdbc:derby:directory:mydb"); + assertThat(connectionInfo.getDBType(), is("Derby")); + assertThat(connectionInfo.getDatabaseName(), is("mydb")); + assertThat(connectionInfo.getDatabasePeer(), is("localhost:-1")); + } + + @Test + public void testParseDerbyJDBCURLWithMemMode() { + ConnectionInfo connectionInfo = URLParser.parser( + "jdbc:derby:memory:mydb;create=true"); + assertThat(connectionInfo.getDBType(), is("Derby")); + assertThat(connectionInfo.getDatabaseName(), is("mydb")); + assertThat(connectionInfo.getDatabasePeer(), is("localhost:-1")); + } + + @Test + public void testParseDerbyJDBCURLWithClassPathMode() { + ConnectionInfo connectionInfo = URLParser.parser( + "jdbc:derby:classpath:/test/mydb"); + assertThat(connectionInfo.getDBType(), is("Derby")); + assertThat(connectionInfo.getDatabaseName(), is("mydb")); + assertThat(connectionInfo.getDatabasePeer(), is("localhost:-1")); + } + + @Test + public void testParseDerbyJDBCURLWithJarMode() { + ConnectionInfo connectionInfo = URLParser.parser( + "jdbc:derby:jar:(C:/dbs.jar)test/mydb"); + assertThat(connectionInfo.getDBType(), is("Derby")); + assertThat(connectionInfo.getDatabaseName(), is("mydb")); + assertThat(connectionInfo.getDatabasePeer(), is("localhost:-1")); + } + + @Test + public void testParseDerbyJDBCURLWithEmbeddedMode() { + ConnectionInfo connectionInfo = URLParser.parser( + "jdbc:derby:test/mydb;create=true"); + assertThat(connectionInfo.getDBType(), is("Derby")); + assertThat(connectionInfo.getDatabaseName(), is("mydb")); + assertThat(connectionInfo.getDatabasePeer(), is("localhost:-1")); + } + + @Test + public void testParseDerbyJDBCURLWithMemModeAndClientServerMode() { + ConnectionInfo connectionInfo = URLParser.parser( + "jdbc:derby://localhost:1527/memory:/test/mydb;create=true"); + assertThat(connectionInfo.getDBType(), is("Derby")); + assertThat(connectionInfo.getDatabaseName(), is("mydb")); + assertThat(connectionInfo.getDatabasePeer(), is("localhost:1527")); + } + + @Test + public void testParseDerbyJDBCURLWithClientServerMode() { + ConnectionInfo connectionInfo = URLParser.parser( + "jdbc:derby://localhost:1527/mydb;create=true;user=root;password=pass"); + assertThat(connectionInfo.getDBType(), is("Derby")); + assertThat(connectionInfo.getDatabaseName(), is("mydb")); + assertThat(connectionInfo.getDatabasePeer(), is("localhost:1527")); + } + + @Test + public void testParseDB2JDBCURL() { + ConnectionInfo connectionInfo = URLParser.parser( + "jdbc:db2://localhost:50000/mydb:user=root;password=pass"); + assertThat(connectionInfo.getDBType(), is("DB2")); + assertThat(connectionInfo.getDatabaseName(), is("mydb")); + assertThat(connectionInfo.getDatabasePeer(), is("localhost:50000")); + } + + @Test + public void testParseDB2JDBCURLWithoutHost() { + ConnectionInfo connectionInfo = URLParser.parser( + "jdbc:db2:mydb:user=root;password=pass"); + assertThat(connectionInfo.getDBType(), is("DB2")); + assertThat(connectionInfo.getDatabaseName(), is("mydb")); + assertThat(connectionInfo.getDatabasePeer(), is("localhost:50000")); + } + + @Test + public void testParseSqliteJDBCURL() { + ConnectionInfo connectionInfo = URLParser.parser( + "jdbc:sqlite:C/test/mydb.db"); + assertThat(connectionInfo.getDBType(), is("Sqlite")); + assertThat(connectionInfo.getDatabaseName(), is("mydb.db")); + assertThat(connectionInfo.getDatabasePeer(), is("localhost:-1")); + } + + @Test + public void testParseSqliteJDBCURLWithMem() { + ConnectionInfo connectionInfo = URLParser.parser( + "jdbc:sqlite::memory:?jdbc.explicit_readonly=true"); + assertThat(connectionInfo.getDBType(), is("Sqlite")); + assertThat(connectionInfo.getDatabaseName(), is("")); + assertThat(connectionInfo.getDatabasePeer(), is("localhost:-1")); + } + + @Test + public void testParseSqliteJDBCURLWithResource() { + ConnectionInfo connectionInfo = URLParser.parser( + "jdbc:sqlite::resource:org/test/mydb.db"); + assertThat(connectionInfo.getDBType(), is("Sqlite")); + assertThat(connectionInfo.getDatabaseName(), is("mydb.db")); + assertThat(connectionInfo.getDatabasePeer(), is("localhost:-1")); + } + + @Test + public void testParseSybaseJDBCURL() { + ConnectionInfo connectionInfo = URLParser.parser( + "jdbc:sybase:Tds:localhost:5000/mydb?charset=utf-8"); + assertThat(connectionInfo.getDBType(), is("Sybase")); + assertThat(connectionInfo.getDatabaseName(), is("mydb")); + assertThat(connectionInfo.getDatabasePeer(), is("localhost:5000")); + } + + @Test + public void testParseDMJDBCURLWithNamedParams() + { + ConnectionInfo connectionInfo = URLParser.parser("jdbc:dm://?host=localhost&port=5236"); + assertThat(connectionInfo.getDBType(), is("DM")); + assertThat(connectionInfo.getDatabaseName(), is("")); + assertThat(connectionInfo.getDatabasePeer(), is("localhost:5236")); + } + + @Test + public void testParseDMJDBCURLWithNamedParamsAndScheme() + { + ConnectionInfo connectionInfo = URLParser.parser("jdbc:dm://?host=localhost&port=5236&schema=dm"); + assertThat(connectionInfo.getDBType(), is("DM")); + assertThat(connectionInfo.getDatabaseName(), is("dm")); + assertThat(connectionInfo.getDatabasePeer(), is("localhost:5236")); + } + + @Test + public void testParseDMJDBCURLWithoutHost() + { + ConnectionInfo connectionInfo = URLParser.parser("jdbc:dm://localhost"); + assertThat(connectionInfo.getDBType(), is("DM")); + assertThat(connectionInfo.getDatabaseName(), is("")); + assertThat(connectionInfo.getDatabasePeer(), is("localhost:5236")); + } + + @Test + public void testParseDMJDBCURLWithoutHostAndScheme() + { + ConnectionInfo connectionInfo = URLParser.parser("jdbc:dm://localhost?schema=dm"); + assertThat(connectionInfo.getDBType(), is("DM")); + assertThat(connectionInfo.getDatabaseName(), is("dm")); + assertThat(connectionInfo.getDatabasePeer(), is("localhost:5236")); + } + + @Test + public void testParseDMJDBCURLWithoutHostPort() + { + ConnectionInfo connectionInfo = URLParser.parser("jdbc:dm://localhost:5237?schema=dm"); + assertThat(connectionInfo.getDBType(), is("DM")); + assertThat(connectionInfo.getDatabaseName(), is("dm")); + assertThat(connectionInfo.getDatabasePeer(), is("localhost:5237")); + } } diff --git a/apm-sniffer/apm-sdk-plugin/jedis-plugins/jedis-2.x-3.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/jedis-plugins/jedis-2.x-3.x-plugin/pom.xml index b4d2e02e57..53f72f82af 100644 --- a/apm-sniffer/apm-sdk-plugin/jedis-plugins/jedis-2.x-3.x-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/jedis-plugins/jedis-2.x-3.x-plugin/pom.xml @@ -21,7 +21,7 @@ org.apache.skywalking jedis-plugins - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-sdk-plugin/jedis-plugins/jedis-4.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/jedis-plugins/jedis-4.x-plugin/pom.xml index cbd586387d..4593b1c654 100644 --- a/apm-sniffer/apm-sdk-plugin/jedis-plugins/jedis-4.x-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/jedis-plugins/jedis-4.x-plugin/pom.xml @@ -21,7 +21,7 @@ jedis-plugins org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-sdk-plugin/jedis-plugins/jedis-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jedis/v4/AbstractConnectionInterceptor.java b/apm-sniffer/apm-sdk-plugin/jedis-plugins/jedis-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jedis/v4/AbstractConnectionInterceptor.java index 79f2f15858..a1a8a16225 100644 --- a/apm-sniffer/apm-sdk-plugin/jedis-plugins/jedis-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jedis/v4/AbstractConnectionInterceptor.java +++ b/apm-sniffer/apm-sdk-plugin/jedis-plugins/jedis-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jedis/v4/AbstractConnectionInterceptor.java @@ -18,6 +18,7 @@ package org.apache.skywalking.apm.plugin.jedis.v4; import org.apache.skywalking.apm.agent.core.context.ContextManager; +import org.apache.skywalking.apm.agent.core.context.tag.StringTag; import org.apache.skywalking.apm.agent.core.context.tag.Tags; import org.apache.skywalking.apm.agent.core.context.trace.AbstractSpan; import org.apache.skywalking.apm.agent.core.context.trace.SpanLayer; @@ -39,6 +40,8 @@ public abstract class AbstractConnectionInterceptor implements InstanceMethodsAr private static final String CACHE_TYPE = "Redis"; + private static final StringTag TAG_ARGS = new StringTag("actual_target"); + @Override public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, MethodInterceptResult result) throws Throwable { Iterator iterator = getCommands(allArguments); @@ -49,12 +52,15 @@ public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allAr // Use lowercase to make config compatible with jedis-2.x-3.x plugin // Refer to `plugin.jedis.operation_mapping_read`, `plugin.jedis.operation_mapping_write` config item in agent.config String cmd = protocolCommand == null ? UNKNOWN : protocolCommand.toLowerCase(); - String peer = String.valueOf(objInst.getSkyWalkingDynamicField()); + ConnectionInformation connectionData = (ConnectionInformation) objInst.getSkyWalkingDynamicField(); + // Use cluster information to adapt Virtual Cache if exists, otherwise use real server host + String peer = StringUtil.isBlank(connectionData.getClusterNodes()) ? connectionData.getActualTarget() : connectionData.getClusterNodes(); AbstractSpan span = ContextManager.createExitSpan("Jedis/" + cmd, peer); span.setComponent(ComponentsDefine.JEDIS); readKeyIfNecessary(iterator).ifPresent(key -> Tags.CACHE_KEY.set(span, key)); Tags.CACHE_CMD.set(span, cmd); Tags.CACHE_TYPE.set(span, CACHE_TYPE); + TAG_ARGS.set(span, connectionData.getActualTarget()); parseOperation(cmd).ifPresent(op -> Tags.CACHE_OP.set(span, op)); SpanLayer.asCache(span); } diff --git a/apm-sniffer/apm-sdk-plugin/jedis-plugins/jedis-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jedis/v4/ConnectionConstructorInterceptor.java b/apm-sniffer/apm-sdk-plugin/jedis-plugins/jedis-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jedis/v4/ConnectionConstructorInterceptor.java index 825042613d..550db0072d 100644 --- a/apm-sniffer/apm-sdk-plugin/jedis-plugins/jedis-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jedis/v4/ConnectionConstructorInterceptor.java +++ b/apm-sniffer/apm-sdk-plugin/jedis-plugins/jedis-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jedis/v4/ConnectionConstructorInterceptor.java @@ -26,6 +26,8 @@ public class ConnectionConstructorInterceptor implements InstanceConstructorInte @Override public void onConstruct(EnhancedInstance objInst, Object[] allArguments) throws Throwable { HostAndPort hostAndPort = ((DefaultJedisSocketFactory) allArguments[0]).getHostAndPort(); - objInst.setSkyWalkingDynamicField(hostAndPort.getHost() + ":" + hostAndPort.getPort()); + ConnectionInformation connectionData = new ConnectionInformation(); + connectionData.setActualTarget(hostAndPort.toString()); + objInst.setSkyWalkingDynamicField(connectionData); } } diff --git a/apm-sniffer/apm-sdk-plugin/jedis-plugins/jedis-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jedis/v4/ConnectionInformation.java b/apm-sniffer/apm-sdk-plugin/jedis-plugins/jedis-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jedis/v4/ConnectionInformation.java new file mode 100644 index 0000000000..002deff4fc --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/jedis-plugins/jedis-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jedis/v4/ConnectionInformation.java @@ -0,0 +1,26 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.skywalking.apm.plugin.jedis.v4; + +import lombok.Data; + +@Data +public class ConnectionInformation { + private String clusterNodes; + private String actualTarget; +} diff --git a/apm-sniffer/apm-sdk-plugin/jedis-plugins/jedis-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jedis/v4/ConnectionProviderConstructorInterceptor.java b/apm-sniffer/apm-sdk-plugin/jedis-plugins/jedis-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jedis/v4/ConnectionProviderConstructorInterceptor.java new file mode 100644 index 0000000000..21939a62e9 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/jedis-plugins/jedis-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jedis/v4/ConnectionProviderConstructorInterceptor.java @@ -0,0 +1,43 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.skywalking.apm.plugin.jedis.v4; + +import java.util.Collection; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceConstructorInterceptor; +import org.apache.skywalking.apm.util.StringUtil; +import redis.clients.jedis.HostAndPort; + +public class ConnectionProviderConstructorInterceptor implements InstanceConstructorInterceptor { + @Override + public void onConstruct(final EnhancedInstance objInst, final Object[] allArguments) throws Throwable { + if (objInst.getSkyWalkingDynamicField() != null) { + return; + } + Object arg = allArguments[0]; + if (arg instanceof Collection) { + Collection collection = (Collection) arg; + final String[] array = collection.stream().map(Object::toString).toArray(String[]::new); + objInst.setSkyWalkingDynamicField(StringUtil.join(',', array)); + } + if (arg instanceof HostAndPort) { + objInst.setSkyWalkingDynamicField(arg.toString()); + } + } + +} diff --git a/apm-sniffer/apm-sdk-plugin/jedis-plugins/jedis-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jedis/v4/ConnectionProviderGetConnectionInterceptor.java b/apm-sniffer/apm-sdk-plugin/jedis-plugins/jedis-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jedis/v4/ConnectionProviderGetConnectionInterceptor.java new file mode 100644 index 0000000000..12409de61d --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/jedis-plugins/jedis-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jedis/v4/ConnectionProviderGetConnectionInterceptor.java @@ -0,0 +1,60 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.skywalking.apm.plugin.jedis.v4; + +import java.lang.reflect.Method; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceMethodsAroundInterceptor; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult; + +public class ConnectionProviderGetConnectionInterceptor implements InstanceMethodsAroundInterceptor { + @Override + public void beforeMethod(final EnhancedInstance objInst, + final Method method, + final Object[] allArguments, + final Class[] argumentsTypes, + final MethodInterceptResult result) throws Throwable { + + } + + @Override + public Object afterMethod(final EnhancedInstance objInst, + final Method method, + final Object[] allArguments, + final Class[] argumentsTypes, + final Object ret) throws Throwable { + if (ret instanceof EnhancedInstance) { + EnhancedInstance connection = (EnhancedInstance) ret; + if (connection.getSkyWalkingDynamicField() != null + && connection.getSkyWalkingDynamicField() instanceof ConnectionInformation) { + ((ConnectionInformation) connection.getSkyWalkingDynamicField()).setClusterNodes( + (String) objInst.getSkyWalkingDynamicField()); + } + } + return ret; + } + + @Override + public void handleMethodException(final EnhancedInstance objInst, + final Method method, + final Object[] allArguments, + final Class[] argumentsTypes, + final Throwable t) { + + } +} diff --git a/apm-sniffer/apm-sdk-plugin/jedis-plugins/jedis-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jedis/v4/JedisMethodInterceptor.java b/apm-sniffer/apm-sdk-plugin/jedis-plugins/jedis-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jedis/v4/JedisMethodInterceptor.java index 26175616e0..dcf181b3e5 100644 --- a/apm-sniffer/apm-sdk-plugin/jedis-plugins/jedis-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jedis/v4/JedisMethodInterceptor.java +++ b/apm-sniffer/apm-sdk-plugin/jedis-plugins/jedis-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jedis/v4/JedisMethodInterceptor.java @@ -18,6 +18,7 @@ package org.apache.skywalking.apm.plugin.jedis.v4; import org.apache.skywalking.apm.agent.core.context.ContextManager; +import org.apache.skywalking.apm.agent.core.context.tag.StringTag; import org.apache.skywalking.apm.agent.core.context.tag.Tags; import org.apache.skywalking.apm.agent.core.context.trace.AbstractSpan; import org.apache.skywalking.apm.agent.core.context.trace.SpanLayer; @@ -27,18 +28,23 @@ import org.apache.skywalking.apm.network.trace.component.ComponentsDefine; import java.lang.reflect.Method; +import org.apache.skywalking.apm.util.StringUtil; public class JedisMethodInterceptor implements InstanceMethodsAroundInterceptor { + private static final StringTag TAG_ARGS = new StringTag("actual_target"); @Override public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, MethodInterceptResult result) throws Throwable { - String peer = String.valueOf(objInst.getSkyWalkingDynamicField()); + final ConnectionInformation connectionData = (ConnectionInformation) objInst.getSkyWalkingDynamicField(); + // Use cluster information to adapt Virtual Cache if exists, otherwise use real server host + String peer = StringUtil.isBlank(connectionData.getClusterNodes()) ? connectionData.getActualTarget() : connectionData.getClusterNodes(); AbstractSpan span = ContextManager.createExitSpan("Jedis/" + method.getName(), peer); span.setComponent(ComponentsDefine.JEDIS); SpanLayer.asCache(span); Tags.CACHE_TYPE.set(span, "Redis"); Tags.CACHE_CMD.set(span, "BATCH_EXECUTE"); + TAG_ARGS.set(span, connectionData.getActualTarget()); } @Override diff --git a/apm-sniffer/apm-sdk-plugin/jedis-plugins/jedis-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jedis/v4/define/ConnectionProviderInstrumentation.java b/apm-sniffer/apm-sdk-plugin/jedis-plugins/jedis-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jedis/v4/define/ConnectionProviderInstrumentation.java new file mode 100644 index 0000000000..d376fb3d6a --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/jedis-plugins/jedis-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jedis/v4/define/ConnectionProviderInstrumentation.java @@ -0,0 +1,81 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.skywalking.apm.plugin.jedis.v4.define; + +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.matcher.ElementMatcher; +import net.bytebuddy.matcher.ElementMatchers; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch; + +import static net.bytebuddy.matcher.ElementMatchers.hasSuperType; +import static net.bytebuddy.matcher.ElementMatchers.named; +import static org.apache.skywalking.apm.agent.core.plugin.match.HierarchyMatch.byHierarchyMatch; + +public class ConnectionProviderInstrumentation extends AbstractWitnessInstrumentation { + + private static final String ENHANCE_INTERFACE = "redis.clients.jedis.providers.ConnectionProvider"; + private static final String CONNECTION_PROVIDER_CONSTRUCTION_INTERCEPT_CLASS = "org.apache.skywalking.apm.plugin.jedis.v4.ConnectionProviderConstructorInterceptor"; + private static final String CONNECTION_PROVIDER_GET_CONNECTION_INTERCEPT_CLASS = "org.apache.skywalking.apm.plugin.jedis.v4.ConnectionProviderGetConnectionInterceptor"; + + @Override + protected ClassMatch enhanceClass() { + return byHierarchyMatch(ENHANCE_INTERFACE); + } + + @Override + public ConstructorInterceptPoint[] getConstructorsInterceptPoints() { + return new ConstructorInterceptPoint[]{ + new ConstructorInterceptPoint() { + @Override + public ElementMatcher getConstructorMatcher() { + return ElementMatchers.takesArgument(0, named("redis.clients.jedis.HostAndPort")) + .or(ElementMatchers.takesArgument(0, hasSuperType(named("java.util.Collection")))); + } + + @Override + public String getConstructorInterceptor() { + return CONNECTION_PROVIDER_CONSTRUCTION_INTERCEPT_CLASS; + } + } + }; + } + + @Override + public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() { + return new InstanceMethodsInterceptPoint[] { + new InstanceMethodsInterceptPoint() { + @Override + public ElementMatcher getMethodsMatcher() { + return named("getConnection"); + } + + @Override + public String getMethodsInterceptor() { + return CONNECTION_PROVIDER_GET_CONNECTION_INTERCEPT_CLASS; + } + + @Override + public boolean isOverrideArgs() { + return false; + } + } + }; + } +} diff --git a/apm-sniffer/apm-sdk-plugin/jedis-plugins/jedis-4.x-plugin/src/main/resources/skywalking-plugin.def b/apm-sniffer/apm-sdk-plugin/jedis-plugins/jedis-4.x-plugin/src/main/resources/skywalking-plugin.def index a226a135b5..ae23cce498 100644 --- a/apm-sniffer/apm-sdk-plugin/jedis-plugins/jedis-4.x-plugin/src/main/resources/skywalking-plugin.def +++ b/apm-sniffer/apm-sdk-plugin/jedis-plugins/jedis-4.x-plugin/src/main/resources/skywalking-plugin.def @@ -16,4 +16,5 @@ jedis-4.x=org.apache.skywalking.apm.plugin.jedis.v4.define.ConnectionInstrumentation jedis-4.x=org.apache.skywalking.apm.plugin.jedis.v4.define.PipelineInstrumentation -jedis-4.x=org.apache.skywalking.apm.plugin.jedis.v4.define.TransactionConstructorInstrumentation \ No newline at end of file +jedis-4.x=org.apache.skywalking.apm.plugin.jedis.v4.define.TransactionConstructorInstrumentation +jedis-4.x=org.apache.skywalking.apm.plugin.jedis.v4.define.ConnectionProviderInstrumentation diff --git a/apm-sniffer/apm-sdk-plugin/jedis-plugins/pom.xml b/apm-sniffer/apm-sdk-plugin/jedis-plugins/pom.xml index 3180436f6d..a337f0c2f8 100644 --- a/apm-sniffer/apm-sdk-plugin/jedis-plugins/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/jedis-plugins/pom.xml @@ -20,14 +20,13 @@ org.apache.skywalking apm-sdk-plugin - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 8 8 UTF-8 - /.. 4.0.0 diff --git a/apm-sniffer/apm-sdk-plugin/jersey-2.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/jersey-2.x-plugin/pom.xml index 225c6a178c..f3a8001daf 100644 --- a/apm-sniffer/apm-sdk-plugin/jersey-2.x-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/jersey-2.x-plugin/pom.xml @@ -21,7 +21,7 @@ apm-sdk-plugin org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-sdk-plugin/jersey-3.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/jersey-3.x-plugin/pom.xml index d5e06143e0..94fd991b19 100644 --- a/apm-sniffer/apm-sdk-plugin/jersey-3.x-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/jersey-3.x-plugin/pom.xml @@ -21,7 +21,7 @@ apm-sdk-plugin org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-sdk-plugin/jetty-plugin/jetty-client-11.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/jetty-plugin/jetty-client-11.x-plugin/pom.xml index 08751914d3..d800a09ddd 100644 --- a/apm-sniffer/apm-sdk-plugin/jetty-plugin/jetty-client-11.x-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/jetty-plugin/jetty-client-11.x-plugin/pom.xml @@ -20,7 +20,7 @@ jetty-plugins org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-sdk-plugin/jetty-plugin/jetty-client-9.0-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/jetty-plugin/jetty-client-9.0-plugin/pom.xml index 7df69012f8..be26052758 100644 --- a/apm-sniffer/apm-sdk-plugin/jetty-plugin/jetty-client-9.0-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/jetty-plugin/jetty-client-9.0-plugin/pom.xml @@ -20,7 +20,7 @@ jetty-plugins org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-sdk-plugin/jetty-plugin/jetty-client-9.0-plugin/src/main/java/org/apache/skywalking/apm/plugin/jetty/v90/client/AsyncHttpRequestSendInterceptor.java b/apm-sniffer/apm-sdk-plugin/jetty-plugin/jetty-client-9.0-plugin/src/main/java/org/apache/skywalking/apm/plugin/jetty/v90/client/AsyncHttpRequestSendInterceptor.java index 669b43ffda..2858afce44 100644 --- a/apm-sniffer/apm-sdk-plugin/jetty-plugin/jetty-client-9.0-plugin/src/main/java/org/apache/skywalking/apm/plugin/jetty/v90/client/AsyncHttpRequestSendInterceptor.java +++ b/apm-sniffer/apm-sdk-plugin/jetty-plugin/jetty-client-9.0-plugin/src/main/java/org/apache/skywalking/apm/plugin/jetty/v90/client/AsyncHttpRequestSendInterceptor.java @@ -58,8 +58,13 @@ public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allAr span.prepareForAsync(); request.attribute(Constants.SW_JETTY_EXIT_SPAN_KEY, span); - Response.CompleteListener callback = (Response.CompleteListener) allArguments[0]; - allArguments[0] = new CompleteListenerWrapper(callback, ContextManager.capture()); + if (allArguments[0] instanceof Response.Listener) { + Response.Listener listener = (Response.Listener) allArguments[0]; + allArguments[0] = new ResponseListenerWrapper(listener, ContextManager.capture()); + } else { + Response.CompleteListener listener = (Response.CompleteListener) allArguments[0]; + allArguments[0] = new CompleteListenerWrapper(listener, ContextManager.capture()); + } } @Override diff --git a/apm-sniffer/apm-sdk-plugin/jetty-plugin/jetty-client-9.0-plugin/src/main/java/org/apache/skywalking/apm/plugin/jetty/v90/client/CompleteListenerWrapper.java b/apm-sniffer/apm-sdk-plugin/jetty-plugin/jetty-client-9.0-plugin/src/main/java/org/apache/skywalking/apm/plugin/jetty/v90/client/CompleteListenerWrapper.java index 0318bd4c00..294b32fe7d 100644 --- a/apm-sniffer/apm-sdk-plugin/jetty-plugin/jetty-client-9.0-plugin/src/main/java/org/apache/skywalking/apm/plugin/jetty/v90/client/CompleteListenerWrapper.java +++ b/apm-sniffer/apm-sdk-plugin/jetty-plugin/jetty-client-9.0-plugin/src/main/java/org/apache/skywalking/apm/plugin/jetty/v90/client/CompleteListenerWrapper.java @@ -27,11 +27,11 @@ import org.eclipse.jetty.client.api.Result; public class CompleteListenerWrapper implements Response.CompleteListener { - private Response.CompleteListener callback; + private Response.CompleteListener listener; private ContextSnapshot context; - public CompleteListenerWrapper(Response.CompleteListener callback, ContextSnapshot context) { - this.callback = callback; + public CompleteListenerWrapper(Response.CompleteListener listener, ContextSnapshot context) { + this.listener = listener; this.context = context; } @@ -43,9 +43,9 @@ public void onComplete(Result result) { if (context != null) { ContextManager.continued(context); } - if (callback != null) { - callback.onComplete(result); + if (listener != null) { + listener.onComplete(result); } ContextManager.stopSpan(); } -} +} \ No newline at end of file diff --git a/apm-sniffer/apm-sdk-plugin/jetty-plugin/jetty-client-9.0-plugin/src/main/java/org/apache/skywalking/apm/plugin/jetty/v90/client/ResponseListenerWrapper.java b/apm-sniffer/apm-sdk-plugin/jetty-plugin/jetty-client-9.0-plugin/src/main/java/org/apache/skywalking/apm/plugin/jetty/v90/client/ResponseListenerWrapper.java new file mode 100644 index 0000000000..cf3e097dda --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/jetty-plugin/jetty-client-9.0-plugin/src/main/java/org/apache/skywalking/apm/plugin/jetty/v90/client/ResponseListenerWrapper.java @@ -0,0 +1,85 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.jetty.v90.client; + +import org.apache.skywalking.apm.agent.core.context.ContextManager; +import org.apache.skywalking.apm.agent.core.context.ContextSnapshot; +import org.apache.skywalking.apm.agent.core.context.trace.AbstractSpan; +import org.apache.skywalking.apm.agent.core.context.trace.SpanLayer; +import org.apache.skywalking.apm.network.trace.component.ComponentsDefine; +import org.eclipse.jetty.client.api.Response; +import org.eclipse.jetty.client.api.Result; +import org.eclipse.jetty.http.HttpField; +import java.nio.ByteBuffer; + +public class ResponseListenerWrapper implements Response.Listener { + + private final Response.Listener listener; + + private final ContextSnapshot context; + + public ResponseListenerWrapper(Response.Listener listener, ContextSnapshot context) { + this.listener = listener; + this.context = context; + } + + @Override + public void onComplete(Result result) { + AbstractSpan span = ContextManager.createLocalSpan(Constants.PLUGIN_NAME + "/CompleteListener/onComplete"); + span.setComponent(ComponentsDefine.JETTY_CLIENT); + SpanLayer.asHttp(span); + if (context != null) { + ContextManager.continued(context); + } + if (listener != null) { + listener.onComplete(result); + } + ContextManager.stopSpan(); + } + + @Override + public void onHeaders(Response response) { + listener.onHeaders(response); + } + + @Override + public void onContent(Response response, ByteBuffer content) { + listener.onContent(response, content); + } + + @Override + public void onBegin(Response response) { + listener.onBegin(response); + } + + @Override + public boolean onHeader(Response response, HttpField field) { + return listener.onHeader(response, field); + } + + @Override + public void onSuccess(Response response) { + listener.onSuccess(response); + } + + @Override + public void onFailure(Response response, Throwable failure) { + listener.onFailure(response, failure); + } +} \ No newline at end of file diff --git a/apm-sniffer/apm-sdk-plugin/jetty-plugin/jetty-client-9.0-plugin/src/main/java/org/apache/skywalking/apm/plugin/jetty/v90/client/define/HttpRequestInstrumentation.java b/apm-sniffer/apm-sdk-plugin/jetty-plugin/jetty-client-9.0-plugin/src/main/java/org/apache/skywalking/apm/plugin/jetty/v90/client/define/HttpRequestInstrumentation.java index ef8daf105a..2bee7ecdc1 100644 --- a/apm-sniffer/apm-sdk-plugin/jetty-plugin/jetty-client-9.0-plugin/src/main/java/org/apache/skywalking/apm/plugin/jetty/v90/client/define/HttpRequestInstrumentation.java +++ b/apm-sniffer/apm-sdk-plugin/jetty-plugin/jetty-client-9.0-plugin/src/main/java/org/apache/skywalking/apm/plugin/jetty/v90/client/define/HttpRequestInstrumentation.java @@ -41,7 +41,7 @@ public class HttpRequestInstrumentation extends ClassInstanceMethodsEnhancePlugi private static final String ENHANCE_CLASS = "org.eclipse.jetty.client.HttpRequest"; private static final String ENHANCE_CLASS_NAME = "send"; public static final String SYNC_SEND_INTERCEPTOR = - "org.apache.skywalking.apm.plugin.jetty.v90.client.SyncHttpRequestSendV90Interceptor"; + "org.apache.skywalking.apm.plugin.jetty.v90.client.SyncHttpRequestSendInterceptor"; public static final String ASYNC_SEND_INTERCEPTOR = "org.apache.skywalking.apm.plugin.jetty.v90.client.AsyncHttpRequestSendInterceptor"; @@ -85,7 +85,7 @@ public String getMethodsInterceptor() { @Override public boolean isOverrideArgs() { - return false; + return true; } } }; diff --git a/apm-sniffer/apm-sdk-plugin/jetty-plugin/jetty-client-9.0-plugin/src/main/java/org/apache/skywalking/apm/plugin/jetty/v90/client/define/ResponseNotifierInstrumentation.java b/apm-sniffer/apm-sdk-plugin/jetty-plugin/jetty-client-9.0-plugin/src/main/java/org/apache/skywalking/apm/plugin/jetty/v90/client/define/ResponseNotifierInstrumentation.java index c4f39c4147..5502c8dc78 100644 --- a/apm-sniffer/apm-sdk-plugin/jetty-plugin/jetty-client-9.0-plugin/src/main/java/org/apache/skywalking/apm/plugin/jetty/v90/client/define/ResponseNotifierInstrumentation.java +++ b/apm-sniffer/apm-sdk-plugin/jetty-plugin/jetty-client-9.0-plugin/src/main/java/org/apache/skywalking/apm/plugin/jetty/v90/client/define/ResponseNotifierInstrumentation.java @@ -41,10 +41,10 @@ */ public class ResponseNotifierInstrumentation extends ClassInstanceMethodsEnhancePluginDefine { - private static final String ENHANCE_CLASS = "org.eclipse.jetty.client.tar"; + private static final String ENHANCE_CLASS = "org.eclipse.jetty.client.ResponseNotifier"; private static final String ENHANCE_CLASS_NAME = "notifyComplete"; public static final String SYNC_SEND_INTERCEPTOR = - "org.apache.skywalking.apm.plugin.jetty.v9.client.ResponseNotifierInterceptor"; + "org.apache.skywalking.apm.plugin.jetty.v90.client.ResponseNotifierInterceptor"; @Override public ConstructorInterceptPoint[] getConstructorsInterceptPoints() { diff --git a/apm-sniffer/apm-sdk-plugin/jetty-plugin/jetty-client-9.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/jetty-plugin/jetty-client-9.x-plugin/pom.xml index 906d2d7675..a7f7168743 100644 --- a/apm-sniffer/apm-sdk-plugin/jetty-plugin/jetty-client-9.x-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/jetty-plugin/jetty-client-9.x-plugin/pom.xml @@ -20,7 +20,7 @@ jetty-plugins org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 @@ -31,7 +31,7 @@ http://maven.apache.org - 9.1.0.v20131115 + 9.2.23.v20171218 diff --git a/apm-sniffer/apm-sdk-plugin/jetty-plugin/jetty-client-9.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jetty/v9/client/AsyncHttpRequestSendInterceptor.java b/apm-sniffer/apm-sdk-plugin/jetty-plugin/jetty-client-9.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jetty/v9/client/AsyncHttpRequestSendInterceptor.java index 6f8870af49..6f39452e0c 100644 --- a/apm-sniffer/apm-sdk-plugin/jetty-plugin/jetty-client-9.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jetty/v9/client/AsyncHttpRequestSendInterceptor.java +++ b/apm-sniffer/apm-sdk-plugin/jetty-plugin/jetty-client-9.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jetty/v9/client/AsyncHttpRequestSendInterceptor.java @@ -57,8 +57,13 @@ public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allAr span.prepareForAsync(); request.attribute(Constants.SW_JETTY_EXIT_SPAN_KEY, span); - Response.CompleteListener callback = (Response.CompleteListener) allArguments[0]; - allArguments[0] = new CompleteListenerWrapper(callback, ContextManager.capture()); + if (allArguments[0] instanceof Response.Listener) { + Response.Listener listener = (Response.Listener) allArguments[0]; + allArguments[0] = new ResponseListenerWrapper(listener, ContextManager.capture()); + } else { + Response.CompleteListener listener = (Response.CompleteListener) allArguments[0]; + allArguments[0] = new CompleteListenerWrapper(listener, ContextManager.capture()); + } } @Override diff --git a/apm-sniffer/apm-sdk-plugin/jetty-plugin/jetty-client-9.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jetty/v9/client/CompleteListenerWrapper.java b/apm-sniffer/apm-sdk-plugin/jetty-plugin/jetty-client-9.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jetty/v9/client/CompleteListenerWrapper.java index 50697bf3e4..156e636884 100644 --- a/apm-sniffer/apm-sdk-plugin/jetty-plugin/jetty-client-9.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jetty/v9/client/CompleteListenerWrapper.java +++ b/apm-sniffer/apm-sdk-plugin/jetty-plugin/jetty-client-9.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jetty/v9/client/CompleteListenerWrapper.java @@ -27,11 +27,11 @@ import org.eclipse.jetty.client.api.Result; public class CompleteListenerWrapper implements Response.CompleteListener { - private Response.CompleteListener callback; + private Response.CompleteListener listener; private ContextSnapshot context; - public CompleteListenerWrapper(Response.CompleteListener callback, ContextSnapshot context) { - this.callback = callback; + public CompleteListenerWrapper(Response.CompleteListener listener, ContextSnapshot context) { + this.listener = listener; this.context = context; } @@ -43,9 +43,9 @@ public void onComplete(Result result) { if (context != null) { ContextManager.continued(context); } - if (callback != null) { - callback.onComplete(result); + if (listener != null) { + listener.onComplete(result); } ContextManager.stopSpan(); } -} +} \ No newline at end of file diff --git a/apm-sniffer/apm-sdk-plugin/jetty-plugin/jetty-client-9.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jetty/v9/client/ResponseListenerWrapper.java b/apm-sniffer/apm-sdk-plugin/jetty-plugin/jetty-client-9.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jetty/v9/client/ResponseListenerWrapper.java new file mode 100644 index 0000000000..09102cdc43 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/jetty-plugin/jetty-client-9.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jetty/v9/client/ResponseListenerWrapper.java @@ -0,0 +1,92 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.jetty.v9.client; + +import org.apache.skywalking.apm.agent.core.context.ContextManager; +import org.apache.skywalking.apm.agent.core.context.ContextSnapshot; +import org.apache.skywalking.apm.agent.core.context.trace.AbstractSpan; +import org.apache.skywalking.apm.agent.core.context.trace.SpanLayer; +import org.apache.skywalking.apm.network.trace.component.ComponentsDefine; +import org.eclipse.jetty.client.api.Response; +import org.eclipse.jetty.client.api.Result; +import org.eclipse.jetty.http.HttpField; +import org.eclipse.jetty.util.Callback; + +import java.nio.ByteBuffer; + +public class ResponseListenerWrapper implements Response.Listener { + + private final Response.Listener listener; + + private final ContextSnapshot context; + + public ResponseListenerWrapper(Response.Listener listener, ContextSnapshot context) { + this.listener = listener; + this.context = context; + } + + @Override + public void onComplete(Result result) { + AbstractSpan span = ContextManager.createLocalSpan(Constants.PLUGIN_NAME + "/CompleteListener/onComplete"); + span.setComponent(ComponentsDefine.JETTY_CLIENT); + SpanLayer.asHttp(span); + if (context != null) { + ContextManager.continued(context); + } + if (listener != null) { + listener.onComplete(result); + } + ContextManager.stopSpan(); + } + + @Override + public void onHeaders(Response response) { + listener.onHeaders(response); + } + + @Override + public void onContent(Response response, ByteBuffer content, Callback callback) { + listener.onContent(response, content, callback); + } + + @Override + public void onContent(Response response, ByteBuffer content) { + listener.onContent(response, content); + } + + @Override + public void onBegin(Response response) { + listener.onBegin(response); + } + + @Override + public boolean onHeader(Response response, HttpField field) { + return listener.onHeader(response, field); + } + + @Override + public void onSuccess(Response response) { + listener.onSuccess(response); + } + + @Override + public void onFailure(Response response, Throwable failure) { + listener.onFailure(response, failure); + } +} \ No newline at end of file diff --git a/apm-sniffer/apm-sdk-plugin/jetty-plugin/jetty-client-9.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/jetty/v9/client/AsyncHttpRequestSendInterceptorTest.java b/apm-sniffer/apm-sdk-plugin/jetty-plugin/jetty-client-9.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/jetty/v9/client/AsyncHttpRequestSendInterceptorTest.java index ae3dc47db1..fb044435da 100644 --- a/apm-sniffer/apm-sdk-plugin/jetty-plugin/jetty-client-9.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/jetty/v9/client/AsyncHttpRequestSendInterceptorTest.java +++ b/apm-sniffer/apm-sdk-plugin/jetty-plugin/jetty-client-9.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/jetty/v9/client/AsyncHttpRequestSendInterceptorTest.java @@ -33,6 +33,7 @@ import org.apache.skywalking.apm.agent.test.tools.TracingSegmentRunner; import org.apache.skywalking.apm.network.trace.component.ComponentsDefine; import org.eclipse.jetty.client.HttpClient; +import org.eclipse.jetty.client.HttpConversation; import org.eclipse.jetty.client.HttpRequest; import org.eclipse.jetty.client.ResponseNotifier; import org.eclipse.jetty.client.api.Response; @@ -82,8 +83,8 @@ public class AsyncHttpRequestSendInterceptorTest { @Before public void setUp() throws Exception { - httpRequestEnhancedInstance = new MockHttpRequest(httpClient, uri); - responseNotifierEnhancedInstance = new MockResponseNotifier(httpClient); + httpRequestEnhancedInstance = new MockHttpRequest(httpClient, new HttpConversation(), uri); + responseNotifierEnhancedInstance = new MockResponseNotifier(); Result results = new Result(httpRequestEnhancedInstance, response); allArguments = new Object[]{(Response.CompleteListener) result -> { }, results}; @@ -146,8 +147,8 @@ private void assertJettySpan() { } private class MockHttpRequest extends HttpRequest implements EnhancedInstance { - public MockHttpRequest(HttpClient httpClient, URI uri) { - super(httpClient, uri); + public MockHttpRequest(HttpClient client, HttpConversation conversation, URI uri) { + super(httpClient, conversation, uri); } @Override @@ -172,8 +173,8 @@ public void setSkyWalkingDynamicField(Object value) { } private class MockResponseNotifier extends ResponseNotifier implements EnhancedInstance { - public MockResponseNotifier(HttpClient client) { - super(client); + public MockResponseNotifier() { + super(); } @Override diff --git a/apm-sniffer/apm-sdk-plugin/jetty-plugin/jetty-client-9.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/jetty/v9/client/SyncHttpRequestSendInterceptorTest.java b/apm-sniffer/apm-sdk-plugin/jetty-plugin/jetty-client-9.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/jetty/v9/client/SyncHttpRequestSendInterceptorTest.java index e4a861fbc7..6f9d2ec489 100644 --- a/apm-sniffer/apm-sdk-plugin/jetty-plugin/jetty-client-9.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/jetty/v9/client/SyncHttpRequestSendInterceptorTest.java +++ b/apm-sniffer/apm-sdk-plugin/jetty-plugin/jetty-client-9.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/jetty/v9/client/SyncHttpRequestSendInterceptorTest.java @@ -34,6 +34,7 @@ import org.apache.skywalking.apm.agent.test.tools.SpanAssert; import org.apache.skywalking.apm.agent.test.tools.TracingSegmentRunner; import org.eclipse.jetty.client.HttpClient; +import org.eclipse.jetty.client.HttpConversation; import org.eclipse.jetty.client.HttpRequest; import org.junit.Assert; import org.junit.Before; @@ -67,7 +68,7 @@ public class SyncHttpRequestSendInterceptorTest { @Before public void setUp() throws Exception { - enhancedInstance = new MockHttpRequest(httpClient, uri); + enhancedInstance = new MockHttpRequest(httpClient, new HttpConversation(), uri); allArguments = new Object[] { "OperationKey", "OperationValue" @@ -123,8 +124,8 @@ public void testMethodsAroundError() throws Throwable { } private class MockHttpRequest extends HttpRequest implements EnhancedInstance { - public MockHttpRequest(HttpClient httpClient, URI uri) { - super(httpClient, uri); + public MockHttpRequest(HttpClient client, HttpConversation conversation, URI uri) { + super(httpClient, conversation, uri); } @Override diff --git a/apm-sniffer/apm-sdk-plugin/jetty-plugin/jetty-server-11.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/jetty-plugin/jetty-server-11.x-plugin/pom.xml index fdede35fcf..c70f0ddd37 100644 --- a/apm-sniffer/apm-sdk-plugin/jetty-plugin/jetty-server-11.x-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/jetty-plugin/jetty-server-11.x-plugin/pom.xml @@ -20,7 +20,7 @@ jetty-plugins org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-sdk-plugin/jetty-plugin/jetty-server-9.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/jetty-plugin/jetty-server-9.x-plugin/pom.xml index 52a860967b..d4602b83c4 100644 --- a/apm-sniffer/apm-sdk-plugin/jetty-plugin/jetty-server-9.x-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/jetty-plugin/jetty-server-9.x-plugin/pom.xml @@ -20,7 +20,7 @@ jetty-plugins org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-sdk-plugin/jetty-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/jetty-plugin/pom.xml index 20f6257f02..0d051bd4c0 100644 --- a/apm-sniffer/apm-sdk-plugin/jetty-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/jetty-plugin/pom.xml @@ -23,7 +23,7 @@ org.apache.skywalking apm-sdk-plugin - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT jetty-plugins @@ -41,6 +41,5 @@ UTF-8 - /.. diff --git a/apm-sniffer/apm-sdk-plugin/jetty-thread-pool-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/jetty-thread-pool-plugin/pom.xml index eceba60962..533868c65d 100644 --- a/apm-sniffer/apm-sdk-plugin/jetty-thread-pool-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/jetty-thread-pool-plugin/pom.xml @@ -21,7 +21,7 @@ apm-sdk-plugin org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-sdk-plugin/jsonrpc4j-1.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/jsonrpc4j-1.x-plugin/pom.xml index a31b247ed4..f4ef9b5118 100644 --- a/apm-sniffer/apm-sdk-plugin/jsonrpc4j-1.x-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/jsonrpc4j-1.x-plugin/pom.xml @@ -21,7 +21,7 @@ apm-sdk-plugin org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-sdk-plugin/kafka-commons/pom.xml b/apm-sniffer/apm-sdk-plugin/kafka-commons/pom.xml index 3d37985fcd..9870095054 100644 --- a/apm-sniffer/apm-sdk-plugin/kafka-commons/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/kafka-commons/pom.xml @@ -23,7 +23,7 @@ org.apache.skywalking apm-sdk-plugin - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT apm-kafka-commons diff --git a/apm-sniffer/apm-sdk-plugin/kafka-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/kafka-plugin/pom.xml index 15bcf8bcdf..9c03ef164a 100644 --- a/apm-sniffer/apm-sdk-plugin/kafka-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/kafka-plugin/pom.xml @@ -21,7 +21,7 @@ apm-sdk-plugin org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-sdk-plugin/kafka-plugin/src/main/java/org/apache/skywalking/apm/plugin/kafka/define/ClassicKafkaConsumerInstrumentation.java b/apm-sniffer/apm-sdk-plugin/kafka-plugin/src/main/java/org/apache/skywalking/apm/plugin/kafka/define/ClassicKafkaConsumerInstrumentation.java new file mode 100644 index 0000000000..c2092c6de6 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/kafka-plugin/src/main/java/org/apache/skywalking/apm/plugin/kafka/define/ClassicKafkaConsumerInstrumentation.java @@ -0,0 +1,40 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.kafka.define; + +import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch; + +import static org.apache.skywalking.apm.agent.core.plugin.match.NameMatch.byName; + +/** + * For Kafka 3.9.x change + * + *
+ *  1. The class named LegacyKafkaConsumer was rename to ClassicKafkaConsumer
+ *  2. Because of the enhance class was changed, so we should create new Instrumentation to enhance the new class
+ * 
+ */ +public class ClassicKafkaConsumerInstrumentation extends KafkaConsumerInstrumentation { + private static final String ENHANCE_CLASS_39_CLASSIC = "org.apache.kafka.clients.consumer.internals.ClassicKafkaConsumer"; + + @Override + protected ClassMatch enhanceClass() { + return byName(ENHANCE_CLASS_39_CLASSIC); + } +} diff --git a/apm-sniffer/apm-sdk-plugin/kafka-plugin/src/main/java/org/apache/skywalking/apm/plugin/kafka/define/Kafka37AsyncConsumerInstrumentation.java b/apm-sniffer/apm-sdk-plugin/kafka-plugin/src/main/java/org/apache/skywalking/apm/plugin/kafka/define/Kafka37AsyncConsumerInstrumentation.java new file mode 100644 index 0000000000..7c24a135ac --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/kafka-plugin/src/main/java/org/apache/skywalking/apm/plugin/kafka/define/Kafka37AsyncConsumerInstrumentation.java @@ -0,0 +1,40 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.kafka.define; + +import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch; +import static org.apache.skywalking.apm.agent.core.plugin.match.NameMatch.byName; + +/** + * For Kafka 3.7.x change + * + *
+ *  1. The method named pollForFetchs was removed from KafkaConsumer to AsyncKafkaConsumer and LegacyKafkaConsumer
+ *  2. Because of the enhance class was changed, so we should create new Instrumentation to intercept the method
+ * 
+ */ +public class Kafka37AsyncConsumerInstrumentation extends KafkaConsumerInstrumentation { + + private static final String ENHANCE_CLASS_37_ASYNC = "org.apache.kafka.clients.consumer.internals.AsyncKafkaConsumer"; + + @Override + protected ClassMatch enhanceClass() { + return byName(ENHANCE_CLASS_37_ASYNC); + } +} \ No newline at end of file diff --git a/apm-sniffer/apm-sdk-plugin/kafka-plugin/src/main/java/org/apache/skywalking/apm/plugin/kafka/define/Kafka37LegacyConsumerInstrumentation.java b/apm-sniffer/apm-sdk-plugin/kafka-plugin/src/main/java/org/apache/skywalking/apm/plugin/kafka/define/Kafka37LegacyConsumerInstrumentation.java new file mode 100644 index 0000000000..95f33bfe82 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/kafka-plugin/src/main/java/org/apache/skywalking/apm/plugin/kafka/define/Kafka37LegacyConsumerInstrumentation.java @@ -0,0 +1,40 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.kafka.define; + +import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch; +import static org.apache.skywalking.apm.agent.core.plugin.match.NameMatch.byName; + +/** + * For Kafka 3.7.x change + * + *
+ *  1. The method named pollForFetchs was removed from KafkaConsumer to AsyncKafkaConsumer and LegacyKafkaConsumer
+ *  2. Because of the enhance class was changed, so we should create new Instrumentation to intercept the method
+ * 
+ */ +public class Kafka37LegacyConsumerInstrumentation extends KafkaConsumerInstrumentation { + + private static final String ENHANCE_CLASS_37_LEGACY = "org.apache.kafka.clients.consumer.internals.LegacyKafkaConsumer"; + + @Override + protected ClassMatch enhanceClass() { + return byName(ENHANCE_CLASS_37_LEGACY); + } +} \ No newline at end of file diff --git a/apm-sniffer/apm-sdk-plugin/kafka-plugin/src/main/resources/skywalking-plugin.def b/apm-sniffer/apm-sdk-plugin/kafka-plugin/src/main/resources/skywalking-plugin.def index d807aceefe..bb98b86a97 100644 --- a/apm-sniffer/apm-sdk-plugin/kafka-plugin/src/main/resources/skywalking-plugin.def +++ b/apm-sniffer/apm-sdk-plugin/kafka-plugin/src/main/resources/skywalking-plugin.def @@ -18,4 +18,7 @@ kafka-0.11.x/1.x/2.x=org.apache.skywalking.apm.plugin.kafka.define.CallbackInstr kafka-0.11.x/1.x/2.x=org.apache.skywalking.apm.plugin.kafka.define.KafkaConsumerInstrumentation kafka-0.11.x/1.x/2.x=org.apache.skywalking.apm.plugin.kafka.define.KafkaProducerInstrumentation kafka-0.11.x/1.x/2.x=org.apache.skywalking.apm.plugin.kafka.define.KafkaProducerMapInstrumentation -kafka-0.11.x/1.x/2.x=org.apache.skywalking.apm.plugin.kafka.define.KafkaTemplateCallbackInstrumentation \ No newline at end of file +kafka-0.11.x/1.x/2.x=org.apache.skywalking.apm.plugin.kafka.define.KafkaTemplateCallbackInstrumentation +kafka-3.7.x=org.apache.skywalking.apm.plugin.kafka.define.Kafka37AsyncConsumerInstrumentation +kafka-3.7.x=org.apache.skywalking.apm.plugin.kafka.define.Kafka37LegacyConsumerInstrumentation +kafka-3.9.x=org.apache.skywalking.apm.plugin.kafka.define.ClassicKafkaConsumerInstrumentation \ No newline at end of file diff --git a/apm-sniffer/apm-sdk-plugin/kylin-jdbc-2.6.x-3.x-4.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/kylin-jdbc-2.6.x-3.x-4.x-plugin/pom.xml index 8896304707..54d4d9e605 100644 --- a/apm-sniffer/apm-sdk-plugin/kylin-jdbc-2.6.x-3.x-4.x-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/kylin-jdbc-2.6.x-3.x-4.x-plugin/pom.xml @@ -21,7 +21,7 @@ apm-sdk-plugin org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-sdk-plugin/lettuce-plugins/lettuce-5.x-6.4.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/lettuce-plugins/lettuce-5.x-6.4.x-plugin/pom.xml new file mode 100644 index 0000000000..c2158fcb4a --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/lettuce-plugins/lettuce-5.x-6.4.x-plugin/pom.xml @@ -0,0 +1,53 @@ + + + + + 4.0.0 + + org.apache.skywalking + lettuce-plugins + 9.7.0-SNAPSHOT + + + apm-lettuce-5.x-6.4.x-plugin + jar + + lettuce-5.x-6.4.x-plugin + + + 5.0.0.RELEASE + + + + + org.apache.skywalking + apm-lettuce-common + ${project.version} + provided + + + + io.lettuce + lettuce-core + ${lettuce-core.version} + provided + + + + \ No newline at end of file diff --git a/apm-sniffer/apm-sdk-plugin/lettuce-plugins/lettuce-5.x-6.4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/lettuce/v5/RedisChannelWriterInterceptorV5.java b/apm-sniffer/apm-sdk-plugin/lettuce-plugins/lettuce-5.x-6.4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/lettuce/v5/RedisChannelWriterInterceptorV5.java new file mode 100644 index 0000000000..d4984d6fd0 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/lettuce-plugins/lettuce-5.x-6.4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/lettuce/v5/RedisChannelWriterInterceptorV5.java @@ -0,0 +1,29 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.lettuce.v5; + +import io.lettuce.core.protocol.ProtocolKeyword; +import org.apache.skywalking.apm.plugin.lettuce.common.RedisChannelWriterInterceptor; + +public class RedisChannelWriterInterceptorV5 extends RedisChannelWriterInterceptor { + @Override + protected String getCommandName(final ProtocolKeyword protocol) { + return protocol.name(); + } +} diff --git a/apm-sniffer/apm-sdk-plugin/lettuce-plugins/lettuce-5.x-6.4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/lettuce/v5/RedisReactiveCreatePublisherMethodInterceptorV5.java b/apm-sniffer/apm-sdk-plugin/lettuce-plugins/lettuce-5.x-6.4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/lettuce/v5/RedisReactiveCreatePublisherMethodInterceptorV5.java new file mode 100644 index 0000000000..12ddea18f7 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/lettuce-plugins/lettuce-5.x-6.4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/lettuce/v5/RedisReactiveCreatePublisherMethodInterceptorV5.java @@ -0,0 +1,89 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.lettuce.v5; + +import org.apache.skywalking.apm.agent.core.context.ContextManager; +import org.apache.skywalking.apm.agent.core.context.ContextSnapshot; +import org.apache.skywalking.apm.agent.core.context.trace.AbstractSpan; +import org.apache.skywalking.apm.agent.core.context.trace.SpanLayer; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.v2.InstanceMethodsAroundInterceptorV2; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.v2.MethodInvocationContext; +import org.apache.skywalking.apm.network.trace.component.ComponentsDefine; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +import reactor.util.context.Context; + +import java.lang.reflect.Method; +import java.util.function.Function; + +/** + * Intercepts reactive publisher factory methods (createMono/createFlux) + * to ensure the SkyWalking context snapshot is propagated via Reactor Context. + * + *

If the Reactor Context does not already contain a snapshot, this interceptor + * captures the current active context and writes it into the subscriber context + * as a fallback propagation mechanism.

+ */ +public class RedisReactiveCreatePublisherMethodInterceptorV5 implements InstanceMethodsAroundInterceptorV2 { + + private static final String SNAPSHOT_KEY = "SKYWALKING_CONTEXT_SNAPSHOT"; + + @Override + public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, + MethodInvocationContext context) { + } + + @Override + public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, Object ret, MethodInvocationContext context) { + + if (!(ret instanceof Mono) && !(ret instanceof Flux)) { + return ret; + } + final AbstractSpan localSpan = ContextManager.createLocalSpan("Lettuce/Reactive/" + method.getName()); + localSpan.setComponent(ComponentsDefine.LETTUCE); + SpanLayer.asCache(localSpan); + + try { + final ContextSnapshot snapshot = ContextManager.capture(); + + Function contextFunction = ctx -> { + if (ctx.hasKey(SNAPSHOT_KEY)) { + return ctx; + } + return ctx.put(SNAPSHOT_KEY, snapshot); + }; + + if (ret instanceof Mono) { + Mono original = (Mono) ret; + return original.subscriberContext(contextFunction); + } else { + Flux original = (Flux) ret; + return original.subscriberContext(contextFunction); + } + } finally { + ContextManager.stopSpan(); + } + } + + @Override + public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] + argumentsTypes, Throwable t, MethodInvocationContext context) { + } +} diff --git a/apm-sniffer/apm-sdk-plugin/lettuce-5.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/lettuce/v5/define/RedisChannelWriterInstrumentation.java b/apm-sniffer/apm-sdk-plugin/lettuce-plugins/lettuce-5.x-6.4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/lettuce/v5/define/RedisChannelWriterInstrumentationV5.java similarity index 81% rename from apm-sniffer/apm-sdk-plugin/lettuce-5.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/lettuce/v5/define/RedisChannelWriterInstrumentation.java rename to apm-sniffer/apm-sdk-plugin/lettuce-plugins/lettuce-5.x-6.4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/lettuce/v5/define/RedisChannelWriterInstrumentationV5.java index e0a4d8c819..bf3e52f79c 100644 --- a/apm-sniffer/apm-sdk-plugin/lettuce-5.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/lettuce/v5/define/RedisChannelWriterInstrumentation.java +++ b/apm-sniffer/apm-sdk-plugin/lettuce-plugins/lettuce-5.x-6.4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/lettuce/v5/define/RedisChannelWriterInstrumentationV5.java @@ -18,27 +18,26 @@ package org.apache.skywalking.apm.plugin.lettuce.v5.define; +import java.util.Collections; +import java.util.List; import net.bytebuddy.description.method.MethodDescription; import net.bytebuddy.matcher.ElementMatcher; +import net.bytebuddy.matcher.ElementMatchers; +import org.apache.skywalking.apm.agent.core.plugin.WitnessMethod; import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint; import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint; import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine; import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch; -import java.util.List; - import static net.bytebuddy.matcher.ElementMatchers.named; import static net.bytebuddy.matcher.ElementMatchers.takesArgument; import static org.apache.skywalking.apm.agent.core.plugin.match.HierarchyMatch.byHierarchyMatch; -/** - * The writeAndFlush method is used in versions lower than 5.0.2.RELEASE - */ -public class RedisChannelWriterInstrumentation extends ClassInstanceMethodsEnhancePluginDefine { +public class RedisChannelWriterInstrumentationV5 extends ClassInstanceMethodsEnhancePluginDefine { private static final String ENHANCE_CLASS = "io.lettuce.core.RedisChannelWriter"; - private static final String REDIS_CHANNEL_WRITER_INTERCEPTOR_CLASS = "org.apache.skywalking.apm.plugin.lettuce.v5.RedisChannelWriterInterceptor"; + private static final String REDIS_CHANNEL_WRITER_INTERCEPTOR_CLASS = "org.apache.skywalking.apm.plugin.lettuce.v5.RedisChannelWriterInterceptorV5"; @Override public ConstructorInterceptPoint[] getConstructorsInterceptPoints() { @@ -47,12 +46,12 @@ public ConstructorInterceptPoint[] getConstructorsInterceptPoints() { @Override public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() { - return new InstanceMethodsInterceptPoint[]{ + return new InstanceMethodsInterceptPoint[] { new InstanceMethodsInterceptPoint() { @Override public ElementMatcher getMethodsMatcher() { return named("write").and(takesArgument(0, named("io.lettuce.core.protocol.RedisCommand")).or( - takesArgument(0, List.class))); + takesArgument(0, List.class))); } @Override @@ -65,11 +64,19 @@ public boolean isOverrideArgs() { return false; } }, - }; + }; } @Override public ClassMatch enhanceClass() { return byHierarchyMatch(ENHANCE_CLASS); } + + @Override + protected List witnessMethods() { + return Collections.singletonList(new WitnessMethod( + "io.lettuce.core.protocol.ProtocolKeyword", + ElementMatchers.named("name") + )); + } } diff --git a/apm-sniffer/apm-sdk-plugin/lettuce-plugins/lettuce-5.x-6.4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/lettuce/v5/define/RedisReactiveCommandsInstrumentationV5.java b/apm-sniffer/apm-sdk-plugin/lettuce-plugins/lettuce-5.x-6.4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/lettuce/v5/define/RedisReactiveCommandsInstrumentationV5.java new file mode 100644 index 0000000000..65716edd68 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/lettuce-plugins/lettuce-5.x-6.4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/lettuce/v5/define/RedisReactiveCommandsInstrumentationV5.java @@ -0,0 +1,89 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.lettuce.v5.define; + +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.matcher.ElementMatcher; +import org.apache.skywalking.apm.agent.core.plugin.WitnessMethod; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.v2.ClassInstanceMethodsEnhancePluginDefineV2; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.v2.InstanceMethodsInterceptV2Point; +import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch; + +import java.util.Collections; +import java.util.List; + +import static net.bytebuddy.matcher.ElementMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.namedOneOf; +import static net.bytebuddy.matcher.ElementMatchers.takesArguments; +import static org.apache.skywalking.apm.agent.core.plugin.match.HierarchyMatch.byHierarchyMatch; + +/** + * + */ +public class RedisReactiveCommandsInstrumentationV5 extends ClassInstanceMethodsEnhancePluginDefineV2 { + + private static final String ENHANCE_CLASS = "io.lettuce.core.AbstractRedisReactiveCommands"; + + private static final String REDIS_REACTIVE_CREATEMONO_METHOD_INTERCEPTOR = "org.apache.skywalking.apm.plugin.lettuce.v5.RedisReactiveCreatePublisherMethodInterceptorV5"; + + @Override + public InstanceMethodsInterceptV2Point[] getInstanceMethodsInterceptV2Points() { + return new InstanceMethodsInterceptV2Point[]{ + new InstanceMethodsInterceptV2Point() { + @Override + public ElementMatcher getMethodsMatcher() { + return namedOneOf( + "createMono", + "createFlux", + "createDissolvingFlux" + ).and(takesArguments(1)); + } + + @Override + public String getMethodsInterceptorV2() { + return REDIS_REACTIVE_CREATEMONO_METHOD_INTERCEPTOR; + } + + @Override + public boolean isOverrideArgs() { + return false; + } + } + }; + } + + @Override + public ClassMatch enhanceClass() { + return byHierarchyMatch(ENHANCE_CLASS); + } + + @Override + public ConstructorInterceptPoint[] getConstructorsInterceptPoints() { + return new ConstructorInterceptPoint[0]; + } + + @Override + protected List witnessMethods() { + return Collections.singletonList(new WitnessMethod( + "reactor.core.publisher.Mono", + named("subscriberContext") + )); + } +} diff --git a/apm-sniffer/apm-sdk-plugin/lettuce-plugins/lettuce-5.x-6.4.x-plugin/src/main/resources/skywalking-plugin.def b/apm-sniffer/apm-sdk-plugin/lettuce-plugins/lettuce-5.x-6.4.x-plugin/src/main/resources/skywalking-plugin.def new file mode 100644 index 0000000000..006ef23680 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/lettuce-plugins/lettuce-5.x-6.4.x-plugin/src/main/resources/skywalking-plugin.def @@ -0,0 +1,18 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +lettuce-5.x-6.4.x=org.apache.skywalking.apm.plugin.lettuce.v5.define.RedisChannelWriterInstrumentationV5 +lettuce-5.x-6.4.x=org.apache.skywalking.apm.plugin.lettuce.v5.define.RedisReactiveCommandsInstrumentationV5 \ No newline at end of file diff --git a/apm-sniffer/apm-sdk-plugin/lettuce-plugins/lettuce-6.5.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/lettuce-plugins/lettuce-6.5.x-plugin/pom.xml new file mode 100644 index 0000000000..2bf20982cf --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/lettuce-plugins/lettuce-6.5.x-plugin/pom.xml @@ -0,0 +1,53 @@ + + + + + 4.0.0 + + org.apache.skywalking + lettuce-plugins + 9.7.0-SNAPSHOT + + apm-lettuce-6.5.x-plugin + + jar + + lettuce-6.5.x-plugin + + + 6.5.0.RELEASE + + + + + org.apache.skywalking + apm-lettuce-common + ${project.version} + provided + + + + io.lettuce + lettuce-core + ${lettuce-core.version} + provided + + + + \ No newline at end of file diff --git a/apm-sniffer/apm-sdk-plugin/lettuce-plugins/lettuce-6.5.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/lettuce/v65/RedisChannelWriterInterceptorV65.java b/apm-sniffer/apm-sdk-plugin/lettuce-plugins/lettuce-6.5.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/lettuce/v65/RedisChannelWriterInterceptorV65.java new file mode 100644 index 0000000000..2205fa3519 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/lettuce-plugins/lettuce-6.5.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/lettuce/v65/RedisChannelWriterInterceptorV65.java @@ -0,0 +1,29 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.lettuce.v65; + +import io.lettuce.core.protocol.ProtocolKeyword; +import org.apache.skywalking.apm.plugin.lettuce.common.RedisChannelWriterInterceptor; + +public class RedisChannelWriterInterceptorV65 extends RedisChannelWriterInterceptor { + @Override + protected String getCommandName(final ProtocolKeyword protocol) { + return protocol.toString(); + } +} diff --git a/apm-sniffer/apm-sdk-plugin/lettuce-plugins/lettuce-6.5.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/lettuce/v65/RedisReactiveCreatePublisherMethodInterceptorV65.java b/apm-sniffer/apm-sdk-plugin/lettuce-plugins/lettuce-6.5.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/lettuce/v65/RedisReactiveCreatePublisherMethodInterceptorV65.java new file mode 100644 index 0000000000..36320b5cd1 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/lettuce-plugins/lettuce-6.5.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/lettuce/v65/RedisReactiveCreatePublisherMethodInterceptorV65.java @@ -0,0 +1,93 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.lettuce.v65; + +import org.apache.skywalking.apm.agent.core.context.ContextManager; +import org.apache.skywalking.apm.agent.core.context.ContextSnapshot; +import org.apache.skywalking.apm.agent.core.context.trace.AbstractSpan; +import org.apache.skywalking.apm.agent.core.context.trace.SpanLayer; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.v2.InstanceMethodsAroundInterceptorV2; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.v2.MethodInvocationContext; +import org.apache.skywalking.apm.network.trace.component.ComponentsDefine; +import org.reactivestreams.Publisher; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +import java.lang.reflect.Method; + +/** + * Intercepts reactive publisher factory methods (createMono/createFlux) + * to ensure the SkyWalking context snapshot is propagated via Reactor Context. + * + *

If the Reactor Context does not already contain a snapshot, this interceptor + * captures the current active context and writes it into the subscriber context + * as a fallback propagation mechanism.

+ */ +public class RedisReactiveCreatePublisherMethodInterceptorV65 implements InstanceMethodsAroundInterceptorV2 { + + private static final String SNAPSHOT_KEY = "SKYWALKING_CONTEXT_SNAPSHOT"; + + @Override + public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, + MethodInvocationContext context) { + } + + @Override + public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, Object ret, MethodInvocationContext context) { + + if (!(ret instanceof Mono) && !(ret instanceof Flux)) { + return ret; + } + + final AbstractSpan localSpan = ContextManager.createLocalSpan("Lettuce/Reactive/" + method.getName()); + localSpan.setComponent(ComponentsDefine.LETTUCE); + SpanLayer.asCache(localSpan); + + try { + final ContextSnapshot snapshot = ContextManager.capture(); + + return wrapPublisher((Publisher) ret, snapshot); + } finally { + ContextManager.stopSpan(); + } + } + + private Publisher wrapPublisher(Publisher original, ContextSnapshot snapshot) { + if (original instanceof Mono) { + return Mono.deferContextual(ctxView -> { + if (ctxView.hasKey(SNAPSHOT_KEY)) { + return (Mono) original; + } + return ((Mono) original).contextWrite(c -> c.put(SNAPSHOT_KEY, snapshot)); + }); + } else { + return Flux.deferContextual(ctxView -> { + if (ctxView.hasKey(SNAPSHOT_KEY)) { + return original; + } + return ((Flux) original).contextWrite(c -> c.put(SNAPSHOT_KEY, snapshot)); + }); + } + } + + @Override + public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, Throwable t, MethodInvocationContext context) { + } +} diff --git a/apm-sniffer/apm-sdk-plugin/lettuce-plugins/lettuce-6.5.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/lettuce/v65/define/RedisChannelWriterInstrumentationV65.java b/apm-sniffer/apm-sdk-plugin/lettuce-plugins/lettuce-6.5.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/lettuce/v65/define/RedisChannelWriterInstrumentationV65.java new file mode 100644 index 0000000000..5e10c43561 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/lettuce-plugins/lettuce-6.5.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/lettuce/v65/define/RedisChannelWriterInstrumentationV65.java @@ -0,0 +1,82 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.lettuce.v65.define; + +import java.util.Collections; +import java.util.List; +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.matcher.ElementMatcher; +import net.bytebuddy.matcher.ElementMatchers; +import org.apache.skywalking.apm.agent.core.plugin.WitnessMethod; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine; +import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch; + +import static net.bytebuddy.matcher.ElementMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.takesArgument; +import static org.apache.skywalking.apm.agent.core.plugin.match.HierarchyMatch.byHierarchyMatch; + +public class RedisChannelWriterInstrumentationV65 extends ClassInstanceMethodsEnhancePluginDefine { + + private static final String ENHANCE_CLASS = "io.lettuce.core.RedisChannelWriter"; + + private static final String REDIS_CHANNEL_WRITER_INTERCEPTOR_CLASS = "org.apache.skywalking.apm.plugin.lettuce.v65.RedisChannelWriterInterceptorV65"; + + @Override + public ConstructorInterceptPoint[] getConstructorsInterceptPoints() { + return new ConstructorInterceptPoint[0]; + } + + @Override + public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() { + return new InstanceMethodsInterceptPoint[] { + new InstanceMethodsInterceptPoint() { + @Override + public ElementMatcher getMethodsMatcher() { + return named("write").and(takesArgument(0, named("io.lettuce.core.protocol.RedisCommand")).or( + takesArgument(0, List.class))); + } + + @Override + public String getMethodsInterceptor() { + return REDIS_CHANNEL_WRITER_INTERCEPTOR_CLASS; + } + + @Override + public boolean isOverrideArgs() { + return false; + } + }, + }; + } + + @Override + public ClassMatch enhanceClass() { + return byHierarchyMatch(ENHANCE_CLASS); + } + + @Override + protected List witnessMethods() { + return Collections.singletonList(new WitnessMethod( + "io.lettuce.core.protocol.ProtocolKeyword", + ElementMatchers.named("toString") + )); + } +} diff --git a/apm-sniffer/apm-sdk-plugin/lettuce-plugins/lettuce-6.5.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/lettuce/v65/define/RedisReactiveCommandsInstrumentationV65.java b/apm-sniffer/apm-sdk-plugin/lettuce-plugins/lettuce-6.5.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/lettuce/v65/define/RedisReactiveCommandsInstrumentationV65.java new file mode 100644 index 0000000000..f1c22b88ee --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/lettuce-plugins/lettuce-6.5.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/lettuce/v65/define/RedisReactiveCommandsInstrumentationV65.java @@ -0,0 +1,89 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.lettuce.v65.define; + +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.matcher.ElementMatcher; +import net.bytebuddy.matcher.ElementMatchers; +import org.apache.skywalking.apm.agent.core.plugin.WitnessMethod; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.v2.ClassInstanceMethodsEnhancePluginDefineV2; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.v2.InstanceMethodsInterceptV2Point; +import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch; + +import java.util.Collections; +import java.util.List; + +import static net.bytebuddy.matcher.ElementMatchers.namedOneOf; +import static net.bytebuddy.matcher.ElementMatchers.takesArguments; +import static org.apache.skywalking.apm.agent.core.plugin.match.HierarchyMatch.byHierarchyMatch; + +/** + * + */ +public class RedisReactiveCommandsInstrumentationV65 extends ClassInstanceMethodsEnhancePluginDefineV2 { + + private static final String ENHANCE_CLASS = "io.lettuce.core.AbstractRedisReactiveCommands"; + + private static final String REDIS_REACTIVE_CREATEMONO_METHOD_INTERCEPTOR = "org.apache.skywalking.apm.plugin.lettuce.v65.RedisReactiveCreatePublisherMethodInterceptorV65"; + + @Override + public InstanceMethodsInterceptV2Point[] getInstanceMethodsInterceptV2Points() { + return new InstanceMethodsInterceptV2Point[]{ + new InstanceMethodsInterceptV2Point() { + @Override + public ElementMatcher getMethodsMatcher() { + return namedOneOf( + "createMono", + "createFlux", + "createDissolvingFlux" + ).and(takesArguments(1)); + } + + @Override + public String getMethodsInterceptorV2() { + return REDIS_REACTIVE_CREATEMONO_METHOD_INTERCEPTOR; + } + + @Override + public boolean isOverrideArgs() { + return false; + } + } + }; + } + + @Override + public ClassMatch enhanceClass() { + return byHierarchyMatch(ENHANCE_CLASS); + } + + @Override + public ConstructorInterceptPoint[] getConstructorsInterceptPoints() { + return new ConstructorInterceptPoint[0]; + } + + @Override + protected List witnessMethods() { + return Collections.singletonList(new WitnessMethod( + "reactor.core.publisher.Mono", + ElementMatchers.named("deferContextual") + )); + } +} diff --git a/apm-sniffer/apm-sdk-plugin/lettuce-plugins/lettuce-6.5.x-plugin/src/main/resources/skywalking-plugin.def b/apm-sniffer/apm-sdk-plugin/lettuce-plugins/lettuce-6.5.x-plugin/src/main/resources/skywalking-plugin.def new file mode 100644 index 0000000000..aac68f9479 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/lettuce-plugins/lettuce-6.5.x-plugin/src/main/resources/skywalking-plugin.def @@ -0,0 +1,18 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +lettuce-6.5.x=org.apache.skywalking.apm.plugin.lettuce.v65.define.RedisChannelWriterInstrumentationV65 +lettuce-6.5.x=org.apache.skywalking.apm.plugin.lettuce.v65.define.RedisReactiveCommandsInstrumentationV65 \ No newline at end of file diff --git a/apm-sniffer/apm-sdk-plugin/lettuce-5.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/lettuce-plugins/lettuce-common/pom.xml similarity index 86% rename from apm-sniffer/apm-sdk-plugin/lettuce-5.x-plugin/pom.xml rename to apm-sniffer/apm-sdk-plugin/lettuce-plugins/lettuce-common/pom.xml index b9a86f9b04..6062c90bce 100644 --- a/apm-sniffer/apm-sdk-plugin/lettuce-5.x-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/lettuce-plugins/lettuce-common/pom.xml @@ -1,4 +1,4 @@ - + + + + 4.0.0 + + org.apache.skywalking + apm-sdk-plugin + 9.7.0-SNAPSHOT + + + lettuce-plugins + pom + + + lettuce-common + lettuce-5.x-6.4.x-plugin + lettuce-6.5.x-plugin + + + \ No newline at end of file diff --git a/apm-sniffer/apm-sdk-plugin/light4j-plugins/light4j-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/light4j-plugins/light4j-plugin/pom.xml index a669c6e9ef..12b4e3a59e 100644 --- a/apm-sniffer/apm-sdk-plugin/light4j-plugins/light4j-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/light4j-plugins/light4j-plugin/pom.xml @@ -20,7 +20,7 @@ light4j-plugins org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-sdk-plugin/light4j-plugins/pom.xml b/apm-sniffer/apm-sdk-plugin/light4j-plugins/pom.xml index 6af9b359fc..cd9ab3c418 100644 --- a/apm-sniffer/apm-sdk-plugin/light4j-plugins/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/light4j-plugins/pom.xml @@ -23,7 +23,7 @@ org.apache.skywalking apm-sdk-plugin - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT light4j-plugins @@ -37,6 +37,5 @@ UTF-8 - /.. diff --git a/apm-sniffer/apm-sdk-plugin/mariadb-2.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/mariadb-2.x-plugin/pom.xml index 17c2426017..2ec8ab3063 100644 --- a/apm-sniffer/apm-sdk-plugin/mariadb-2.x-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/mariadb-2.x-plugin/pom.xml @@ -20,7 +20,7 @@ apm-sdk-plugin org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-sdk-plugin/micronaut-plugins/micronaut-http-client-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/micronaut-plugins/micronaut-http-client-plugin/pom.xml index 758f5b7c3e..a0955c19d2 100644 --- a/apm-sniffer/apm-sdk-plugin/micronaut-plugins/micronaut-http-client-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/micronaut-plugins/micronaut-http-client-plugin/pom.xml @@ -21,7 +21,7 @@ org.apache.skywalking micronaut-plugins - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-sdk-plugin/micronaut-plugins/micronaut-http-server-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/micronaut-plugins/micronaut-http-server-plugin/pom.xml index f5217fbe8b..a8fd47deac 100644 --- a/apm-sniffer/apm-sdk-plugin/micronaut-plugins/micronaut-http-server-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/micronaut-plugins/micronaut-http-server-plugin/pom.xml @@ -21,7 +21,7 @@ org.apache.skywalking micronaut-plugins - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-sdk-plugin/micronaut-plugins/pom.xml b/apm-sniffer/apm-sdk-plugin/micronaut-plugins/pom.xml index 04b6a26e72..0025bcf432 100644 --- a/apm-sniffer/apm-sdk-plugin/micronaut-plugins/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/micronaut-plugins/pom.xml @@ -20,7 +20,7 @@ apm-sdk-plugin org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 @@ -29,7 +29,6 @@ UTF-8 - /.. diff --git a/apm-sniffer/apm-sdk-plugin/mongodb-2.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/mongodb-2.x-plugin/pom.xml index 14c0c230bf..e16638a4b9 100644 --- a/apm-sniffer/apm-sdk-plugin/mongodb-2.x-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/mongodb-2.x-plugin/pom.xml @@ -21,7 +21,7 @@ apm-sdk-plugin org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/pom.xml index 99adf67594..212222c6d6 100644 --- a/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/pom.xml @@ -21,7 +21,7 @@ apm-sdk-plugin org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT apm-mongodb-3.x-plugin diff --git a/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/define/readOperation/AggregateExplainOperationInstrumentation.java b/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/define/readOperation/AggregateExplainOperationInstrumentation.java new file mode 100644 index 0000000000..929935a2c1 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/define/readOperation/AggregateExplainOperationInstrumentation.java @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.mongodb.v3.define.readOperation; + +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.matcher.ElementMatcher; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine; +import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch; +import org.apache.skywalking.apm.agent.core.plugin.match.NameMatch; + +import static net.bytebuddy.matcher.ElementMatchers.any; + +public class AggregateExplainOperationInstrumentation extends ClassInstanceMethodsEnhancePluginDefine { + + private static final String ENHANCE_CLASS = "com.mongodb.operation.AggregateExplainOperation"; + + private static final String INTERCEPTOR_CLASS = "org.apache.skywalking.apm.plugin.mongodb.v3.interceptor.operation.OperationNamespaceConstructInterceptor"; + + @Override + protected ClassMatch enhanceClass() { + return NameMatch.byName(ENHANCE_CLASS); + } + + @Override + public ConstructorInterceptPoint[] getConstructorsInterceptPoints() { + return new ConstructorInterceptPoint[] { + new ConstructorInterceptPoint() { + @Override + public ElementMatcher getConstructorMatcher() { + return any(); + } + + @Override + public String getConstructorInterceptor() { + return INTERCEPTOR_CLASS; + } + } + }; + } + + @Override + public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() { + return new InstanceMethodsInterceptPoint[0]; + } + +} diff --git a/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/define/readOperation/AggregateOperationImplInstrumentation.java b/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/define/readOperation/AggregateOperationImplInstrumentation.java new file mode 100644 index 0000000000..208d0a2146 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/define/readOperation/AggregateOperationImplInstrumentation.java @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.mongodb.v3.define.readOperation; + +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.matcher.ElementMatcher; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine; +import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch; +import org.apache.skywalking.apm.agent.core.plugin.match.NameMatch; + +import static net.bytebuddy.matcher.ElementMatchers.any; + +public class AggregateOperationImplInstrumentation extends ClassInstanceMethodsEnhancePluginDefine { + + private static final String ENHANCE_CLASS = "com.mongodb.operation.AggregateOperationImpl"; + + private static final String INTERCEPTOR_CLASS = "org.apache.skywalking.apm.plugin.mongodb.v3.interceptor.operation.OperationNamespaceConstructInterceptor"; + + @Override + protected ClassMatch enhanceClass() { + return NameMatch.byName(ENHANCE_CLASS); + } + + @Override + public ConstructorInterceptPoint[] getConstructorsInterceptPoints() { + return new ConstructorInterceptPoint[] { + new ConstructorInterceptPoint() { + + @Override + public ElementMatcher getConstructorMatcher() { + return any(); + } + + @Override + public String getConstructorInterceptor() { + return INTERCEPTOR_CLASS; + } + } + }; + } + + @Override + public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() { + return new InstanceMethodsInterceptPoint[0]; + } +} \ No newline at end of file diff --git a/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/define/readOperation/AggregateOperationInstrumentation.java b/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/define/readOperation/AggregateOperationInstrumentation.java new file mode 100644 index 0000000000..bffd857fcb --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/define/readOperation/AggregateOperationInstrumentation.java @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.mongodb.v3.define.readOperation; + +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.matcher.ElementMatcher; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine; +import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch; +import org.apache.skywalking.apm.agent.core.plugin.match.NameMatch; + +import static net.bytebuddy.matcher.ElementMatchers.any; + +public class AggregateOperationInstrumentation extends ClassInstanceMethodsEnhancePluginDefine { + + private static final String ENHANCE_CLASS = "com.mongodb.operation.AggregateOperation"; + + private static final String INTERCEPTOR_CLASS = "org.apache.skywalking.apm.plugin.mongodb.v3.interceptor.operation.OperationNamespaceConstructInterceptor"; + + @Override + protected ClassMatch enhanceClass() { + return NameMatch.byName(ENHANCE_CLASS); + } + + @Override + public ConstructorInterceptPoint[] getConstructorsInterceptPoints() { + return new ConstructorInterceptPoint[] { + new ConstructorInterceptPoint() { + + @Override + public ElementMatcher getConstructorMatcher() { + return any(); + } + + @Override + public String getConstructorInterceptor() { + return INTERCEPTOR_CLASS; + } + } + }; + } + + @Override + public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() { + return new InstanceMethodsInterceptPoint[0]; + } +} \ No newline at end of file diff --git a/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/define/readOperation/ChangeStreamOperationInstrumentation.java b/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/define/readOperation/ChangeStreamOperationInstrumentation.java new file mode 100644 index 0000000000..f71ba7e4db --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/define/readOperation/ChangeStreamOperationInstrumentation.java @@ -0,0 +1,65 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.mongodb.v3.define.readOperation; + +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.matcher.ElementMatcher; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine; +import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch; +import org.apache.skywalking.apm.agent.core.plugin.match.NameMatch; + +import static net.bytebuddy.matcher.ElementMatchers.takesArguments; + +public class ChangeStreamOperationInstrumentation extends ClassInstanceMethodsEnhancePluginDefine { + + private static final String ENHANCE_CLASS = "com.mongodb.operation.ChangeStreamOperation"; + + private static final String INTERCEPTOR_CLASS = "org.apache.skywalking.apm.plugin.mongodb.v3.interceptor.operation.OperationNamespaceConstructInterceptor"; + + @Override + protected ClassMatch enhanceClass() { + return NameMatch.byName(ENHANCE_CLASS); + } + + @Override + public ConstructorInterceptPoint[] getConstructorsInterceptPoints() { + return new ConstructorInterceptPoint[] { + new ConstructorInterceptPoint() { + + @Override + public ElementMatcher getConstructorMatcher() { + return takesArguments(5); + } + + @Override + public String getConstructorInterceptor() { + return INTERCEPTOR_CLASS; + } + } + }; + } + + @Override + public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() { + return new InstanceMethodsInterceptPoint[0]; + } + +} \ No newline at end of file diff --git a/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/define/readOperation/CommandReadOperationInstrumentation.java b/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/define/readOperation/CommandReadOperationInstrumentation.java new file mode 100644 index 0000000000..ab317963f2 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/define/readOperation/CommandReadOperationInstrumentation.java @@ -0,0 +1,65 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.mongodb.v3.define.readOperation; + +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.matcher.ElementMatcher; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine; +import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch; +import org.apache.skywalking.apm.agent.core.plugin.match.NameMatch; + +import static net.bytebuddy.matcher.ElementMatchers.any; + +public class CommandReadOperationInstrumentation extends ClassInstanceMethodsEnhancePluginDefine { + + private static final String ENHANCE_CLASS = "com.mongodb.operation.CommandReadOperation"; + + private static final String INTERCEPTOR_CLASS = "org.apache.skywalking.apm.plugin.mongodb.v3.interceptor.operation.OperationDatabaseConstructInterceptor"; + + @Override + protected ClassMatch enhanceClass() { + return NameMatch.byName(ENHANCE_CLASS); + } + + @Override + public ConstructorInterceptPoint[] getConstructorsInterceptPoints() { + return new ConstructorInterceptPoint[] { + new ConstructorInterceptPoint() { + + @Override + public ElementMatcher getConstructorMatcher() { + return any(); + } + + @Override + public String getConstructorInterceptor() { + return INTERCEPTOR_CLASS; + } + } + }; + } + + @Override + public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() { + return new InstanceMethodsInterceptPoint[0]; + } + +} diff --git a/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/define/readOperation/CountOperationInstrumentation.java b/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/define/readOperation/CountOperationInstrumentation.java new file mode 100644 index 0000000000..6ee8e1ea29 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/define/readOperation/CountOperationInstrumentation.java @@ -0,0 +1,65 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.mongodb.v3.define.readOperation; + +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.matcher.ElementMatcher; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine; +import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch; +import org.apache.skywalking.apm.agent.core.plugin.match.NameMatch; + +import static net.bytebuddy.matcher.ElementMatchers.any; + +public class CountOperationInstrumentation extends ClassInstanceMethodsEnhancePluginDefine { + + private static final String ENHANCE_CLASS = "com.mongodb.operation.CountOperation"; + + private static final String INTERCEPTOR_CLASS = "org.apache.skywalking.apm.plugin.mongodb.v3.interceptor.operation.OperationNamespaceConstructInterceptor"; + + @Override + protected ClassMatch enhanceClass() { + return NameMatch.byName(ENHANCE_CLASS); + } + + @Override + public ConstructorInterceptPoint[] getConstructorsInterceptPoints() { + return new ConstructorInterceptPoint[] { + new ConstructorInterceptPoint() { + + @Override + public ElementMatcher getConstructorMatcher() { + return any(); + } + + @Override + public String getConstructorInterceptor() { + return INTERCEPTOR_CLASS; + } + } + }; + } + + @Override + public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() { + return new InstanceMethodsInterceptPoint[0]; + } + +} \ No newline at end of file diff --git a/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/define/readOperation/DistinctOperationInstrumentation.java b/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/define/readOperation/DistinctOperationInstrumentation.java new file mode 100644 index 0000000000..43cf1f9db3 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/define/readOperation/DistinctOperationInstrumentation.java @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.mongodb.v3.define.readOperation; + +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.matcher.ElementMatcher; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine; +import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch; +import org.apache.skywalking.apm.agent.core.plugin.match.NameMatch; + +import static net.bytebuddy.matcher.ElementMatchers.any; + +public class DistinctOperationInstrumentation extends ClassInstanceMethodsEnhancePluginDefine { + + private static final String ENHANCE_CLASS = "com.mongodb.operation.DistinctOperation"; + + private static final String INTERCEPTOR_CLASS = "org.apache.skywalking.apm.plugin.mongodb.v3.interceptor.operation.OperationNamespaceConstructInterceptor"; + + @Override + protected ClassMatch enhanceClass() { + return NameMatch.byName(ENHANCE_CLASS); + } + + @Override + public ConstructorInterceptPoint[] getConstructorsInterceptPoints() { + return new ConstructorInterceptPoint[] { + new ConstructorInterceptPoint() { + @Override + public ElementMatcher getConstructorMatcher() { + return any(); + } + + @Override + public String getConstructorInterceptor() { + return INTERCEPTOR_CLASS; + } + } + }; + } + + @Override + public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() { + return new InstanceMethodsInterceptPoint[0]; + } + +} \ No newline at end of file diff --git a/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/define/readOperation/FindOperationInstrumentation.java b/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/define/readOperation/FindOperationInstrumentation.java new file mode 100644 index 0000000000..702d78138a --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/define/readOperation/FindOperationInstrumentation.java @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.mongodb.v3.define.readOperation; + +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.matcher.ElementMatcher; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine; +import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch; +import org.apache.skywalking.apm.agent.core.plugin.match.NameMatch; + +import static net.bytebuddy.matcher.ElementMatchers.any; + +public class FindOperationInstrumentation extends ClassInstanceMethodsEnhancePluginDefine { + + private static final String ENHANCE_CLASS = "com.mongodb.operation.FindOperation"; + + private static final String INTERCEPTOR_CLASS = "org.apache.skywalking.apm.plugin.mongodb.v3.interceptor.operation.OperationNamespaceConstructInterceptor"; + + @Override + protected ClassMatch enhanceClass() { + return NameMatch.byName(ENHANCE_CLASS); + } + + @Override + public ConstructorInterceptPoint[] getConstructorsInterceptPoints() { + return new ConstructorInterceptPoint[] { + new ConstructorInterceptPoint() { + @Override + public ElementMatcher getConstructorMatcher() { + return any(); + } + + @Override + public String getConstructorInterceptor() { + return INTERCEPTOR_CLASS; + } + } + }; + } + + @Override + public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() { + return new InstanceMethodsInterceptPoint[0]; + } + +} \ No newline at end of file diff --git a/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/define/readOperation/GroupOperationInstrumentation.java b/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/define/readOperation/GroupOperationInstrumentation.java new file mode 100644 index 0000000000..0ad153cdef --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/define/readOperation/GroupOperationInstrumentation.java @@ -0,0 +1,63 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.mongodb.v3.define.readOperation; + +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.matcher.ElementMatcher; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine; +import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch; +import org.apache.skywalking.apm.agent.core.plugin.match.NameMatch; + +import static net.bytebuddy.matcher.ElementMatchers.any; + +public class GroupOperationInstrumentation extends ClassInstanceMethodsEnhancePluginDefine { + + private static final String ENHANCE_CLASS = "com.mongodb.operation.GroupOperation"; + + private static final String INTERCEPTOR_CLASS = "org.apache.skywalking.apm.plugin.mongodb.v3.interceptor.operation.OperationNamespaceConstructInterceptor"; + + @Override + protected ClassMatch enhanceClass() { + return NameMatch.byName(ENHANCE_CLASS); + } + + @Override + public ConstructorInterceptPoint[] getConstructorsInterceptPoints() { + return new ConstructorInterceptPoint[] { + new ConstructorInterceptPoint() { + @Override + public ElementMatcher getConstructorMatcher() { + return any(); + } + + @Override + public String getConstructorInterceptor() { + return INTERCEPTOR_CLASS; + } + } + }; + } + + @Override + public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() { + return new InstanceMethodsInterceptPoint[0]; + } +} diff --git a/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/define/readOperation/ListCollectionsOperationInstrumentation.java b/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/define/readOperation/ListCollectionsOperationInstrumentation.java new file mode 100644 index 0000000000..0eff3237db --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/define/readOperation/ListCollectionsOperationInstrumentation.java @@ -0,0 +1,63 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.mongodb.v3.define.readOperation; + +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.matcher.ElementMatcher; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine; +import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch; +import org.apache.skywalking.apm.agent.core.plugin.match.NameMatch; + +import static net.bytebuddy.matcher.ElementMatchers.any; + +public class ListCollectionsOperationInstrumentation extends ClassInstanceMethodsEnhancePluginDefine { + + private static final String ENHANCE_CLASS = "com.mongodb.operation.ListCollectionsOperation"; + + private static final String INTERCEPTOR_CLASS = "org.apache.skywalking.apm.plugin.mongodb.v3.interceptor.operation.OperationDatabaseConstructInterceptor"; + + @Override + protected ClassMatch enhanceClass() { + return NameMatch.byName(ENHANCE_CLASS); + } + + @Override + public ConstructorInterceptPoint[] getConstructorsInterceptPoints() { + return new ConstructorInterceptPoint[] { + new ConstructorInterceptPoint() { + @Override + public ElementMatcher getConstructorMatcher() { + return any(); + } + + @Override + public String getConstructorInterceptor() { + return INTERCEPTOR_CLASS; + } + } + }; + } + + @Override + public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() { + return new InstanceMethodsInterceptPoint[0]; + } +} diff --git a/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/define/readOperation/ListIndexesOperationInstrumentation.java b/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/define/readOperation/ListIndexesOperationInstrumentation.java new file mode 100644 index 0000000000..7a1870badc --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/define/readOperation/ListIndexesOperationInstrumentation.java @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.mongodb.v3.define.readOperation; + +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.matcher.ElementMatcher; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine; +import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch; +import org.apache.skywalking.apm.agent.core.plugin.match.NameMatch; + +import static net.bytebuddy.matcher.ElementMatchers.any; + +public class ListIndexesOperationInstrumentation extends ClassInstanceMethodsEnhancePluginDefine { + + private static final String ENHANCE_CLASS = "com.mongodb.operation.ListIndexesOperation"; + + private static final String INTERCEPTOR_CLASS = "org.apache.skywalking.apm.plugin.mongodb.v3.interceptor.operation.OperationNamespaceConstructInterceptor"; + + @Override + protected ClassMatch enhanceClass() { + return NameMatch.byName(ENHANCE_CLASS); + } + + @Override + public ConstructorInterceptPoint[] getConstructorsInterceptPoints() { + return new ConstructorInterceptPoint[] { + new ConstructorInterceptPoint() { + @Override + public ElementMatcher getConstructorMatcher() { + return any(); + } + + @Override + public String getConstructorInterceptor() { + return INTERCEPTOR_CLASS; + } + } + }; + } + + @Override + public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() { + return new InstanceMethodsInterceptPoint[0]; + } + +} \ No newline at end of file diff --git a/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/define/readOperation/MapReduceWithInlineResultsOperationInstrumentation.java b/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/define/readOperation/MapReduceWithInlineResultsOperationInstrumentation.java new file mode 100644 index 0000000000..e0fcadfecf --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/define/readOperation/MapReduceWithInlineResultsOperationInstrumentation.java @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.mongodb.v3.define.readOperation; + +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.matcher.ElementMatcher; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine; +import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch; +import org.apache.skywalking.apm.agent.core.plugin.match.NameMatch; + +import static net.bytebuddy.matcher.ElementMatchers.any; + +public class MapReduceWithInlineResultsOperationInstrumentation extends ClassInstanceMethodsEnhancePluginDefine { + + private static final String ENHANCE_CLASS = "com.mongodb.operation.MapReduceWithInlineResultsOperation"; + + private static final String INTERCEPTOR_CLASS = "org.apache.skywalking.apm.plugin.mongodb.v3.interceptor.operation.OperationNamespaceConstructInterceptor"; + + @Override + protected ClassMatch enhanceClass() { + return NameMatch.byName(ENHANCE_CLASS); + } + + @Override + public ConstructorInterceptPoint[] getConstructorsInterceptPoints() { + return new ConstructorInterceptPoint[] { + new ConstructorInterceptPoint() { + @Override + public ElementMatcher getConstructorMatcher() { + return any(); + } + + @Override + public String getConstructorInterceptor() { + return INTERCEPTOR_CLASS; + } + } + }; + } + + @Override + public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() { + return new InstanceMethodsInterceptPoint[0]; + } + +} \ No newline at end of file diff --git a/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/define/readOperation/ParallelCollectionScanOperationInstrumentation.java b/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/define/readOperation/ParallelCollectionScanOperationInstrumentation.java new file mode 100644 index 0000000000..554d178570 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/define/readOperation/ParallelCollectionScanOperationInstrumentation.java @@ -0,0 +1,63 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.mongodb.v3.define.readOperation; + +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.matcher.ElementMatcher; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine; +import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch; +import org.apache.skywalking.apm.agent.core.plugin.match.NameMatch; + +import static net.bytebuddy.matcher.ElementMatchers.any; + +public class ParallelCollectionScanOperationInstrumentation extends ClassInstanceMethodsEnhancePluginDefine { + + private static final String ENHANCE_CLASS = "com.mongodb.operation.ParallelCollectionScanOperation"; + + private static final String INTERCEPTOR_CLASS = "org.apache.skywalking.apm.plugin.mongodb.v3.interceptor.operation.OperationNamespaceConstructInterceptor"; + + @Override + protected ClassMatch enhanceClass() { + return NameMatch.byName(ENHANCE_CLASS); + } + + @Override + public ConstructorInterceptPoint[] getConstructorsInterceptPoints() { + return new ConstructorInterceptPoint[] { + new ConstructorInterceptPoint() { + @Override + public ElementMatcher getConstructorMatcher() { + return any(); + } + + @Override + public String getConstructorInterceptor() { + return INTERCEPTOR_CLASS; + } + } + }; + } + + @Override + public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() { + return new InstanceMethodsInterceptPoint[0]; + } +} diff --git a/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/define/readOperation/UserExistsOperationInstrumentation.java b/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/define/readOperation/UserExistsOperationInstrumentation.java new file mode 100644 index 0000000000..393b0096a7 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/define/readOperation/UserExistsOperationInstrumentation.java @@ -0,0 +1,63 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.mongodb.v3.define.readOperation; + +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.matcher.ElementMatcher; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine; +import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch; +import org.apache.skywalking.apm.agent.core.plugin.match.NameMatch; + +import static net.bytebuddy.matcher.ElementMatchers.any; + +public class UserExistsOperationInstrumentation extends ClassInstanceMethodsEnhancePluginDefine { + + private static final String ENHANCE_CLASS = "com.mongodb.operation.UserExistsOperation"; + + private static final String INTERCEPTOR_CLASS = "org.apache.skywalking.apm.plugin.mongodb.v3.interceptor.operation.OperationDatabaseConstructInterceptor"; + + @Override + protected ClassMatch enhanceClass() { + return NameMatch.byName(ENHANCE_CLASS); + } + + @Override + public ConstructorInterceptPoint[] getConstructorsInterceptPoints() { + return new ConstructorInterceptPoint[] { + new ConstructorInterceptPoint() { + @Override + public ElementMatcher getConstructorMatcher() { + return any(); + } + + @Override + public String getConstructorInterceptor() { + return INTERCEPTOR_CLASS; + } + } + }; + } + + @Override + public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() { + return new InstanceMethodsInterceptPoint[0]; + } +} diff --git a/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/define/readOperation/WrappedMapReduceReadOperationInstrumentation.java b/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/define/readOperation/WrappedMapReduceReadOperationInstrumentation.java new file mode 100644 index 0000000000..4e76a8e06c --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/define/readOperation/WrappedMapReduceReadOperationInstrumentation.java @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.mongodb.v3.define.readOperation; + +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.matcher.ElementMatcher; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine; +import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch; +import org.apache.skywalking.apm.agent.core.plugin.match.NameMatch; + +import static net.bytebuddy.matcher.ElementMatchers.any; + +public class WrappedMapReduceReadOperationInstrumentation extends ClassInstanceMethodsEnhancePluginDefine { + + private static final String ENHANCE_CLASS = "com.mongodb.MapReduceIterableImpl"; + + private static final String INTERCEPTOR_CLASS = "org.apache.skywalking.apm.plugin.mongodb.v3.interceptor.operation.OperationNamespaceConstructInterceptor"; + + @Override + protected ClassMatch enhanceClass() { + return NameMatch.byName(ENHANCE_CLASS); + } + + @Override + public ConstructorInterceptPoint[] getConstructorsInterceptPoints() { + return new ConstructorInterceptPoint[] { + new ConstructorInterceptPoint() { + @Override + public ElementMatcher getConstructorMatcher() { + return any(); + } + + @Override + public String getConstructorInterceptor() { + return INTERCEPTOR_CLASS; + } + } + }; + } + + @Override + public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() { + return new InstanceMethodsInterceptPoint[0]; + } + +} diff --git a/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/define/v36/readOperation/WrappedMapReduceReadOperationInstrumentation.java b/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/define/v36/readOperation/WrappedMapReduceReadOperationInstrumentation.java new file mode 100644 index 0000000000..c3fabf603e --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/define/v36/readOperation/WrappedMapReduceReadOperationInstrumentation.java @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.mongodb.v3.define.v36.readOperation; + +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.matcher.ElementMatcher; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine; +import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch; +import org.apache.skywalking.apm.agent.core.plugin.match.NameMatch; + +import static net.bytebuddy.matcher.ElementMatchers.any; + +public class WrappedMapReduceReadOperationInstrumentation extends ClassInstanceMethodsEnhancePluginDefine { + + private static final String ENHANCE_CLASS = "com.mongodb.client.internal.MapReduceIterableImpl$WrappedMapReduceReadOperation"; + + private static final String INTERCEPTOR_CLASS = "org.apache.skywalking.apm.plugin.mongodb.v3.interceptor.operation.WrappedMapReduceReadOperationInterceptor"; + + @Override + protected ClassMatch enhanceClass() { + return NameMatch.byName(ENHANCE_CLASS); + } + + @Override + public ConstructorInterceptPoint[] getConstructorsInterceptPoints() { + return new ConstructorInterceptPoint[] { + new ConstructorInterceptPoint() { + @Override + public ElementMatcher getConstructorMatcher() { + return any(); + } + + @Override + public String getConstructorInterceptor() { + return INTERCEPTOR_CLASS; + } + } + }; + } + + @Override + public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() { + return new InstanceMethodsInterceptPoint[0]; + } + +} diff --git a/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/define/writeOperation/AggregateToCollectionOperationInstrumentation.java b/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/define/writeOperation/AggregateToCollectionOperationInstrumentation.java new file mode 100644 index 0000000000..62bde0f7f8 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/define/writeOperation/AggregateToCollectionOperationInstrumentation.java @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.mongodb.v3.define.writeOperation; + +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.matcher.ElementMatcher; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine; +import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch; +import org.apache.skywalking.apm.agent.core.plugin.match.NameMatch; + +import static net.bytebuddy.matcher.ElementMatchers.takesArguments; + +public class AggregateToCollectionOperationInstrumentation extends ClassInstanceMethodsEnhancePluginDefine { + + private static final String ENHANCE_CLASS = "com.mongodb.operation.AggregateToCollectionOperation"; + + private static final String INTERCEPTOR_CLASS = "org.apache.skywalking.apm.plugin.mongodb.v3.interceptor.operation.OperationNamespaceConstructInterceptor"; + + @Override + protected ClassMatch enhanceClass() { + return NameMatch.byName(ENHANCE_CLASS); + } + + @Override + public ConstructorInterceptPoint[] getConstructorsInterceptPoints() { + return new ConstructorInterceptPoint[] { + new ConstructorInterceptPoint() { + @Override + public ElementMatcher getConstructorMatcher() { + return takesArguments(5); + } + + @Override + public String getConstructorInterceptor() { + return INTERCEPTOR_CLASS; + } + } + }; + } + + @Override + public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() { + return new InstanceMethodsInterceptPoint[0]; + } + +} diff --git a/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/define/writeOperation/BaseFindAndModifyOperationInstrumentation.java b/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/define/writeOperation/BaseFindAndModifyOperationInstrumentation.java new file mode 100644 index 0000000000..bb1c0445ee --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/define/writeOperation/BaseFindAndModifyOperationInstrumentation.java @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.mongodb.v3.define.writeOperation; + +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.matcher.ElementMatcher; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine; +import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch; +import org.apache.skywalking.apm.agent.core.plugin.match.NameMatch; + +import static net.bytebuddy.matcher.ElementMatchers.any; + +public class BaseFindAndModifyOperationInstrumentation extends ClassInstanceMethodsEnhancePluginDefine { + + private static final String ENHANCE_CLASS = "com.mongodb.operation.BaseFindAndModifyOperation"; + + private static final String INTERCEPTOR_CLASS = "org.apache.skywalking.apm.plugin.mongodb.v3.interceptor.operation.OperationNamespaceConstructInterceptor"; + + @Override + protected ClassMatch enhanceClass() { + return NameMatch.byName(ENHANCE_CLASS); + } + + @Override + public ConstructorInterceptPoint[] getConstructorsInterceptPoints() { + return new ConstructorInterceptPoint[] { + new ConstructorInterceptPoint() { + @Override + public ElementMatcher getConstructorMatcher() { + return any(); + } + + @Override + public String getConstructorInterceptor() { + return INTERCEPTOR_CLASS; + } + } + }; + } + + @Override + public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() { + return new InstanceMethodsInterceptPoint[0]; + } + +} diff --git a/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/define/writeOperation/BaseWriteOperationInstrumentation.java b/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/define/writeOperation/BaseWriteOperationInstrumentation.java new file mode 100644 index 0000000000..fdb6b8ffe3 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/define/writeOperation/BaseWriteOperationInstrumentation.java @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.mongodb.v3.define.writeOperation; + +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.matcher.ElementMatcher; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine; +import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch; +import org.apache.skywalking.apm.agent.core.plugin.match.NameMatch; + +import static net.bytebuddy.matcher.ElementMatchers.takesArguments; + +public class BaseWriteOperationInstrumentation extends ClassInstanceMethodsEnhancePluginDefine { + + private static final String ENHANCE_CLASS = "com.mongodb.operation.BaseWriteOperation"; + + private static final String INTERCEPTOR_CLASS = "org.apache.skywalking.apm.plugin.mongodb.v3.interceptor.operation.OperationNamespaceConstructInterceptor"; + + @Override + protected ClassMatch enhanceClass() { + return NameMatch.byName(ENHANCE_CLASS); + } + + @Override + public ConstructorInterceptPoint[] getConstructorsInterceptPoints() { + return new ConstructorInterceptPoint[] { + new ConstructorInterceptPoint() { + @Override + public ElementMatcher getConstructorMatcher() { + return takesArguments(4); + } + + @Override + public String getConstructorInterceptor() { + return INTERCEPTOR_CLASS; + } + } + }; + } + + @Override + public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() { + return new InstanceMethodsInterceptPoint[0]; + } + +} diff --git a/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/define/writeOperation/CommandWriteOperationInstrumentation.java b/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/define/writeOperation/CommandWriteOperationInstrumentation.java new file mode 100644 index 0000000000..77d3d4f165 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/define/writeOperation/CommandWriteOperationInstrumentation.java @@ -0,0 +1,63 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.mongodb.v3.define.writeOperation; + +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.matcher.ElementMatcher; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine; +import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch; +import org.apache.skywalking.apm.agent.core.plugin.match.NameMatch; + +import static net.bytebuddy.matcher.ElementMatchers.any; + +public class CommandWriteOperationInstrumentation extends ClassInstanceMethodsEnhancePluginDefine { + + private static final String ENHANCE_CLASS = "com.mongodb.operation.CommandWriteOperation"; + + private static final String INTERCEPTOR_CLASS = "org.apache.skywalking.apm.plugin.mongodb.v3.interceptor.operation.OperationDatabaseConstructInterceptor"; + + @Override + protected ClassMatch enhanceClass() { + return NameMatch.byName(ENHANCE_CLASS); + } + + @Override + public ConstructorInterceptPoint[] getConstructorsInterceptPoints() { + return new ConstructorInterceptPoint[] { + new ConstructorInterceptPoint() { + @Override + public ElementMatcher getConstructorMatcher() { + return any(); + } + + @Override + public String getConstructorInterceptor() { + return INTERCEPTOR_CLASS; + } + } + }; + } + + @Override + public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() { + return new InstanceMethodsInterceptPoint[0]; + } +} diff --git a/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/define/writeOperation/CreateCollectionOperationInstrumentation.java b/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/define/writeOperation/CreateCollectionOperationInstrumentation.java new file mode 100644 index 0000000000..a5f45f68e3 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/define/writeOperation/CreateCollectionOperationInstrumentation.java @@ -0,0 +1,63 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.mongodb.v3.define.writeOperation; + +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.matcher.ElementMatcher; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine; +import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch; +import org.apache.skywalking.apm.agent.core.plugin.match.NameMatch; + +import static net.bytebuddy.matcher.ElementMatchers.takesArguments; + +public class CreateCollectionOperationInstrumentation extends ClassInstanceMethodsEnhancePluginDefine { + + private static final String ENHANCE_CLASS = "com.mongodb.operation.CreateCollectionOperation"; + + private static final String INTERCEPTOR_CLASS = "org.apache.skywalking.apm.plugin.mongodb.v3.interceptor.operation.OperationDatabaseConstructInterceptor"; + + @Override + protected ClassMatch enhanceClass() { + return NameMatch.byName(ENHANCE_CLASS); + } + + @Override + public ConstructorInterceptPoint[] getConstructorsInterceptPoints() { + return new ConstructorInterceptPoint[] { + new ConstructorInterceptPoint() { + @Override + public ElementMatcher getConstructorMatcher() { + return takesArguments(3); + } + + @Override + public String getConstructorInterceptor() { + return INTERCEPTOR_CLASS; + } + } + }; + } + + @Override + public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() { + return new InstanceMethodsInterceptPoint[0]; + } +} diff --git a/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/define/writeOperation/CreateIndexesOperationInstrumentation.java b/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/define/writeOperation/CreateIndexesOperationInstrumentation.java new file mode 100644 index 0000000000..556205173a --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/define/writeOperation/CreateIndexesOperationInstrumentation.java @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.mongodb.v3.define.writeOperation; + +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.matcher.ElementMatcher; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine; +import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch; +import org.apache.skywalking.apm.agent.core.plugin.match.NameMatch; + +import static net.bytebuddy.matcher.ElementMatchers.takesArguments; + +public class CreateIndexesOperationInstrumentation extends ClassInstanceMethodsEnhancePluginDefine { + + private static final String ENHANCE_CLASS = "com.mongodb.operation.CreateIndexesOperation"; + + private static final String INTERCEPTOR_CLASS = "org.apache.skywalking.apm.plugin.mongodb.v3.interceptor.operation.OperationNamespaceConstructInterceptor"; + + @Override + protected ClassMatch enhanceClass() { + return NameMatch.byName(ENHANCE_CLASS); + } + + @Override + public ConstructorInterceptPoint[] getConstructorsInterceptPoints() { + return new ConstructorInterceptPoint[] { + new ConstructorInterceptPoint() { + @Override + public ElementMatcher getConstructorMatcher() { + return takesArguments(3); + } + + @Override + public String getConstructorInterceptor() { + return INTERCEPTOR_CLASS; + } + } + }; + } + + @Override + public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() { + return new InstanceMethodsInterceptPoint[0]; + } + +} diff --git a/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/define/writeOperation/CreateViewOperationInstrumentation.java b/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/define/writeOperation/CreateViewOperationInstrumentation.java new file mode 100644 index 0000000000..e16c434818 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/define/writeOperation/CreateViewOperationInstrumentation.java @@ -0,0 +1,63 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.mongodb.v3.define.writeOperation; + +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.matcher.ElementMatcher; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine; +import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch; +import org.apache.skywalking.apm.agent.core.plugin.match.NameMatch; + +import static net.bytebuddy.matcher.ElementMatchers.any; + +public class CreateViewOperationInstrumentation extends ClassInstanceMethodsEnhancePluginDefine { + + private static final String ENHANCE_CLASS = "com.mongodb.operation.CreateViewOperation"; + + private static final String INTERCEPTOR_CLASS = "org.apache.skywalking.apm.plugin.mongodb.v3.interceptor.operation.OperationDatabaseConstructInterceptor"; + + @Override + protected ClassMatch enhanceClass() { + return NameMatch.byName(ENHANCE_CLASS); + } + + @Override + public ConstructorInterceptPoint[] getConstructorsInterceptPoints() { + return new ConstructorInterceptPoint[] { + new ConstructorInterceptPoint() { + @Override + public ElementMatcher getConstructorMatcher() { + return any(); + } + + @Override + public String getConstructorInterceptor() { + return INTERCEPTOR_CLASS; + } + } + }; + } + + @Override + public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() { + return new InstanceMethodsInterceptPoint[0]; + } +} diff --git a/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/define/writeOperation/DropCollectionOperationInstrumentation.java b/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/define/writeOperation/DropCollectionOperationInstrumentation.java new file mode 100644 index 0000000000..88a04f6900 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/define/writeOperation/DropCollectionOperationInstrumentation.java @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.mongodb.v3.define.writeOperation; + +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.matcher.ElementMatcher; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine; +import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch; +import org.apache.skywalking.apm.agent.core.plugin.match.NameMatch; + +import static net.bytebuddy.matcher.ElementMatchers.takesArguments; + +public class DropCollectionOperationInstrumentation extends ClassInstanceMethodsEnhancePluginDefine { + + private static final String ENHANCE_CLASS = "com.mongodb.operation.DropCollectionOperation"; + + private static final String INTERCEPTOR_CLASS = "org.apache.skywalking.apm.plugin.mongodb.v3.interceptor.operation.OperationNamespaceConstructInterceptor"; + + @Override + protected ClassMatch enhanceClass() { + return NameMatch.byName(ENHANCE_CLASS); + } + + @Override + public ConstructorInterceptPoint[] getConstructorsInterceptPoints() { + return new ConstructorInterceptPoint[] { + new ConstructorInterceptPoint() { + @Override + public ElementMatcher getConstructorMatcher() { + return takesArguments(2); + } + + @Override + public String getConstructorInterceptor() { + return INTERCEPTOR_CLASS; + } + } + }; + } + + @Override + public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() { + return new InstanceMethodsInterceptPoint[0]; + } + +} \ No newline at end of file diff --git a/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/define/writeOperation/DropDatabaseOperationInstrumentation.java b/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/define/writeOperation/DropDatabaseOperationInstrumentation.java new file mode 100644 index 0000000000..88450fec86 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/define/writeOperation/DropDatabaseOperationInstrumentation.java @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.mongodb.v3.define.writeOperation; + +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.matcher.ElementMatcher; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine; +import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch; +import org.apache.skywalking.apm.agent.core.plugin.match.NameMatch; + +import static net.bytebuddy.matcher.ElementMatchers.takesArguments; + +public class DropDatabaseOperationInstrumentation extends ClassInstanceMethodsEnhancePluginDefine { + + private static final String ENHANCE_CLASS = "com.mongodb.operation.DropDatabaseOperation"; + + private static final String INTERCEPTOR_CLASS = "org.apache.skywalking.apm.plugin.mongodb.v3.interceptor.operation.OperationDatabaseConstructInterceptor"; + + @Override + protected ClassMatch enhanceClass() { + return NameMatch.byName(ENHANCE_CLASS); + } + + @Override + public ConstructorInterceptPoint[] getConstructorsInterceptPoints() { + return new ConstructorInterceptPoint[] { + new ConstructorInterceptPoint() { + @Override + public ElementMatcher getConstructorMatcher() { + return takesArguments(2); + } + + @Override + public String getConstructorInterceptor() { + return INTERCEPTOR_CLASS; + } + } + }; + } + + @Override + public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() { + return new InstanceMethodsInterceptPoint[0]; + } + +} \ No newline at end of file diff --git a/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/define/writeOperation/DropIndexOperationInstrumentation.java b/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/define/writeOperation/DropIndexOperationInstrumentation.java new file mode 100644 index 0000000000..8be077745b --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/define/writeOperation/DropIndexOperationInstrumentation.java @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.mongodb.v3.define.writeOperation; + +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.matcher.ElementMatcher; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine; +import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch; +import org.apache.skywalking.apm.agent.core.plugin.match.NameMatch; + +import static net.bytebuddy.matcher.ElementMatchers.takesArguments; + +public class DropIndexOperationInstrumentation extends ClassInstanceMethodsEnhancePluginDefine { + + private static final String ENHANCE_CLASS = "com.mongodb.operation.DropIndexOperation"; + + private static final String INTERCEPTOR_CLASS = "org.apache.skywalking.apm.plugin.mongodb.v3.interceptor.operation.OperationNamespaceConstructInterceptor"; + + @Override + protected ClassMatch enhanceClass() { + return NameMatch.byName(ENHANCE_CLASS); + } + + @Override + public ConstructorInterceptPoint[] getConstructorsInterceptPoints() { + return new ConstructorInterceptPoint[] { + new ConstructorInterceptPoint() { + @Override + public ElementMatcher getConstructorMatcher() { + return takesArguments(3); + } + + @Override + public String getConstructorInterceptor() { + return INTERCEPTOR_CLASS; + } + } + }; + } + + @Override + public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() { + return new InstanceMethodsInterceptPoint[0]; + } + +} diff --git a/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/define/writeOperation/MapReduceToCollectionOperationInstrumentation.java b/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/define/writeOperation/MapReduceToCollectionOperationInstrumentation.java new file mode 100644 index 0000000000..cc72bd315b --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/define/writeOperation/MapReduceToCollectionOperationInstrumentation.java @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.mongodb.v3.define.writeOperation; + +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.matcher.ElementMatcher; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine; +import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch; +import org.apache.skywalking.apm.agent.core.plugin.match.NameMatch; + +import static net.bytebuddy.matcher.ElementMatchers.takesArguments; + +public class MapReduceToCollectionOperationInstrumentation extends ClassInstanceMethodsEnhancePluginDefine { + + private static final String ENHANCE_CLASS = "com.mongodb.operation.MapReduceToCollectionOperation"; + + private static final String INTERCEPTOR_CLASS = "org.apache.skywalking.apm.plugin.mongodb.v3.interceptor.operation.OperationNamespaceConstructInterceptor"; + + @Override + protected ClassMatch enhanceClass() { + return NameMatch.byName(ENHANCE_CLASS); + } + + @Override + public ConstructorInterceptPoint[] getConstructorsInterceptPoints() { + return new ConstructorInterceptPoint[] { + new ConstructorInterceptPoint() { + @Override + public ElementMatcher getConstructorMatcher() { + return takesArguments(5); + } + + @Override + public String getConstructorInterceptor() { + return INTERCEPTOR_CLASS; + } + } + }; + } + + @Override + public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() { + return new InstanceMethodsInterceptPoint[0]; + } + +} \ No newline at end of file diff --git a/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/define/writeOperation/MixedBulkWriteOperationInstrumentation.java b/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/define/writeOperation/MixedBulkWriteOperationInstrumentation.java new file mode 100644 index 0000000000..44a90e3001 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/define/writeOperation/MixedBulkWriteOperationInstrumentation.java @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.mongodb.v3.define.writeOperation; + +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.matcher.ElementMatcher; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine; +import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch; +import org.apache.skywalking.apm.agent.core.plugin.match.NameMatch; + +import static net.bytebuddy.matcher.ElementMatchers.any; + +public class MixedBulkWriteOperationInstrumentation extends ClassInstanceMethodsEnhancePluginDefine { + + private static final String ENHANCE_CLASS = "com.mongodb.operation.MixedBulkWriteOperation"; + + private static final String INTERCEPTOR_CLASS = "org.apache.skywalking.apm.plugin.mongodb.v3.interceptor.operation.OperationNamespaceConstructInterceptor"; + + @Override + protected ClassMatch enhanceClass() { + return NameMatch.byName(ENHANCE_CLASS); + } + + @Override + public ConstructorInterceptPoint[] getConstructorsInterceptPoints() { + return new ConstructorInterceptPoint[] { + new ConstructorInterceptPoint() { + @Override + public ElementMatcher getConstructorMatcher() { + return any(); + } + + @Override + public String getConstructorInterceptor() { + return INTERCEPTOR_CLASS; + } + } + }; + } + + @Override + public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() { + return new InstanceMethodsInterceptPoint[0]; + } + +} \ No newline at end of file diff --git a/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/define/writeOperation/RenameCollectionOperationInstrumentation.java b/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/define/writeOperation/RenameCollectionOperationInstrumentation.java new file mode 100644 index 0000000000..95c9a4fdb9 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/define/writeOperation/RenameCollectionOperationInstrumentation.java @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.mongodb.v3.define.writeOperation; + +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.matcher.ElementMatcher; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine; +import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch; +import org.apache.skywalking.apm.agent.core.plugin.match.NameMatch; + +import static net.bytebuddy.matcher.ElementMatchers.takesArguments; + +public class RenameCollectionOperationInstrumentation extends ClassInstanceMethodsEnhancePluginDefine { + + private static final String ENHANCE_CLASS = "com.mongodb.operation.RenameCollectionOperation"; + + private static final String INTERCEPTOR_CLASS = "org.apache.skywalking.apm.plugin.mongodb.v3.interceptor.operation.OperationNamespaceConstructInterceptor"; + + @Override + protected ClassMatch enhanceClass() { + return NameMatch.byName(ENHANCE_CLASS); + } + + @Override + public ConstructorInterceptPoint[] getConstructorsInterceptPoints() { + return new ConstructorInterceptPoint[] { + new ConstructorInterceptPoint() { + @Override + public ElementMatcher getConstructorMatcher() { + return takesArguments(3); + } + + @Override + public String getConstructorInterceptor() { + return INTERCEPTOR_CLASS; + } + } + }; + } + + @Override + public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() { + return new InstanceMethodsInterceptPoint[0]; + } + +} \ No newline at end of file diff --git a/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/interceptor/operation/OperationDatabaseConstructInterceptor.java b/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/interceptor/operation/OperationDatabaseConstructInterceptor.java new file mode 100644 index 0000000000..a948de5128 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/interceptor/operation/OperationDatabaseConstructInterceptor.java @@ -0,0 +1,33 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.mongodb.v3.interceptor.operation; + +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceConstructorInterceptor; +import org.apache.skywalking.apm.plugin.mongodb.v3.support.MongoNamespaceInfo; + +public class OperationDatabaseConstructInterceptor implements InstanceConstructorInterceptor { + + @Override + public void onConstruct(EnhancedInstance objInst, Object[] allArguments) { + String databaseName = (String) allArguments[0]; + objInst.setSkyWalkingDynamicField(new MongoNamespaceInfo(databaseName)); + } + +} \ No newline at end of file diff --git a/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/interceptor/operation/OperationNamespaceConstructInterceptor.java b/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/interceptor/operation/OperationNamespaceConstructInterceptor.java new file mode 100644 index 0000000000..f6c97a6d37 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/interceptor/operation/OperationNamespaceConstructInterceptor.java @@ -0,0 +1,34 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.mongodb.v3.interceptor.operation; + +import com.mongodb.MongoNamespace; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceConstructorInterceptor; +import org.apache.skywalking.apm.plugin.mongodb.v3.support.MongoNamespaceInfo; + +public class OperationNamespaceConstructInterceptor implements InstanceConstructorInterceptor { + + @Override + public void onConstruct(EnhancedInstance objInst, Object[] allArguments) { + MongoNamespace mongoNamespace = (MongoNamespace) allArguments[0]; + objInst.setSkyWalkingDynamicField(new MongoNamespaceInfo(mongoNamespace)); + } + +} diff --git a/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/interceptor/operation/WrappedMapReduceReadOperationInterceptor.java b/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/interceptor/operation/WrappedMapReduceReadOperationInterceptor.java new file mode 100644 index 0000000000..3cb37c8cb5 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/interceptor/operation/WrappedMapReduceReadOperationInterceptor.java @@ -0,0 +1,32 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.mongodb.v3.interceptor.operation; + +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceConstructorInterceptor; + +public class WrappedMapReduceReadOperationInterceptor implements InstanceConstructorInterceptor { + + @Override + public void onConstruct(EnhancedInstance objInst, Object[] allArguments) { + EnhancedInstance enhancedInstance = (EnhancedInstance) allArguments[0]; + objInst.setSkyWalkingDynamicField(enhancedInstance.getSkyWalkingDynamicField()); + } + +} diff --git a/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/support/MongoNamespaceInfo.java b/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/support/MongoNamespaceInfo.java new file mode 100644 index 0000000000..16ba7f8aae --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/support/MongoNamespaceInfo.java @@ -0,0 +1,62 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.mongodb.v3.support; + +import com.mongodb.MongoNamespace; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import org.apache.skywalking.apm.util.StringUtil; + +@EqualsAndHashCode +@Getter +public class MongoNamespaceInfo { + + private final String databaseName; + private final String collectionName; + + public MongoNamespaceInfo(String databaseName) { + this(databaseName, null); + } + + public MongoNamespaceInfo(MongoNamespace mongoNamespace) { + this(mongoNamespace.getDatabaseName(), mongoNamespace.getCollectionName()); + } + + public MongoNamespaceInfo(String databaseName, String collectionName) { + this.databaseName = databaseName; + this.collectionName = collectionName; + } + + public String getDatabaseName() { + return databaseName; + } + + public String getCollectionName() { + return collectionName; + } + + public String toString() { + if (StringUtil.isNotBlank(collectionName)) { + return databaseName + '.' + collectionName; + } else { + return databaseName; + } + } + +} diff --git a/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/support/MongoOperationHelper.java b/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/support/MongoOperationHelper.java index 99ab80c051..d68d642ec0 100644 --- a/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/support/MongoOperationHelper.java +++ b/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/support/MongoOperationHelper.java @@ -22,6 +22,7 @@ import com.mongodb.bulk.InsertRequest; import com.mongodb.bulk.UpdateRequest; import com.mongodb.bulk.WriteRequest; +import com.mongodb.operation.AggregateOperation; import com.mongodb.operation.CountOperation; import com.mongodb.operation.CreateCollectionOperation; import com.mongodb.operation.CreateIndexesOperation; @@ -104,6 +105,9 @@ public static String getTraceParam(Object obj) { } else if (obj instanceof FindAndUpdateOperation) { BsonDocument filter = ((FindAndUpdateOperation) obj).getFilter(); return limitFilter(filter.toString()); + } else if (obj instanceof AggregateOperation) { + List pipelines = ((AggregateOperation) obj).getPipeline(); + return getPipelines(pipelines); } else if (obj instanceof MapReduceToCollectionOperation) { BsonDocument filter = ((MapReduceToCollectionOperation) obj).getFilter(); return limitFilter(filter.toString()); @@ -115,6 +119,18 @@ public static String getTraceParam(Object obj) { } } + private static String getPipelines(List pipelines) { + StringBuilder params = new StringBuilder(); + for (BsonDocument pipeline : pipelines) { + params.append(pipeline.toString()).append(","); + final int filterLengthLimit = MongoPluginConfig.Plugin.MongoDB.FILTER_LENGTH_LIMIT; + if (filterLengthLimit > 0 && params.length() > filterLengthLimit) { + return params.substring(0, filterLengthLimit) + "..."; + } + } + return params.toString(); + } + private static String getFilter(List writeRequestList) { StringBuilder params = new StringBuilder(); for (WriteRequest request : writeRequestList) { diff --git a/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/support/MongoSpanHelper.java b/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/support/MongoSpanHelper.java index 759abc4cd0..c7a88151a4 100644 --- a/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/support/MongoSpanHelper.java +++ b/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/support/MongoSpanHelper.java @@ -18,28 +18,48 @@ package org.apache.skywalking.apm.plugin.mongodb.v3.support; +import lombok.SneakyThrows; import org.apache.skywalking.apm.agent.core.context.ContextCarrier; import org.apache.skywalking.apm.agent.core.context.ContextManager; +import org.apache.skywalking.apm.agent.core.context.tag.AbstractTag; import org.apache.skywalking.apm.agent.core.context.tag.Tags; import org.apache.skywalking.apm.agent.core.context.trace.AbstractSpan; import org.apache.skywalking.apm.agent.core.context.trace.SpanLayer; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; import org.apache.skywalking.apm.network.trace.component.ComponentsDefine; import org.apache.skywalking.apm.plugin.mongodb.v3.MongoPluginConfig; +import org.apache.skywalking.apm.util.StringUtil; public class MongoSpanHelper { + private static final AbstractTag DB_COLLECTION_TAG = Tags.ofKey("db.collection"); + private MongoSpanHelper() { } + @SneakyThrows public static void createExitSpan(String executeMethod, String remotePeer, Object operation) { AbstractSpan span = ContextManager.createExitSpan( - MongoConstants.MONGO_DB_OP_PREFIX + executeMethod, new ContextCarrier(), remotePeer); + MongoConstants.MONGO_DB_OP_PREFIX + executeMethod, new ContextCarrier(), remotePeer); span.setComponent(ComponentsDefine.MONGO_DRIVER); Tags.DB_TYPE.set(span, MongoConstants.DB_TYPE); SpanLayer.asDB(span); + if (operation instanceof EnhancedInstance) { + MongoNamespaceInfo mongoNamespaceInfo = (MongoNamespaceInfo) ((EnhancedInstance) operation).getSkyWalkingDynamicField(); + if (mongoNamespaceInfo != null) { + if (StringUtil.isNotEmpty(mongoNamespaceInfo.getDatabaseName())) { + Tags.DB_INSTANCE.set(span, mongoNamespaceInfo.getDatabaseName()); + } + if (StringUtil.isNotEmpty(mongoNamespaceInfo.getCollectionName())) { + span.tag(DB_COLLECTION_TAG, mongoNamespaceInfo.getCollectionName()); + } + } + } + if (MongoPluginConfig.Plugin.MongoDB.TRACE_PARAM) { Tags.DB_BIND_VARIABLES.set(span, MongoOperationHelper.getTraceParam(operation)); } } + } diff --git a/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/resources/skywalking-plugin.def b/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/resources/skywalking-plugin.def index 8416fd8e52..8e6b05fdf1 100644 --- a/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/resources/skywalking-plugin.def +++ b/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/resources/skywalking-plugin.def @@ -19,8 +19,40 @@ mongodb-3.x=org.apache.skywalking.apm.plugin.mongodb.v3.define.v30.MongoDBInstru # v3.7.x~ mongodb-3.x=org.apache.skywalking.apm.plugin.mongodb.v3.define.v37.MongoDBClientDelegateInstrumentation mongodb-3.x=org.apache.skywalking.apm.plugin.mongodb.v3.define.v37.MongoDBOperationExecutorInstrumentation +mongodb-3.x=org.apache.skywalking.apm.plugin.mongodb.v3.define.v36.readOperation.WrappedMapReduceReadOperationInstrumentation # v3.8.x~ mongodb-3.x=org.apache.skywalking.apm.plugin.mongodb.v3.define.v38.MongoDBOperationExecutorInstrumentation # v3.6.x mongodb-3.x=org.apache.skywalking.apm.plugin.mongodb.v3.define.v36.MongoDBInstrumentation -mongodb-3.x=org.apache.skywalking.apm.plugin.mongodb.v3.define.v36.MongoDBOperationExecutorInstrumentation \ No newline at end of file +mongodb-3.x=org.apache.skywalking.apm.plugin.mongodb.v3.define.v36.MongoDBOperationExecutorInstrumentation + +# readOperation +mongodb-3.x=org.apache.skywalking.apm.plugin.mongodb.v3.define.readOperation.AggregateExplainOperationInstrumentation +mongodb-3.x=org.apache.skywalking.apm.plugin.mongodb.v3.define.readOperation.AggregateOperationImplInstrumentation +mongodb-3.x=org.apache.skywalking.apm.plugin.mongodb.v3.define.readOperation.AggregateOperationInstrumentation +mongodb-3.x=org.apache.skywalking.apm.plugin.mongodb.v3.define.readOperation.ChangeStreamOperationInstrumentation +mongodb-3.x=org.apache.skywalking.apm.plugin.mongodb.v3.define.readOperation.CommandReadOperationInstrumentation +mongodb-3.x=org.apache.skywalking.apm.plugin.mongodb.v3.define.readOperation.CountOperationInstrumentation +mongodb-3.x=org.apache.skywalking.apm.plugin.mongodb.v3.define.readOperation.DistinctOperationInstrumentation +mongodb-3.x=org.apache.skywalking.apm.plugin.mongodb.v3.define.readOperation.FindOperationInstrumentation +mongodb-3.x=org.apache.skywalking.apm.plugin.mongodb.v3.define.readOperation.ListCollectionsOperationInstrumentation +mongodb-3.x=org.apache.skywalking.apm.plugin.mongodb.v3.define.readOperation.ListIndexesOperationInstrumentation +mongodb-3.x=org.apache.skywalking.apm.plugin.mongodb.v3.define.readOperation.MapReduceWithInlineResultsOperationInstrumentation +mongodb-3.x=org.apache.skywalking.apm.plugin.mongodb.v3.define.readOperation.WrappedMapReduceReadOperationInstrumentation +mongodb-3.x=org.apache.skywalking.apm.plugin.mongodb.v3.define.readOperation.GroupOperationInstrumentation +mongodb-3.x=org.apache.skywalking.apm.plugin.mongodb.v3.define.readOperation.ParallelCollectionScanOperationInstrumentation +mongodb-3.x=org.apache.skywalking.apm.plugin.mongodb.v3.define.readOperation.UserExistsOperationInstrumentation +# writeOperation +mongodb-3.x=org.apache.skywalking.apm.plugin.mongodb.v3.define.writeOperation.AggregateToCollectionOperationInstrumentation +mongodb-3.x=org.apache.skywalking.apm.plugin.mongodb.v3.define.writeOperation.BaseFindAndModifyOperationInstrumentation +mongodb-3.x=org.apache.skywalking.apm.plugin.mongodb.v3.define.writeOperation.BaseWriteOperationInstrumentation +mongodb-3.x=org.apache.skywalking.apm.plugin.mongodb.v3.define.writeOperation.CommandWriteOperationInstrumentation +mongodb-3.x=org.apache.skywalking.apm.plugin.mongodb.v3.define.writeOperation.CreateCollectionOperationInstrumentation +mongodb-3.x=org.apache.skywalking.apm.plugin.mongodb.v3.define.writeOperation.CreateIndexesOperationInstrumentation +mongodb-3.x=org.apache.skywalking.apm.plugin.mongodb.v3.define.writeOperation.CreateViewOperationInstrumentation +mongodb-3.x=org.apache.skywalking.apm.plugin.mongodb.v3.define.writeOperation.DropCollectionOperationInstrumentation +mongodb-3.x=org.apache.skywalking.apm.plugin.mongodb.v3.define.writeOperation.DropDatabaseOperationInstrumentation +mongodb-3.x=org.apache.skywalking.apm.plugin.mongodb.v3.define.writeOperation.DropIndexOperationInstrumentation +mongodb-3.x=org.apache.skywalking.apm.plugin.mongodb.v3.define.writeOperation.MapReduceToCollectionOperationInstrumentation +mongodb-3.x=org.apache.skywalking.apm.plugin.mongodb.v3.define.writeOperation.MixedBulkWriteOperationInstrumentation +mongodb-3.x=org.apache.skywalking.apm.plugin.mongodb.v3.define.writeOperation.RenameCollectionOperationInstrumentation diff --git a/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/mongodb/v3/interceptor/v30/MongoDBInterceptorTest.java b/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/mongodb/v3/interceptor/v30/MongoDBInterceptorTest.java index 62d28a6c14..2319ad54e3 100644 --- a/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/mongodb/v3/interceptor/v30/MongoDBInterceptorTest.java +++ b/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/mongodb/v3/interceptor/v30/MongoDBInterceptorTest.java @@ -18,12 +18,10 @@ package org.apache.skywalking.apm.plugin.mongodb.v3.interceptor.v30; -import static org.hamcrest.CoreMatchers.is; -import static org.junit.Assert.assertThat; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; -import java.lang.reflect.Method; -import java.util.List; +import com.mongodb.Mongo; +import com.mongodb.MongoNamespace; +import com.mongodb.operation.AggregateOperation; +import com.mongodb.operation.FindOperation; import org.apache.skywalking.apm.agent.core.context.trace.AbstractTracingSpan; import org.apache.skywalking.apm.agent.core.context.trace.LogDataEntity; import org.apache.skywalking.apm.agent.core.context.trace.SpanLayer; @@ -38,6 +36,7 @@ import org.apache.skywalking.apm.agent.test.tools.SpanAssert; import org.apache.skywalking.apm.agent.test.tools.TracingSegmentRunner; import org.apache.skywalking.apm.plugin.mongodb.v3.MongoPluginConfig; +import org.apache.skywalking.apm.plugin.mongodb.v3.support.MongoNamespaceInfo; import org.bson.BsonDocument; import org.bson.BsonString; import org.bson.codecs.Decoder; @@ -48,11 +47,19 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; +import org.mockito.Mockito; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; -import com.mongodb.Mongo; -import com.mongodb.MongoNamespace; -import com.mongodb.operation.FindOperation; + +import java.lang.reflect.Method; +import java.util.Collections; +import java.util.List; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.startsWith; +import static org.junit.Assert.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; @RunWith(TracingSegmentRunner.class) public class MongoDBInterceptorTest { @@ -67,11 +74,16 @@ public class MongoDBInterceptorTest { private MongoDBInterceptor interceptor; + private FindOperation enhancedInstanceForFindOperation; + @Mock private EnhancedInstance enhancedInstance; private Object[] arguments; + private Class[] argumentTypes; + private Decoder decoder; + private MongoNamespace mongoNamespace; @SuppressWarnings({ "rawtypes", @@ -88,12 +100,14 @@ public void setUp() throws Exception { BsonDocument document = new BsonDocument(); document.append("name", new BsonString("by")); - MongoNamespace mongoNamespace = new MongoNamespace("test.user"); - Decoder decoder = mock(Decoder.class); + mongoNamespace = new MongoNamespace("test.user"); + decoder = mock(Decoder.class); FindOperation findOperation = new FindOperation(mongoNamespace, decoder); findOperation.filter(document); - - arguments = new Object[] {findOperation}; + enhancedInstanceForFindOperation = mock(FindOperation.class, Mockito.withSettings().extraInterfaces(EnhancedInstance.class)); + when(((EnhancedInstance) enhancedInstanceForFindOperation).getSkyWalkingDynamicField()).thenReturn(new MongoNamespaceInfo(mongoNamespace)); + when(enhancedInstanceForFindOperation.getFilter()).thenReturn(findOperation.getFilter()); + arguments = new Object[] {enhancedInstanceForFindOperation}; argumentTypes = new Class[] {findOperation.getClass()}; } @@ -105,7 +119,29 @@ public void testIntercept() throws Throwable { MatcherAssert.assertThat(segmentStorage.getTraceSegments().size(), is(1)); TraceSegment traceSegment = segmentStorage.getTraceSegments().get(0); List spans = SegmentHelper.getSpans(traceSegment); - assertRedisSpan(spans.get(0)); + assertFindOperationSpan(spans.get(0)); + } + + @Test + public void testAggregateOperationIntercept() throws Throwable { + MongoNamespace mongoNamespace = new MongoNamespace("test.user"); + BsonDocument matchStage = new BsonDocument("$match", new BsonDocument("name", new BsonString("by"))); + List pipeline = Collections.singletonList(matchStage); + AggregateOperation aggregateOperation = new AggregateOperation(mongoNamespace, pipeline, decoder); + + AggregateOperation enhancedInstanceForAggregateOperation = mock(AggregateOperation.class, Mockito.withSettings().extraInterfaces(EnhancedInstance.class)); + when(((EnhancedInstance) enhancedInstanceForAggregateOperation).getSkyWalkingDynamicField()).thenReturn(new MongoNamespaceInfo(mongoNamespace)); + when(enhancedInstanceForAggregateOperation.getPipeline()).thenReturn(aggregateOperation.getPipeline()); + Object[] arguments = {enhancedInstanceForAggregateOperation}; + Class[] argumentTypes = {aggregateOperation.getClass()}; + + interceptor.beforeMethod(enhancedInstance, getExecuteMethod(), arguments, argumentTypes, null); + interceptor.afterMethod(enhancedInstance, getExecuteMethod(), arguments, argumentTypes, null); + + MatcherAssert.assertThat(segmentStorage.getTraceSegments().size(), is(1)); + TraceSegment traceSegment = segmentStorage.getTraceSegments().get(0); + List spans = SegmentHelper.getSpans(traceSegment); + assertMongoAggregateOperationSpan(spans.get(0)); } @Test @@ -118,18 +154,32 @@ public void testInterceptWithException() throws Throwable { MatcherAssert.assertThat(segmentStorage.getTraceSegments().size(), is(1)); TraceSegment traceSegment = segmentStorage.getTraceSegments().get(0); List spans = SegmentHelper.getSpans(traceSegment); - assertRedisSpan(spans.get(0)); + assertFindOperationSpan(spans.get(0)); List logDataEntities = SpanHelper.getLogs(spans.get(0)); assertThat(logDataEntities.size(), is(1)); SpanAssert.assertException(logDataEntities.get(0), RuntimeException.class); } - private void assertRedisSpan(AbstractTracingSpan span) { - assertThat(span.getOperationName(), is("MongoDB/FindOperation")); + private void assertFindOperationSpan(AbstractTracingSpan span) { + assertThat(span.getOperationName(), startsWith("MongoDB/FindOperation")); + assertThat(SpanHelper.getComponentId(span), is(42)); + List tags = SpanHelper.getTags(span); + assertThat(tags.get(0).getValue(), is("MongoDB")); + assertThat(tags.get(1).getValue(), is("test")); + assertThat(tags.get(2).getValue(), is("user")); + assertThat(tags.get(3).getValue(), is("{\"name\": \"by\"}")); + assertThat(span.isExit(), is(true)); + assertThat(SpanHelper.getLayer(span), CoreMatchers.is(SpanLayer.DB)); + } + + private void assertMongoAggregateOperationSpan(AbstractTracingSpan span) { + assertThat(span.getOperationName(), startsWith("MongoDB/AggregateOperation")); assertThat(SpanHelper.getComponentId(span), is(42)); List tags = SpanHelper.getTags(span); - assertThat(tags.get(1).getValue(), is("{\"name\": \"by\"}")); assertThat(tags.get(0).getValue(), is("MongoDB")); + assertThat(tags.get(1).getValue(), is("test")); + assertThat(tags.get(2).getValue(), is("user")); + assertThat(tags.get(3).getValue(), is("{\"$match\": {\"name\": \"by\"}},")); assertThat(span.isExit(), is(true)); assertThat(SpanHelper.getLayer(span), CoreMatchers.is(SpanLayer.DB)); } diff --git a/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/mongodb/v3/interceptor/v37/MongoDBOperationExecutorInterceptorTest.java b/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/mongodb/v3/interceptor/v37/MongoDBOperationExecutorInterceptorTest.java index 60de9f5c21..8c6112e7fc 100644 --- a/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/mongodb/v3/interceptor/v37/MongoDBOperationExecutorInterceptorTest.java +++ b/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/mongodb/v3/interceptor/v37/MongoDBOperationExecutorInterceptorTest.java @@ -18,12 +18,13 @@ package org.apache.skywalking.apm.plugin.mongodb.v3.interceptor.v37; -import static org.hamcrest.CoreMatchers.is; -import static org.junit.Assert.assertThat; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; -import java.lang.reflect.Method; -import java.util.List; +import com.mongodb.MongoNamespace; +import com.mongodb.ReadConcern; +import com.mongodb.client.internal.OperationExecutor; +import com.mongodb.operation.AggregateOperation; +import com.mongodb.operation.CreateCollectionOperation; +import com.mongodb.operation.FindOperation; +import com.mongodb.operation.WriteOperation; import org.apache.skywalking.apm.agent.core.context.trace.AbstractTracingSpan; import org.apache.skywalking.apm.agent.core.context.trace.LogDataEntity; import org.apache.skywalking.apm.agent.core.context.trace.SpanLayer; @@ -38,6 +39,7 @@ import org.apache.skywalking.apm.agent.test.tools.SpanAssert; import org.apache.skywalking.apm.agent.test.tools.TracingSegmentRunner; import org.apache.skywalking.apm.plugin.mongodb.v3.MongoPluginConfig; +import org.apache.skywalking.apm.plugin.mongodb.v3.support.MongoNamespaceInfo; import org.bson.BsonDocument; import org.bson.BsonString; import org.bson.codecs.Decoder; @@ -48,13 +50,19 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; +import org.mockito.Mockito; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; -import com.mongodb.MongoNamespace; -import com.mongodb.ReadConcern; -import com.mongodb.client.internal.OperationExecutor; -import com.mongodb.operation.FindOperation; -import com.mongodb.operation.WriteOperation; + +import java.lang.reflect.Method; +import java.util.Collections; +import java.util.List; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.startsWith; +import static org.junit.Assert.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; @RunWith(TracingSegmentRunner.class) public class MongoDBOperationExecutorInterceptorTest { @@ -70,11 +78,15 @@ public class MongoDBOperationExecutorInterceptorTest { @Mock private EnhancedInstance enhancedInstance; + private FindOperation enhancedInstanceForFindOperation; + private MongoDBOperationExecutorInterceptor interceptor; private Object[] arguments; private Class[] argumentTypes; + private Decoder decoder; + private MongoNamespace mongoNamespace; @Before public void setUp() { @@ -87,12 +99,14 @@ public void setUp() { BsonDocument document = new BsonDocument(); document.append("name", new BsonString("by")); - MongoNamespace mongoNamespace = new MongoNamespace("test.user"); - Decoder decoder = mock(Decoder.class); + mongoNamespace = new MongoNamespace("test.user"); + decoder = mock(Decoder.class); FindOperation findOperation = new FindOperation(mongoNamespace, decoder); findOperation.filter(document); - - arguments = new Object[] {findOperation}; + enhancedInstanceForFindOperation = mock(FindOperation.class, Mockito.withSettings().extraInterfaces(EnhancedInstance.class)); + when(((EnhancedInstance) enhancedInstanceForFindOperation).getSkyWalkingDynamicField()).thenReturn(new MongoNamespaceInfo(mongoNamespace)); + when(enhancedInstanceForFindOperation.getFilter()).thenReturn(findOperation.getFilter()); + arguments = new Object[] {enhancedInstanceForFindOperation}; argumentTypes = new Class[] {findOperation.getClass()}; } @@ -104,11 +118,51 @@ public void testIntercept() throws Throwable { MatcherAssert.assertThat(segmentStorage.getTraceSegments().size(), is(1)); TraceSegment traceSegment = segmentStorage.getTraceSegments().get(0); List spans = SegmentHelper.getSpans(traceSegment); - assertRedisSpan(spans.get(0)); + assertMongoFindOperationSpan(spans.get(0)); } @Test - public void testInterceptWithException() throws Throwable { + public void testCreateCollectionOperationIntercept() throws Throwable { + CreateCollectionOperation createCollectionOperation = new CreateCollectionOperation("test", "user"); + CreateCollectionOperation enhancedInstanceForCreateCollectionOperation = mock(CreateCollectionOperation.class, Mockito.withSettings().extraInterfaces(EnhancedInstance.class)); + when(((EnhancedInstance) enhancedInstanceForCreateCollectionOperation).getSkyWalkingDynamicField()).thenReturn(new MongoNamespaceInfo(mongoNamespace)); + when(enhancedInstanceForCreateCollectionOperation.getCollectionName()).thenReturn("user"); + Object[] arguments = {enhancedInstanceForCreateCollectionOperation}; + Class[] argumentTypes = {createCollectionOperation.getClass()}; + + interceptor.beforeMethod(enhancedInstance, getMethod(), arguments, argumentTypes, null); + interceptor.afterMethod(enhancedInstance, getMethod(), arguments, argumentTypes, null); + + MatcherAssert.assertThat(segmentStorage.getTraceSegments().size(), is(1)); + TraceSegment traceSegment = segmentStorage.getTraceSegments().get(0); + List spans = SegmentHelper.getSpans(traceSegment); + assertMongoCreateCollectionOperationSpan(spans.get(0)); + } + + @Test + public void testAggregateOperationIntercept() throws Throwable { + MongoNamespace mongoNamespace = new MongoNamespace("test.user"); + BsonDocument matchStage = new BsonDocument("$match", new BsonDocument("name", new BsonString("by"))); + List pipeline = Collections.singletonList(matchStage); + AggregateOperation aggregateOperation = new AggregateOperation(mongoNamespace, pipeline, decoder); + + AggregateOperation enhancedInstanceForAggregateOperation = mock(AggregateOperation.class, Mockito.withSettings().extraInterfaces(EnhancedInstance.class)); + when(((EnhancedInstance) enhancedInstanceForAggregateOperation).getSkyWalkingDynamicField()).thenReturn(new MongoNamespaceInfo(mongoNamespace)); + when(enhancedInstanceForAggregateOperation.getPipeline()).thenReturn(aggregateOperation.getPipeline()); + Object[] arguments = {enhancedInstanceForAggregateOperation}; + Class[] argumentTypes = {aggregateOperation.getClass()}; + + interceptor.beforeMethod(enhancedInstance, getMethod(), arguments, argumentTypes, null); + interceptor.afterMethod(enhancedInstance, getMethod(), arguments, argumentTypes, null); + + MatcherAssert.assertThat(segmentStorage.getTraceSegments().size(), is(1)); + TraceSegment traceSegment = segmentStorage.getTraceSegments().get(0); + List spans = SegmentHelper.getSpans(traceSegment); + assertMongoAggregateOperationSpan(spans.get(0)); + } + + @Test + public void testInterceptFindOperationWithException() throws Throwable { interceptor.beforeMethod(enhancedInstance, getMethod(), arguments, argumentTypes, null); interceptor.handleMethodException( enhancedInstance, getMethod(), arguments, argumentTypes, new RuntimeException()); @@ -117,18 +171,44 @@ public void testInterceptWithException() throws Throwable { MatcherAssert.assertThat(segmentStorage.getTraceSegments().size(), is(1)); TraceSegment traceSegment = segmentStorage.getTraceSegments().get(0); List spans = SegmentHelper.getSpans(traceSegment); - assertRedisSpan(spans.get(0)); + assertMongoFindOperationSpan(spans.get(0)); List logDataEntities = SpanHelper.getLogs(spans.get(0)); assertThat(logDataEntities.size(), is(1)); SpanAssert.assertException(logDataEntities.get(0), RuntimeException.class); } - private void assertRedisSpan(AbstractTracingSpan span) { - assertThat(span.getOperationName(), is("MongoDB/FindOperation")); + private void assertMongoCreateCollectionOperationSpan(AbstractTracingSpan span) { + assertThat(span.getOperationName(), startsWith("MongoDB/CreateCollectionOperation")); + assertThat(SpanHelper.getComponentId(span), is(42)); + List tags = SpanHelper.getTags(span); + assertThat(tags.get(0).getValue(), is("MongoDB")); + assertThat(tags.get(1).getValue(), is("test")); + assertThat(tags.get(2).getValue(), is("user")); + assertThat(tags.get(3).getValue(), is("user")); + assertThat(span.isExit(), is(true)); + assertThat(SpanHelper.getLayer(span), CoreMatchers.is(SpanLayer.DB)); + } + + private void assertMongoAggregateOperationSpan(AbstractTracingSpan span) { + assertThat(span.getOperationName(), startsWith("MongoDB/AggregateOperation")); + assertThat(SpanHelper.getComponentId(span), is(42)); + List tags = SpanHelper.getTags(span); + assertThat(tags.get(0).getValue(), is("MongoDB")); + assertThat(tags.get(1).getValue(), is("test")); + assertThat(tags.get(2).getValue(), is("user")); + assertThat(tags.get(3).getValue(), is("{\"$match\": {\"name\": \"by\"}},")); + assertThat(span.isExit(), is(true)); + assertThat(SpanHelper.getLayer(span), CoreMatchers.is(SpanLayer.DB)); + } + + private void assertMongoFindOperationSpan(AbstractTracingSpan span) { + assertThat(span.getOperationName(), startsWith("MongoDB/FindOperation")); assertThat(SpanHelper.getComponentId(span), is(42)); List tags = SpanHelper.getTags(span); - assertThat(tags.get(1).getValue(), is("{\"name\": \"by\"}")); assertThat(tags.get(0).getValue(), is("MongoDB")); + assertThat(tags.get(1).getValue(), is("test")); + assertThat(tags.get(2).getValue(), is("user")); + assertThat(tags.get(3).getValue(), is("{\"name\": \"by\"}")); assertThat(span.isExit(), is(true)); assertThat(SpanHelper.getLayer(span), CoreMatchers.is(SpanLayer.DB)); } diff --git a/apm-sniffer/apm-sdk-plugin/mongodb-4.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/mongodb-4.x-plugin/pom.xml index 43c8648fd7..cb0363cb8e 100644 --- a/apm-sniffer/apm-sdk-plugin/mongodb-4.x-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/mongodb-4.x-plugin/pom.xml @@ -21,7 +21,7 @@ apm-sdk-plugin org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT apm-mongodb-4.x-plugin diff --git a/apm-sniffer/apm-sdk-plugin/mongodb-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v4/define/readOperation/AggregateOperationImplInstrumentation.java b/apm-sniffer/apm-sdk-plugin/mongodb-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v4/define/readOperation/AggregateOperationImplInstrumentation.java index 49cb53e085..1e50102399 100644 --- a/apm-sniffer/apm-sdk-plugin/mongodb-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v4/define/readOperation/AggregateOperationImplInstrumentation.java +++ b/apm-sniffer/apm-sdk-plugin/mongodb-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v4/define/readOperation/AggregateOperationImplInstrumentation.java @@ -26,7 +26,7 @@ import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch; import org.apache.skywalking.apm.agent.core.plugin.match.NameMatch; -import static net.bytebuddy.matcher.ElementMatchers.takesArguments; +import static net.bytebuddy.matcher.ElementMatchers.any; public class AggregateOperationImplInstrumentation extends ClassInstanceMethodsEnhancePluginDefine { @@ -53,7 +53,7 @@ public ConstructorInterceptPoint[] getConstructorsInterceptPoints() { @Override public ElementMatcher getConstructorMatcher() { - return takesArguments(4); + return any(); } @Override diff --git a/apm-sniffer/apm-sdk-plugin/mongodb-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v4/define/readOperation/AggregateOperationInstrumentation.java b/apm-sniffer/apm-sdk-plugin/mongodb-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v4/define/readOperation/AggregateOperationInstrumentation.java index b95124195b..6e56abd504 100644 --- a/apm-sniffer/apm-sdk-plugin/mongodb-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v4/define/readOperation/AggregateOperationInstrumentation.java +++ b/apm-sniffer/apm-sdk-plugin/mongodb-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v4/define/readOperation/AggregateOperationInstrumentation.java @@ -26,7 +26,7 @@ import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch; import org.apache.skywalking.apm.agent.core.plugin.match.NameMatch; -import static net.bytebuddy.matcher.ElementMatchers.takesArguments; +import static net.bytebuddy.matcher.ElementMatchers.any; public class AggregateOperationInstrumentation extends ClassInstanceMethodsEnhancePluginDefine { @@ -53,7 +53,7 @@ public ConstructorInterceptPoint[] getConstructorsInterceptPoints() { @Override public ElementMatcher getConstructorMatcher() { - return takesArguments(5); + return any(); } @Override diff --git a/apm-sniffer/apm-sdk-plugin/mongodb-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v4/interceptor/operation/OperationDatabaseConstructInterceptor.java b/apm-sniffer/apm-sdk-plugin/mongodb-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v4/interceptor/operation/OperationDatabaseConstructInterceptor.java index a643e3f201..850b625153 100644 --- a/apm-sniffer/apm-sdk-plugin/mongodb-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v4/interceptor/operation/OperationDatabaseConstructInterceptor.java +++ b/apm-sniffer/apm-sdk-plugin/mongodb-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v4/interceptor/operation/OperationDatabaseConstructInterceptor.java @@ -20,13 +20,14 @@ import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceConstructorInterceptor; +import org.apache.skywalking.apm.plugin.mongodb.v4.support.MongoNamespaceInfo; public class OperationDatabaseConstructInterceptor implements InstanceConstructorInterceptor { @Override public void onConstruct(EnhancedInstance objInst, Object[] allArguments) { String databaseName = (String) allArguments[0]; - objInst.setSkyWalkingDynamicField(databaseName); + objInst.setSkyWalkingDynamicField(new MongoNamespaceInfo(databaseName)); } } \ No newline at end of file diff --git a/apm-sniffer/apm-sdk-plugin/mongodb-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v4/interceptor/operation/OperationNamespaceConstructInterceptor.java b/apm-sniffer/apm-sdk-plugin/mongodb-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v4/interceptor/operation/OperationNamespaceConstructInterceptor.java index 6641012fbf..9a9fec49f0 100644 --- a/apm-sniffer/apm-sdk-plugin/mongodb-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v4/interceptor/operation/OperationNamespaceConstructInterceptor.java +++ b/apm-sniffer/apm-sdk-plugin/mongodb-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v4/interceptor/operation/OperationNamespaceConstructInterceptor.java @@ -21,14 +21,14 @@ import com.mongodb.MongoNamespace; import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceConstructorInterceptor; +import org.apache.skywalking.apm.plugin.mongodb.v4.support.MongoNamespaceInfo; public class OperationNamespaceConstructInterceptor implements InstanceConstructorInterceptor { @Override public void onConstruct(EnhancedInstance objInst, Object[] allArguments) { MongoNamespace mongoNamespace = (MongoNamespace) allArguments[0]; - String databaseName = mongoNamespace.getDatabaseName(); - objInst.setSkyWalkingDynamicField(databaseName); + objInst.setSkyWalkingDynamicField(new MongoNamespaceInfo(mongoNamespace)); } } diff --git a/apm-sniffer/apm-sdk-plugin/mongodb-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v4/interceptor/operation/WrappedMapReduceReadOperationInterceptor.java b/apm-sniffer/apm-sdk-plugin/mongodb-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v4/interceptor/operation/WrappedMapReduceReadOperationInterceptor.java index 39c4699ffb..90614d3fc7 100644 --- a/apm-sniffer/apm-sdk-plugin/mongodb-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v4/interceptor/operation/WrappedMapReduceReadOperationInterceptor.java +++ b/apm-sniffer/apm-sdk-plugin/mongodb-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v4/interceptor/operation/WrappedMapReduceReadOperationInterceptor.java @@ -25,11 +25,8 @@ public class WrappedMapReduceReadOperationInterceptor implements InstanceConstru @Override public void onConstruct(EnhancedInstance objInst, Object[] allArguments) { - if (allArguments[0] instanceof EnhancedInstance) { EnhancedInstance enhancedInstance = (EnhancedInstance) allArguments[0]; - String databaseName = (String) enhancedInstance.getSkyWalkingDynamicField(); - objInst.setSkyWalkingDynamicField(databaseName); - } + objInst.setSkyWalkingDynamicField(enhancedInstance.getSkyWalkingDynamicField()); } } diff --git a/apm-sniffer/apm-sdk-plugin/mongodb-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v4/support/MongoNamespaceInfo.java b/apm-sniffer/apm-sdk-plugin/mongodb-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v4/support/MongoNamespaceInfo.java new file mode 100644 index 0000000000..54d54b4d8a --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/mongodb-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v4/support/MongoNamespaceInfo.java @@ -0,0 +1,62 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.mongodb.v4.support; + +import com.mongodb.MongoNamespace; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import org.apache.skywalking.apm.util.StringUtil; + +@EqualsAndHashCode +@Getter +public class MongoNamespaceInfo { + + private final String databaseName; + private final String collectionName; + + public MongoNamespaceInfo(String databaseName) { + this(databaseName, null); + } + + public MongoNamespaceInfo(MongoNamespace mongoNamespace) { + this(mongoNamespace.getDatabaseName(), mongoNamespace.getCollectionName()); + } + + public MongoNamespaceInfo(String databaseName, String collectionName) { + this.databaseName = databaseName; + this.collectionName = collectionName; + } + + public String getDatabaseName() { + return databaseName; + } + + public String getCollectionName() { + return collectionName; + } + + public String toString() { + if (StringUtil.isNotBlank(collectionName)) { + return databaseName + '.' + collectionName; + } else { + return databaseName; + } + } + +} diff --git a/apm-sniffer/apm-sdk-plugin/mongodb-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v4/support/MongoOperationHelper.java b/apm-sniffer/apm-sdk-plugin/mongodb-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v4/support/MongoOperationHelper.java index 45d0481fbc..8a561cc887 100644 --- a/apm-sniffer/apm-sdk-plugin/mongodb-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v4/support/MongoOperationHelper.java +++ b/apm-sniffer/apm-sdk-plugin/mongodb-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v4/support/MongoOperationHelper.java @@ -22,6 +22,7 @@ import com.mongodb.internal.bulk.InsertRequest; import com.mongodb.internal.bulk.UpdateRequest; import com.mongodb.internal.bulk.WriteRequest; +import com.mongodb.internal.operation.AggregateOperation; import com.mongodb.internal.operation.CountOperation; import com.mongodb.internal.operation.CreateCollectionOperation; import com.mongodb.internal.operation.CreateIndexesOperation; @@ -101,6 +102,9 @@ public static String getTraceParam(Object obj) { } else if (obj instanceof FindAndUpdateOperation) { BsonDocument filter = ((FindAndUpdateOperation) obj).getFilter(); return limitFilter(filter.toString()); + } else if (obj instanceof AggregateOperation) { + List pipelines = ((AggregateOperation) obj).getPipeline(); + return getPipelines(pipelines); } else if (obj instanceof MapReduceToCollectionOperation) { BsonDocument filter = ((MapReduceToCollectionOperation) obj).getFilter(); return limitFilter(filter.toString()); @@ -112,6 +116,18 @@ public static String getTraceParam(Object obj) { } } + private static String getPipelines(List pipelines) { + StringBuilder params = new StringBuilder(); + for (BsonDocument pipeline : pipelines) { + params.append(pipeline.toString()).append(","); + final int filterLengthLimit = MongoPluginConfig.Plugin.MongoDB.FILTER_LENGTH_LIMIT; + if (filterLengthLimit > 0 && params.length() > filterLengthLimit) { + return params.substring(0, filterLengthLimit) + "..."; + } + } + return params.toString(); + } + private static String getFilter(List writeRequestList) { StringBuilder params = new StringBuilder(); for (WriteRequest request : writeRequestList) { diff --git a/apm-sniffer/apm-sdk-plugin/mongodb-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v4/support/MongoSpanHelper.java b/apm-sniffer/apm-sdk-plugin/mongodb-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v4/support/MongoSpanHelper.java index 4f81a0f058..d87b66a825 100644 --- a/apm-sniffer/apm-sdk-plugin/mongodb-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v4/support/MongoSpanHelper.java +++ b/apm-sniffer/apm-sdk-plugin/mongodb-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v4/support/MongoSpanHelper.java @@ -20,34 +20,44 @@ import org.apache.skywalking.apm.agent.core.context.ContextCarrier; import org.apache.skywalking.apm.agent.core.context.ContextManager; +import org.apache.skywalking.apm.agent.core.context.tag.AbstractTag; import org.apache.skywalking.apm.agent.core.context.tag.Tags; import org.apache.skywalking.apm.agent.core.context.trace.AbstractSpan; import org.apache.skywalking.apm.agent.core.context.trace.SpanLayer; import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; import org.apache.skywalking.apm.network.trace.component.ComponentsDefine; +import org.apache.skywalking.apm.util.StringUtil; public class MongoSpanHelper { + private static final AbstractTag DB_COLLECTION_TAG = Tags.ofKey("db.collection"); + private MongoSpanHelper() { } /** * createExitSpan + * * @param executeMethod executeMethod - * @param remotePeer remotePeer - * @param operation operation + * @param remotePeer remotePeer + * @param operation operation */ public static void createExitSpan(String executeMethod, String remotePeer, Object operation) { AbstractSpan span = ContextManager.createExitSpan( - MongoConstants.MONGO_DB_OP_PREFIX + executeMethod, new ContextCarrier(), remotePeer); + MongoConstants.MONGO_DB_OP_PREFIX + executeMethod, new ContextCarrier(), remotePeer); span.setComponent(ComponentsDefine.MONGO_DRIVER); Tags.DB_TYPE.set(span, MongoConstants.DB_TYPE); SpanLayer.asDB(span); if (operation instanceof EnhancedInstance) { - Object databaseName = ((EnhancedInstance) operation).getSkyWalkingDynamicField(); - if (databaseName != null) { - Tags.DB_INSTANCE.set(span, (String) databaseName); + MongoNamespaceInfo mongoNamespaceInfo = (MongoNamespaceInfo) ((EnhancedInstance) operation).getSkyWalkingDynamicField(); + if (mongoNamespaceInfo != null) { + if (StringUtil.isNotEmpty(mongoNamespaceInfo.getDatabaseName())) { + Tags.DB_INSTANCE.set(span, mongoNamespaceInfo.getDatabaseName()); + } + if (StringUtil.isNotEmpty(mongoNamespaceInfo.getCollectionName())) { + span.tag(DB_COLLECTION_TAG, mongoNamespaceInfo.getCollectionName()); + } } } @@ -56,3 +66,4 @@ public static void createExitSpan(String executeMethod, String remotePeer, Objec } } } + diff --git a/apm-sniffer/apm-sdk-plugin/mongodb-4.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/mongodb/v4/MongoDBOperationExecutorInterceptorTest.java b/apm-sniffer/apm-sdk-plugin/mongodb-4.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/mongodb/v4/MongoDBOperationExecutorInterceptorTest.java index ed2154056e..4bb4b1213b 100644 --- a/apm-sniffer/apm-sdk-plugin/mongodb-4.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/mongodb/v4/MongoDBOperationExecutorInterceptorTest.java +++ b/apm-sniffer/apm-sdk-plugin/mongodb-4.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/mongodb/v4/MongoDBOperationExecutorInterceptorTest.java @@ -18,12 +18,13 @@ package org.apache.skywalking.apm.plugin.mongodb.v4; -import static org.hamcrest.CoreMatchers.is; -import static org.junit.Assert.assertThat; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; -import java.lang.reflect.Method; -import java.util.List; +import com.mongodb.MongoNamespace; +import com.mongodb.ReadConcern; +import com.mongodb.client.internal.OperationExecutor; +import com.mongodb.internal.operation.AggregateOperation; +import com.mongodb.internal.operation.CreateCollectionOperation; +import com.mongodb.internal.operation.FindOperation; +import com.mongodb.internal.operation.WriteOperation; import org.apache.skywalking.apm.agent.core.context.trace.AbstractTracingSpan; import org.apache.skywalking.apm.agent.core.context.trace.LogDataEntity; import org.apache.skywalking.apm.agent.core.context.trace.SpanLayer; @@ -38,24 +39,34 @@ import org.apache.skywalking.apm.agent.test.tools.SpanAssert; import org.apache.skywalking.apm.agent.test.tools.TracingSegmentRunner; import org.apache.skywalking.apm.plugin.mongodb.v4.interceptor.MongoDBOperationExecutorInterceptor; +import org.apache.skywalking.apm.plugin.mongodb.v4.support.MongoNamespaceInfo; +import org.apache.skywalking.apm.plugin.mongodb.v4.interceptor.operation.OperationNamespaceConstructInterceptor; import org.apache.skywalking.apm.plugin.mongodb.v4.support.MongoPluginConfig; import org.bson.BsonDocument; import org.bson.BsonString; import org.bson.codecs.Decoder; import org.hamcrest.CoreMatchers; import org.hamcrest.MatcherAssert; +import org.hamcrest.core.Is; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.Spy; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; -import com.mongodb.MongoNamespace; -import com.mongodb.ReadConcern; -import com.mongodb.client.internal.OperationExecutor; -import com.mongodb.internal.operation.FindOperation; -import com.mongodb.internal.operation.WriteOperation; + +import java.lang.reflect.Method; +import java.util.Collections; +import java.util.List; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.startsWith; +import static org.junit.Assert.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; @RunWith(TracingSegmentRunner.class) public class MongoDBOperationExecutorInterceptorTest { @@ -71,32 +82,85 @@ public class MongoDBOperationExecutorInterceptorTest { @Mock private EnhancedInstance enhancedInstance; + private FindOperation enhancedInstanceForFindOperation; + + @Spy + private EnhancedInstance enhancedObjInstance = new EnhancedInstance() { + private MongoNamespaceInfo namespace; + + @Override + public Object getSkyWalkingDynamicField() { + return namespace; + } + + @Override + public void setSkyWalkingDynamicField(Object value) { + this.namespace = (MongoNamespaceInfo) value; + } + }; + private MongoDBOperationExecutorInterceptor interceptor; + private OperationNamespaceConstructInterceptor constructInterceptor; + private Object[] arguments; private Class[] argumentTypes; + private Decoder decoder; + + private MongoNamespace mongoNamespace; + + private MongoNamespaceInfo mongoNamespaceInfo; + @Before public void setUp() { interceptor = new MongoDBOperationExecutorInterceptor(); - + constructInterceptor = new OperationNamespaceConstructInterceptor(); + enhancedInstanceForFindOperation = mock(FindOperation.class, Mockito.withSettings().extraInterfaces(EnhancedInstance.class)); MongoPluginConfig.Plugin.MongoDB.TRACE_PARAM = true; - - when(enhancedInstance.getSkyWalkingDynamicField()).thenReturn("127.0.0.1:27017"); - + decoder = mock(Decoder.class); + mongoNamespace = new MongoNamespace("test.user"); + mongoNamespaceInfo = new MongoNamespaceInfo(mongoNamespace); BsonDocument document = new BsonDocument(); document.append("name", new BsonString("by")); - MongoNamespace mongoNamespace = new MongoNamespace("test.user"); - Decoder decoder = mock(Decoder.class); + FindOperation findOperation = new FindOperation(mongoNamespace, decoder); findOperation.filter(document); - - arguments = new Object[] {findOperation}; + decoder = mock(Decoder.class); + when(enhancedInstance.getSkyWalkingDynamicField()).thenReturn("127.0.0.1:27017"); + enhancedInstanceForFindOperation = mock(FindOperation.class, Mockito.withSettings().extraInterfaces(EnhancedInstance.class)); + when(((EnhancedInstance) enhancedInstanceForFindOperation).getSkyWalkingDynamicField()).thenReturn(mongoNamespaceInfo); + when(enhancedInstanceForFindOperation.getFilter()).thenReturn(findOperation.getFilter()); + arguments = new Object[] {enhancedInstanceForFindOperation}; argumentTypes = new Class[] {findOperation.getClass()}; } + @Test + public void testConstructIntercept() throws Throwable { + constructInterceptor.onConstruct(enhancedObjInstance, new Object[]{mongoNamespace}); + MatcherAssert.assertThat(enhancedObjInstance.getSkyWalkingDynamicField(), Is.is(new MongoNamespaceInfo(mongoNamespace))); + } + + @Test + public void testCreateCollectionOperationIntercept() throws Throwable { + CreateCollectionOperation createCollectionOperation = new CreateCollectionOperation("test", "user"); + CreateCollectionOperation enhancedInstanceForCreateCollectionOperation = mock(CreateCollectionOperation.class, Mockito.withSettings().extraInterfaces(EnhancedInstance.class)); + when(((EnhancedInstance) enhancedInstanceForCreateCollectionOperation).getSkyWalkingDynamicField()).thenReturn(new MongoNamespaceInfo("test")); + when(enhancedInstanceForCreateCollectionOperation.getCollectionName()).thenReturn("user"); + + Object[] arguments = {enhancedInstanceForCreateCollectionOperation}; + Class[] argumentTypes = {createCollectionOperation.getClass()}; + interceptor.beforeMethod(enhancedInstance, getMethod(), arguments, argumentTypes, null); + interceptor.afterMethod(enhancedInstance, getMethod(), arguments, argumentTypes, null); + + MatcherAssert.assertThat(segmentStorage.getTraceSegments().size(), is(1)); + TraceSegment traceSegment = segmentStorage.getTraceSegments().get(0); + List spans = SegmentHelper.getSpans(traceSegment); + assertMongoCreateCollectionOperationSpan(spans.get(0)); + } + @Test public void testIntercept() throws Throwable { interceptor.beforeMethod(enhancedInstance, getMethod(), arguments, argumentTypes, null); @@ -105,31 +169,77 @@ public void testIntercept() throws Throwable { MatcherAssert.assertThat(segmentStorage.getTraceSegments().size(), is(1)); TraceSegment traceSegment = segmentStorage.getTraceSegments().get(0); List spans = SegmentHelper.getSpans(traceSegment); - assertMongoSpan(spans.get(0)); + assertMongoFindOperationSpan(spans.get(0)); + } + + @Test + public void testAggregateOperationIntercept() throws Throwable { + MongoNamespace mongoNamespace = new MongoNamespace("test.user"); + BsonDocument matchStage = new BsonDocument("$match", new BsonDocument("name", new BsonString("by"))); + List pipeline = Collections.singletonList(matchStage); + AggregateOperation aggregateOperation = new AggregateOperation(mongoNamespace, pipeline, decoder); + + AggregateOperation enhancedInstanceForAggregateOperation = mock(AggregateOperation.class, Mockito.withSettings().extraInterfaces(EnhancedInstance.class)); + when(((EnhancedInstance) enhancedInstanceForAggregateOperation).getSkyWalkingDynamicField()).thenReturn(new MongoNamespaceInfo(mongoNamespace)); + when(enhancedInstanceForAggregateOperation.getPipeline()).thenReturn(aggregateOperation.getPipeline()); + Object[] arguments = {enhancedInstanceForAggregateOperation}; + Class[] argumentTypes = {aggregateOperation.getClass()}; + + interceptor.beforeMethod(enhancedInstance, getMethod(), arguments, argumentTypes, null); + interceptor.afterMethod(enhancedInstance, getMethod(), arguments, argumentTypes, null); + + MatcherAssert.assertThat(segmentStorage.getTraceSegments().size(), is(1)); + TraceSegment traceSegment = segmentStorage.getTraceSegments().get(0); + List spans = SegmentHelper.getSpans(traceSegment); + assertMongoAggregateOperationSpan(spans.get(0)); } @Test public void testInterceptWithException() throws Throwable { interceptor.beforeMethod(enhancedInstance, getMethod(), arguments, argumentTypes, null); - interceptor.handleMethodException( - enhancedInstance, getMethod(), arguments, argumentTypes, new RuntimeException()); + interceptor.handleMethodException(enhancedInstance, getMethod(), arguments, argumentTypes, new RuntimeException()); interceptor.afterMethod(enhancedInstance, getMethod(), arguments, argumentTypes, null); MatcherAssert.assertThat(segmentStorage.getTraceSegments().size(), is(1)); TraceSegment traceSegment = segmentStorage.getTraceSegments().get(0); List spans = SegmentHelper.getSpans(traceSegment); - assertMongoSpan(spans.get(0)); + assertMongoFindOperationSpan(spans.get(0)); List logDataEntities = SpanHelper.getLogs(spans.get(0)); assertThat(logDataEntities.size(), is(1)); SpanAssert.assertException(logDataEntities.get(0), RuntimeException.class); } - private void assertMongoSpan(AbstractTracingSpan span) { - assertThat(span.getOperationName(), is("MongoDB/FindOperation")); + private void assertMongoFindOperationSpan(AbstractTracingSpan span) { + assertThat(span.getOperationName(), startsWith("MongoDB/FindOperation")); + assertThat(SpanHelper.getComponentId(span), is(42)); + List tags = SpanHelper.getTags(span); + assertThat(tags.get(0).getValue(), is("MongoDB")); + assertThat(tags.get(1).getValue(), is("test")); + assertThat(tags.get(2).getValue(), is("user")); + assertThat(tags.get(3).getValue(), is("{\"name\": \"by\"}")); + assertThat(span.isExit(), is(true)); + assertThat(SpanHelper.getLayer(span), CoreMatchers.is(SpanLayer.DB)); + } + + private void assertMongoCreateCollectionOperationSpan(AbstractTracingSpan span) { + assertThat(span.getOperationName(), startsWith("MongoDB/CreateCollectionOperation")); + assertThat(SpanHelper.getComponentId(span), is(42)); + List tags = SpanHelper.getTags(span); + assertThat(tags.get(0).getValue(), is("MongoDB")); + assertThat(tags.get(1).getValue(), is("test")); + assertThat(tags.get(2).getValue(), is("user")); + assertThat(span.isExit(), is(true)); + assertThat(SpanHelper.getLayer(span), CoreMatchers.is(SpanLayer.DB)); + } + + private void assertMongoAggregateOperationSpan(AbstractTracingSpan span) { + assertThat(span.getOperationName(), startsWith("MongoDB/AggregateOperation")); assertThat(SpanHelper.getComponentId(span), is(42)); List tags = SpanHelper.getTags(span); - assertThat(tags.get(1).getValue(), is("{\"name\": \"by\"}")); assertThat(tags.get(0).getValue(), is("MongoDB")); + assertThat(tags.get(1).getValue(), is("test")); + assertThat(tags.get(2).getValue(), is("user")); + assertThat(tags.get(3).getValue(), is("{\"$match\": {\"name\": \"by\"}},")); assertThat(span.isExit(), is(true)); assertThat(SpanHelper.getLayer(span), CoreMatchers.is(SpanLayer.DB)); } diff --git a/apm-sniffer/apm-sdk-plugin/motan-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/motan-plugin/pom.xml index 3c57658c57..b74bc6d54d 100644 --- a/apm-sniffer/apm-sdk-plugin/motan-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/motan-plugin/pom.xml @@ -20,7 +20,7 @@ apm-sdk-plugin org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-sdk-plugin/mssql-commons/pom.xml b/apm-sniffer/apm-sdk-plugin/mssql-commons/pom.xml index a5c6aadfec..ff9f083f6d 100644 --- a/apm-sniffer/apm-sdk-plugin/mssql-commons/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/mssql-commons/pom.xml @@ -20,7 +20,7 @@ apm-sdk-plugin org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-sdk-plugin/mssql-jdbc-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/mssql-jdbc-plugin/pom.xml index 0802c98edc..ff678a1a00 100644 --- a/apm-sniffer/apm-sdk-plugin/mssql-jdbc-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/mssql-jdbc-plugin/pom.xml @@ -20,7 +20,7 @@ apm-sdk-plugin org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-sdk-plugin/mssql-jtds-1.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/mssql-jtds-1.x-plugin/pom.xml index d9845d6ff1..2f3dd90ea6 100644 --- a/apm-sniffer/apm-sdk-plugin/mssql-jtds-1.x-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/mssql-jtds-1.x-plugin/pom.xml @@ -20,7 +20,7 @@ apm-sdk-plugin org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-sdk-plugin/mysql-5.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/mysql-5.x-plugin/pom.xml index 01fd51a514..361f5bc821 100755 --- a/apm-sniffer/apm-sdk-plugin/mysql-5.x-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/mysql-5.x-plugin/pom.xml @@ -20,7 +20,7 @@ apm-sdk-plugin org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-sdk-plugin/mysql-6.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/mysql-6.x-plugin/pom.xml index fb21d0faff..edaef2cf28 100755 --- a/apm-sniffer/apm-sdk-plugin/mysql-6.x-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/mysql-6.x-plugin/pom.xml @@ -20,7 +20,7 @@ apm-sdk-plugin org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-sdk-plugin/mysql-8.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/mysql-8.x-plugin/pom.xml index 7579e67444..11ee950a57 100755 --- a/apm-sniffer/apm-sdk-plugin/mysql-8.x-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/mysql-8.x-plugin/pom.xml @@ -20,7 +20,7 @@ apm-sdk-plugin org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-sdk-plugin/mysql-common/pom.xml b/apm-sniffer/apm-sdk-plugin/mysql-common/pom.xml index 6aa80d8210..92aa8ee5d8 100755 --- a/apm-sniffer/apm-sdk-plugin/mysql-common/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/mysql-common/pom.xml @@ -20,7 +20,7 @@ apm-sdk-plugin org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-sdk-plugin/mysql-common/src/main/java/org/apache/skywalking/apm/plugin/jdbc/mysql/StatementExecuteMethodsInterceptor.java b/apm-sniffer/apm-sdk-plugin/mysql-common/src/main/java/org/apache/skywalking/apm/plugin/jdbc/mysql/StatementExecuteMethodsInterceptor.java index 8895e118a5..0c2f5047b9 100644 --- a/apm-sniffer/apm-sdk-plugin/mysql-common/src/main/java/org/apache/skywalking/apm/plugin/jdbc/mysql/StatementExecuteMethodsInterceptor.java +++ b/apm-sniffer/apm-sdk-plugin/mysql-common/src/main/java/org/apache/skywalking/apm/plugin/jdbc/mysql/StatementExecuteMethodsInterceptor.java @@ -28,6 +28,7 @@ import org.apache.skywalking.apm.plugin.jdbc.SqlBodyUtil; import org.apache.skywalking.apm.plugin.jdbc.define.StatementEnhanceInfos; import org.apache.skywalking.apm.plugin.jdbc.trace.ConnectionInfo; +import org.apache.skywalking.apm.util.StringUtil; import java.lang.reflect.Method; @@ -52,14 +53,17 @@ public final void beforeMethod(EnhancedInstance objInst, Method method, Object[] Tags.DB_INSTANCE.set(span, connectInfo.getDatabaseName()); /** - * The first argument of all intercept method in `com.mysql.jdbc.StatementImpl` class is SQL, except the - * `executeBatch` method that the jdbc plugin need to trace, because of this method argument size is zero. + * Except for the `executeBatch` method, the first parameter of all enhanced methods in `com.mysql.jdbc.StatementImpl` is the SQL statement. + * Therefore, executeBatch will attempt to obtain the SQL from `cacheObject`. */ String sql = ""; if (allArguments.length > 0) { sql = (String) allArguments[0]; sql = SqlBodyUtil.limitSqlBodySize(sql); + } else if (StringUtil.isNotBlank(cacheObject.getSql())) { + sql = SqlBodyUtil.limitSqlBodySize(cacheObject.getSql()); } + Tags.DB_STATEMENT.set(span, sql); span.setComponent(connectInfo.getComponent()); diff --git a/apm-sniffer/apm-sdk-plugin/mysql-common/src/test/java/org/apache/skywalking/apm/plugin/jdbc/mysql/StatementExecuteMethodsInterceptorTest.java b/apm-sniffer/apm-sdk-plugin/mysql-common/src/test/java/org/apache/skywalking/apm/plugin/jdbc/mysql/StatementExecuteMethodsInterceptorTest.java index e3ceaa6228..df2769b8cd 100644 --- a/apm-sniffer/apm-sdk-plugin/mysql-common/src/test/java/org/apache/skywalking/apm/plugin/jdbc/mysql/StatementExecuteMethodsInterceptorTest.java +++ b/apm-sniffer/apm-sdk-plugin/mysql-common/src/test/java/org/apache/skywalking/apm/plugin/jdbc/mysql/StatementExecuteMethodsInterceptorTest.java @@ -72,7 +72,8 @@ public void setUp() { JDBCPluginConfig.Plugin.JDBC.SQL_BODY_MAX_LENGTH = 2048; serviceMethodInterceptor = new StatementExecuteMethodsInterceptor(); - enhanceRequireCacheObject = new StatementEnhanceInfos(connectionInfo, "SELECT * FROM test", "CallableStatement"); + enhanceRequireCacheObject = new StatementEnhanceInfos(connectionInfo, SQL, "CallableStatement"); + when(objectInstance.getSkyWalkingDynamicField()).thenReturn(enhanceRequireCacheObject); when(method.getName()).thenReturn("executeQuery"); when(connectionInfo.getComponent()).thenReturn(ComponentsDefine.H2_JDBC_DRIVER); @@ -81,6 +82,24 @@ public void setUp() { when(connectionInfo.getDatabasePeer()).thenReturn("localhost:3307"); } + @Test + public void testCreateDatabaseSpanWithNoMethodParamButWithCache() throws Throwable { + JDBCPluginConfig.Plugin.JDBC.SQL_BODY_MAX_LENGTH = 2048; + + serviceMethodInterceptor.beforeMethod(objectInstance, method, new Object[0], null, null); + serviceMethodInterceptor.afterMethod(objectInstance, method, new Object[0], null, null); + + assertThat(segmentStorage.getTraceSegments().size(), is(1)); + TraceSegment segment = segmentStorage.getTraceSegments().get(0); + assertThat(SegmentHelper.getSpans(segment).size(), is(1)); + AbstractTracingSpan span = SegmentHelper.getSpans(segment).get(0); + SpanAssert.assertLayer(span, SpanLayer.DB); + assertThat(span.getOperationName(), is("H2/JDBC/CallableStatement/executeQuery")); + SpanAssert.assertTag(span, 0, "H2"); + SpanAssert.assertTag(span, 1, "test"); + SpanAssert.assertTag(span, 2, SQL); + } + @Test public void testCreateDatabaseSpan() throws Throwable { JDBCPluginConfig.Plugin.JDBC.SQL_BODY_MAX_LENGTH = 2048; diff --git a/apm-sniffer/apm-sdk-plugin/nats-2.14.x-2.15.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/nats-2.14.x-2.16.5-plugin/pom.xml similarity index 94% rename from apm-sniffer/apm-sdk-plugin/nats-2.14.x-2.15.x-plugin/pom.xml rename to apm-sniffer/apm-sdk-plugin/nats-2.14.x-2.16.5-plugin/pom.xml index b447f3bbaf..60bba313b0 100644 --- a/apm-sniffer/apm-sdk-plugin/nats-2.14.x-2.15.x-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/nats-2.14.x-2.16.5-plugin/pom.xml @@ -21,12 +21,12 @@ apm-sdk-plugin org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 - nats-2.14.x-2.15.x-plugin + nats-2.14.x-2.16.5-plugin jar diff --git a/apm-sniffer/apm-sdk-plugin/nats-2.14.x-2.15.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/nats/client/CreateDispatcherInterceptor.java b/apm-sniffer/apm-sdk-plugin/nats-2.14.x-2.16.5-plugin/src/main/java/org/apache/skywalking/apm/plugin/nats/client/CreateDispatcherInterceptor.java similarity index 100% rename from apm-sniffer/apm-sdk-plugin/nats-2.14.x-2.15.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/nats/client/CreateDispatcherInterceptor.java rename to apm-sniffer/apm-sdk-plugin/nats-2.14.x-2.16.5-plugin/src/main/java/org/apache/skywalking/apm/plugin/nats/client/CreateDispatcherInterceptor.java diff --git a/apm-sniffer/apm-sdk-plugin/nats-2.14.x-2.15.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/nats/client/CreateSubInterceptor.java b/apm-sniffer/apm-sdk-plugin/nats-2.14.x-2.16.5-plugin/src/main/java/org/apache/skywalking/apm/plugin/nats/client/CreateSubInterceptor.java similarity index 100% rename from apm-sniffer/apm-sdk-plugin/nats-2.14.x-2.15.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/nats/client/CreateSubInterceptor.java rename to apm-sniffer/apm-sdk-plugin/nats-2.14.x-2.16.5-plugin/src/main/java/org/apache/skywalking/apm/plugin/nats/client/CreateSubInterceptor.java diff --git a/apm-sniffer/apm-sdk-plugin/nats-2.14.x-2.15.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/nats/client/DeliverReplyInterceptor.java b/apm-sniffer/apm-sdk-plugin/nats-2.14.x-2.16.5-plugin/src/main/java/org/apache/skywalking/apm/plugin/nats/client/DeliverReplyInterceptor.java similarity index 100% rename from apm-sniffer/apm-sdk-plugin/nats-2.14.x-2.15.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/nats/client/DeliverReplyInterceptor.java rename to apm-sniffer/apm-sdk-plugin/nats-2.14.x-2.16.5-plugin/src/main/java/org/apache/skywalking/apm/plugin/nats/client/DeliverReplyInterceptor.java diff --git a/apm-sniffer/apm-sdk-plugin/nats-2.14.x-2.15.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/nats/client/NatsCommons.java b/apm-sniffer/apm-sdk-plugin/nats-2.14.x-2.16.5-plugin/src/main/java/org/apache/skywalking/apm/plugin/nats/client/NatsCommons.java similarity index 100% rename from apm-sniffer/apm-sdk-plugin/nats-2.14.x-2.15.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/nats/client/NatsCommons.java rename to apm-sniffer/apm-sdk-plugin/nats-2.14.x-2.16.5-plugin/src/main/java/org/apache/skywalking/apm/plugin/nats/client/NatsCommons.java diff --git a/apm-sniffer/apm-sdk-plugin/nats-2.14.x-2.15.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/nats/client/NatsConnectionWriterConstructorInterceptor.java b/apm-sniffer/apm-sdk-plugin/nats-2.14.x-2.16.5-plugin/src/main/java/org/apache/skywalking/apm/plugin/nats/client/NatsConnectionWriterConstructorInterceptor.java similarity index 100% rename from apm-sniffer/apm-sdk-plugin/nats-2.14.x-2.15.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/nats/client/NatsConnectionWriterConstructorInterceptor.java rename to apm-sniffer/apm-sdk-plugin/nats-2.14.x-2.16.5-plugin/src/main/java/org/apache/skywalking/apm/plugin/nats/client/NatsConnectionWriterConstructorInterceptor.java diff --git a/apm-sniffer/apm-sdk-plugin/nats-2.14.x-2.15.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/nats/client/NatsJetStreamConstructorInterceptor.java b/apm-sniffer/apm-sdk-plugin/nats-2.14.x-2.16.5-plugin/src/main/java/org/apache/skywalking/apm/plugin/nats/client/NatsJetStreamConstructorInterceptor.java similarity index 100% rename from apm-sniffer/apm-sdk-plugin/nats-2.14.x-2.15.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/nats/client/NatsJetStreamConstructorInterceptor.java rename to apm-sniffer/apm-sdk-plugin/nats-2.14.x-2.16.5-plugin/src/main/java/org/apache/skywalking/apm/plugin/nats/client/NatsJetStreamConstructorInterceptor.java diff --git a/apm-sniffer/apm-sdk-plugin/nats-2.14.x-2.15.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/nats/client/NatsMessageInterceptor.java b/apm-sniffer/apm-sdk-plugin/nats-2.14.x-2.16.5-plugin/src/main/java/org/apache/skywalking/apm/plugin/nats/client/NatsMessageInterceptor.java similarity index 100% rename from apm-sniffer/apm-sdk-plugin/nats-2.14.x-2.15.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/nats/client/NatsMessageInterceptor.java rename to apm-sniffer/apm-sdk-plugin/nats-2.14.x-2.16.5-plugin/src/main/java/org/apache/skywalking/apm/plugin/nats/client/NatsMessageInterceptor.java diff --git a/apm-sniffer/apm-sdk-plugin/nats-2.14.x-2.15.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/nats/client/NatsSubscriptionConstructorInterceptor.java b/apm-sniffer/apm-sdk-plugin/nats-2.14.x-2.16.5-plugin/src/main/java/org/apache/skywalking/apm/plugin/nats/client/NatsSubscriptionConstructorInterceptor.java similarity index 100% rename from apm-sniffer/apm-sdk-plugin/nats-2.14.x-2.15.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/nats/client/NatsSubscriptionConstructorInterceptor.java rename to apm-sniffer/apm-sdk-plugin/nats-2.14.x-2.16.5-plugin/src/main/java/org/apache/skywalking/apm/plugin/nats/client/NatsSubscriptionConstructorInterceptor.java diff --git a/apm-sniffer/apm-sdk-plugin/nats-2.14.x-2.15.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/nats/client/SubscriptionNextMsgInterceptor.java b/apm-sniffer/apm-sdk-plugin/nats-2.14.x-2.16.5-plugin/src/main/java/org/apache/skywalking/apm/plugin/nats/client/SubscriptionNextMsgInterceptor.java similarity index 100% rename from apm-sniffer/apm-sdk-plugin/nats-2.14.x-2.15.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/nats/client/SubscriptionNextMsgInterceptor.java rename to apm-sniffer/apm-sdk-plugin/nats-2.14.x-2.16.5-plugin/src/main/java/org/apache/skywalking/apm/plugin/nats/client/SubscriptionNextMsgInterceptor.java diff --git a/apm-sniffer/apm-sdk-plugin/nats-2.14.x-2.15.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/nats/client/WriterQueueInterceptor.java b/apm-sniffer/apm-sdk-plugin/nats-2.14.x-2.16.5-plugin/src/main/java/org/apache/skywalking/apm/plugin/nats/client/WriterQueueInterceptor.java similarity index 100% rename from apm-sniffer/apm-sdk-plugin/nats-2.14.x-2.15.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/nats/client/WriterQueueInterceptor.java rename to apm-sniffer/apm-sdk-plugin/nats-2.14.x-2.16.5-plugin/src/main/java/org/apache/skywalking/apm/plugin/nats/client/WriterQueueInterceptor.java diff --git a/apm-sniffer/apm-sdk-plugin/nats-2.14.x-2.15.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/nats/client/WriterSendMessageBatchInterceptor.java b/apm-sniffer/apm-sdk-plugin/nats-2.14.x-2.16.5-plugin/src/main/java/org/apache/skywalking/apm/plugin/nats/client/WriterSendMessageBatchInterceptor.java similarity index 100% rename from apm-sniffer/apm-sdk-plugin/nats-2.14.x-2.15.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/nats/client/WriterSendMessageBatchInterceptor.java rename to apm-sniffer/apm-sdk-plugin/nats-2.14.x-2.16.5-plugin/src/main/java/org/apache/skywalking/apm/plugin/nats/client/WriterSendMessageBatchInterceptor.java diff --git a/apm-sniffer/apm-sdk-plugin/nats-2.14.x-2.16.5-plugin/src/main/java/org/apache/skywalking/apm/plugin/nats/client/define/AbstractWitnessInstrumentation.java b/apm-sniffer/apm-sdk-plugin/nats-2.14.x-2.16.5-plugin/src/main/java/org/apache/skywalking/apm/plugin/nats/client/define/AbstractWitnessInstrumentation.java new file mode 100644 index 0000000000..1acf6e525e --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/nats-2.14.x-2.16.5-plugin/src/main/java/org/apache/skywalking/apm/plugin/nats/client/define/AbstractWitnessInstrumentation.java @@ -0,0 +1,43 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.skywalking.apm.plugin.nats.client.define; + +import java.util.Collections; +import java.util.List; +import org.apache.skywalking.apm.agent.core.plugin.WitnessMethod; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine; + +import static net.bytebuddy.matcher.ElementMatchers.named; + +public abstract class AbstractWitnessInstrumentation extends ClassInstanceMethodsEnhancePluginDefine { + + /* + * Currently, we only support 2.14.x-2.16.5, as there is no proper way and opportunity + * to change the message header and re-calculate the message length for 2.16.5+ yet. + * This method prevents users from applying this plugin to unsupported versions, + * which may cause unknown errors. + * For more information: https://github.com/apache/skywalking/discussions/11650 + */ + @Override + protected List witnessMethods() { + return Collections.singletonList(new WitnessMethod( + "io.nats.client.impl.NatsMessage", + named("calculateIfDirty") + )); + } +} diff --git a/apm-sniffer/apm-sdk-plugin/nats-2.14.x-2.15.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/nats/client/define/NatsConnectionInstrumentation.java b/apm-sniffer/apm-sdk-plugin/nats-2.14.x-2.16.5-plugin/src/main/java/org/apache/skywalking/apm/plugin/nats/client/define/NatsConnectionInstrumentation.java similarity index 94% rename from apm-sniffer/apm-sdk-plugin/nats-2.14.x-2.15.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/nats/client/define/NatsConnectionInstrumentation.java rename to apm-sniffer/apm-sdk-plugin/nats-2.14.x-2.16.5-plugin/src/main/java/org/apache/skywalking/apm/plugin/nats/client/define/NatsConnectionInstrumentation.java index 1822732b15..9b2d5087d7 100644 --- a/apm-sniffer/apm-sdk-plugin/nats-2.14.x-2.15.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/nats/client/define/NatsConnectionInstrumentation.java +++ b/apm-sniffer/apm-sdk-plugin/nats-2.14.x-2.16.5-plugin/src/main/java/org/apache/skywalking/apm/plugin/nats/client/define/NatsConnectionInstrumentation.java @@ -21,14 +21,13 @@ import net.bytebuddy.matcher.ElementMatcher; import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint; import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint; -import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine; import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch; import static net.bytebuddy.matcher.ElementMatchers.named; import static net.bytebuddy.matcher.ElementMatchers.takesArguments; import static org.apache.skywalking.apm.agent.core.plugin.match.NameMatch.byName; -public class NatsConnectionInstrumentation extends ClassInstanceMethodsEnhancePluginDefine { +public class NatsConnectionInstrumentation extends AbstractWitnessInstrumentation { private static final String ENHANCE_CLASS = "io.nats.client.impl.NatsConnection"; @@ -82,4 +81,4 @@ public boolean isOverrideArgs() { } }; } -} \ No newline at end of file +} diff --git a/apm-sniffer/apm-sdk-plugin/nats-2.14.x-2.15.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/nats/client/define/NatsConnectionWriterInstrumentation.java b/apm-sniffer/apm-sdk-plugin/nats-2.14.x-2.16.5-plugin/src/main/java/org/apache/skywalking/apm/plugin/nats/client/define/NatsConnectionWriterInstrumentation.java similarity index 95% rename from apm-sniffer/apm-sdk-plugin/nats-2.14.x-2.15.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/nats/client/define/NatsConnectionWriterInstrumentation.java rename to apm-sniffer/apm-sdk-plugin/nats-2.14.x-2.16.5-plugin/src/main/java/org/apache/skywalking/apm/plugin/nats/client/define/NatsConnectionWriterInstrumentation.java index f3a315314f..b6e56ddc4a 100644 --- a/apm-sniffer/apm-sdk-plugin/nats-2.14.x-2.15.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/nats/client/define/NatsConnectionWriterInstrumentation.java +++ b/apm-sniffer/apm-sdk-plugin/nats-2.14.x-2.16.5-plugin/src/main/java/org/apache/skywalking/apm/plugin/nats/client/define/NatsConnectionWriterInstrumentation.java @@ -21,14 +21,13 @@ import net.bytebuddy.matcher.ElementMatcher; import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint; import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint; -import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine; import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch; import static net.bytebuddy.matcher.ElementMatchers.named; import static net.bytebuddy.matcher.ElementMatchers.takesArguments; import static org.apache.skywalking.apm.agent.core.plugin.match.NameMatch.byName; -public class NatsConnectionWriterInstrumentation extends ClassInstanceMethodsEnhancePluginDefine { +public class NatsConnectionWriterInstrumentation extends AbstractWitnessInstrumentation { private static final String ENHANCE_CLASS = "io.nats.client.impl.NatsConnectionWriter"; private static final String PUBLISH_INTERCEPTOR_CLASS_NAME = "org.apache.skywalking.apm.plugin.nats.client.WriterSendMessageBatchInterceptor"; diff --git a/apm-sniffer/apm-sdk-plugin/nats-2.14.x-2.15.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/nats/client/define/NatsJetStreamInstrumentation.java b/apm-sniffer/apm-sdk-plugin/nats-2.14.x-2.16.5-plugin/src/main/java/org/apache/skywalking/apm/plugin/nats/client/define/NatsJetStreamInstrumentation.java similarity index 94% rename from apm-sniffer/apm-sdk-plugin/nats-2.14.x-2.15.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/nats/client/define/NatsJetStreamInstrumentation.java rename to apm-sniffer/apm-sdk-plugin/nats-2.14.x-2.16.5-plugin/src/main/java/org/apache/skywalking/apm/plugin/nats/client/define/NatsJetStreamInstrumentation.java index a635658c4a..cf940e7e39 100644 --- a/apm-sniffer/apm-sdk-plugin/nats-2.14.x-2.15.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/nats/client/define/NatsJetStreamInstrumentation.java +++ b/apm-sniffer/apm-sdk-plugin/nats-2.14.x-2.16.5-plugin/src/main/java/org/apache/skywalking/apm/plugin/nats/client/define/NatsJetStreamInstrumentation.java @@ -21,14 +21,13 @@ import net.bytebuddy.matcher.ElementMatcher; import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint; import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint; -import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine; import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch; import static net.bytebuddy.matcher.ElementMatchers.named; import static net.bytebuddy.matcher.ElementMatchers.takesArguments; import static org.apache.skywalking.apm.agent.core.plugin.match.NameMatch.byName; -public class NatsJetStreamInstrumentation extends ClassInstanceMethodsEnhancePluginDefine { +public class NatsJetStreamInstrumentation extends AbstractWitnessInstrumentation { private static final String ENHANCE_CLASS = "io.nats.client.impl.NatsJetStream"; private static final String CREATE_SUB_INTERCEPTOR = "org.apache.skywalking.apm.plugin.nats.client.CreateSubInterceptor"; @@ -78,4 +77,4 @@ public boolean isOverrideArgs() { }; } -} \ No newline at end of file +} diff --git a/apm-sniffer/apm-sdk-plugin/nats-2.14.x-2.15.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/nats/client/define/NatsMessageInstrumentation.java b/apm-sniffer/apm-sdk-plugin/nats-2.14.x-2.16.5-plugin/src/main/java/org/apache/skywalking/apm/plugin/nats/client/define/NatsMessageInstrumentation.java similarity index 93% rename from apm-sniffer/apm-sdk-plugin/nats-2.14.x-2.15.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/nats/client/define/NatsMessageInstrumentation.java rename to apm-sniffer/apm-sdk-plugin/nats-2.14.x-2.16.5-plugin/src/main/java/org/apache/skywalking/apm/plugin/nats/client/define/NatsMessageInstrumentation.java index 79d061a396..95d229690b 100644 --- a/apm-sniffer/apm-sdk-plugin/nats-2.14.x-2.15.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/nats/client/define/NatsMessageInstrumentation.java +++ b/apm-sniffer/apm-sdk-plugin/nats-2.14.x-2.16.5-plugin/src/main/java/org/apache/skywalking/apm/plugin/nats/client/define/NatsMessageInstrumentation.java @@ -21,7 +21,6 @@ import net.bytebuddy.matcher.ElementMatcher; import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint; import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint; -import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine; import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch; import static net.bytebuddy.matcher.ElementMatchers.named; @@ -35,7 +34,7 @@ * * BTW , ACK is done by publishing a message , So we needn't enhance ACK method */ -public class NatsMessageInstrumentation extends ClassInstanceMethodsEnhancePluginDefine { +public class NatsMessageInstrumentation extends AbstractWitnessInstrumentation { private static final String ENHANCE_CLASS = "io.nats.client.impl.NatsMessage"; private static final String PUBLISH_INTERCEPTOR = "org.apache.skywalking.apm.plugin.nats.client.NatsMessageInterceptor"; diff --git a/apm-sniffer/apm-sdk-plugin/nats-2.14.x-2.15.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/nats/client/define/NatsSubscriptionInstrumentation.java b/apm-sniffer/apm-sdk-plugin/nats-2.14.x-2.16.5-plugin/src/main/java/org/apache/skywalking/apm/plugin/nats/client/define/NatsSubscriptionInstrumentation.java similarity index 93% rename from apm-sniffer/apm-sdk-plugin/nats-2.14.x-2.15.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/nats/client/define/NatsSubscriptionInstrumentation.java rename to apm-sniffer/apm-sdk-plugin/nats-2.14.x-2.16.5-plugin/src/main/java/org/apache/skywalking/apm/plugin/nats/client/define/NatsSubscriptionInstrumentation.java index a04433722a..62bc14b017 100644 --- a/apm-sniffer/apm-sdk-plugin/nats-2.14.x-2.15.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/nats/client/define/NatsSubscriptionInstrumentation.java +++ b/apm-sniffer/apm-sdk-plugin/nats-2.14.x-2.16.5-plugin/src/main/java/org/apache/skywalking/apm/plugin/nats/client/define/NatsSubscriptionInstrumentation.java @@ -21,14 +21,13 @@ import net.bytebuddy.matcher.ElementMatcher; import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint; import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint; -import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine; import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch; import static net.bytebuddy.matcher.ElementMatchers.named; import static net.bytebuddy.matcher.ElementMatchers.takesArguments; import static org.apache.skywalking.apm.agent.core.plugin.match.NameMatch.byName; -public class NatsSubscriptionInstrumentation extends ClassInstanceMethodsEnhancePluginDefine { +public class NatsSubscriptionInstrumentation extends AbstractWitnessInstrumentation { private static final String ENHANCE_CLASS = "io.nats.client.impl.NatsSubscription"; private static final String NEXT_MSG_INTERCEPTOR = "org.apache.skywalking.apm.plugin.nats.client.SubscriptionNextMsgInterceptor"; diff --git a/apm-sniffer/apm-sdk-plugin/nats-2.14.x-2.15.x-plugin/src/main/resources/skywalking-plugin.def b/apm-sniffer/apm-sdk-plugin/nats-2.14.x-2.16.5-plugin/src/main/resources/skywalking-plugin.def similarity index 72% rename from apm-sniffer/apm-sdk-plugin/nats-2.14.x-2.15.x-plugin/src/main/resources/skywalking-plugin.def rename to apm-sniffer/apm-sdk-plugin/nats-2.14.x-2.16.5-plugin/src/main/resources/skywalking-plugin.def index cc15654fcf..cbbf148d66 100644 --- a/apm-sniffer/apm-sdk-plugin/nats-2.14.x-2.15.x-plugin/src/main/resources/skywalking-plugin.def +++ b/apm-sniffer/apm-sdk-plugin/nats-2.14.x-2.16.5-plugin/src/main/resources/skywalking-plugin.def @@ -14,8 +14,8 @@ # See the License for the specific language governing permissions and # limitations under the License. -nats-client-2.14.x-2.15.x=org.apache.skywalking.apm.plugin.nats.client.define.NatsMessageInstrumentation -nats-client-2.14.x-2.15.x=org.apache.skywalking.apm.plugin.nats.client.define.NatsConnectionInstrumentation -nats-client-2.14.x-2.15.x=org.apache.skywalking.apm.plugin.nats.client.define.NatsConnectionWriterInstrumentation -nats-client-2.14.x-2.15.x=org.apache.skywalking.apm.plugin.nats.client.define.NatsSubscriptionInstrumentation -nats-client-2.14.x-2.15.x=org.apache.skywalking.apm.plugin.nats.client.define.NatsJetStreamInstrumentation \ No newline at end of file +nats-client-2.14.x-2.16.5=org.apache.skywalking.apm.plugin.nats.client.define.NatsMessageInstrumentation +nats-client-2.14.x-2.16.5=org.apache.skywalking.apm.plugin.nats.client.define.NatsConnectionInstrumentation +nats-client-2.14.x-2.16.5=org.apache.skywalking.apm.plugin.nats.client.define.NatsConnectionWriterInstrumentation +nats-client-2.14.x-2.16.5=org.apache.skywalking.apm.plugin.nats.client.define.NatsSubscriptionInstrumentation +nats-client-2.14.x-2.16.5=org.apache.skywalking.apm.plugin.nats.client.define.NatsJetStreamInstrumentation diff --git a/apm-sniffer/apm-sdk-plugin/neo4j-4.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/neo4j-4.x-plugin/pom.xml index 30857885b8..1bf1de4bfd 100644 --- a/apm-sniffer/apm-sdk-plugin/neo4j-4.x-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/neo4j-4.x-plugin/pom.xml @@ -21,7 +21,7 @@ apm-sdk-plugin org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-sdk-plugin/netty-socketio-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/netty-socketio-plugin/pom.xml index 55a33e8286..2fc96f1c2f 100644 --- a/apm-sniffer/apm-sdk-plugin/netty-socketio-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/netty-socketio-plugin/pom.xml @@ -21,7 +21,7 @@ apm-sdk-plugin org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-sdk-plugin/nutz-plugins/http-1.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/nutz-plugins/http-1.x-plugin/pom.xml index 2148c6f271..3f2b4d0b67 100644 --- a/apm-sniffer/apm-sdk-plugin/nutz-plugins/http-1.x-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/nutz-plugins/http-1.x-plugin/pom.xml @@ -20,7 +20,7 @@ nutz-plugins org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-sdk-plugin/nutz-plugins/mvc-annotation-1.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/nutz-plugins/mvc-annotation-1.x-plugin/pom.xml index 1d8c46a0bc..cd772375c6 100644 --- a/apm-sniffer/apm-sdk-plugin/nutz-plugins/mvc-annotation-1.x-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/nutz-plugins/mvc-annotation-1.x-plugin/pom.xml @@ -20,7 +20,7 @@ nutz-plugins org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-sdk-plugin/nutz-plugins/pom.xml b/apm-sniffer/apm-sdk-plugin/nutz-plugins/pom.xml index c83b08a2e7..0b8fe2d545 100644 --- a/apm-sniffer/apm-sdk-plugin/nutz-plugins/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/nutz-plugins/pom.xml @@ -23,7 +23,7 @@ org.apache.skywalking apm-sdk-plugin - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT nutz-plugins @@ -39,7 +39,6 @@ UTF-8 - /.. diff --git a/apm-sniffer/apm-sdk-plugin/okhttp-2.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/okhttp-2.x-plugin/pom.xml index 5419edf70e..14d05d0135 100644 --- a/apm-sniffer/apm-sdk-plugin/okhttp-2.x-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/okhttp-2.x-plugin/pom.xml @@ -21,7 +21,7 @@ apm-sdk-plugin org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-sdk-plugin/okhttp-3.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/okhttp-3.x-plugin/pom.xml index 2ec173d6dd..b25f764a6e 100644 --- a/apm-sniffer/apm-sdk-plugin/okhttp-3.x-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/okhttp-3.x-plugin/pom.xml @@ -21,7 +21,7 @@ org.apache.skywalking apm-sdk-plugin - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-sdk-plugin/okhttp-4.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/okhttp-4.x-plugin/pom.xml index 432652f441..ce5434d9cb 100644 --- a/apm-sniffer/apm-sdk-plugin/okhttp-4.x-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/okhttp-4.x-plugin/pom.xml @@ -21,7 +21,7 @@ org.apache.skywalking apm-sdk-plugin - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-sdk-plugin/okhttp-common/pom.xml b/apm-sniffer/apm-sdk-plugin/okhttp-common/pom.xml index 8d0ecba8d4..68437b314a 100644 --- a/apm-sniffer/apm-sdk-plugin/okhttp-common/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/okhttp-common/pom.xml @@ -21,7 +21,7 @@ apm-sdk-plugin org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-sdk-plugin/play-2.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/play-2.x-plugin/pom.xml index 40f8aebc8e..5c74e133d8 100644 --- a/apm-sniffer/apm-sdk-plugin/play-2.x-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/play-2.x-plugin/pom.xml @@ -22,7 +22,7 @@ apm-sdk-plugin org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT apm-play-2.x-plugin diff --git a/apm-sniffer/apm-sdk-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/pom.xml index bfb4f47444..1c792ed425 100644 --- a/apm-sniffer/apm-sdk-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/pom.xml @@ -23,7 +23,7 @@ org.apache.skywalking java-agent-sniffer - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT apm-sdk-plugin @@ -87,7 +87,7 @@ netty-socketio-plugin httpclient-3.x-plugin play-2.x-plugin - lettuce-5.x-plugin + lettuce-plugins avro-plugin finagle-6.25.x-plugin quasar-plugin @@ -123,9 +123,8 @@ guava-eventbus-plugin hutool-plugins micronaut-plugins - nats-2.14.x-2.15.x-plugin + nats-2.14.x-2.16.5-plugin jedis-plugins - impala-jdbc-2.6.x-plugin apm-armeria-plugins jetty-thread-pool-plugin jersey-2.x-plugin @@ -137,6 +136,8 @@ aerospike-plugin rocketMQ-client-java-5.x-plugin activemq-artemis-jakarta-client-2.x-plugin + c3p0-0.9.x-plugin + solon-2.x-plugin pom @@ -145,13 +146,10 @@ UTF-8 - net.bytebuddy ${shade.package}.${shade.net.bytebuddy.source} - ${project.build.directory}${sdk.plugin.related.dir}/../../../../skywalking-agent - - ${agent.package.dest.dir}/plugins + ${maven.multiModuleProjectDirectory}/skywalking-agent/plugins 1.0b3 1.8.1 @@ -167,7 +165,6 @@ net.bytebuddy byte-buddy - ${bytebuddy.version} provided diff --git a/apm-sniffer/apm-sdk-plugin/postgresql-8.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/postgresql-8.x-plugin/pom.xml index bbd44aed03..181a48d08c 100755 --- a/apm-sniffer/apm-sdk-plugin/postgresql-8.x-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/postgresql-8.x-plugin/pom.xml @@ -20,7 +20,7 @@ apm-sdk-plugin org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-sdk-plugin/pulsar-2.2-2.7-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/pulsar-2.2-2.7-plugin/pom.xml index 13c3c9d6ae..862269d01e 100644 --- a/apm-sniffer/apm-sdk-plugin/pulsar-2.2-2.7-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/pulsar-2.2-2.7-plugin/pom.xml @@ -21,7 +21,7 @@ apm-sdk-plugin org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-sdk-plugin/pulsar-2.8.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/pulsar-2.8.x-plugin/pom.xml index 6b128f50a6..c464911398 100644 --- a/apm-sniffer/apm-sdk-plugin/pulsar-2.8.x-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/pulsar-2.8.x-plugin/pom.xml @@ -21,7 +21,7 @@ apm-sdk-plugin org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-sdk-plugin/pulsar-common/pom.xml b/apm-sniffer/apm-sdk-plugin/pulsar-common/pom.xml index ef06531d74..9679ea88fb 100644 --- a/apm-sniffer/apm-sdk-plugin/pulsar-common/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/pulsar-common/pom.xml @@ -21,7 +21,7 @@ apm-sdk-plugin org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-sdk-plugin/quasar-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/quasar-plugin/pom.xml index b34d95a9b9..a980a658d8 100644 --- a/apm-sniffer/apm-sdk-plugin/quasar-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/quasar-plugin/pom.xml @@ -22,7 +22,7 @@ org.apache.skywalking apm-sdk-plugin - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT apm-quasar-plugin diff --git a/apm-sniffer/apm-sdk-plugin/rabbitmq-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/rabbitmq-plugin/pom.xml index f6a48a3415..492b479b07 100644 --- a/apm-sniffer/apm-sdk-plugin/rabbitmq-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/rabbitmq-plugin/pom.xml @@ -21,7 +21,7 @@ apm-sdk-plugin org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-sdk-plugin/rabbitmq-plugin/src/main/java/org/apache/skywalking/apm/plugin/rabbitmq/RabbitMQConsumerInterceptor.java b/apm-sniffer/apm-sdk-plugin/rabbitmq-plugin/src/main/java/org/apache/skywalking/apm/plugin/rabbitmq/RabbitMQConsumerInterceptor.java index 50240c9729..3ac0caf121 100644 --- a/apm-sniffer/apm-sdk-plugin/rabbitmq-plugin/src/main/java/org/apache/skywalking/apm/plugin/rabbitmq/RabbitMQConsumerInterceptor.java +++ b/apm-sniffer/apm-sdk-plugin/rabbitmq-plugin/src/main/java/org/apache/skywalking/apm/plugin/rabbitmq/RabbitMQConsumerInterceptor.java @@ -26,10 +26,19 @@ public class RabbitMQConsumerInterceptor implements InstanceMethodsAroundInterceptor { + public static final String SMLC_INTERNAL_CONSUMER = "org.springframework.amqp.rabbit.listener.BlockingQueueConsumer$InternalConsumer"; + public static final String DMLC_INTERNAL_CONSUMER = "org.springframework.amqp.rabbit.listener.DirectMessageListenerContainer$SimpleConsumer"; + @Override public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, MethodInterceptResult result) throws Throwable { Consumer consumer = (Consumer) allArguments[6]; + if (consumer != null) { + String className = consumer.getClass().getName(); + if (SMLC_INTERNAL_CONSUMER.equals(className) || DMLC_INTERNAL_CONSUMER.equals(className)) { + return; + } + } allArguments[6] = new TracerConsumer(consumer, (String) objInst.getSkyWalkingDynamicField()); } diff --git a/apm-sniffer/apm-sdk-plugin/rabbitmq-plugin/src/main/java/org/apache/skywalking/apm/plugin/rabbitmq/TracerConsumer.java b/apm-sniffer/apm-sdk-plugin/rabbitmq-plugin/src/main/java/org/apache/skywalking/apm/plugin/rabbitmq/TracerConsumer.java index bb6f134fca..c3ae14470e 100644 --- a/apm-sniffer/apm-sdk-plugin/rabbitmq-plugin/src/main/java/org/apache/skywalking/apm/plugin/rabbitmq/TracerConsumer.java +++ b/apm-sniffer/apm-sdk-plugin/rabbitmq-plugin/src/main/java/org/apache/skywalking/apm/plugin/rabbitmq/TracerConsumer.java @@ -50,7 +50,7 @@ public void handleConsumeOk(final String consumerTag) { @Override public void handleCancelOk(final String consumerTag) { - this.delegate.handleRecoverOk(consumerTag); + this.delegate.handleCancelOk(consumerTag); } @Override diff --git a/apm-sniffer/apm-sdk-plugin/redisson-3.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/redisson-3.x-plugin/pom.xml index 343cbac755..7ba9d05be3 100644 --- a/apm-sniffer/apm-sdk-plugin/redisson-3.x-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/redisson-3.x-plugin/pom.xml @@ -22,7 +22,7 @@ org.apache.skywalking apm-sdk-plugin - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT apm-redisson-3.x-plugin @@ -31,7 +31,7 @@ redisson-3.x-plugin http://maven.apache.org - 3.6.0 + 3.20.0 diff --git a/apm-sniffer/apm-sdk-plugin/redisson-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/redisson/v3/ConnectionManagerInterceptor.java b/apm-sniffer/apm-sdk-plugin/redisson-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/redisson/v3/ConnectionManagerInterceptor.java index d4b352deea..8cdb79b9ff 100644 --- a/apm-sniffer/apm-sdk-plugin/redisson-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/redisson/v3/ConnectionManagerInterceptor.java +++ b/apm-sniffer/apm-sdk-plugin/redisson-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/redisson/v3/ConnectionManagerInterceptor.java @@ -18,6 +18,7 @@ package org.apache.skywalking.apm.plugin.redisson.v3; +import java.util.Objects; import org.apache.skywalking.apm.agent.core.context.util.PeerFormat; import org.apache.skywalking.apm.agent.core.logging.api.ILog; import org.apache.skywalking.apm.agent.core.logging.api.LogManager; @@ -26,11 +27,12 @@ import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult; import org.apache.skywalking.apm.plugin.redisson.v3.util.ClassUtil; import org.redisson.config.Config; -import org.redisson.connection.ConnectionManager; import java.lang.reflect.Method; import java.net.URI; import java.util.Collection; +import org.redisson.connection.MasterSlaveConnectionManager; +import org.redisson.connection.ServiceManager; public class ConnectionManagerInterceptor implements InstanceMethodsAroundInterceptor { @@ -45,14 +47,19 @@ public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allAr public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, Object ret) throws Throwable { try { - ConnectionManager connectionManager = (ConnectionManager) objInst; - Config config = connectionManager.getCfg(); - - Object singleServerConfig = ClassUtil.getObjectField(config, "singleServerConfig"); - Object sentinelServersConfig = ClassUtil.getObjectField(config, "sentinelServersConfig"); - Object masterSlaveServersConfig = ClassUtil.getObjectField(config, "masterSlaveServersConfig"); - Object clusterServersConfig = ClassUtil.getObjectField(config, "clusterServersConfig"); - Object replicatedServersConfig = ClassUtil.getObjectField(config, "replicatedServersConfig"); + Config config = getConfig(objInst); + Object singleServerConfig = null; + Object sentinelServersConfig = null; + Object masterSlaveServersConfig = null; + Object clusterServersConfig = null; + Object replicatedServersConfig = null; + if (Objects.nonNull(config)) { + singleServerConfig = ClassUtil.getObjectField(config, "singleServerConfig"); + sentinelServersConfig = ClassUtil.getObjectField(config, "sentinelServersConfig"); + masterSlaveServersConfig = ClassUtil.getObjectField(config, "masterSlaveServersConfig"); + clusterServersConfig = ClassUtil.getObjectField(config, "clusterServersConfig"); + replicatedServersConfig = ClassUtil.getObjectField(config, "replicatedServersConfig"); + } StringBuilder peer = new StringBuilder(); EnhancedInstance retInst = (EnhancedInstance) ret; @@ -70,7 +77,7 @@ public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allA } if (masterSlaveServersConfig != null) { Object masterAddress = ClassUtil.getObjectField(masterSlaveServersConfig, "masterAddress"); - peer.append(getPeer(masterAddress)); + peer.append(getPeer(masterAddress)).append(";"); appendAddresses(peer, (Collection) ClassUtil.getObjectField(masterSlaveServersConfig, "slaveAddresses")); retInst.setSkyWalkingDynamicField(PeerFormat.shorten(peer.toString())); return ret; @@ -118,6 +125,22 @@ static String getPeer(Object obj) { } } + private Config getConfig(EnhancedInstance objInst) { + Config config = null; + MasterSlaveConnectionManager connectionManager = (MasterSlaveConnectionManager) objInst; + try { + config = (Config) ClassUtil.getObjectField(connectionManager, "cfg"); + } catch (NoSuchFieldException | IllegalAccessException ignore) { + try { + ServiceManager serviceManager = (ServiceManager) ClassUtil.getObjectField( + connectionManager, "serviceManager"); + config = serviceManager.getCfg(); + } catch (NoSuchFieldException | IllegalAccessException ignore2) { + } + } + return config; + } + @Override public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, Throwable t) { diff --git a/apm-sniffer/apm-sdk-plugin/redisson-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/redisson/v3/RedisConnectionMethodInterceptor.java b/apm-sniffer/apm-sdk-plugin/redisson-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/redisson/v3/RedisConnectionMethodInterceptor.java index 60956c7a64..d475382c8f 100644 --- a/apm-sniffer/apm-sdk-plugin/redisson-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/redisson/v3/RedisConnectionMethodInterceptor.java +++ b/apm-sniffer/apm-sdk-plugin/redisson-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/redisson/v3/RedisConnectionMethodInterceptor.java @@ -19,6 +19,9 @@ package org.apache.skywalking.apm.plugin.redisson.v3; import io.netty.channel.Channel; + +import java.util.Objects; +import java.util.stream.Collectors; import org.apache.skywalking.apm.agent.core.context.ContextManager; import org.apache.skywalking.apm.agent.core.context.tag.Tags; import org.apache.skywalking.apm.agent.core.context.trace.AbstractSpan; @@ -27,8 +30,8 @@ import org.apache.skywalking.apm.agent.core.logging.api.LogManager; import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceConstructorInterceptor; -import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceMethodsAroundInterceptor; -import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.v2.InstanceMethodsAroundInterceptorV2; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.v2.MethodInvocationContext; import org.apache.skywalking.apm.network.trace.component.ComponentsDefine; import org.apache.skywalking.apm.plugin.redisson.v3.util.ClassUtil; import org.apache.skywalking.apm.util.StringUtil; @@ -41,17 +44,17 @@ import java.net.InetSocketAddress; import java.util.Optional; -public class RedisConnectionMethodInterceptor implements InstanceMethodsAroundInterceptor, InstanceConstructorInterceptor { +public class RedisConnectionMethodInterceptor implements InstanceMethodsAroundInterceptorV2, InstanceConstructorInterceptor { private static final ILog LOGGER = LogManager.getLogger(RedisConnectionMethodInterceptor.class); private static final String ABBR = "..."; private static final String QUESTION_MARK = "?"; private static final String DELIMITER_SPACE = " "; + public static final Object STOP_SPAN_FLAG = new Object(); @Override - public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, - MethodInterceptResult result) throws Throwable { + public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, MethodInvocationContext context) throws Throwable { String peer = (String) objInst.getSkyWalkingDynamicField(); RedisConnection connection = (RedisConnection) objInst; @@ -66,14 +69,22 @@ public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allAr if (allArguments[0] instanceof CommandsData) { operationName = operationName + "BATCH_EXECUTE"; command = "BATCH_EXECUTE"; + if (RedissonPluginConfig.Plugin.Redisson.SHOW_BATCH_COMMANDS) { + command += ":" + showBatchCommands((CommandsData) allArguments[0]); + } } else if (allArguments[0] instanceof CommandData) { CommandData commandData = (CommandData) allArguments[0]; command = commandData.getCommand().getName(); - operationName = operationName + command; - arguments = commandData.getParams(); + if ("PING".equals(command) && !RedissonPluginConfig.Plugin.Redisson.SHOW_PING_COMMAND) { + return; + } else { + operationName = operationName + command; + arguments = commandData.getParams(); + } } AbstractSpan span = ContextManager.createExitSpan(operationName, peer); + context.setContext(STOP_SPAN_FLAG); span.setComponent(ComponentsDefine.REDISSON); Tags.CACHE_TYPE.set(span, "Redis"); Tags.CACHE_INSTANCE.set(span, dbInstance); @@ -85,17 +96,19 @@ public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allAr } @Override - public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, - Object ret) throws Throwable { - ContextManager.stopSpan(); + public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, Object ret, MethodInvocationContext context) throws Throwable { + if (Objects.nonNull(context.getContext())) { + ContextManager.stopSpan(); + } return ret; } @Override - public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments, - Class[] argumentsTypes, Throwable t) { - AbstractSpan span = ContextManager.activeSpan(); - span.log(t); + public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, Throwable t, MethodInvocationContext context) { + if (Objects.nonNull(context.getContext())) { + AbstractSpan span = ContextManager.activeSpan(); + span.log(t); + } } @Override @@ -143,4 +156,11 @@ private Optional parseOperation(String cmd) { } return Optional.empty(); } + + private String showBatchCommands(CommandsData commandsData) { + return commandsData.getCommands() + .stream() + .map(data -> data.getCommand().getName()) + .collect(Collectors.joining(";")); + } } diff --git a/apm-sniffer/apm-sdk-plugin/redisson-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/redisson/v3/RedissonPluginConfig.java b/apm-sniffer/apm-sdk-plugin/redisson-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/redisson/v3/RedissonPluginConfig.java index 52dedcb10a..aeb0a5f270 100644 --- a/apm-sniffer/apm-sdk-plugin/redisson-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/redisson/v3/RedissonPluginConfig.java +++ b/apm-sniffer/apm-sdk-plugin/redisson-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/redisson/v3/RedissonPluginConfig.java @@ -40,6 +40,14 @@ public static class Redisson { * Set a negative number to save specified length of parameter string to the tag. */ public static int REDIS_PARAMETER_MAX_LENGTH = 128; + /** + * If set to true, the PING command would be collected. + */ + public static boolean SHOW_PING_COMMAND = false; + /** + * If set to true, the detail of the Redis batch commands would be collected. + */ + public static boolean SHOW_BATCH_COMMANDS = false; /** * Operation represent a cache span is "write" or "read" action , and "op"(operation) is tagged with key "cache.op" usually diff --git a/apm-sniffer/apm-sdk-plugin/redisson-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/redisson/v3/define/RedisConnectionInstrumentation.java b/apm-sniffer/apm-sdk-plugin/redisson-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/redisson/v3/define/RedisConnectionInstrumentation.java index 84bc82535f..34678af6b7 100644 --- a/apm-sniffer/apm-sdk-plugin/redisson-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/redisson/v3/define/RedisConnectionInstrumentation.java +++ b/apm-sniffer/apm-sdk-plugin/redisson-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/redisson/v3/define/RedisConnectionInstrumentation.java @@ -21,15 +21,15 @@ import net.bytebuddy.description.method.MethodDescription; import net.bytebuddy.matcher.ElementMatcher; import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint; -import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint; -import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.v2.ClassInstanceMethodsEnhancePluginDefineV2; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.v2.InstanceMethodsInterceptV2Point; import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch; import static net.bytebuddy.matcher.ElementMatchers.named; import static org.apache.skywalking.apm.agent.core.plugin.bytebuddy.ArgumentTypeNameMatch.takesArgumentWithType; import static org.apache.skywalking.apm.agent.core.plugin.match.NameMatch.byName; -public class RedisConnectionInstrumentation extends ClassInstanceMethodsEnhancePluginDefine { +public class RedisConnectionInstrumentation extends ClassInstanceMethodsEnhancePluginDefineV2 { private static final String ENHANCE_CLASS = "org.redisson.client.RedisConnection"; @@ -53,16 +53,16 @@ public String getConstructorInterceptor() { } @Override - public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() { - return new InstanceMethodsInterceptPoint[] { - new InstanceMethodsInterceptPoint() { + public InstanceMethodsInterceptV2Point[] getInstanceMethodsInterceptV2Points() { + return new InstanceMethodsInterceptV2Point[] { + new InstanceMethodsInterceptV2Point() { @Override public ElementMatcher getMethodsMatcher() { return named("send"); } @Override - public String getMethodsInterceptor() { + public String getMethodsInterceptorV2() { return REDISSON_METHOD_INTERCEPTOR_CLASS; } diff --git a/apm-sniffer/apm-sdk-plugin/resteasy-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/resteasy-plugin/pom.xml index 85f23f8f7d..4f661c38ba 100644 --- a/apm-sniffer/apm-sdk-plugin/resteasy-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/resteasy-plugin/pom.xml @@ -21,7 +21,7 @@ org.apache.skywalking apm-sdk-plugin - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT resteasy-plugin @@ -38,6 +38,5 @@ UTF-8 - /.. \ No newline at end of file diff --git a/apm-sniffer/apm-sdk-plugin/resteasy-plugin/resteasy-server-3.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/resteasy-plugin/resteasy-server-3.x-plugin/pom.xml index 066461cc65..83b9d357cc 100644 --- a/apm-sniffer/apm-sdk-plugin/resteasy-plugin/resteasy-server-3.x-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/resteasy-plugin/resteasy-server-3.x-plugin/pom.xml @@ -21,7 +21,7 @@ resteasy-plugin org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT resteasy-server-3.x-plugin diff --git a/apm-sniffer/apm-sdk-plugin/resteasy-plugin/resteasy-server-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/resteasy/v3/server/define/SynchronousDispatcherInstrumentation.java b/apm-sniffer/apm-sdk-plugin/resteasy-plugin/resteasy-server-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/resteasy/v3/server/define/SynchronousDispatcherInstrumentation.java index aaade19398..77093e85a8 100644 --- a/apm-sniffer/apm-sdk-plugin/resteasy-plugin/resteasy-server-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/resteasy/v3/server/define/SynchronousDispatcherInstrumentation.java +++ b/apm-sniffer/apm-sdk-plugin/resteasy-plugin/resteasy-server-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/resteasy/v3/server/define/SynchronousDispatcherInstrumentation.java @@ -83,4 +83,9 @@ public boolean isOverrideArgs() { protected ClassMatch enhanceClass() { return NameMatch.byName(ENHANCE_CLASS); } + + @Override + protected String[] witnessClasses() { + return new String[]{"org.jboss.resteasy.core.Dispatcher"}; + } } diff --git a/apm-sniffer/apm-sdk-plugin/resteasy-plugin/resteasy-server-4.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/resteasy-plugin/resteasy-server-4.x-plugin/pom.xml index 6b216a9bec..63eb0c4826 100644 --- a/apm-sniffer/apm-sdk-plugin/resteasy-plugin/resteasy-server-4.x-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/resteasy-plugin/resteasy-server-4.x-plugin/pom.xml @@ -21,7 +21,7 @@ resteasy-plugin org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT resteasy-server-4.x-plugin diff --git a/apm-sniffer/apm-sdk-plugin/resteasy-plugin/resteasy-server-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/resteasy/v4/server/define/SynchronousDispatcherInstrumentation.java b/apm-sniffer/apm-sdk-plugin/resteasy-plugin/resteasy-server-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/resteasy/v4/server/define/SynchronousDispatcherInstrumentation.java index 245994dc69..206af100d8 100644 --- a/apm-sniffer/apm-sdk-plugin/resteasy-plugin/resteasy-server-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/resteasy/v4/server/define/SynchronousDispatcherInstrumentation.java +++ b/apm-sniffer/apm-sdk-plugin/resteasy-plugin/resteasy-server-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/resteasy/v4/server/define/SynchronousDispatcherInstrumentation.java @@ -20,13 +20,18 @@ import net.bytebuddy.description.method.MethodDescription; import net.bytebuddy.matcher.ElementMatcher; +import org.apache.skywalking.apm.agent.core.plugin.WitnessMethod; import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint; import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint; import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine; import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch; import org.apache.skywalking.apm.agent.core.plugin.match.NameMatch; +import java.util.Collections; +import java.util.List; + import static net.bytebuddy.matcher.ElementMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.returns; import static net.bytebuddy.matcher.ElementMatchers.takesArguments; public class SynchronousDispatcherInstrumentation extends ClassInstanceMethodsEnhancePluginDefine { @@ -83,4 +88,17 @@ public boolean isOverrideArgs() { protected ClassMatch enhanceClass() { return NameMatch.byName(ENHANCE_CLASS); } + + @Override + protected String[] witnessClasses() { + return new String[]{"org.jboss.resteasy.core.InternalDispatcher"}; + } + + @Override + protected List witnessMethods() { + return Collections.singletonList(new WitnessMethod( + "org.jboss.resteasy.spi.Dispatcher", + named("internalInvocation").and(returns(named("javax.ws.rs.core.Response"))) + )); + } } diff --git a/apm-sniffer/apm-sdk-plugin/resteasy-plugin/resteasy-server-6.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/resteasy-plugin/resteasy-server-6.x-plugin/pom.xml index 822d6a5c5f..f20ab70b3e 100644 --- a/apm-sniffer/apm-sdk-plugin/resteasy-plugin/resteasy-server-6.x-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/resteasy-plugin/resteasy-server-6.x-plugin/pom.xml @@ -21,7 +21,7 @@ resteasy-plugin org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT resteasy-server-6.x-plugin diff --git a/apm-sniffer/apm-sdk-plugin/resteasy-plugin/resteasy-server-6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/resteasy/v6/server/define/SynchronousDispatcherInstrumentation.java b/apm-sniffer/apm-sdk-plugin/resteasy-plugin/resteasy-server-6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/resteasy/v6/server/define/SynchronousDispatcherInstrumentation.java index c4a472487c..d38fb5775d 100644 --- a/apm-sniffer/apm-sdk-plugin/resteasy-plugin/resteasy-server-6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/resteasy/v6/server/define/SynchronousDispatcherInstrumentation.java +++ b/apm-sniffer/apm-sdk-plugin/resteasy-plugin/resteasy-server-6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/resteasy/v6/server/define/SynchronousDispatcherInstrumentation.java @@ -20,13 +20,18 @@ import net.bytebuddy.description.method.MethodDescription; import net.bytebuddy.matcher.ElementMatcher; +import org.apache.skywalking.apm.agent.core.plugin.WitnessMethod; import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint; import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint; import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine; import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch; import org.apache.skywalking.apm.agent.core.plugin.match.NameMatch; +import java.util.Collections; +import java.util.List; + import static net.bytebuddy.matcher.ElementMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.returns; import static net.bytebuddy.matcher.ElementMatchers.takesArguments; public class SynchronousDispatcherInstrumentation extends ClassInstanceMethodsEnhancePluginDefine { @@ -83,4 +88,17 @@ public boolean isOverrideArgs() { protected ClassMatch enhanceClass() { return NameMatch.byName(ENHANCE_CLASS); } + + @Override + protected String[] witnessClasses() { + return new String[]{"org.jboss.resteasy.core.InternalDispatcher"}; + } + + @Override + protected List witnessMethods() { + return Collections.singletonList(new WitnessMethod( + "org.jboss.resteasy.spi.Dispatcher", + named("internalInvocation").and(returns(named("jakarta.ws.rs.core.Response"))) + )); + } } diff --git a/apm-sniffer/apm-sdk-plugin/rocketMQ-3.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/rocketMQ-3.x-plugin/pom.xml index 55687939a6..5a7c711dd2 100644 --- a/apm-sniffer/apm-sdk-plugin/rocketMQ-3.x-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/rocketMQ-3.x-plugin/pom.xml @@ -21,7 +21,7 @@ apm-sdk-plugin org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-sdk-plugin/rocketMQ-3.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/rocketMQ/v3/OnExceptionInterceptorTest.java b/apm-sniffer/apm-sdk-plugin/rocketMQ-3.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/rocketMQ/v3/OnExceptionInterceptorTest.java index 59bf780148..6b5ba982f8 100644 --- a/apm-sniffer/apm-sdk-plugin/rocketMQ-3.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/rocketMQ/v3/OnExceptionInterceptorTest.java +++ b/apm-sniffer/apm-sdk-plugin/rocketMQ-3.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/rocketMQ/v3/OnExceptionInterceptorTest.java @@ -23,6 +23,7 @@ import static org.mockito.Mockito.when; import java.util.List; import org.apache.skywalking.apm.agent.core.context.ContextSnapshot; +import org.apache.skywalking.apm.agent.core.context.MockContextSnapshot; import org.apache.skywalking.apm.agent.core.context.trace.AbstractTracingSpan; import org.apache.skywalking.apm.agent.core.context.trace.TraceSegment; import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; @@ -55,7 +56,6 @@ public class OnExceptionInterceptorTest { @Rule public MockitoRule rule = MockitoJUnit.rule(); - @Mock private ContextSnapshot contextSnapshot; private SendCallBackEnhanceInfo enhanceInfo; @@ -65,6 +65,7 @@ public class OnExceptionInterceptorTest { @Before public void setUp() { exceptionInterceptor = new OnExceptionInterceptor(); + contextSnapshot = MockContextSnapshot.INSTANCE.mockContextSnapshot(); enhanceInfo = new SendCallBackEnhanceInfo("test", contextSnapshot); when(enhancedInstance.getSkyWalkingDynamicField()).thenReturn(enhanceInfo); diff --git a/apm-sniffer/apm-sdk-plugin/rocketMQ-3.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/rocketMQ/v3/OnSuccessInterceptorTest.java b/apm-sniffer/apm-sdk-plugin/rocketMQ-3.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/rocketMQ/v3/OnSuccessInterceptorTest.java index 0927f8a793..77c69a01e6 100644 --- a/apm-sniffer/apm-sdk-plugin/rocketMQ-3.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/rocketMQ/v3/OnSuccessInterceptorTest.java +++ b/apm-sniffer/apm-sdk-plugin/rocketMQ-3.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/rocketMQ/v3/OnSuccessInterceptorTest.java @@ -23,6 +23,7 @@ import static org.mockito.Mockito.when; import java.util.List; import org.apache.skywalking.apm.agent.core.context.ContextSnapshot; +import org.apache.skywalking.apm.agent.core.context.MockContextSnapshot; import org.apache.skywalking.apm.agent.core.context.trace.AbstractTracingSpan; import org.apache.skywalking.apm.agent.core.context.trace.TraceSegment; import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; @@ -57,7 +58,6 @@ public class OnSuccessInterceptorTest { @Rule public MockitoRule rule = MockitoJUnit.rule(); - @Mock private ContextSnapshot contextSnapshot; @Mock private SendResult sendResult; @@ -70,6 +70,7 @@ public class OnSuccessInterceptorTest { @Before public void setUp() { successInterceptor = new OnSuccessInterceptor(); + contextSnapshot = MockContextSnapshot.INSTANCE.mockContextSnapshot(); enhanceInfo = new SendCallBackEnhanceInfo("test", contextSnapshot); when(enhancedInstance.getSkyWalkingDynamicField()).thenReturn(enhanceInfo); diff --git a/apm-sniffer/apm-sdk-plugin/rocketMQ-4.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/rocketMQ-4.x-plugin/pom.xml index 08f8accaf4..a30e1b09e0 100644 --- a/apm-sniffer/apm-sdk-plugin/rocketMQ-4.x-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/rocketMQ-4.x-plugin/pom.xml @@ -21,7 +21,7 @@ apm-sdk-plugin org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-sdk-plugin/rocketMQ-4.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/rocketMQ/v4/OnExceptionInterceptorTest.java b/apm-sniffer/apm-sdk-plugin/rocketMQ-4.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/rocketMQ/v4/OnExceptionInterceptorTest.java index 169c3b59bb..3bc9436b1d 100644 --- a/apm-sniffer/apm-sdk-plugin/rocketMQ-4.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/rocketMQ/v4/OnExceptionInterceptorTest.java +++ b/apm-sniffer/apm-sdk-plugin/rocketMQ-4.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/rocketMQ/v4/OnExceptionInterceptorTest.java @@ -23,6 +23,7 @@ import static org.mockito.Mockito.when; import java.util.List; import org.apache.skywalking.apm.agent.core.context.ContextSnapshot; +import org.apache.skywalking.apm.agent.core.context.MockContextSnapshot; import org.apache.skywalking.apm.agent.core.context.trace.AbstractTracingSpan; import org.apache.skywalking.apm.agent.core.context.trace.TraceSegment; import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; @@ -55,7 +56,6 @@ public class OnExceptionInterceptorTest { @Rule public MockitoRule rule = MockitoJUnit.rule(); - @Mock private ContextSnapshot contextSnapshot; private SendCallBackEnhanceInfo enhanceInfo; @@ -69,6 +69,7 @@ public void setUp() { @Test public void testOnException() throws Throwable { + contextSnapshot = MockContextSnapshot.INSTANCE.mockContextSnapshot(); enhanceInfo = new SendCallBackEnhanceInfo("test", contextSnapshot); when(enhancedInstance.getSkyWalkingDynamicField()).thenReturn(enhanceInfo); diff --git a/apm-sniffer/apm-sdk-plugin/rocketMQ-4.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/rocketMQ/v4/OnSuccessInterceptorTest.java b/apm-sniffer/apm-sdk-plugin/rocketMQ-4.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/rocketMQ/v4/OnSuccessInterceptorTest.java index d432daafd8..40c718d9b3 100644 --- a/apm-sniffer/apm-sdk-plugin/rocketMQ-4.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/rocketMQ/v4/OnSuccessInterceptorTest.java +++ b/apm-sniffer/apm-sdk-plugin/rocketMQ-4.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/rocketMQ/v4/OnSuccessInterceptorTest.java @@ -25,6 +25,7 @@ import org.apache.rocketmq.client.producer.SendResult; import org.apache.rocketmq.client.producer.SendStatus; import org.apache.skywalking.apm.agent.core.context.ContextSnapshot; +import org.apache.skywalking.apm.agent.core.context.MockContextSnapshot; import org.apache.skywalking.apm.agent.core.context.trace.AbstractTracingSpan; import org.apache.skywalking.apm.agent.core.context.trace.TraceSegment; import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; @@ -57,7 +58,6 @@ public class OnSuccessInterceptorTest { @Rule public MockitoRule rule = MockitoJUnit.rule(); - @Mock private ContextSnapshot contextSnapshot; @Mock private SendResult sendResult; @@ -70,6 +70,7 @@ public class OnSuccessInterceptorTest { @Before public void setUp() { successInterceptor = new OnSuccessInterceptor(); + contextSnapshot = MockContextSnapshot.INSTANCE.mockContextSnapshot(); enhanceInfo = new SendCallBackEnhanceInfo("test", contextSnapshot); when(enhancedInstance.getSkyWalkingDynamicField()).thenReturn(enhanceInfo); diff --git a/apm-sniffer/apm-sdk-plugin/rocketMQ-5.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/rocketMQ-5.x-plugin/pom.xml index 0c5d663df1..62c1fbb3e3 100644 --- a/apm-sniffer/apm-sdk-plugin/rocketMQ-5.x-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/rocketMQ-5.x-plugin/pom.xml @@ -21,7 +21,7 @@ apm-sdk-plugin org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-sdk-plugin/rocketMQ-client-java-5.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/rocketMQ-client-java-5.x-plugin/pom.xml index 69d049ab83..e4dc8d8988 100644 --- a/apm-sniffer/apm-sdk-plugin/rocketMQ-client-java-5.x-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/rocketMQ-client-java-5.x-plugin/pom.xml @@ -21,7 +21,7 @@ apm-sdk-plugin org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-sdk-plugin/servicecomb-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/servicecomb-plugin/pom.xml index 7c231e0dab..06f9d58672 100644 --- a/apm-sniffer/apm-sdk-plugin/servicecomb-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/servicecomb-plugin/pom.xml @@ -21,7 +21,7 @@ apm-sdk-plugin org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 @@ -37,6 +37,5 @@ UTF-8 - /.. diff --git a/apm-sniffer/apm-sdk-plugin/servicecomb-plugin/servicecomb-java-chassis-2.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/servicecomb-plugin/servicecomb-java-chassis-2.x-plugin/pom.xml index beec444fba..6c26908ce4 100644 --- a/apm-sniffer/apm-sdk-plugin/servicecomb-plugin/servicecomb-java-chassis-2.x-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/servicecomb-plugin/servicecomb-java-chassis-2.x-plugin/pom.xml @@ -21,7 +21,7 @@ servicecomb-plugin org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-sdk-plugin/shardingsphere-plugins/pom.xml b/apm-sniffer/apm-sdk-plugin/shardingsphere-plugins/pom.xml index c2f9bb6529..6d38f040f2 100644 --- a/apm-sniffer/apm-sdk-plugin/shardingsphere-plugins/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/shardingsphere-plugins/pom.xml @@ -21,7 +21,7 @@ apm-sdk-plugin org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 pom @@ -36,6 +36,5 @@ UTF-8 - /.. diff --git a/apm-sniffer/apm-sdk-plugin/shardingsphere-plugins/sharding-sphere-3.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/shardingsphere-plugins/sharding-sphere-3.x-plugin/pom.xml index 7d8268cba3..033f5afc04 100644 --- a/apm-sniffer/apm-sdk-plugin/shardingsphere-plugins/sharding-sphere-3.x-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/shardingsphere-plugins/sharding-sphere-3.x-plugin/pom.xml @@ -21,7 +21,7 @@ shardingsphere-plugins org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-sdk-plugin/shardingsphere-plugins/sharding-sphere-4.0.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/shardingsphere-plugins/sharding-sphere-4.0.x-plugin/pom.xml index f772019ae2..fa282b6571 100644 --- a/apm-sniffer/apm-sdk-plugin/shardingsphere-plugins/sharding-sphere-4.0.x-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/shardingsphere-plugins/sharding-sphere-4.0.x-plugin/pom.xml @@ -20,7 +20,7 @@ shardingsphere-plugins org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-sdk-plugin/shardingsphere-plugins/sharding-sphere-4.1.0-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/shardingsphere-plugins/sharding-sphere-4.1.0-plugin/pom.xml index a34ab484c2..803c61dbeb 100644 --- a/apm-sniffer/apm-sdk-plugin/shardingsphere-plugins/sharding-sphere-4.1.0-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/shardingsphere-plugins/sharding-sphere-4.1.0-plugin/pom.xml @@ -20,7 +20,7 @@ shardingsphere-plugins org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-sdk-plugin/shardingsphere-plugins/sharding-sphere-5.0.0-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/shardingsphere-plugins/sharding-sphere-5.0.0-plugin/pom.xml index 8ebfdfc1d1..e17d47b46d 100644 --- a/apm-sniffer/apm-sdk-plugin/shardingsphere-plugins/sharding-sphere-5.0.0-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/shardingsphere-plugins/sharding-sphere-5.0.0-plugin/pom.xml @@ -21,7 +21,7 @@ shardingsphere-plugins org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-sdk-plugin/sofarpc-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/sofarpc-plugin/pom.xml index c87bf752fb..21f8795238 100644 --- a/apm-sniffer/apm-sdk-plugin/sofarpc-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/sofarpc-plugin/pom.xml @@ -20,7 +20,7 @@ apm-sdk-plugin org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-sdk-plugin/sofarpc-plugin/src/main/java/org/apache/skywalking/apm/plugin/sofarpc/InvokeCallbackWrapper.java b/apm-sniffer/apm-sdk-plugin/sofarpc-plugin/src/main/java/org/apache/skywalking/apm/plugin/sofarpc/InvokeCallbackWrapper.java new file mode 100644 index 0000000000..cf806cb592 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/sofarpc-plugin/src/main/java/org/apache/skywalking/apm/plugin/sofarpc/InvokeCallbackWrapper.java @@ -0,0 +1,88 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.sofarpc; + +import com.alipay.remoting.InvokeCallback; +import java.util.concurrent.Executor; +import lombok.AccessLevel; +import lombok.Getter; +import org.apache.skywalking.apm.agent.core.context.ContextManager; +import org.apache.skywalking.apm.agent.core.context.ContextSnapshot; +import org.apache.skywalking.apm.agent.core.context.trace.AbstractSpan; + +public class InvokeCallbackWrapper implements InvokeCallback { + + @Getter(AccessLevel.PACKAGE) + private ContextSnapshot contextSnapshot; + @Getter(AccessLevel.PACKAGE) + private final InvokeCallback invokeCallback; + + public InvokeCallbackWrapper(InvokeCallback invokeCallback) { + if (ContextManager.isActive()) { + this.contextSnapshot = ContextManager.capture(); + } + this.invokeCallback = invokeCallback; + } + + @Override + public void onResponse(final Object o) { + ContextManager.createLocalSpan("Thread/" + invokeCallback.getClass().getName() + "/onResponse"); + if (contextSnapshot != null) { + ContextManager.continued(contextSnapshot); + } + try { + invokeCallback.onResponse(o); + } catch (Throwable t) { + ContextManager.activeSpan().log(t); + throw t; + } finally { + contextSnapshot = null; + ContextManager.stopSpan(); + } + + } + + @Override + public void onException(final Throwable throwable) { + ContextManager.createLocalSpan("Thread/" + invokeCallback.getClass().getName() + "/onException"); + if (contextSnapshot != null) { + ContextManager.continued(contextSnapshot); + } + if (throwable != null) { + AbstractSpan abstractSpan = ContextManager.activeSpan(); + if (abstractSpan != null) { + abstractSpan.log(throwable); + } + } + try { + invokeCallback.onException(throwable); + } catch (Throwable t) { + ContextManager.activeSpan().log(t); + throw t; + } finally { + contextSnapshot = null; + ContextManager.stopSpan(); + } + } + + @Override + public Executor getExecutor() { + return invokeCallback.getExecutor(); + } +} diff --git a/apm-sniffer/apm-sdk-plugin/sofarpc-plugin/src/main/java/org/apache/skywalking/apm/plugin/sofarpc/SofaBoltCallbackInstrumentation.java b/apm-sniffer/apm-sdk-plugin/sofarpc-plugin/src/main/java/org/apache/skywalking/apm/plugin/sofarpc/SofaBoltCallbackInstrumentation.java new file mode 100644 index 0000000000..6547042bc6 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/sofarpc-plugin/src/main/java/org/apache/skywalking/apm/plugin/sofarpc/SofaBoltCallbackInstrumentation.java @@ -0,0 +1,70 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.sofarpc; + +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.matcher.ElementMatcher; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine; +import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch; +import org.apache.skywalking.apm.agent.core.plugin.match.NameMatch; + +import static net.bytebuddy.matcher.ElementMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.takesArguments; + +public class SofaBoltCallbackInstrumentation extends ClassInstanceMethodsEnhancePluginDefine { + + private static final String ENHANCE_CLASS = "com.alipay.remoting.BaseRemoting"; + private static final String INVOKE_METHOD_INTERCEPTOR = "org.apache.skywalking.apm.plugin.sofarpc.SofaBoltCallbackInvokeInterceptor"; + private static final String INVOKE_METHOD = "invokeWithCallback"; + + @Override + protected ClassMatch enhanceClass() { + return NameMatch.byName(ENHANCE_CLASS); + } + + @Override + public ConstructorInterceptPoint[] getConstructorsInterceptPoints() { + return null; + } + + @Override + public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() { + return new InstanceMethodsInterceptPoint[] { + new InstanceMethodsInterceptPoint() { + @Override + public ElementMatcher getMethodsMatcher() { + return named(INVOKE_METHOD).and( + takesArguments(4)); + } + + @Override + public String getMethodsInterceptor() { + return INVOKE_METHOD_INTERCEPTOR; + } + + @Override + public boolean isOverrideArgs() { + return true; + } + } + }; + } +} diff --git a/apm-sniffer/apm-sdk-plugin/sofarpc-plugin/src/main/java/org/apache/skywalking/apm/plugin/sofarpc/SofaBoltCallbackInvokeInterceptor.java b/apm-sniffer/apm-sdk-plugin/sofarpc-plugin/src/main/java/org/apache/skywalking/apm/plugin/sofarpc/SofaBoltCallbackInvokeInterceptor.java new file mode 100644 index 0000000000..c890b4a90d --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/sofarpc-plugin/src/main/java/org/apache/skywalking/apm/plugin/sofarpc/SofaBoltCallbackInvokeInterceptor.java @@ -0,0 +1,55 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.sofarpc; + +import com.alipay.remoting.InvokeCallback; +import java.lang.reflect.Method; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceMethodsAroundInterceptor; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult; + +public class SofaBoltCallbackInvokeInterceptor implements InstanceMethodsAroundInterceptor { + @Override + public void beforeMethod(EnhancedInstance objInst, + Method method, + Object[] allArguments, + Class[] argumentsTypes, + MethodInterceptResult result) { + if (allArguments[2] instanceof InvokeCallback) { + allArguments[2] = new InvokeCallbackWrapper((InvokeCallback) allArguments[2]); + } + } + + @Override + public Object afterMethod(EnhancedInstance objInst, + Method method, + Object[] allArguments, + Class[] argumentsTypes, + Object ret) { + return ret; + } + + @Override + public void handleMethodException(EnhancedInstance objInst, + Method method, + Object[] allArguments, + Class[] argumentsTypes, + Throwable t) { + } +} diff --git a/apm-sniffer/apm-sdk-plugin/sofarpc-plugin/src/main/resources/skywalking-plugin.def b/apm-sniffer/apm-sdk-plugin/sofarpc-plugin/src/main/resources/skywalking-plugin.def index 9850487d56..72682ac2bc 100644 --- a/apm-sniffer/apm-sdk-plugin/sofarpc-plugin/src/main/resources/skywalking-plugin.def +++ b/apm-sniffer/apm-sdk-plugin/sofarpc-plugin/src/main/resources/skywalking-plugin.def @@ -16,3 +16,4 @@ sofarpc=org.apache.skywalking.apm.plugin.sofarpc.SofaRpcConsumerInstrumentation sofarpc=org.apache.skywalking.apm.plugin.sofarpc.SofaRpcProviderInstrumentation +sofarpc=org.apache.skywalking.apm.plugin.sofarpc.SofaBoltCallbackInstrumentation diff --git a/apm-sniffer/apm-sdk-plugin/sofarpc-plugin/src/test/java/org/apache/skywalking/apm/plugin/sofarpc/InvokeCallbackWrapperTest.java b/apm-sniffer/apm-sdk-plugin/sofarpc-plugin/src/test/java/org/apache/skywalking/apm/plugin/sofarpc/InvokeCallbackWrapperTest.java new file mode 100644 index 0000000000..d05d4026c2 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/sofarpc-plugin/src/test/java/org/apache/skywalking/apm/plugin/sofarpc/InvokeCallbackWrapperTest.java @@ -0,0 +1,176 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.sofarpc; + +import com.alipay.remoting.InvokeCallback; +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; +import org.apache.skywalking.apm.agent.core.context.ContextManager; +import org.apache.skywalking.apm.agent.core.context.trace.AbstractTracingSpan; +import org.apache.skywalking.apm.agent.core.context.trace.TraceSegment; +import org.apache.skywalking.apm.agent.test.helper.SegmentHelper; +import org.apache.skywalking.apm.agent.test.helper.SpanHelper; +import org.apache.skywalking.apm.agent.test.tools.AgentServiceRule; +import org.apache.skywalking.apm.agent.test.tools.SegmentStorage; +import org.apache.skywalking.apm.agent.test.tools.SegmentStoragePoint; +import org.apache.skywalking.apm.agent.test.tools.TracingSegmentRunner; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThat; + +@RunWith(TracingSegmentRunner.class) +public class InvokeCallbackWrapperTest { + + @SegmentStoragePoint + private SegmentStorage segmentStorage; + + private Executor executor = Executors.newFixedThreadPool(1); + + @Rule + public AgentServiceRule agentServiceRule = new AgentServiceRule(); + @Rule + public MockitoRule rule = MockitoJUnit.rule(); + + private InvokeCallback callback; + + @Before + public void before() { + callback = new InvokeCallback() { + @Override + public void onResponse(final Object o) { + } + + @Override + public void onException(final Throwable throwable) { + } + + @Override + public Executor getExecutor() { + return null; + } + }; + } + + static class WrapperWrapper implements InvokeCallback { + + private InvokeCallback callback; + + private CountDownLatch countDownLatch; + + public CountDownLatch getCountDownLatch() { + return countDownLatch; + } + + public WrapperWrapper(InvokeCallback callback) { + this.countDownLatch = new CountDownLatch(1); + this.callback = callback; + } + + @Override + public void onResponse(final Object o) { + callback.onResponse(o); + countDownLatch.countDown(); + } + + @Override + public void onException(final Throwable throwable) { + callback.onException(throwable); + countDownLatch.countDown(); + } + + @Override + public Executor getExecutor() { + return null; + } + } + + @Test + public void testConstruct() { + InvokeCallbackWrapper wrapper = new InvokeCallbackWrapper(callback); + Assert.assertSame(callback, wrapper.getInvokeCallback()); + Assert.assertNull(wrapper.getContextSnapshot()); + + ContextManager.createEntrySpan("sofarpc", null); + wrapper = new InvokeCallbackWrapper(callback); + Assert.assertSame(callback, wrapper.getInvokeCallback()); + Assert.assertEquals(ContextManager.getGlobalTraceId(), wrapper.getContextSnapshot().getTraceId().getId()); + Assert.assertEquals("sofarpc", wrapper.getContextSnapshot().getParentEndpoint()); + ContextManager.stopSpan(); + } + + @Test + public void testOnResponse() throws InterruptedException { + ContextManager.createEntrySpan("sofarpc", null); + InvokeCallbackWrapper wrapper = new InvokeCallbackWrapper(callback); + final WrapperWrapper wrapperWrapper = new WrapperWrapper(wrapper); + executor.execute(() -> wrapperWrapper.onResponse(null)); + ContextManager.stopSpan(); + wrapperWrapper.getCountDownLatch().await(); + + assertThat(segmentStorage.getTraceSegments().size(), is(2)); + TraceSegment traceSegment = segmentStorage.getTraceSegments().get(0); + List spans = SegmentHelper.getSpans(traceSegment); + assertThat(spans.size(), is(1)); + + TraceSegment traceSegment2 = segmentStorage.getTraceSegments().get(1); + List spans2 = SegmentHelper.getSpans(traceSegment2); + assertThat(spans2.size(), is(1)); + + // Segment order is non-deterministic; find the child segment (the one with a ref) + TraceSegment childSegment = traceSegment.getRef() != null ? traceSegment : traceSegment2; + assertEquals("sofarpc", childSegment.getRef().getParentEndpoint()); + } + + @Test + public void testOnException() throws InterruptedException { + ContextManager.createEntrySpan("sofarpc", null); + InvokeCallbackWrapper wrapper = new InvokeCallbackWrapper(callback); + final WrapperWrapper wrapperWrapper = new WrapperWrapper(wrapper); + final Throwable throwable = new Throwable(); + executor.execute(() -> wrapperWrapper.onException(throwable)); + ContextManager.stopSpan(); + wrapperWrapper.getCountDownLatch().await(); + + assertThat(segmentStorage.getTraceSegments().size(), is(2)); + TraceSegment traceSegment = segmentStorage.getTraceSegments().get(0); + List spans = SegmentHelper.getSpans(traceSegment); + assertThat(spans.size(), is(1)); + + TraceSegment traceSegment2 = segmentStorage.getTraceSegments().get(1); + List spans2 = SegmentHelper.getSpans(traceSegment2); + assertThat(spans2.size(), is(1)); + + // Segment order is non-deterministic; find the child segment (the one with a ref) + TraceSegment childSegment = traceSegment.getRef() != null ? traceSegment : traceSegment2; + List childSpans = SegmentHelper.getSpans(childSegment); + assertThat(SpanHelper.getLogs(childSpans.get(0)).size(), is(1)); + + } + +} \ No newline at end of file diff --git a/apm-sniffer/apm-sdk-plugin/sofarpc-plugin/src/test/java/org/apache/skywalking/apm/plugin/sofarpc/SofaBoltCallbackInvokeInterceptorTest.java b/apm-sniffer/apm-sdk-plugin/sofarpc-plugin/src/test/java/org/apache/skywalking/apm/plugin/sofarpc/SofaBoltCallbackInvokeInterceptorTest.java new file mode 100644 index 0000000000..bc082d7d2b --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/sofarpc-plugin/src/test/java/org/apache/skywalking/apm/plugin/sofarpc/SofaBoltCallbackInvokeInterceptorTest.java @@ -0,0 +1,79 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.sofarpc; + +import com.alipay.remoting.InvokeCallback; +import java.util.concurrent.Executor; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +public class SofaBoltCallbackInvokeInterceptorTest { + InvokeCallback callback; + Object obj; + Object[] matchArgs; + Object[] mismatchArgs; + + @Before + public void before() { + callback = new InvokeCallback() { + @Override + public void onResponse(final Object o) { + + } + + @Override + public void onException(final Throwable throwable) { + + } + + @Override + public Executor getExecutor() { + return null; + } + }; + + obj = new Object(); + + matchArgs = new Object[] { + null, + null, + callback, + null + }; + mismatchArgs = new Object[] { + null, + null, + obj, + null + }; + } + + @Test + public void testOverrideArguments() { + final SofaBoltCallbackInvokeInterceptor interceptor = new SofaBoltCallbackInvokeInterceptor(); + interceptor.beforeMethod(null, null, matchArgs, null, null); + Assert.assertTrue(matchArgs[2] instanceof InvokeCallbackWrapper); + Assert.assertSame(callback, ((InvokeCallbackWrapper) matchArgs[2]).getInvokeCallback()); + + interceptor.beforeMethod(null, null, mismatchArgs, null, null); + Assert.assertSame(obj, mismatchArgs[2]); + } + +} \ No newline at end of file diff --git a/apm-sniffer/apm-sdk-plugin/sofarpc-plugin/src/test/java/org/apache/skywalking/apm/plugin/sofarpc/SofaRpcConsumerInterceptorTest.java b/apm-sniffer/apm-sdk-plugin/sofarpc-plugin/src/test/java/org/apache/skywalking/apm/plugin/sofarpc/SofaRpcConsumerInterceptorTest.java index 3dc0903ec0..c0c3420966 100644 --- a/apm-sniffer/apm-sdk-plugin/sofarpc-plugin/src/test/java/org/apache/skywalking/apm/plugin/sofarpc/SofaRpcConsumerInterceptorTest.java +++ b/apm-sniffer/apm-sdk-plugin/sofarpc-plugin/src/test/java/org/apache/skywalking/apm/plugin/sofarpc/SofaRpcConsumerInterceptorTest.java @@ -18,11 +18,11 @@ package org.apache.skywalking.apm.plugin.sofarpc; -import static org.hamcrest.CoreMatchers.is; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.when; +import com.alipay.sofa.rpc.client.ProviderInfo; +import com.alipay.sofa.rpc.context.RpcInternalContext; +import com.alipay.sofa.rpc.core.request.SofaRequest; +import com.alipay.sofa.rpc.core.response.SofaResponse; +import com.alipay.sofa.rpc.filter.ConsumerInvoker; import java.util.List; import org.apache.skywalking.apm.agent.core.conf.Config; import org.apache.skywalking.apm.agent.core.context.trace.AbstractTracingSpan; @@ -50,11 +50,12 @@ import org.mockito.Mockito; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; -import com.alipay.sofa.rpc.client.ProviderInfo; -import com.alipay.sofa.rpc.context.RpcInternalContext; -import com.alipay.sofa.rpc.core.request.SofaRequest; -import com.alipay.sofa.rpc.core.response.SofaResponse; -import com.alipay.sofa.rpc.filter.ConsumerInvoker; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.when; @RunWith(TracingSegmentRunner.class) public class SofaRpcConsumerInterceptorTest { @@ -121,7 +122,8 @@ public void setUp() throws Exception { @Test public void testConsumerWithAttachment() throws Throwable { - sofaRpcConsumerInterceptor.beforeMethod(enhancedInstance, null, allArguments, argumentTypes, methodInterceptResult); + sofaRpcConsumerInterceptor.beforeMethod( + enhancedInstance, null, allArguments, argumentTypes, methodInterceptResult); sofaRpcConsumerInterceptor.afterMethod(enhancedInstance, null, allArguments, argumentTypes, sofaResponse); assertThat(segmentStorage.getTraceSegments().size(), is(1)); @@ -133,8 +135,10 @@ public void testConsumerWithAttachment() throws Throwable { @Test public void testConsumerWithException() throws Throwable { - sofaRpcConsumerInterceptor.beforeMethod(enhancedInstance, null, allArguments, argumentTypes, methodInterceptResult); - sofaRpcConsumerInterceptor.handleMethodException(enhancedInstance, null, allArguments, argumentTypes, new RuntimeException()); + sofaRpcConsumerInterceptor.beforeMethod( + enhancedInstance, null, allArguments, argumentTypes, methodInterceptResult); + sofaRpcConsumerInterceptor.handleMethodException( + enhancedInstance, null, allArguments, argumentTypes, new RuntimeException()); sofaRpcConsumerInterceptor.afterMethod(enhancedInstance, null, allArguments, argumentTypes, sofaResponse); assertThat(segmentStorage.getTraceSegments().size(), is(1)); TraceSegment traceSegment = segmentStorage.getTraceSegments().get(0); @@ -146,7 +150,8 @@ public void testConsumerWithResultHasException() throws Throwable { when(sofaResponse.isError()).thenReturn(true); when(sofaResponse.getAppResponse()).thenReturn(new RuntimeException()); - sofaRpcConsumerInterceptor.beforeMethod(enhancedInstance, null, allArguments, argumentTypes, methodInterceptResult); + sofaRpcConsumerInterceptor.beforeMethod( + enhancedInstance, null, allArguments, argumentTypes, methodInterceptResult); sofaRpcConsumerInterceptor.afterMethod(enhancedInstance, null, allArguments, argumentTypes, sofaResponse); assertThat(segmentStorage.getTraceSegments().size(), is(1)); @@ -180,8 +185,11 @@ private void assertCommonsAttribute(AbstractTracingSpan span) { assertThat(tags.size(), is(1)); assertThat(SpanHelper.getLayer(span), CoreMatchers.is(SpanLayer.RPC_FRAMEWORK)); assertThat(SpanHelper.getComponentId(span), is(43)); - assertThat(tags.get(0) - .getValue(), is("bolt://127.0.0.1:12200/org.apache.skywalking.apm.test.TestSofaRpcService.test(String)")); + assertThat( + tags.get(0) + .getValue(), + is("bolt://127.0.0.1:12200/org.apache.skywalking.apm.test.TestSofaRpcService.test(String)") + ); assertThat(span.getOperationName(), is("org.apache.skywalking.apm.test.TestSofaRpcService.test(String)")); } } diff --git a/apm-sniffer/apm-sdk-plugin/sofarpc-plugin/src/test/java/org/apache/skywalking/apm/plugin/sofarpc/SofaRpcProviderInterceptorTest.java b/apm-sniffer/apm-sdk-plugin/sofarpc-plugin/src/test/java/org/apache/skywalking/apm/plugin/sofarpc/SofaRpcProviderInterceptorTest.java index 43c2938e5e..80edc8016a 100644 --- a/apm-sniffer/apm-sdk-plugin/sofarpc-plugin/src/test/java/org/apache/skywalking/apm/plugin/sofarpc/SofaRpcProviderInterceptorTest.java +++ b/apm-sniffer/apm-sdk-plugin/sofarpc-plugin/src/test/java/org/apache/skywalking/apm/plugin/sofarpc/SofaRpcProviderInterceptorTest.java @@ -18,10 +18,11 @@ package org.apache.skywalking.apm.plugin.sofarpc; -import static org.hamcrest.CoreMatchers.is; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.when; +import com.alipay.sofa.rpc.client.ProviderInfo; +import com.alipay.sofa.rpc.context.RpcInternalContext; +import com.alipay.sofa.rpc.core.request.SofaRequest; +import com.alipay.sofa.rpc.core.response.SofaResponse; +import com.alipay.sofa.rpc.filter.ProviderInvoker; import java.util.List; import org.apache.skywalking.apm.agent.core.conf.Config; import org.apache.skywalking.apm.agent.core.context.SW8CarrierItem; @@ -51,11 +52,11 @@ import org.mockito.Mockito; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; -import com.alipay.sofa.rpc.client.ProviderInfo; -import com.alipay.sofa.rpc.context.RpcInternalContext; -import com.alipay.sofa.rpc.core.request.SofaRequest; -import com.alipay.sofa.rpc.core.response.SofaResponse; -import com.alipay.sofa.rpc.filter.ProviderInvoker; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.when; @RunWith(TracingSegmentRunner.class) public class SofaRpcProviderInterceptorTest { diff --git a/apm-sniffer/apm-sdk-plugin/solon-2.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/solon-2.x-plugin/pom.xml new file mode 100644 index 0000000000..543d27c2c4 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/solon-2.x-plugin/pom.xml @@ -0,0 +1,48 @@ + + + + + 4.0.0 + + apm-sdk-plugin + org.apache.skywalking + 9.7.0-SNAPSHOT + + + solon-2.x-plugin + jar + + solon-2.x-plugin + http://maven.apache.org + + + UTF-8 + 4.3 + 4.12 + + + + + org.noear + solon-lib + 2.8.3 + provided + + + diff --git a/apm-sniffer/apm-sdk-plugin/solon-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/solon/SolonActionExecuteInterceptor.java b/apm-sniffer/apm-sdk-plugin/solon-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/solon/SolonActionExecuteInterceptor.java new file mode 100644 index 0000000000..239e21cb80 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/solon-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/solon/SolonActionExecuteInterceptor.java @@ -0,0 +1,104 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.solon; + +import lombok.extern.slf4j.Slf4j; +import org.apache.skywalking.apm.agent.core.context.CarrierItem; +import org.apache.skywalking.apm.agent.core.context.ContextCarrier; +import org.apache.skywalking.apm.agent.core.context.ContextManager; +import org.apache.skywalking.apm.agent.core.context.tag.Tags; +import org.apache.skywalking.apm.agent.core.context.trace.AbstractSpan; +import org.apache.skywalking.apm.agent.core.context.trace.SpanLayer; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.v2.InstanceMethodsAroundInterceptorV2; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.v2.MethodInvocationContext; +import org.apache.skywalking.apm.network.trace.component.ComponentsDefine; +import org.apache.skywalking.apm.util.StringUtil; +import org.noear.solon.core.NvMap; +import org.noear.solon.core.handle.Context; + +import java.lang.reflect.Method; + +@Slf4j +public class SolonActionExecuteInterceptor implements InstanceMethodsAroundInterceptorV2 { + + @Override + public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, MethodInvocationContext context) throws Throwable { + Context ctx = (Context) allArguments[0]; + ContextCarrier contextCarrier = new ContextCarrier(); + CarrierItem next = contextCarrier.items(); + while (next.hasNext()) { + next = next.next(); + next.setHeadValue(ctx.header(next.getHeadKey())); + } + String operationName = ctx.method() + ":" + ctx.path(); + AbstractSpan span = ContextManager.createEntrySpan(operationName, contextCarrier); + span.setComponent(ComponentsDefine.SOLON_MVC); + SpanLayer.asHttp(span); + Tags.URL.set(span, ctx.url()); + Tags.HTTP.METHOD.set(span, ctx.method()); + if (SolonPluginConfig.Plugin.Solon.INCLUDE_HTTP_HEADERS != null && !SolonPluginConfig.Plugin.Solon.INCLUDE_HTTP_HEADERS.isEmpty()) { + NvMap includeHeaders = new NvMap(); + for (String header : SolonPluginConfig.Plugin.Solon.INCLUDE_HTTP_HEADERS) { + String value = ctx.header(header); + if (StringUtil.isNotBlank(value)) { + includeHeaders.put(header, value); + } + } + Tags.HTTP.HEADERS.set(span, includeHeaders.toString()); + } + if (SolonPluginConfig.Plugin.Solon.HTTP_BODY_LENGTH_THRESHOLD != 0) { + String body = ctx.body(); + if (StringUtil.isNotBlank(body)) { + if (SolonPluginConfig.Plugin.Solon.HTTP_BODY_LENGTH_THRESHOLD > 0 && body.length() > SolonPluginConfig.Plugin.Solon.HTTP_BODY_LENGTH_THRESHOLD) { + body = body.substring(0, SolonPluginConfig.Plugin.Solon.HTTP_BODY_LENGTH_THRESHOLD); + } + Tags.HTTP.BODY.set(span, body); + } + } + if (SolonPluginConfig.Plugin.Solon.HTTP_PARAMS_LENGTH_THRESHOLD != 0) { + String param = ctx.paramMap().toString(); + if (SolonPluginConfig.Plugin.Solon.HTTP_PARAMS_LENGTH_THRESHOLD > 0 && param.length() > SolonPluginConfig.Plugin.Solon.HTTP_PARAMS_LENGTH_THRESHOLD) { + param = param.substring(0, SolonPluginConfig.Plugin.Solon.HTTP_PARAMS_LENGTH_THRESHOLD); + } + Tags.HTTP.PARAMS.set(span, param); + } + } + + @Override + public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, Object ret, MethodInvocationContext context) { + Context ctx = (Context) allArguments[0]; + Tags.HTTP_RESPONSE_STATUS_CODE.set(ContextManager.activeSpan(), ctx.status()); + if (ctx.errors != null && context.getContext() == null) { + AbstractSpan activeSpan = ContextManager.activeSpan(); + activeSpan.errorOccurred(); + activeSpan.log(ctx.errors); + } + ContextManager.stopSpan(); + return ret; + } + + @Override + public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, Throwable t, MethodInvocationContext context) { + AbstractSpan activeSpan = ContextManager.activeSpan(); + activeSpan.errorOccurred(); + activeSpan.log(t); + context.setContext(true); + } +} diff --git a/apm-sniffer/apm-sdk-plugin/solon-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/solon/SolonPluginConfig.java b/apm-sniffer/apm-sdk-plugin/solon-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/solon/SolonPluginConfig.java new file mode 100644 index 0000000000..63a3f3ba98 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/solon-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/solon/SolonPluginConfig.java @@ -0,0 +1,43 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.solon; + +import org.apache.skywalking.apm.agent.core.boot.PluginConfig; + +import java.util.List; + +public class SolonPluginConfig { + public static class Plugin { + @PluginConfig(root = SolonPluginConfig.class) + public static class Solon { + /** + * Define the max length of collected HTTP parameters. The default value(=0) means not collecting. + */ + public static int HTTP_PARAMS_LENGTH_THRESHOLD = 0; + /** + * Define the max length of collected HTTP body. The default value(=0) means not collecting. + */ + public static int HTTP_BODY_LENGTH_THRESHOLD = 0; + /** + * It controls what header data should be collected, values must be in lower case, if empty, no header data will be collected. default is empty. + */ + public static List INCLUDE_HTTP_HEADERS ; + } + } +} diff --git a/apm-sniffer/apm-sdk-plugin/solon-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/solon/define/SolonActionInstrumentation.java b/apm-sniffer/apm-sdk-plugin/solon-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/solon/define/SolonActionInstrumentation.java new file mode 100644 index 0000000000..d41d15ed45 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/solon-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/solon/define/SolonActionInstrumentation.java @@ -0,0 +1,66 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.solon.define; + +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.matcher.ElementMatcher; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.v2.ClassInstanceMethodsEnhancePluginDefineV2; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.v2.InstanceMethodsInterceptV2Point; +import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch; +import org.apache.skywalking.apm.agent.core.plugin.match.NameMatch; + +import static net.bytebuddy.matcher.ElementMatchers.named; + +public class SolonActionInstrumentation extends ClassInstanceMethodsEnhancePluginDefineV2 { + + private static final String INTERCEPT_CLASS = "org.apache.skywalking.apm.plugin.solon.SolonActionExecuteInterceptor"; + + @Override + public ClassMatch enhanceClass() { + return NameMatch.byName("org.noear.solon.SolonApp"); + } + + @Override + public ConstructorInterceptPoint[] getConstructorsInterceptPoints() { + return new ConstructorInterceptPoint[0]; + } + + @Override + public InstanceMethodsInterceptV2Point[] getInstanceMethodsInterceptV2Points() { + return new InstanceMethodsInterceptV2Point[] { + new InstanceMethodsInterceptV2Point() { + @Override + public ElementMatcher getMethodsMatcher() { + return named("tryHandle"); + } + + @Override + public String getMethodsInterceptorV2() { + return INTERCEPT_CLASS; + } + + @Override + public boolean isOverrideArgs() { + return true; + } + } + }; + } +} diff --git a/apm-sniffer/apm-sdk-plugin/solon-2.x-plugin/src/main/resources/skywalking-plugin.def b/apm-sniffer/apm-sdk-plugin/solon-2.x-plugin/src/main/resources/skywalking-plugin.def new file mode 100644 index 0000000000..01e6af6f77 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/solon-2.x-plugin/src/main/resources/skywalking-plugin.def @@ -0,0 +1,17 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +solon-2.x=org.apache.skywalking.apm.plugin.solon.define.SolonActionInstrumentation \ No newline at end of file diff --git a/apm-sniffer/apm-sdk-plugin/solrj-7.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/solrj-7.x-plugin/pom.xml index 3a89801c3e..2377a233f7 100644 --- a/apm-sniffer/apm-sdk-plugin/solrj-7.x-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/solrj-7.x-plugin/pom.xml @@ -20,7 +20,7 @@ apm-sdk-plugin org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/async-annotation-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/spring-plugins/async-annotation-plugin/pom.xml index a5e13159e5..337a154135 100644 --- a/apm-sniffer/apm-sdk-plugin/spring-plugins/async-annotation-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/async-annotation-plugin/pom.xml @@ -20,7 +20,7 @@ spring-plugins org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/concurrent-util-4.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/spring-plugins/concurrent-util-4.x-plugin/pom.xml index 6965f8d5fd..fa85530d61 100644 --- a/apm-sniffer/apm-sdk-plugin/spring-plugins/concurrent-util-4.x-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/concurrent-util-4.x-plugin/pom.xml @@ -20,7 +20,7 @@ spring-plugins org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/core-patch/pom.xml b/apm-sniffer/apm-sdk-plugin/spring-plugins/core-patch/pom.xml index 0115d32d5a..58e530913a 100644 --- a/apm-sniffer/apm-sdk-plugin/spring-plugins/core-patch/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/core-patch/pom.xml @@ -21,7 +21,7 @@ spring-plugins org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/core-patch/src/main/java/org/apache/skywalking/apm/plugin/spring/patch/AutoProxyCreatorInterceptor.java b/apm-sniffer/apm-sdk-plugin/spring-plugins/core-patch/src/main/java/org/apache/skywalking/apm/plugin/spring/patch/AutoProxyCreatorInterceptor.java new file mode 100644 index 0000000000..943ac8983d --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/core-patch/src/main/java/org/apache/skywalking/apm/plugin/spring/patch/AutoProxyCreatorInterceptor.java @@ -0,0 +1,53 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.spring.patch; + +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceMethodsAroundInterceptor; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult; + +import java.lang.reflect.Method; + +/** + * AutoProxyCreatorInterceptor determines whether the given interface is {@link EnhancedInstance}, + * and therefore does not consider it a reasonable proxy interface. + */ +public class AutoProxyCreatorInterceptor implements InstanceMethodsAroundInterceptor { + + @Override + public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, + MethodInterceptResult result) throws Throwable { + + } + + @Override + public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, + Object ret) throws Throwable { + + Class ifc = (Class) allArguments[0]; + return ((boolean) ret) || ifc.getName().equals("org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance"); + } + + @Override + public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments, + Class[] argumentsTypes, Throwable t) { + + } + +} diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/core-patch/src/main/java/org/apache/skywalking/apm/plugin/spring/patch/CreateAopProxyInterceptor.java b/apm-sniffer/apm-sdk-plugin/spring-plugins/core-patch/src/main/java/org/apache/skywalking/apm/plugin/spring/patch/CreateAopProxyInterceptor.java index 97bab4ad33..6e55d80b1a 100644 --- a/apm-sniffer/apm-sdk-plugin/spring-plugins/core-patch/src/main/java/org/apache/skywalking/apm/plugin/spring/patch/CreateAopProxyInterceptor.java +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/core-patch/src/main/java/org/apache/skywalking/apm/plugin/spring/patch/CreateAopProxyInterceptor.java @@ -18,12 +18,14 @@ package org.apache.skywalking.apm.plugin.spring.patch; -import java.lang.reflect.Method; import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceMethodsAroundInterceptor; import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult; +import org.springframework.aop.SpringProxy; import org.springframework.aop.framework.AdvisedSupport; +import java.lang.reflect.Method; + /** * CreateAopProxyInterceptor check that the bean has been implement {@link EnhancedInstance}. * if yes, true will be returned. @@ -32,25 +34,45 @@ public class CreateAopProxyInterceptor implements InstanceMethodsAroundIntercept @Override public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, - MethodInterceptResult result) throws Throwable { + MethodInterceptResult result) throws Throwable { } @Override public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, - Object ret) throws Throwable { + Object ret) throws Throwable { AdvisedSupport advisedSupport = (AdvisedSupport) allArguments[0]; - Class targetClass = advisedSupport.getTargetClass(); - if (targetClass != null && EnhancedInstance.class.isAssignableFrom(targetClass)) { - return true; + if (maybeHasUserSuppliedProxyInterfaces(ret)) { + Class targetClass = advisedSupport.getTargetClass(); + if (targetClass != null) { + if (onlyImplementsEnhancedInstance(advisedSupport) || onlyImplementsEnhancedInstanceAndSpringProxy(advisedSupport)) { + return true; + } + } } return ret; } + private boolean maybeHasUserSuppliedProxyInterfaces(Object ret) { + return !(Boolean) ret; + } + + private boolean onlyImplementsEnhancedInstanceAndSpringProxy(AdvisedSupport advisedSupport) { + Class[] ifcs = advisedSupport.getProxiedInterfaces(); + Class targetClass = advisedSupport.getTargetClass(); + return ifcs.length == 2 && EnhancedInstance.class.isAssignableFrom(targetClass) && SpringProxy.class.isAssignableFrom(targetClass); + } + + private boolean onlyImplementsEnhancedInstance(AdvisedSupport advisedSupport) { + Class[] ifcs = advisedSupport.getProxiedInterfaces(); + Class targetClass = advisedSupport.getTargetClass(); + return ifcs.length == 1 && EnhancedInstance.class.isAssignableFrom(targetClass); + } + @Override public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments, - Class[] argumentsTypes, Throwable t) { + Class[] argumentsTypes, Throwable t) { } } diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/core-patch/src/main/java/org/apache/skywalking/apm/plugin/spring/patch/ProxyProcessorSupportInterceptor.java b/apm-sniffer/apm-sdk-plugin/spring-plugins/core-patch/src/main/java/org/apache/skywalking/apm/plugin/spring/patch/ProxyProcessorSupportInterceptor.java new file mode 100644 index 0000000000..d8840d4ef2 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/core-patch/src/main/java/org/apache/skywalking/apm/plugin/spring/patch/ProxyProcessorSupportInterceptor.java @@ -0,0 +1,52 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.spring.patch; + +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceMethodsAroundInterceptor; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult; + +import java.lang.reflect.Method; + +/** + * ProxyProcessorSupportInterceptor determines whether the given interface is {@link EnhancedInstance}, + * and therefore does not consider it a reasonable proxy interface. + */ +public class ProxyProcessorSupportInterceptor implements InstanceMethodsAroundInterceptor { + + @Override + public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, + MethodInterceptResult result) throws Throwable { + + } + + @Override + public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, + Object ret) throws Throwable { + Class ifc = (Class) allArguments[0]; + return ((boolean) ret) || ifc.getName().equals("org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance"); + } + + @Override + public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments, + Class[] argumentsTypes, Throwable t) { + + } + +} diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/core-patch/src/main/java/org/apache/skywalking/apm/plugin/spring/patch/define/AutoProxyCreatorInstrumentation.java b/apm-sniffer/apm-sdk-plugin/spring-plugins/core-patch/src/main/java/org/apache/skywalking/apm/plugin/spring/patch/define/AutoProxyCreatorInstrumentation.java new file mode 100644 index 0000000000..26e6147e69 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/core-patch/src/main/java/org/apache/skywalking/apm/plugin/spring/patch/define/AutoProxyCreatorInstrumentation.java @@ -0,0 +1,78 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.spring.patch.define; + +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.matcher.ElementMatcher; +import org.apache.skywalking.apm.agent.core.plugin.WitnessMethod; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine; +import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch; +import org.apache.skywalking.apm.agent.core.plugin.match.NameMatch; + +import java.util.Collections; +import java.util.List; + +import static net.bytebuddy.matcher.ElementMatchers.named; + +public class AutoProxyCreatorInstrumentation extends ClassInstanceMethodsEnhancePluginDefine { + + private static final String ENHANCE_CLASS = "org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator"; + public static final String ENHANCE_METHOD = "isConfigurationCallbackInterface"; + public static final String INTERCEPT_CLASS = "org.apache.skywalking.apm.plugin.spring.patch.AutoProxyCreatorInterceptor"; + + @Override + public final ConstructorInterceptPoint[] getConstructorsInterceptPoints() { + return new ConstructorInterceptPoint[0]; + } + + @Override + public final InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() { + return new InstanceMethodsInterceptPoint[] { + new InstanceMethodsInterceptPoint() { + @Override + public ElementMatcher getMethodsMatcher() { + return named(ENHANCE_METHOD); + } + + @Override + public String getMethodsInterceptor() { + return INTERCEPT_CLASS; + } + + @Override + public boolean isOverrideArgs() { + return false; + } + } + }; + } + + @Override + protected ClassMatch enhanceClass() { + return NameMatch.byName(ENHANCE_CLASS); + } + + @Override + protected List witnessMethods() { + return Collections.singletonList(new WitnessMethod(ENHANCE_CLASS, named(ENHANCE_METHOD))); + } + +} diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/core-patch/src/main/java/org/apache/skywalking/apm/plugin/spring/patch/define/ProxyProcessorSupportInstrumentation.java b/apm-sniffer/apm-sdk-plugin/spring-plugins/core-patch/src/main/java/org/apache/skywalking/apm/plugin/spring/patch/define/ProxyProcessorSupportInstrumentation.java new file mode 100644 index 0000000000..78de2969c2 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/core-patch/src/main/java/org/apache/skywalking/apm/plugin/spring/patch/define/ProxyProcessorSupportInstrumentation.java @@ -0,0 +1,69 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.spring.patch.define; + +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.matcher.ElementMatcher; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine; +import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch; +import org.apache.skywalking.apm.agent.core.plugin.match.NameMatch; + +import static net.bytebuddy.matcher.ElementMatchers.named; + +public class ProxyProcessorSupportInstrumentation extends ClassInstanceMethodsEnhancePluginDefine { + + private static final String ENHANCE_CLASS = "org.springframework.aop.framework.ProxyProcessorSupport"; + public static final String ENHANCE_METHOD = "isInternalLanguageInterface"; + public static final String INTERCEPT_CLASS = "org.apache.skywalking.apm.plugin.spring.patch.ProxyProcessorSupportInterceptor"; + + @Override + public final ConstructorInterceptPoint[] getConstructorsInterceptPoints() { + return new ConstructorInterceptPoint[0]; + } + + @Override + public final InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() { + return new InstanceMethodsInterceptPoint[] { + new InstanceMethodsInterceptPoint() { + @Override + public ElementMatcher getMethodsMatcher() { + return named(ENHANCE_METHOD); + } + + @Override + public String getMethodsInterceptor() { + return INTERCEPT_CLASS; + } + + @Override + public boolean isOverrideArgs() { + return false; + } + } + }; + } + + @Override + protected ClassMatch enhanceClass() { + return NameMatch.byName(ENHANCE_CLASS); + } + +} diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/core-patch/src/main/resources/skywalking-plugin.def b/apm-sniffer/apm-sdk-plugin/spring-plugins/core-patch/src/main/resources/skywalking-plugin.def index e51178b97b..3a21d2b4aa 100644 --- a/apm-sniffer/apm-sdk-plugin/spring-plugins/core-patch/src/main/resources/skywalking-plugin.def +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/core-patch/src/main/resources/skywalking-plugin.def @@ -19,3 +19,5 @@ spring-core-patch=org.apache.skywalking.apm.plugin.spring.patch.define.Autowired spring-core-patch=org.apache.skywalking.apm.plugin.spring.patch.define.AopExpressionMatchInstrumentation spring-core-patch=org.apache.skywalking.apm.plugin.spring.patch.define.AspectJExpressionPointCutInstrumentation spring-core-patch=org.apache.skywalking.apm.plugin.spring.patch.define.BeanWrapperImplInstrumentation +spring-core-patch=org.apache.skywalking.apm.plugin.spring.patch.define.ProxyProcessorSupportInstrumentation +spring-core-patch=org.apache.skywalking.apm.plugin.spring.patch.define.AutoProxyCreatorInstrumentation diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/core-patch/src/test/java/org/apache/skywalking/apm/plugin/spring/patch/CreateAopProxyInterceptorTest.java b/apm-sniffer/apm-sdk-plugin/spring-plugins/core-patch/src/test/java/org/apache/skywalking/apm/plugin/spring/patch/CreateAopProxyInterceptorTest.java index 12b5c96633..ae513567da 100644 --- a/apm-sniffer/apm-sdk-plugin/spring-plugins/core-patch/src/test/java/org/apache/skywalking/apm/plugin/spring/patch/CreateAopProxyInterceptorTest.java +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/core-patch/src/test/java/org/apache/skywalking/apm/plugin/spring/patch/CreateAopProxyInterceptorTest.java @@ -24,6 +24,7 @@ import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.aop.SpringProxy; import org.springframework.aop.framework.AdvisedSupport; import static org.hamcrest.core.Is.is; @@ -48,18 +49,69 @@ public void setUp() { } @Test - public void testInterceptNormalObject() throws Throwable { - doReturn(Object.class).when(advisedSupport).getTargetClass(); + public void testInterceptClassImplementsNoInterfaces() throws Throwable { + // doReturn(Object.class).when(advisedSupport).getTargetClass(); + // doReturn(Object.class.getInterfaces()).when(advisedSupport).getProxiedInterfaces(); + assertThat(true, is(interceptor.afterMethod(enhancedInstance, null, new Object[] {advisedSupport}, new Class[] {Object.class}, true))); + } + + @Test + public void testInterceptClassImplementsUserSuppliedInterface() throws Throwable { + doReturn(MockClassImplementsUserSuppliedInterface.class).when(advisedSupport).getTargetClass(); + doReturn(MockClassImplementsUserSuppliedInterface.class.getInterfaces()).when(advisedSupport).getProxiedInterfaces(); assertThat(false, is(interceptor.afterMethod(enhancedInstance, null, new Object[] {advisedSupport}, new Class[] {Object.class}, false))); } @Test - public void testInterceptEnhanceInstanceObject() throws Throwable { - doReturn(MockClass.class).when(advisedSupport).getTargetClass(); + public void testInterceptClassImplementsSpringProxy() throws Throwable { + // doReturn(MockClassImplementsSpringProxy.class).when(advisedSupport).getTargetClass(); + // doReturn(MockClassImplementsSpringProxy.class.getInterfaces()).when(advisedSupport).getProxiedInterfaces(); + assertThat(true, is(interceptor.afterMethod(enhancedInstance, null, new Object[] {advisedSupport}, new Class[] {Object.class}, true))); + } + + @Test + public void testInterceptClassImplementsEnhancedInstance() throws Throwable { + doReturn(MockClassImplementsEnhancedInstance.class).when(advisedSupport).getTargetClass(); + doReturn(MockClassImplementsEnhancedInstance.class.getInterfaces()).when(advisedSupport).getProxiedInterfaces(); assertThat(true, is(interceptor.afterMethod(enhancedInstance, null, new Object[] {advisedSupport}, new Class[] {Object.class}, false))); } - private class MockClass implements EnhancedInstance { + @Test + public void testClassImplementsEnhancedInstanceAndUserSuppliedInterface() throws Throwable { + doReturn(MockClassImplementsSpringProxyAndUserSuppliedInterface.class).when(advisedSupport).getTargetClass(); + doReturn(MockClassImplementsSpringProxyAndUserSuppliedInterface.class.getInterfaces()).when(advisedSupport).getProxiedInterfaces(); + assertThat(false, is(interceptor.afterMethod(enhancedInstance, null, new Object[] {advisedSupport}, new Class[] {Object.class}, false))); + } + + @Test + public void testInterceptClassImplementsSpringProxyAndEnhancedInstance() throws Throwable { + doReturn(MockClassImplementsSpringProxyAndEnhancedInstance.class).when(advisedSupport).getTargetClass(); + doReturn(MockClassImplementsSpringProxyAndEnhancedInstance.class.getInterfaces()).when(advisedSupport).getProxiedInterfaces(); + assertThat(true, is(interceptor.afterMethod(enhancedInstance, null, new Object[] {advisedSupport}, new Class[] {Object.class}, false))); + } + + @Test + public void testInterceptClassImplementsSpringProxyAndUserSuppliedInterface() throws Throwable { + doReturn(MockClassImplementsSpringProxyAndUserSuppliedInterface.class).when(advisedSupport).getTargetClass(); + doReturn(MockClassImplementsSpringProxyAndUserSuppliedInterface.class.getInterfaces()).when(advisedSupport).getProxiedInterfaces(); + assertThat(false, is(interceptor.afterMethod(enhancedInstance, null, new Object[] {advisedSupport}, new Class[] {Object.class}, false))); + } + + @Test + public void testInterceptClassImplementsEnhancedInstanceAndUserSuppliedInterface() throws Throwable { + doReturn(MockClassImplementsEnhancedInstanceAndUserSuppliedInterface.class).when(advisedSupport).getTargetClass(); + doReturn(MockClassImplementsEnhancedInstanceAndUserSuppliedInterface.class.getInterfaces()).when(advisedSupport).getProxiedInterfaces(); + assertThat(false, is(interceptor.afterMethod(enhancedInstance, null, new Object[] {advisedSupport}, new Class[] {Object.class}, false))); + } + + @Test + public void testInterceptClassImplementsSpringProxyAndEnhancedInstanceAndUserSuppliedInterface() throws Throwable { + doReturn(MockClassImplementsSpringProxyAndEnhancedInstanceAndUserSuppliedInterface.class).when(advisedSupport).getTargetClass(); + doReturn(MockClassImplementsSpringProxyAndEnhancedInstanceAndUserSuppliedInterface.class.getInterfaces()).when(advisedSupport).getProxiedInterfaces(); + assertThat(false, is(interceptor.afterMethod(enhancedInstance, null, new Object[] {advisedSupport}, new Class[] {Object.class}, false))); + } + + private class MockClassImplementsEnhancedInstance implements EnhancedInstance { @Override public Object getSkyWalkingDynamicField() { @@ -72,4 +124,81 @@ public void setSkyWalkingDynamicField(Object value) { } } + private class MockClassImplementsUserSuppliedInterface implements UserSuppliedInterface { + + @Override + public void methodOfUserSuppliedInterface() { + + } + + } + + private class MockClassImplementsSpringProxy implements SpringProxy { + + } + + private class MockClassImplementsSpringProxyAndEnhancedInstance implements EnhancedInstance, SpringProxy { + + @Override + public Object getSkyWalkingDynamicField() { + return null; + } + + @Override + public void setSkyWalkingDynamicField(Object value) { + + } + + } + + private class MockClassImplementsEnhancedInstanceAndUserSuppliedInterface implements EnhancedInstance, UserSuppliedInterface { + + @Override + public Object getSkyWalkingDynamicField() { + return null; + } + + @Override + public void setSkyWalkingDynamicField(Object value) { + + } + + @Override + public void methodOfUserSuppliedInterface() { + } + + } + + private class MockClassImplementsSpringProxyAndUserSuppliedInterface implements SpringProxy, UserSuppliedInterface { + + @Override + public void methodOfUserSuppliedInterface() { + } + + } + + private class MockClassImplementsSpringProxyAndEnhancedInstanceAndUserSuppliedInterface implements EnhancedInstance, SpringProxy, UserSuppliedInterface { + + @Override + public Object getSkyWalkingDynamicField() { + return null; + } + + @Override + public void setSkyWalkingDynamicField(Object value) { + + } + + @Override + public void methodOfUserSuppliedInterface() { + } + + } + + interface UserSuppliedInterface { + + void methodOfUserSuppliedInterface(); + + } + } diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-3.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-3.x-plugin/pom.xml index 4738e506da..02fa8639d0 100644 --- a/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-3.x-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-3.x-plugin/pom.xml @@ -20,7 +20,7 @@ spring-plugins org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-4.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-4.x-plugin/pom.xml index 0ccb9edfb8..f48ace4499 100644 --- a/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-4.x-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-4.x-plugin/pom.xml @@ -20,7 +20,7 @@ spring-plugins org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-5.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-5.x-plugin/pom.xml index 7b2234b64b..bea4ce00a9 100644 --- a/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-5.x-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-5.x-plugin/pom.xml @@ -19,7 +19,7 @@ spring-plugins org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/pom.xml b/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/pom.xml index 348d608020..65c554bb88 100644 --- a/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/pom.xml @@ -20,7 +20,7 @@ spring-plugins org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/commons/HttpServletRequestWrapper.java b/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/commons/HttpServletRequestWrapper.java new file mode 100644 index 0000000000..d258f7a5f2 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/commons/HttpServletRequestWrapper.java @@ -0,0 +1,37 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.spring.mvc.commons; + +import java.util.Enumeration; +import java.util.Map; + +public interface HttpServletRequestWrapper { + + String getHeader(String name); + + String getMethod(); + + StringBuffer getRequestURL(); + + String getRemoteHost(); + + Map getParameterMap(); + + public Enumeration getHeaders(String name); +} diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/commons/HttpServletRequestWrappers.java b/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/commons/HttpServletRequestWrappers.java new file mode 100644 index 0000000000..663ad53c31 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/commons/HttpServletRequestWrappers.java @@ -0,0 +1,112 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.spring.mvc.commons; + +import java.util.Enumeration; +import java.util.Map; + +public class HttpServletRequestWrappers { + + public static HttpServletRequestWrapper wrap(jakarta.servlet.http.HttpServletRequest request) { + return new JakartaHttpServletRequest(request); + } + + public static HttpServletRequestWrapper wrap(javax.servlet.http.HttpServletRequest request) { + return new JavaxHttpServletRequest(request); + } + + public static class JakartaHttpServletRequest implements HttpServletRequestWrapper { + + private jakarta.servlet.http.HttpServletRequest jakartaRequest; + + public JakartaHttpServletRequest(jakarta.servlet.http.HttpServletRequest jakartaRequest) { + this.jakartaRequest = jakartaRequest; + } + + @Override + public String getHeader(String name) { + return jakartaRequest.getHeader(name); + } + + @Override + public String getMethod() { + return jakartaRequest.getMethod(); + } + + @Override + public StringBuffer getRequestURL() { + return jakartaRequest.getRequestURL(); + } + + @Override + public String getRemoteHost() { + return jakartaRequest.getRemoteHost(); + } + + @Override + public Map getParameterMap() { + return jakartaRequest.getParameterMap(); + } + + @Override + public Enumeration getHeaders(String name) { + return jakartaRequest.getHeaders(name); + } + + } + + public static class JavaxHttpServletRequest implements HttpServletRequestWrapper { + private javax.servlet.http.HttpServletRequest javaxRequest; + + public JavaxHttpServletRequest(javax.servlet.http.HttpServletRequest javaxRequest) { + this.javaxRequest = javaxRequest; + } + + @Override + public String getHeader(String name) { + return javaxRequest.getHeader(name); + } + + @Override + public String getMethod() { + return javaxRequest.getMethod(); + } + + @Override + public StringBuffer getRequestURL() { + return javaxRequest.getRequestURL(); + } + + @Override + public String getRemoteHost() { + return javaxRequest.getRemoteHost(); + } + + @Override + public Map getParameterMap() { + return javaxRequest.getParameterMap(); + } + + @Override + public Enumeration getHeaders(String name) { + return javaxRequest.getHeaders(name); + } + + } +} diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/commons/RequestUtil.java b/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/commons/RequestUtil.java index b278c8f9e2..f4f23b1c5b 100644 --- a/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/commons/RequestUtil.java +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/commons/RequestUtil.java @@ -52,6 +52,16 @@ public static void collectHttpParam(jakarta.servlet.http.HttpServletRequest requ } } + public static void collectHttpParam(HttpServletRequestWrapper request, AbstractSpan span) { + final Map parameterMap = request.getParameterMap(); + if (parameterMap != null && !parameterMap.isEmpty()) { + String tagValue = CollectionUtil.toString(parameterMap); + tagValue = SpringMVCPluginConfig.Plugin.Http.HTTP_PARAMS_LENGTH_THRESHOLD > 0 ? + StringUtil.cut(tagValue, SpringMVCPluginConfig.Plugin.Http.HTTP_PARAMS_LENGTH_THRESHOLD) : tagValue; + Tags.HTTP.PARAMS.set(span, tagValue); + } + } + public static void collectHttpParam(ServerHttpRequest request, AbstractSpan span) { Map parameterMap = new HashMap<>(request.getQueryParams().size()); request.getQueryParams().forEach((key, value) -> { @@ -65,6 +75,25 @@ public static void collectHttpParam(ServerHttpRequest request, AbstractSpan span } } + public static void collectHttpHeaders(HttpServletRequestWrapper request, AbstractSpan span) { + final List headersList = new ArrayList<>(SpringMVCPluginConfig.Plugin.Http.INCLUDE_HTTP_HEADERS.size()); + SpringMVCPluginConfig.Plugin.Http.INCLUDE_HTTP_HEADERS.stream() + .filter( + headerName -> request.getHeaders(headerName) != null) + .forEach(headerName -> { + Enumeration headerValues = request.getHeaders( + headerName); + List valueList = Collections.list( + headerValues); + if (!CollectionUtil.isEmpty(valueList)) { + String headerValue = valueList.toString(); + headersList.add(headerName + "=" + headerValue); + } + }); + + collectHttpHeaders(headersList, span); + } + public static void collectHttpHeaders(HttpServletRequest request, AbstractSpan span) { final List headersList = new ArrayList<>(SpringMVCPluginConfig.Plugin.Http.INCLUDE_HTTP_HEADERS.size()); SpringMVCPluginConfig.Plugin.Http.INCLUDE_HTTP_HEADERS.stream() diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/commons/interceptor/AbstractMethodInterceptor.java b/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/commons/interceptor/AbstractMethodInterceptor.java index df3aa7583f..ac51e60b93 100644 --- a/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/commons/interceptor/AbstractMethodInterceptor.java +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/commons/interceptor/AbstractMethodInterceptor.java @@ -18,6 +18,9 @@ package org.apache.skywalking.apm.plugin.spring.mvc.commons.interceptor; +import java.lang.reflect.Method; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; import org.apache.skywalking.apm.agent.core.context.CarrierItem; import org.apache.skywalking.apm.agent.core.context.ContextCarrier; import org.apache.skywalking.apm.agent.core.context.ContextManager; @@ -34,6 +37,8 @@ import org.apache.skywalking.apm.agent.core.util.MethodUtil; import org.apache.skywalking.apm.network.trace.component.ComponentsDefine; import org.apache.skywalking.apm.plugin.spring.mvc.commons.EnhanceRequireObjectCache; +import org.apache.skywalking.apm.plugin.spring.mvc.commons.HttpServletRequestWrapper; +import org.apache.skywalking.apm.plugin.spring.mvc.commons.HttpServletRequestWrappers; import org.apache.skywalking.apm.plugin.spring.mvc.commons.RequestUtil; import org.apache.skywalking.apm.plugin.spring.mvc.commons.SpringMVCPluginConfig; import org.apache.skywalking.apm.plugin.spring.mvc.commons.exception.IllegalMethodStackDepthException; @@ -41,10 +46,6 @@ import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.http.server.reactive.ServerHttpResponse; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.lang.reflect.Method; - import static org.apache.skywalking.apm.plugin.spring.mvc.commons.Constants.CONTROLLER_METHOD_STACK_DEPTH; import static org.apache.skywalking.apm.plugin.spring.mvc.commons.Constants.FORWARD_REQUEST_FLAG; import static org.apache.skywalking.apm.plugin.spring.mvc.commons.Constants.REACTIVE_ASYNC_SPAN_IN_RUNTIME_CONTEXT; @@ -68,10 +69,11 @@ public abstract class AbstractMethodInterceptor implements InstanceMethodsAround static { IS_SERVLET_GET_STATUS_METHOD_EXIST = MethodUtil.isMethodExist( - AbstractMethodInterceptor.class.getClassLoader(), SERVLET_RESPONSE_CLASS, GET_STATUS_METHOD); + AbstractMethodInterceptor.class.getClassLoader(), SERVLET_RESPONSE_CLASS, GET_STATUS_METHOD); IS_JAKARTA_SERVLET_GET_STATUS_METHOD_EXIST = MethodUtil.isMethodExist( - AbstractMethodInterceptor.class.getClassLoader(), - JAKARTA_SERVLET_RESPONSE_CLASS, GET_STATUS_METHOD); + AbstractMethodInterceptor.class.getClassLoader(), + JAKARTA_SERVLET_RESPONSE_CLASS, GET_STATUS_METHOD + ); try { Class.forName(SERVLET_RESPONSE_CLASS, true, AbstractMethodInterceptor.class.getClassLoader()); IN_SERVLET_CONTAINER = true; @@ -113,53 +115,15 @@ public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allAr if (IN_SERVLET_CONTAINER && IS_JAVAX && HttpServletRequest.class.isAssignableFrom(request.getClass())) { final HttpServletRequest httpServletRequest = (HttpServletRequest) request; - CarrierItem next = contextCarrier.items(); - while (next.hasNext()) { - next = next.next(); - next.setHeadValue(httpServletRequest.getHeader(next.getHeadKey())); - } - - String operationName = this.buildOperationName(method, httpServletRequest.getMethod(), - (EnhanceRequireObjectCache) objInst.getSkyWalkingDynamicField()); - AbstractSpan span = ContextManager.createEntrySpan(operationName, contextCarrier); - Tags.URL.set(span, httpServletRequest.getRequestURL().toString()); - Tags.HTTP.METHOD.set(span, httpServletRequest.getMethod()); - span.setComponent(ComponentsDefine.SPRING_MVC_ANNOTATION); - SpanLayer.asHttp(span); + handleBeforeMethod( + objInst, method, HttpServletRequestWrappers.wrap(httpServletRequest), contextCarrier); - if (SpringMVCPluginConfig.Plugin.SpringMVC.COLLECT_HTTP_PARAMS) { - RequestUtil.collectHttpParam(httpServletRequest, span); - } - - if (!CollectionUtil.isEmpty(SpringMVCPluginConfig.Plugin.Http.INCLUDE_HTTP_HEADERS)) { - RequestUtil.collectHttpHeaders(httpServletRequest, span); - } - } else if (IN_SERVLET_CONTAINER && IS_JAKARTA && jakarta.servlet.http.HttpServletRequest.class.isAssignableFrom(request.getClass())) { + } else if (IN_SERVLET_CONTAINER && IS_JAKARTA && jakarta.servlet.http.HttpServletRequest.class.isAssignableFrom( + request.getClass())) { final jakarta.servlet.http.HttpServletRequest httpServletRequest = (jakarta.servlet.http.HttpServletRequest) request; - CarrierItem next = contextCarrier.items(); - while (next.hasNext()) { - next = next.next(); - next.setHeadValue(httpServletRequest.getHeader(next.getHeadKey())); - } - - String operationName = - this.buildOperationName(method, httpServletRequest.getMethod(), - (EnhanceRequireObjectCache) objInst.getSkyWalkingDynamicField()); - AbstractSpan span = - ContextManager.createEntrySpan(operationName, contextCarrier); - Tags.URL.set(span, httpServletRequest.getRequestURL().toString()); - Tags.HTTP.METHOD.set(span, httpServletRequest.getMethod()); - span.setComponent(ComponentsDefine.SPRING_MVC_ANNOTATION); - SpanLayer.asHttp(span); - - if (SpringMVCPluginConfig.Plugin.SpringMVC.COLLECT_HTTP_PARAMS) { - RequestUtil.collectHttpParam(httpServletRequest, span); - } + handleBeforeMethod( + objInst, method, HttpServletRequestWrappers.wrap(httpServletRequest), contextCarrier); - if (!CollectionUtil - .isEmpty(SpringMVCPluginConfig.Plugin.Http.INCLUDE_HTTP_HEADERS)) { - RequestUtil.collectHttpHeaders(httpServletRequest, span); - } } else if (ServerHttpRequest.class.isAssignableFrom(request.getClass())) { final ServerHttpRequest serverHttpRequest = (ServerHttpRequest) request; CarrierItem next = contextCarrier.items(); @@ -168,8 +132,10 @@ public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allAr next.setHeadValue(serverHttpRequest.getHeaders().getFirst(next.getHeadKey())); } - String operationName = this.buildOperationName(method, serverHttpRequest.getMethod().name(), - (EnhanceRequireObjectCache) objInst.getSkyWalkingDynamicField()); + String operationName = this.buildOperationName( + method, serverHttpRequest.getMethod().name(), + (EnhanceRequireObjectCache) objInst.getSkyWalkingDynamicField() + ); AbstractSpan span = ContextManager.createEntrySpan(operationName, contextCarrier); Tags.URL.set(span, serverHttpRequest.getURI().toString()); Tags.HTTP.METHOD.set(span, serverHttpRequest.getMethod().name()); @@ -198,6 +164,31 @@ public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allAr } } + private void handleBeforeMethod(EnhancedInstance objInst, Method method, + HttpServletRequestWrapper httpServletRequest, ContextCarrier contextCarrier) { + CarrierItem next = contextCarrier.items(); + while (next.hasNext()) { + next = next.next(); + next.setHeadValue(httpServletRequest.getHeader(next.getHeadKey())); + } + + String operationName = this.buildOperationName( + method, httpServletRequest.getMethod(), (EnhanceRequireObjectCache) objInst.getSkyWalkingDynamicField()); + AbstractSpan span = ContextManager.createEntrySpan(operationName, contextCarrier); + Tags.URL.set(span, httpServletRequest.getRequestURL().toString()); + Tags.HTTP.METHOD.set(span, httpServletRequest.getMethod()); + span.setComponent(ComponentsDefine.SPRING_MVC_ANNOTATION); + SpanLayer.asHttp(span); + + if (SpringMVCPluginConfig.Plugin.SpringMVC.COLLECT_HTTP_PARAMS) { + RequestUtil.collectHttpParam(httpServletRequest, span); + } + + if (!CollectionUtil.isEmpty(SpringMVCPluginConfig.Plugin.Http.INCLUDE_HTTP_HEADERS)) { + RequestUtil.collectHttpHeaders(httpServletRequest, span); + } + } + @Override public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, Object ret) throws Throwable { @@ -232,9 +223,11 @@ public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allA Integer statusCode = null; - if (IS_SERVLET_GET_STATUS_METHOD_EXIST && HttpServletResponse.class.isAssignableFrom(response.getClass())) { + if (IS_SERVLET_GET_STATUS_METHOD_EXIST && HttpServletResponse.class.isAssignableFrom( + response.getClass())) { statusCode = ((HttpServletResponse) response).getStatus(); - } else if (IS_JAKARTA_SERVLET_GET_STATUS_METHOD_EXIST && jakarta.servlet.http.HttpServletResponse.class.isAssignableFrom(response.getClass())) { + } else if (IS_JAKARTA_SERVLET_GET_STATUS_METHOD_EXIST && jakarta.servlet.http.HttpServletResponse.class.isAssignableFrom( + response.getClass())) { statusCode = ((jakarta.servlet.http.HttpServletResponse) response).getStatus(); } else if (ServerHttpResponse.class.isAssignableFrom(response.getClass())) { if (IS_SERVLET_GET_STATUS_METHOD_EXIST || IS_JAKARTA_SERVLET_GET_STATUS_METHOD_EXIST) { @@ -263,7 +256,8 @@ public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allA if (!SpringMVCPluginConfig.Plugin.SpringMVC.COLLECT_HTTP_PARAMS && span.isProfiling()) { if (IS_JAVAX && HttpServletRequest.class.isAssignableFrom(request.getClass())) { RequestUtil.collectHttpParam((HttpServletRequest) request, span); - } else if (IS_JAKARTA && jakarta.servlet.http.HttpServletRequest.class.isAssignableFrom(request.getClass())) { + } else if (IS_JAKARTA && jakarta.servlet.http.HttpServletRequest.class.isAssignableFrom( + request.getClass())) { RequestUtil.collectHttpParam((jakarta.servlet.http.HttpServletRequest) request, span); } else if (ServerHttpRequest.class.isAssignableFrom(request.getClass())) { RequestUtil.collectHttpParam((ServerHttpRequest) request, span); @@ -309,7 +303,7 @@ private String buildOperationName(Method method, String httpMethod, EnhanceRequi pathMappingCache.addPathMapping(method, requestURL); requestURL = pathMappingCache.findPathMapping(method); } - operationName = String.join(":", httpMethod, requestURL); + operationName = String.join(":", httpMethod, requestURL); } return operationName; diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/pom.xml b/apm-sniffer/apm-sdk-plugin/spring-plugins/pom.xml index d9a61849aa..4326ec7354 100644 --- a/apm-sniffer/apm-sdk-plugin/spring-plugins/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/pom.xml @@ -23,7 +23,7 @@ org.apache.skywalking apm-sdk-plugin - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT spring-plugins @@ -45,15 +45,16 @@ spring-webflux-5.x-webclient-plugin spring-webflux-6.x-webclient-plugin resttemplate-commons + spring-ai-1.x-plugin + spring-rabbitmq-plugin pom - apm-sdk-plugin + spring-plugins http://maven.apache.org UTF-8 - /.. diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/resttemplate-3.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/spring-plugins/resttemplate-3.x-plugin/pom.xml index 3fb9d5a0f0..2dab6a46c9 100644 --- a/apm-sniffer/apm-sdk-plugin/spring-plugins/resttemplate-3.x-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/resttemplate-3.x-plugin/pom.xml @@ -20,7 +20,7 @@ spring-plugins org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/resttemplate-4.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/spring-plugins/resttemplate-4.x-plugin/pom.xml index 608b1b9f75..966de1995f 100644 --- a/apm-sniffer/apm-sdk-plugin/spring-plugins/resttemplate-4.x-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/resttemplate-4.x-plugin/pom.xml @@ -20,7 +20,7 @@ spring-plugins org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/resttemplate-commons/pom.xml b/apm-sniffer/apm-sdk-plugin/spring-plugins/resttemplate-commons/pom.xml index f493560080..19ecc7989c 100644 --- a/apm-sniffer/apm-sdk-plugin/spring-plugins/resttemplate-commons/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/resttemplate-commons/pom.xml @@ -20,7 +20,7 @@ spring-plugins org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/scheduled-annotation-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/spring-plugins/scheduled-annotation-plugin/pom.xml index 602738f418..c4dfd6d15f 100644 --- a/apm-sniffer/apm-sdk-plugin/spring-plugins/scheduled-annotation-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/scheduled-annotation-plugin/pom.xml @@ -20,7 +20,7 @@ spring-plugins org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/scheduled-annotation-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/scheduled/define/ScheduledMethodInterceptorInstrumentation.java b/apm-sniffer/apm-sdk-plugin/spring-plugins/scheduled-annotation-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/scheduled/define/ScheduledMethodInterceptorInstrumentation.java index 2aacd3720b..44b36778bb 100644 --- a/apm-sniffer/apm-sdk-plugin/spring-plugins/scheduled-annotation-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/scheduled/define/ScheduledMethodInterceptorInstrumentation.java +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/scheduled-annotation-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/scheduled/define/ScheduledMethodInterceptorInstrumentation.java @@ -82,6 +82,19 @@ public ElementMatcher getConstructorMatcher() { public String getConstructorInterceptor() { return CONSTRUCTOR_WITH_STRING_INTERCEPTOR_CLASS; } + }, + new ConstructorInterceptPoint() { + @Override + public ElementMatcher getConstructorMatcher() { + return takesArguments(4) + .and(takesArgument(0, Object.class)) + .and(takesArgument(1, Method.class)); + } + + @Override + public String getConstructorInterceptor() { + return CONSTRUCTOR_WITH_METHOD_INTERCEPTOR_CLASS; + } } }; } diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-ai-1.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-ai-1.x-plugin/pom.xml new file mode 100644 index 0000000000..b6ea5c9863 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-ai-1.x-plugin/pom.xml @@ -0,0 +1,47 @@ + + + + + + spring-plugins + org.apache.skywalking + 9.7.0-SNAPSHOT + + 4.0.0 + + spring-ai-1.x-plugin + jar + + spring-ai-1.x-plugin + http://maven.apache.org + + + 17 + + + + + org.springframework.ai + spring-ai-client-chat + 1.1.0 + provided + + + + \ No newline at end of file diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-ai-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/ai/v1/ChatModelCallInterceptor.java b/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-ai-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/ai/v1/ChatModelCallInterceptor.java new file mode 100644 index 0000000000..302db3c407 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-ai-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/ai/v1/ChatModelCallInterceptor.java @@ -0,0 +1,179 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.spring.ai.v1; + +import org.apache.skywalking.apm.agent.core.context.ContextManager; +import org.apache.skywalking.apm.agent.core.context.tag.Tags; +import org.apache.skywalking.apm.agent.core.context.trace.AbstractSpan; +import org.apache.skywalking.apm.agent.core.context.trace.SpanLayer; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceMethodsAroundInterceptor; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult; +import org.apache.skywalking.apm.plugin.spring.ai.v1.common.ChatModelMetadataResolver; +import org.apache.skywalking.apm.plugin.spring.ai.v1.config.SpringAiPluginConfig; +import org.apache.skywalking.apm.plugin.spring.ai.v1.contant.Constants; +import org.apache.skywalking.apm.plugin.spring.ai.v1.messages.InputMessages; +import org.apache.skywalking.apm.plugin.spring.ai.v1.messages.OutputMessages; +import org.springframework.ai.chat.metadata.ChatResponseMetadata; +import org.springframework.ai.chat.metadata.Usage; +import org.springframework.ai.chat.model.ChatResponse; +import org.springframework.ai.chat.model.Generation; +import org.springframework.ai.chat.prompt.ChatOptions; +import org.springframework.ai.chat.prompt.Prompt; + +import java.lang.reflect.Method; + +public class ChatModelCallInterceptor implements InstanceMethodsAroundInterceptor { + + @Override + public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, MethodInterceptResult result) throws Throwable { + ChatModelMetadataResolver.ApiMetadata apiMetadata = ChatModelMetadataResolver.getMetadata(objInst); + AbstractSpan span = ContextManager.createExitSpan("Spring-ai/" + apiMetadata.getProviderName() + "/call", apiMetadata.getPeer()); + SpanLayer.asGenAI(span); + span.setComponent(apiMetadata.getComponent()); + Tags.GEN_AI_OPERATION_NAME.set(span, Constants.CHAT); + Tags.GEN_AI_PROVIDER_NAME.set(span, apiMetadata.getProviderName()); + + Prompt prompt = (Prompt) allArguments[0]; + ChatOptions chatOptions = prompt.getOptions(); + if (chatOptions == null) { + return; + } + + if (chatOptions.getModel() != null) { + Tags.GEN_AI_REQUEST_MODEL.set(span, chatOptions.getModel()); + } + if (chatOptions.getTemperature() != null) { + Tags.GEN_AI_TEMPERATURE.set(span, String.valueOf(chatOptions.getTemperature())); + } + if (chatOptions.getTopK() != null) { + Tags.GEN_AI_TOP_K.set(span, String.valueOf(chatOptions.getTopK())); + } + if (chatOptions.getTopP() != null) { + Tags.GEN_AI_TOP_P.set(span, String.valueOf(chatOptions.getTopP())); + } + } + + @Override + public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, Object ret) throws Throwable { + if (!ContextManager.isActive()) { + return ret; + } + + try { + if (!(ret instanceof ChatResponse)) { + return ret; + } + + ChatResponse response = (ChatResponse) ret; + + AbstractSpan span = ContextManager.activeSpan(); + ChatResponseMetadata metadata = response.getMetadata(); + + long totalTokens = 0; + + if (metadata != null) { + if (metadata.getId() != null) { + Tags.GEN_AI_RESPONSE_ID.set(span, metadata.getId()); + } + if (metadata.getModel() != null) { + Tags.GEN_AI_RESPONSE_MODEL.set(span, metadata.getModel()); + } + + Usage usage = metadata.getUsage(); + if (usage != null) { + if (usage.getPromptTokens() != null) { + Tags.GEN_AI_USAGE_INPUT_TOKENS.set(span, String.valueOf(usage.getPromptTokens())); + } + if (usage.getCompletionTokens() != null) { + Tags.GEN_AI_USAGE_OUTPUT_TOKENS.set(span, String.valueOf(usage.getCompletionTokens())); + } + if (usage.getTotalTokens() != null) { + totalTokens = usage.getTotalTokens(); + Tags.GEN_AI_CLIENT_TOKEN_USAGE.set(span, String.valueOf(totalTokens)); + } + } + } + + Generation generation = response.getResult(); + if (generation != null && generation.getMetadata() != null) { + String finishReason = generation.getMetadata().getFinishReason(); + if (finishReason != null) { + Tags.GEN_AI_RESPONSE_FINISH_REASONS.set(span, finishReason); + } + } + + collectContent(span, allArguments, response, totalTokens); + } finally { + ContextManager.stopSpan(); + } + return ret; + } + + @Override + public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, Throwable t) { + if (ContextManager.isActive()) { + ContextManager.activeSpan().log(t); + } + } + + private void collectContent(AbstractSpan span, Object[] allArguments, ChatResponse response, long totalTokens) { + int tokenThreshold = SpringAiPluginConfig.Plugin.SpringAi.CONTENT_COLLECT_THRESHOLD_TOKENS; + + if (tokenThreshold >= 0 && totalTokens < tokenThreshold) { + return; + } + + if (SpringAiPluginConfig.Plugin.SpringAi.COLLECT_INPUT_MESSAGES) { + collectPrompt(span, allArguments); + } + + if (SpringAiPluginConfig.Plugin.SpringAi.COLLECT_OUTPUT_MESSAGES) { + collectCompletion(span, response); + } + } + + private void collectPrompt(AbstractSpan span, Object[] allArguments) { + Prompt prompt = (Prompt) allArguments[0]; + if (prompt == null) { + return; + } + + InputMessages inputMessages = InputMessages.fromPrompt(prompt); + String inputMessagesJson = inputMessages.toJson(); + int limit = SpringAiPluginConfig.Plugin.SpringAi.INPUT_MESSAGES_LENGTH_LIMIT; + if (limit > 0 && inputMessagesJson.length() > limit) { + inputMessagesJson = inputMessagesJson.substring(0, limit); + } + + Tags.GEN_AI_INPUT_MESSAGES.set(span, inputMessagesJson); + } + + private void collectCompletion(AbstractSpan span, ChatResponse response) { + + OutputMessages outputMessages = OutputMessages.fromChatResponse(response); + String outputMessagesJson = outputMessages.toJson(); + int limit = SpringAiPluginConfig.Plugin.SpringAi.OUTPUT_MESSAGES_LENGTH_LIMIT; + + if (limit > 0 && outputMessagesJson.length() > limit) { + outputMessagesJson = outputMessagesJson.substring(0, limit); + } + Tags.GEN_AI_OUTPUT_MESSAGES.set(span, outputMessagesJson); + } +} diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-ai-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/ai/v1/ChatModelStreamInterceptor.java b/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-ai-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/ai/v1/ChatModelStreamInterceptor.java new file mode 100644 index 0000000000..1674e82011 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-ai-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/ai/v1/ChatModelStreamInterceptor.java @@ -0,0 +1,266 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.spring.ai.v1; + +import org.apache.skywalking.apm.agent.core.context.ContextManager; +import org.apache.skywalking.apm.agent.core.context.ContextSnapshot; +import org.apache.skywalking.apm.agent.core.context.tag.Tags; +import org.apache.skywalking.apm.agent.core.context.trace.AbstractSpan; +import org.apache.skywalking.apm.agent.core.context.trace.SpanLayer; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceMethodsAroundInterceptor; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult; +import org.apache.skywalking.apm.plugin.spring.ai.v1.common.ChatModelMetadataResolver; +import org.apache.skywalking.apm.plugin.spring.ai.v1.config.SpringAiPluginConfig; +import org.apache.skywalking.apm.plugin.spring.ai.v1.contant.Constants; +import org.apache.skywalking.apm.plugin.spring.ai.v1.messages.InputMessages; +import org.apache.skywalking.apm.plugin.spring.ai.v1.messages.OutputMessages; +import org.springframework.ai.chat.metadata.ChatResponseMetadata; +import org.springframework.ai.chat.metadata.Usage; +import org.springframework.ai.chat.model.ChatResponse; +import org.springframework.ai.chat.model.Generation; +import org.springframework.ai.chat.prompt.ChatOptions; +import org.springframework.ai.chat.prompt.Prompt; +import org.springframework.util.StringUtils; +import reactor.core.publisher.Flux; + +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; + +public class ChatModelStreamInterceptor implements InstanceMethodsAroundInterceptor { + + @Override + public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, MethodInterceptResult result) throws Throwable { + ChatModelMetadataResolver.ApiMetadata apiMetadata = ChatModelMetadataResolver.getMetadata(objInst); + AbstractSpan span = ContextManager.createExitSpan("Spring-ai/" + apiMetadata.getProviderName() + "/stream", apiMetadata.getPeer()); + SpanLayer.asGenAI(span); + + span.setComponent(apiMetadata.getComponent()); + Tags.GEN_AI_OPERATION_NAME.set(span, Constants.CHAT); + Tags.GEN_AI_PROVIDER_NAME.set(span, apiMetadata.getProviderName()); + + Prompt prompt = (Prompt) allArguments[0]; + if (prompt == null) { + return; + } + + ContextManager.getRuntimeContext().put(Constants.SPRING_AI_STREAM_START_TIME, System.currentTimeMillis()); + + ChatOptions chatOptions = prompt.getOptions(); + if (chatOptions == null) { + return; + } + + Tags.GEN_AI_REQUEST_MODEL.set(span, chatOptions.getModel()); + Tags.GEN_AI_TEMPERATURE.set(span, String.valueOf(chatOptions.getTemperature())); + Tags.GEN_AI_TOP_K.set(span, String.valueOf(chatOptions.getTopK())); + Tags.GEN_AI_TOP_P.set(span, String.valueOf(chatOptions.getTopP())); + } + + @Override + public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, Object ret) throws Throwable { + if (!ContextManager.isActive()) { + return ret; + } + + final AbstractSpan span = ContextManager.activeSpan(); + final ContextSnapshot snapshot = ContextManager.capture(); + + span.prepareForAsync(); + ContextManager.stopSpan(); + + @SuppressWarnings("unchecked") final Flux flux = (Flux) ret; + + final StreamState state = new StreamState(readAndClearStartTime()); + + return flux + .doOnNext(response -> onStreamNext(span, response, state)) + .doOnError(span::log) + .doFinally(signalType -> onStreamFinally(span, allArguments, state)) + .contextWrite(c -> c.put(Constants.SKYWALKING_CONTEXT_SNAPSHOT, snapshot)); + } + + private void onStreamNext(AbstractSpan span, ChatResponse response, StreamState state) { + state.lastResponseRef.set(response); + + final Generation generation = response.getResult(); + if (generation == null) { + return; + } + + recordTtfrIfFirstToken(span, generation, state); + recordFinishReason(generation, state); + appendCompletionChunk(generation, state); + } + + private void onStreamFinally(AbstractSpan span, Object[] allArguments, StreamState state) { + try { + ChatResponse finalResponse = state.lastResponseRef.get(); + long totalTokens = 0; + + if (finalResponse != null && finalResponse.getMetadata() != null) { + ChatResponseMetadata metadata = finalResponse.getMetadata(); + collectResponseTags(span, metadata, state); + totalTokens = collectUsageTags(span, metadata.getUsage()); + } + + int tokenThreshold = SpringAiPluginConfig.Plugin.SpringAi.CONTENT_COLLECT_THRESHOLD_TOKENS; + if (tokenThreshold >= 0 && totalTokens < tokenThreshold) { + return; + } + + if (SpringAiPluginConfig.Plugin.SpringAi.COLLECT_INPUT_MESSAGES) { + collectPrompt(span, allArguments); + } + + if (SpringAiPluginConfig.Plugin.SpringAi.COLLECT_OUTPUT_MESSAGES) { + collectCompletion(span, state); + } + } catch (Throwable t) { + span.log(t); + } finally { + span.asyncFinish(); + } + } + + private void recordTtfrIfFirstToken(AbstractSpan span, Generation generation, StreamState state) { + if (state.startTime == null) { + return; + } + if (generation.getOutput() == null || !StringUtils.hasText(generation.getOutput().getText())) { + return; + } + if (state.firstResponseReceived.compareAndSet(false, true)) { + Tags.GEN_AI_SERVER_TIME_TO_FIRST_TOKEN.set(span, String.valueOf(System.currentTimeMillis() - state.startTime)); + } + } + + private void recordFinishReason(Generation generation, StreamState state) { + if (generation.getMetadata() == null) { + return; + } + String reason = generation.getMetadata().getFinishReason(); + if (reason != null) { + state.finishReason.set(reason); + } + } + + private void appendCompletionChunk(Generation generation, StreamState state) { + if (generation.getOutput() == null) { + return; + } + String text = generation.getOutput().getText(); + if (text != null) { + state.completionBuilder.append(text); + } + } + + private void collectResponseTags(AbstractSpan span, ChatResponseMetadata metadata, StreamState state) { + if (metadata.getId() != null) { + Tags.GEN_AI_RESPONSE_ID.set(span, metadata.getId()); + } + if (metadata.getModel() != null) { + Tags.GEN_AI_RESPONSE_MODEL.set(span, metadata.getModel()); + } + Tags.GEN_AI_RESPONSE_FINISH_REASONS.set(span, state.finishReason.get()); + } + + private long collectUsageTags(AbstractSpan span, Usage usage) { + if (usage == null) { + return 0; + } + + if (usage.getPromptTokens() != null) { + Tags.GEN_AI_USAGE_INPUT_TOKENS.set(span, String.valueOf(usage.getPromptTokens())); + } + + if (usage.getCompletionTokens() != null) { + Tags.GEN_AI_USAGE_OUTPUT_TOKENS.set(span, String.valueOf(usage.getCompletionTokens())); + } + + long total = usage.getTotalTokens() != null ? usage.getTotalTokens() : 0; + Tags.GEN_AI_CLIENT_TOKEN_USAGE.set(span, String.valueOf(total)); + return total; + } + + private void collectPrompt(AbstractSpan span, Object[] allArguments) { + Prompt prompt = (Prompt) allArguments[0]; + if (prompt == null) { + return; + } + + InputMessages inputMessages = InputMessages.fromPrompt(prompt); + String inputMessagesJson = inputMessages.toJson(); + + int limit = SpringAiPluginConfig.Plugin.SpringAi.INPUT_MESSAGES_LENGTH_LIMIT; + if (limit > 0 && inputMessagesJson.length() > limit) { + inputMessagesJson = inputMessagesJson.substring(0, limit); + } + + Tags.GEN_AI_INPUT_MESSAGES.set(span, inputMessagesJson); + } + + private void collectCompletion(AbstractSpan span, StreamState state) { + + String fullText = state.completionBuilder.toString(); + String finishReason = state.finishReason.get(); + List parts = new ArrayList<>(); + if (fullText != null && !fullText.isEmpty()) { + parts.add(new InputMessages.TextPart(fullText)); + } + + OutputMessages outputMessages = OutputMessages.create().append(OutputMessages.OutputMessage.create("assistant", parts, finishReason)); + String outputMessagesJson = outputMessages.toJson(); + + int limit = SpringAiPluginConfig.Plugin.SpringAi.OUTPUT_MESSAGES_LENGTH_LIMIT; + if (limit > 0 && outputMessagesJson.length() > limit) { + outputMessagesJson = outputMessagesJson.substring(0, limit); + } + + Tags.GEN_AI_OUTPUT_MESSAGES.set(span, outputMessagesJson); + } + + private Long readAndClearStartTime() { + Long startTime = (Long) ContextManager.getRuntimeContext().get(Constants.SPRING_AI_STREAM_START_TIME); + ContextManager.getRuntimeContext().remove(Constants.SPRING_AI_STREAM_START_TIME); + return startTime; + } + + @Override + public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, Throwable t) { + if (ContextManager.isActive()) { + ContextManager.activeSpan().log(t); + } + } + + private static final class StreamState { + final AtomicReference lastResponseRef = new AtomicReference<>(); + final StringBuilder completionBuilder = new StringBuilder(); + final AtomicReference finishReason = new AtomicReference<>(""); + final AtomicBoolean firstResponseReceived = new AtomicBoolean(false); + final Long startTime; + + StreamState(Long startTime) { + this.startTime = startTime; + } + } +} diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-ai-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/ai/v1/DefaultToolCallingManagerInterceptor.java b/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-ai-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/ai/v1/DefaultToolCallingManagerInterceptor.java new file mode 100644 index 0000000000..929fd9bbd8 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-ai-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/ai/v1/DefaultToolCallingManagerInterceptor.java @@ -0,0 +1,63 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.spring.ai.v1; + +import org.apache.skywalking.apm.agent.core.context.ContextManager; +import org.apache.skywalking.apm.agent.core.context.ContextSnapshot; +import org.apache.skywalking.apm.agent.core.context.trace.AbstractSpan; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceMethodsAroundInterceptor; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult; +import org.apache.skywalking.apm.network.trace.component.ComponentsDefine; +import org.apache.skywalking.apm.plugin.spring.ai.v1.contant.Constants; +import org.springframework.ai.model.tool.internal.ToolCallReactiveContextHolder; +import reactor.util.context.ContextView; + +import java.lang.reflect.Method; + +public class DefaultToolCallingManagerInterceptor implements InstanceMethodsAroundInterceptor { + + @Override + public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, MethodInterceptResult result) throws Throwable { + AbstractSpan span = ContextManager.createLocalSpan("Spring-ai/tool/call"); + span.setComponent(ComponentsDefine.SPRING_AI); + + ContextView reactorCtx = ToolCallReactiveContextHolder.getContext(); + + if (reactorCtx != null && reactorCtx.hasKey(Constants.SKYWALKING_CONTEXT_SNAPSHOT)) { + ContextSnapshot snapshot = reactorCtx.get(Constants.SKYWALKING_CONTEXT_SNAPSHOT); + ContextManager.continued(snapshot); + } + } + + @Override + public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, Object ret) throws Throwable { + if (ContextManager.isActive()) { + ContextManager.stopSpan(); + } + return ret; + } + + @Override + public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, Throwable t) { + if (ContextManager.isActive()) { + ContextManager.activeSpan().log(t); + } + } +} diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-ai-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/ai/v1/ToolCallbackCallInterceptor.java b/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-ai-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/ai/v1/ToolCallbackCallInterceptor.java new file mode 100644 index 0000000000..637599da57 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-ai-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/ai/v1/ToolCallbackCallInterceptor.java @@ -0,0 +1,79 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.spring.ai.v1; + +import org.apache.skywalking.apm.agent.core.context.ContextManager; +import org.apache.skywalking.apm.agent.core.context.tag.Tags; +import org.apache.skywalking.apm.agent.core.context.trace.AbstractSpan; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceMethodsAroundInterceptor; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult; +import org.apache.skywalking.apm.network.trace.component.ComponentsDefine; +import org.apache.skywalking.apm.plugin.spring.ai.v1.config.SpringAiPluginConfig; +import org.apache.skywalking.apm.plugin.spring.ai.v1.contant.Constants; +import org.springframework.ai.tool.ToolCallback; +import org.springframework.ai.tool.definition.ToolDefinition; + +import java.lang.reflect.Method; + +public class ToolCallbackCallInterceptor implements InstanceMethodsAroundInterceptor { + + @Override + public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, + MethodInterceptResult result) throws Throwable { + ToolCallback toolCallback = (ToolCallback) objInst; + ToolDefinition definition = toolCallback.getToolDefinition(); + + String toolName = definition.name(); + + AbstractSpan span = ContextManager.createLocalSpan("Spring-ai/tool/execute/" + toolName); + span.setComponent(ComponentsDefine.SPRING_AI); + + Tags.GEN_AI_TOOL_NAME.set(span, toolName); + Tags.GEN_AI_OPERATION_NAME.set(span, Constants.EXECUTE_TOOL); + + if (SpringAiPluginConfig.Plugin.SpringAi.COLLECT_TOOL_INPUT) { + String toolInput = (String) allArguments[0]; + Tags.GEN_AI_TOOL_CALL_ARGUMENTS.set(span, toolInput); + } + } + + @Override + public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, + Object ret) throws Throwable { + if (ContextManager.isActive()) { + AbstractSpan span = ContextManager.activeSpan(); + if (SpringAiPluginConfig.Plugin.SpringAi.COLLECT_TOOL_OUTPUT && ret != null) { + Tags.GEN_AI_TOOL_CALL_RESULT.set(span, (String) ret); + } + + ContextManager.stopSpan(); + } + + return ret; + } + + @Override + public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments, + Class[] argumentsTypes, Throwable t) { + if (ContextManager.isActive()) { + ContextManager.activeSpan().log(t); + } + } +} diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-ai-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/ai/v1/common/ChatModelMetadataResolver.java b/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-ai-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/ai/v1/common/ChatModelMetadataResolver.java new file mode 100644 index 0000000000..501f5e026c --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-ai-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/ai/v1/common/ChatModelMetadataResolver.java @@ -0,0 +1,147 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.spring.ai.v1.common; + +import org.apache.skywalking.apm.agent.core.logging.api.ILog; +import org.apache.skywalking.apm.agent.core.logging.api.LogManager; +import org.apache.skywalking.apm.network.trace.component.ComponentsDefine; +import org.apache.skywalking.apm.network.trace.component.OfficialComponent; +import org.apache.skywalking.apm.plugin.spring.ai.v1.enums.AiProviderEnum; + +import javax.validation.constraints.NotNull; +import java.util.HashMap; +import java.util.Map; + +public class ChatModelMetadataResolver { + + private static final ILog LOGGER = LogManager.getLogger(ChatModelMetadataResolver.class); + + private static final Map MODEL_METADATA_MAP = new HashMap<>(); + + static { + for (AiProviderEnum provider : AiProviderEnum.values()) { + if (provider.getModelClassName() != null && provider.getValue() != null) { + MODEL_METADATA_MAP.put( + provider.getModelClassName(), + new ApiMetadata(provider.getValue(), matchComponent(provider)) + ); + } + } + } + + private static OfficialComponent matchComponent(AiProviderEnum provider) { + switch (provider) { + case ANTHROPIC_CLAUDE: + return ComponentsDefine.SPRING_AI_ANTHROPIC; + case AMAZON_BEDROCK_CONVERSE: + return ComponentsDefine.SPRING_AI_BEDROCK; + case AZURE_OPENAI: + return ComponentsDefine.SPRING_AI_AZURE_OPENAI; + case OCI_GENAI_COHERE: + return ComponentsDefine.SPRING_AI_COHERE; + case DEEPSEEK: + return ComponentsDefine.SPRING_AI_DEEPSEEK; + case GOOGLE_GENAI: + return ComponentsDefine.SPRING_AI_GOOGLE_GENAI; + case GOOGLE_VERTEXAI_GEMINI: + return ComponentsDefine.SPRING_AI_VERTEXAI; + case MISTRAL_AI: + return ComponentsDefine.SPRING_AI_MISTRAL_AI; + case OPENAI: + return ComponentsDefine.SPRING_AI_OPENAI; + case HUGGINGFACE: + return ComponentsDefine.SPRING_AI_HUGGINGFACE; + case MINIMAX: + return ComponentsDefine.SPRING_AI_MINIMAX; + case OLLAMA: + return ComponentsDefine.SPRING_AI_OLLAMA; + case OPENAI_SDK_OFFICIAL: + return ComponentsDefine.SPRING_AI_OPENAI; + case ZHIPU_AI: + return ComponentsDefine.SPRING_AI_ZHIPU_AI; + case UNKNOWN: + default: + return ComponentsDefine.SPRING_AI_UNKNOWN; + } + } + + @NotNull + public static ApiMetadata getMetadata(Object chatModelInstance) { + ApiMetadata metadata = MODEL_METADATA_MAP.get(chatModelInstance.getClass().getName()); + if (metadata == null) { + MODEL_METADATA_MAP.get(AiProviderEnum.UNKNOWN); + } + return metadata; + } + + public static ApiMetadata getMetadata(String modelClassName) { + try { + return MODEL_METADATA_MAP.get(modelClassName); + } catch (Exception e) { + LOGGER.error("spring-ai plugin get modelMetadata error: ", e); + return null; + } + } + + public static class ApiMetadata { + + private final String providerName; + private final OfficialComponent component; + private volatile String baseUrl; + private volatile String completionsPath; + + ApiMetadata(String providerName, OfficialComponent component) { + this.providerName = providerName; + this.component = component; + } + + public String getProviderName() { + return providerName; + } + + public OfficialComponent getComponent() { + return component; + } + + public String getBaseUrl() { + return baseUrl; + } + + public void setBaseUrl(String baseUrl) { + this.baseUrl = baseUrl; + } + + public String getCompletionsPath() { + return completionsPath; + } + + public void setCompletionsPath(String completionsPath) { + this.completionsPath = completionsPath; + } + + public String getPeer() { + if (baseUrl != null && !baseUrl.isEmpty()) { + return completionsPath != null && !completionsPath.isEmpty() + ? baseUrl + completionsPath + : baseUrl; + } + return providerName; + } + } +} \ No newline at end of file diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-ai-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/ai/v1/config/SpringAiPluginConfig.java b/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-ai-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/ai/v1/config/SpringAiPluginConfig.java new file mode 100644 index 0000000000..d2d5eef6f6 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-ai-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/ai/v1/config/SpringAiPluginConfig.java @@ -0,0 +1,74 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.spring.ai.v1.config; + +import org.apache.skywalking.apm.agent.core.boot.PluginConfig; + +public class SpringAiPluginConfig { + + public static class Plugin { + + @PluginConfig(root = SpringAiPluginConfig.class) + public static class SpringAi { + + /** + * Whether to collect the prompt content (input text) of the GenAI request. + */ + public static boolean COLLECT_INPUT_MESSAGES = false; + + /** + * Whether to collect the completion content (output text) of the GenAI response. + */ + public static boolean COLLECT_OUTPUT_MESSAGES = false; + + /** + * The maximum characters of the collected prompt content. + * If the content exceeds this limit, it will be truncated. + * Use a negative value to represent no limit, but be aware this could cause OOM. + */ + public static int INPUT_MESSAGES_LENGTH_LIMIT = 1024; + + /** + * The maximum characters of the collected completion content. + * If the content exceeds this limit, it will be truncated. + * Use a negative value to represent no limit, but be aware this could cause OOM. + */ + public static int OUTPUT_MESSAGES_LENGTH_LIMIT = 1024; + + /** + * The threshold for token usage to trigger content collection. + * When set to a positive value, prompt and completion will only be collected + * if the total token usage of the request exceeds this threshold. + * * This requires {@link #COLLECT_INPUT_MESSAGES} or {@link #COLLECT_OUTPUT_MESSAGES} to be enabled first. + * Use a negative value to disable this threshold-based filtering (collect all). + */ + public static int CONTENT_COLLECT_THRESHOLD_TOKENS = -1; + + /** + * Whether to collect the arguments (input parameters) of the tool/function call. + */ + public static boolean COLLECT_TOOL_INPUT = false; + + /** + * Whether to collect the execution result (output) of the tool/function call. + */ + public static boolean COLLECT_TOOL_OUTPUT = false; + } + } +} diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-ai-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/ai/v1/contant/Constants.java b/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-ai-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/ai/v1/contant/Constants.java new file mode 100644 index 0000000000..7348a0c11f --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-ai-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/ai/v1/contant/Constants.java @@ -0,0 +1,31 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.spring.ai.v1.contant; + +public class Constants { + public static final String SPRING_AI_STREAM_START_TIME = "Spring-ai.stream.startTime"; + + public static final String SKYWALKING_CONTEXT_SNAPSHOT = "SKYWALKING_CONTEXT_SNAPSHOT"; + + public static final String CHAT = "chat"; + + public static final String EXECUTE_TOOL = "execute_tool"; + + public static final String DEFAULT_COMPLETIONS_PATH = "/v1/chat/completions"; +} diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-ai-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/ai/v1/define/ChatModelInstrumentation.java b/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-ai-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/ai/v1/define/ChatModelInstrumentation.java new file mode 100644 index 0000000000..13aa1d381d --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-ai-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/ai/v1/define/ChatModelInstrumentation.java @@ -0,0 +1,92 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.spring.ai.v1.define; + +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.matcher.ElementMatcher; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine; +import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch; +import org.apache.skywalking.apm.agent.core.plugin.match.HierarchyMatch; + +import static net.bytebuddy.matcher.ElementMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.takesArguments; +import static org.apache.skywalking.apm.agent.core.plugin.bytebuddy.ArgumentTypeNameMatch.takesArgumentWithType; + +public class ChatModelInstrumentation extends ClassInstanceMethodsEnhancePluginDefine { + + private static final String ENHANCE_CLASS = "org.springframework.ai.chat.model.ChatModel"; + + private static final String INTERCEPT_CALL_METHOD = "call"; + + private static final String INTERCEPT_STREAM_METHOD = "stream"; + + private static final String INTERCEPTOR_CALL_CLASS = "org.apache.skywalking.apm.plugin.spring.ai.v1.ChatModelCallInterceptor"; + + private static final String INTERCEPTOR_STREAM_CLASS = "org.apache.skywalking.apm.plugin.spring.ai.v1.ChatModelStreamInterceptor"; + + @Override + protected ClassMatch enhanceClass() { + return HierarchyMatch.byHierarchyMatch(ENHANCE_CLASS); + } + + @Override + public ConstructorInterceptPoint[] getConstructorsInterceptPoints() { + return new ConstructorInterceptPoint[0]; + } + + @Override + public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() { + return new InstanceMethodsInterceptPoint[]{ + new InstanceMethodsInterceptPoint() { + @Override + public ElementMatcher getMethodsMatcher() { + return named(INTERCEPT_CALL_METHOD); + } + + @Override + public String getMethodsInterceptor() { + return INTERCEPTOR_CALL_CLASS; + } + + @Override + public boolean isOverrideArgs() { + return false; + } + }, + new InstanceMethodsInterceptPoint() { + @Override + public ElementMatcher getMethodsMatcher() { + return named(INTERCEPT_STREAM_METHOD).and(takesArguments(1)).and(takesArgumentWithType(0, "org.springframework.ai.chat.prompt.Prompt")); + } + + @Override + public String getMethodsInterceptor() { + return INTERCEPTOR_STREAM_CLASS; + } + + @Override + public boolean isOverrideArgs() { + return true; + } + } + }; + } +} diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-ai-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/ai/v1/define/DefaultToolCallingManagerInstrumentation.java b/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-ai-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/ai/v1/define/DefaultToolCallingManagerInstrumentation.java new file mode 100644 index 0000000000..595472834f --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-ai-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/ai/v1/define/DefaultToolCallingManagerInstrumentation.java @@ -0,0 +1,72 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.spring.ai.v1.define; + +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.matcher.ElementMatcher; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine; +import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch; +import org.apache.skywalking.apm.agent.core.plugin.match.NameMatch; + +import static net.bytebuddy.matcher.ElementMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.takesArguments; +import static org.apache.skywalking.apm.agent.core.plugin.bytebuddy.ArgumentTypeNameMatch.takesArgumentWithType; + +public class DefaultToolCallingManagerInstrumentation extends ClassInstanceMethodsEnhancePluginDefine { + + private static final String ENHANCE_CLASS = "org.springframework.ai.model.tool.DefaultToolCallingManager"; + + private static final String INTERCEPTOR_CLASS = "org.apache.skywalking.apm.plugin.spring.ai.v1.DefaultToolCallingManagerInterceptor"; + + private static final String INTERCEPT_METHOD = "executeToolCall"; + + @Override + protected ClassMatch enhanceClass() { + return NameMatch.byName(ENHANCE_CLASS); + } + + @Override + public ConstructorInterceptPoint[] getConstructorsInterceptPoints() { + return new ConstructorInterceptPoint[0]; + } + + @Override + public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() { + return new InstanceMethodsInterceptPoint[]{ + new InstanceMethodsInterceptPoint() { + @Override + public ElementMatcher getMethodsMatcher() { + return named(INTERCEPT_METHOD).and(takesArguments(3)).and(takesArgumentWithType(0, "org.springframework.ai.chat.prompt.Prompt")); + } + + @Override + public String getMethodsInterceptor() { + return INTERCEPTOR_CLASS; + } + + @Override + public boolean isOverrideArgs() { + return false; + } + } + }; + } +} diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-ai-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/ai/v1/define/ToolCallbackInstrumentation.java b/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-ai-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/ai/v1/define/ToolCallbackInstrumentation.java new file mode 100644 index 0000000000..99a4029b00 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-ai-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/ai/v1/define/ToolCallbackInstrumentation.java @@ -0,0 +1,73 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.spring.ai.v1.define; + +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.matcher.ElementMatcher; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine; +import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch; +import org.apache.skywalking.apm.agent.core.plugin.match.HierarchyMatch; + +import static net.bytebuddy.matcher.ElementMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.returns; +import static net.bytebuddy.matcher.ElementMatchers.takesArguments; +import static org.apache.skywalking.apm.agent.core.plugin.bytebuddy.ArgumentTypeNameMatch.takesArgumentWithType; + +public class ToolCallbackInstrumentation extends ClassInstanceMethodsEnhancePluginDefine { + + private static final String ENHANCE_INTERFACE = "org.springframework.ai.tool.ToolCallback"; + private static final String INTERCEPTOR_CLASS = "org.apache.skywalking.apm.plugin.spring.ai.v1.ToolCallbackCallInterceptor"; + + @Override + protected ClassMatch enhanceClass() { + return HierarchyMatch.byHierarchyMatch(ENHANCE_INTERFACE); + } + + @Override + public ConstructorInterceptPoint[] getConstructorsInterceptPoints() { + return new ConstructorInterceptPoint[0]; + } + + @Override + public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() { + return new InstanceMethodsInterceptPoint[]{ + new InstanceMethodsInterceptPoint() { + @Override + public ElementMatcher getMethodsMatcher() { + return named("call") + .and(takesArguments(2)) + .and(takesArgumentWithType(0, "java.lang.String")) + .and(returns(named("java.lang.String"))); + } + + @Override + public String getMethodsInterceptor() { + return INTERCEPTOR_CLASS; + } + + @Override + public boolean isOverrideArgs() { + return false; + } + } + }; + } +} diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-ai-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/ai/v1/define/provider/AnthropicApiInstrumentation.java b/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-ai-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/ai/v1/define/provider/AnthropicApiInstrumentation.java new file mode 100644 index 0000000000..e226864906 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-ai-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/ai/v1/define/provider/AnthropicApiInstrumentation.java @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.spring.ai.v1.define.provider; + +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.matcher.ElementMatcher; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine; +import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch; +import org.apache.skywalking.apm.agent.core.plugin.match.NameMatch; + +import static net.bytebuddy.matcher.ElementMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.takesArgument; +import static net.bytebuddy.matcher.ElementMatchers.takesArguments; + +public class AnthropicApiInstrumentation extends ClassInstanceMethodsEnhancePluginDefine { + + private static final String ENHANCE_CLASS = "org.springframework.ai.anthropic.api.AnthropicApi"; + private static final String INTERCEPTOR_CLASS = "org.apache.skywalking.apm.plugin.spring.ai.v1.provider.AnthropicApiConstructorInterceptor"; + + @Override + protected ClassMatch enhanceClass() { + return NameMatch.byName(ENHANCE_CLASS); + } + + @Override + public ConstructorInterceptPoint[] getConstructorsInterceptPoints() { + return new ConstructorInterceptPoint[]{ + new ConstructorInterceptPoint() { + @Override + public ElementMatcher getConstructorMatcher() { + return takesArguments(8).and(takesArgument(0, named("java.lang.String"))).and(takesArgument(1, named("java.lang.String"))); + } + + @Override + public String getConstructorInterceptor() { + return INTERCEPTOR_CLASS; + } + } + }; + } + + @Override + public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() { + return new InstanceMethodsInterceptPoint[0]; + } +} diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-ai-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/ai/v1/define/provider/DeepSeekApiInstrumentation.java b/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-ai-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/ai/v1/define/provider/DeepSeekApiInstrumentation.java new file mode 100644 index 0000000000..17382e75eb --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-ai-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/ai/v1/define/provider/DeepSeekApiInstrumentation.java @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.spring.ai.v1.define.provider; + +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.matcher.ElementMatcher; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine; +import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch; +import org.apache.skywalking.apm.agent.core.plugin.match.NameMatch; + +import static net.bytebuddy.matcher.ElementMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.takesArgument; +import static net.bytebuddy.matcher.ElementMatchers.takesArguments; + +public class DeepSeekApiInstrumentation extends ClassInstanceMethodsEnhancePluginDefine { + + private static final String ENHANCE_CLASS = "org.springframework.ai.deepseek.api.DeepSeekApi"; + private static final String INTERCEPTOR_CLASS = "org.apache.skywalking.apm.plugin.spring.ai.v1.provider.DeepSeekApiConstructorInterceptor"; + + @Override + protected ClassMatch enhanceClass() { + return NameMatch.byName(ENHANCE_CLASS); + } + + @Override + public ConstructorInterceptPoint[] getConstructorsInterceptPoints() { + return new ConstructorInterceptPoint[]{ + new ConstructorInterceptPoint() { + @Override + public ElementMatcher getConstructorMatcher() { + return takesArguments(8).and(takesArgument(0, named("java.lang.String"))).and(takesArgument(3, named("java.lang.String"))); + } + + @Override + public String getConstructorInterceptor() { + return INTERCEPTOR_CLASS; + } + } + }; + } + + @Override + public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() { + return new InstanceMethodsInterceptPoint[0]; + } +} diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-ai-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/ai/v1/define/provider/HuggingfaceChatModelInstrumentation.java b/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-ai-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/ai/v1/define/provider/HuggingfaceChatModelInstrumentation.java new file mode 100644 index 0000000000..93f7b57af4 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-ai-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/ai/v1/define/provider/HuggingfaceChatModelInstrumentation.java @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.spring.ai.v1.define.provider; + +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.matcher.ElementMatcher; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine; +import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch; +import org.apache.skywalking.apm.agent.core.plugin.match.NameMatch; + +import static net.bytebuddy.matcher.ElementMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.takesArgument; +import static net.bytebuddy.matcher.ElementMatchers.takesArguments; + +public class HuggingfaceChatModelInstrumentation extends ClassInstanceMethodsEnhancePluginDefine { + + private static final String ENHANCE_CLASS = "org.springframework.ai.huggingface.HuggingfaceChatModel"; + private static final String INTERCEPTOR_CLASS = "org.apache.skywalking.apm.plugin.spring.ai.v1.provider.HuggingfaceChatModelConstructorInterceptor"; + + @Override + protected ClassMatch enhanceClass() { + return NameMatch.byName(ENHANCE_CLASS); + } + + @Override + public ConstructorInterceptPoint[] getConstructorsInterceptPoints() { + return new ConstructorInterceptPoint[]{ + new ConstructorInterceptPoint() { + @Override + public ElementMatcher getConstructorMatcher() { + return takesArguments(2).and(takesArgument(1, named("java.lang.String"))); + } + + @Override + public String getConstructorInterceptor() { + return INTERCEPTOR_CLASS; + } + } + }; + } + + @Override + public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() { + return new InstanceMethodsInterceptPoint[0]; + } +} diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-ai-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/ai/v1/define/provider/MiniMaxApiInstrumentation.java b/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-ai-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/ai/v1/define/provider/MiniMaxApiInstrumentation.java new file mode 100644 index 0000000000..c8a5ca3ad1 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-ai-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/ai/v1/define/provider/MiniMaxApiInstrumentation.java @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.spring.ai.v1.define.provider; + +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.matcher.ElementMatcher; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine; +import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch; +import org.apache.skywalking.apm.agent.core.plugin.match.NameMatch; + +import static net.bytebuddy.matcher.ElementMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.takesArgument; +import static net.bytebuddy.matcher.ElementMatchers.takesArguments; + +public class MiniMaxApiInstrumentation extends ClassInstanceMethodsEnhancePluginDefine { + + private static final String ENHANCE_CLASS = "org.springframework.ai.minimax.api.MiniMaxApi"; + private static final String INTERCEPTOR_CLASS = "org.apache.skywalking.apm.plugin.spring.ai.v1.provider.MiniMaxApiConstructorInterceptor"; + + @Override + protected ClassMatch enhanceClass() { + return NameMatch.byName(ENHANCE_CLASS); + } + + @Override + public ConstructorInterceptPoint[] getConstructorsInterceptPoints() { + return new ConstructorInterceptPoint[]{ + new ConstructorInterceptPoint() { + @Override + public ElementMatcher getConstructorMatcher() { + return takesArguments(4).and(takesArgument(0, named("java.lang.String"))); + } + + @Override + public String getConstructorInterceptor() { + return INTERCEPTOR_CLASS; + } + } + }; + } + + @Override + public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() { + return new InstanceMethodsInterceptPoint[0]; + } +} diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-ai-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/ai/v1/define/provider/MistralAiApiInstrumentation.java b/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-ai-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/ai/v1/define/provider/MistralAiApiInstrumentation.java new file mode 100644 index 0000000000..ffcaee9198 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-ai-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/ai/v1/define/provider/MistralAiApiInstrumentation.java @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.spring.ai.v1.define.provider; + +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.matcher.ElementMatcher; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine; +import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch; +import org.apache.skywalking.apm.agent.core.plugin.match.NameMatch; + +import static net.bytebuddy.matcher.ElementMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.takesArgument; +import static net.bytebuddy.matcher.ElementMatchers.takesArguments; + +public class MistralAiApiInstrumentation extends ClassInstanceMethodsEnhancePluginDefine { + + private static final String ENHANCE_CLASS = "org.springframework.ai.mistralai.api.MistralAiApi"; + private static final String INTERCEPTOR_CLASS = "org.apache.skywalking.apm.plugin.spring.ai.v1.provider.MistralAiApiConstructorInterceptor"; + + @Override + protected ClassMatch enhanceClass() { + return NameMatch.byName(ENHANCE_CLASS); + } + + @Override + public ConstructorInterceptPoint[] getConstructorsInterceptPoints() { + return new ConstructorInterceptPoint[]{ + new ConstructorInterceptPoint() { + @Override + public ElementMatcher getConstructorMatcher() { + return takesArguments(5).and(takesArgument(0, named("java.lang.String"))); + } + + @Override + public String getConstructorInterceptor() { + return INTERCEPTOR_CLASS; + } + } + }; + } + + @Override + public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() { + return new InstanceMethodsInterceptPoint[0]; + } +} diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-ai-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/ai/v1/define/provider/OllamaApiInstrumentation.java b/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-ai-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/ai/v1/define/provider/OllamaApiInstrumentation.java new file mode 100644 index 0000000000..7e8fd5ebd8 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-ai-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/ai/v1/define/provider/OllamaApiInstrumentation.java @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.spring.ai.v1.define.provider; + +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.matcher.ElementMatcher; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine; +import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch; +import org.apache.skywalking.apm.agent.core.plugin.match.NameMatch; + +import static net.bytebuddy.matcher.ElementMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.takesArgument; +import static net.bytebuddy.matcher.ElementMatchers.takesArguments; + +public class OllamaApiInstrumentation extends ClassInstanceMethodsEnhancePluginDefine { + + private static final String ENHANCE_CLASS = "org.springframework.ai.ollama.api.OllamaApi"; + private static final String INTERCEPTOR_CLASS = "org.apache.skywalking.apm.plugin.spring.ai.v1.provider.OllamaApiConstructorInterceptor"; + + @Override + protected ClassMatch enhanceClass() { + return NameMatch.byName(ENHANCE_CLASS); + } + + @Override + public ConstructorInterceptPoint[] getConstructorsInterceptPoints() { + return new ConstructorInterceptPoint[]{ + new ConstructorInterceptPoint() { + @Override + public ElementMatcher getConstructorMatcher() { + return takesArguments(4).and(takesArgument(0, named("java.lang.String"))); + } + + @Override + public String getConstructorInterceptor() { + return INTERCEPTOR_CLASS; + } + } + }; + } + + @Override + public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() { + return new InstanceMethodsInterceptPoint[0]; + } +} diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-ai-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/ai/v1/define/provider/OpenAiApiInstrumentation.java b/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-ai-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/ai/v1/define/provider/OpenAiApiInstrumentation.java new file mode 100644 index 0000000000..624262322e --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-ai-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/ai/v1/define/provider/OpenAiApiInstrumentation.java @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.spring.ai.v1.define.provider; + +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.matcher.ElementMatcher; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine; +import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch; +import org.apache.skywalking.apm.agent.core.plugin.match.NameMatch; + +import static net.bytebuddy.matcher.ElementMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.takesArgument; +import static net.bytebuddy.matcher.ElementMatchers.takesArguments; + +public class OpenAiApiInstrumentation extends ClassInstanceMethodsEnhancePluginDefine { + + private static final String ENHANCE_CLASS = "org.springframework.ai.openai.api.OpenAiApi"; + private static final String INTERCEPTOR_CLASS = "org.apache.skywalking.apm.plugin.spring.ai.v1.provider.OpenAiApiConstructorInterceptor"; + + @Override + protected ClassMatch enhanceClass() { + return NameMatch.byName(ENHANCE_CLASS); + } + + @Override + public ConstructorInterceptPoint[] getConstructorsInterceptPoints() { + return new ConstructorInterceptPoint[]{ + new ConstructorInterceptPoint() { + @Override + public ElementMatcher getConstructorMatcher() { + return takesArguments(8).and(takesArgument(0, named("java.lang.String"))).and(takesArgument(3, named("java.lang.String"))); + } + + @Override + public String getConstructorInterceptor() { + return INTERCEPTOR_CLASS; + } + } + }; + } + + @Override + public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() { + return new InstanceMethodsInterceptPoint[0]; + } +} diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-ai-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/ai/v1/define/provider/ZhiPuAiApiInstrumentation.java b/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-ai-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/ai/v1/define/provider/ZhiPuAiApiInstrumentation.java new file mode 100644 index 0000000000..583f045f6b --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-ai-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/ai/v1/define/provider/ZhiPuAiApiInstrumentation.java @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.spring.ai.v1.define.provider; + +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.matcher.ElementMatcher; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine; +import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch; +import org.apache.skywalking.apm.agent.core.plugin.match.NameMatch; + +import static net.bytebuddy.matcher.ElementMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.takesArgument; +import static net.bytebuddy.matcher.ElementMatchers.takesArguments; + +public class ZhiPuAiApiInstrumentation extends ClassInstanceMethodsEnhancePluginDefine { + + private static final String ENHANCE_CLASS = "org.springframework.ai.zhipuai.api.ZhiPuAiApi"; + private static final String INTERCEPTOR_CLASS = "org.apache.skywalking.apm.plugin.spring.ai.v1.provider.ZhiPuAiApiConstructorInterceptor"; + + @Override + protected ClassMatch enhanceClass() { + return NameMatch.byName(ENHANCE_CLASS); + } + + @Override + public ConstructorInterceptPoint[] getConstructorsInterceptPoints() { + return new ConstructorInterceptPoint[]{ + new ConstructorInterceptPoint() { + @Override + public ElementMatcher getConstructorMatcher() { + return takesArguments(8).and(takesArgument(0, named("java.lang.String"))).and(takesArgument(3, named("java.lang.String"))); + } + + @Override + public String getConstructorInterceptor() { + return INTERCEPTOR_CLASS; + } + } + }; + } + + @Override + public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() { + return new InstanceMethodsInterceptPoint[0]; + } +} diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-ai-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/ai/v1/enums/AiProviderEnum.java b/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-ai-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/ai/v1/enums/AiProviderEnum.java new file mode 100644 index 0000000000..5137ab7d7e --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-ai-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/ai/v1/enums/AiProviderEnum.java @@ -0,0 +1,68 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.spring.ai.v1.enums; + +public enum AiProviderEnum { + + UNKNOWN("unknown", null), + + ANTHROPIC_CLAUDE("anthropic", "org.springframework.ai.anthropic.AnthropicChatModel"), + + AMAZON_BEDROCK_CONVERSE("aws.bedrock", "org.springframework.ai.bedrock.converse.BedrockProxyChatModel"), + + AZURE_OPENAI("azure.openai", "org.springframework.ai.azure.openai.AzureOpenAiChatModel"), + + OCI_GENAI_COHERE("cohere", "org.springframework.ai.oci.cohere.OCICohereChatModel"), + + DEEPSEEK("deepseek", "org.springframework.ai.deepseek.DeepSeekChatModel"), + + GOOGLE_GENAI("gcp.gen_ai", "org.springframework.ai.google.genai.GoogleGenAiChatModel"), + + GOOGLE_VERTEXAI_GEMINI("gcp.vertex_ai", "org.springframework.ai.vertexai.gemini.VertexAiGeminiChatModel"), + + MISTRAL_AI("mistral_ai", "org.springframework.ai.mistralai.MistralAiChatModel"), + + OPENAI("openai", "org.springframework.ai.openai.OpenAiChatModel"), + + HUGGINGFACE("huggingface", "org.springframework.ai.huggingface.HuggingfaceChatModel"), + + MINIMAX("minimax", "org.springframework.ai.minimax.MiniMaxChatModel"), + + OLLAMA("ollama", "org.springframework.ai.ollama.OllamaChatModel"), + + OPENAI_SDK_OFFICIAL("openai", "org.springframework.ai.openaisdk.OpenAiSdkChatModel"), + + ZHIPU_AI("zhipu_ai", "org.springframework.ai.zhipuai.ZhiPuAiChatModel"); + + private final String value; + private final String modelClassName; + + AiProviderEnum(String value, String modelClassName) { + this.value = value; + this.modelClassName = modelClassName; + } + + public String getValue() { + return value; + } + + public String getModelClassName() { + return modelClassName; + } +} diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-ai-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/ai/v1/messages/InputMessages.java b/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-ai-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/ai/v1/messages/InputMessages.java new file mode 100644 index 0000000000..794748bc49 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-ai-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/ai/v1/messages/InputMessages.java @@ -0,0 +1,271 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.spring.ai.v1.messages; + +import org.apache.skywalking.apm.agent.core.util.GsonUtil; +import org.springframework.ai.chat.messages.AssistantMessage; +import org.springframework.ai.chat.messages.Message; +import org.springframework.ai.chat.messages.MessageType; +import org.springframework.ai.chat.messages.ToolResponseMessage; +import org.springframework.ai.chat.messages.UserMessage; +import org.springframework.ai.chat.prompt.Prompt; +import org.springframework.ai.content.Media; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +public class InputMessages { + + private final List messages = new ArrayList<>(); + + public static InputMessages create() { + return new InputMessages(); + } + + public InputMessages append(InputMessage message) { + this.messages.add(message); + return this; + } + + public String toJson() { + return GsonUtil.toJson( + messages.stream() + .map(InputMessage::toMap) + .collect(Collectors.toList()) + ); + } + + public static class InputMessage { + private final String role; + private final List parts; + + private InputMessage(String role, List parts) { + this.role = role; + this.parts = parts; + } + + public static InputMessage create(String role, List parts) { + return new InputMessage(role, parts); + } + + public Map toMap() { + List> partMaps = parts.stream() + .map(MessagePart::toMap) + .collect(Collectors.toList()); + + Map map = new HashMap<>(); + map.put("role", role != null ? role : "unknown"); + map.put("parts", partMaps); + return map; + } + } + + public interface MessagePart { + Map toMap(); + } + + public static class TextPart implements MessagePart { + private final String content; + + public TextPart(String content) { + this.content = content; + } + + @Override + public Map toMap() { + return Map.of("type", "text", "content", content != null ? content : ""); + } + } + + public static class ToolCallPart implements MessagePart { + private final String id; + private final String name; + private final String arguments; + + public ToolCallPart(String id, String name, String arguments) { + this.id = id; + this.name = name; + this.arguments = arguments; + } + + @Override + public Map toMap() { + Map map = new java.util.LinkedHashMap<>(); + map.put("type", "tool_call"); + if (id != null) { + map.put("id", id); + } + map.put("name", name != null ? name : ""); + map.put("arguments", arguments != null ? arguments : ""); + return map; + } + } + + public static class ToolCallResponsePart implements MessagePart { + private final String id; + private final String result; + + public ToolCallResponsePart(String id, String result) { + this.id = id; + this.result = result; + } + + @Override + public Map toMap() { + Map map = new LinkedHashMap<>(); + map.put("type", "tool_call_response"); + if (id != null) { + map.put("id", id); + } + map.put("result", result != null ? result : ""); + return map; + } + } + + public static InputMessages fromPrompt(Prompt prompt) { + InputMessages inputMessages = InputMessages.create(); + + if (prompt == null || prompt.getInstructions() == null) { + return inputMessages; + } + + for (Message message : prompt.getInstructions()) { + MessageType type = message.getMessageType(); + + switch (type) { + case SYSTEM: + inputMessages.append(InputMessage.create( + type.getValue(), + textParts(message.getText()) + )); + break; + + case USER: + inputMessages.append(InputMessage.create( + type.getValue(), + userMessageParts(message) + )); + break; + + case ASSISTANT: + inputMessages.append(InputMessage.create( + type.getValue(), + assistantMessageParts(message) + )); + break; + + case TOOL: + inputMessages.append(InputMessage.create( + type.getValue(), + toolMessageParts(message) + )); + break; + default: + inputMessages.append(InputMessage.create( + type.getValue(), + textParts(message.getText()) + )); + break; + } + } + + return inputMessages; + } + + private static List textParts(String text) { + List parts = new ArrayList<>(); + if (text != null && !text.isEmpty()) { + parts.add(new TextPart(text)); + } + return parts; + } + + private static List userMessageParts(Message message) { + List parts = new ArrayList<>(); + + String text = message.getText(); + if (text != null && !text.isEmpty()) { + parts.add(new TextPart(text)); + } + + if (message instanceof UserMessage) { + UserMessage userMessage = (UserMessage) message; + if (userMessage.getMedia() != null) { + for (Media media : userMessage.getMedia()) { + parts.add(new TextPart("[media: " + media.getMimeType() + "]")); + } + } + } + + return parts; + } + + private static List assistantMessageParts(Message message) { + List parts = new ArrayList<>(); + + String text = message.getText(); + if (text != null && !text.isEmpty()) { + parts.add(new TextPart(text)); + } + + if (message instanceof AssistantMessage) { + AssistantMessage assistantMessage = (AssistantMessage) message; + List toolCalls = assistantMessage.getToolCalls(); + if (toolCalls != null) { + for (AssistantMessage.ToolCall toolCall : toolCalls) { + parts.add(new ToolCallPart( + toolCall.id(), + toolCall.name(), + toolCall.arguments() + )); + } + } + } + + return parts; + } + + private static List toolMessageParts(Message message) { + List parts = new ArrayList<>(); + + if (message instanceof ToolResponseMessage) { + ToolResponseMessage toolResponse = (ToolResponseMessage) message; + List responses = toolResponse.getResponses(); + if (responses != null) { + for (ToolResponseMessage.ToolResponse response : responses) { + parts.add(new ToolCallResponsePart( + response.id(), + response.responseData() + )); + } + } + } else { + String text = message.getText(); + if (text != null && !text.isEmpty()) { + parts.add(new ToolCallResponsePart(null, text)); + } + } + + return parts; + } +} diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-ai-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/ai/v1/messages/OutputMessages.java b/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-ai-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/ai/v1/messages/OutputMessages.java new file mode 100644 index 0000000000..bc73165fc0 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-ai-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/ai/v1/messages/OutputMessages.java @@ -0,0 +1,121 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.spring.ai.v1.messages; + +import org.apache.skywalking.apm.agent.core.util.GsonUtil; +import org.springframework.ai.chat.messages.AssistantMessage; +import org.springframework.ai.chat.model.ChatResponse; +import org.springframework.ai.chat.model.Generation; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +public class OutputMessages { + private final List messages = new ArrayList<>(); + + public static OutputMessages create() { + return new OutputMessages(); + } + + public OutputMessages append(OutputMessage message) { + this.messages.add(message); + return this; + } + + public String toJson() { + return GsonUtil.toJson( + messages.stream() + .map(OutputMessage::toMap) + .collect(Collectors.toList()) + ); + } + + public static class OutputMessage { + private final String role; + private final List parts; + private final String finishReason; + + private OutputMessage(String role, List parts, String finishReason) { + this.role = role; + this.parts = parts; + this.finishReason = finishReason; + } + + public static OutputMessage create(String role, List parts, String finishReason) { + return new OutputMessage(role, parts, finishReason); + } + + public Map toMap() { + Map map = new LinkedHashMap<>(); + map.put("role", role != null ? role : "assistant"); + map.put("parts", parts.stream() + .map(InputMessages.MessagePart::toMap) + .collect(Collectors.toList())); + if (finishReason != null && !finishReason.isEmpty()) { + map.put("finish_reason", finishReason); + } + return map; + } + } + + public static OutputMessages fromChatResponse(ChatResponse chatResponse) { + OutputMessages outputMessages = OutputMessages.create(); + + if (chatResponse == null || chatResponse.getResults() == null) { + return outputMessages; + } + + for (Generation generation : chatResponse.getResults()) { + List messageParts = new ArrayList<>(); + + AssistantMessage assistantMessage = generation.getOutput(); + if (assistantMessage != null) { + // Text content + String text = assistantMessage.getText(); + if (text != null && !text.isEmpty()) { + messageParts.add(new InputMessages.TextPart(text)); + } + + // Tool calls + List toolCalls = assistantMessage.getToolCalls(); + if (toolCalls != null) { + for (AssistantMessage.ToolCall toolCall : toolCalls) { + messageParts.add(new InputMessages.ToolCallPart( + toolCall.id(), + toolCall.name(), + toolCall.arguments() + )); + } + } + } + + String finishReason = ""; + if (generation.getMetadata() != null && generation.getMetadata().getFinishReason() != null) { + finishReason = generation.getMetadata().getFinishReason().toLowerCase(); + } + + outputMessages.append(OutputMessage.create("assistant", messageParts, finishReason)); + } + + return outputMessages; + } +} diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-ai-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/ai/v1/provider/AnthropicApiConstructorInterceptor.java b/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-ai-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/ai/v1/provider/AnthropicApiConstructorInterceptor.java new file mode 100644 index 0000000000..4f9e73ca7e --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-ai-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/ai/v1/provider/AnthropicApiConstructorInterceptor.java @@ -0,0 +1,38 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.spring.ai.v1.provider; + +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceConstructorInterceptor; +import org.apache.skywalking.apm.plugin.spring.ai.v1.common.ChatModelMetadataResolver; +import org.apache.skywalking.apm.plugin.spring.ai.v1.enums.AiProviderEnum; + +public class AnthropicApiConstructorInterceptor implements InstanceConstructorInterceptor { + + @Override + public void onConstruct(EnhancedInstance objInst, Object[] allArguments) throws Throwable { + ChatModelMetadataResolver.ApiMetadata metadata = ChatModelMetadataResolver.getMetadata(AiProviderEnum.ANTHROPIC_CLAUDE.getModelClassName()); + if (metadata == null) { + return; + } + + metadata.setBaseUrl((String) allArguments[0]); + metadata.setCompletionsPath((String) allArguments[1]); + } +} diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-ai-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/ai/v1/provider/DeepSeekApiConstructorInterceptor.java b/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-ai-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/ai/v1/provider/DeepSeekApiConstructorInterceptor.java new file mode 100644 index 0000000000..e1f26e5e8b --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-ai-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/ai/v1/provider/DeepSeekApiConstructorInterceptor.java @@ -0,0 +1,38 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.spring.ai.v1.provider; + +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceConstructorInterceptor; +import org.apache.skywalking.apm.plugin.spring.ai.v1.common.ChatModelMetadataResolver; +import org.apache.skywalking.apm.plugin.spring.ai.v1.enums.AiProviderEnum; + +public class DeepSeekApiConstructorInterceptor implements InstanceConstructorInterceptor { + + @Override + public void onConstruct(EnhancedInstance objInst, Object[] allArguments) throws Throwable { + ChatModelMetadataResolver.ApiMetadata metadata = ChatModelMetadataResolver.getMetadata(AiProviderEnum.DEEPSEEK.getModelClassName()); + if (metadata == null) { + return; + } + + metadata.setBaseUrl((String) allArguments[0]); + metadata.setCompletionsPath((String) allArguments[3]); + } +} diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-ai-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/ai/v1/provider/HuggingfaceChatModelConstructorInterceptor.java b/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-ai-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/ai/v1/provider/HuggingfaceChatModelConstructorInterceptor.java new file mode 100644 index 0000000000..234b876434 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-ai-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/ai/v1/provider/HuggingfaceChatModelConstructorInterceptor.java @@ -0,0 +1,37 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.spring.ai.v1.provider; + +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceConstructorInterceptor; +import org.apache.skywalking.apm.plugin.spring.ai.v1.common.ChatModelMetadataResolver; +import org.apache.skywalking.apm.plugin.spring.ai.v1.enums.AiProviderEnum; + +public class HuggingfaceChatModelConstructorInterceptor implements InstanceConstructorInterceptor { + + @Override + public void onConstruct(EnhancedInstance objInst, Object[] allArguments) throws Throwable { + ChatModelMetadataResolver.ApiMetadata metadata = ChatModelMetadataResolver.getMetadata(AiProviderEnum.HUGGINGFACE.getModelClassName()); + if (metadata == null) { + return; + } + + metadata.setBaseUrl((String) allArguments[1]); + } +} diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-ai-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/ai/v1/provider/MiniMaxApiConstructorInterceptor.java b/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-ai-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/ai/v1/provider/MiniMaxApiConstructorInterceptor.java new file mode 100644 index 0000000000..939e440ae1 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-ai-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/ai/v1/provider/MiniMaxApiConstructorInterceptor.java @@ -0,0 +1,38 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.spring.ai.v1.provider; + +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceConstructorInterceptor; +import org.apache.skywalking.apm.plugin.spring.ai.v1.common.ChatModelMetadataResolver; +import org.apache.skywalking.apm.plugin.spring.ai.v1.enums.AiProviderEnum; + +public class MiniMaxApiConstructorInterceptor implements InstanceConstructorInterceptor { + + @Override + public void onConstruct(EnhancedInstance objInst, Object[] allArguments) throws Throwable { + ChatModelMetadataResolver.ApiMetadata metadata = ChatModelMetadataResolver.getMetadata(AiProviderEnum.MINIMAX.getModelClassName()); + if (metadata == null) { + return; + } + + metadata.setBaseUrl((String) allArguments[0]); + metadata.setCompletionsPath("/v1/text/chatcompletion_v2"); + } +} diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-ai-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/ai/v1/provider/MistralAiApiConstructorInterceptor.java b/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-ai-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/ai/v1/provider/MistralAiApiConstructorInterceptor.java new file mode 100644 index 0000000000..07eac44d5b --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-ai-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/ai/v1/provider/MistralAiApiConstructorInterceptor.java @@ -0,0 +1,40 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.spring.ai.v1.provider; + +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceConstructorInterceptor; +import org.apache.skywalking.apm.plugin.spring.ai.v1.common.ChatModelMetadataResolver; +import org.apache.skywalking.apm.plugin.spring.ai.v1.enums.AiProviderEnum; + +import static org.apache.skywalking.apm.plugin.spring.ai.v1.contant.Constants.DEFAULT_COMPLETIONS_PATH; + +public class MistralAiApiConstructorInterceptor implements InstanceConstructorInterceptor { + + @Override + public void onConstruct(EnhancedInstance objInst, Object[] allArguments) throws Throwable { + ChatModelMetadataResolver.ApiMetadata metadata = ChatModelMetadataResolver.getMetadata(AiProviderEnum.MISTRAL_AI.getModelClassName()); + if (metadata == null) { + return; + } + + metadata.setBaseUrl((String) allArguments[0]); + metadata.setCompletionsPath(DEFAULT_COMPLETIONS_PATH); + } +} diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-ai-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/ai/v1/provider/OllamaApiConstructorInterceptor.java b/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-ai-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/ai/v1/provider/OllamaApiConstructorInterceptor.java new file mode 100644 index 0000000000..abdc2d9f5a --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-ai-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/ai/v1/provider/OllamaApiConstructorInterceptor.java @@ -0,0 +1,38 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.spring.ai.v1.provider; + +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceConstructorInterceptor; +import org.apache.skywalking.apm.plugin.spring.ai.v1.common.ChatModelMetadataResolver; +import org.apache.skywalking.apm.plugin.spring.ai.v1.enums.AiProviderEnum; + +public class OllamaApiConstructorInterceptor implements InstanceConstructorInterceptor { + + @Override + public void onConstruct(EnhancedInstance objInst, Object[] allArguments) throws Throwable { + ChatModelMetadataResolver.ApiMetadata metadata = ChatModelMetadataResolver.getMetadata(AiProviderEnum.OLLAMA.getModelClassName()); + if (metadata == null) { + return; + } + + metadata.setBaseUrl((String) allArguments[0]); + metadata.setCompletionsPath("/api/chat"); + } +} diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-ai-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/ai/v1/provider/OpenAiApiConstructorInterceptor.java b/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-ai-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/ai/v1/provider/OpenAiApiConstructorInterceptor.java new file mode 100644 index 0000000000..da0f3bbf2f --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-ai-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/ai/v1/provider/OpenAiApiConstructorInterceptor.java @@ -0,0 +1,38 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.spring.ai.v1.provider; + +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceConstructorInterceptor; +import org.apache.skywalking.apm.plugin.spring.ai.v1.common.ChatModelMetadataResolver; +import org.apache.skywalking.apm.plugin.spring.ai.v1.enums.AiProviderEnum; + +public class OpenAiApiConstructorInterceptor implements InstanceConstructorInterceptor { + + @Override + public void onConstruct(EnhancedInstance objInst, Object[] allArguments) throws Throwable { + ChatModelMetadataResolver.ApiMetadata metadata = ChatModelMetadataResolver.getMetadata(AiProviderEnum.OPENAI.getModelClassName()); + if (metadata == null) { + return; + } + + metadata.setBaseUrl((String) allArguments[0]); + metadata.setCompletionsPath((String) allArguments[3]); + } +} diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-ai-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/ai/v1/provider/ZhiPuAiApiConstructorInterceptor.java b/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-ai-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/ai/v1/provider/ZhiPuAiApiConstructorInterceptor.java new file mode 100644 index 0000000000..734c807a6a --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-ai-1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/ai/v1/provider/ZhiPuAiApiConstructorInterceptor.java @@ -0,0 +1,38 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.spring.ai.v1.provider; + +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceConstructorInterceptor; +import org.apache.skywalking.apm.plugin.spring.ai.v1.common.ChatModelMetadataResolver; +import org.apache.skywalking.apm.plugin.spring.ai.v1.enums.AiProviderEnum; + +public class ZhiPuAiApiConstructorInterceptor implements InstanceConstructorInterceptor { + + @Override + public void onConstruct(EnhancedInstance objInst, Object[] allArguments) throws Throwable { + ChatModelMetadataResolver.ApiMetadata metadata = ChatModelMetadataResolver.getMetadata(AiProviderEnum.ZHIPU_AI.getModelClassName()); + if (metadata == null) { + return; + } + + metadata.setBaseUrl((String) allArguments[0]); + metadata.setCompletionsPath((String) allArguments[3]); + } +} diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-ai-1.x-plugin/src/main/resources/skywalking-plugin.def b/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-ai-1.x-plugin/src/main/resources/skywalking-plugin.def new file mode 100644 index 0000000000..5c7eec110c --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-ai-1.x-plugin/src/main/resources/skywalking-plugin.def @@ -0,0 +1,27 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +spring-ai-1.x=org.apache.skywalking.apm.plugin.spring.ai.v1.define.ChatModelInstrumentation +spring-ai-1.x=org.apache.skywalking.apm.plugin.spring.ai.v1.define.ToolCallbackInstrumentation +spring-ai-1.x=org.apache.skywalking.apm.plugin.spring.ai.v1.define.DefaultToolCallingManagerInstrumentation +spring-ai-1.x=org.apache.skywalking.apm.plugin.spring.ai.v1.define.provider.AnthropicApiInstrumentation +spring-ai-1.x=org.apache.skywalking.apm.plugin.spring.ai.v1.define.provider.DeepSeekApiInstrumentation +spring-ai-1.x=org.apache.skywalking.apm.plugin.spring.ai.v1.define.provider.HuggingfaceChatModelInstrumentation +spring-ai-1.x=org.apache.skywalking.apm.plugin.spring.ai.v1.define.provider.MiniMaxApiInstrumentation +spring-ai-1.x=org.apache.skywalking.apm.plugin.spring.ai.v1.define.provider.MistralAiApiInstrumentation +spring-ai-1.x=org.apache.skywalking.apm.plugin.spring.ai.v1.define.provider.OllamaApiInstrumentation +spring-ai-1.x=org.apache.skywalking.apm.plugin.spring.ai.v1.define.provider.OpenAiApiInstrumentation +spring-ai-1.x=org.apache.skywalking.apm.plugin.spring.ai.v1.define.provider.ZhiPuAiApiInstrumentation diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-cloud/netflix-plugins/pom.xml b/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-cloud/netflix-plugins/pom.xml index e783b45119..34769eadbb 100644 --- a/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-cloud/netflix-plugins/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-cloud/netflix-plugins/pom.xml @@ -23,7 +23,7 @@ org.apache.skywalking spring-cloud - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT netflix-plugins @@ -37,7 +37,6 @@ UTF-8 - /../../.. diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-cloud/netflix-plugins/spring-cloud-feign-1.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-cloud/netflix-plugins/spring-cloud-feign-1.x-plugin/pom.xml index c5264d7570..6fb11aaa57 100644 --- a/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-cloud/netflix-plugins/spring-cloud-feign-1.x-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-cloud/netflix-plugins/spring-cloud-feign-1.x-plugin/pom.xml @@ -23,7 +23,7 @@ org.apache.skywalking netflix-plugins - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT apm-spring-cloud-feign-1.x-plugin diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-cloud/pom.xml b/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-cloud/pom.xml index 283b73e6ab..d1f4409bce 100644 --- a/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-cloud/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-cloud/pom.xml @@ -23,7 +23,7 @@ org.apache.skywalking spring-plugins - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT spring-cloud @@ -38,7 +38,6 @@ UTF-8 - /../.. diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-cloud/spring-cloud-feign-2.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-cloud/spring-cloud-feign-2.x-plugin/pom.xml index 9155ab7b17..579c404b7a 100644 --- a/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-cloud/spring-cloud-feign-2.x-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-cloud/spring-cloud-feign-2.x-plugin/pom.xml @@ -23,7 +23,7 @@ org.apache.skywalking spring-cloud - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT apm-spring-cloud-feign-2.x-plugin diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-commons/pom.xml b/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-commons/pom.xml index 1d3911d0e1..ff67ee32df 100644 --- a/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-commons/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-commons/pom.xml @@ -20,7 +20,7 @@ spring-plugins org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-kafka-1.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-kafka-1.x-plugin/pom.xml index 99c8c4013c..d8bf75bf9e 100644 --- a/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-kafka-1.x-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-kafka-1.x-plugin/pom.xml @@ -22,7 +22,7 @@ org.apache.skywalking spring-plugins - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT apm-spring-kafka-1.x-plugin diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-kafka-2.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-kafka-2.x-plugin/pom.xml index 45bb135adb..2cff5cf300 100644 --- a/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-kafka-2.x-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-kafka-2.x-plugin/pom.xml @@ -22,7 +22,7 @@ org.apache.skywalking spring-plugins - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT apm-spring-kafka-2.x-plugin diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-rabbitmq-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-rabbitmq-plugin/pom.xml new file mode 100644 index 0000000000..cb0c687268 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-rabbitmq-plugin/pom.xml @@ -0,0 +1,46 @@ + + + + 4.0.0 + + + org.apache.skywalking + spring-plugins + 9.7.0-SNAPSHOT + + + apm-spring-rabbitmq-plugin + apm-spring-rabbitmq-plugin + jar + + + 3.0.0 + + + + + org.springframework.amqp + spring-rabbit + ${spring-rabbit.version} + provided + + + \ No newline at end of file diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-rabbitmq-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/rabbitmq/SpringRabbitMQConsumerInterceptor.java b/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-rabbitmq-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/rabbitmq/SpringRabbitMQConsumerInterceptor.java new file mode 100644 index 0000000000..29bf40599f --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-rabbitmq-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/rabbitmq/SpringRabbitMQConsumerInterceptor.java @@ -0,0 +1,146 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.spring.rabbitmq; + +import java.lang.reflect.Method; +import java.util.List; +import java.util.Map; + +import org.apache.skywalking.apm.agent.core.context.CarrierItem; +import org.apache.skywalking.apm.agent.core.context.ContextCarrier; +import org.apache.skywalking.apm.agent.core.context.ContextManager; +import org.apache.skywalking.apm.agent.core.context.tag.Tags; +import org.apache.skywalking.apm.agent.core.context.trace.AbstractSpan; +import org.apache.skywalking.apm.agent.core.context.trace.SpanLayer; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.v2.InstanceMethodsAroundInterceptorV2; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.v2.MethodInvocationContext; +import org.apache.skywalking.apm.network.trace.component.ComponentsDefine; +import org.springframework.amqp.core.Message; +import org.springframework.amqp.core.MessageProperties; + +import com.rabbitmq.client.Channel; +import com.rabbitmq.client.Connection; + +public class SpringRabbitMQConsumerInterceptor implements InstanceMethodsAroundInterceptorV2 { + public static final String OPERATE_NAME_PREFIX = "RabbitMQ/"; + public static final String CONSUMER_OPERATE_NAME_SUFFIX = "/Consumer"; + + @Override + public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, + MethodInvocationContext context) throws Throwable { + Channel channel = (Channel) allArguments[0]; + + if (allArguments[1] instanceof Message) { + // Single message consume + Message message = (Message) allArguments[1]; + MessageProperties messageProperties = message.getMessageProperties(); + Map headers = messageProperties.getHeaders(); + + ContextCarrier contextCarrier = buildContextCarrier(headers); + String operationName = buildOperationName(messageProperties); + AbstractSpan activeSpan = ContextManager.createEntrySpan(operationName, contextCarrier); + + setSpanAttributes(activeSpan, channel, messageProperties); + } else if (allArguments[1] instanceof List) { + // Batch message consume + List messages = (List) allArguments[1]; + if (messages.isEmpty()) { + return; + } + + // Use the first message to create EntrySpan + Message firstMessage = (Message) messages.get(0); + MessageProperties firstMessageProperties = firstMessage.getMessageProperties(); + Map firstMessageHeaders = firstMessageProperties.getHeaders(); + + ContextCarrier contextCarrier = buildContextCarrier(firstMessageHeaders); + String operationName = buildOperationName(firstMessageProperties); + AbstractSpan activeSpan = ContextManager.createEntrySpan(operationName, contextCarrier); + + setSpanAttributes(activeSpan, channel, firstMessageProperties); + + // Extract trace context from remaining messages (skip first, already used for EntrySpan) + // to correlate all producer traces with this consumer span + for (int i = 1; i < messages.size(); i++) { + Object msg = messages.get(i); + if (msg instanceof Message) { + Message message = (Message) msg; + MessageProperties messageProperties = message.getMessageProperties(); + Map headers = messageProperties.getHeaders(); + + ContextCarrier carrier = buildContextCarrier(headers); + if (carrier.isValid()) { + ContextManager.extract(carrier); + } + } + } + } + } + + /** + * Build ContextCarrier from message headers + */ + private ContextCarrier buildContextCarrier(Map headers) { + ContextCarrier contextCarrier = new ContextCarrier(); + CarrierItem next = contextCarrier.items(); + while (next.hasNext()) { + next = next.next(); + Object value = headers.get(next.getHeadKey()); + if (value != null) { + next.setHeadValue(value.toString()); + } + } + return contextCarrier; + } + + private String buildOperationName(MessageProperties messageProperties) { + return OPERATE_NAME_PREFIX + "Topic/" + messageProperties.getReceivedExchange() + + "Queue/" + messageProperties.getReceivedRoutingKey() + + CONSUMER_OPERATE_NAME_SUFFIX; + } + + private void setSpanAttributes(AbstractSpan span, Channel channel, MessageProperties messageProperties) { + Connection connection = channel.getConnection(); + String serverUrl = connection.getAddress().getHostAddress() + ":" + connection.getPort(); + Tags.MQ_BROKER.set(span, serverUrl); + Tags.MQ_TOPIC.set(span, messageProperties.getReceivedExchange()); + Tags.MQ_QUEUE.set(span, messageProperties.getReceivedRoutingKey()); + span.setComponent(ComponentsDefine.RABBITMQ_CONSUMER); + span.setPeer(serverUrl); + SpanLayer.asMQ(span); + } + + @Override + public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, + Object ret, MethodInvocationContext context) throws Throwable { + if (ContextManager.isActive()) { + ContextManager.stopSpan(); + } + return ret; + } + + @Override + public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments, + Class[] argumentsTypes, Throwable t, MethodInvocationContext context) { + if (ContextManager.isActive()) { + ContextManager.activeSpan().log(t); + } + } +} diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-rabbitmq-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/rabbitmq/define/SpringRabbitMQConsumerInstrumentation.java b/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-rabbitmq-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/rabbitmq/define/SpringRabbitMQConsumerInstrumentation.java new file mode 100644 index 0000000000..b4a541adc8 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-rabbitmq-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/rabbitmq/define/SpringRabbitMQConsumerInstrumentation.java @@ -0,0 +1,69 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.spring.rabbitmq.define; + +import static net.bytebuddy.matcher.ElementMatchers.named; + +import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.v2.ClassInstanceMethodsEnhancePluginDefineV2; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.v2.DeclaredInstanceMethodsInterceptV2Point; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.v2.InstanceMethodsInterceptV2Point; +import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch; +import org.apache.skywalking.apm.agent.core.plugin.match.NameMatch; + +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.matcher.ElementMatcher; + +public class SpringRabbitMQConsumerInstrumentation extends ClassInstanceMethodsEnhancePluginDefineV2 { + public static final String ENHANCE_CLASS = "org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer"; + public static final String ENHANCE_METHOD = "executeListener"; + public static final String INTERCEPTOR_CLASS = "org.apache.skywalking.apm.plugin.spring.rabbitmq.SpringRabbitMQConsumerInterceptor"; + + @Override + public ConstructorInterceptPoint[] getConstructorsInterceptPoints() { + return null; + } + + @Override + public InstanceMethodsInterceptV2Point[] getInstanceMethodsInterceptV2Points() { + return new InstanceMethodsInterceptV2Point[] { + new DeclaredInstanceMethodsInterceptV2Point() { + @Override + public ElementMatcher getMethodsMatcher() { + return named(ENHANCE_METHOD); + } + + @Override + public String getMethodsInterceptorV2() { + return INTERCEPTOR_CLASS; + } + + @Override + public boolean isOverrideArgs() { + return false; + } + } + }; + } + + @Override + protected ClassMatch enhanceClass() { + return NameMatch.byName(ENHANCE_CLASS); + } +} diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-rabbitmq-plugin/src/main/resources/skywalking-plugin.def b/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-rabbitmq-plugin/src/main/resources/skywalking-plugin.def new file mode 100644 index 0000000000..94d87de414 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-rabbitmq-plugin/src/main/resources/skywalking-plugin.def @@ -0,0 +1,17 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +spring-rabbitmq=org.apache.skywalking.apm.plugin.spring.rabbitmq.define.SpringRabbitMQConsumerInstrumentation \ No newline at end of file diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-rabbitmq-plugin/src/test/java/org/apache/skywalking/apm/plugin/spring/rabbitmq/RabbitMQSpringConsumerInterceptorTest.java b/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-rabbitmq-plugin/src/test/java/org/apache/skywalking/apm/plugin/spring/rabbitmq/RabbitMQSpringConsumerInterceptorTest.java new file mode 100644 index 0000000000..e29864204e --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-rabbitmq-plugin/src/test/java/org/apache/skywalking/apm/plugin/spring/rabbitmq/RabbitMQSpringConsumerInterceptorTest.java @@ -0,0 +1,192 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.spring.rabbitmq; + +import static org.hamcrest.CoreMatchers.is; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.net.InetAddress; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.skywalking.apm.agent.core.context.SW8CarrierItem; +import org.apache.skywalking.apm.agent.core.context.trace.TraceSegment; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; +import org.apache.skywalking.apm.agent.test.tools.AgentServiceRule; +import org.apache.skywalking.apm.agent.test.tools.SegmentStorage; +import org.apache.skywalking.apm.agent.test.tools.SegmentStoragePoint; +import org.apache.skywalking.apm.agent.test.tools.TracingSegmentRunner; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.springframework.amqp.core.Message; +import org.springframework.amqp.core.MessageProperties; + +import com.rabbitmq.client.Channel; +import com.rabbitmq.client.Connection; + +@RunWith(TracingSegmentRunner.class) +public class RabbitMQSpringConsumerInterceptorTest { + + @SegmentStoragePoint + private SegmentStorage segmentStorage; + + private SpringRabbitMQConsumerInterceptor rabbitMQConsumerInterceptor; + + @Mock + private EnhancedInstance enhancedInstance; + + @Rule + public AgentServiceRule serviceRule = new AgentServiceRule(); + + @Before + public void setUp() throws Exception { + rabbitMQConsumerInterceptor = new SpringRabbitMQConsumerInterceptor(); + } + + @Test + public void testRabbitMQConsumerInterceptorWithNilHeaders() throws Throwable { + Object[] args = prepareMockData(false); + rabbitMQConsumerInterceptor.beforeMethod(enhancedInstance, null, args, new Class[0], null); + rabbitMQConsumerInterceptor.afterMethod(enhancedInstance, null, args, new Class[0], null, null); + List traceSegments = segmentStorage.getTraceSegments(); + Assert.assertThat(traceSegments.size(), is(1)); + } + + @Test + public void testRabbitMQConsumerInterceptor() throws Throwable { + Object[] args = prepareMockData(true); + rabbitMQConsumerInterceptor.beforeMethod(enhancedInstance, null, args, new Class[0], null); + rabbitMQConsumerInterceptor.afterMethod(enhancedInstance, null, args, new Class[0], null, null); + List traceSegments = segmentStorage.getTraceSegments(); + Assert.assertThat(traceSegments.size(), is(1)); + } + + @Test + public void testBatchMessageConsumption() throws Throwable { + Object[] args = prepareMockBatchData(true); + rabbitMQConsumerInterceptor.beforeMethod(enhancedInstance, null, args, new Class[0], null); + rabbitMQConsumerInterceptor.afterMethod(enhancedInstance, null, args, new Class[0], null, null); + List traceSegments = segmentStorage.getTraceSegments(); + Assert.assertThat(traceSegments.size(), is(1)); + } + + @Test + public void testEmptyBatchMessageConsumption() throws Throwable { + Channel channel = mock(Channel.class); + Connection connection = mock(Connection.class); + InetAddress address = mock(InetAddress.class); + + when(channel.getConnection()).thenReturn(connection); + when(connection.getAddress()).thenReturn(address); + when(address.getHostAddress()).thenReturn("127.0.0.1"); + when(connection.getPort()).thenReturn(5672); + + Object[] args = new Object[] {channel, new java.util.ArrayList()}; + rabbitMQConsumerInterceptor.beforeMethod(enhancedInstance, null, args, new Class[0], null); + rabbitMQConsumerInterceptor.afterMethod(enhancedInstance, null, args, new Class[0], null, null); + List traceSegments = segmentStorage.getTraceSegments(); + Assert.assertThat(traceSegments.size(), is(0)); + } + + private Object[] prepareMockData(boolean withHeaders) throws Exception { + Channel channel = mock(Channel.class); + Connection connection = mock(Connection.class); + InetAddress address = mock(InetAddress.class); + Message message = mock(Message.class); + MessageProperties messageProperties = mock(MessageProperties.class); + + when(channel.getConnection()).thenReturn(connection); + when(connection.getAddress()).thenReturn(address); + when(address.getHostAddress()).thenReturn("127.0.0.1"); + when(connection.getPort()).thenReturn(5672); + when(message.getMessageProperties()).thenReturn(messageProperties); + when(messageProperties.getReceivedExchange()).thenReturn("test-exchange"); + when(messageProperties.getReceivedRoutingKey()).thenReturn("test-routing-key"); + + if (withHeaders) { + Map headers = new HashMap<>(); + headers.put(SW8CarrierItem.HEADER_NAME, + "1-My40LjU=-MS4yLjM=-3-c2VydmljZQ==-aW5zdGFuY2U=-L2FwcA==-MTI3LjAuMC4xOjgwODA="); + when(messageProperties.getHeader(SW8CarrierItem.HEADER_NAME)) + .thenReturn(headers.get(SW8CarrierItem.HEADER_NAME)); + } + + return new Object[] {channel, message}; + } + + private Object[] prepareMockBatchData(boolean withHeaders) throws Exception { + Channel channel = mock(Channel.class); + Connection connection = mock(Connection.class); + InetAddress address = mock(InetAddress.class); + + when(channel.getConnection()).thenReturn(connection); + when(connection.getAddress()).thenReturn(address); + when(address.getHostAddress()).thenReturn("127.0.0.1"); + when(connection.getPort()).thenReturn(5672); + + Message message1 = mock(Message.class); + Message message2 = mock(Message.class); + Message message3 = mock(Message.class); + MessageProperties props1 = mock(MessageProperties.class); + MessageProperties props2 = mock(MessageProperties.class); + MessageProperties props3 = mock(MessageProperties.class); + + when(message1.getMessageProperties()).thenReturn(props1); + when(message2.getMessageProperties()).thenReturn(props2); + when(message3.getMessageProperties()).thenReturn(props3); + + when(props1.getReceivedExchange()).thenReturn("test-exchange"); + when(props1.getReceivedRoutingKey()).thenReturn("test-routing-key"); + when(props2.getReceivedExchange()).thenReturn("test-exchange"); + when(props2.getReceivedRoutingKey()).thenReturn("test-routing-key"); + when(props3.getReceivedExchange()).thenReturn("test-exchange"); + when(props3.getReceivedRoutingKey()).thenReturn("test-routing-key"); + + if (withHeaders) { + Map headers1 = new HashMap<>(); + headers1.put(SW8CarrierItem.HEADER_NAME, + "1-My40LjU=-MS4yLjM=-3-c2VydmljZQ==-aW5zdGFuY2U=-L2FwcA==-MTI3LjAuMC4xOjgwODA="); + when(props1.getHeader(SW8CarrierItem.HEADER_NAME)) + .thenReturn(headers1.get(SW8CarrierItem.HEADER_NAME)); + + Map headers2 = new HashMap<>(); + headers2.put(SW8CarrierItem.HEADER_NAME, + "1-NTY3Ljg=-OS4xMC4xMQ==-12-ZXJ2aWNlMg==-aW5zdGFuY2UyLU9hcHA=-MTI3LjAuMC4yOjgwODA="); + when(props2.getHeader(SW8CarrierItem.HEADER_NAME)) + .thenReturn(headers2.get(SW8CarrierItem.HEADER_NAME)); + + Map headers3 = new HashMap<>(); + headers3.put(SW8CarrierItem.HEADER_NAME, + "1-MTExLjIyMi4zMzM=-NDQ0LjU1NS42NjY=-MzQ1-ZXJ2aWNlMw==-aW5zdGFuY2UzLU9hcHA=-MTI3LjAuMC4zOjgwODA="); + when(props3.getHeader(SW8CarrierItem.HEADER_NAME)) + .thenReturn(headers3.get(SW8CarrierItem.HEADER_NAME)); + } + + List messages = new ArrayList<>(Arrays.asList(message1, message2, message3)); + return new Object[] {channel, messages}; + } +} diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-webflux-5.x-webclient-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-webflux-5.x-webclient-plugin/pom.xml index 067bc2df0f..1a3cfbfeb7 100644 --- a/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-webflux-5.x-webclient-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-webflux-5.x-webclient-plugin/pom.xml @@ -21,7 +21,7 @@ spring-plugins org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-webflux-6.x-webclient-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-webflux-6.x-webclient-plugin/pom.xml index 691973be6c..6f67c4b024 100644 --- a/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-webflux-6.x-webclient-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-webflux-6.x-webclient-plugin/pom.xml @@ -21,7 +21,7 @@ spring-plugins org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-sdk-plugin/spymemcached-2.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/spymemcached-2.x-plugin/pom.xml index 0deb79662c..1b123e2207 100644 --- a/apm-sniffer/apm-sdk-plugin/spymemcached-2.x-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/spymemcached-2.x-plugin/pom.xml @@ -22,7 +22,7 @@ org.apache.skywalking apm-sdk-plugin - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT apm-spymemcached-2.x-plugin diff --git a/apm-sniffer/apm-sdk-plugin/struts2-2.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/struts2-2.x-plugin/pom.xml index 45046d6808..eb3e342fd3 100644 --- a/apm-sniffer/apm-sdk-plugin/struts2-2.x-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/struts2-2.x-plugin/pom.xml @@ -20,7 +20,7 @@ apm-sdk-plugin org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-sdk-plugin/thrift-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/thrift-plugin/pom.xml index 81abe91fb0..dc51e01a99 100644 --- a/apm-sniffer/apm-sdk-plugin/thrift-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/thrift-plugin/pom.xml @@ -21,7 +21,7 @@ org.apache.skywalking apm-sdk-plugin - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-sdk-plugin/tomcat-10x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/tomcat-10x-plugin/pom.xml index 0cda2bb576..62896f4b61 100644 --- a/apm-sniffer/apm-sdk-plugin/tomcat-10x-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/tomcat-10x-plugin/pom.xml @@ -21,12 +21,11 @@ apm-sdk-plugin org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 tomcat-10x-plugin - 8.11.0 8 diff --git a/apm-sniffer/apm-sdk-plugin/tomcat-7.x-8.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/tomcat-7.x-8.x-plugin/pom.xml index 176db30b0a..0763bcca65 100644 --- a/apm-sniffer/apm-sdk-plugin/tomcat-7.x-8.x-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/tomcat-7.x-8.x-plugin/pom.xml @@ -20,7 +20,7 @@ apm-sdk-plugin org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 @@ -40,7 +40,6 @@ junit junit - ${junit.version} test diff --git a/apm-sniffer/apm-sdk-plugin/tomcat-thread-pool-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/tomcat-thread-pool-plugin/pom.xml index 8db664a50b..2007b9711e 100644 --- a/apm-sniffer/apm-sdk-plugin/tomcat-thread-pool-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/tomcat-thread-pool-plugin/pom.xml @@ -21,7 +21,7 @@ apm-sdk-plugin org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-sdk-plugin/undertow-plugins/pom.xml b/apm-sniffer/apm-sdk-plugin/undertow-plugins/pom.xml index 7fe5ee485a..14d23e7692 100644 --- a/apm-sniffer/apm-sdk-plugin/undertow-plugins/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/undertow-plugins/pom.xml @@ -23,7 +23,7 @@ org.apache.skywalking apm-sdk-plugin - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT undertow-plugins @@ -37,6 +37,5 @@ UTF-8 - /.. diff --git a/apm-sniffer/apm-sdk-plugin/undertow-plugins/undertow-2.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/undertow-plugins/undertow-2.x-plugin/pom.xml index e07b1920fd..8b722364c6 100644 --- a/apm-sniffer/apm-sdk-plugin/undertow-plugins/undertow-2.x-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/undertow-plugins/undertow-2.x-plugin/pom.xml @@ -20,7 +20,7 @@ undertow-plugins org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-sdk-plugin/undertow-worker-thread-pool-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/undertow-worker-thread-pool-plugin/pom.xml index b2bdda708b..aed38b73f1 100644 --- a/apm-sniffer/apm-sdk-plugin/undertow-worker-thread-pool-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/undertow-worker-thread-pool-plugin/pom.xml @@ -21,7 +21,7 @@ apm-sdk-plugin org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 @@ -29,5 +29,12 @@ undertow-worker-thread-pool-plugin http://maven.apache.org - + + + org.jboss.xnio + xnio-api + 3.8.4.Final + provided + + \ No newline at end of file diff --git a/apm-sniffer/apm-sdk-plugin/undertow-worker-thread-pool-plugin/src/main/java/org/apache/skywalking/apm/plugin/undertow/worker/thread/pool/XnioWorkerConstructorInterceptor.java b/apm-sniffer/apm-sdk-plugin/undertow-worker-thread-pool-plugin/src/main/java/org/apache/skywalking/apm/plugin/undertow/worker/thread/pool/XnioWorkerConstructorInterceptor.java new file mode 100644 index 0000000000..88cecc5ca1 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/undertow-worker-thread-pool-plugin/src/main/java/org/apache/skywalking/apm/plugin/undertow/worker/thread/pool/XnioWorkerConstructorInterceptor.java @@ -0,0 +1,65 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.undertow.worker.thread.pool; + +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.function.Function; +import java.util.function.Supplier; +import org.apache.skywalking.apm.agent.core.meter.MeterFactory; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceConstructorInterceptor; +import org.apache.skywalking.apm.plugin.undertow.worker.thread.pool.util.XnioWorkerTaskPoolAccessor; +import org.xnio.XnioWorker; + +public class XnioWorkerConstructorInterceptor implements InstanceConstructorInterceptor { + + private static final String THREAD_POOL_NAME = "undertow_worker_pool"; + + private static final Map>> METRIC_MAP = new HashMap>>() {{ + put("core_pool_size", (XnioWorkerTaskPoolAccessor threadPoolExecutor) -> () -> (double) threadPoolExecutor.getCorePoolSize()); + put("max_pool_size", (XnioWorkerTaskPoolAccessor threadPoolExecutor) -> () -> (double) threadPoolExecutor.getMaximumPoolSize()); + put("pool_size", (XnioWorkerTaskPoolAccessor threadPoolExecutor) -> () -> (double) threadPoolExecutor.getPoolSize()); + put("queue_size", (XnioWorkerTaskPoolAccessor threadPoolExecutor) -> () -> (double) threadPoolExecutor.getQueueSize()); + put("active_size", (XnioWorkerTaskPoolAccessor threadPoolExecutor) -> () -> (double) threadPoolExecutor.getActiveCount()); + }}; + + @Override + public void onConstruct(EnhancedInstance objInst, Object[] allArguments) throws Throwable { + buildThreadPoolMeterMetric(new XnioWorkerTaskPoolAccessor((XnioWorker) objInst)); + } + + private void buildThreadPoolMeterMetric(XnioWorkerTaskPoolAccessor xnioWorkerTaskPoolAccessor) { + String threadPoolMeterName = "thread_pool"; + String poolNameTag = "pool_name"; + String metricTypeTag = "metric_type"; + METRIC_MAP.forEach((key, value) -> { + if (Objects.equals(key, "pool_size")) { + if (xnioWorkerTaskPoolAccessor.isContainsGetPoolSizeMethod()) { + MeterFactory.gauge(threadPoolMeterName, value.apply(xnioWorkerTaskPoolAccessor)) + .tag(poolNameTag, THREAD_POOL_NAME).tag(metricTypeTag, key).build(); + } + } else { + MeterFactory.gauge(threadPoolMeterName, value.apply(xnioWorkerTaskPoolAccessor)) + .tag(poolNameTag, THREAD_POOL_NAME).tag(metricTypeTag, key).build(); + } + }); + } +} diff --git a/apm-sniffer/apm-sdk-plugin/undertow-worker-thread-pool-plugin/src/main/java/org/apache/skywalking/apm/plugin/undertow/worker/thread/pool/define/UndertowWorkerThreadPoolInstrumentation.java b/apm-sniffer/apm-sdk-plugin/undertow-worker-thread-pool-plugin/src/main/java/org/apache/skywalking/apm/plugin/undertow/worker/thread/pool/define/UndertowWorkerThreadPoolInstrumentation.java index edca92a998..ce827e349c 100644 --- a/apm-sniffer/apm-sdk-plugin/undertow-worker-thread-pool-plugin/src/main/java/org/apache/skywalking/apm/plugin/undertow/worker/thread/pool/define/UndertowWorkerThreadPoolInstrumentation.java +++ b/apm-sniffer/apm-sdk-plugin/undertow-worker-thread-pool-plugin/src/main/java/org/apache/skywalking/apm/plugin/undertow/worker/thread/pool/define/UndertowWorkerThreadPoolInstrumentation.java @@ -19,43 +19,56 @@ package org.apache.skywalking.apm.plugin.undertow.worker.thread.pool.define; import static net.bytebuddy.matcher.ElementMatchers.any; -import static org.apache.skywalking.apm.agent.core.plugin.match.HierarchyMatch.byHierarchyMatch; -import static org.apache.skywalking.apm.agent.core.plugin.match.PrefixMatch.nameStartsWith; +import static net.bytebuddy.matcher.ElementMatchers.named; +import static org.apache.skywalking.apm.agent.core.plugin.match.NameMatch.byName; +import java.util.Collections; +import java.util.List; import net.bytebuddy.description.method.MethodDescription; import net.bytebuddy.matcher.ElementMatcher; +import org.apache.skywalking.apm.agent.core.plugin.WitnessMethod; import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint; import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint; import org.apache.skywalking.apm.agent.core.plugin.interceptor.StaticMethodsInterceptPoint; import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassEnhancePluginDefine; import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch; -import org.apache.skywalking.apm.agent.core.plugin.match.logical.LogicalMatchOperation; +/** + * ThreadPoolExecutor implemented xnio worker task pool before 3.6.0 + */ public class UndertowWorkerThreadPoolInstrumentation extends ClassEnhancePluginDefine { - private static final String THREAD_POOL_EXECUTOR_CLASS = "java.util.concurrent.ThreadPoolExecutor"; + private static final String THREAD_POOL_EXECUTOR_CLASS = "org.xnio.XnioWorker$TaskPool"; private static final String UNDERTOW_WORKER_THREAD_POOL_INTERCEPT = "org.apache.skywalking.apm.plugin.undertow.worker.thread.pool.UndertowWorkerThreadPoolConstructorIntercept"; + @Override + protected List witnessMethods() { + return Collections.singletonList(new WitnessMethod( + "org.xnio.XnioWorker$TaskPool", + named("terminated") + )); + } + @Override protected ClassMatch enhanceClass() { - return LogicalMatchOperation.and(nameStartsWith("org.xnio"), byHierarchyMatch(THREAD_POOL_EXECUTOR_CLASS)); + return byName(THREAD_POOL_EXECUTOR_CLASS); } @Override public ConstructorInterceptPoint[] getConstructorsInterceptPoints() { - return new ConstructorInterceptPoint[]{ - new ConstructorInterceptPoint() { - @Override - public ElementMatcher getConstructorMatcher() { - return any(); - } + return new ConstructorInterceptPoint[] { + new ConstructorInterceptPoint() { + @Override + public ElementMatcher getConstructorMatcher() { + return any(); + } - @Override - public String getConstructorInterceptor() { - return UNDERTOW_WORKER_THREAD_POOL_INTERCEPT; - } + @Override + public String getConstructorInterceptor() { + return UNDERTOW_WORKER_THREAD_POOL_INTERCEPT; } + } }; } diff --git a/apm-sniffer/apm-sdk-plugin/undertow-worker-thread-pool-plugin/src/main/java/org/apache/skywalking/apm/plugin/undertow/worker/thread/pool/define/XnioWorkerConstructorInstrumentation.java b/apm-sniffer/apm-sdk-plugin/undertow-worker-thread-pool-plugin/src/main/java/org/apache/skywalking/apm/plugin/undertow/worker/thread/pool/define/XnioWorkerConstructorInstrumentation.java new file mode 100644 index 0000000000..2bc13dd20f --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/undertow-worker-thread-pool-plugin/src/main/java/org/apache/skywalking/apm/plugin/undertow/worker/thread/pool/define/XnioWorkerConstructorInstrumentation.java @@ -0,0 +1,78 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.undertow.worker.thread.pool.define; + +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.matcher.ElementMatcher; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.StaticMethodsInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassEnhancePluginDefine; +import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch; + +import static net.bytebuddy.matcher.ElementMatchers.any; +import static org.apache.skywalking.apm.agent.core.plugin.match.NameMatch.byName; + +/** + * xnio task pool new implementation since 3.6.0 + * https://github.com/xnio/xnio/commit/071800e0a85c9da9b88a976ac7ecb85760924dbf + */ +public class XnioWorkerConstructorInstrumentation extends ClassEnhancePluginDefine { + + private static final String XNIO_WORKER_CLASS = "org.xnio.XnioWorker"; + + private static final String UNDERTOW_WORKER_THREAD_POOL_INTERCEPT = "org.apache.skywalking.apm.plugin.undertow.worker.thread.pool.XnioWorkerConstructorInterceptor"; + + @Override + protected String[] witnessClasses() { + return new String[] {"org.xnio.XnioWorker$EnhancedQueueExecutorTaskPool"}; + } + + @Override + protected ClassMatch enhanceClass() { + return byName(XNIO_WORKER_CLASS); + } + + @Override + public ConstructorInterceptPoint[] getConstructorsInterceptPoints() { + return new ConstructorInterceptPoint[] { + new ConstructorInterceptPoint() { + @Override + public ElementMatcher getConstructorMatcher() { + return any(); + } + + @Override + public String getConstructorInterceptor() { + return UNDERTOW_WORKER_THREAD_POOL_INTERCEPT; + } + } + }; + } + + @Override + public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() { + return new InstanceMethodsInterceptPoint[0]; + } + + @Override + public StaticMethodsInterceptPoint[] getStaticMethodsInterceptPoints() { + return new StaticMethodsInterceptPoint[0]; + } +} diff --git a/apm-sniffer/apm-sdk-plugin/undertow-worker-thread-pool-plugin/src/main/java/org/apache/skywalking/apm/plugin/undertow/worker/thread/pool/util/XnioWorkerTaskPoolAccessor.java b/apm-sniffer/apm-sdk-plugin/undertow-worker-thread-pool-plugin/src/main/java/org/apache/skywalking/apm/plugin/undertow/worker/thread/pool/util/XnioWorkerTaskPoolAccessor.java new file mode 100644 index 0000000000..2284115437 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/undertow-worker-thread-pool-plugin/src/main/java/org/apache/skywalking/apm/plugin/undertow/worker/thread/pool/util/XnioWorkerTaskPoolAccessor.java @@ -0,0 +1,117 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.undertow.worker.thread.pool.util; + +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import lombok.Getter; +import org.xnio.XnioWorker; + +public class XnioWorkerTaskPoolAccessor { + + private final Object taskPool; + @Getter + private boolean containsGetPoolSizeMethod; + + private Method getCorePoolSizeMethod; + private Method getMaximumPoolSizeMethod; + private Method getActiveCountMethod; + private Method getPoolSizeMethod; + private Method getQueueSizeMethod; + + public XnioWorkerTaskPoolAccessor(final XnioWorker worker) throws NoSuchFieldException, IllegalAccessException { + Field field = worker.getClass().getSuperclass().getDeclaredField("taskPool"); + field.setAccessible(true); + this.taskPool = field.get(worker); + + try { + getCorePoolSizeMethod = taskPool.getClass().getDeclaredMethod("getCorePoolSize"); + getCorePoolSizeMethod.setAccessible(true); + } catch (NoSuchMethodException e) { + // ignore + } + try { + getMaximumPoolSizeMethod = taskPool.getClass().getDeclaredMethod("getMaximumPoolSize"); + getMaximumPoolSizeMethod.setAccessible(true); + } catch (NoSuchMethodException e) { + // ignore + } + try { + getActiveCountMethod = taskPool.getClass().getDeclaredMethod("getActiveCount"); + getActiveCountMethod.setAccessible(true); + } catch (NoSuchMethodException e) { + // ignore + } + try { + // getPoolSize add since 3.8.0 + getPoolSizeMethod = taskPool.getClass().getDeclaredMethod("getPoolSize"); + getPoolSizeMethod.setAccessible(true); + containsGetPoolSizeMethod = true; + } catch (NoSuchMethodException e) { + containsGetPoolSizeMethod = false; + } + try { + getQueueSizeMethod = taskPool.getClass().getDeclaredMethod("getQueueSize"); + getQueueSizeMethod.setAccessible(true); + } catch (NoSuchMethodException e) { + // ignore + } + } + + public int getCorePoolSize() { + try { + return (int) getCorePoolSizeMethod.invoke(taskPool); + } catch (IllegalAccessException | InvocationTargetException e) { + throw new RuntimeException(e); + } + } + + public int getMaximumPoolSize() { + try { + return (int) getMaximumPoolSizeMethod.invoke(taskPool); + } catch (IllegalAccessException | InvocationTargetException e) { + throw new RuntimeException(e); + } + } + + public int getActiveCount() { + try { + return (int) getActiveCountMethod.invoke(taskPool); + } catch (IllegalAccessException | InvocationTargetException e) { + throw new RuntimeException(e); + } + } + + public int getPoolSize() { + try { + return (int) getPoolSizeMethod.invoke(taskPool); + } catch (IllegalAccessException | InvocationTargetException e) { + throw new RuntimeException(e); + } + } + + public int getQueueSize() { + try { + return (int) getQueueSizeMethod.invoke(taskPool); + } catch (IllegalAccessException | InvocationTargetException e) { + throw new RuntimeException(e); + } + } +} diff --git a/apm-sniffer/apm-sdk-plugin/undertow-worker-thread-pool-plugin/src/main/resources/skywalking-plugin.def b/apm-sniffer/apm-sdk-plugin/undertow-worker-thread-pool-plugin/src/main/resources/skywalking-plugin.def index 22837a7aad..8ff6aacdbd 100644 --- a/apm-sniffer/apm-sdk-plugin/undertow-worker-thread-pool-plugin/src/main/resources/skywalking-plugin.def +++ b/apm-sniffer/apm-sdk-plugin/undertow-worker-thread-pool-plugin/src/main/resources/skywalking-plugin.def @@ -14,4 +14,5 @@ # See the License for the specific language governing permissions and # limitations under the License. -undertow-worker-thread-pool=org.apache.skywalking.apm.plugin.undertow.worker.thread.pool.define.UndertowWorkerThreadPoolInstrumentation \ No newline at end of file +undertow-worker-thread-pool=org.apache.skywalking.apm.plugin.undertow.worker.thread.pool.define.UndertowWorkerThreadPoolInstrumentation +undertow-worker-thread-pool=org.apache.skywalking.apm.plugin.undertow.worker.thread.pool.define.XnioWorkerConstructorInstrumentation \ No newline at end of file diff --git a/apm-sniffer/apm-sdk-plugin/vertx-plugins/pom.xml b/apm-sniffer/apm-sdk-plugin/vertx-plugins/pom.xml index 97d0535cea..a7f441800d 100644 --- a/apm-sniffer/apm-sdk-plugin/vertx-plugins/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/vertx-plugins/pom.xml @@ -23,7 +23,7 @@ org.apache.skywalking apm-sdk-plugin - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT vertx-plugins @@ -38,7 +38,6 @@ UTF-8 - /.. diff --git a/apm-sniffer/apm-sdk-plugin/vertx-plugins/vertx-core-3.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/vertx-plugins/vertx-core-3.x-plugin/pom.xml index c011313320..b1d89698a5 100644 --- a/apm-sniffer/apm-sdk-plugin/vertx-plugins/vertx-core-3.x-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/vertx-plugins/vertx-core-3.x-plugin/pom.xml @@ -20,7 +20,7 @@ vertx-plugins org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-sdk-plugin/vertx-plugins/vertx-core-4.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/vertx-plugins/vertx-core-4.x-plugin/pom.xml index be902137b7..9e21fe716a 100644 --- a/apm-sniffer/apm-sdk-plugin/vertx-plugins/vertx-core-4.x-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/vertx-plugins/vertx-core-4.x-plugin/pom.xml @@ -20,7 +20,7 @@ vertx-plugins org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-sdk-plugin/websphere-liberty-23.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/websphere-liberty-23.x-plugin/pom.xml index 6b54ab3248..7795fd9659 100644 --- a/apm-sniffer/apm-sdk-plugin/websphere-liberty-23.x-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/websphere-liberty-23.x-plugin/pom.xml @@ -21,7 +21,7 @@ apm-sdk-plugin org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 @@ -35,7 +35,6 @@ junit junit - ${junit.version} test diff --git a/apm-sniffer/apm-sdk-plugin/xmemcached-2.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/xmemcached-2.x-plugin/pom.xml index 21b1411836..35876e8aff 100644 --- a/apm-sniffer/apm-sdk-plugin/xmemcached-2.x-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/xmemcached-2.x-plugin/pom.xml @@ -21,7 +21,7 @@ org.apache.skywalking apm-sdk-plugin - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT apm-xmemcached-2.x-plugin diff --git a/apm-sniffer/apm-sdk-plugin/xxl-job-2.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/xxl-job-2.x-plugin/pom.xml index 6400d0cca7..da1409f18d 100644 --- a/apm-sniffer/apm-sdk-plugin/xxl-job-2.x-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/xxl-job-2.x-plugin/pom.xml @@ -21,7 +21,7 @@ apm-sdk-plugin org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-test-tools/pom.xml b/apm-sniffer/apm-test-tools/pom.xml index 9e1dc85659..9dd0f43cb3 100644 --- a/apm-sniffer/apm-test-tools/pom.xml +++ b/apm-sniffer/apm-test-tools/pom.xml @@ -22,7 +22,7 @@ org.apache.skywalking java-agent-sniffer - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT apm-test-tools @@ -38,7 +38,6 @@ junit junit - ${junit.version} provided diff --git a/apm-sniffer/apm-test-tools/src/main/java/org/apache/skywalking/apm/agent/core/context/MockContextSnapshot.java b/apm-sniffer/apm-test-tools/src/main/java/org/apache/skywalking/apm/agent/core/context/MockContextSnapshot.java index 505a4924a9..c6d5f91fcc 100644 --- a/apm-sniffer/apm-test-tools/src/main/java/org/apache/skywalking/apm/agent/core/context/MockContextSnapshot.java +++ b/apm-sniffer/apm-test-tools/src/main/java/org/apache/skywalking/apm/agent/core/context/MockContextSnapshot.java @@ -26,6 +26,8 @@ public enum MockContextSnapshot { private ContextSnapshot contextSnapshot; + private ContextSnapshot ignoreContextSnapshot; + MockContextSnapshot() { contextSnapshot = new ContextSnapshot( "1, 2, 3", @@ -36,9 +38,21 @@ public enum MockContextSnapshot { new ExtensionContext(), ProfileStatusContext.createWithNone() ); + ignoreContextSnapshot = new ContextSnapshot( + null, + -1, + null, + null, + new CorrelationContext(), + new ExtensionContext(), + ProfileStatusContext.createWithNone()); } public ContextSnapshot mockContextSnapshot() { return contextSnapshot; } + + public ContextSnapshot mockIgnoreContextSnapshot() { + return ignoreContextSnapshot; + } } diff --git a/apm-sniffer/apm-test-tools/src/main/java/org/apache/skywalking/apm/agent/test/tools/TracingSegmentRunner.java b/apm-sniffer/apm-test-tools/src/main/java/org/apache/skywalking/apm/agent/test/tools/TracingSegmentRunner.java index 0626f36d01..8dbb6cf1a3 100644 --- a/apm-sniffer/apm-test-tools/src/main/java/org/apache/skywalking/apm/agent/test/tools/TracingSegmentRunner.java +++ b/apm-sniffer/apm-test-tools/src/main/java/org/apache/skywalking/apm/agent/test/tools/TracingSegmentRunner.java @@ -55,7 +55,7 @@ protected Object createTest() throws Exception { @Override protected Statement withAfters(FrameworkMethod method, Object target, final Statement statement) { - return new Statement() { + Statement st = new Statement() { @Override public void evaluate() throws Throwable { if (field != null) { @@ -89,5 +89,7 @@ public void afterFinished(IgnoredTracerContext tracerContext) { } } }; + + return super.withAfters(method, target, st); } } diff --git a/apm-sniffer/apm-toolkit-activation/apm-toolkit-kafka-activation/pom.xml b/apm-sniffer/apm-toolkit-activation/apm-toolkit-kafka-activation/pom.xml index f2be4b6591..a914b8d528 100644 --- a/apm-sniffer/apm-toolkit-activation/apm-toolkit-kafka-activation/pom.xml +++ b/apm-sniffer/apm-toolkit-activation/apm-toolkit-kafka-activation/pom.xml @@ -21,7 +21,7 @@ apm-toolkit-activation org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-toolkit-activation/apm-toolkit-log4j-1.x-activation/pom.xml b/apm-sniffer/apm-toolkit-activation/apm-toolkit-log4j-1.x-activation/pom.xml index 55d214e1d6..86e57d28bb 100644 --- a/apm-sniffer/apm-toolkit-activation/apm-toolkit-log4j-1.x-activation/pom.xml +++ b/apm-sniffer/apm-toolkit-activation/apm-toolkit-log4j-1.x-activation/pom.xml @@ -21,7 +21,7 @@ apm-toolkit-activation org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-toolkit-activation/apm-toolkit-log4j-2.x-activation/pom.xml b/apm-sniffer/apm-toolkit-activation/apm-toolkit-log4j-2.x-activation/pom.xml index 45190854e3..5e1a0fb598 100644 --- a/apm-sniffer/apm-toolkit-activation/apm-toolkit-log4j-2.x-activation/pom.xml +++ b/apm-sniffer/apm-toolkit-activation/apm-toolkit-log4j-2.x-activation/pom.xml @@ -21,7 +21,7 @@ apm-toolkit-activation org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-toolkit-activation/apm-toolkit-logback-1.x-activation/pom.xml b/apm-sniffer/apm-toolkit-activation/apm-toolkit-logback-1.x-activation/pom.xml index e56f573c60..341faac073 100644 --- a/apm-sniffer/apm-toolkit-activation/apm-toolkit-logback-1.x-activation/pom.xml +++ b/apm-sniffer/apm-toolkit-activation/apm-toolkit-logback-1.x-activation/pom.xml @@ -21,7 +21,7 @@ apm-toolkit-activation org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-toolkit-activation/apm-toolkit-logging-common/pom.xml b/apm-sniffer/apm-toolkit-activation/apm-toolkit-logging-common/pom.xml index 2c42d0ea78..ae19d88816 100644 --- a/apm-sniffer/apm-toolkit-activation/apm-toolkit-logging-common/pom.xml +++ b/apm-sniffer/apm-toolkit-activation/apm-toolkit-logging-common/pom.xml @@ -20,7 +20,7 @@ apm-toolkit-activation org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-toolkit-activation/apm-toolkit-meter-activation/pom.xml b/apm-sniffer/apm-toolkit-activation/apm-toolkit-meter-activation/pom.xml index a767d9fa99..4d260f63e5 100644 --- a/apm-sniffer/apm-toolkit-activation/apm-toolkit-meter-activation/pom.xml +++ b/apm-sniffer/apm-toolkit-activation/apm-toolkit-meter-activation/pom.xml @@ -21,7 +21,7 @@ apm-toolkit-activation org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-toolkit-activation/apm-toolkit-micrometer-activation/pom.xml b/apm-sniffer/apm-toolkit-activation/apm-toolkit-micrometer-activation/pom.xml index 84559dac5e..7e9c7d103f 100644 --- a/apm-sniffer/apm-toolkit-activation/apm-toolkit-micrometer-activation/pom.xml +++ b/apm-sniffer/apm-toolkit-activation/apm-toolkit-micrometer-activation/pom.xml @@ -20,7 +20,7 @@ apm-toolkit-activation org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-toolkit-activation/apm-toolkit-micrometer-activation/src/test/java/org/apache/skywalking/apm/toolkit/activation/micrometer/MicrometerContextSnapshotThreadLocalAccessorInterceptorTest.java b/apm-sniffer/apm-toolkit-activation/apm-toolkit-micrometer-activation/src/test/java/org/apache/skywalking/apm/toolkit/activation/micrometer/MicrometerContextSnapshotThreadLocalAccessorInterceptorTest.java index 3e3f66c844..258808f8f1 100644 --- a/apm-sniffer/apm-toolkit-activation/apm-toolkit-micrometer-activation/src/test/java/org/apache/skywalking/apm/toolkit/activation/micrometer/MicrometerContextSnapshotThreadLocalAccessorInterceptorTest.java +++ b/apm-sniffer/apm-toolkit-activation/apm-toolkit-micrometer-activation/src/test/java/org/apache/skywalking/apm/toolkit/activation/micrometer/MicrometerContextSnapshotThreadLocalAccessorInterceptorTest.java @@ -92,8 +92,6 @@ public void clear() { @AfterClass public static void clearAfterAll() { // test from threadlocalaccessor test x 2 TODO: I have no idea what is going on - ContextManager.stopSpan(); - ContextManager.stopSpan(); assertThat(ContextManager.isActive(), is(false)); } @@ -103,6 +101,7 @@ public void testServiceFromPlugin() { PluginBootService.class); Assert.assertNotNull(service); + ContextManager.stopSpan(); } @Test @@ -110,6 +109,7 @@ public void testServiceOverrideFromPlugin() { ContextManagerExtendService service = ServiceManager.INSTANCE.findService(ContextManagerExtendService.class); Assert.assertTrue(service instanceof ContextManagerExtendOverrideService); + ContextManager.stopSpan(); } @Test diff --git a/apm-sniffer/apm-toolkit-activation/apm-toolkit-opentracing-activation/pom.xml b/apm-sniffer/apm-toolkit-activation/apm-toolkit-opentracing-activation/pom.xml index 3b556d06d2..05dc1b4ac9 100644 --- a/apm-sniffer/apm-toolkit-activation/apm-toolkit-opentracing-activation/pom.xml +++ b/apm-sniffer/apm-toolkit-activation/apm-toolkit-opentracing-activation/pom.xml @@ -21,7 +21,7 @@ apm-toolkit-activation org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-toolkit-activation/apm-toolkit-trace-activation/pom.xml b/apm-sniffer/apm-toolkit-activation/apm-toolkit-trace-activation/pom.xml index 268aa4f9ea..f93d0a3397 100644 --- a/apm-sniffer/apm-toolkit-activation/apm-toolkit-trace-activation/pom.xml +++ b/apm-sniffer/apm-toolkit-activation/apm-toolkit-trace-activation/pom.xml @@ -21,7 +21,7 @@ apm-toolkit-activation org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-toolkit-activation/apm-toolkit-trace-activation/src/test/java/org/apache/skywalking/apm/toolkit/activation/trace/CallableOrRunnableInterceptorTest.java b/apm-sniffer/apm-toolkit-activation/apm-toolkit-trace-activation/src/test/java/org/apache/skywalking/apm/toolkit/activation/trace/CallableOrRunnableInterceptorTest.java index 45e00baa73..3aacc88c67 100644 --- a/apm-sniffer/apm-toolkit-activation/apm-toolkit-trace-activation/src/test/java/org/apache/skywalking/apm/toolkit/activation/trace/CallableOrRunnableInterceptorTest.java +++ b/apm-sniffer/apm-toolkit-activation/apm-toolkit-trace-activation/src/test/java/org/apache/skywalking/apm/toolkit/activation/trace/CallableOrRunnableInterceptorTest.java @@ -22,7 +22,7 @@ import java.lang.reflect.Method; import java.util.List; import java.util.concurrent.Callable; -import org.apache.skywalking.apm.agent.core.context.ContextSnapshot; +import org.apache.skywalking.apm.agent.core.context.MockContextSnapshot; import org.apache.skywalking.apm.agent.core.context.trace.AbstractTracingSpan; import org.apache.skywalking.apm.agent.core.context.trace.TraceSegment; import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; @@ -36,7 +36,6 @@ import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; -import org.mockito.Mock; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; @@ -69,9 +68,6 @@ public void setSkyWalkingDynamicField(Object value) { } }; - @Mock - private ContextSnapshot contextSnapshot; - private Object[] arguments; private Method callMethod; @@ -100,7 +96,7 @@ public void testOnConstructor() { @Test public void testCall() throws Throwable { - enhancedInstance.setSkyWalkingDynamicField(contextSnapshot); + enhancedInstance.setSkyWalkingDynamicField(MockContextSnapshot.INSTANCE.mockContextSnapshot()); callableCallInterceptor.beforeMethod(enhancedInstance, callMethod, arguments, null, null); callableCallInterceptor.afterMethod(enhancedInstance, callMethod, arguments, null, "result"); @@ -111,4 +107,16 @@ public void testCall() throws Throwable { } + @Test + public void testCallWithIgnoreSnapshot() throws Throwable { + + enhancedInstance.setSkyWalkingDynamicField(MockContextSnapshot.INSTANCE.mockIgnoreContextSnapshot()); + callableCallInterceptor.beforeMethod(enhancedInstance, callMethod, arguments, null, null); + callableCallInterceptor.afterMethod(enhancedInstance, callMethod, arguments, null, "result"); + + assertThat(segmentStorage.getTraceSegments().size(), is(0)); + assertThat(segmentStorage.getIgnoredTracerContexts().size(), is(1)); + + } + } diff --git a/apm-sniffer/apm-toolkit-activation/apm-toolkit-webflux-activation/pom.xml b/apm-sniffer/apm-toolkit-activation/apm-toolkit-webflux-activation/pom.xml index cbdf7f0850..d8832e24f5 100644 --- a/apm-sniffer/apm-toolkit-activation/apm-toolkit-webflux-activation/pom.xml +++ b/apm-sniffer/apm-toolkit-activation/apm-toolkit-webflux-activation/pom.xml @@ -21,7 +21,7 @@ apm-toolkit-activation org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/apm-toolkit-activation/pom.xml b/apm-sniffer/apm-toolkit-activation/pom.xml index 96d08926f6..f6e3b542c5 100644 --- a/apm-sniffer/apm-toolkit-activation/pom.xml +++ b/apm-sniffer/apm-toolkit-activation/pom.xml @@ -21,7 +21,7 @@ java-agent-sniffer org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 pom diff --git a/apm-sniffer/bootstrap-plugins/CLAUDE.md b/apm-sniffer/bootstrap-plugins/CLAUDE.md new file mode 100644 index 0000000000..51b4db99e7 --- /dev/null +++ b/apm-sniffer/bootstrap-plugins/CLAUDE.md @@ -0,0 +1,53 @@ +# CLAUDE.md - Bootstrap Plugin Development Guide + +This guide covers developing bootstrap-level plugins in `apm-sniffer/bootstrap-plugins/`. + +For general plugin development concepts (V2 API, tracing, class matching, testing), see `apm-sniffer/apm-sdk-plugin/CLAUDE.md`. + +## What Are Bootstrap Plugins? + +Bootstrap plugins instrument JDK core classes (rt.jar / java.base module) at the JVM bootstrap phase. They are loaded before application classes and can intercept fundamental JDK APIs. + +**Examples:** +- `jdk-threading-plugin` - Thread pool context propagation +- `jdk-http-plugin` - `HttpURLConnection` instrumentation +- `jdk-httpclient-plugin` - JDK 11+ `HttpClient` instrumentation +- `jdk-virtual-thread-executor-plugin` - JDK 21+ virtual thread support +- `jdk-forkjoinpool-plugin` - `ForkJoinPool` instrumentation + +## Key Difference from SDK Plugins + +Bootstrap plugins **must** override `isBootstrapInstrumentation()`: +```java +@Override +public boolean isBootstrapInstrumentation() { + return true; +} +``` + +**WARNING**: Use bootstrap instrumentation only where absolutely necessary. It affects JDK core classes and has broader impact than SDK plugins. + +## Development Rules + +All general plugin development rules apply (see `apm-sdk-plugin/CLAUDE.md`), plus: + +1. **Use V2 API** for new bootstrap plugins, same as SDK plugins +2. **Minimal scope**: Only intercept what is strictly necessary in JDK classes +3. **Performance critical**: Bootstrap plugins run on core JDK paths - keep interceptor logic lightweight +4. **Class loading awareness**: JDK core classes are loaded by the bootstrap classloader; be careful with class references that might not be visible at bootstrap level + +## Testing Bootstrap Plugins + +Bootstrap plugin test scenarios use `runningMode: with_bootstrap` in `configuration.yml`: +```yaml +type: jvm +entryService: http://localhost:8080/case +healthCheck: http://localhost:8080/health +startScript: ./bin/startup.sh +runningMode: with_bootstrap +withPlugins: jdk-threading-plugin-*.jar +``` + +This tells the test framework to load the plugin at the bootstrap level instead of the normal plugin directory. + +See `apm-sdk-plugin/CLAUDE.md` for full test framework documentation. diff --git a/apm-sniffer/bootstrap-plugins/jdk-forkjoinpool-plugin/pom.xml b/apm-sniffer/bootstrap-plugins/jdk-forkjoinpool-plugin/pom.xml index b71269dc8d..5de97c5a33 100644 --- a/apm-sniffer/bootstrap-plugins/jdk-forkjoinpool-plugin/pom.xml +++ b/apm-sniffer/bootstrap-plugins/jdk-forkjoinpool-plugin/pom.xml @@ -20,7 +20,7 @@ org.apache.skywalking bootstrap-plugins - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/bootstrap-plugins/jdk-http-plugin/pom.xml b/apm-sniffer/bootstrap-plugins/jdk-http-plugin/pom.xml index c7d8003e22..1692e684a6 100755 --- a/apm-sniffer/bootstrap-plugins/jdk-http-plugin/pom.xml +++ b/apm-sniffer/bootstrap-plugins/jdk-http-plugin/pom.xml @@ -20,7 +20,7 @@ org.apache.skywalking bootstrap-plugins - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 @@ -32,6 +32,8 @@ UTF-8 + + diff --git a/apm-sniffer/bootstrap-plugins/jdk-httpclient-plugin/pom.xml b/apm-sniffer/bootstrap-plugins/jdk-httpclient-plugin/pom.xml new file mode 100644 index 0000000000..f537f685e6 --- /dev/null +++ b/apm-sniffer/bootstrap-plugins/jdk-httpclient-plugin/pom.xml @@ -0,0 +1,46 @@ + + + + + bootstrap-plugins + org.apache.skywalking + 9.7.0-SNAPSHOT + + 4.0.0 + + apm-jdk-httpclient-plugin + jar + + apm-jdk-httpclient-plugin + http://maven.apache.org + + + UTF-8 + 11 + + + + + + maven-deploy-plugin + + + + + \ No newline at end of file diff --git a/apm-sniffer/bootstrap-plugins/jdk-httpclient-plugin/src/main/java/org/apache/skywalking/apm/plugin/HttpClientSendAsyncInterceptor.java b/apm-sniffer/bootstrap-plugins/jdk-httpclient-plugin/src/main/java/org/apache/skywalking/apm/plugin/HttpClientSendAsyncInterceptor.java new file mode 100644 index 0000000000..0661c4705f --- /dev/null +++ b/apm-sniffer/bootstrap-plugins/jdk-httpclient-plugin/src/main/java/org/apache/skywalking/apm/plugin/HttpClientSendAsyncInterceptor.java @@ -0,0 +1,123 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin; + +import org.apache.skywalking.apm.agent.core.context.ContextCarrier; +import org.apache.skywalking.apm.agent.core.context.ContextManager; +import org.apache.skywalking.apm.agent.core.context.tag.Tags; +import org.apache.skywalking.apm.agent.core.context.trace.AbstractSpan; +import org.apache.skywalking.apm.agent.core.context.trace.SpanLayer; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceMethodsAroundInterceptor; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult; +import org.apache.skywalking.apm.network.trace.component.ComponentsDefine; + +import java.lang.reflect.Method; +import java.net.URI; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +public class HttpClientSendAsyncInterceptor implements InstanceMethodsAroundInterceptor { + + @Override + public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, MethodInterceptResult result) throws Throwable { + HttpRequest request = (HttpRequest) allArguments[0]; + URI uri = request.uri(); + + ContextCarrier contextCarrier = new ContextCarrier(); + AbstractSpan span = ContextManager.createExitSpan(buildOperationName(request.method(), uri), contextCarrier, getPeer(uri)); + + if (request instanceof EnhancedInstance) { + ((EnhancedInstance) request).setSkyWalkingDynamicField(contextCarrier); + } + + span.setComponent(ComponentsDefine.JDK_HTTP); + Tags.HTTP.METHOD.set(span, request.method()); + Tags.URL.set(span, String.valueOf(uri)); + SpanLayer.asHttp(span); + } + + @Override + public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, Object ret) throws Throwable { + + if (ContextManager.isActive()) { + AbstractSpan span = ContextManager.activeSpan(); + span.prepareForAsync(); + + if (ret != null) { + CompletableFuture future = (CompletableFuture) ret; + ret = future.whenComplete((response, throwable) -> { + try { + if (throwable != null) { + span.errorOccurred(); + span.log(throwable); + return; + } + if (response instanceof HttpResponse) { + HttpResponse httpResponse = (HttpResponse) response; + int statusCode = httpResponse.statusCode(); + Tags.HTTP_RESPONSE_STATUS_CODE.set(span, statusCode); + if (statusCode >= 400) { + span.errorOccurred(); + } + } + } finally { + span.asyncFinish(); + } + }); + } else { + Map eventMap = new HashMap(); + eventMap.put("error", "No response"); + span.log(System.currentTimeMillis(), eventMap); + span.errorOccurred(); + } + ContextManager.stopSpan(); + } + return ret; + } + + @Override + public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, Throwable t) { + if (ContextManager.isActive()) { + ContextManager.activeSpan().log(t); + } + } + + private String buildOperationName(String method, URI uri) { + String path = uri.getPath(); + if (path == null || path.isEmpty()) { + path = "/"; + } + return method + ":" + path; + } + + private String getPeer(URI uri) { + String host = uri.getHost(); + int port = uri.getPort(); + + if (port == -1) { + port = "https".equalsIgnoreCase(uri.getScheme()) ? 443 : 80; + } + + return host + ":" + port; + } +} diff --git a/apm-sniffer/bootstrap-plugins/jdk-httpclient-plugin/src/main/java/org/apache/skywalking/apm/plugin/HttpClientSendInterceptor.java b/apm-sniffer/bootstrap-plugins/jdk-httpclient-plugin/src/main/java/org/apache/skywalking/apm/plugin/HttpClientSendInterceptor.java new file mode 100644 index 0000000000..3a06f59ad9 --- /dev/null +++ b/apm-sniffer/bootstrap-plugins/jdk-httpclient-plugin/src/main/java/org/apache/skywalking/apm/plugin/HttpClientSendInterceptor.java @@ -0,0 +1,108 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin; + +import org.apache.skywalking.apm.agent.core.context.ContextCarrier; +import org.apache.skywalking.apm.agent.core.context.ContextManager; +import org.apache.skywalking.apm.agent.core.context.tag.Tags; +import org.apache.skywalking.apm.agent.core.context.trace.AbstractSpan; +import org.apache.skywalking.apm.agent.core.context.trace.SpanLayer; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceMethodsAroundInterceptor; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult; +import org.apache.skywalking.apm.network.trace.component.ComponentsDefine; + +import java.lang.reflect.Method; +import java.net.URI; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.util.HashMap; +import java.util.Map; + +public class HttpClientSendInterceptor implements InstanceMethodsAroundInterceptor { + + @Override + public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, MethodInterceptResult result) throws Throwable { + HttpRequest request = (HttpRequest) allArguments[0]; + URI uri = request.uri(); + + ContextCarrier contextCarrier = new ContextCarrier(); + AbstractSpan span = ContextManager.createExitSpan(buildOperationName(request.method(), uri), contextCarrier, getPeer(uri)); + + if (request instanceof EnhancedInstance) { + ((EnhancedInstance) request).setSkyWalkingDynamicField(contextCarrier); + } + + span.setComponent(ComponentsDefine.JDK_HTTP); + Tags.HTTP.METHOD.set(span, request.method()); + Tags.URL.set(span, String.valueOf(uri)); + SpanLayer.asHttp(span); + } + + @Override + public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, Object ret) throws Throwable { + + if (ContextManager.isActive()) { + AbstractSpan span = ContextManager.activeSpan(); + if (ret != null) { + HttpResponse response = (HttpResponse) ret; + int statusCode = response.statusCode(); + + Tags.HTTP_RESPONSE_STATUS_CODE.set(span, response.statusCode()); + if (statusCode >= 400) { + span.errorOccurred(); + } + } else { + Map eventMap = new HashMap(); + eventMap.put("error", "No response"); + span.log(System.currentTimeMillis(), eventMap); + span.errorOccurred(); + } + + ContextManager.stopSpan(); + } + return ret; + } + + @Override + public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, Throwable t) { + if (ContextManager.isActive()) { + ContextManager.activeSpan().log(t); + } + } + + private String buildOperationName(String method, URI uri) { + String path = uri.getPath(); + if (path == null || path.isEmpty()) { + path = "/"; + } + return method + ":" + path; + } + + private String getPeer(URI uri) { + String host = uri.getHost(); + int port = uri.getPort(); + + if (port == -1) { + port = "https".equalsIgnoreCase(uri.getScheme()) ? 443 : 80; + } + + return host + ":" + port; + } +} diff --git a/apm-sniffer/bootstrap-plugins/jdk-httpclient-plugin/src/main/java/org/apache/skywalking/apm/plugin/HttpRequestHeadersInterceptor.java b/apm-sniffer/bootstrap-plugins/jdk-httpclient-plugin/src/main/java/org/apache/skywalking/apm/plugin/HttpRequestHeadersInterceptor.java new file mode 100644 index 0000000000..a6dc76650f --- /dev/null +++ b/apm-sniffer/bootstrap-plugins/jdk-httpclient-plugin/src/main/java/org/apache/skywalking/apm/plugin/HttpRequestHeadersInterceptor.java @@ -0,0 +1,62 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin; + +import org.apache.skywalking.apm.agent.core.context.CarrierItem; +import org.apache.skywalking.apm.agent.core.context.ContextCarrier; +import org.apache.skywalking.apm.agent.core.context.ContextManager; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceMethodsAroundInterceptor; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult; + +import java.lang.reflect.Method; +import java.net.http.HttpHeaders; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class HttpRequestHeadersInterceptor implements InstanceMethodsAroundInterceptor { + + @Override + public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, MethodInterceptResult result) throws Throwable { + + } + + @Override + public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, Object ret) throws Throwable { + + ContextCarrier contextCarrier = (ContextCarrier) objInst.getSkyWalkingDynamicField(); + HttpHeaders originalHeaders = (HttpHeaders) ret; + final Map> headerMap = new HashMap<>(originalHeaders.map()); + CarrierItem next = contextCarrier.items(); + while (next.hasNext()) { + next = next.next(); + headerMap.put(next.getHeadKey(), List.of(next.getHeadValue())); + } + + return HttpHeaders.of(headerMap, (k, v) -> true); + } + + @Override + public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, Throwable t) { + if (ContextManager.isActive()) { + ContextManager.activeSpan().log(t); + } + } +} diff --git a/apm-sniffer/bootstrap-plugins/jdk-httpclient-plugin/src/main/java/org/apache/skywalking/apm/plugin/define/HttpClientInstrumentation.java b/apm-sniffer/bootstrap-plugins/jdk-httpclient-plugin/src/main/java/org/apache/skywalking/apm/plugin/define/HttpClientInstrumentation.java new file mode 100644 index 0000000000..18f7cf298b --- /dev/null +++ b/apm-sniffer/bootstrap-plugins/jdk-httpclient-plugin/src/main/java/org/apache/skywalking/apm/plugin/define/HttpClientInstrumentation.java @@ -0,0 +1,109 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.define; + +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.matcher.ElementMatcher; +import net.bytebuddy.matcher.ElementMatchers; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine; +import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch; +import org.apache.skywalking.apm.agent.core.plugin.match.HierarchyMatch; +import org.apache.skywalking.apm.agent.core.plugin.match.IndirectMatch; +import org.apache.skywalking.apm.agent.core.plugin.match.MultiClassNameMatch; +import org.apache.skywalking.apm.agent.core.plugin.match.logical.LogicalMatchOperation; + +import static net.bytebuddy.matcher.ElementMatchers.named; + +public class HttpClientInstrumentation extends ClassInstanceMethodsEnhancePluginDefine { + + private static final String ENHANCE_PARENT_CLASS = "java.net.http.HttpClient"; + + private static final String EXCLUDE_CLASS = "jdk.internal.net.http.HttpClientFacade"; + + private static final String INTERCEPT_SEND_METHOD = "send"; + + private static final String INTERCEPT_SEND_ASYNC_METHOD = "sendAsync"; + + private static final String INTERCEPT_SEND_HANDLE = "org.apache.skywalking.apm.plugin.HttpClientSendInterceptor"; + + private static final String INTERCEPT_SEND_ASYNC_HANDLE = "org.apache.skywalking.apm.plugin.HttpClientSendAsyncInterceptor"; + + @Override + public boolean isBootstrapInstrumentation() { + return true; + } + + @Override + public ConstructorInterceptPoint[] getConstructorsInterceptPoints() { + return new ConstructorInterceptPoint[0]; + } + + @Override + protected ClassMatch enhanceClass() { + IndirectMatch parentType = HierarchyMatch.byHierarchyMatch(ENHANCE_PARENT_CLASS); + IndirectMatch excludeClass = LogicalMatchOperation.not(MultiClassNameMatch.byMultiClassMatch(EXCLUDE_CLASS)); + return LogicalMatchOperation.and(parentType, excludeClass); + } + + @Override + public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() { + return new InstanceMethodsInterceptPoint[]{ + new InstanceMethodsInterceptPoint() { + @Override + public ElementMatcher getMethodsMatcher() { + return ElementMatchers.named(INTERCEPT_SEND_METHOD) + .and(ElementMatchers.takesArgument(0, named("java.net.http.HttpRequest"))) + .and(ElementMatchers.takesArguments(2)); + } + + @Override + public String getMethodsInterceptor() { + return INTERCEPT_SEND_HANDLE; + } + + @Override + public boolean isOverrideArgs() { + return false; + } + }, + new InstanceMethodsInterceptPoint() { + @Override + public ElementMatcher getMethodsMatcher() { + return ElementMatchers.named(INTERCEPT_SEND_ASYNC_METHOD) + .and(ElementMatchers.takesArgument(0, named("java.net.http.HttpRequest"))) + .and(ElementMatchers.takesArgument(1, named("java.net.http.HttpResponse$BodyHandler"))) + .and(ElementMatchers.takesArgument(2, named("java.net.http.HttpResponse$PushPromiseHandler"))) + .and(ElementMatchers.takesArguments(3)); + } + + @Override + public String getMethodsInterceptor() { + return INTERCEPT_SEND_ASYNC_HANDLE; + } + + @Override + public boolean isOverrideArgs() { + return false; + } + }, + }; + } +} diff --git a/apm-sniffer/bootstrap-plugins/jdk-httpclient-plugin/src/main/java/org/apache/skywalking/apm/plugin/define/HttpRequestInstrumentation.java b/apm-sniffer/bootstrap-plugins/jdk-httpclient-plugin/src/main/java/org/apache/skywalking/apm/plugin/define/HttpRequestInstrumentation.java new file mode 100644 index 0000000000..18d47b6923 --- /dev/null +++ b/apm-sniffer/bootstrap-plugins/jdk-httpclient-plugin/src/main/java/org/apache/skywalking/apm/plugin/define/HttpRequestInstrumentation.java @@ -0,0 +1,75 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.define; + +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.matcher.ElementMatcher; +import net.bytebuddy.matcher.ElementMatchers; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine; +import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch; + +import static org.apache.skywalking.apm.agent.core.plugin.match.NameMatch.byName; + +public class HttpRequestInstrumentation extends ClassInstanceMethodsEnhancePluginDefine { + + private static final String ENHANCE_CLASS = "jdk.internal.net.http.ImmutableHttpRequest"; + + private static final String INTERCEPT_HEADERS_METHOD = "headers"; + + private static final String INTERCEPT_HEADERS_HANDLE = "org.apache.skywalking.apm.plugin.HttpRequestHeadersInterceptor"; + + @Override + public boolean isBootstrapInstrumentation() { + return true; + } + + @Override + public ConstructorInterceptPoint[] getConstructorsInterceptPoints() { + return new ConstructorInterceptPoint[0]; + } + + @Override + protected ClassMatch enhanceClass() { + return byName(ENHANCE_CLASS); + } + + @Override + public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() { + return new InstanceMethodsInterceptPoint[]{ + new InstanceMethodsInterceptPoint() { + @Override + public ElementMatcher getMethodsMatcher() { + return ElementMatchers.named(INTERCEPT_HEADERS_METHOD); + } + + @Override + public String getMethodsInterceptor() { + return INTERCEPT_HEADERS_HANDLE; + } + + @Override + public boolean isOverrideArgs() { + return true; + } + } + }; + } +} diff --git a/apm-sniffer/bootstrap-plugins/jdk-httpclient-plugin/src/main/resources/skywalking-plugin.def b/apm-sniffer/bootstrap-plugins/jdk-httpclient-plugin/src/main/resources/skywalking-plugin.def new file mode 100644 index 0000000000..e4d1ef2562 --- /dev/null +++ b/apm-sniffer/bootstrap-plugins/jdk-httpclient-plugin/src/main/resources/skywalking-plugin.def @@ -0,0 +1,18 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +jdk-httpclient-plugin=org.apache.skywalking.apm.plugin.define.HttpClientInstrumentation +jdk-httpclient-plugin=org.apache.skywalking.apm.plugin.define.HttpRequestInstrumentation \ No newline at end of file diff --git a/apm-sniffer/bootstrap-plugins/jdk-threading-plugin/pom.xml b/apm-sniffer/bootstrap-plugins/jdk-threading-plugin/pom.xml index 7cae75e919..33dd7a3b4d 100755 --- a/apm-sniffer/bootstrap-plugins/jdk-threading-plugin/pom.xml +++ b/apm-sniffer/bootstrap-plugins/jdk-threading-plugin/pom.xml @@ -20,7 +20,7 @@ org.apache.skywalking bootstrap-plugins - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/bootstrap-plugins/jdk-threadpool-plugin/pom.xml b/apm-sniffer/bootstrap-plugins/jdk-threadpool-plugin/pom.xml index 26b48ac5d9..3da184c381 100644 --- a/apm-sniffer/bootstrap-plugins/jdk-threadpool-plugin/pom.xml +++ b/apm-sniffer/bootstrap-plugins/jdk-threadpool-plugin/pom.xml @@ -20,7 +20,7 @@ bootstrap-plugins org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/bootstrap-plugins/jdk-threadpool-plugin/src/main/java/org/apache/skywalking/apm/plugin/AbstractThreadingPoolInterceptor.java b/apm-sniffer/bootstrap-plugins/jdk-threadpool-plugin/src/main/java/org/apache/skywalking/apm/plugin/AbstractThreadingPoolInterceptor.java index 7239453074..152064960b 100644 --- a/apm-sniffer/bootstrap-plugins/jdk-threadpool-plugin/src/main/java/org/apache/skywalking/apm/plugin/AbstractThreadingPoolInterceptor.java +++ b/apm-sniffer/bootstrap-plugins/jdk-threadpool-plugin/src/main/java/org/apache/skywalking/apm/plugin/AbstractThreadingPoolInterceptor.java @@ -24,26 +24,16 @@ import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceMethodsAroundInterceptor; import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult; import java.lang.reflect.Method; +import java.util.concurrent.ForkJoinTask; public abstract class AbstractThreadingPoolInterceptor implements InstanceMethodsAroundInterceptor { @Override public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, MethodInterceptResult result) throws Throwable { - if (!ContextManager.isActive()) { + if (notToEnhance(allArguments)) { return; } - if (allArguments == null || allArguments.length < 1) { - return; - } - - Object argument = allArguments[0]; - - // Avoid duplicate enhancement, such as the case where it has already been enhanced by RunnableWrapper or CallableWrapper with toolkit. - if (argument instanceof EnhancedInstance && ((EnhancedInstance) argument).getSkyWalkingDynamicField() instanceof ContextSnapshot) { - return; - } - - Object wrappedObject = wrap(argument); + Object wrappedObject = wrap(allArguments[0]); if (wrappedObject != null) { allArguments[0] = wrappedObject; } @@ -63,6 +53,27 @@ public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allA @Override public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, Throwable t) { + if (notToEnhance(allArguments)) { + return; + } + ContextManager.activeSpan().log(t); } + + private boolean notToEnhance(Object[] allArguments) { + if (!ContextManager.isActive()) { + return true; + } + + if (allArguments == null || allArguments.length < 1) { + return true; + } + + Object argument = allArguments[0]; + + // Avoid duplicate enhancement, such as the case where it has already been enhanced by RunnableWrapper or CallableWrapper with toolkit. + return argument instanceof EnhancedInstance + && ((EnhancedInstance) argument).getSkyWalkingDynamicField() instanceof ContextSnapshot + && !(argument instanceof ForkJoinTask); + } } diff --git a/apm-sniffer/bootstrap-plugins/jdk-virtual-thread-executor-plugin/pom.xml b/apm-sniffer/bootstrap-plugins/jdk-virtual-thread-executor-plugin/pom.xml new file mode 100644 index 0000000000..9a3eee820f --- /dev/null +++ b/apm-sniffer/bootstrap-plugins/jdk-virtual-thread-executor-plugin/pom.xml @@ -0,0 +1,46 @@ + + + + + bootstrap-plugins + org.apache.skywalking + 9.7.0-SNAPSHOT + + 4.0.0 + + apm-jdk-virtual-thread-executor-plugin + jar + + apm-jdk-virtual-thread-executor-plugin + http://maven.apache.org + + + UTF-8 + + + + + + + maven-deploy-plugin + + + + + \ No newline at end of file diff --git a/apm-sniffer/bootstrap-plugins/jdk-virtual-thread-executor-plugin/src/main/java/org/apache/skywalking/apm/plugin/ThreadPerTaskExecutorConstructInterceptor.java b/apm-sniffer/bootstrap-plugins/jdk-virtual-thread-executor-plugin/src/main/java/org/apache/skywalking/apm/plugin/ThreadPerTaskExecutorConstructInterceptor.java new file mode 100644 index 0000000000..c8f18193f9 --- /dev/null +++ b/apm-sniffer/bootstrap-plugins/jdk-virtual-thread-executor-plugin/src/main/java/org/apache/skywalking/apm/plugin/ThreadPerTaskExecutorConstructInterceptor.java @@ -0,0 +1,33 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin; + +import org.apache.skywalking.apm.agent.core.context.ContextManager; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceConstructorInterceptor; + +public class ThreadPerTaskExecutorConstructInterceptor implements InstanceConstructorInterceptor { + + @Override + public void onConstruct(EnhancedInstance objInst, Object[] allArguments) throws Throwable { + if (ContextManager.isActive()) { + objInst.setSkyWalkingDynamicField(ContextManager.capture()); + } + } +} diff --git a/apm-sniffer/bootstrap-plugins/jdk-virtual-thread-executor-plugin/src/main/java/org/apache/skywalking/apm/plugin/ThreadPerTaskExecutorRunInterceptor.java b/apm-sniffer/bootstrap-plugins/jdk-virtual-thread-executor-plugin/src/main/java/org/apache/skywalking/apm/plugin/ThreadPerTaskExecutorRunInterceptor.java new file mode 100644 index 0000000000..9cd9289116 --- /dev/null +++ b/apm-sniffer/bootstrap-plugins/jdk-virtual-thread-executor-plugin/src/main/java/org/apache/skywalking/apm/plugin/ThreadPerTaskExecutorRunInterceptor.java @@ -0,0 +1,81 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin; + +import org.apache.skywalking.apm.agent.core.context.ContextManager; +import org.apache.skywalking.apm.agent.core.context.ContextSnapshot; +import org.apache.skywalking.apm.agent.core.context.tag.Tags; +import org.apache.skywalking.apm.agent.core.context.trace.AbstractSpan; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceMethodsAroundInterceptor; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult; +import org.apache.skywalking.apm.network.trace.component.ComponentsDefine; + +import java.lang.reflect.Method; + +public class ThreadPerTaskExecutorRunInterceptor implements InstanceMethodsAroundInterceptor { + + @Override + public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, MethodInterceptResult result) throws Throwable { + Object skyWalkingDynamicField = objInst.getSkyWalkingDynamicField(); + if (skyWalkingDynamicField == null) { + return; + } + + if (!(skyWalkingDynamicField instanceof ContextSnapshot)) { + return; + } + + AbstractSpan span = ContextManager.createLocalSpan(getOperationName(objInst, method)); + span.setComponent(ComponentsDefine.THREAD_PER_TASK_EXECUTOR); + setCarrierThread(span); + + ContextSnapshot contextSnapshot = (ContextSnapshot) skyWalkingDynamicField; + ContextManager.continued(contextSnapshot); + } + + @Override + public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, Object ret) throws Throwable { + if (ContextManager.isActive()) { + ContextManager.stopSpan(); + } + return ret; + } + + @Override + public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, Throwable t) { + if (ContextManager.isActive()) { + ContextManager.activeSpan().log(t); + } + } + + private String getOperationName(EnhancedInstance objInst, Method method) { + return objInst.getClass().getName() + "." + method.getName(); + } + + private void setCarrierThread(AbstractSpan span) { + String threadInfo = Thread.currentThread().toString(); + if (threadInfo.startsWith("VirtualThread")) { + String[] parts = threadInfo.split("@"); + if (parts.length >= 1) { + Tags.THREAD_CARRIER.set(span, parts[1]); + } + } + } +} diff --git a/apm-sniffer/bootstrap-plugins/jdk-virtual-thread-executor-plugin/src/main/java/org/apache/skywalking/apm/plugin/define/ThreadPerTaskExecutorFutureInstrumentation.java b/apm-sniffer/bootstrap-plugins/jdk-virtual-thread-executor-plugin/src/main/java/org/apache/skywalking/apm/plugin/define/ThreadPerTaskExecutorFutureInstrumentation.java new file mode 100644 index 0000000000..3ed5788a1e --- /dev/null +++ b/apm-sniffer/bootstrap-plugins/jdk-virtual-thread-executor-plugin/src/main/java/org/apache/skywalking/apm/plugin/define/ThreadPerTaskExecutorFutureInstrumentation.java @@ -0,0 +1,90 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.define; + +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.matcher.ElementMatcher; +import net.bytebuddy.matcher.ElementMatchers; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine; +import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch; +import org.apache.skywalking.apm.agent.core.plugin.match.MultiClassNameMatch; + +import static net.bytebuddy.matcher.ElementMatchers.any; + +public class ThreadPerTaskExecutorFutureInstrumentation extends ClassInstanceMethodsEnhancePluginDefine { + + private static final String ENHANCE_CLASS = "java.util.concurrent.ThreadPerTaskExecutor$ThreadBoundFuture"; + + private static final String INTERCEPT_RUN_METHOD = "run"; + + private static final String INTERCEPT_RUN_HANDLE = "org.apache.skywalking.apm.plugin.ThreadPerTaskExecutorRunInterceptor"; + + private static final String TASK_RUNNER_CONSTRUCT_METHOD_INTERCEPTOR = "org.apache.skywalking.apm.plugin.ThreadPerTaskExecutorConstructInterceptor"; + + @Override + public boolean isBootstrapInstrumentation() { + return true; + } + + @Override + protected ClassMatch enhanceClass() { + return MultiClassNameMatch.byMultiClassMatch(ENHANCE_CLASS); + } + + @Override + public ConstructorInterceptPoint[] getConstructorsInterceptPoints() { + return new ConstructorInterceptPoint[]{ + new ConstructorInterceptPoint() { + @Override + public ElementMatcher getConstructorMatcher() { + return any(); + } + + @Override + public String getConstructorInterceptor() { + return TASK_RUNNER_CONSTRUCT_METHOD_INTERCEPTOR; + } + } + }; + } + + @Override + public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() { + return new InstanceMethodsInterceptPoint[]{ + new InstanceMethodsInterceptPoint() { + @Override + public ElementMatcher getMethodsMatcher() { + return ElementMatchers.named(INTERCEPT_RUN_METHOD); + } + + @Override + public String getMethodsInterceptor() { + return INTERCEPT_RUN_HANDLE; + } + + @Override + public boolean isOverrideArgs() { + return true; + } + } + }; + } +} diff --git a/apm-sniffer/bootstrap-plugins/jdk-virtual-thread-executor-plugin/src/main/java/org/apache/skywalking/apm/plugin/define/ThreadPerTaskExecutorTaskRunnerInstrumentation.java b/apm-sniffer/bootstrap-plugins/jdk-virtual-thread-executor-plugin/src/main/java/org/apache/skywalking/apm/plugin/define/ThreadPerTaskExecutorTaskRunnerInstrumentation.java new file mode 100644 index 0000000000..9a0efd075f --- /dev/null +++ b/apm-sniffer/bootstrap-plugins/jdk-virtual-thread-executor-plugin/src/main/java/org/apache/skywalking/apm/plugin/define/ThreadPerTaskExecutorTaskRunnerInstrumentation.java @@ -0,0 +1,90 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.define; + +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.matcher.ElementMatcher; +import net.bytebuddy.matcher.ElementMatchers; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine; +import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch; +import org.apache.skywalking.apm.agent.core.plugin.match.MultiClassNameMatch; + +import static net.bytebuddy.matcher.ElementMatchers.any; + +public class ThreadPerTaskExecutorTaskRunnerInstrumentation extends ClassInstanceMethodsEnhancePluginDefine { + + private static final String ENHANCE_CLASS = "java.util.concurrent.ThreadPerTaskExecutor$TaskRunner"; + + private static final String INTERCEPT_RUN_METHOD = "run"; + + private static final String INTERCEPT_RUN_HANDLE = "org.apache.skywalking.apm.plugin.ThreadPerTaskExecutorRunInterceptor"; + + private static final String TASK_RUNNER_CONSTRUCT_METHOD_INTERCEPTOR = "org.apache.skywalking.apm.plugin.ThreadPerTaskExecutorConstructInterceptor"; + + @Override + public boolean isBootstrapInstrumentation() { + return true; + } + + @Override + protected ClassMatch enhanceClass() { + return MultiClassNameMatch.byMultiClassMatch(ENHANCE_CLASS); + } + + @Override + public ConstructorInterceptPoint[] getConstructorsInterceptPoints() { + return new ConstructorInterceptPoint[]{ + new ConstructorInterceptPoint() { + @Override + public ElementMatcher getConstructorMatcher() { + return any(); + } + + @Override + public String getConstructorInterceptor() { + return TASK_RUNNER_CONSTRUCT_METHOD_INTERCEPTOR; + } + } + }; + } + + @Override + public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() { + return new InstanceMethodsInterceptPoint[]{ + new InstanceMethodsInterceptPoint() { + @Override + public ElementMatcher getMethodsMatcher() { + return ElementMatchers.named(INTERCEPT_RUN_METHOD); + } + + @Override + public String getMethodsInterceptor() { + return INTERCEPT_RUN_HANDLE; + } + + @Override + public boolean isOverrideArgs() { + return true; + } + } + }; + } +} diff --git a/apm-sniffer/bootstrap-plugins/jdk-virtual-thread-executor-plugin/src/main/resources/skywalking-plugin.def b/apm-sniffer/bootstrap-plugins/jdk-virtual-thread-executor-plugin/src/main/resources/skywalking-plugin.def new file mode 100644 index 0000000000..3ab6a970a9 --- /dev/null +++ b/apm-sniffer/bootstrap-plugins/jdk-virtual-thread-executor-plugin/src/main/resources/skywalking-plugin.def @@ -0,0 +1,18 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +jdk-virtual-thread-executor-plugin=org.apache.skywalking.apm.plugin.define.ThreadPerTaskExecutorTaskRunnerInstrumentation +jdk-virtual-thread-executor-plugin=org.apache.skywalking.apm.plugin.define.ThreadPerTaskExecutorFutureInstrumentation \ No newline at end of file diff --git a/apm-sniffer/bootstrap-plugins/pom.xml b/apm-sniffer/bootstrap-plugins/pom.xml index 8601027091..fe3bf24930 100644 --- a/apm-sniffer/bootstrap-plugins/pom.xml +++ b/apm-sniffer/bootstrap-plugins/pom.xml @@ -21,7 +21,7 @@ java-agent-sniffer org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 @@ -32,10 +32,7 @@ ${shade.package}.${shade.net.bytebuddy.source} UTF-8 - - ${project.build.directory}${sdk.plugin.related.dir}/../../../../skywalking-agent - - ${agent.package.dest.dir}/bootstrap-plugins + ${maven.multiModuleProjectDirectory}/skywalking-agent/bootstrap-plugins 1.0b3 1.8.1 @@ -46,6 +43,8 @@ jdk-threading-plugin jdk-threadpool-plugin jdk-forkjoinpool-plugin + jdk-virtual-thread-executor-plugin + jdk-httpclient-plugin diff --git a/apm-sniffer/bytebuddy-patch/pom.xml b/apm-sniffer/bytebuddy-patch/pom.xml index 4cc94f81d8..460631dfc3 100644 --- a/apm-sniffer/bytebuddy-patch/pom.xml +++ b/apm-sniffer/bytebuddy-patch/pom.xml @@ -21,7 +21,7 @@ org.apache.skywalking java-agent-sniffer - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/bytebuddy-patch/src/main/java/net/bytebuddy/agent/builder/SWDescriptionStrategy.java b/apm-sniffer/bytebuddy-patch/src/main/java/net/bytebuddy/agent/builder/SWDescriptionStrategy.java index 62e8a0606c..0d10a039bb 100644 --- a/apm-sniffer/bytebuddy-patch/src/main/java/net/bytebuddy/agent/builder/SWDescriptionStrategy.java +++ b/apm-sniffer/bytebuddy-patch/src/main/java/net/bytebuddy/agent/builder/SWDescriptionStrategy.java @@ -34,10 +34,12 @@ import java.io.Serializable; import java.lang.annotation.Annotation; import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.WeakHashMap; import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Collectors; @@ -125,9 +127,16 @@ static class SWTypeDescriptionWrapper extends TypeDescription.AbstractBase imple /** * Original type cache. - * classloader hashcode -> ( typeName -> type cache ) + * ClassLoader -> (typeName -> TypeCache) + * Using WeakHashMap to automatically clean up cache when ClassLoader is garbage collected. */ - private static final Map> CLASS_LOADER_TYPE_CACHE = new ConcurrentHashMap<>(); + private static final Map> CLASS_LOADER_TYPE_CACHE = + Collections.synchronizedMap(new WeakHashMap<>()); + + /** + * Bootstrap ClassLoader cache (null key cannot be stored in WeakHashMap) + */ + private static final Map BOOTSTRAP_TYPE_CACHE = new ConcurrentHashMap<>(); private static final List IGNORED_INTERFACES = Arrays.asList(EnhancedInstance.class.getName()); @@ -153,10 +162,15 @@ public SWTypeDescriptionWrapper(TypeDescription delegate, String nameTrait, Clas } private TypeCache getTypeCache() { - int classLoaderHashCode = classLoader != null ? classLoader.hashCode() : 0; - Map typeCacheMap = CLASS_LOADER_TYPE_CACHE.computeIfAbsent(classLoaderHashCode, k -> new ConcurrentHashMap<>()); - TypeCache typeCache = typeCacheMap.computeIfAbsent(typeName, k -> new TypeCache(typeName)); - return typeCache; + if (classLoader == null) { + // Bootstrap ClassLoader - use separate cache since null key cannot be stored in WeakHashMap + return BOOTSTRAP_TYPE_CACHE.computeIfAbsent(typeName, k -> new TypeCache(typeName)); + } else { + // Regular ClassLoader - use WeakHashMap for automatic cleanup + Map typeCacheMap = CLASS_LOADER_TYPE_CACHE.computeIfAbsent( + classLoader, k -> new ConcurrentHashMap<>()); + return typeCacheMap.computeIfAbsent(typeName, k -> new TypeCache(typeName)); + } } @Override diff --git a/apm-sniffer/bytebuddy-patch/src/test/java/net/bytebuddy/agent/builder/SWDescriptionStrategyCacheTest.java b/apm-sniffer/bytebuddy-patch/src/test/java/net/bytebuddy/agent/builder/SWDescriptionStrategyCacheTest.java new file mode 100644 index 0000000000..90f9e4a437 --- /dev/null +++ b/apm-sniffer/bytebuddy-patch/src/test/java/net/bytebuddy/agent/builder/SWDescriptionStrategyCacheTest.java @@ -0,0 +1,239 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package net.bytebuddy.agent.builder; + +import org.junit.Test; +import org.junit.Assert; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.net.URLClassLoader; +import java.net.URL; +import java.util.Map; + +/** + * Tests the behavior of the WeakHashMap caching mechanism in SWDescriptionStrategy. + */ +public class SWDescriptionStrategyCacheTest { + + @Test + public void testWeakHashMapCacheCleanup() throws Exception { + // Get static cache field + Field cacheField = SWDescriptionStrategy.SWTypeDescriptionWrapper.class + .getDeclaredField("CLASS_LOADER_TYPE_CACHE"); + cacheField.setAccessible(true); + @SuppressWarnings("unchecked") + Map> cache = + (Map>) cacheField.get(null); + + // Record initial cache size + int initialCacheSize = cache.size(); + + // Create test ClassLoader + URLClassLoader testClassLoader = new URLClassLoader(new URL[0], null); + String testTypeName = "com.test.DynamicClass"; + + // Create SWTypeDescriptionWrapper instance + SWDescriptionStrategy.SWTypeDescriptionWrapper wrapper = + new SWDescriptionStrategy.SWTypeDescriptionWrapper( + null, "test", testClassLoader, testTypeName); + + // Call getTypeCache method via reflection to trigger caching + Method getTypeCacheMethod = wrapper.getClass() + .getDeclaredMethod("getTypeCache"); + getTypeCacheMethod.setAccessible(true); + SWDescriptionStrategy.TypeCache typeCache = + (SWDescriptionStrategy.TypeCache) getTypeCacheMethod.invoke(wrapper); + + // Verify that the ClassLoader exists in cache + Assert.assertTrue("Cache should contain the test ClassLoader", + cache.containsKey(testClassLoader)); + Assert.assertNotNull("TypeCache should be created", typeCache); + Assert.assertEquals("Cache size should increase by 1", + initialCacheSize + 1, cache.size()); + + // Clear ClassLoader references, prepare for GC test + testClassLoader = null; + wrapper = null; + typeCache = null; + + // Force garbage collection + System.gc(); + Thread.sleep(100); + System.gc(); + Thread.sleep(100); + + // WeakHashMap should automatically clean up garbage collected ClassLoader entries + int attempts = 0; + int currentCacheSize = cache.size(); + while (currentCacheSize > initialCacheSize && attempts < 20) { + System.gc(); + Thread.sleep(50); + currentCacheSize = cache.size(); + attempts++; + } + + System.out.println("Cache size after GC: " + currentCacheSize + + " (initial: " + initialCacheSize + ", attempts: " + attempts + ")"); + + // Verify that WeakHashMap cleanup mechanism works properly + Assert.assertTrue("WeakHashMap should clean up entries or attempts should be reasonable", + currentCacheSize <= initialCacheSize + 1 && attempts < 20); + } + + @Test + public void testBootstrapClassLoaderHandling() throws Exception { + // Get Bootstrap ClassLoader cache field + Field bootstrapCacheField = SWDescriptionStrategy.SWTypeDescriptionWrapper.class + .getDeclaredField("BOOTSTRAP_TYPE_CACHE"); + bootstrapCacheField.setAccessible(true); + @SuppressWarnings("unchecked") + Map bootstrapCache = + (Map) bootstrapCacheField.get(null); + + int initialBootstrapCacheSize = bootstrapCache.size(); + + // Test Bootstrap ClassLoader (null) handling + String testTypeName = "test.BootstrapClass"; + SWDescriptionStrategy.SWTypeDescriptionWrapper wrapper = + new SWDescriptionStrategy.SWTypeDescriptionWrapper( + null, "test", null, testTypeName); + + // Call getTypeCache method via reflection + Method getTypeCacheMethod = wrapper.getClass() + .getDeclaredMethod("getTypeCache"); + getTypeCacheMethod.setAccessible(true); + SWDescriptionStrategy.TypeCache typeCache = + (SWDescriptionStrategy.TypeCache) getTypeCacheMethod.invoke(wrapper); + + // Verify Bootstrap ClassLoader cache handling + Assert.assertNotNull("TypeCache should be created for bootstrap classloader", typeCache); + Assert.assertTrue("Bootstrap cache should contain the type", + bootstrapCache.containsKey(testTypeName)); + Assert.assertEquals("Bootstrap cache size should increase by 1", + initialBootstrapCacheSize + 1, bootstrapCache.size()); + } + + @Test + public void testMultipleClassLoadersIndependentCache() throws Exception { + Field cacheField = SWDescriptionStrategy.SWTypeDescriptionWrapper.class + .getDeclaredField("CLASS_LOADER_TYPE_CACHE"); + cacheField.setAccessible(true); + @SuppressWarnings("unchecked") + Map> cache = + (Map>) cacheField.get(null); + + int initialCacheSize = cache.size(); + + // Create two different ClassLoaders + URLClassLoader classLoader1 = new URLClassLoader(new URL[0], null); + URLClassLoader classLoader2 = new URLClassLoader(new URL[0], null); + String testTypeName = "com.test.SameClassName"; + + // Create TypeCache with same class name for both ClassLoaders + SWDescriptionStrategy.SWTypeDescriptionWrapper wrapper1 = + new SWDescriptionStrategy.SWTypeDescriptionWrapper( + null, "test", classLoader1, testTypeName); + SWDescriptionStrategy.SWTypeDescriptionWrapper wrapper2 = + new SWDescriptionStrategy.SWTypeDescriptionWrapper( + null, "test", classLoader2, testTypeName); + + // Call getTypeCache method via reflection + Method getTypeCacheMethod = + SWDescriptionStrategy.SWTypeDescriptionWrapper.class.getDeclaredMethod("getTypeCache"); + getTypeCacheMethod.setAccessible(true); + + SWDescriptionStrategy.TypeCache typeCache1 = + (SWDescriptionStrategy.TypeCache) getTypeCacheMethod.invoke(wrapper1); + SWDescriptionStrategy.TypeCache typeCache2 = + (SWDescriptionStrategy.TypeCache) getTypeCacheMethod.invoke(wrapper2); + + // Verify that the two ClassLoaders have independent cache entries + Assert.assertNotNull("TypeCache1 should be created", typeCache1); + Assert.assertNotNull("TypeCache2 should be created", typeCache2); + Assert.assertNotSame("TypeCaches should be different instances", typeCache1, typeCache2); + + // Verify cache structure + Assert.assertEquals("Cache should contain both classloaders", + initialCacheSize + 2, cache.size()); + Assert.assertTrue("Cache should contain classloader1", cache.containsKey(classLoader1)); + Assert.assertTrue("Cache should contain classloader2", cache.containsKey(classLoader2)); + + // Verify each ClassLoader has independent type cache + Map typeCacheMap1 = cache.get(classLoader1); + Map typeCacheMap2 = cache.get(classLoader2); + + Assert.assertNotNull("ClassLoader1 should have type cache map", typeCacheMap1); + Assert.assertNotNull("ClassLoader2 should have type cache map", typeCacheMap2); + Assert.assertNotSame("Type cache maps should be different", typeCacheMap1, typeCacheMap2); + + Assert.assertTrue("ClassLoader1 cache should contain the type", + typeCacheMap1.containsKey(testTypeName)); + Assert.assertTrue("ClassLoader2 cache should contain the type", + typeCacheMap2.containsKey(testTypeName)); + } + + @Test + public void testConcurrentAccess() throws Exception { + // Test concurrent access scenario + final String testTypeName = "com.test.ConcurrentClass"; + final int threadCount = 10; + final Thread[] threads = new Thread[threadCount]; + final URLClassLoader[] classLoaders = new URLClassLoader[threadCount]; + final SWDescriptionStrategy.TypeCache[] results = new SWDescriptionStrategy.TypeCache[threadCount]; + + // Create multiple threads to access cache simultaneously + for (int i = 0; i < threadCount; i++) { + final int index = i; + classLoaders[i] = new URLClassLoader(new URL[0], null); + threads[i] = new Thread(() -> { + try { + SWDescriptionStrategy.SWTypeDescriptionWrapper wrapper = + new SWDescriptionStrategy.SWTypeDescriptionWrapper( + null, "test", classLoaders[index], testTypeName); + + Method getTypeCacheMethod = wrapper.getClass() + .getDeclaredMethod("getTypeCache"); + getTypeCacheMethod.setAccessible(true); + results[index] = (SWDescriptionStrategy.TypeCache) + getTypeCacheMethod.invoke(wrapper); + } catch (Exception e) { + Assert.fail("Concurrent access should not throw exception: " + e.getMessage()); + } + }); + } + + // Start all threads + for (Thread thread : threads) { + thread.start(); + } + + // Wait for all threads to complete + for (Thread thread : threads) { + thread.join(1000); // Wait at most 1 second + } + + // Verify all results + for (int i = 0; i < threadCount; i++) { + Assert.assertNotNull("Result " + i + " should not be null", results[i]); + } + + System.out.println("Concurrent access test completed successfully with " + threadCount + " threads"); + } +} \ No newline at end of file diff --git a/apm-sniffer/bytebuddy-patch/src/test/java/org/apache/skywalking/apm/agent/bytebuddy/biz/ChildBar.java b/apm-sniffer/bytebuddy-patch/src/test/java/org/apache/skywalking/apm/agent/bytebuddy/biz/ChildBar.java new file mode 100644 index 0000000000..e7ac8d49ff --- /dev/null +++ b/apm-sniffer/bytebuddy-patch/src/test/java/org/apache/skywalking/apm/agent/bytebuddy/biz/ChildBar.java @@ -0,0 +1,27 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.agent.bytebuddy.biz; + +public class ChildBar extends ParentBar { + + public String sayHelloChild() { + return "Joe"; + } + +} diff --git a/apm-sniffer/bytebuddy-patch/src/test/java/org/apache/skywalking/apm/agent/bytebuddy/biz/ParentBar.java b/apm-sniffer/bytebuddy-patch/src/test/java/org/apache/skywalking/apm/agent/bytebuddy/biz/ParentBar.java new file mode 100644 index 0000000000..6113b4c41a --- /dev/null +++ b/apm-sniffer/bytebuddy-patch/src/test/java/org/apache/skywalking/apm/agent/bytebuddy/biz/ParentBar.java @@ -0,0 +1,26 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.agent.bytebuddy.biz; + +public class ParentBar { + + public String sayHelloParent() { + return "Joe"; + } +} diff --git a/apm-sniffer/bytebuddy-patch/src/test/java/org/apache/skywalking/apm/agent/bytebuddy/cases/AbstractInterceptTest.java b/apm-sniffer/bytebuddy-patch/src/test/java/org/apache/skywalking/apm/agent/bytebuddy/cases/AbstractInterceptTest.java index ba9cf4d372..627fc8888d 100644 --- a/apm-sniffer/bytebuddy-patch/src/test/java/org/apache/skywalking/apm/agent/bytebuddy/cases/AbstractInterceptTest.java +++ b/apm-sniffer/bytebuddy-patch/src/test/java/org/apache/skywalking/apm/agent/bytebuddy/cases/AbstractInterceptTest.java @@ -24,6 +24,7 @@ import net.bytebuddy.agent.builder.SWAgentBuilderDefault; import net.bytebuddy.agent.builder.SWDescriptionStrategy; import net.bytebuddy.agent.builder.SWNativeMethodStrategy; +import net.bytebuddy.description.modifier.Visibility; import net.bytebuddy.implementation.FieldAccessor; import net.bytebuddy.implementation.MethodDelegation; import net.bytebuddy.implementation.SWImplementationContextFactory; @@ -38,6 +39,7 @@ import org.apache.skywalking.apm.agent.bytebuddy.SWAuxiliaryTypeNamingStrategy; import org.apache.skywalking.apm.agent.bytebuddy.SWClassFileLocator; import org.apache.skywalking.apm.agent.bytebuddy.biz.BizFoo; +import org.apache.skywalking.apm.agent.bytebuddy.biz.ChildBar; import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; import org.junit.Assert; import org.junit.BeforeClass; @@ -58,11 +60,15 @@ import static net.bytebuddy.jar.asm.Opcodes.ACC_PRIVATE; import static net.bytebuddy.jar.asm.Opcodes.ACC_VOLATILE; import static org.apache.skywalking.apm.agent.core.plugin.AbstractClassEnhancePluginDefine.CONTEXT_ATTR_NAME; +import static org.apache.skywalking.apm.agent.core.plugin.AbstractClassEnhancePluginDefine.CONTEXT_GETTER_NAME; +import static org.apache.skywalking.apm.agent.core.plugin.AbstractClassEnhancePluginDefine.CONTEXT_SETTER_NAME; public class AbstractInterceptTest { public static final String BIZ_FOO_CLASS_NAME = "org.apache.skywalking.apm.agent.bytebuddy.biz.BizFoo"; public static final String PROJECT_SERVICE_CLASS_NAME = "org.apache.skywalking.apm.agent.bytebuddy.biz.ProjectService"; public static final String DOC_SERVICE_CLASS_NAME = "org.apache.skywalking.apm.agent.bytebuddy.biz.DocService"; + public static final String PARENT_BAR_CLASS_NAME = "org.apache.skywalking.apm.agent.bytebuddy.biz.ParentBar"; + public static final String CHILD_BAR_CLASS_NAME = "org.apache.skywalking.apm.agent.bytebuddy.biz.ChildBar"; public static final String SAY_HELLO_METHOD = "sayHello"; public static final int BASE_INT_VALUE = 100; public static final String CONSTRUCTOR_INTERCEPTOR_CLASS = "constructorInterceptorClass"; @@ -85,6 +91,20 @@ protected void failed(Throwable e, Description description) { } }; + protected static void callBar(int round) { + Log.info("-------------"); + Log.info("callChildBar: " + round); + // load target class + String strResultChild = new ChildBar().sayHelloChild(); + Log.info("result: " + strResultChild); + + String strResultParent = new ChildBar().sayHelloParent(); + Log.info("result: " + strResultParent); + + Assert.assertEquals("String value is unexpected", "John", strResultChild); + Assert.assertEquals("String value is unexpected", "John", strResultParent); + } + protected static void callBizFoo(int round) { Log.info("-------------"); Log.info("callBizFoo: " + round); @@ -115,7 +135,7 @@ protected static void checkConstructorInterceptor(String className, int round) { protected static void checkInterface(Class testClass, Class interfaceCls) { Assert.assertTrue("Check interface failure, the test class: " + testClass + " does not implement the expected interface: " + interfaceCls, - EnhancedInstance.class.isAssignableFrom(BizFoo.class)); + EnhancedInstance.class.isAssignableFrom(testClass)); } protected static void checkErrors() { @@ -195,6 +215,9 @@ protected void installInterface(String className) { builder = builder.defineField( CONTEXT_ATTR_NAME, Object.class, ACC_PRIVATE | ACC_VOLATILE) .implement(EnhancedInstance.class) + .defineMethod(CONTEXT_GETTER_NAME, Object.class, Visibility.PUBLIC) + .intercept(FieldAccessor.ofField(CONTEXT_ATTR_NAME)) + .defineMethod(CONTEXT_SETTER_NAME, void.class, Visibility.PUBLIC).withParameters(Object.class) .intercept(FieldAccessor.ofField(CONTEXT_ATTR_NAME)); } return builder; @@ -223,7 +246,7 @@ protected void installTraceClassTransformer(String msg) { ClassFileTransformer classFileTransformer = new ClassFileTransformer() { @Override public byte[] transform(ClassLoader loader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException { - if (className.endsWith("BizFoo") || className.endsWith("ProjectService") || className.endsWith("DocService")) { + if (className.endsWith("BizFoo") || className.endsWith("ProjectService") || className.endsWith("DocService") || className.endsWith("ChildBar") || className.endsWith("ParentBar")) { Log.error(msg + className); ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); ClassReader cr = new ClassReader(classfileBuffer); diff --git a/apm-sniffer/bytebuddy-patch/src/test/java/org/apache/skywalking/apm/agent/bytebuddy/cases/ReTransform4Test.java b/apm-sniffer/bytebuddy-patch/src/test/java/org/apache/skywalking/apm/agent/bytebuddy/cases/ReTransform4Test.java new file mode 100644 index 0000000000..877dd416e7 --- /dev/null +++ b/apm-sniffer/bytebuddy-patch/src/test/java/org/apache/skywalking/apm/agent/bytebuddy/cases/ReTransform4Test.java @@ -0,0 +1,62 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.agent.bytebuddy.cases; + +import net.bytebuddy.agent.ByteBuddyAgent; +import org.apache.skywalking.apm.agent.bytebuddy.biz.ChildBar; +import org.apache.skywalking.apm.agent.bytebuddy.biz.ParentBar; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; +import org.junit.Test; + +import java.lang.instrument.Instrumentation; + +public class ReTransform4Test extends AbstractReTransformTest { + + @Test + public void testInterceptConstructor() throws Exception { + Instrumentation instrumentation = ByteBuddyAgent.install(); + + // install transformer + installMethodInterceptor(PARENT_BAR_CLASS_NAME, "sayHelloParent", 1); + installMethodInterceptor(CHILD_BAR_CLASS_NAME, "sayHelloChild", 1); + // implement EnhancedInstance + installInterface(PARENT_BAR_CLASS_NAME); + installInterface(CHILD_BAR_CLASS_NAME); + + // call target class + callBar(1); + + // check interceptors + checkInterface(ParentBar.class, EnhancedInstance.class); + checkInterface(ChildBar.class, EnhancedInstance.class); + checkErrors(); + + installTraceClassTransformer("Trace class: "); + + // do retransform + reTransform(instrumentation, ChildBar.class); + + // check interceptors + checkInterface(ParentBar.class, EnhancedInstance.class); + checkInterface(ChildBar.class, EnhancedInstance.class); + checkErrors(); + } + +} + diff --git a/apm-sniffer/config/agent.config b/apm-sniffer/config/agent.config index 65b95a6fbd..84b5c41622 100755 --- a/apm-sniffer/config/agent.config +++ b/apm-sniffer/config/agent.config @@ -36,6 +36,9 @@ agent.authentication=${SW_AGENT_AUTHENTICATION:} # The max number of TraceSegmentRef in a single span to keep memory cost estimatable. agent.trace_segment_ref_limit_per_span=${SW_TRACE_SEGMENT_LIMIT:500} +# The max number of logs in a single span to keep memory cost estimatable. +agent.log_limit_per_span=${SW_LOG_LIMIT_PER_SPAN:500} + # The max amount of spans in a single segment. # Through this config item, SkyWalking keep your application memory cost estimated. agent.span_limit_per_segment=${SW_AGENT_SPAN_LIMIT:300} @@ -164,6 +167,12 @@ profile.duration=${SW_AGENT_PROFILE_DURATION:10} profile.dump_max_stack_depth=${SW_AGENT_PROFILE_DUMP_MAX_STACK_DEPTH:500} # Snapshot transport to backend buffer size profile.snapshot_transport_buffer_size=${SW_AGENT_PROFILE_SNAPSHOT_TRANSPORT_BUFFER_SIZE:4500} +# If true, async profiler will be enabled when user creates a new async profiler task. If false, it will be disabled. The default value is true. +asyncprofiler.active=${SW_AGENT_ASYNC_PROFILER_ACTIVE:true} +# Max execution time(second) for the Async Profiler. The task will be stopped even if a longer time is specified. default 20min. +asyncprofiler.max_duration=${SW_AGENT_ASYNC_PROFILER_MAX_DURATION:1200} +# Path for the JFR outputs from the Async Profiler. If the parameter is not empty, the file will be created in the specified directory, otherwise the Files.createTemp method will be used to create the file. +asyncprofiler.output_path=${SW_AGENT_ASYNC_PROFILER_OUTPUT_PATH:} # If true, the agent collects and reports metrics to the backend. meter.active=${SW_METER_ACTIVE:true} # Report meters interval. The unit is second @@ -320,3 +329,35 @@ plugin.nettyhttp.supported_content_types_prefix=${SW_PLUGIN_NETTYHTTP_SUPPORTED_ plugin.rocketmqclient.collect_message_keys=${SW_PLUGIN_ROCKETMQCLIENT_COLLECT_MESSAGE_KEYS:false} # If set to true, the tags of messages would be collected by the plugin for RocketMQ Java client. plugin.rocketmqclient.collect_message_tags=${SW_PLUGIN_ROCKETMQCLIENT_COLLECT_MESSAGE_TAGS:false} +# Define the max length of collected HTTP parameters. The default value(=0) means not collecting. +plugin.solon.http_params_length_threshold=${SW_PLUGIN_SOLON_HTTP_PARAMS_LENGTH_THRESHOLD:0} +# It controls what header data should be collected, values must be in lower case, if empty, no header data will be collected. default is empty. +plugin.solon.include_http_headers=${SW_PLUGIN_SOLON_INCLUDE_HTTP_HEADERS:} +# Define the max length of collected HTTP body. The default value(=0) means not collecting. +plugin.solon.http_body_length_threshold=${SW_PLUGIN_SOLON_HTTP_BODY_LENGTH_THRESHOLD:0} +# Specify which command should be converted to write operation +plugin.caffeine.operation_mapping_write=${SW_PLUGIN_CAFFEINE_OPERATION_MAPPING_WRITE:put,putAll,remove,clear} +# Specify which command should be converted to read operation +plugin.caffeine.operation_mapping_read=${SW_PLUGIN_CAFFEINE_OPERATION_MAPPING_READ:getIfPresent,getAllPresent,computeIfAbsent} +# Whether to collect the input messages of the GenAI request. +plugin.springai.collect_input_messages=${SW_PLUGIN_SPRINGAI_COLLECT_INPUT_MESSAGES:false} +# Whether to collect the output messages of the GenAI response. +plugin.springai.collect_output_messages=${SW_PLUGIN_SPRINGAI_COLLECT_OUTPUT_MESSAGES:false} +# The maximum characters of the collected prompt content. +# If the content exceeds this limit, it will be truncated. +# Use a negative value to represent no limit, but be aware this could cause OOM. +plugin.springai.prompt_length_limit=${SW_PLUGIN_SPRINGAI_PROMPT_LENGTH_LIMIT:1024} +# The maximum characters of the collected completion content. +# If the content exceeds this limit, it will be truncated. +# Use a negative value to represent no limit, but be aware this could cause OOM. +plugin.springai.completion_length_limit=${SW_PLUGIN_SPRINGAI_COMPLETION_LENGTH_LIMIT:1024} +# The threshold for token usage to trigger content collection. +# When set to a positive value, prompt and completion will only be collected +# if the total token usage of the request exceeds this threshold. +# This requires collect_prompt or collect_completion to be enabled first. +# Use a negative value to disable this threshold-based filtering (collect all). +plugin.springai.content_collect_threshold_tokens=${SW_PLUGIN_SPRINGAI_CONTENT_COLLECT_THRESHOLD_TOKENS:-1} +# Whether to collect the arguments (input parameters) of the tool/function call. +plugin.springai.collect_tool_input=${SW_PLUGIN_SPRINGAI_COLLECT_TOOL_INPUT:false} +# Whether to collect the execution result (output) of the tool/function call. +plugin.springai.collect_tool_output=${SW_PLUGIN_SPRINGAI_COLLECT_TOOL_OUTPUT:false} \ No newline at end of file diff --git a/apm-sniffer/apm-sdk-plugin/impala-jdbc-2.6.x-plugin/pom.xml b/apm-sniffer/expired-plugins/impala-jdbc-2.6.x-plugin/pom.xml similarity index 86% rename from apm-sniffer/apm-sdk-plugin/impala-jdbc-2.6.x-plugin/pom.xml rename to apm-sniffer/expired-plugins/impala-jdbc-2.6.x-plugin/pom.xml index 3c0c748fd9..5dfd4ed7ab 100644 --- a/apm-sniffer/apm-sdk-plugin/impala-jdbc-2.6.x-plugin/pom.xml +++ b/apm-sniffer/expired-plugins/impala-jdbc-2.6.x-plugin/pom.xml @@ -23,8 +23,8 @@ org.apache.skywalking - apm-sdk-plugin - 9.2.0-SNAPSHOT + expired-plugins + 9.7.0-SNAPSHOT apm-impala-jdbc-2.6.x-plugin @@ -43,12 +43,4 @@ provided
- - - - - maven-deploy-plugin - - - \ No newline at end of file diff --git a/apm-sniffer/apm-sdk-plugin/impala-jdbc-2.6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdbc/impala/CreateCallableStatementInterceptor.java b/apm-sniffer/expired-plugins/impala-jdbc-2.6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdbc/impala/CreateCallableStatementInterceptor.java similarity index 100% rename from apm-sniffer/apm-sdk-plugin/impala-jdbc-2.6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdbc/impala/CreateCallableStatementInterceptor.java rename to apm-sniffer/expired-plugins/impala-jdbc-2.6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdbc/impala/CreateCallableStatementInterceptor.java diff --git a/apm-sniffer/apm-sdk-plugin/impala-jdbc-2.6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdbc/impala/CreatePreparedStatementInterceptor.java b/apm-sniffer/expired-plugins/impala-jdbc-2.6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdbc/impala/CreatePreparedStatementInterceptor.java similarity index 100% rename from apm-sniffer/apm-sdk-plugin/impala-jdbc-2.6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdbc/impala/CreatePreparedStatementInterceptor.java rename to apm-sniffer/expired-plugins/impala-jdbc-2.6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdbc/impala/CreatePreparedStatementInterceptor.java diff --git a/apm-sniffer/apm-sdk-plugin/impala-jdbc-2.6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdbc/impala/CreateStatementInterceptor.java b/apm-sniffer/expired-plugins/impala-jdbc-2.6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdbc/impala/CreateStatementInterceptor.java similarity index 100% rename from apm-sniffer/apm-sdk-plugin/impala-jdbc-2.6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdbc/impala/CreateStatementInterceptor.java rename to apm-sniffer/expired-plugins/impala-jdbc-2.6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdbc/impala/CreateStatementInterceptor.java diff --git a/apm-sniffer/apm-sdk-plugin/impala-jdbc-2.6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdbc/impala/PreparedStatementExecuteMethodsInterceptor.java b/apm-sniffer/expired-plugins/impala-jdbc-2.6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdbc/impala/PreparedStatementExecuteMethodsInterceptor.java similarity index 100% rename from apm-sniffer/apm-sdk-plugin/impala-jdbc-2.6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdbc/impala/PreparedStatementExecuteMethodsInterceptor.java rename to apm-sniffer/expired-plugins/impala-jdbc-2.6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdbc/impala/PreparedStatementExecuteMethodsInterceptor.java diff --git a/apm-sniffer/apm-sdk-plugin/impala-jdbc-2.6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdbc/impala/SetCatalogInterceptor.java b/apm-sniffer/expired-plugins/impala-jdbc-2.6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdbc/impala/SetCatalogInterceptor.java similarity index 100% rename from apm-sniffer/apm-sdk-plugin/impala-jdbc-2.6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdbc/impala/SetCatalogInterceptor.java rename to apm-sniffer/expired-plugins/impala-jdbc-2.6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdbc/impala/SetCatalogInterceptor.java diff --git a/apm-sniffer/apm-sdk-plugin/impala-jdbc-2.6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdbc/impala/StatementExecuteMethodsInterceptor.java b/apm-sniffer/expired-plugins/impala-jdbc-2.6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdbc/impala/StatementExecuteMethodsInterceptor.java similarity index 100% rename from apm-sniffer/apm-sdk-plugin/impala-jdbc-2.6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdbc/impala/StatementExecuteMethodsInterceptor.java rename to apm-sniffer/expired-plugins/impala-jdbc-2.6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdbc/impala/StatementExecuteMethodsInterceptor.java diff --git a/apm-sniffer/apm-sdk-plugin/impala-jdbc-2.6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdbc/impala/define/ConnectionInstrumentation.java b/apm-sniffer/expired-plugins/impala-jdbc-2.6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdbc/impala/define/ConnectionInstrumentation.java similarity index 100% rename from apm-sniffer/apm-sdk-plugin/impala-jdbc-2.6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdbc/impala/define/ConnectionInstrumentation.java rename to apm-sniffer/expired-plugins/impala-jdbc-2.6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdbc/impala/define/ConnectionInstrumentation.java diff --git a/apm-sniffer/apm-sdk-plugin/impala-jdbc-2.6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdbc/impala/define/DriverInstrumentation.java b/apm-sniffer/expired-plugins/impala-jdbc-2.6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdbc/impala/define/DriverInstrumentation.java similarity index 100% rename from apm-sniffer/apm-sdk-plugin/impala-jdbc-2.6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdbc/impala/define/DriverInstrumentation.java rename to apm-sniffer/expired-plugins/impala-jdbc-2.6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdbc/impala/define/DriverInstrumentation.java diff --git a/apm-sniffer/apm-sdk-plugin/impala-jdbc-2.6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdbc/impala/define/PreparedStatementIgnoredSetterInstrumentation.java b/apm-sniffer/expired-plugins/impala-jdbc-2.6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdbc/impala/define/PreparedStatementIgnoredSetterInstrumentation.java similarity index 100% rename from apm-sniffer/apm-sdk-plugin/impala-jdbc-2.6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdbc/impala/define/PreparedStatementIgnoredSetterInstrumentation.java rename to apm-sniffer/expired-plugins/impala-jdbc-2.6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdbc/impala/define/PreparedStatementIgnoredSetterInstrumentation.java diff --git a/apm-sniffer/apm-sdk-plugin/impala-jdbc-2.6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdbc/impala/define/PreparedStatementInstrumentation.java b/apm-sniffer/expired-plugins/impala-jdbc-2.6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdbc/impala/define/PreparedStatementInstrumentation.java similarity index 100% rename from apm-sniffer/apm-sdk-plugin/impala-jdbc-2.6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdbc/impala/define/PreparedStatementInstrumentation.java rename to apm-sniffer/expired-plugins/impala-jdbc-2.6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdbc/impala/define/PreparedStatementInstrumentation.java diff --git a/apm-sniffer/apm-sdk-plugin/impala-jdbc-2.6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdbc/impala/define/PreparedStatementNullSetterInstrumentation.java b/apm-sniffer/expired-plugins/impala-jdbc-2.6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdbc/impala/define/PreparedStatementNullSetterInstrumentation.java similarity index 100% rename from apm-sniffer/apm-sdk-plugin/impala-jdbc-2.6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdbc/impala/define/PreparedStatementNullSetterInstrumentation.java rename to apm-sniffer/expired-plugins/impala-jdbc-2.6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdbc/impala/define/PreparedStatementNullSetterInstrumentation.java diff --git a/apm-sniffer/apm-sdk-plugin/impala-jdbc-2.6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdbc/impala/define/PreparedStatementSetterInstrumentation.java b/apm-sniffer/expired-plugins/impala-jdbc-2.6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdbc/impala/define/PreparedStatementSetterInstrumentation.java similarity index 100% rename from apm-sniffer/apm-sdk-plugin/impala-jdbc-2.6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdbc/impala/define/PreparedStatementSetterInstrumentation.java rename to apm-sniffer/expired-plugins/impala-jdbc-2.6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdbc/impala/define/PreparedStatementSetterInstrumentation.java diff --git a/apm-sniffer/apm-sdk-plugin/impala-jdbc-2.6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdbc/impala/define/StatementInstrumentation.java b/apm-sniffer/expired-plugins/impala-jdbc-2.6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdbc/impala/define/StatementInstrumentation.java similarity index 100% rename from apm-sniffer/apm-sdk-plugin/impala-jdbc-2.6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdbc/impala/define/StatementInstrumentation.java rename to apm-sniffer/expired-plugins/impala-jdbc-2.6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdbc/impala/define/StatementInstrumentation.java diff --git a/apm-sniffer/apm-sdk-plugin/impala-jdbc-2.6.x-plugin/src/main/resources/skywalking-plugin.def b/apm-sniffer/expired-plugins/impala-jdbc-2.6.x-plugin/src/main/resources/skywalking-plugin.def similarity index 100% rename from apm-sniffer/apm-sdk-plugin/impala-jdbc-2.6.x-plugin/src/main/resources/skywalking-plugin.def rename to apm-sniffer/expired-plugins/impala-jdbc-2.6.x-plugin/src/main/resources/skywalking-plugin.def diff --git a/apm-sniffer/apm-sdk-plugin/impala-jdbc-2.6.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/jdbc/impala/CreateCallableStatementInterceptorTest.java b/apm-sniffer/expired-plugins/impala-jdbc-2.6.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/jdbc/impala/CreateCallableStatementInterceptorTest.java similarity index 100% rename from apm-sniffer/apm-sdk-plugin/impala-jdbc-2.6.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/jdbc/impala/CreateCallableStatementInterceptorTest.java rename to apm-sniffer/expired-plugins/impala-jdbc-2.6.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/jdbc/impala/CreateCallableStatementInterceptorTest.java diff --git a/apm-sniffer/apm-sdk-plugin/impala-jdbc-2.6.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/jdbc/impala/CreatePreparedStatementInterceptorTest.java b/apm-sniffer/expired-plugins/impala-jdbc-2.6.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/jdbc/impala/CreatePreparedStatementInterceptorTest.java similarity index 100% rename from apm-sniffer/apm-sdk-plugin/impala-jdbc-2.6.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/jdbc/impala/CreatePreparedStatementInterceptorTest.java rename to apm-sniffer/expired-plugins/impala-jdbc-2.6.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/jdbc/impala/CreatePreparedStatementInterceptorTest.java diff --git a/apm-sniffer/apm-sdk-plugin/impala-jdbc-2.6.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/jdbc/impala/CreateStatementInterceptorTest.java b/apm-sniffer/expired-plugins/impala-jdbc-2.6.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/jdbc/impala/CreateStatementInterceptorTest.java similarity index 100% rename from apm-sniffer/apm-sdk-plugin/impala-jdbc-2.6.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/jdbc/impala/CreateStatementInterceptorTest.java rename to apm-sniffer/expired-plugins/impala-jdbc-2.6.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/jdbc/impala/CreateStatementInterceptorTest.java diff --git a/apm-sniffer/apm-sdk-plugin/impala-jdbc-2.6.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/jdbc/impala/PreparedStatementExecuteMethodsInterceptorTest.java b/apm-sniffer/expired-plugins/impala-jdbc-2.6.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/jdbc/impala/PreparedStatementExecuteMethodsInterceptorTest.java similarity index 100% rename from apm-sniffer/apm-sdk-plugin/impala-jdbc-2.6.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/jdbc/impala/PreparedStatementExecuteMethodsInterceptorTest.java rename to apm-sniffer/expired-plugins/impala-jdbc-2.6.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/jdbc/impala/PreparedStatementExecuteMethodsInterceptorTest.java diff --git a/apm-sniffer/apm-sdk-plugin/impala-jdbc-2.6.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/jdbc/impala/StatementExecuteMethodsInterceptorTest.java b/apm-sniffer/expired-plugins/impala-jdbc-2.6.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/jdbc/impala/StatementExecuteMethodsInterceptorTest.java similarity index 100% rename from apm-sniffer/apm-sdk-plugin/impala-jdbc-2.6.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/jdbc/impala/StatementExecuteMethodsInterceptorTest.java rename to apm-sniffer/expired-plugins/impala-jdbc-2.6.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/jdbc/impala/StatementExecuteMethodsInterceptorTest.java diff --git a/apm-sniffer/expired-plugins/pom.xml b/apm-sniffer/expired-plugins/pom.xml new file mode 100644 index 0000000000..4385464c61 --- /dev/null +++ b/apm-sniffer/expired-plugins/pom.xml @@ -0,0 +1,135 @@ + + + + + + java-agent-sniffer + org.apache.skywalking + 9.7.0-SNAPSHOT + + 4.0.0 + + expired-plugins + pom + + net.bytebuddy + ${shade.package}.${shade.net.bytebuddy.source} + UTF-8 + + ${maven.multiModuleProjectDirectory}/skywalking-agent/expired-plugins + + 1.0b3 + 1.8.1 + + + + impala-jdbc-2.6.x-plugin + + + + + org.apache.skywalking + apm-agent-core + ${project.version} + provided + + + org.apache.skywalking + java-agent-util + ${project.version} + provided + + + org.apache.skywalking + apm-test-tools + ${project.version} + test + + + + + + + maven-shade-plugin + + + package + + shade + + + false + true + true + true + + + ${shade.net.bytebuddy.source} + ${shade.net.bytebuddy.target} + + + + + + + + maven-antrun-plugin + + + package + + run + + + + + + + + + + + + + + + + + + ant-contrib + ant-contrib + ${ant-contrib.version} + + + ant + ant + + + + + org.apache.ant + ant-nodeps + ${ant-nodeps.version} + + + + + + + diff --git a/apm-sniffer/optional-plugins/caffeine-3.x-plugin/pom.xml b/apm-sniffer/optional-plugins/caffeine-3.x-plugin/pom.xml new file mode 100644 index 0000000000..89d8d96b65 --- /dev/null +++ b/apm-sniffer/optional-plugins/caffeine-3.x-plugin/pom.xml @@ -0,0 +1,45 @@ + + + + + 4.0.0 + + org.apache.skywalking + optional-plugins + 9.7.0-SNAPSHOT + + + apm-caffeine-3.x-plugin + jar + caffeine-3.x-plugin + + + UTF-8 + 3.1.8 + + + + + com.github.ben-manes.caffeine + caffeine + ${caffeine.version} + provided + + + diff --git a/apm-sniffer/optional-plugins/caffeine-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/caffeine/v3/AbstractCaffeineInterceptor.java b/apm-sniffer/optional-plugins/caffeine-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/caffeine/v3/AbstractCaffeineInterceptor.java new file mode 100644 index 0000000000..9c66cabd08 --- /dev/null +++ b/apm-sniffer/optional-plugins/caffeine-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/caffeine/v3/AbstractCaffeineInterceptor.java @@ -0,0 +1,71 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.caffeine.v3; + +import java.lang.reflect.Method; +import org.apache.skywalking.apm.agent.core.context.ContextManager; +import org.apache.skywalking.apm.agent.core.context.tag.Tags; +import org.apache.skywalking.apm.agent.core.context.trace.AbstractSpan; +import org.apache.skywalking.apm.agent.core.context.trace.SpanLayer; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceMethodsAroundInterceptor; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult; +import org.apache.skywalking.apm.network.trace.component.ComponentsDefine; + +import static org.apache.skywalking.apm.plugin.caffeine.v3.CaffeineOperationTransform.transformOperation; + +abstract public class AbstractCaffeineInterceptor implements InstanceMethodsAroundInterceptor { + + protected AbstractSpan generateSpanInfo(String methodName) { + AbstractSpan span = ContextManager.createLocalSpan("Caffeine/" + methodName); + span.setComponent(ComponentsDefine.CAFFEINE); + Tags.CACHE_TYPE.set(span, ComponentsDefine.CAFFEINE.getName()); + Tags.CACHE_CMD.set(span, methodName); + transformOperation(methodName).ifPresent(op -> Tags.CACHE_OP.set(span, op)); + SpanLayer.asCache(span); + return span; + } + + @Override + public void beforeMethod(final EnhancedInstance objInst, + final Method method, + final Object[] allArguments, + final Class[] argumentsTypes, + final MethodInterceptResult result) throws Throwable { + } + + @Override + public Object afterMethod(final EnhancedInstance objInst, + final Method method, + final Object[] allArguments, + final Class[] argumentsTypes, + final Object ret) throws Throwable { + ContextManager.stopSpan(); + return ret; + } + + @Override + public void handleMethodException(final EnhancedInstance objInst, + final Method method, + final Object[] allArguments, + final Class[] argumentsTypes, + final Throwable t) { + ContextManager.activeSpan().log(t); + } +} diff --git a/apm-sniffer/optional-plugins/caffeine-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/caffeine/v3/CaffeineIterableInterceptor.java b/apm-sniffer/optional-plugins/caffeine-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/caffeine/v3/CaffeineIterableInterceptor.java new file mode 100644 index 0000000000..ef5860e46f --- /dev/null +++ b/apm-sniffer/optional-plugins/caffeine-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/caffeine/v3/CaffeineIterableInterceptor.java @@ -0,0 +1,46 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.caffeine.v3; + +import java.lang.reflect.Method; +import java.util.stream.Collectors; +import java.util.stream.StreamSupport; +import org.apache.skywalking.apm.agent.core.context.tag.Tags; +import org.apache.skywalking.apm.agent.core.context.trace.AbstractSpan; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult; + +public class CaffeineIterableInterceptor extends AbstractCaffeineInterceptor { + + @Override + public void beforeMethod(final EnhancedInstance objInst, + final Method method, + final Object[] allArguments, + final Class[] argumentsTypes, + final MethodInterceptResult result) throws Throwable { + AbstractSpan span = generateSpanInfo(method.getName()); + if (allArguments != null && allArguments.length > 0) { + String keys = StreamSupport + .stream(((Iterable) allArguments[0]).spliterator(), false) + .map(String::valueOf) + .collect(Collectors.joining(",")); + Tags.CACHE_KEY.set(span, keys); + } + } +} diff --git a/apm-sniffer/optional-plugins/caffeine-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/caffeine/v3/CaffeineMapInterceptor.java b/apm-sniffer/optional-plugins/caffeine-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/caffeine/v3/CaffeineMapInterceptor.java new file mode 100644 index 0000000000..98258bbb38 --- /dev/null +++ b/apm-sniffer/optional-plugins/caffeine-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/caffeine/v3/CaffeineMapInterceptor.java @@ -0,0 +1,45 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.caffeine.v3; + +import java.lang.reflect.Method; +import java.util.Map; +import java.util.stream.Collectors; +import org.apache.skywalking.apm.agent.core.context.tag.Tags; +import org.apache.skywalking.apm.agent.core.context.trace.AbstractSpan; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult; + +public class CaffeineMapInterceptor extends AbstractCaffeineInterceptor { + + @Override + public void beforeMethod(final EnhancedInstance objInst, + final Method method, + final Object[] allArguments, + final Class[] argumentsTypes, + final MethodInterceptResult result) throws Throwable { + AbstractSpan span = generateSpanInfo(method.getName()); + if (allArguments != null && allArguments.length > 0) { + String keys = ((Map) allArguments[0]) + .keySet().stream().map(String::valueOf) + .collect(Collectors.joining(",")); + Tags.CACHE_KEY.set(span, keys); + } + } +} diff --git a/apm-sniffer/optional-plugins/caffeine-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/caffeine/v3/CaffeineOperationTransform.java b/apm-sniffer/optional-plugins/caffeine-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/caffeine/v3/CaffeineOperationTransform.java new file mode 100644 index 0000000000..0f4a128a92 --- /dev/null +++ b/apm-sniffer/optional-plugins/caffeine-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/caffeine/v3/CaffeineOperationTransform.java @@ -0,0 +1,34 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.caffeine.v3; + +import java.util.Optional; + +public class CaffeineOperationTransform { + + public static Optional transformOperation(String cmd) { + if (CaffeinePluginConfig.Plugin.Caffeine.OPERATION_MAPPING_READ.contains(cmd)) { + return Optional.of("read"); + } + if (CaffeinePluginConfig.Plugin.Caffeine.OPERATION_MAPPING_WRITE.contains(cmd)) { + return Optional.of("write"); + } + return Optional.empty(); + } +} diff --git a/apm-sniffer/optional-plugins/caffeine-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/caffeine/v3/CaffeinePluginConfig.java b/apm-sniffer/optional-plugins/caffeine-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/caffeine/v3/CaffeinePluginConfig.java new file mode 100644 index 0000000000..f775668bee --- /dev/null +++ b/apm-sniffer/optional-plugins/caffeine-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/caffeine/v3/CaffeinePluginConfig.java @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.caffeine.v3; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; +import org.apache.skywalking.apm.agent.core.boot.PluginConfig; + +/** + * Operation represent a cache span is "write" or "read" action , and "op"(operation) is tagged with key "cache.op" + * usually This config term define which command should be converted to write Operation . + * + * @see org.apache.skywalking.apm.agent.core.context.tag.Tags#CACHE_OP + * @see CaffeineOperationTransform#transformOperation(String) + */ +public class CaffeinePluginConfig { + public static class Plugin { + @PluginConfig(root = CaffeinePluginConfig.class) + public static class Caffeine { + public static Set OPERATION_MAPPING_WRITE = new HashSet<>(Arrays.asList( + "put", + "putAll", + "remove", + "clear" + )); + public static Set OPERATION_MAPPING_READ = new HashSet<>(Arrays.asList( + "getIfPresent", + "getAllPresent", + "computeIfAbsent" + )); + } + } +} \ No newline at end of file diff --git a/apm-sniffer/optional-plugins/caffeine-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/caffeine/v3/CaffeineStringInterceptor.java b/apm-sniffer/optional-plugins/caffeine-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/caffeine/v3/CaffeineStringInterceptor.java new file mode 100644 index 0000000000..82b34ca48d --- /dev/null +++ b/apm-sniffer/optional-plugins/caffeine-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/caffeine/v3/CaffeineStringInterceptor.java @@ -0,0 +1,40 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.caffeine.v3; + +import java.lang.reflect.Method; +import org.apache.skywalking.apm.agent.core.context.tag.Tags; +import org.apache.skywalking.apm.agent.core.context.trace.AbstractSpan; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult; + +public class CaffeineStringInterceptor extends AbstractCaffeineInterceptor { + + @Override + public void beforeMethod(final EnhancedInstance objInst, + final Method method, + final Object[] allArguments, + final Class[] argumentsTypes, + final MethodInterceptResult result) throws Throwable { + AbstractSpan span = generateSpanInfo(method.getName()); + if (allArguments != null && allArguments.length > 0 && allArguments[0] instanceof String) { + Tags.CACHE_KEY.set(span, allArguments[0].toString()); + } + } +} diff --git a/apm-sniffer/optional-plugins/caffeine-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/caffeine/v3/define/CaffeineInstrumentation.java b/apm-sniffer/optional-plugins/caffeine-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/caffeine/v3/define/CaffeineInstrumentation.java new file mode 100644 index 0000000000..50e5b3751a --- /dev/null +++ b/apm-sniffer/optional-plugins/caffeine-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/caffeine/v3/define/CaffeineInstrumentation.java @@ -0,0 +1,119 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.caffeine.v3.define; + +import java.util.Map; +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.matcher.ElementMatcher; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine; +import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch; + +import static net.bytebuddy.matcher.ElementMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.takesArgument; +import static net.bytebuddy.matcher.ElementMatchers.takesArguments; +import static org.apache.skywalking.apm.agent.core.plugin.match.MultiClassNameMatch.byMultiClassMatch; + +public class CaffeineInstrumentation extends ClassInstanceMethodsEnhancePluginDefine { + + public static final String BOUNDED_LOCAL_INTERCEPT_CLASS = "com.github.benmanes.caffeine.cache.BoundedLocalCache"; + public static final String UNBOUNDED_LOCAL_INTERCEPT_CLASS = "com.github.benmanes.caffeine.cache.UnboundedLocalCache"; + public static final String CAFFEINE_ITERABLE_ENHANCE_CLASS = "org.apache.skywalking.apm.plugin.caffeine.v3.CaffeineIterableInterceptor"; + public static final String CAFFEINE_MAP_ENHANCE_CLASS = "org.apache.skywalking.apm.plugin.caffeine.v3.CaffeineMapInterceptor"; + public static final String CAFFEINE_STRING_ENHANCE_CLASS = "org.apache.skywalking.apm.plugin.caffeine.v3.CaffeineStringInterceptor"; + + // read/write operations + public static final String GET_IF_PRESENT_METHOD = "getIfPresent"; + public static final String GET_ALL_PRESENT_METHOD = "getAllPresent"; + public static final String COMPUTE_IF_ABSENT_METHOD = "computeIfAbsent"; + public static final String PUT_METHOD = "put"; + public static final String PUT_ALL_METHOD = "putAll"; + public static final String REMOVE_METHOD = "remove"; + public static final String CLEAR_METHOD = "clear"; + + @Override + protected ClassMatch enhanceClass() { + return byMultiClassMatch(BOUNDED_LOCAL_INTERCEPT_CLASS, UNBOUNDED_LOCAL_INTERCEPT_CLASS); + } + + @Override + public ConstructorInterceptPoint[] getConstructorsInterceptPoints() { + return new ConstructorInterceptPoint[0]; + } + + @Override + public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() { + return new InstanceMethodsInterceptPoint[] { + new InstanceMethodsInterceptPoint() { + @Override + public ElementMatcher getMethodsMatcher() { + return named(GET_IF_PRESENT_METHOD) + .and(takesArguments(2)) + .or(named(COMPUTE_IF_ABSENT_METHOD).and(takesArguments(4))) + .or(named(PUT_METHOD).and(takesArguments(2))) + .or(named(REMOVE_METHOD).and(takesArguments(1))) + .or(named(CLEAR_METHOD).and(takesArguments(0))); + } + + @Override + public String getMethodsInterceptor() { + return CAFFEINE_STRING_ENHANCE_CLASS; + } + + @Override + public boolean isOverrideArgs() { + return false; + } + }, + new InstanceMethodsInterceptPoint() { + @Override + public ElementMatcher getMethodsMatcher() { + return named(GET_ALL_PRESENT_METHOD).and(takesArgument(0, Iterable.class)); + } + + @Override + public String getMethodsInterceptor() { + return CAFFEINE_ITERABLE_ENHANCE_CLASS; + } + + @Override + public boolean isOverrideArgs() { + return false; + } + }, + new InstanceMethodsInterceptPoint() { + @Override + public ElementMatcher getMethodsMatcher() { + return named(PUT_ALL_METHOD).and(takesArgument(0, Map.class)); + } + + @Override + public String getMethodsInterceptor() { + return CAFFEINE_MAP_ENHANCE_CLASS; + } + + @Override + public boolean isOverrideArgs() { + return false; + } + } + }; + } +} diff --git a/apm-sniffer/optional-plugins/caffeine-3.x-plugin/src/main/resources/skywalking-plugin.def b/apm-sniffer/optional-plugins/caffeine-3.x-plugin/src/main/resources/skywalking-plugin.def new file mode 100644 index 0000000000..1edf1e3a0e --- /dev/null +++ b/apm-sniffer/optional-plugins/caffeine-3.x-plugin/src/main/resources/skywalking-plugin.def @@ -0,0 +1,17 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +caffeine-3.x=org.apache.skywalking.apm.plugin.caffeine.v3.define.CaffeineInstrumentation diff --git a/apm-sniffer/optional-plugins/caffeine-3.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/caffeine/v3/CaffeineInterceptorTest.java b/apm-sniffer/optional-plugins/caffeine-3.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/caffeine/v3/CaffeineInterceptorTest.java new file mode 100644 index 0000000000..7642ae110d --- /dev/null +++ b/apm-sniffer/optional-plugins/caffeine-3.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/caffeine/v3/CaffeineInterceptorTest.java @@ -0,0 +1,149 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.caffeine.v3; + +import com.github.benmanes.caffeine.cache.Expiry; +import java.lang.reflect.Method; +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import org.apache.skywalking.apm.agent.core.context.trace.TraceSegment; +import org.apache.skywalking.apm.agent.test.tools.AgentServiceRule; +import org.apache.skywalking.apm.agent.test.tools.SegmentStorage; +import org.apache.skywalking.apm.agent.test.tools.SegmentStoragePoint; +import org.apache.skywalking.apm.agent.test.tools.TracingSegmentRunner; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +import static org.hamcrest.CoreMatchers.is; + +@RunWith(TracingSegmentRunner.class) +public class CaffeineInterceptorTest { + + @SegmentStoragePoint + private SegmentStorage segmentStorage; + + private CaffeineIterableInterceptor caffeineIterableInterceptor; + private CaffeineMapInterceptor caffeineMapInterceptor; + private CaffeineStringInterceptor caffeineStringInterceptor; + private Object[] operateObjectArguments; + + private Exception exception; + + private Method getAllPresentMethod; + private Method getIfPresentMethod; + private Method computeIfAbsentMethod; + + private Method putMethod; + private Method putAllMethod; + private Method removeMethod; + private Method cleanMethod; + + @Rule + public AgentServiceRule serviceRule = new AgentServiceRule(); + @Rule + public MockitoRule rule = MockitoJUnit.rule(); + + @Before + public void setUp() throws Exception { + caffeineIterableInterceptor = new CaffeineIterableInterceptor(); + caffeineMapInterceptor = new CaffeineMapInterceptor(); + caffeineStringInterceptor = new CaffeineStringInterceptor(); + exception = new Exception(); + operateObjectArguments = new Object[] {"dataKey"}; + Class cache = Class.forName("com.github.benmanes.caffeine.cache.BoundedLocalCache"); + getAllPresentMethod = cache.getDeclaredMethod("getAllPresent", Iterable.class); + getIfPresentMethod = cache.getDeclaredMethod("getIfPresent", Object.class, boolean.class); + computeIfAbsentMethod = cache.getDeclaredMethod( + "computeIfAbsent", Object.class, Function.class, boolean.class, boolean.class); + putMethod = cache.getDeclaredMethod("put", Object.class, Object.class, Expiry.class, boolean.class); + putAllMethod = cache.getDeclaredMethod("putAll", Map.class); + removeMethod = cache.getDeclaredMethod("remove", Object.class); + cleanMethod = cache.getDeclaredMethod("clear"); + } + + @Test + public void testGetAllPresentSuccess() throws Throwable { + caffeineIterableInterceptor.beforeMethod(null, getAllPresentMethod, null, null, null); + caffeineIterableInterceptor.handleMethodException(null, getAllPresentMethod, null, null, exception); + caffeineIterableInterceptor.afterMethod(null, getAllPresentMethod, null, null, null); + List traceSegments = segmentStorage.getTraceSegments(); + Assert.assertThat(traceSegments.size(), is(1)); + } + + @Test + public void testGetIfPresentSuccess() throws Throwable { + caffeineStringInterceptor.beforeMethod(null, getIfPresentMethod, operateObjectArguments, null, null); + caffeineStringInterceptor.handleMethodException( + null, getIfPresentMethod, operateObjectArguments, null, exception); + caffeineStringInterceptor.afterMethod(null, getIfPresentMethod, operateObjectArguments, null, null); + List traceSegments = segmentStorage.getTraceSegments(); + Assert.assertThat(traceSegments.size(), is(1)); + } + + @Test + public void testComputeIfAbsentMethodSuccess() throws Throwable { + caffeineStringInterceptor.beforeMethod(null, computeIfAbsentMethod, null, null, null); + caffeineStringInterceptor.handleMethodException(null, computeIfAbsentMethod, null, null, exception); + caffeineStringInterceptor.afterMethod(null, computeIfAbsentMethod, null, null, null); + List traceSegments = segmentStorage.getTraceSegments(); + Assert.assertThat(traceSegments.size(), is(1)); + } + + @Test + public void testPutMethodSuccess() throws Throwable { + caffeineStringInterceptor.beforeMethod(null, putMethod, operateObjectArguments, null, null); + caffeineStringInterceptor.handleMethodException(null, putMethod, operateObjectArguments, null, exception); + caffeineStringInterceptor.afterMethod(null, putMethod, operateObjectArguments, null, null); + List traceSegments = segmentStorage.getTraceSegments(); + Assert.assertThat(traceSegments.size(), is(1)); + } + + @Test + public void testPutAllMethodSuccess() throws Throwable { + caffeineMapInterceptor.beforeMethod(null, putAllMethod, null, null, null); + caffeineMapInterceptor.handleMethodException(null, putAllMethod, null, null, exception); + caffeineMapInterceptor.afterMethod(null, putAllMethod, null, null, null); + List traceSegments = segmentStorage.getTraceSegments(); + Assert.assertThat(traceSegments.size(), is(1)); + } + + @Test + public void testRemoveMethodSuccess() throws Throwable { + caffeineStringInterceptor.beforeMethod(null, removeMethod, operateObjectArguments, null, null); + caffeineStringInterceptor.handleMethodException(null, removeMethod, operateObjectArguments, null, exception); + caffeineStringInterceptor.afterMethod(null, removeMethod, operateObjectArguments, null, null); + List traceSegments = segmentStorage.getTraceSegments(); + Assert.assertThat(traceSegments.size(), is(1)); + } + + @Test + public void testClearMethodSuccess() throws Throwable { + caffeineStringInterceptor.beforeMethod(null, cleanMethod, null, null, null); + caffeineStringInterceptor.handleMethodException(null, cleanMethod, null, null, exception); + caffeineStringInterceptor.afterMethod(null, cleanMethod, null, null, null); + List traceSegments = segmentStorage.getTraceSegments(); + Assert.assertThat(traceSegments.size(), is(1)); + } +} diff --git a/apm-sniffer/optional-plugins/caffeine-3.x-plugin/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker b/apm-sniffer/optional-plugins/caffeine-3.x-plugin/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker new file mode 100644 index 0000000000..1f0955d450 --- /dev/null +++ b/apm-sniffer/optional-plugins/caffeine-3.x-plugin/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker @@ -0,0 +1 @@ +mock-maker-inline diff --git a/apm-sniffer/optional-plugins/customize-enhance-plugin/pom.xml b/apm-sniffer/optional-plugins/customize-enhance-plugin/pom.xml index 60bb7c115e..fb100e0d94 100644 --- a/apm-sniffer/optional-plugins/customize-enhance-plugin/pom.xml +++ b/apm-sniffer/optional-plugins/customize-enhance-plugin/pom.xml @@ -21,7 +21,7 @@ org.apache.skywalking optional-plugins - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/optional-plugins/customize-enhance-plugin/src/main/java/org/apache/skywalking/apm/plugin/customize/conf/CustomizeConfiguration.java b/apm-sniffer/optional-plugins/customize-enhance-plugin/src/main/java/org/apache/skywalking/apm/plugin/customize/conf/CustomizeConfiguration.java index ba48a9280e..ba3dd5f843 100644 --- a/apm-sniffer/optional-plugins/customize-enhance-plugin/src/main/java/org/apache/skywalking/apm/plugin/customize/conf/CustomizeConfiguration.java +++ b/apm-sniffer/optional-plugins/customize-enhance-plugin/src/main/java/org/apache/skywalking/apm/plugin/customize/conf/CustomizeConfiguration.java @@ -27,6 +27,7 @@ import java.util.Map; import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.locks.ReentrantLock; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; @@ -67,6 +68,8 @@ public enum CustomizeConfiguration { private static final Map CONTEXT_ENHANCE_CLASSES = new HashMap<>(); private static final AtomicBoolean LOAD_FOR_CONFIGURATION = new AtomicBoolean(false); + private static volatile List> RESOLVED_FILE_CONFIGURATIONS; + private static final ReentrantLock RESOLVED_FILE_LOCK = new ReentrantLock(); /** * The loadForEnhance method is resolver configuration file, and parse it */ @@ -107,13 +110,26 @@ public synchronized void loadForConfiguration() { * @throws SAXException link {@link SAXException} */ private List> resolver() throws ParserConfigurationException, IOException, SAXException { - List> customizeMethods = new ArrayList>(); - File file = new File(CustomizePluginConfig.Plugin.Customize.ENHANCE_FILE); - if (file.exists() && file.isFile()) { - NodeList classNodeList = resolverFileClassDesc(file); - resolverClassNodeList(classNodeList, customizeMethods); + if (RESOLVED_FILE_CONFIGURATIONS != null) { + return RESOLVED_FILE_CONFIGURATIONS; } - return customizeMethods; + + RESOLVED_FILE_LOCK.lock(); + try { + if (RESOLVED_FILE_CONFIGURATIONS != null) { + return RESOLVED_FILE_CONFIGURATIONS; + } + List> customizeMethods = new ArrayList>(); + File file = new File(CustomizePluginConfig.Plugin.Customize.ENHANCE_FILE); + if (file.exists() && file.isFile()) { + NodeList classNodeList = resolverFileClassDesc(file); + resolverClassNodeList(classNodeList, customizeMethods); + } + RESOLVED_FILE_CONFIGURATIONS = customizeMethods; + } finally { + RESOLVED_FILE_LOCK.unlock(); + } + return RESOLVED_FILE_CONFIGURATIONS; } /** diff --git a/apm-sniffer/optional-plugins/ehcache-2.x-plugin/pom.xml b/apm-sniffer/optional-plugins/ehcache-2.x-plugin/pom.xml index 13941d9d08..60906ea1df 100644 --- a/apm-sniffer/optional-plugins/ehcache-2.x-plugin/pom.xml +++ b/apm-sniffer/optional-plugins/ehcache-2.x-plugin/pom.xml @@ -21,7 +21,7 @@ org.apache.skywalking optional-plugins - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/optional-plugins/fastjson-1.2.x-plugin/pom.xml b/apm-sniffer/optional-plugins/fastjson-1.2.x-plugin/pom.xml index 5793a15788..fd47548245 100644 --- a/apm-sniffer/optional-plugins/fastjson-1.2.x-plugin/pom.xml +++ b/apm-sniffer/optional-plugins/fastjson-1.2.x-plugin/pom.xml @@ -22,7 +22,7 @@ org.apache.skywalking optional-plugins - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT apm-fastjson-1.x-plugin diff --git a/apm-sniffer/optional-plugins/gson-2.8.x-plugin/pom.xml b/apm-sniffer/optional-plugins/gson-2.8.x-plugin/pom.xml index 3c45a2e5ce..3cacbd71f0 100644 --- a/apm-sniffer/optional-plugins/gson-2.8.x-plugin/pom.xml +++ b/apm-sniffer/optional-plugins/gson-2.8.x-plugin/pom.xml @@ -22,7 +22,7 @@ org.apache.skywalking optional-plugins - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT apm-gson-2.x-plugin diff --git a/apm-sniffer/optional-plugins/guava-cache-plugin/pom.xml b/apm-sniffer/optional-plugins/guava-cache-plugin/pom.xml index 54b4cf9a53..4b678e32ed 100644 --- a/apm-sniffer/optional-plugins/guava-cache-plugin/pom.xml +++ b/apm-sniffer/optional-plugins/guava-cache-plugin/pom.xml @@ -21,7 +21,7 @@ org.apache.skywalking optional-plugins - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT apm-guava-cache-plugin diff --git a/apm-sniffer/optional-plugins/jackson-2.x-plugin/pom.xml b/apm-sniffer/optional-plugins/jackson-2.x-plugin/pom.xml index dda756c727..6cc5017d9c 100644 --- a/apm-sniffer/optional-plugins/jackson-2.x-plugin/pom.xml +++ b/apm-sniffer/optional-plugins/jackson-2.x-plugin/pom.xml @@ -22,7 +22,7 @@ org.apache.skywalking optional-plugins - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT apm-jackson-2.x-plugin diff --git a/apm-sniffer/optional-plugins/kotlin-coroutine-plugin/pom.xml b/apm-sniffer/optional-plugins/kotlin-coroutine-plugin/pom.xml index a674607cf2..ab831b6c1b 100644 --- a/apm-sniffer/optional-plugins/kotlin-coroutine-plugin/pom.xml +++ b/apm-sniffer/optional-plugins/kotlin-coroutine-plugin/pom.xml @@ -22,7 +22,7 @@ org.apache.skywalking optional-plugins - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT apm-kotlin-coroutine-plugin diff --git a/apm-sniffer/optional-plugins/mybatis-3.x-plugin/pom.xml b/apm-sniffer/optional-plugins/mybatis-3.x-plugin/pom.xml index e1aacd4df9..7da71f0c1c 100644 --- a/apm-sniffer/optional-plugins/mybatis-3.x-plugin/pom.xml +++ b/apm-sniffer/optional-plugins/mybatis-3.x-plugin/pom.xml @@ -22,7 +22,7 @@ org.apache.skywalking optional-plugins - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT apm-mybatis-3.x-plugin diff --git a/apm-sniffer/optional-plugins/nacos-client-2.x-plugin/pom.xml b/apm-sniffer/optional-plugins/nacos-client-2.x-plugin/pom.xml index 994c32e5d1..7f5ea9441e 100644 --- a/apm-sniffer/optional-plugins/nacos-client-2.x-plugin/pom.xml +++ b/apm-sniffer/optional-plugins/nacos-client-2.x-plugin/pom.xml @@ -21,7 +21,7 @@ org.apache.skywalking optional-plugins - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/optional-plugins/netty-http-4.1.x-plugin/pom.xml b/apm-sniffer/optional-plugins/netty-http-4.1.x-plugin/pom.xml index 1d05a2cd07..b8313a57c7 100644 --- a/apm-sniffer/optional-plugins/netty-http-4.1.x-plugin/pom.xml +++ b/apm-sniffer/optional-plugins/netty-http-4.1.x-plugin/pom.xml @@ -21,7 +21,7 @@ org.apache.skywalking optional-plugins - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 @@ -30,14 +30,12 @@ UTF-8 - 4.1.51.Final io.netty netty-all - ${netty.version} provided diff --git a/apm-sniffer/optional-plugins/netty-http-4.1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/netty/http/handler/NettyHttpRequestEncoderTracingHandler.java b/apm-sniffer/optional-plugins/netty-http-4.1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/netty/http/handler/NettyHttpRequestEncoderTracingHandler.java index bb34adbaac..38c8518fb3 100644 --- a/apm-sniffer/optional-plugins/netty-http-4.1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/netty/http/handler/NettyHttpRequestEncoderTracingHandler.java +++ b/apm-sniffer/optional-plugins/netty-http-4.1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/netty/http/handler/NettyHttpRequestEncoderTracingHandler.java @@ -80,7 +80,6 @@ public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) InetSocketAddress address = (InetSocketAddress) ctx.channel().remoteAddress(); String peer = address.getHostString() + ":" + address.getPort(); String url = peer + uri; - String method = request.method().toString(); ContextCarrier contextCarrier = new ContextCarrier(); AbstractSpan span = ContextManager.createExitSpan(NettyConstants.NETTY_HTTP_OPERATION_PREFIX + uri, contextCarrier, peer); diff --git a/apm-sniffer/optional-plugins/optional-spring-plugins/mvc-annotation-6.x-plugin/pom.xml b/apm-sniffer/optional-plugins/optional-spring-plugins/mvc-annotation-6.x-plugin/pom.xml index 9323c57f16..a2399d0bc6 100644 --- a/apm-sniffer/optional-plugins/optional-spring-plugins/mvc-annotation-6.x-plugin/pom.xml +++ b/apm-sniffer/optional-plugins/optional-spring-plugins/mvc-annotation-6.x-plugin/pom.xml @@ -19,7 +19,7 @@ optional-spring-plugins org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-2.0.x-plugin/pom.xml b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-2.0.x-plugin/pom.xml index 77f5a9c5ae..b2963c4c23 100644 --- a/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-2.0.x-plugin/pom.xml +++ b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-2.0.x-plugin/pom.xml @@ -23,7 +23,7 @@ org.apache.skywalking optional-spring-cloud - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT apm-spring-cloud-gateway-2.0.x-plugin @@ -61,7 +61,6 @@ org.apache.maven.plugins maven-shade-plugin - 3.2.4 package diff --git a/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-2.0.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v20x/GatewayFilterInterceptor.java b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-2.0.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v20x/GatewayFilterInterceptor.java new file mode 100644 index 0000000000..b5174773c3 --- /dev/null +++ b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-2.0.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v20x/GatewayFilterInterceptor.java @@ -0,0 +1,94 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.skywalking.apm.plugin.spring.cloud.gateway.v20x; + +import org.apache.skywalking.apm.agent.core.context.ContextManager; +import org.apache.skywalking.apm.agent.core.context.ContextSnapshot; +import org.apache.skywalking.apm.agent.core.context.trace.AbstractSpan; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceMethodsAroundInterceptor; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult; +import org.springframework.web.server.ServerWebExchange; +import org.springframework.web.server.ServerWebExchangeDecorator; +import org.springframework.web.server.adapter.DefaultServerWebExchange; + +import java.lang.reflect.Method; +import java.util.concurrent.atomic.AtomicInteger; + +import static org.apache.skywalking.apm.network.trace.component.ComponentsDefine.SPRING_CLOUD_GATEWAY; + +public class GatewayFilterInterceptor implements InstanceMethodsAroundInterceptor { + + private static final ThreadLocal STACK_DEEP = ThreadLocal.withInitial(() -> new AtomicInteger(0)); + + @Override + public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, + MethodInterceptResult result) throws Throwable { + if (isEntry()) { + ServerWebExchange exchange = (ServerWebExchange) allArguments[0]; + + EnhancedInstance enhancedInstance = getInstance(exchange); + + AbstractSpan span = ContextManager.createLocalSpan("SpringCloudGateway/GatewayFilter"); + if (enhancedInstance != null && enhancedInstance.getSkyWalkingDynamicField() != null) { + ContextManager.continued((ContextSnapshot) enhancedInstance.getSkyWalkingDynamicField()); + } + span.setComponent(SPRING_CLOUD_GATEWAY); + } + } + + public static EnhancedInstance getInstance(Object o) { + EnhancedInstance instance = null; + if (o instanceof DefaultServerWebExchange) { + instance = (EnhancedInstance) o; + } else if (o instanceof ServerWebExchangeDecorator) { + ServerWebExchange delegate = ((ServerWebExchangeDecorator) o).getDelegate(); + return getInstance(delegate); + } + return instance; + } + + @Override + public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, + Object ret) throws Throwable { + if (isExit()) { + if (ContextManager.isActive()) { + ContextManager.stopSpan(); + } + } + return ret; + } + + @Override + public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments, + Class[] argumentsTypes, Throwable t) { + ContextManager.activeSpan().log(t); + } + + private boolean isEntry() { + return STACK_DEEP.get().getAndIncrement() == 0; + } + + private boolean isExit() { + boolean isExit = STACK_DEEP.get().decrementAndGet() == 0; + if (isExit) { + STACK_DEEP.remove(); + } + return isExit; + } +} diff --git a/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-2.0.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v20x/define/AbstractGateway200EnhancePluginDefine.java b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-2.0.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v20x/define/AbstractGateway200EnhancePluginDefine.java index baeb619eca..e260b8813c 100644 --- a/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-2.0.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v20x/define/AbstractGateway200EnhancePluginDefine.java +++ b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-2.0.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v20x/define/AbstractGateway200EnhancePluginDefine.java @@ -24,7 +24,7 @@ public abstract class AbstractGateway200EnhancePluginDefine extends ClassInstanc @Override protected String[] witnessClasses() { return new String[] { - "org.springframework.cloud.gateway.config.GatewayAutoConfiguration$1" + "org.springframework.cloud.gateway.config.GatewayAutoConfiguration" }; } } diff --git a/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-2.0.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v20x/define/AbstractGateway200EnhancePluginDefineV2.java b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-2.0.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v20x/define/AbstractGateway200EnhancePluginDefineV2.java index 1f07c0ba89..c42e2df60d 100644 --- a/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-2.0.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v20x/define/AbstractGateway200EnhancePluginDefineV2.java +++ b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-2.0.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v20x/define/AbstractGateway200EnhancePluginDefineV2.java @@ -24,7 +24,7 @@ public abstract class AbstractGateway200EnhancePluginDefineV2 extends ClassInsta @Override protected String[] witnessClasses() { return new String[] { - "org.springframework.cloud.gateway.config.GatewayAutoConfiguration$1" + "org.springframework.cloud.gateway.config.GatewayAutoConfiguration" }; } } diff --git a/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-2.0.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v20x/define/Constants.java b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-2.0.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v20x/define/Constants.java index 450699c959..f20a1c22a6 100644 --- a/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-2.0.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v20x/define/Constants.java +++ b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-2.0.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v20x/define/Constants.java @@ -29,4 +29,8 @@ public interface Constants { // HttpClientRequest String INTERCEPT_CLASS_HTTP_CLIENT_REQUEST = "reactor.ipc.netty.http.client.HttpClientRequest"; String HTTPCLIENT_REQUEST_HEADERS_INTERCEPTOR = "org.apache.skywalking.apm.plugin.spring.cloud.gateway.v20x.HttpclientRequestHeadersInterceptor"; + + // GatewayFilter + String INTERCEPT_CLASS_GATEWAY_FILTER = "org.springframework.cloud.gateway.filter.GatewayFilter"; + String GATEWAY_FILTER_INTERCEPTOR = "org.apache.skywalking.apm.plugin.spring.cloud.gateway.v20x.GatewayFilterInterceptor"; } diff --git a/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-2.0.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v20x/define/DispatcherHandlerInstrumentation.java b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-2.0.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v20x/define/DispatcherHandlerInstrumentation.java index be9f8668ab..1188ae1804 100644 --- a/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-2.0.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v20x/define/DispatcherHandlerInstrumentation.java +++ b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-2.0.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v20x/define/DispatcherHandlerInstrumentation.java @@ -23,7 +23,7 @@ public class DispatcherHandlerInstrumentation extends org.apache.skywalking.apm. @Override protected String[] witnessClasses() { return new String[] { - "org.springframework.cloud.gateway.config.GatewayAutoConfiguration$1" + "org.springframework.cloud.gateway.config.GatewayAutoConfiguration" }; } } diff --git a/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-2.0.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v20x/define/GatewayFilterInstrumentation.java b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-2.0.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v20x/define/GatewayFilterInstrumentation.java new file mode 100644 index 0000000000..5435af8a08 --- /dev/null +++ b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-2.0.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v20x/define/GatewayFilterInstrumentation.java @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.skywalking.apm.plugin.spring.cloud.gateway.v20x.define; + +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.matcher.ElementMatcher; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch; +import org.apache.skywalking.apm.agent.core.plugin.match.HierarchyMatch; + +import static net.bytebuddy.matcher.ElementMatchers.named; +import static org.apache.skywalking.apm.agent.core.plugin.bytebuddy.ArgumentTypeNameMatch.takesArgumentWithType; + +public class GatewayFilterInstrumentation extends AbstractGateway200EnhancePluginDefine { + + @Override + protected ClassMatch enhanceClass() { + return HierarchyMatch.byHierarchyMatch(Constants.INTERCEPT_CLASS_GATEWAY_FILTER); + } + + @Override + public ConstructorInterceptPoint[] getConstructorsInterceptPoints() { + return new ConstructorInterceptPoint[0]; + } + + @Override + public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() { + return new InstanceMethodsInterceptPoint[] { + new InstanceMethodsInterceptPoint() { + @Override + public ElementMatcher getMethodsMatcher() { + return named("filter").and( + takesArgumentWithType(0, "org.springframework.web.server.ServerWebExchange")); + } + + @Override + public String getMethodsInterceptor() { + return Constants.GATEWAY_FILTER_INTERCEPTOR; + } + + @Override + public boolean isOverrideArgs() { + return false; + } + } + }; + } +} diff --git a/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-2.0.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v20x/define/ServerWebExchangeInstrumentation.java b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-2.0.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v20x/define/ServerWebExchangeInstrumentation.java index 029172d69d..bb2af27b54 100644 --- a/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-2.0.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v20x/define/ServerWebExchangeInstrumentation.java +++ b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-2.0.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v20x/define/ServerWebExchangeInstrumentation.java @@ -23,7 +23,7 @@ public class ServerWebExchangeInstrumentation extends org.apache.skywalking.apm. @Override protected String[] witnessClasses() { return new String[] { - "org.springframework.cloud.gateway.config.GatewayAutoConfiguration$1" + "org.springframework.cloud.gateway.config.GatewayAutoConfiguration" }; } } diff --git a/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-2.0.x-plugin/src/main/resources/skywalking-plugin.def b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-2.0.x-plugin/src/main/resources/skywalking-plugin.def index 4bd0c3f419..0e4bf641bd 100644 --- a/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-2.0.x-plugin/src/main/resources/skywalking-plugin.def +++ b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-2.0.x-plugin/src/main/resources/skywalking-plugin.def @@ -18,4 +18,5 @@ spring-cloud-gateway-2.0.x=org.apache.skywalking.apm.plugin.spring.cloud.gateway spring-cloud-gateway-2.0.x=org.apache.skywalking.apm.plugin.spring.cloud.gateway.v20x.define.HttpClientInstrumentation spring-cloud-gateway-2.0.x=org.apache.skywalking.apm.plugin.spring.cloud.gateway.v20x.define.HttpClientRequestInstrumentation spring-cloud-gateway-2.0.x=org.apache.skywalking.apm.plugin.spring.cloud.gateway.v20x.define.ServerWebExchangeInstrumentation -spring-cloud-gateway-2.0.x=org.apache.skywalking.apm.plugin.spring.cloud.gateway.v20x.define.DispatcherHandlerInstrumentation \ No newline at end of file +spring-cloud-gateway-2.0.x=org.apache.skywalking.apm.plugin.spring.cloud.gateway.v20x.define.DispatcherHandlerInstrumentation +spring-cloud-gateway-2.0.x=org.apache.skywalking.apm.plugin.spring.cloud.gateway.v20x.define.GatewayFilterInstrumentation \ No newline at end of file diff --git a/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-2.0.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v20x/GatewayFilterInterceptorTest.java b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-2.0.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v20x/GatewayFilterInterceptorTest.java new file mode 100644 index 0000000000..3cd7bc8705 --- /dev/null +++ b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-2.0.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v20x/GatewayFilterInterceptorTest.java @@ -0,0 +1,333 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.spring.cloud.gateway.v20x; + +import org.apache.skywalking.apm.agent.core.context.ContextManager; +import org.apache.skywalking.apm.agent.core.context.ContextSnapshot; +import org.apache.skywalking.apm.agent.core.context.trace.AbstractSpan; +import org.apache.skywalking.apm.agent.core.context.trace.AbstractTracingSpan; +import org.apache.skywalking.apm.agent.core.context.trace.SpanLayer; +import org.apache.skywalking.apm.agent.core.context.trace.TraceSegment; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; +import org.apache.skywalking.apm.agent.test.helper.SegmentHelper; + +import org.apache.skywalking.apm.agent.test.tools.AgentServiceRule; +import org.apache.skywalking.apm.agent.test.tools.SegmentStorage; +import org.apache.skywalking.apm.agent.test.tools.SegmentStoragePoint; +import org.apache.skywalking.apm.agent.test.tools.SpanAssert; +import org.apache.skywalking.apm.agent.test.tools.TracingSegmentRunner; +import org.apache.skywalking.apm.network.trace.component.ComponentsDefine; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; +import org.springframework.context.ApplicationContext; +import org.springframework.context.i18n.LocaleContext; +import org.springframework.http.codec.multipart.Part; +import org.springframework.http.server.reactive.ServerHttpRequest; +import org.springframework.http.server.reactive.ServerHttpResponse; +import org.springframework.util.MultiValueMap; +import org.springframework.web.server.ServerWebExchange; +import org.springframework.web.server.WebSession; +import reactor.core.publisher.Mono; + +import java.security.Principal; +import java.time.Instant; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Function; + +@RunWith(TracingSegmentRunner.class) +public class GatewayFilterInterceptorTest { + + private static final String GATEWAY_FILTER_INTERCEPTOR_LOCAL_SPAN_OPERATION_NAME = "SpringCloudGateway/GatewayFilter"; + + private static class ServerWebExchangeEnhancedInstance implements ServerWebExchange, EnhancedInstance { + private ContextSnapshot snapshot; + Map attributes = new HashMap<>(); + + @Override + public Object getSkyWalkingDynamicField() { + return snapshot; + } + + @Override + public void setSkyWalkingDynamicField(Object value) { + this.snapshot = (ContextSnapshot) value; + } + + @Override + public ServerHttpRequest getRequest() { + return null; + } + + @Override + public ServerHttpResponse getResponse() { + return null; + } + + @Override + public Map getAttributes() { + return attributes; + } + + @Override + public Mono getSession() { + return null; + } + + @Override + public Mono getPrincipal() { + return null; + } + + @Override + public Mono> getFormData() { + return null; + } + + @Override + public Mono> getMultipartData() { + return null; + } + + @Override + public LocaleContext getLocaleContext() { + return null; + } + + @Override + public ApplicationContext getApplicationContext() { + return null; + } + + @Override + public boolean isNotModified() { + return false; + } + + @Override + public boolean checkNotModified(Instant instant) { + return false; + } + + @Override + public boolean checkNotModified(String s) { + return false; + } + + @Override + public boolean checkNotModified(String s, Instant instant) { + return false; + } + + @Override + public String transformUrl(String s) { + return null; + } + + @Override + public void addUrlTransformer(Function function) { + + } + + @Override + public String getLogPrefix() { + return null; + } + } + + private final ServerWebExchangeEnhancedInstance enhancedInstance = new ServerWebExchangeEnhancedInstance(); + private final GatewayFilterInterceptor interceptor = new GatewayFilterInterceptor(); + @Rule + public AgentServiceRule serviceRule = new AgentServiceRule(); + @Rule + public MockitoRule rule = MockitoJUnit.rule(); + + @SegmentStoragePoint + private SegmentStorage segmentStorage; + + @Before + public void setUp() throws Exception { + } + + private final ServerWebExchange exchange = new ServerWebExchange() { + Map attributes = new HashMap<>(); + @Override + public ServerHttpRequest getRequest() { + return null; + } + + @Override + public ServerHttpResponse getResponse() { + return null; + } + + @Override + public Map getAttributes() { + return attributes; + } + + @Override + public Mono getSession() { + return null; + } + + @Override + public Mono getPrincipal() { + return null; + } + + @Override + public Mono> getFormData() { + return null; + } + + @Override + public Mono> getMultipartData() { + return null; + } + + @Override + public LocaleContext getLocaleContext() { + return null; + } + + @Override + public ApplicationContext getApplicationContext() { + return null; + } + + @Override + public boolean isNotModified() { + return false; + } + + @Override + public boolean checkNotModified(Instant instant) { + return false; + } + + @Override + public boolean checkNotModified(String s) { + return false; + } + + @Override + public boolean checkNotModified(String s, Instant instant) { + return false; + } + + @Override + public String transformUrl(String s) { + return null; + } + + @Override + public void addUrlTransformer(Function function) { + + } + + @Override + public String getLogPrefix() { + return null; + } + }; + + @Test + public void testInterceptOnlyOnce() throws Throwable { + interceptor.beforeMethod(null, null, new Object[]{exchange}, null, null); + Assert.assertTrue(ContextManager.isActive()); + AbstractSpan activeSpan = ContextManager.activeSpan(); + Assert.assertTrue(activeSpan instanceof AbstractTracingSpan); + Assert.assertEquals(activeSpan.getOperationName(), GATEWAY_FILTER_INTERCEPTOR_LOCAL_SPAN_OPERATION_NAME); + + interceptor.afterMethod(null, null, null, null, null); + Assert.assertFalse(ContextManager.isActive()); + final List traceSegments = segmentStorage.getTraceSegments(); + Assert.assertEquals(traceSegments.size(), 1); + final List spans = SegmentHelper.getSpans(traceSegments.get(0)); + Assert.assertNotNull(spans); + Assert.assertEquals(spans.size(), 1); + SpanAssert.assertComponent(spans.get(0), ComponentsDefine.SPRING_CLOUD_GATEWAY); + + } + + @Test + public void testNestedInterception() throws Throwable { + interceptor.beforeMethod(null, null, new Object[]{enhancedInstance}, null, null); + Assert.assertTrue(ContextManager.isActive()); + + interceptor.beforeMethod(null, null, new Object[]{enhancedInstance}, null, null); + Assert.assertTrue(ContextManager.isActive()); + + interceptor.afterMethod(null, null, null, null, null); + Assert.assertTrue(ContextManager.isActive()); + + interceptor.afterMethod(null, null, null, null, null); + Assert.assertFalse(ContextManager.isActive()); + + final List traceSegments = segmentStorage.getTraceSegments(); + Assert.assertEquals(traceSegments.size(), 1); + final List spans = SegmentHelper.getSpans(traceSegments.get(0)); + Assert.assertNotNull(spans); + Assert.assertEquals(spans.size(), 1); + SpanAssert.assertComponent(spans.get(0), ComponentsDefine.SPRING_CLOUD_GATEWAY); + } + + @Test + public void testWithNullDynamicField() throws Throwable { + interceptor.beforeMethod(null, null, new Object[]{enhancedInstance}, null, null); + interceptor.afterMethod(null, null, null, null, null); + // no more need this, span was stopped at interceptor#afterMethod + // ContextManager.stopSpan(); + final List traceSegments = segmentStorage.getTraceSegments(); + Assert.assertEquals(traceSegments.size(), 1); + final List spans = SegmentHelper.getSpans(traceSegments.get(0)); + Assert.assertNotNull(spans); + Assert.assertEquals(spans.size(), 1); + SpanAssert.assertComponent(spans.get(0), ComponentsDefine.SPRING_CLOUD_GATEWAY); + } + + @Test + public void testWithContextSnapshot() throws Throwable { + final AbstractSpan entrySpan = ContextManager.createEntrySpan("/get", null); + SpanLayer.asHttp(entrySpan); + entrySpan.setComponent(ComponentsDefine.SPRING_WEBFLUX); + enhancedInstance.setSkyWalkingDynamicField(ContextManager.capture()); + interceptor.beforeMethod(null, null, new Object[]{enhancedInstance}, null, null); + interceptor.afterMethod(null, null, null, null, null); + // no more need this, span was stopped at interceptor#afterMethod + // ContextManager.stopSpan(); + ContextManager.stopSpan(entrySpan); + final List traceSegments = segmentStorage.getTraceSegments(); + Assert.assertEquals(traceSegments.size(), 1); + final List spans = SegmentHelper.getSpans(traceSegments.get(0)); + Assert.assertNotNull(spans); + Assert.assertEquals(spans.size(), 2); + SpanAssert.assertComponent(spans.get(0), ComponentsDefine.SPRING_CLOUD_GATEWAY); + SpanAssert.assertComponent(spans.get(1), ComponentsDefine.SPRING_WEBFLUX); + SpanAssert.assertLayer(spans.get(1), SpanLayer.HTTP); + } + +} diff --git a/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-2.1.x-plugin/pom.xml b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-2.1.x-plugin/pom.xml index bfb4ad8979..c9a0dfb040 100644 --- a/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-2.1.x-plugin/pom.xml +++ b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-2.1.x-plugin/pom.xml @@ -23,7 +23,7 @@ org.apache.skywalking optional-spring-cloud - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT apm-spring-cloud-gateway-2.1.x-plugin @@ -61,7 +61,6 @@ org.apache.maven.plugins maven-shade-plugin - 3.2.4 package diff --git a/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-2.1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v21x/GatewayFilterInterceptor.java b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-2.1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v21x/GatewayFilterInterceptor.java new file mode 100644 index 0000000000..18b93a08d1 --- /dev/null +++ b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-2.1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v21x/GatewayFilterInterceptor.java @@ -0,0 +1,94 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.skywalking.apm.plugin.spring.cloud.gateway.v21x; + +import org.apache.skywalking.apm.agent.core.context.ContextManager; +import org.apache.skywalking.apm.agent.core.context.ContextSnapshot; +import org.apache.skywalking.apm.agent.core.context.trace.AbstractSpan; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceMethodsAroundInterceptor; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult; +import org.springframework.web.server.ServerWebExchange; +import org.springframework.web.server.ServerWebExchangeDecorator; +import org.springframework.web.server.adapter.DefaultServerWebExchange; + +import java.lang.reflect.Method; +import java.util.concurrent.atomic.AtomicInteger; + +import static org.apache.skywalking.apm.network.trace.component.ComponentsDefine.SPRING_CLOUD_GATEWAY; + +public class GatewayFilterInterceptor implements InstanceMethodsAroundInterceptor { + + private static final ThreadLocal STACK_DEEP = ThreadLocal.withInitial(() -> new AtomicInteger(0)); + + @Override + public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, + MethodInterceptResult result) throws Throwable { + if (isEntry()) { + ServerWebExchange exchange = (ServerWebExchange) allArguments[0]; + + EnhancedInstance enhancedInstance = getInstance(exchange); + + AbstractSpan span = ContextManager.createLocalSpan("SpringCloudGateway/GatewayFilter"); + if (enhancedInstance != null && enhancedInstance.getSkyWalkingDynamicField() != null) { + ContextManager.continued((ContextSnapshot) enhancedInstance.getSkyWalkingDynamicField()); + } + span.setComponent(SPRING_CLOUD_GATEWAY); + } + } + + public static EnhancedInstance getInstance(Object o) { + EnhancedInstance instance = null; + if (o instanceof DefaultServerWebExchange) { + instance = (EnhancedInstance) o; + } else if (o instanceof ServerWebExchangeDecorator) { + ServerWebExchange delegate = ((ServerWebExchangeDecorator) o).getDelegate(); + return getInstance(delegate); + } + return instance; + } + + @Override + public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, + Object ret) throws Throwable { + if (isExit()) { + if (ContextManager.isActive()) { + ContextManager.stopSpan(); + } + } + return ret; + } + + @Override + public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments, + Class[] argumentsTypes, Throwable t) { + ContextManager.activeSpan().log(t); + } + + private boolean isEntry() { + return STACK_DEEP.get().getAndIncrement() == 0; + } + + private boolean isExit() { + boolean isExit = STACK_DEEP.get().decrementAndGet() == 0; + if (isExit) { + STACK_DEEP.remove(); + } + return isExit; + } +} diff --git a/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-2.1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v21x/NettyRoutingFilterInterceptor.java b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-2.1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v21x/NettyRoutingFilterInterceptor.java index 578352e8e8..6e7dba5726 100644 --- a/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-2.1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v21x/NettyRoutingFilterInterceptor.java +++ b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-2.1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v21x/NettyRoutingFilterInterceptor.java @@ -74,6 +74,10 @@ public static EnhancedInstance getInstance(Object o) { @Override public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, Object ret) throws Throwable { + if (ContextManager.isActive()) { + // if HttpClientFinalizerSendInterceptor does not invoke, we will stop the span there to avoid context leak. + ContextManager.stopSpan(); + } return ret; } diff --git a/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-2.1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v21x/define/Constants.java b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-2.1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v21x/define/Constants.java index 1dbb09ff16..5c88626779 100644 --- a/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-2.1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v21x/define/Constants.java +++ b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-2.1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v21x/define/Constants.java @@ -33,4 +33,7 @@ public interface Constants { String INTERCEPT_CLASS_TCP_CLIENT = "reactor.netty.tcp.TcpClient"; String TCP_CLIENT_CONSTRUCTOR_INTERCEPTOR = "org.apache.skywalking.apm.plugin.spring.cloud.gateway.v21x.TcpClientConstructorInterceptor"; + // GatewayFilter + String INTERCEPT_CLASS_GATEWAY_FILTER = "org.springframework.cloud.gateway.filter.GatewayFilter"; + String GATEWAY_FILTER_INTERCEPTOR = "org.apache.skywalking.apm.plugin.spring.cloud.gateway.v21x.GatewayFilterInterceptor"; } diff --git a/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-2.1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v21x/define/GatewayFilterInstrumentation.java b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-2.1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v21x/define/GatewayFilterInstrumentation.java new file mode 100644 index 0000000000..1031478c4a --- /dev/null +++ b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-2.1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v21x/define/GatewayFilterInstrumentation.java @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.skywalking.apm.plugin.spring.cloud.gateway.v21x.define; + +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.matcher.ElementMatcher; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch; +import org.apache.skywalking.apm.agent.core.plugin.match.HierarchyMatch; + +import static net.bytebuddy.matcher.ElementMatchers.named; +import static org.apache.skywalking.apm.agent.core.plugin.bytebuddy.ArgumentTypeNameMatch.takesArgumentWithType; + +public class GatewayFilterInstrumentation extends AbstractGateway210EnhancePluginDefine { + + @Override + protected ClassMatch enhanceClass() { + return HierarchyMatch.byHierarchyMatch(Constants.INTERCEPT_CLASS_GATEWAY_FILTER); + } + + @Override + public ConstructorInterceptPoint[] getConstructorsInterceptPoints() { + return new ConstructorInterceptPoint[0]; + } + + @Override + public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() { + return new InstanceMethodsInterceptPoint[] { + new InstanceMethodsInterceptPoint() { + @Override + public ElementMatcher getMethodsMatcher() { + return named("filter").and( + takesArgumentWithType(0, "org.springframework.web.server.ServerWebExchange")); + } + + @Override + public String getMethodsInterceptor() { + return Constants.GATEWAY_FILTER_INTERCEPTOR; + } + + @Override + public boolean isOverrideArgs() { + return false; + } + } + }; + } +} diff --git a/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-2.1.x-plugin/src/main/resources/skywalking-plugin.def b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-2.1.x-plugin/src/main/resources/skywalking-plugin.def index 9929db9240..8ce727bb5d 100644 --- a/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-2.1.x-plugin/src/main/resources/skywalking-plugin.def +++ b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-2.1.x-plugin/src/main/resources/skywalking-plugin.def @@ -19,3 +19,4 @@ spring-cloud-gateway-2.1.x=org.apache.skywalking.apm.plugin.spring.cloud.gateway spring-cloud-gateway-2.1.x=org.apache.skywalking.apm.plugin.spring.cloud.gateway.v21x.define.TcpClientInstrumentation spring-cloud-gateway-2.1.x=org.apache.skywalking.apm.plugin.spring.cloud.gateway.v21x.define.ServerWebExchangeInstrumentation spring-cloud-gateway-2.1.x=org.apache.skywalking.apm.plugin.spring.cloud.gateway.v21x.define.DispatcherHandlerInstrumentation +spring-cloud-gateway-2.1.x=org.apache.skywalking.apm.plugin.spring.cloud.gateway.v21x.define.GatewayFilterInstrumentation diff --git a/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-2.1.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v21x/GatewayFilterInterceptorTest.java b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-2.1.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v21x/GatewayFilterInterceptorTest.java new file mode 100644 index 0000000000..c2250e3beb --- /dev/null +++ b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-2.1.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v21x/GatewayFilterInterceptorTest.java @@ -0,0 +1,332 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.spring.cloud.gateway.v21x; + +import org.apache.skywalking.apm.agent.core.context.ContextManager; +import org.apache.skywalking.apm.agent.core.context.ContextSnapshot; +import org.apache.skywalking.apm.agent.core.context.trace.AbstractSpan; +import org.apache.skywalking.apm.agent.core.context.trace.AbstractTracingSpan; +import org.apache.skywalking.apm.agent.core.context.trace.SpanLayer; +import org.apache.skywalking.apm.agent.core.context.trace.TraceSegment; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; +import org.apache.skywalking.apm.agent.test.helper.SegmentHelper; +import org.apache.skywalking.apm.agent.test.tools.AgentServiceRule; +import org.apache.skywalking.apm.agent.test.tools.SegmentStorage; +import org.apache.skywalking.apm.agent.test.tools.SegmentStoragePoint; +import org.apache.skywalking.apm.agent.test.tools.SpanAssert; +import org.apache.skywalking.apm.agent.test.tools.TracingSegmentRunner; +import org.apache.skywalking.apm.network.trace.component.ComponentsDefine; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; +import org.springframework.context.ApplicationContext; +import org.springframework.context.i18n.LocaleContext; +import org.springframework.http.codec.multipart.Part; +import org.springframework.http.server.reactive.ServerHttpRequest; +import org.springframework.http.server.reactive.ServerHttpResponse; +import org.springframework.util.MultiValueMap; +import org.springframework.web.server.ServerWebExchange; +import org.springframework.web.server.WebSession; +import reactor.core.publisher.Mono; + +import java.security.Principal; +import java.time.Instant; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Function; + +@RunWith(TracingSegmentRunner.class) +public class GatewayFilterInterceptorTest { + + private static final String GATEWAY_FILTER_INTERCEPTOR_LOCAL_SPAN_OPERATION_NAME = "SpringCloudGateway/GatewayFilter"; + + private static class ServerWebExchangeEnhancedInstance implements ServerWebExchange, EnhancedInstance { + private ContextSnapshot snapshot; + Map attributes = new HashMap<>(); + + @Override + public Object getSkyWalkingDynamicField() { + return snapshot; + } + + @Override + public void setSkyWalkingDynamicField(Object value) { + this.snapshot = (ContextSnapshot) value; + } + + @Override + public ServerHttpRequest getRequest() { + return null; + } + + @Override + public ServerHttpResponse getResponse() { + return null; + } + + @Override + public Map getAttributes() { + return attributes; + } + + @Override + public Mono getSession() { + return null; + } + + @Override + public Mono getPrincipal() { + return null; + } + + @Override + public Mono> getFormData() { + return null; + } + + @Override + public Mono> getMultipartData() { + return null; + } + + @Override + public LocaleContext getLocaleContext() { + return null; + } + + @Override + public ApplicationContext getApplicationContext() { + return null; + } + + @Override + public boolean isNotModified() { + return false; + } + + @Override + public boolean checkNotModified(Instant instant) { + return false; + } + + @Override + public boolean checkNotModified(String s) { + return false; + } + + @Override + public boolean checkNotModified(String s, Instant instant) { + return false; + } + + @Override + public String transformUrl(String s) { + return null; + } + + @Override + public void addUrlTransformer(Function function) { + + } + + @Override + public String getLogPrefix() { + return null; + } + } + + private final ServerWebExchangeEnhancedInstance enhancedInstance = new ServerWebExchangeEnhancedInstance(); + private final GatewayFilterInterceptor interceptor = new GatewayFilterInterceptor(); + @Rule + public AgentServiceRule serviceRule = new AgentServiceRule(); + @Rule + public MockitoRule rule = MockitoJUnit.rule(); + + @SegmentStoragePoint + private SegmentStorage segmentStorage; + + @Before + public void setUp() throws Exception { + } + + private final ServerWebExchange exchange = new ServerWebExchange() { + Map attributes = new HashMap<>(); + @Override + public ServerHttpRequest getRequest() { + return null; + } + + @Override + public ServerHttpResponse getResponse() { + return null; + } + + @Override + public Map getAttributes() { + return attributes; + } + + @Override + public Mono getSession() { + return null; + } + + @Override + public Mono getPrincipal() { + return null; + } + + @Override + public Mono> getFormData() { + return null; + } + + @Override + public Mono> getMultipartData() { + return null; + } + + @Override + public LocaleContext getLocaleContext() { + return null; + } + + @Override + public ApplicationContext getApplicationContext() { + return null; + } + + @Override + public boolean isNotModified() { + return false; + } + + @Override + public boolean checkNotModified(Instant instant) { + return false; + } + + @Override + public boolean checkNotModified(String s) { + return false; + } + + @Override + public boolean checkNotModified(String s, Instant instant) { + return false; + } + + @Override + public String transformUrl(String s) { + return null; + } + + @Override + public void addUrlTransformer(Function function) { + + } + + @Override + public String getLogPrefix() { + return null; + } + }; + + @Test + public void testInterceptOnlyOnce() throws Throwable { + interceptor.beforeMethod(null, null, new Object[]{exchange}, null, null); + Assert.assertTrue(ContextManager.isActive()); + AbstractSpan activeSpan = ContextManager.activeSpan(); + Assert.assertTrue(activeSpan instanceof AbstractTracingSpan); + Assert.assertEquals(activeSpan.getOperationName(), GATEWAY_FILTER_INTERCEPTOR_LOCAL_SPAN_OPERATION_NAME); + + interceptor.afterMethod(null, null, null, null, null); + Assert.assertFalse(ContextManager.isActive()); + final List traceSegments = segmentStorage.getTraceSegments(); + Assert.assertEquals(traceSegments.size(), 1); + final List spans = SegmentHelper.getSpans(traceSegments.get(0)); + Assert.assertNotNull(spans); + Assert.assertEquals(spans.size(), 1); + SpanAssert.assertComponent(spans.get(0), ComponentsDefine.SPRING_CLOUD_GATEWAY); + + } + + @Test + public void testNestedInterception() throws Throwable { + interceptor.beforeMethod(null, null, new Object[]{enhancedInstance}, null, null); + Assert.assertTrue(ContextManager.isActive()); + + interceptor.beforeMethod(null, null, new Object[]{enhancedInstance}, null, null); + Assert.assertTrue(ContextManager.isActive()); + + interceptor.afterMethod(null, null, null, null, null); + Assert.assertTrue(ContextManager.isActive()); + + interceptor.afterMethod(null, null, null, null, null); + Assert.assertFalse(ContextManager.isActive()); + + final List traceSegments = segmentStorage.getTraceSegments(); + Assert.assertEquals(traceSegments.size(), 1); + final List spans = SegmentHelper.getSpans(traceSegments.get(0)); + Assert.assertNotNull(spans); + Assert.assertEquals(spans.size(), 1); + SpanAssert.assertComponent(spans.get(0), ComponentsDefine.SPRING_CLOUD_GATEWAY); + } + + @Test + public void testWithNullDynamicField() throws Throwable { + interceptor.beforeMethod(null, null, new Object[]{enhancedInstance}, null, null); + interceptor.afterMethod(null, null, null, null, null); + // no more need this, span was stopped at interceptor#afterMethod + // ContextManager.stopSpan(); + final List traceSegments = segmentStorage.getTraceSegments(); + Assert.assertEquals(traceSegments.size(), 1); + final List spans = SegmentHelper.getSpans(traceSegments.get(0)); + Assert.assertNotNull(spans); + Assert.assertEquals(spans.size(), 1); + SpanAssert.assertComponent(spans.get(0), ComponentsDefine.SPRING_CLOUD_GATEWAY); + } + + @Test + public void testWithContextSnapshot() throws Throwable { + final AbstractSpan entrySpan = ContextManager.createEntrySpan("/get", null); + SpanLayer.asHttp(entrySpan); + entrySpan.setComponent(ComponentsDefine.SPRING_WEBFLUX); + enhancedInstance.setSkyWalkingDynamicField(ContextManager.capture()); + interceptor.beforeMethod(null, null, new Object[]{enhancedInstance}, null, null); + interceptor.afterMethod(null, null, null, null, null); + // no more need this, span was stopped at interceptor#afterMethod + // ContextManager.stopSpan(); + ContextManager.stopSpan(entrySpan); + final List traceSegments = segmentStorage.getTraceSegments(); + Assert.assertEquals(traceSegments.size(), 1); + final List spans = SegmentHelper.getSpans(traceSegments.get(0)); + Assert.assertNotNull(spans); + Assert.assertEquals(spans.size(), 2); + SpanAssert.assertComponent(spans.get(0), ComponentsDefine.SPRING_CLOUD_GATEWAY); + SpanAssert.assertComponent(spans.get(1), ComponentsDefine.SPRING_WEBFLUX); + SpanAssert.assertLayer(spans.get(1), SpanLayer.HTTP); + } + +} diff --git a/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-2.1.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v21x/NettyRoutingFilterInterceptorTest.java b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-2.1.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v21x/NettyRoutingFilterInterceptorTest.java index 43d75b0cee..30bc2d6ce2 100644 --- a/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-2.1.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v21x/NettyRoutingFilterInterceptorTest.java +++ b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-2.1.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v21x/NettyRoutingFilterInterceptorTest.java @@ -21,13 +21,23 @@ import java.security.Principal; import java.time.Instant; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.function.Function; import org.apache.skywalking.apm.agent.core.context.ContextManager; +import org.apache.skywalking.apm.agent.core.context.ContextSnapshot; +import org.apache.skywalking.apm.agent.core.context.trace.AbstractSpan; +import org.apache.skywalking.apm.agent.core.context.trace.AbstractTracingSpan; +import org.apache.skywalking.apm.agent.core.context.trace.SpanLayer; +import org.apache.skywalking.apm.agent.core.context.trace.TraceSegment; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; +import org.apache.skywalking.apm.agent.test.helper.SegmentHelper; import org.apache.skywalking.apm.agent.test.tools.AgentServiceRule; import org.apache.skywalking.apm.agent.test.tools.SegmentStorage; import org.apache.skywalking.apm.agent.test.tools.SegmentStoragePoint; +import org.apache.skywalking.apm.agent.test.tools.SpanAssert; import org.apache.skywalking.apm.agent.test.tools.TracingSegmentRunner; +import org.apache.skywalking.apm.network.trace.component.ComponentsDefine; import org.junit.Assert; import org.junit.Before; import org.junit.Rule; @@ -47,6 +57,103 @@ @RunWith(TracingSegmentRunner.class) public class NettyRoutingFilterInterceptorTest { + + private static class ServerWebExchangeEnhancedInstance implements ServerWebExchange, EnhancedInstance { + private ContextSnapshot snapshot; + Map attributes = new HashMap<>(); + + @Override + public Object getSkyWalkingDynamicField() { + return snapshot; + } + + @Override + public void setSkyWalkingDynamicField(Object value) { + this.snapshot = (ContextSnapshot) value; + } + + @Override + public ServerHttpRequest getRequest() { + return null; + } + + @Override + public ServerHttpResponse getResponse() { + return null; + } + + @Override + public Map getAttributes() { + return attributes; + } + + @Override + public Mono getSession() { + return null; + } + + @Override + public Mono getPrincipal() { + return null; + } + + @Override + public Mono> getFormData() { + return null; + } + + @Override + public Mono> getMultipartData() { + return null; + } + + @Override + public LocaleContext getLocaleContext() { + return null; + } + + @Override + public ApplicationContext getApplicationContext() { + return null; + } + + @Override + public boolean isNotModified() { + return false; + } + + @Override + public boolean checkNotModified(Instant instant) { + return false; + } + + @Override + public boolean checkNotModified(String s) { + return false; + } + + @Override + public boolean checkNotModified(String s, Instant instant) { + return false; + } + + @Override + public String transformUrl(String s) { + return null; + } + + @Override + public void addUrlTransformer(Function function) { + + } + + @Override + public String getLogPrefix() { + return null; + } + } + + private final ServerWebExchangeEnhancedInstance enhancedInstance = new ServerWebExchangeEnhancedInstance(); private final NettyRoutingFilterInterceptor interceptor = new NettyRoutingFilterInterceptor(); @Rule public AgentServiceRule serviceRule = new AgentServiceRule(); @@ -151,12 +258,48 @@ public void testIsTraced() throws Throwable { interceptor.beforeMethod(null, null, new Object[]{exchange}, null, null); interceptor.afterMethod(null, null, null, null, null); Assert.assertEquals(exchange.getAttributes().get(NETTY_ROUTING_FILTER_TRACED_ATTR), true); - Assert.assertNotNull(ContextManager.activeSpan()); + Assert.assertFalse(ContextManager.isActive()); - ContextManager.stopSpan(); + // no more need this, span was stopped at interceptor#afterMethod + // ContextManager.stopSpan(); interceptor.beforeMethod(null, null, new Object[]{exchange}, null, null); interceptor.afterMethod(null, null, null, null, null); Assert.assertEquals(exchange.getAttributes().get(NETTY_ROUTING_FILTER_TRACED_ATTR), true); } + + @Test + public void testWithNullDynamicField() throws Throwable { + interceptor.beforeMethod(null, null, new Object[]{enhancedInstance}, null, null); + interceptor.afterMethod(null, null, null, null, null); + // no more need this, span was stopped at interceptor#afterMethod + // ContextManager.stopSpan(); + final List traceSegments = segmentStorage.getTraceSegments(); + Assert.assertEquals(traceSegments.size(), 1); + final List spans = SegmentHelper.getSpans(traceSegments.get(0)); + Assert.assertNotNull(spans); + Assert.assertEquals(spans.size(), 1); + SpanAssert.assertComponent(spans.get(0), ComponentsDefine.SPRING_CLOUD_GATEWAY); + } + + @Test + public void testWithContextSnapshot() throws Throwable { + final AbstractSpan entrySpan = ContextManager.createEntrySpan("/get", null); + SpanLayer.asHttp(entrySpan); + entrySpan.setComponent(ComponentsDefine.SPRING_WEBFLUX); + enhancedInstance.setSkyWalkingDynamicField(ContextManager.capture()); + interceptor.beforeMethod(null, null, new Object[]{enhancedInstance}, null, null); + interceptor.afterMethod(null, null, null, null, null); + // no more need this, span was stopped at interceptor#afterMethod + // ContextManager.stopSpan(); + ContextManager.stopSpan(entrySpan); + final List traceSegments = segmentStorage.getTraceSegments(); + Assert.assertEquals(traceSegments.size(), 1); + final List spans = SegmentHelper.getSpans(traceSegments.get(0)); + Assert.assertNotNull(spans); + Assert.assertEquals(spans.size(), 2); + SpanAssert.assertComponent(spans.get(0), ComponentsDefine.SPRING_CLOUD_GATEWAY); + SpanAssert.assertComponent(spans.get(1), ComponentsDefine.SPRING_WEBFLUX); + SpanAssert.assertLayer(spans.get(1), SpanLayer.HTTP); + } } diff --git a/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-3.x-plugin/pom.xml b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-3.x-plugin/pom.xml index ee9b877aaa..33180c560c 100644 --- a/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-3.x-plugin/pom.xml +++ b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-3.x-plugin/pom.xml @@ -21,7 +21,7 @@ optional-spring-cloud org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 @@ -52,7 +52,6 @@ org.apache.maven.plugins maven-shade-plugin - 3.2.4 package diff --git a/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v3x/GatewayFilterInterceptor.java b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v3x/GatewayFilterInterceptor.java new file mode 100644 index 0000000000..133424539b --- /dev/null +++ b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v3x/GatewayFilterInterceptor.java @@ -0,0 +1,94 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.skywalking.apm.plugin.spring.cloud.gateway.v3x; + +import org.apache.skywalking.apm.agent.core.context.ContextManager; +import org.apache.skywalking.apm.agent.core.context.ContextSnapshot; +import org.apache.skywalking.apm.agent.core.context.trace.AbstractSpan; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceMethodsAroundInterceptor; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult; +import org.springframework.web.server.ServerWebExchange; +import org.springframework.web.server.ServerWebExchangeDecorator; +import org.springframework.web.server.adapter.DefaultServerWebExchange; + +import java.lang.reflect.Method; +import java.util.concurrent.atomic.AtomicInteger; + +import static org.apache.skywalking.apm.network.trace.component.ComponentsDefine.SPRING_CLOUD_GATEWAY; + +public class GatewayFilterInterceptor implements InstanceMethodsAroundInterceptor { + + private static final ThreadLocal STACK_DEEP = ThreadLocal.withInitial(() -> new AtomicInteger(0)); + + @Override + public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, + MethodInterceptResult result) throws Throwable { + if (isEntry()) { + ServerWebExchange exchange = (ServerWebExchange) allArguments[0]; + + EnhancedInstance enhancedInstance = getInstance(exchange); + + AbstractSpan span = ContextManager.createLocalSpan("SpringCloudGateway/GatewayFilter"); + if (enhancedInstance != null && enhancedInstance.getSkyWalkingDynamicField() != null) { + ContextManager.continued((ContextSnapshot) enhancedInstance.getSkyWalkingDynamicField()); + } + span.setComponent(SPRING_CLOUD_GATEWAY); + } + } + + public static EnhancedInstance getInstance(Object o) { + EnhancedInstance instance = null; + if (o instanceof DefaultServerWebExchange) { + instance = (EnhancedInstance) o; + } else if (o instanceof ServerWebExchangeDecorator) { + ServerWebExchange delegate = ((ServerWebExchangeDecorator) o).getDelegate(); + return getInstance(delegate); + } + return instance; + } + + @Override + public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, + Object ret) throws Throwable { + if (isExit()) { + if (ContextManager.isActive()) { + ContextManager.stopSpan(); + } + } + return ret; + } + + @Override + public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments, + Class[] argumentsTypes, Throwable t) { + ContextManager.activeSpan().log(t); + } + + private boolean isEntry() { + return STACK_DEEP.get().getAndIncrement() == 0; + } + + private boolean isExit() { + boolean isExit = STACK_DEEP.get().decrementAndGet() == 0; + if (isExit) { + STACK_DEEP.remove(); + } + return isExit; + } +} diff --git a/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v3x/NettyRoutingFilterInterceptor.java b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v3x/NettyRoutingFilterInterceptor.java index 8b53595e4e..014dd4b6c4 100644 --- a/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v3x/NettyRoutingFilterInterceptor.java +++ b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v3x/NettyRoutingFilterInterceptor.java @@ -81,6 +81,10 @@ private EnhancedInstance getInstance(Object o) { @Override public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, Object ret) throws Throwable { + if (ContextManager.isActive()) { + // if HttpClientFinalizerSendInterceptor does not invoke, we will stop the span there to avoid context leak. + ContextManager.stopSpan(); + } return ret; } diff --git a/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v3x/define/GatewayFilterInstrumentation.java b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v3x/define/GatewayFilterInstrumentation.java new file mode 100644 index 0000000000..4488516f19 --- /dev/null +++ b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v3x/define/GatewayFilterInstrumentation.java @@ -0,0 +1,67 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.skywalking.apm.plugin.spring.cloud.gateway.v3x.define; + +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.matcher.ElementMatcher; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch; +import org.apache.skywalking.apm.agent.core.plugin.match.HierarchyMatch; + +import static net.bytebuddy.matcher.ElementMatchers.named; +import static org.apache.skywalking.apm.agent.core.plugin.bytebuddy.ArgumentTypeNameMatch.takesArgumentWithType; + +public class GatewayFilterInstrumentation extends AbstractGatewayV3EnhancePluginDefine { + + private static final String INTERCEPT_CLASS_GATEWAY_FILTER = "org.springframework.cloud.gateway.filter.GatewayFilter"; + private static final String GATEWAY_FILTER_INTERCEPTOR = "org.apache.skywalking.apm.plugin.spring.cloud.gateway.v3x.GatewayFilterInterceptor"; + + @Override + protected ClassMatch enhanceClass() { + return HierarchyMatch.byHierarchyMatch(INTERCEPT_CLASS_GATEWAY_FILTER); + } + + @Override + public ConstructorInterceptPoint[] getConstructorsInterceptPoints() { + return new ConstructorInterceptPoint[0]; + } + + @Override + public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() { + return new InstanceMethodsInterceptPoint[] { + new InstanceMethodsInterceptPoint() { + @Override + public ElementMatcher getMethodsMatcher() { + return named("filter").and( + takesArgumentWithType(0, "org.springframework.web.server.ServerWebExchange")); + } + + @Override + public String getMethodsInterceptor() { + return GATEWAY_FILTER_INTERCEPTOR; + } + + @Override + public boolean isOverrideArgs() { + return false; + } + } + }; + } +} diff --git a/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-3.x-plugin/src/main/resources/skywalking-plugin.def b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-3.x-plugin/src/main/resources/skywalking-plugin.def index ba4b480501..10525a931a 100644 --- a/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-3.x-plugin/src/main/resources/skywalking-plugin.def +++ b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-3.x-plugin/src/main/resources/skywalking-plugin.def @@ -18,3 +18,4 @@ spring-cloud-gateway-3.x=org.apache.skywalking.apm.plugin.spring.cloud.gateway.v spring-cloud-gateway-3.x=org.apache.skywalking.apm.plugin.spring.cloud.gateway.v3x.define.NettyRoutingFilterInstrumentation spring-cloud-gateway-3.x=org.apache.skywalking.apm.plugin.spring.cloud.gateway.v3x.define.ServerWebExchangeInstrumentation spring-cloud-gateway-3.x=org.apache.skywalking.apm.plugin.spring.cloud.gateway.v3x.define.DispatcherHandlerInstrumentation +spring-cloud-gateway-3.x=org.apache.skywalking.apm.plugin.spring.cloud.gateway.v3x.define.GatewayFilterInstrumentation diff --git a/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-3.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v3x/GatewayFilterInterceptorTest.java b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-3.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v3x/GatewayFilterInterceptorTest.java new file mode 100644 index 0000000000..db0e0e3174 --- /dev/null +++ b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-3.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v3x/GatewayFilterInterceptorTest.java @@ -0,0 +1,332 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.spring.cloud.gateway.v3x; + +import org.apache.skywalking.apm.agent.core.context.ContextManager; +import org.apache.skywalking.apm.agent.core.context.ContextSnapshot; +import org.apache.skywalking.apm.agent.core.context.trace.AbstractSpan; +import org.apache.skywalking.apm.agent.core.context.trace.AbstractTracingSpan; +import org.apache.skywalking.apm.agent.core.context.trace.SpanLayer; +import org.apache.skywalking.apm.agent.core.context.trace.TraceSegment; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; +import org.apache.skywalking.apm.agent.test.helper.SegmentHelper; +import org.apache.skywalking.apm.agent.test.tools.AgentServiceRule; +import org.apache.skywalking.apm.agent.test.tools.SegmentStorage; +import org.apache.skywalking.apm.agent.test.tools.SegmentStoragePoint; +import org.apache.skywalking.apm.agent.test.tools.SpanAssert; +import org.apache.skywalking.apm.agent.test.tools.TracingSegmentRunner; +import org.apache.skywalking.apm.network.trace.component.ComponentsDefine; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; +import org.springframework.context.ApplicationContext; +import org.springframework.context.i18n.LocaleContext; +import org.springframework.http.codec.multipart.Part; +import org.springframework.http.server.reactive.ServerHttpRequest; +import org.springframework.http.server.reactive.ServerHttpResponse; +import org.springframework.util.MultiValueMap; +import org.springframework.web.server.ServerWebExchange; +import org.springframework.web.server.WebSession; +import reactor.core.publisher.Mono; + +import java.security.Principal; +import java.time.Instant; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Function; + +@RunWith(TracingSegmentRunner.class) +public class GatewayFilterInterceptorTest { + + private static final String GATEWAY_FILTER_INTERCEPTOR_LOCAL_SPAN_OPERATION_NAME = "SpringCloudGateway/GatewayFilter"; + + private static class ServerWebExchangeEnhancedInstance implements ServerWebExchange, EnhancedInstance { + private ContextSnapshot snapshot; + Map attributes = new HashMap<>(); + + @Override + public Object getSkyWalkingDynamicField() { + return snapshot; + } + + @Override + public void setSkyWalkingDynamicField(Object value) { + this.snapshot = (ContextSnapshot) value; + } + + @Override + public ServerHttpRequest getRequest() { + return null; + } + + @Override + public ServerHttpResponse getResponse() { + return null; + } + + @Override + public Map getAttributes() { + return attributes; + } + + @Override + public Mono getSession() { + return null; + } + + @Override + public Mono getPrincipal() { + return null; + } + + @Override + public Mono> getFormData() { + return null; + } + + @Override + public Mono> getMultipartData() { + return null; + } + + @Override + public LocaleContext getLocaleContext() { + return null; + } + + @Override + public ApplicationContext getApplicationContext() { + return null; + } + + @Override + public boolean isNotModified() { + return false; + } + + @Override + public boolean checkNotModified(Instant instant) { + return false; + } + + @Override + public boolean checkNotModified(String s) { + return false; + } + + @Override + public boolean checkNotModified(String s, Instant instant) { + return false; + } + + @Override + public String transformUrl(String s) { + return null; + } + + @Override + public void addUrlTransformer(Function function) { + + } + + @Override + public String getLogPrefix() { + return null; + } + } + + private final ServerWebExchangeEnhancedInstance enhancedInstance = new ServerWebExchangeEnhancedInstance(); + private final GatewayFilterInterceptor interceptor = new GatewayFilterInterceptor(); + @Rule + public AgentServiceRule serviceRule = new AgentServiceRule(); + @Rule + public MockitoRule rule = MockitoJUnit.rule(); + + @SegmentStoragePoint + private SegmentStorage segmentStorage; + + @Before + public void setUp() throws Exception { + } + + private final ServerWebExchange exchange = new ServerWebExchange() { + Map attributes = new HashMap<>(); + @Override + public ServerHttpRequest getRequest() { + return null; + } + + @Override + public ServerHttpResponse getResponse() { + return null; + } + + @Override + public Map getAttributes() { + return attributes; + } + + @Override + public Mono getSession() { + return null; + } + + @Override + public Mono getPrincipal() { + return null; + } + + @Override + public Mono> getFormData() { + return null; + } + + @Override + public Mono> getMultipartData() { + return null; + } + + @Override + public LocaleContext getLocaleContext() { + return null; + } + + @Override + public ApplicationContext getApplicationContext() { + return null; + } + + @Override + public boolean isNotModified() { + return false; + } + + @Override + public boolean checkNotModified(Instant instant) { + return false; + } + + @Override + public boolean checkNotModified(String s) { + return false; + } + + @Override + public boolean checkNotModified(String s, Instant instant) { + return false; + } + + @Override + public String transformUrl(String s) { + return null; + } + + @Override + public void addUrlTransformer(Function function) { + + } + + @Override + public String getLogPrefix() { + return null; + } + }; + + @Test + public void testInterceptOnlyOnce() throws Throwable { + interceptor.beforeMethod(null, null, new Object[]{exchange}, null, null); + Assert.assertTrue(ContextManager.isActive()); + AbstractSpan activeSpan = ContextManager.activeSpan(); + Assert.assertTrue(activeSpan instanceof AbstractTracingSpan); + Assert.assertEquals(activeSpan.getOperationName(), GATEWAY_FILTER_INTERCEPTOR_LOCAL_SPAN_OPERATION_NAME); + + interceptor.afterMethod(null, null, null, null, null); + Assert.assertFalse(ContextManager.isActive()); + final List traceSegments = segmentStorage.getTraceSegments(); + Assert.assertEquals(traceSegments.size(), 1); + final List spans = SegmentHelper.getSpans(traceSegments.get(0)); + Assert.assertNotNull(spans); + Assert.assertEquals(spans.size(), 1); + SpanAssert.assertComponent(spans.get(0), ComponentsDefine.SPRING_CLOUD_GATEWAY); + + } + + @Test + public void testNestedInterception() throws Throwable { + interceptor.beforeMethod(null, null, new Object[]{enhancedInstance}, null, null); + Assert.assertTrue(ContextManager.isActive()); + + interceptor.beforeMethod(null, null, new Object[]{enhancedInstance}, null, null); + Assert.assertTrue(ContextManager.isActive()); + + interceptor.afterMethod(null, null, null, null, null); + Assert.assertTrue(ContextManager.isActive()); + + interceptor.afterMethod(null, null, null, null, null); + Assert.assertFalse(ContextManager.isActive()); + + final List traceSegments = segmentStorage.getTraceSegments(); + Assert.assertEquals(traceSegments.size(), 1); + final List spans = SegmentHelper.getSpans(traceSegments.get(0)); + Assert.assertNotNull(spans); + Assert.assertEquals(spans.size(), 1); + SpanAssert.assertComponent(spans.get(0), ComponentsDefine.SPRING_CLOUD_GATEWAY); + } + + @Test + public void testWithNullDynamicField() throws Throwable { + interceptor.beforeMethod(null, null, new Object[]{enhancedInstance}, null, null); + interceptor.afterMethod(null, null, null, null, null); + // no more need this, span was stopped at interceptor#afterMethod + // ContextManager.stopSpan(); + final List traceSegments = segmentStorage.getTraceSegments(); + Assert.assertEquals(traceSegments.size(), 1); + final List spans = SegmentHelper.getSpans(traceSegments.get(0)); + Assert.assertNotNull(spans); + Assert.assertEquals(spans.size(), 1); + SpanAssert.assertComponent(spans.get(0), ComponentsDefine.SPRING_CLOUD_GATEWAY); + } + + @Test + public void testWithContextSnapshot() throws Throwable { + final AbstractSpan entrySpan = ContextManager.createEntrySpan("/get", null); + SpanLayer.asHttp(entrySpan); + entrySpan.setComponent(ComponentsDefine.SPRING_WEBFLUX); + enhancedInstance.setSkyWalkingDynamicField(ContextManager.capture()); + interceptor.beforeMethod(null, null, new Object[]{enhancedInstance}, null, null); + interceptor.afterMethod(null, null, null, null, null); + // no more need this, span was stopped at interceptor#afterMethod + // ContextManager.stopSpan(); + ContextManager.stopSpan(entrySpan); + final List traceSegments = segmentStorage.getTraceSegments(); + Assert.assertEquals(traceSegments.size(), 1); + final List spans = SegmentHelper.getSpans(traceSegments.get(0)); + Assert.assertNotNull(spans); + Assert.assertEquals(spans.size(), 2); + SpanAssert.assertComponent(spans.get(0), ComponentsDefine.SPRING_CLOUD_GATEWAY); + SpanAssert.assertComponent(spans.get(1), ComponentsDefine.SPRING_WEBFLUX); + SpanAssert.assertLayer(spans.get(1), SpanLayer.HTTP); + } + +} diff --git a/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-3.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v3x/NettyRoutingFilterInterceptorTest.java b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-3.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v3x/NettyRoutingFilterInterceptorTest.java index 36a262ab30..a2614b2245 100644 --- a/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-3.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v3x/NettyRoutingFilterInterceptorTest.java +++ b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-3.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v3x/NettyRoutingFilterInterceptorTest.java @@ -170,7 +170,8 @@ public void setUp() throws Exception { public void testWithNullDynamicField() throws Throwable { interceptor.beforeMethod(null, null, new Object[]{enhancedInstance}, null, null); interceptor.afterMethod(null, null, null, null, null); - ContextManager.stopSpan(); + // no more need this, span was stopped at interceptor#afterMethod + // ContextManager.stopSpan(); final List traceSegments = segmentStorage.getTraceSegments(); Assert.assertEquals(traceSegments.size(), 1); final List spans = SegmentHelper.getSpans(traceSegments.get(0)); @@ -187,7 +188,8 @@ public void testWithContextSnapshot() throws Throwable { enhancedInstance.setSkyWalkingDynamicField(ContextManager.capture()); interceptor.beforeMethod(null, null, new Object[]{enhancedInstance}, null, null); interceptor.afterMethod(null, null, null, null, null); - ContextManager.stopSpan(); + // no more need this, span was stopped at interceptor#afterMethod + // ContextManager.stopSpan(); ContextManager.stopSpan(entrySpan); final List traceSegments = segmentStorage.getTraceSegments(); Assert.assertEquals(traceSegments.size(), 1); @@ -207,9 +209,10 @@ public void testIsTraced() throws Throwable { interceptor.beforeMethod(null, null, new Object[]{enhancedInstance}, null, null); interceptor.afterMethod(null, null, null, null, null); Assert.assertEquals(enhancedInstance.getAttributes().get(NETTY_ROUTING_FILTER_TRACED_ATTR), true); - Assert.assertNotNull(ContextManager.activeSpan()); + Assert.assertFalse(ContextManager.isActive()); - ContextManager.stopSpan(); + // no more need this, span was stopped at interceptor#afterMethod + // ContextManager.stopSpan(); interceptor.beforeMethod(null, null, new Object[]{enhancedInstance}, null, null); interceptor.afterMethod(null, null, null, null, null); diff --git a/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-4.x-plugin/pom.xml b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-4.x-plugin/pom.xml index c7dfeb2165..bcd6ffcca4 100644 --- a/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-4.x-plugin/pom.xml +++ b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-4.x-plugin/pom.xml @@ -21,7 +21,7 @@ optional-spring-cloud org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 @@ -52,7 +52,6 @@ org.apache.maven.plugins maven-shade-plugin - 3.2.4 package diff --git a/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v412x/GatewayFilterV412Interceptor.java b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v412x/GatewayFilterV412Interceptor.java new file mode 100644 index 0000000000..c1a9ec505f --- /dev/null +++ b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v412x/GatewayFilterV412Interceptor.java @@ -0,0 +1,94 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.skywalking.apm.plugin.spring.cloud.gateway.v412x; + +import org.apache.skywalking.apm.agent.core.context.ContextManager; +import org.apache.skywalking.apm.agent.core.context.ContextSnapshot; +import org.apache.skywalking.apm.agent.core.context.trace.AbstractSpan; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceMethodsAroundInterceptor; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult; +import org.springframework.web.server.ServerWebExchange; +import org.springframework.web.server.ServerWebExchangeDecorator; +import org.springframework.web.server.adapter.DefaultServerWebExchange; + +import java.lang.reflect.Method; +import java.util.concurrent.atomic.AtomicInteger; + +import static org.apache.skywalking.apm.network.trace.component.ComponentsDefine.SPRING_CLOUD_GATEWAY; + +public class GatewayFilterV412Interceptor implements InstanceMethodsAroundInterceptor { + + private static final ThreadLocal STACK_DEEP = ThreadLocal.withInitial(() -> new AtomicInteger(0)); + + @Override + public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, + MethodInterceptResult result) throws Throwable { + if (isEntry()) { + ServerWebExchange exchange = (ServerWebExchange) allArguments[0]; + + EnhancedInstance enhancedInstance = getInstance(exchange); + + AbstractSpan span = ContextManager.createLocalSpan("SpringCloudGateway/GatewayFilter"); + if (enhancedInstance != null && enhancedInstance.getSkyWalkingDynamicField() != null) { + ContextManager.continued((ContextSnapshot) enhancedInstance.getSkyWalkingDynamicField()); + } + span.setComponent(SPRING_CLOUD_GATEWAY); + } + } + + public static EnhancedInstance getInstance(Object o) { + EnhancedInstance instance = null; + if (o instanceof DefaultServerWebExchange) { + instance = (EnhancedInstance) o; + } else if (o instanceof ServerWebExchangeDecorator) { + ServerWebExchange delegate = ((ServerWebExchangeDecorator) o).getDelegate(); + return getInstance(delegate); + } + return instance; + } + + @Override + public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, + Object ret) throws Throwable { + if (isExit()) { + if (ContextManager.isActive()) { + ContextManager.stopSpan(); + } + } + return ret; + } + + @Override + public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments, + Class[] argumentsTypes, Throwable t) { + ContextManager.activeSpan().log(t); + } + + private boolean isEntry() { + return STACK_DEEP.get().getAndIncrement() == 0; + } + + private boolean isExit() { + boolean isExit = STACK_DEEP.get().decrementAndGet() == 0; + if (isExit) { + STACK_DEEP.remove(); + } + return isExit; + } +} diff --git a/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v412x/HttpClientConnectDuplicateV412Interceptor.java b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v412x/HttpClientConnectDuplicateV412Interceptor.java new file mode 100644 index 0000000000..5658134748 --- /dev/null +++ b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v412x/HttpClientConnectDuplicateV412Interceptor.java @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.skywalking.apm.plugin.spring.cloud.gateway.v412x; + +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceMethodsAroundInterceptor; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult; +import org.apache.skywalking.apm.plugin.spring.cloud.gateway.v4x.define.EnhanceObjectCache; + +import java.lang.reflect.Method; + +public class HttpClientConnectDuplicateV412Interceptor implements InstanceMethodsAroundInterceptor { + + @Override + public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, MethodInterceptResult result) throws Throwable { + + } + + @Override + public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, Object ret) throws Throwable { + if (objInst.getSkyWalkingDynamicField() != null) { + EnhanceObjectCache enhanceObjectCache = (EnhanceObjectCache) objInst.getSkyWalkingDynamicField(); + if (ret instanceof EnhancedInstance) { + EnhancedInstance retEnhancedInstance = (EnhancedInstance) ret; + retEnhancedInstance.setSkyWalkingDynamicField(enhanceObjectCache); + } + } + return ret; + } + + @Override + public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, Throwable t) { + + } +} diff --git a/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v412x/HttpClientConnectRequestV412Interceptor.java b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v412x/HttpClientConnectRequestV412Interceptor.java new file mode 100644 index 0000000000..f35f3551fb --- /dev/null +++ b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v412x/HttpClientConnectRequestV412Interceptor.java @@ -0,0 +1,54 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.skywalking.apm.plugin.spring.cloud.gateway.v412x; + +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceMethodsAroundInterceptor; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult; +import org.apache.skywalking.apm.plugin.spring.cloud.gateway.v4x.define.EnhanceObjectCache; + +import java.lang.reflect.Method; + +public class HttpClientConnectRequestV412Interceptor implements InstanceMethodsAroundInterceptor { + + @Override + public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, MethodInterceptResult result) throws Throwable { + + } + + @Override + public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, Object ret) throws Throwable { + if (objInst.getSkyWalkingDynamicField() != null) { + if (ret instanceof EnhancedInstance) { + EnhancedInstance retEnhancedInstance = (EnhancedInstance) ret; + Object retSkyWalkingDynamicField = retEnhancedInstance.getSkyWalkingDynamicField(); + if (retSkyWalkingDynamicField != null) { + EnhanceObjectCache retEnhanceObjectCache = (EnhanceObjectCache) retSkyWalkingDynamicField; + EnhanceObjectCache objEnhanceObjectCache = (EnhanceObjectCache) objInst.getSkyWalkingDynamicField(); + retEnhanceObjectCache.setContextSnapshot(objEnhanceObjectCache.getContextSnapshot()); + } + } + } + return ret; + } + + @Override + public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, Throwable t) { + + } +} diff --git a/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v412x/HttpClientFinalizerConstructorV412Interceptor.java b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v412x/HttpClientFinalizerConstructorV412Interceptor.java new file mode 100644 index 0000000000..410baa61a7 --- /dev/null +++ b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v412x/HttpClientFinalizerConstructorV412Interceptor.java @@ -0,0 +1,43 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.skywalking.apm.plugin.spring.cloud.gateway.v412x; + +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceConstructorInterceptor; +import org.apache.skywalking.apm.plugin.spring.cloud.gateway.v4x.define.EnhanceObjectCache; +import reactor.netty.http.client.HttpClientConfig; + +/** + * Intercept the constructor and inject {@link EnhanceObjectCache}. + *

+ * The first constructor argument is {@link HttpClientConfig} class instance which can get the + * request uri string. + */ +public class HttpClientFinalizerConstructorV412Interceptor implements InstanceConstructorInterceptor { + + @Override + public void onConstruct(EnhancedInstance objInst, Object[] allArguments) { + final HttpClientConfig httpClientConfig = (HttpClientConfig) allArguments[0]; + if (httpClientConfig == null) { + return; + } + final EnhanceObjectCache enhanceObjectCache = new EnhanceObjectCache(); + enhanceObjectCache.setUrl(httpClientConfig.uri()); + objInst.setSkyWalkingDynamicField(enhanceObjectCache); + } +} diff --git a/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v412x/HttpClientFinalizerResponseConnectionV412Interceptor.java b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v412x/HttpClientFinalizerResponseConnectionV412Interceptor.java new file mode 100644 index 0000000000..7199c85744 --- /dev/null +++ b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v412x/HttpClientFinalizerResponseConnectionV412Interceptor.java @@ -0,0 +1,107 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.skywalking.apm.plugin.spring.cloud.gateway.v412x; + +import io.netty.handler.codec.http.HttpResponseStatus; +import org.apache.skywalking.apm.agent.core.context.tag.Tags; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceMethodsAroundInterceptor; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult; +import org.apache.skywalking.apm.plugin.spring.cloud.gateway.v4x.define.EnhanceObjectCache; +import org.reactivestreams.Publisher; +import reactor.core.publisher.Flux; +import reactor.core.publisher.SignalType; +import reactor.netty.Connection; +import reactor.netty.http.client.HttpClientResponse; + +import java.lang.reflect.Method; +import java.util.function.BiFunction; + +/** + * This class intercept responseConnection method. + *

+ * After downstream service response, finish the span in the {@link EnhanceObjectCache}. + */ +public class HttpClientFinalizerResponseConnectionV412Interceptor implements InstanceMethodsAroundInterceptor { + + @Override + public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, + MethodInterceptResult result) { + BiFunction finalReceiver = (BiFunction) allArguments[0]; + EnhanceObjectCache cache = (EnhanceObjectCache) objInst.getSkyWalkingDynamicField(); + allArguments[0] = (BiFunction) (response, connection) -> { + Publisher publisher = finalReceiver.apply(response, connection); + if (cache == null) { + return publisher; + } + // receive the response. + if (cache.getSpan() != null) { + if (response.status().code() >= HttpResponseStatus.BAD_REQUEST.code()) { + cache.getSpan().errorOccurred(); + } + Tags.HTTP_RESPONSE_STATUS_CODE.set(cache.getSpan(), response.status().code()); + } + + return publisher; + }; + } + + @Override + public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, + Object ret) { + Flux responseFlux = (Flux) ret; + + responseFlux = responseFlux + .doOnError(e -> { + EnhanceObjectCache cache = (EnhanceObjectCache) objInst.getSkyWalkingDynamicField(); + if (cache == null) { + return; + } + + if (cache.getSpan() != null) { + cache.getSpan().errorOccurred(); + cache.getSpan().log(e); + } + }) + .doFinally(signalType -> { + EnhanceObjectCache cache = (EnhanceObjectCache) objInst.getSkyWalkingDynamicField(); + if (cache == null) { + return; + } + // do finally. Finish the span. + if (cache.getSpan() != null) { + if (signalType == SignalType.CANCEL) { + cache.getSpan().errorOccurred(); + } + cache.getSpan().asyncFinish(); + } + + if (cache.getSpan1() != null) { + cache.getSpan1().asyncFinish(); + } + }); + + return responseFlux; + } + + @Override + public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments, + Class[] argumentsTypes, Throwable t) { + + } +} diff --git a/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v412x/HttpClientFinalizerSendV412Interceptor.java b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v412x/HttpClientFinalizerSendV412Interceptor.java new file mode 100644 index 0000000000..a1bc32f329 --- /dev/null +++ b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v412x/HttpClientFinalizerSendV412Interceptor.java @@ -0,0 +1,112 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.skywalking.apm.plugin.spring.cloud.gateway.v412x; + +import org.apache.skywalking.apm.agent.core.context.CarrierItem; +import org.apache.skywalking.apm.agent.core.context.ContextCarrier; +import org.apache.skywalking.apm.agent.core.context.ContextManager; +import org.apache.skywalking.apm.agent.core.context.ContextSnapshot; +import org.apache.skywalking.apm.agent.core.context.tag.Tags; +import org.apache.skywalking.apm.agent.core.context.trace.AbstractSpan; +import org.apache.skywalking.apm.agent.core.context.trace.SpanLayer; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceMethodsAroundInterceptor; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult; +import org.apache.skywalking.apm.plugin.spring.cloud.gateway.v4x.define.EnhanceObjectCache; +import org.apache.skywalking.apm.util.StringUtil; +import org.reactivestreams.Publisher; +import reactor.netty.NettyOutbound; +import reactor.netty.http.client.HttpClientRequest; + +import java.lang.reflect.Method; +import java.net.URL; +import java.util.function.BiFunction; + +import static org.apache.skywalking.apm.network.trace.component.ComponentsDefine.SPRING_CLOUD_GATEWAY; + +/** + * This class intercept send method. + *

+ * In before method, create a new BiFunction lambda expression for setting ContextCarrier to http header + * and replace the original lambda in argument + */ +public class HttpClientFinalizerSendV412Interceptor implements InstanceMethodsAroundInterceptor { + + @Override + public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, + MethodInterceptResult result) throws Throwable { + EnhanceObjectCache enhanceObjectCache = (EnhanceObjectCache) objInst.getSkyWalkingDynamicField(); + if (enhanceObjectCache == null) { + return; + } + ContextSnapshot contextSnapshot = enhanceObjectCache.getContextSnapshot(); + if (contextSnapshot == null) { + return; + } + AbstractSpan span = ContextManager.createLocalSpan("SpringCloudGateway/send"); + ContextManager.continued(contextSnapshot); + span.setComponent(SPRING_CLOUD_GATEWAY); + span.prepareForAsync(); + + if (StringUtil.isNotEmpty(enhanceObjectCache.getUrl())) { + URL url = new URL(enhanceObjectCache.getUrl()); + + ContextCarrier contextCarrier = new ContextCarrier(); + AbstractSpan abstractSpan = ContextManager.createExitSpan( + "SpringCloudGateway/sendRequest", contextCarrier, getPeer(url)); + Tags.URL.set(abstractSpan, enhanceObjectCache.getUrl()); + abstractSpan.prepareForAsync(); + abstractSpan.setComponent(SPRING_CLOUD_GATEWAY); + abstractSpan.setLayer(SpanLayer.HTTP); + ContextManager.stopSpan(abstractSpan); + + BiFunction> finalSender = (BiFunction>) allArguments[0]; + allArguments[0] = (BiFunction>) (request, outbound) -> { + Publisher publisher = finalSender.apply(request, outbound); + + CarrierItem next = contextCarrier.items(); + while (next.hasNext()) { + next = next.next(); + request.requestHeaders().remove(next.getHeadKey()); + request.requestHeaders().set(next.getHeadKey(), next.getHeadValue()); + } + return publisher; + }; + enhanceObjectCache.setCacheSpan(abstractSpan); + } + ContextManager.stopSpan(span); + enhanceObjectCache.setSpan1(span); + } + + private String getPeer(URL url) { + return url.getHost() + ":" + url.getPort(); + } + + @Override + public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, + Object ret) { + ((EnhancedInstance) ret).setSkyWalkingDynamicField(objInst.getSkyWalkingDynamicField()); + return ret; + } + + @Override + public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments, + Class[] argumentsTypes, Throwable t) { + + } +} diff --git a/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v412x/HttpClientFinalizerUriV412Interceptor.java b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v412x/HttpClientFinalizerUriV412Interceptor.java new file mode 100644 index 0000000000..1d5c020210 --- /dev/null +++ b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v412x/HttpClientFinalizerUriV412Interceptor.java @@ -0,0 +1,57 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.skywalking.apm.plugin.spring.cloud.gateway.v412x; + +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceMethodsAroundInterceptor; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult; +import org.apache.skywalking.apm.plugin.spring.cloud.gateway.v4x.define.EnhanceObjectCache; + +import java.lang.reflect.Method; + +/** + * This class intercept uri method to get the url of downstream service + */ +public class HttpClientFinalizerUriV412Interceptor implements InstanceMethodsAroundInterceptor { + + @Override + public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, + MethodInterceptResult result) throws Throwable { + } + + @Override + public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, + Object ret) throws Throwable { + if (ret instanceof EnhancedInstance) { + EnhanceObjectCache retEnhanceObjectCache = (EnhanceObjectCache) ((EnhancedInstance) ret).getSkyWalkingDynamicField(); + if (retEnhanceObjectCache != null) { + retEnhanceObjectCache.setUrl(String.valueOf(allArguments[0])); + if (objInst.getSkyWalkingDynamicField() != null) { + EnhanceObjectCache objInstEnhanceObjectCache = (EnhanceObjectCache) objInst.getSkyWalkingDynamicField(); + retEnhanceObjectCache.setContextSnapshot(objInstEnhanceObjectCache.getContextSnapshot()); + } + } + } + return ret; + } + + @Override + public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments, + Class[] argumentsTypes, Throwable t) { + } +} diff --git a/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v412x/NettyRoutingFilterV412Interceptor.java b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v412x/NettyRoutingFilterV412Interceptor.java new file mode 100644 index 0000000000..1d6aa76da8 --- /dev/null +++ b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v412x/NettyRoutingFilterV412Interceptor.java @@ -0,0 +1,91 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.spring.cloud.gateway.v412x; + +import org.apache.skywalking.apm.agent.core.context.ContextManager; +import org.apache.skywalking.apm.agent.core.context.ContextSnapshot; +import org.apache.skywalking.apm.agent.core.context.trace.AbstractSpan; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceMethodsAroundInterceptor; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult; +import org.springframework.web.server.ServerWebExchange; +import org.springframework.web.server.ServerWebExchangeDecorator; + +import java.lang.reflect.Method; + +import static org.apache.skywalking.apm.network.trace.component.ComponentsDefine.SPRING_CLOUD_GATEWAY; + +public class NettyRoutingFilterV412Interceptor implements InstanceMethodsAroundInterceptor { + + private static final String NETTY_ROUTING_FILTER_TRACED_ATTR = NettyRoutingFilterV412Interceptor.class.getName() + ".isTraced"; + + @Override + public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, + MethodInterceptResult result) throws Throwable { + ServerWebExchange exchange = (ServerWebExchange) allArguments[0]; + if (isTraced(exchange)) { + return; + } + + setTracedStatus(exchange); + + EnhancedInstance enhancedInstance = getInstance(allArguments[0]); + + AbstractSpan span = ContextManager.createLocalSpan("SpringCloudGateway/RoutingFilter"); + if (enhancedInstance != null && enhancedInstance.getSkyWalkingDynamicField() != null) { + ContextManager.continued((ContextSnapshot) enhancedInstance.getSkyWalkingDynamicField()); + } + span.setComponent(SPRING_CLOUD_GATEWAY); + } + + private static void setTracedStatus(ServerWebExchange exchange) { + exchange.getAttributes().put(NETTY_ROUTING_FILTER_TRACED_ATTR, true); + } + + private static boolean isTraced(ServerWebExchange exchange) { + return exchange.getAttributeOrDefault(NETTY_ROUTING_FILTER_TRACED_ATTR, false); + } + + private EnhancedInstance getInstance(Object o) { + EnhancedInstance instance = null; + if (o instanceof EnhancedInstance) { + instance = (EnhancedInstance) o; + } else if (o instanceof ServerWebExchangeDecorator) { + ServerWebExchange delegate = ((ServerWebExchangeDecorator) o).getDelegate(); + return getInstance(delegate); + } + return instance; + } + + @Override + public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, + Object ret) throws Throwable { + if (ContextManager.isActive()) { + // if HttpClientFinalizerSendInterceptor does not invoke, we will stop the span there to avoid context leak. + ContextManager.stopSpan(); + } + return ret; + } + + @Override + public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments, + Class[] argumentsTypes, Throwable t) { + ContextManager.activeSpan().log(t); + } +} diff --git a/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v412x/NettyRoutingGetHttpClientV412Interceptor.java b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v412x/NettyRoutingGetHttpClientV412Interceptor.java new file mode 100644 index 0000000000..282e6f5f17 --- /dev/null +++ b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v412x/NettyRoutingGetHttpClientV412Interceptor.java @@ -0,0 +1,55 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.spring.cloud.gateway.v412x; + +import org.apache.skywalking.apm.agent.core.context.ContextManager; +import org.apache.skywalking.apm.agent.core.context.ContextSnapshot; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceMethodsAroundInterceptor; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult; +import org.apache.skywalking.apm.plugin.spring.cloud.gateway.v4x.define.EnhanceObjectCache; + +import java.lang.reflect.Method; + +public class NettyRoutingGetHttpClientV412Interceptor implements InstanceMethodsAroundInterceptor { + + @Override + public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, MethodInterceptResult result) throws Throwable { + + } + + @Override + public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, Object ret) throws Throwable { + if (ret instanceof EnhancedInstance) { + if (ContextManager.isActive()) { + ContextSnapshot contextSnapshot = ContextManager.capture(); + EnhanceObjectCache retEnhanceObjectCache = new EnhanceObjectCache(); + retEnhanceObjectCache.setContextSnapshot(contextSnapshot); + EnhancedInstance retEnhancedInstance = (EnhancedInstance) ret; + retEnhancedInstance.setSkyWalkingDynamicField(retEnhanceObjectCache); + } + } + return ret; + } + + @Override + public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, Throwable t) { + } + +} diff --git a/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v412x/define/AbstractGatewayV412EnhancePluginDefine.java b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v412x/define/AbstractGatewayV412EnhancePluginDefine.java new file mode 100644 index 0000000000..7cde134ef8 --- /dev/null +++ b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v412x/define/AbstractGatewayV412EnhancePluginDefine.java @@ -0,0 +1,35 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.skywalking.apm.plugin.spring.cloud.gateway.v412x.define; + +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine; + +/** + * This abstract class defines the witnessClasses() method, + * and other plugin define classes need to inherit from this class + */ +public abstract class AbstractGatewayV412EnhancePluginDefine extends ClassInstanceMethodsEnhancePluginDefine { + + /** + * @since 4.0.0 + */ + @Override + protected String[] witnessClasses() { + return new String[]{"org.springframework.cloud.gateway.event.RouteDeletedEvent"}; + } +} diff --git a/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v412x/define/GatewayFilterV412Instrumentation.java b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v412x/define/GatewayFilterV412Instrumentation.java new file mode 100644 index 0000000000..499502a07d --- /dev/null +++ b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v412x/define/GatewayFilterV412Instrumentation.java @@ -0,0 +1,67 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.skywalking.apm.plugin.spring.cloud.gateway.v412x.define; + +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.matcher.ElementMatcher; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch; +import org.apache.skywalking.apm.agent.core.plugin.match.HierarchyMatch; + +import static net.bytebuddy.matcher.ElementMatchers.named; +import static org.apache.skywalking.apm.agent.core.plugin.bytebuddy.ArgumentTypeNameMatch.takesArgumentWithType; + +public class GatewayFilterV412Instrumentation extends AbstractGatewayV412EnhancePluginDefine { + + private static final String INTERCEPT_CLASS_GATEWAY_FILTER = "org.springframework.cloud.gateway.filter.GatewayFilter"; + private static final String GATEWAY_FILTER_INTERCEPTOR = "org.apache.skywalking.apm.plugin.spring.cloud.gateway.v412x.GatewayFilterV412Interceptor"; + + @Override + protected ClassMatch enhanceClass() { + return HierarchyMatch.byHierarchyMatch(INTERCEPT_CLASS_GATEWAY_FILTER); + } + + @Override + public ConstructorInterceptPoint[] getConstructorsInterceptPoints() { + return new ConstructorInterceptPoint[0]; + } + + @Override + public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() { + return new InstanceMethodsInterceptPoint[] { + new InstanceMethodsInterceptPoint() { + @Override + public ElementMatcher getMethodsMatcher() { + return named("filter").and( + takesArgumentWithType(0, "org.springframework.web.server.ServerWebExchange")); + } + + @Override + public String getMethodsInterceptor() { + return GATEWAY_FILTER_INTERCEPTOR; + } + + @Override + public boolean isOverrideArgs() { + return false; + } + } + }; + } +} diff --git a/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v412x/define/HttpClientConnectV412Instrumentation.java b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v412x/define/HttpClientConnectV412Instrumentation.java new file mode 100644 index 0000000000..3d1bb3462b --- /dev/null +++ b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v412x/define/HttpClientConnectV412Instrumentation.java @@ -0,0 +1,87 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.skywalking.apm.plugin.spring.cloud.gateway.v412x.define; + +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.matcher.ElementMatcher; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch; + +import static net.bytebuddy.matcher.ElementMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.returns; +import static net.bytebuddy.matcher.ElementMatchers.takesNoArguments; +import static org.apache.skywalking.apm.agent.core.plugin.bytebuddy.ArgumentTypeNameMatch.takesArgumentWithType; +import static org.apache.skywalking.apm.agent.core.plugin.match.NameMatch.byName; + +public class HttpClientConnectV412Instrumentation extends AbstractGatewayV412EnhancePluginDefine { + + private static final String ENHANCE_CLASS = "reactor.netty.http.client.HttpClientConnect"; + private static final String HTTP_CLIENT_CONNECT_DUPLICATE_INTERCEPTOR = "org.apache.skywalking.apm.plugin.spring.cloud.gateway.v412x.HttpClientConnectDuplicateV412Interceptor"; + private static final String HTTP_CLIENT_CONNECT_REQUEST_INTERCEPTOR = "org.apache.skywalking.apm.plugin.spring.cloud.gateway.v412x.HttpClientConnectRequestV412Interceptor"; + + @Override + protected ClassMatch enhanceClass() { + return byName(ENHANCE_CLASS); + } + + @Override + public ConstructorInterceptPoint[] getConstructorsInterceptPoints() { + return new ConstructorInterceptPoint[0]; + } + + @Override + public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() { + return new InstanceMethodsInterceptPoint[]{ + new InstanceMethodsInterceptPoint() { + @Override + public ElementMatcher getMethodsMatcher() { + return named("duplicate").and(takesNoArguments()) + .and(returns(named("reactor.netty.http.client.HttpClient"))); + } + + @Override + public String getMethodsInterceptor() { + return HTTP_CLIENT_CONNECT_DUPLICATE_INTERCEPTOR; + } + + @Override + public boolean isOverrideArgs() { + return false; + } + }, + new InstanceMethodsInterceptPoint() { + @Override + public ElementMatcher getMethodsMatcher() { + return named("request").and(takesArgumentWithType(0, "io.netty.handler.codec.http.HttpMethod")) + .and(returns(named("reactor.netty.http.client.HttpClient$RequestSender"))); + } + + @Override + public String getMethodsInterceptor() { + return HTTP_CLIENT_CONNECT_REQUEST_INTERCEPTOR; + } + + @Override + public boolean isOverrideArgs() { + return false; + } + } + }; + } +} diff --git a/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v412x/define/HttpClientFinalizerV412Instrumentation.java b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v412x/define/HttpClientFinalizerV412Instrumentation.java new file mode 100644 index 0000000000..7a5b9b888e --- /dev/null +++ b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v412x/define/HttpClientFinalizerV412Instrumentation.java @@ -0,0 +1,114 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.skywalking.apm.plugin.spring.cloud.gateway.v412x.define; + +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.matcher.ElementMatcher; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch; + +import static net.bytebuddy.matcher.ElementMatchers.named; +import static org.apache.skywalking.apm.agent.core.plugin.bytebuddy.ArgumentTypeNameMatch.takesArgumentWithType; +import static org.apache.skywalking.apm.agent.core.plugin.match.NameMatch.byName; + +public class HttpClientFinalizerV412Instrumentation extends AbstractGatewayV412EnhancePluginDefine { + + private static final String INTERCEPT_CLASS_HTTP_CLIENT_FINALIZER = "reactor.netty.http.client.HttpClientFinalizer"; + private static final String CLIENT_FINALIZER_CONSTRUCTOR_INTERCEPTOR = "org.apache.skywalking.apm.plugin.spring.cloud.gateway.v412x.HttpClientFinalizerConstructorV412Interceptor"; + private static final String CLIENT_FINALIZER_CONSTRUCTOR_ARGUMENT_TYPE = "reactor.netty.http.client.HttpClientConfig"; + private static final String HTTP_CLIENT_FINALIZER_URI_INTERCEPTOR = "org.apache.skywalking.apm.plugin.spring.cloud.gateway.v412x.HttpClientFinalizerUriV412Interceptor"; + private static final String HTTP_CLIENT_FINALIZER_SEND_INTERCEPTOR = "org.apache.skywalking.apm.plugin.spring.cloud.gateway.v412x.HttpClientFinalizerSendV412Interceptor"; + private static final String HTTP_CLIENT_FINALIZER_RESPONSE_CONNECTION_INTERCEPTOR = "org.apache.skywalking.apm.plugin.spring.cloud.gateway.v412x.HttpClientFinalizerResponseConnectionV412Interceptor"; + + @Override + protected ClassMatch enhanceClass() { + return byName(INTERCEPT_CLASS_HTTP_CLIENT_FINALIZER); + } + + @Override + public ConstructorInterceptPoint[] getConstructorsInterceptPoints() { + return new ConstructorInterceptPoint[]{ + new ConstructorInterceptPoint() { + @Override + public ElementMatcher getConstructorMatcher() { + return takesArgumentWithType(0, CLIENT_FINALIZER_CONSTRUCTOR_ARGUMENT_TYPE); + } + + @Override + public String getConstructorInterceptor() { + return CLIENT_FINALIZER_CONSTRUCTOR_INTERCEPTOR; + } + } + }; + } + + @Override + public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() { + return new InstanceMethodsInterceptPoint[]{ + new InstanceMethodsInterceptPoint() { + @Override + public ElementMatcher getMethodsMatcher() { + return named("uri"); + } + + @Override + public String getMethodsInterceptor() { + return HTTP_CLIENT_FINALIZER_URI_INTERCEPTOR; + } + + @Override + public boolean isOverrideArgs() { + return false; + } + }, + new InstanceMethodsInterceptPoint() { + @Override + public ElementMatcher getMethodsMatcher() { + return named("send").and(takesArgumentWithType(0, "java.util.function.BiFunction")); + } + + @Override + public String getMethodsInterceptor() { + return HTTP_CLIENT_FINALIZER_SEND_INTERCEPTOR; + } + + @Override + public boolean isOverrideArgs() { + return true; + } + }, + new InstanceMethodsInterceptPoint() { + @Override + public ElementMatcher getMethodsMatcher() { + return named("responseConnection"); + } + + @Override + public String getMethodsInterceptor() { + return HTTP_CLIENT_FINALIZER_RESPONSE_CONNECTION_INTERCEPTOR; + } + + @Override + public boolean isOverrideArgs() { + return true; + } + } + }; + } +} diff --git a/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v412x/define/NettyRoutingFilterV412Instrumentation.java b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v412x/define/NettyRoutingFilterV412Instrumentation.java new file mode 100644 index 0000000000..ca06147ba3 --- /dev/null +++ b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v412x/define/NettyRoutingFilterV412Instrumentation.java @@ -0,0 +1,89 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.spring.cloud.gateway.v412x.define; + +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.matcher.ElementMatcher; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch; + +import static net.bytebuddy.matcher.ElementMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.returns; +import static org.apache.skywalking.apm.agent.core.plugin.bytebuddy.ArgumentTypeNameMatch.takesArgumentWithType; +import static org.apache.skywalking.apm.agent.core.plugin.match.NameMatch.byName; + +public class NettyRoutingFilterV412Instrumentation extends AbstractGatewayV412EnhancePluginDefine { + + private static final String INTERCEPT_CLASS_NETTY_ROUTING_FILTER = "org.springframework.cloud.gateway.filter.NettyRoutingFilter"; + private static final String NETTY_ROUTING_FILTER_INTERCEPTOR = "org.apache.skywalking.apm.plugin.spring.cloud.gateway.v412x.NettyRoutingFilterV412Interceptor"; + private static final String NETTY_ROUTING_GET_HTTP_CLIENT_INTERCEPTOR = "org.apache.skywalking.apm.plugin.spring.cloud.gateway.v412x.NettyRoutingGetHttpClientV412Interceptor"; + + @Override + protected ClassMatch enhanceClass() { + return byName(INTERCEPT_CLASS_NETTY_ROUTING_FILTER); + } + + @Override + public ConstructorInterceptPoint[] getConstructorsInterceptPoints() { + return new ConstructorInterceptPoint[0]; + } + + @Override + public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() { + return new InstanceMethodsInterceptPoint[]{ + new InstanceMethodsInterceptPoint() { + @Override + public ElementMatcher getMethodsMatcher() { + return named("filter").and( + takesArgumentWithType(0, "org.springframework.web.server.ServerWebExchange")); + } + + @Override + public String getMethodsInterceptor() { + return NETTY_ROUTING_FILTER_INTERCEPTOR; + } + + @Override + public boolean isOverrideArgs() { + return true; + } + }, + new InstanceMethodsInterceptPoint() { + @Override + public ElementMatcher getMethodsMatcher() { + return named("getHttpClient") + .and(takesArgumentWithType(0, "org.springframework.cloud.gateway.route.Route")) + .and(takesArgumentWithType(1, "org.springframework.web.server.ServerWebExchange")) + .and(returns(named("reactor.netty.http.client.HttpClient"))); + } + + @Override + public String getMethodsInterceptor() { + return NETTY_ROUTING_GET_HTTP_CLIENT_INTERCEPTOR; + } + + @Override + public boolean isOverrideArgs() { + return false; + } + } + }; + } +} diff --git a/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v4x/GatewayFilterInterceptor.java b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v4x/GatewayFilterInterceptor.java new file mode 100644 index 0000000000..233eb24ea7 --- /dev/null +++ b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v4x/GatewayFilterInterceptor.java @@ -0,0 +1,94 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.skywalking.apm.plugin.spring.cloud.gateway.v4x; + +import org.apache.skywalking.apm.agent.core.context.ContextManager; +import org.apache.skywalking.apm.agent.core.context.ContextSnapshot; +import org.apache.skywalking.apm.agent.core.context.trace.AbstractSpan; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceMethodsAroundInterceptor; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult; +import org.springframework.web.server.ServerWebExchange; +import org.springframework.web.server.ServerWebExchangeDecorator; +import org.springframework.web.server.adapter.DefaultServerWebExchange; + +import java.lang.reflect.Method; +import java.util.concurrent.atomic.AtomicInteger; + +import static org.apache.skywalking.apm.network.trace.component.ComponentsDefine.SPRING_CLOUD_GATEWAY; + +public class GatewayFilterInterceptor implements InstanceMethodsAroundInterceptor { + + private static final ThreadLocal STACK_DEEP = ThreadLocal.withInitial(() -> new AtomicInteger(0)); + + @Override + public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, + MethodInterceptResult result) throws Throwable { + if (isEntry()) { + ServerWebExchange exchange = (ServerWebExchange) allArguments[0]; + + EnhancedInstance enhancedInstance = getInstance(exchange); + + AbstractSpan span = ContextManager.createLocalSpan("SpringCloudGateway/GatewayFilter"); + if (enhancedInstance != null && enhancedInstance.getSkyWalkingDynamicField() != null) { + ContextManager.continued((ContextSnapshot) enhancedInstance.getSkyWalkingDynamicField()); + } + span.setComponent(SPRING_CLOUD_GATEWAY); + } + } + + public static EnhancedInstance getInstance(Object o) { + EnhancedInstance instance = null; + if (o instanceof DefaultServerWebExchange) { + instance = (EnhancedInstance) o; + } else if (o instanceof ServerWebExchangeDecorator) { + ServerWebExchange delegate = ((ServerWebExchangeDecorator) o).getDelegate(); + return getInstance(delegate); + } + return instance; + } + + @Override + public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, + Object ret) throws Throwable { + if (isExit()) { + if (ContextManager.isActive()) { + ContextManager.stopSpan(); + } + } + return ret; + } + + @Override + public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments, + Class[] argumentsTypes, Throwable t) { + ContextManager.activeSpan().log(t); + } + + private boolean isEntry() { + return STACK_DEEP.get().getAndIncrement() == 0; + } + + private boolean isExit() { + boolean isExit = STACK_DEEP.get().decrementAndGet() == 0; + if (isExit) { + STACK_DEEP.remove(); + } + return isExit; + } +} diff --git a/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v4x/NettyRoutingFilterInterceptor.java b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v4x/NettyRoutingFilterInterceptor.java index c5271f9a82..f2337856e0 100644 --- a/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v4x/NettyRoutingFilterInterceptor.java +++ b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v4x/NettyRoutingFilterInterceptor.java @@ -81,6 +81,10 @@ private EnhancedInstance getInstance(Object o) { @Override public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, Object ret) throws Throwable { + if (ContextManager.isActive()) { + // if HttpClientFinalizerSendInterceptor does not invoke, we will stop the span there to avoid context leak. + ContextManager.stopSpan(); + } return ret; } diff --git a/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v4x/define/AbstractGatewayV4EnhancePluginDefine.java b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v4x/define/AbstractGatewayV4EnhancePluginDefine.java index 803097ba62..f40d8832d4 100644 --- a/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v4x/define/AbstractGatewayV4EnhancePluginDefine.java +++ b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v4x/define/AbstractGatewayV4EnhancePluginDefine.java @@ -17,8 +17,15 @@ package org.apache.skywalking.apm.plugin.spring.cloud.gateway.v4x.define; +import org.apache.skywalking.apm.agent.core.plugin.WitnessMethod; import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine; +import java.util.Collections; +import java.util.List; + +import static net.bytebuddy.matcher.ElementMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.returns; + /** * This abstract class defines the witnessClasses() method, * and other plugin define classes need to inherit from this class @@ -32,4 +39,12 @@ public abstract class AbstractGatewayV4EnhancePluginDefine extends ClassInstance protected String[] witnessClasses() { return new String[]{"org.springframework.cloud.gateway.filter.factory.cache.LocalResponseCacheProperties"}; } + + @Override + protected List witnessMethods() { + return Collections.singletonList( + new WitnessMethod("org.springframework.cloud.gateway.config.LocalResponseCacheAutoConfiguration", + named("responseCacheSizeWeigher"). + and(returns(named("org.springframework.cloud.gateway.filter.factory.cache.ResponseCacheSizeWeigher"))))); + } } diff --git a/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v4x/define/EnhanceObjectCache.java b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v4x/define/EnhanceObjectCache.java index 419d5118a2..335d5ab6cd 100644 --- a/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v4x/define/EnhanceObjectCache.java +++ b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v4x/define/EnhanceObjectCache.java @@ -17,6 +17,7 @@ package org.apache.skywalking.apm.plugin.spring.cloud.gateway.v4x.define; +import org.apache.skywalking.apm.agent.core.context.ContextSnapshot; import org.apache.skywalking.apm.agent.core.context.trace.AbstractSpan; /** @@ -27,6 +28,7 @@ public class EnhanceObjectCache { private String url; private AbstractSpan span; private AbstractSpan span1; + private ContextSnapshot contextSnapshot; public String getUrl() { return url; @@ -51,4 +53,13 @@ public AbstractSpan getSpan1() { public void setSpan1(final AbstractSpan span) { span1 = span; } + + public ContextSnapshot getContextSnapshot() { + return contextSnapshot; + } + + public void setContextSnapshot(final ContextSnapshot contextSnapshot) { + this.contextSnapshot = contextSnapshot; + } + } diff --git a/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v4x/define/GatewayFilterInstrumentation.java b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v4x/define/GatewayFilterInstrumentation.java new file mode 100644 index 0000000000..e6bbde9c8a --- /dev/null +++ b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v4x/define/GatewayFilterInstrumentation.java @@ -0,0 +1,67 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.skywalking.apm.plugin.spring.cloud.gateway.v4x.define; + +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.matcher.ElementMatcher; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch; +import org.apache.skywalking.apm.agent.core.plugin.match.HierarchyMatch; + +import static net.bytebuddy.matcher.ElementMatchers.named; +import static org.apache.skywalking.apm.agent.core.plugin.bytebuddy.ArgumentTypeNameMatch.takesArgumentWithType; + +public class GatewayFilterInstrumentation extends AbstractGatewayV4EnhancePluginDefine { + + private static final String INTERCEPT_CLASS_GATEWAY_FILTER = "org.springframework.cloud.gateway.filter.GatewayFilter"; + private static final String GATEWAY_FILTER_INTERCEPTOR = "org.apache.skywalking.apm.plugin.spring.cloud.gateway.v4x.GatewayFilterInterceptor"; + + @Override + protected ClassMatch enhanceClass() { + return HierarchyMatch.byHierarchyMatch(INTERCEPT_CLASS_GATEWAY_FILTER); + } + + @Override + public ConstructorInterceptPoint[] getConstructorsInterceptPoints() { + return new ConstructorInterceptPoint[0]; + } + + @Override + public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() { + return new InstanceMethodsInterceptPoint[] { + new InstanceMethodsInterceptPoint() { + @Override + public ElementMatcher getMethodsMatcher() { + return named("filter").and( + takesArgumentWithType(0, "org.springframework.web.server.ServerWebExchange")); + } + + @Override + public String getMethodsInterceptor() { + return GATEWAY_FILTER_INTERCEPTOR; + } + + @Override + public boolean isOverrideArgs() { + return false; + } + } + }; + } +} diff --git a/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-4.x-plugin/src/main/resources/skywalking-plugin.def b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-4.x-plugin/src/main/resources/skywalking-plugin.def index 5cbcbfcdd8..bbb2bb4cf9 100644 --- a/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-4.x-plugin/src/main/resources/skywalking-plugin.def +++ b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-4.x-plugin/src/main/resources/skywalking-plugin.def @@ -18,3 +18,8 @@ spring-cloud-gateway-4.x=org.apache.skywalking.apm.plugin.spring.cloud.gateway.v spring-cloud-gateway-4.x=org.apache.skywalking.apm.plugin.spring.cloud.gateway.v4x.define.NettyRoutingFilterInstrumentation spring-cloud-gateway-4.x=org.apache.skywalking.apm.plugin.spring.cloud.gateway.v4x.define.ServerWebExchangeInstrumentation spring-cloud-gateway-4.x=org.apache.skywalking.apm.plugin.spring.cloud.gateway.v4x.define.DispatcherHandlerInstrumentation +spring-cloud-gateway-4.x=org.apache.skywalking.apm.plugin.spring.cloud.gateway.v4x.define.GatewayFilterInstrumentation +spring-cloud-gateway-4.x=org.apache.skywalking.apm.plugin.spring.cloud.gateway.v412x.define.HttpClientFinalizerV412Instrumentation +spring-cloud-gateway-4.x=org.apache.skywalking.apm.plugin.spring.cloud.gateway.v412x.define.NettyRoutingFilterV412Instrumentation +spring-cloud-gateway-4.x=org.apache.skywalking.apm.plugin.spring.cloud.gateway.v412x.define.GatewayFilterV412Instrumentation +spring-cloud-gateway-4.x=org.apache.skywalking.apm.plugin.spring.cloud.gateway.v412x.define.HttpClientConnectV412Instrumentation diff --git a/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-4.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v412x/GatewayFilterV412InterceptorTest.java b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-4.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v412x/GatewayFilterV412InterceptorTest.java new file mode 100644 index 0000000000..d8265ee075 --- /dev/null +++ b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-4.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v412x/GatewayFilterV412InterceptorTest.java @@ -0,0 +1,332 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.spring.cloud.gateway.v412x; + +import org.apache.skywalking.apm.agent.core.context.ContextManager; +import org.apache.skywalking.apm.agent.core.context.ContextSnapshot; +import org.apache.skywalking.apm.agent.core.context.trace.AbstractSpan; +import org.apache.skywalking.apm.agent.core.context.trace.AbstractTracingSpan; +import org.apache.skywalking.apm.agent.core.context.trace.SpanLayer; +import org.apache.skywalking.apm.agent.core.context.trace.TraceSegment; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; +import org.apache.skywalking.apm.agent.test.helper.SegmentHelper; +import org.apache.skywalking.apm.agent.test.tools.AgentServiceRule; +import org.apache.skywalking.apm.agent.test.tools.SegmentStorage; +import org.apache.skywalking.apm.agent.test.tools.SegmentStoragePoint; +import org.apache.skywalking.apm.agent.test.tools.SpanAssert; +import org.apache.skywalking.apm.agent.test.tools.TracingSegmentRunner; +import org.apache.skywalking.apm.network.trace.component.ComponentsDefine; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; +import org.springframework.context.ApplicationContext; +import org.springframework.context.i18n.LocaleContext; +import org.springframework.http.codec.multipart.Part; +import org.springframework.http.server.reactive.ServerHttpRequest; +import org.springframework.http.server.reactive.ServerHttpResponse; +import org.springframework.util.MultiValueMap; +import org.springframework.web.server.ServerWebExchange; +import org.springframework.web.server.WebSession; +import reactor.core.publisher.Mono; + +import java.security.Principal; +import java.time.Instant; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Function; + +@RunWith(TracingSegmentRunner.class) +public class GatewayFilterV412InterceptorTest { + + private static final String GATEWAY_FILTER_INTERCEPTOR_LOCAL_SPAN_OPERATION_NAME = "SpringCloudGateway/GatewayFilter"; + + private static class ServerWebExchangeEnhancedInstance implements ServerWebExchange, EnhancedInstance { + private ContextSnapshot snapshot; + Map attributes = new HashMap<>(); + + @Override + public Object getSkyWalkingDynamicField() { + return snapshot; + } + + @Override + public void setSkyWalkingDynamicField(Object value) { + this.snapshot = (ContextSnapshot) value; + } + + @Override + public ServerHttpRequest getRequest() { + return null; + } + + @Override + public ServerHttpResponse getResponse() { + return null; + } + + @Override + public Map getAttributes() { + return attributes; + } + + @Override + public Mono getSession() { + return null; + } + + @Override + public Mono getPrincipal() { + return null; + } + + @Override + public Mono> getFormData() { + return null; + } + + @Override + public Mono> getMultipartData() { + return null; + } + + @Override + public LocaleContext getLocaleContext() { + return null; + } + + @Override + public ApplicationContext getApplicationContext() { + return null; + } + + @Override + public boolean isNotModified() { + return false; + } + + @Override + public boolean checkNotModified(Instant instant) { + return false; + } + + @Override + public boolean checkNotModified(String s) { + return false; + } + + @Override + public boolean checkNotModified(String s, Instant instant) { + return false; + } + + @Override + public String transformUrl(String s) { + return null; + } + + @Override + public void addUrlTransformer(Function function) { + + } + + @Override + public String getLogPrefix() { + return null; + } + } + + private final ServerWebExchangeEnhancedInstance enhancedInstance = new ServerWebExchangeEnhancedInstance(); + private final GatewayFilterV412Interceptor interceptor = new GatewayFilterV412Interceptor(); + @Rule + public AgentServiceRule serviceRule = new AgentServiceRule(); + @Rule + public MockitoRule rule = MockitoJUnit.rule(); + + @SegmentStoragePoint + private SegmentStorage segmentStorage; + + @Before + public void setUp() throws Exception { + } + + private final ServerWebExchange exchange = new ServerWebExchange() { + Map attributes = new HashMap<>(); + @Override + public ServerHttpRequest getRequest() { + return null; + } + + @Override + public ServerHttpResponse getResponse() { + return null; + } + + @Override + public Map getAttributes() { + return attributes; + } + + @Override + public Mono getSession() { + return null; + } + + @Override + public Mono getPrincipal() { + return null; + } + + @Override + public Mono> getFormData() { + return null; + } + + @Override + public Mono> getMultipartData() { + return null; + } + + @Override + public LocaleContext getLocaleContext() { + return null; + } + + @Override + public ApplicationContext getApplicationContext() { + return null; + } + + @Override + public boolean isNotModified() { + return false; + } + + @Override + public boolean checkNotModified(Instant instant) { + return false; + } + + @Override + public boolean checkNotModified(String s) { + return false; + } + + @Override + public boolean checkNotModified(String s, Instant instant) { + return false; + } + + @Override + public String transformUrl(String s) { + return null; + } + + @Override + public void addUrlTransformer(Function function) { + + } + + @Override + public String getLogPrefix() { + return null; + } + }; + + @Test + public void testInterceptOnlyOnce() throws Throwable { + interceptor.beforeMethod(null, null, new Object[]{exchange}, null, null); + Assert.assertTrue(ContextManager.isActive()); + AbstractSpan activeSpan = ContextManager.activeSpan(); + Assert.assertTrue(activeSpan instanceof AbstractTracingSpan); + Assert.assertEquals(activeSpan.getOperationName(), GATEWAY_FILTER_INTERCEPTOR_LOCAL_SPAN_OPERATION_NAME); + + interceptor.afterMethod(null, null, null, null, null); + Assert.assertFalse(ContextManager.isActive()); + final List traceSegments = segmentStorage.getTraceSegments(); + Assert.assertEquals(traceSegments.size(), 1); + final List spans = SegmentHelper.getSpans(traceSegments.get(0)); + Assert.assertNotNull(spans); + Assert.assertEquals(spans.size(), 1); + SpanAssert.assertComponent(spans.get(0), ComponentsDefine.SPRING_CLOUD_GATEWAY); + + } + + @Test + public void testNestedInterception() throws Throwable { + interceptor.beforeMethod(null, null, new Object[]{enhancedInstance}, null, null); + Assert.assertTrue(ContextManager.isActive()); + + interceptor.beforeMethod(null, null, new Object[]{enhancedInstance}, null, null); + Assert.assertTrue(ContextManager.isActive()); + + interceptor.afterMethod(null, null, null, null, null); + Assert.assertTrue(ContextManager.isActive()); + + interceptor.afterMethod(null, null, null, null, null); + Assert.assertFalse(ContextManager.isActive()); + + final List traceSegments = segmentStorage.getTraceSegments(); + Assert.assertEquals(traceSegments.size(), 1); + final List spans = SegmentHelper.getSpans(traceSegments.get(0)); + Assert.assertNotNull(spans); + Assert.assertEquals(spans.size(), 1); + SpanAssert.assertComponent(spans.get(0), ComponentsDefine.SPRING_CLOUD_GATEWAY); + } + + @Test + public void testWithNullDynamicField() throws Throwable { + interceptor.beforeMethod(null, null, new Object[]{enhancedInstance}, null, null); + interceptor.afterMethod(null, null, null, null, null); + // no more need this, span was stopped at interceptor#afterMethod + // ContextManager.stopSpan(); + final List traceSegments = segmentStorage.getTraceSegments(); + Assert.assertEquals(traceSegments.size(), 1); + final List spans = SegmentHelper.getSpans(traceSegments.get(0)); + Assert.assertNotNull(spans); + Assert.assertEquals(spans.size(), 1); + SpanAssert.assertComponent(spans.get(0), ComponentsDefine.SPRING_CLOUD_GATEWAY); + } + + @Test + public void testWithContextSnapshot() throws Throwable { + final AbstractSpan entrySpan = ContextManager.createEntrySpan("/get", null); + SpanLayer.asHttp(entrySpan); + entrySpan.setComponent(ComponentsDefine.SPRING_WEBFLUX); + enhancedInstance.setSkyWalkingDynamicField(ContextManager.capture()); + interceptor.beforeMethod(null, null, new Object[]{enhancedInstance}, null, null); + interceptor.afterMethod(null, null, null, null, null); + // no more need this, span was stopped at interceptor#afterMethod + // ContextManager.stopSpan(); + ContextManager.stopSpan(entrySpan); + final List traceSegments = segmentStorage.getTraceSegments(); + Assert.assertEquals(traceSegments.size(), 1); + final List spans = SegmentHelper.getSpans(traceSegments.get(0)); + Assert.assertNotNull(spans); + Assert.assertEquals(spans.size(), 2); + SpanAssert.assertComponent(spans.get(0), ComponentsDefine.SPRING_CLOUD_GATEWAY); + SpanAssert.assertComponent(spans.get(1), ComponentsDefine.SPRING_WEBFLUX); + SpanAssert.assertLayer(spans.get(1), SpanLayer.HTTP); + } + +} diff --git a/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-4.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v412x/HttpClientConnectDuplicateV412InterceptorTest.java b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-4.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v412x/HttpClientConnectDuplicateV412InterceptorTest.java new file mode 100644 index 0000000000..8cf3e9d830 --- /dev/null +++ b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-4.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v412x/HttpClientConnectDuplicateV412InterceptorTest.java @@ -0,0 +1,125 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.spring.cloud.gateway.v412x; + +import org.apache.skywalking.apm.agent.core.context.ContextManager; +import org.apache.skywalking.apm.agent.core.context.ContextSnapshot; +import org.apache.skywalking.apm.agent.core.context.trace.AbstractSpan; +import org.apache.skywalking.apm.agent.core.context.trace.SpanLayer; +import org.apache.skywalking.apm.agent.core.context.trace.TraceSegment; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; +import org.apache.skywalking.apm.agent.test.tools.AgentServiceRule; +import org.apache.skywalking.apm.agent.test.tools.SegmentStorage; +import org.apache.skywalking.apm.agent.test.tools.SegmentStoragePoint; +import org.apache.skywalking.apm.agent.test.tools.TracingSegmentRunner; +import org.apache.skywalking.apm.network.trace.component.ComponentsDefine; +import org.apache.skywalking.apm.plugin.spring.cloud.gateway.v4x.define.EnhanceObjectCache; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +@RunWith(TracingSegmentRunner.class) +public class HttpClientConnectDuplicateV412InterceptorTest { + + private final static String ENTRY_OPERATION_NAME = "/get"; + private final HttpClientConnectDuplicateV412Interceptor duplicateInterceptor = new HttpClientConnectDuplicateV412Interceptor(); + private final EnhancedInstance enhancedInstance = new EnhancedInstance() { + private EnhanceObjectCache enhanceObjectCache; + + @Override + public Object getSkyWalkingDynamicField() { + return enhanceObjectCache; + } + + @Override + public void setSkyWalkingDynamicField(Object value) { + this.enhanceObjectCache = (EnhanceObjectCache) value; + } + }; + private final EnhancedInstance retEnhancedInstance = new EnhancedInstance() { + private EnhanceObjectCache enhanceObjectCache; + @Override + public Object getSkyWalkingDynamicField() { + return enhanceObjectCache; + } + + @Override + public void setSkyWalkingDynamicField(Object value) { + this.enhanceObjectCache = (EnhanceObjectCache) value; + } + }; + + @Rule + public AgentServiceRule serviceRule = new AgentServiceRule(); + @Rule + public MockitoRule rule = MockitoJUnit.rule(); + + @SegmentStoragePoint + private SegmentStorage segmentStorage; + private AbstractSpan entrySpan; + + @Before + public void setUp() throws Exception { + entrySpan = ContextManager.createEntrySpan(ENTRY_OPERATION_NAME, null); + entrySpan.setLayer(SpanLayer.HTTP); + entrySpan.setComponent(ComponentsDefine.SPRING_WEBFLUX); + } + + @Test + public void testWithDynamicFieldNull() throws Throwable { + enhancedInstance.setSkyWalkingDynamicField(null); + duplicateInterceptor.afterMethod(enhancedInstance, null, null, null, retEnhancedInstance); + assertNull(retEnhancedInstance.getSkyWalkingDynamicField()); + final List traceSegments = segmentStorage.getTraceSegments(); + assertEquals(traceSegments.size(), 0); + if (ContextManager.isActive()) { + ContextManager.stopSpan(); + } + } + + @Test + public void testWithDynamicFieldNotNull() throws Throwable { + ContextSnapshot contextSnapshot = ContextManager.capture(); + EnhanceObjectCache enhanceObjectCache = new EnhanceObjectCache(); + enhanceObjectCache.setContextSnapshot(contextSnapshot); + enhancedInstance.setSkyWalkingDynamicField(enhanceObjectCache); + duplicateInterceptor.afterMethod(enhancedInstance, null, null, null, retEnhancedInstance); + Object retEnhancedInstanceSkyWalkingDynamicField = retEnhancedInstance.getSkyWalkingDynamicField(); + assertNotNull(retEnhancedInstanceSkyWalkingDynamicField); + assertTrue(retEnhancedInstanceSkyWalkingDynamicField instanceof EnhanceObjectCache); + EnhanceObjectCache retEnhanceObjectCache = (EnhanceObjectCache) retEnhancedInstanceSkyWalkingDynamicField; + assertNotNull(retEnhanceObjectCache.getContextSnapshot()); + final List traceSegments = segmentStorage.getTraceSegments(); + assertEquals(traceSegments.size(), 0); + if (ContextManager.isActive()) { + ContextManager.stopSpan(); + } + } + +} diff --git a/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-4.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v412x/HttpClientConnectRequestV412InterceptorTest.java b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-4.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v412x/HttpClientConnectRequestV412InterceptorTest.java new file mode 100644 index 0000000000..06668c1eb2 --- /dev/null +++ b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-4.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v412x/HttpClientConnectRequestV412InterceptorTest.java @@ -0,0 +1,165 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.spring.cloud.gateway.v412x; + +import org.apache.skywalking.apm.agent.core.context.ContextManager; +import org.apache.skywalking.apm.agent.core.context.ContextSnapshot; +import org.apache.skywalking.apm.agent.core.context.trace.AbstractSpan; +import org.apache.skywalking.apm.agent.core.context.trace.SpanLayer; +import org.apache.skywalking.apm.agent.core.context.trace.TraceSegment; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; +import org.apache.skywalking.apm.agent.test.tools.AgentServiceRule; +import org.apache.skywalking.apm.agent.test.tools.SegmentStorage; +import org.apache.skywalking.apm.agent.test.tools.SegmentStoragePoint; +import org.apache.skywalking.apm.agent.test.tools.TracingSegmentRunner; +import org.apache.skywalking.apm.network.trace.component.ComponentsDefine; +import org.apache.skywalking.apm.plugin.spring.cloud.gateway.v4x.define.EnhanceObjectCache; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +@RunWith(TracingSegmentRunner.class) +public class HttpClientConnectRequestV412InterceptorTest { + private final static String URI = "http://localhost:8080/get"; + private final static String ENTRY_OPERATION_NAME = "/get"; + private final HttpClientConnectRequestV412Interceptor requestInterceptor = new HttpClientConnectRequestV412Interceptor(); + private final EnhancedInstance enhancedInstance = new EnhancedInstance() { + private EnhanceObjectCache enhanceObjectCache; + + @Override + public Object getSkyWalkingDynamicField() { + return enhanceObjectCache; + } + + @Override + public void setSkyWalkingDynamicField(Object value) { + this.enhanceObjectCache = (EnhanceObjectCache) value; + } + }; + private final EnhancedInstance retEnhancedInstance = new EnhancedInstance() { + private EnhanceObjectCache enhanceObjectCache; + @Override + public Object getSkyWalkingDynamicField() { + return enhanceObjectCache; + } + + @Override + public void setSkyWalkingDynamicField(Object value) { + this.enhanceObjectCache = (EnhanceObjectCache) value; + } + }; + + @Rule + public AgentServiceRule serviceRule = new AgentServiceRule(); + @Rule + public MockitoRule rule = MockitoJUnit.rule(); + + @SegmentStoragePoint + private SegmentStorage segmentStorage; + private AbstractSpan entrySpan; + + @Before + public void setUp() throws Exception { + entrySpan = ContextManager.createEntrySpan(ENTRY_OPERATION_NAME, null); + entrySpan.setLayer(SpanLayer.HTTP); + entrySpan.setComponent(ComponentsDefine.SPRING_WEBFLUX); + } + + @Test + public void testWithDynamicFieldNull() throws Throwable { + enhancedInstance.setSkyWalkingDynamicField(null); + retEnhancedInstance.setSkyWalkingDynamicField(null); + requestInterceptor.afterMethod(enhancedInstance, null, null, null, retEnhancedInstance); + assertNull(retEnhancedInstance.getSkyWalkingDynamicField()); + final List traceSegments = segmentStorage.getTraceSegments(); + assertEquals(traceSegments.size(), 0); + if (ContextManager.isActive()) { + ContextManager.stopSpan(); + } + } + + @Test + public void testWithRetDynamicFieldNotNull() throws Throwable { + enhancedInstance.setSkyWalkingDynamicField(null); + EnhanceObjectCache retEnhanceObjectCache = new EnhanceObjectCache(); + retEnhancedInstance.setSkyWalkingDynamicField(retEnhanceObjectCache); + + requestInterceptor.afterMethod(enhancedInstance, null, null, null, retEnhancedInstance); + assertNotNull(retEnhancedInstance.getSkyWalkingDynamicField()); + assertTrue(retEnhancedInstance.getSkyWalkingDynamicField() instanceof EnhanceObjectCache); + assertNull(((EnhanceObjectCache) retEnhancedInstance.getSkyWalkingDynamicField()).getContextSnapshot()); + final List traceSegments = segmentStorage.getTraceSegments(); + assertEquals(traceSegments.size(), 0); + if (ContextManager.isActive()) { + ContextManager.stopSpan(); + } + } + + @Test + public void testWithDynamicFieldNotNull() throws Throwable { + ContextSnapshot contextSnapshot = ContextManager.capture(); + EnhanceObjectCache enhanceObjectCache = new EnhanceObjectCache(); + enhanceObjectCache.setContextSnapshot(contextSnapshot); + enhancedInstance.setSkyWalkingDynamicField(enhanceObjectCache); + + EnhanceObjectCache enhanceObjectCache2 = new EnhanceObjectCache(); + enhanceObjectCache2.setUrl(URI); + retEnhancedInstance.setSkyWalkingDynamicField(enhanceObjectCache2); + requestInterceptor.afterMethod(enhancedInstance, null, null, null, retEnhancedInstance); + Object retEnhancedInstanceSkyWalkingDynamicField = retEnhancedInstance.getSkyWalkingDynamicField(); + assertNotNull(retEnhancedInstanceSkyWalkingDynamicField); + assertTrue(retEnhancedInstanceSkyWalkingDynamicField instanceof EnhanceObjectCache); + EnhanceObjectCache retEnhanceObjectCache = (EnhanceObjectCache) retEnhancedInstanceSkyWalkingDynamicField; + assertNotNull(retEnhanceObjectCache.getContextSnapshot()); + final List traceSegments = segmentStorage.getTraceSegments(); + assertEquals(traceSegments.size(), 0); + if (ContextManager.isActive()) { + ContextManager.stopSpan(); + } + } + + @Test + public void testWithRetDynamicFieldNull() throws Throwable { + ContextSnapshot contextSnapshot = ContextManager.capture(); + EnhanceObjectCache enhanceObjectCache = new EnhanceObjectCache(); + enhanceObjectCache.setContextSnapshot(contextSnapshot); + enhancedInstance.setSkyWalkingDynamicField(enhanceObjectCache); + retEnhancedInstance.setSkyWalkingDynamicField(null); + + requestInterceptor.afterMethod(enhancedInstance, null, null, null, retEnhancedInstance); + Object retEnhancedInstanceSkyWalkingDynamicField = retEnhancedInstance.getSkyWalkingDynamicField(); + assertNull(retEnhancedInstanceSkyWalkingDynamicField); + final List traceSegments = segmentStorage.getTraceSegments(); + assertEquals(traceSegments.size(), 0); + if (ContextManager.isActive()) { + ContextManager.stopSpan(); + } + } + +} diff --git a/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-4.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v412x/HttpClientFinalizerConstructorV412InterceptorTest.java b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-4.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v412x/HttpClientFinalizerConstructorV412InterceptorTest.java new file mode 100644 index 0000000000..a87a4e4979 --- /dev/null +++ b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-4.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v412x/HttpClientFinalizerConstructorV412InterceptorTest.java @@ -0,0 +1,68 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.spring.cloud.gateway.v412x; + +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; +import org.apache.skywalking.apm.plugin.spring.cloud.gateway.v4x.define.EnhanceObjectCache; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.junit.MockitoJUnitRunner; +import reactor.netty.http.client.HttpClientConfig; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +@RunWith(MockitoJUnitRunner.class) +public class HttpClientFinalizerConstructorV412InterceptorTest { + + private static final String URI = "http://localhost:8080/get"; + private final EnhancedInstance enhancedInstance = new EnhancedInstance() { + private EnhanceObjectCache enhanceObjectCache; + + @Override + public Object getSkyWalkingDynamicField() { + return enhanceObjectCache; + } + + @Override + public void setSkyWalkingDynamicField(Object value) { + this.enhanceObjectCache = (EnhanceObjectCache) value; + } + }; + private HttpClientConfig httpClientConfig; + private HttpClientFinalizerConstructorV412Interceptor httpClientFinalizerConstructorInterceptor; + + @Before + public void setUp() { + httpClientConfig = mock(HttpClientConfig.class); + when(httpClientConfig.uri()).thenReturn(URI); + httpClientFinalizerConstructorInterceptor = new HttpClientFinalizerConstructorV412Interceptor(); + } + + @Test + public void onConstruct() { + httpClientFinalizerConstructorInterceptor.onConstruct(enhancedInstance, new Object[]{httpClientConfig}); + final EnhanceObjectCache enhanceCache = (EnhanceObjectCache) enhancedInstance.getSkyWalkingDynamicField(); + assertNotNull(enhanceCache); + assertEquals(enhanceCache.getUrl(), URI); + } +} diff --git a/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-4.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v412x/HttpClientFinalizerUriV412InterceptorTest.java b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-4.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v412x/HttpClientFinalizerUriV412InterceptorTest.java new file mode 100644 index 0000000000..0cb079b299 --- /dev/null +++ b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-4.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v412x/HttpClientFinalizerUriV412InterceptorTest.java @@ -0,0 +1,176 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.spring.cloud.gateway.v412x; + +import org.apache.skywalking.apm.agent.core.context.ContextManager; +import org.apache.skywalking.apm.agent.core.context.ContextSnapshot; +import org.apache.skywalking.apm.agent.core.context.trace.AbstractSpan; +import org.apache.skywalking.apm.agent.core.context.trace.SpanLayer; +import org.apache.skywalking.apm.agent.core.context.trace.TraceSegment; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; +import org.apache.skywalking.apm.agent.test.tools.AgentServiceRule; +import org.apache.skywalking.apm.agent.test.tools.SegmentStorage; +import org.apache.skywalking.apm.agent.test.tools.SegmentStoragePoint; +import org.apache.skywalking.apm.agent.test.tools.TracingSegmentRunner; +import org.apache.skywalking.apm.network.trace.component.ComponentsDefine; +import org.apache.skywalking.apm.plugin.spring.cloud.gateway.v4x.define.EnhanceObjectCache; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +@RunWith(TracingSegmentRunner.class) +public class HttpClientFinalizerUriV412InterceptorTest { + + private final static String URI = "http://localhost:8080/get"; + private final static String ENTRY_OPERATION_NAME = "/get"; + private final HttpClientFinalizerUriV412Interceptor uriInterceptor = new HttpClientFinalizerUriV412Interceptor(); + private final EnhancedInstance enhancedInstance = new EnhancedInstance() { + private EnhanceObjectCache enhanceObjectCache; + + @Override + public Object getSkyWalkingDynamicField() { + return enhanceObjectCache; + } + + @Override + public void setSkyWalkingDynamicField(Object value) { + this.enhanceObjectCache = (EnhanceObjectCache) value; + } + }; + private final EnhancedInstance retEnhancedInstance = new EnhancedInstance() { + private EnhanceObjectCache enhanceObjectCache; + @Override + public Object getSkyWalkingDynamicField() { + return enhanceObjectCache; + } + + @Override + public void setSkyWalkingDynamicField(Object value) { + this.enhanceObjectCache = (EnhanceObjectCache) value; + } + }; + + @Rule + public AgentServiceRule serviceRule = new AgentServiceRule(); + @Rule + public MockitoRule rule = MockitoJUnit.rule(); + + @SegmentStoragePoint + private SegmentStorage segmentStorage; + private AbstractSpan entrySpan; + + @Before + public void setUp() throws Exception { + entrySpan = ContextManager.createEntrySpan(ENTRY_OPERATION_NAME, null); + entrySpan.setLayer(SpanLayer.HTTP); + entrySpan.setComponent(ComponentsDefine.SPRING_WEBFLUX); + } + + @Test + public void testWithDynamicFieldNull() throws Throwable { + enhancedInstance.setSkyWalkingDynamicField(null); + retEnhancedInstance.setSkyWalkingDynamicField(null); + uriInterceptor.afterMethod(enhancedInstance, null, null, null, retEnhancedInstance); + assertNull(retEnhancedInstance.getSkyWalkingDynamicField()); + final List traceSegments = segmentStorage.getTraceSegments(); + assertEquals(traceSegments.size(), 0); + if (ContextManager.isActive()) { + ContextManager.stopSpan(); + } + } + + @Test + public void testWithRetDynamicFieldNotNull() throws Throwable { + enhancedInstance.setSkyWalkingDynamicField(null); + EnhanceObjectCache enhanceObjectCache2 = new EnhanceObjectCache(); + retEnhancedInstance.setSkyWalkingDynamicField(enhanceObjectCache2); + + uriInterceptor.afterMethod(enhancedInstance, null, new Object[]{URI}, null, retEnhancedInstance); + Object retEnhancedInstanceSkyWalkingDynamicField = retEnhancedInstance.getSkyWalkingDynamicField(); + assertNotNull(retEnhancedInstanceSkyWalkingDynamicField); + assertTrue(retEnhancedInstanceSkyWalkingDynamicField instanceof EnhanceObjectCache); + EnhanceObjectCache retEnhanceObjectCache = (EnhanceObjectCache) retEnhancedInstanceSkyWalkingDynamicField; + assertNull(retEnhanceObjectCache.getContextSnapshot()); + assertNotNull(retEnhanceObjectCache.getUrl()); + assertEquals(URI, retEnhanceObjectCache.getUrl()); + final List traceSegments = segmentStorage.getTraceSegments(); + assertEquals(traceSegments.size(), 0); + if (ContextManager.isActive()) { + ContextManager.stopSpan(); + } + } + + @Test + public void testWithDynamicFieldNotNull() throws Throwable { + ContextSnapshot contextSnapshot = ContextManager.capture(); + EnhanceObjectCache enhanceObjectCache = new EnhanceObjectCache(); + enhanceObjectCache.setContextSnapshot(contextSnapshot); + enhancedInstance.setSkyWalkingDynamicField(enhanceObjectCache); + EnhanceObjectCache enhanceObjectCache2 = new EnhanceObjectCache(); + retEnhancedInstance.setSkyWalkingDynamicField(enhanceObjectCache2); + + uriInterceptor.afterMethod(enhancedInstance, null, new Object[]{URI}, null, retEnhancedInstance); + Object retEnhancedInstanceSkyWalkingDynamicField = retEnhancedInstance.getSkyWalkingDynamicField(); + assertNotNull(retEnhancedInstanceSkyWalkingDynamicField); + assertTrue(retEnhancedInstanceSkyWalkingDynamicField instanceof EnhanceObjectCache); + EnhanceObjectCache retEnhanceObjectCache = (EnhanceObjectCache) retEnhancedInstanceSkyWalkingDynamicField; + assertNotNull(retEnhanceObjectCache.getContextSnapshot()); + assertTrue(contextSnapshot == retEnhanceObjectCache.getContextSnapshot()); + assertNotNull(retEnhanceObjectCache.getUrl()); + assertEquals(URI, retEnhanceObjectCache.getUrl()); + final List traceSegments = segmentStorage.getTraceSegments(); + assertEquals(traceSegments.size(), 0); + if (ContextManager.isActive()) { + ContextManager.stopSpan(); + } + } + + @Test + public void testWithRetDynamicFieldNull() throws Throwable { + ContextSnapshot contextSnapshot = ContextManager.capture(); + EnhanceObjectCache enhanceObjectCache = new EnhanceObjectCache(); + enhanceObjectCache.setContextSnapshot(contextSnapshot); + enhancedInstance.setSkyWalkingDynamicField(enhanceObjectCache); + retEnhancedInstance.setSkyWalkingDynamicField(null); + + uriInterceptor.afterMethod(enhancedInstance, null, new Object[]{URI}, null, retEnhancedInstance); + Object retEnhancedInstanceSkyWalkingDynamicField = retEnhancedInstance.getSkyWalkingDynamicField(); + assertNull(retEnhancedInstanceSkyWalkingDynamicField); + assertNotNull(enhancedInstance.getSkyWalkingDynamicField()); + assertTrue(enhancedInstance.getSkyWalkingDynamicField() instanceof EnhanceObjectCache); + EnhanceObjectCache objInstObjectCache = (EnhanceObjectCache) enhancedInstance.getSkyWalkingDynamicField(); + assertTrue(contextSnapshot == objInstObjectCache.getContextSnapshot()); + assertNull(objInstObjectCache.getUrl()); + final List traceSegments = segmentStorage.getTraceSegments(); + assertEquals(traceSegments.size(), 0); + if (ContextManager.isActive()) { + ContextManager.stopSpan(); + } + } +} diff --git a/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-4.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v412x/HttpClientFinalizerV412InterceptorTest.java b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-4.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v412x/HttpClientFinalizerV412InterceptorTest.java new file mode 100644 index 0000000000..8018013db0 --- /dev/null +++ b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-4.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v412x/HttpClientFinalizerV412InterceptorTest.java @@ -0,0 +1,204 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.spring.cloud.gateway.v412x; + +import io.netty.handler.codec.http.HttpResponseStatus; +import org.apache.skywalking.apm.agent.core.context.ContextManager; +import org.apache.skywalking.apm.agent.core.context.trace.AbstractSpan; +import org.apache.skywalking.apm.agent.core.context.trace.AbstractTracingSpan; +import org.apache.skywalking.apm.agent.core.context.trace.SpanLayer; +import org.apache.skywalking.apm.agent.core.context.trace.TraceSegment; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; +import org.apache.skywalking.apm.agent.test.helper.SegmentHelper; +import org.apache.skywalking.apm.agent.test.tools.AgentServiceRule; +import org.apache.skywalking.apm.agent.test.tools.SegmentStorage; +import org.apache.skywalking.apm.agent.test.tools.SegmentStoragePoint; +import org.apache.skywalking.apm.agent.test.tools.SpanAssert; +import org.apache.skywalking.apm.agent.test.tools.TracingSegmentRunner; +import org.apache.skywalking.apm.network.trace.component.ComponentsDefine; +import org.apache.skywalking.apm.plugin.spring.cloud.gateway.v4x.define.EnhanceObjectCache; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; +import org.reactivestreams.Publisher; +import reactor.core.publisher.Flux; +import reactor.netty.Connection; +import reactor.netty.NettyOutbound; +import reactor.netty.http.client.HttpClientRequest; +import reactor.netty.http.client.HttpClientResponse; + +import java.util.List; +import java.util.function.BiFunction; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +@RunWith(TracingSegmentRunner.class) +public class HttpClientFinalizerV412InterceptorTest { + + private final static String URI = "http://localhost:8080/get"; + private final static String ENTRY_OPERATION_NAME = "/get"; + private final HttpClientFinalizerSendV412Interceptor sendInterceptor = new HttpClientFinalizerSendV412Interceptor(); + private final HttpClientFinalizerResponseConnectionV412Interceptor responseConnectionInterceptor = new HttpClientFinalizerResponseConnectionV412Interceptor(); + private final BiFunction> originalSendBiFunction = (httpClientRequest, nettyOutbound) -> (Publisher) s -> { + }; + private final BiFunction> originalResponseConnectionBiFunction = (httpClientResponse, connection) -> (Publisher) s -> { + }; + private final EnhancedInstance enhancedInstance = new EnhancedInstance() { + private EnhanceObjectCache enhanceObjectCache; + + @Override + public Object getSkyWalkingDynamicField() { + return enhanceObjectCache; + } + + @Override + public void setSkyWalkingDynamicField(Object value) { + this.enhanceObjectCache = (EnhanceObjectCache) value; + } + }; + @Rule + public AgentServiceRule serviceRule = new AgentServiceRule(); + @Rule + public MockitoRule rule = MockitoJUnit.rule(); + + private HttpClientResponse mockResponse; + private HttpClientRequest mockRequest; + @SegmentStoragePoint + private SegmentStorage segmentStorage; + private AbstractSpan entrySpan; + + @Before + public void setUp() throws Exception { + entrySpan = ContextManager.createEntrySpan(ENTRY_OPERATION_NAME, null); + entrySpan.setLayer(SpanLayer.HTTP); + entrySpan.setComponent(ComponentsDefine.SPRING_WEBFLUX); + mockRequest = new MockClientRequest(); + mockResponse = new MockClientResponse(); + final EnhanceObjectCache enhanceObjectCache = new EnhanceObjectCache(); + enhanceObjectCache.setUrl(URI); + enhanceObjectCache.setContextSnapshot(ContextManager.capture()); + enhancedInstance.setSkyWalkingDynamicField(enhanceObjectCache); + } + + @Test + public void testWithDynamicFieldNull() throws Throwable { + enhancedInstance.setSkyWalkingDynamicField(null); + executeSendRequest(); + stopEntrySpan(); + final List traceSegments = segmentStorage.getTraceSegments(); + assertEquals(1, traceSegments.size()); + } + + @Test + public void testWithContextSnapshotNull() throws Throwable { + EnhanceObjectCache objectCache = (EnhanceObjectCache) enhancedInstance.getSkyWalkingDynamicField(); + objectCache.setContextSnapshot(null); + executeSendRequest(); + stopEntrySpan(); + final List traceSegments = segmentStorage.getTraceSegments(); + assertEquals(1, traceSegments.size()); + } + + @Test + public void testWithEmptyUri() throws Throwable { + final EnhanceObjectCache objectCache = (EnhanceObjectCache) enhancedInstance.getSkyWalkingDynamicField(); + objectCache.setUrl(""); + executeSendRequest(); + stopEntrySpan(); + final List traceSegments = segmentStorage.getTraceSegments(); + assertEquals(1, traceSegments.size()); + final List spans = SegmentHelper.getSpans(traceSegments.get(0)); + assertNotNull(spans); + assertEquals(2, spans.size()); + assertNotNull(spans.get(1)); + assertUpstreamSpan(spans.get(1)); + assertNotNull(spans.get(0)); + assertSendSpan(spans.get(0)); + assertNotNull(objectCache.getSpan1()); + assertEquals(objectCache.getSpan1().getSpanId(), spans.get(0).getSpanId()); + } + + @Test + public void testWithUri() throws Throwable { + executeSendRequest(); + stopEntrySpan(); + final List traceSegments = segmentStorage.getTraceSegments(); + assertEquals(1, traceSegments.size()); + final EnhanceObjectCache objectCache = (EnhanceObjectCache) enhancedInstance + .getSkyWalkingDynamicField(); + assertNotNull(objectCache.getSpan1()); + assertNotNull(objectCache.getSpan()); + assertTrue(objectCache.getSpan().isExit()); + assertEquals(objectCache.getSpan().getOperationName(), "SpringCloudGateway/sendRequest"); + assertEquals(objectCache.getSpan1().getOperationName(), "SpringCloudGateway/send"); + final List spans = SegmentHelper.getSpans(traceSegments.get(0)); + assertNotNull(spans); + assertEquals(3, spans.size()); + assertUpstreamSpan(spans.get(2)); + assertSendSpan(spans.get(1)); + assertDownstreamSpan(spans.get(0)); + } + + private void executeSendRequest() throws Throwable { + Object[] sendArguments = new Object[]{originalSendBiFunction}; + sendInterceptor.beforeMethod(enhancedInstance, null, sendArguments, null, null); + sendInterceptor.afterMethod(enhancedInstance, null, new Object[0], null, enhancedInstance); + ((BiFunction>) sendArguments[0]) + .apply(mockRequest, null); + Object[] responseConnectionArguments = new Object[]{originalResponseConnectionBiFunction}; + responseConnectionInterceptor + .beforeMethod(enhancedInstance, null, responseConnectionArguments, null, null); + Flux flux = Flux.just(0); + + flux = (Flux) responseConnectionInterceptor.afterMethod(enhancedInstance, null, new Object[0], null, flux); + + ((BiFunction>) responseConnectionArguments[0]) + .apply(mockResponse, null); + flux.blockFirst(); + } + + private void assertUpstreamSpan(AbstractSpan span) { + SpanAssert.assertLayer(span, SpanLayer.HTTP); + SpanAssert.assertComponent(span, ComponentsDefine.SPRING_WEBFLUX); + } + + private void assertSendSpan(AbstractSpan span) { + SpanAssert.assertComponent(span, ComponentsDefine.SPRING_CLOUD_GATEWAY); + assertEquals(span.getOperationName(), "SpringCloudGateway/send"); + } + + private void assertDownstreamSpan(AbstractSpan span) { + SpanAssert.assertLayer(span, SpanLayer.HTTP); + SpanAssert.assertComponent(span, ComponentsDefine.SPRING_CLOUD_GATEWAY); + SpanAssert.assertTagSize(span, 2); + SpanAssert.assertTag(span, 0, URI); + SpanAssert.assertTag(span, 1, String.valueOf(HttpResponseStatus.OK.code())); + } + + private void stopEntrySpan() { + if (ContextManager.isActive() && ContextManager.activeSpan() == entrySpan) { + ContextManager.stopSpan(entrySpan); + } + } +} diff --git a/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-4.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v412x/MockClientRequest.java b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-4.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v412x/MockClientRequest.java new file mode 100644 index 0000000000..ad357e3e29 --- /dev/null +++ b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-4.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v412x/MockClientRequest.java @@ -0,0 +1,135 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.spring.cloud.gateway.v412x; + +import io.netty.handler.codec.http.DefaultHttpHeaders; +import io.netty.handler.codec.http.HttpHeaders; +import io.netty.handler.codec.http.HttpMethod; +import io.netty.handler.codec.http.HttpVersion; +import io.netty.handler.codec.http.cookie.Cookie; +import reactor.netty.http.client.HttpClientRequest; +import reactor.util.context.Context; +import reactor.util.context.ContextView; + +import java.time.Duration; +import java.util.Map; +import java.util.Set; + +public class MockClientRequest implements HttpClientRequest { + + @Override + public HttpClientRequest addCookie(Cookie cookie) { + return null; + } + + @Override + public HttpClientRequest addHeader(CharSequence charSequence, CharSequence charSequence1) { + return null; + } + + @Override + public HttpClientRequest header(CharSequence charSequence, CharSequence charSequence1) { + return null; + } + + @Override + public HttpClientRequest headers(HttpHeaders httpHeaders) { + return null; + } + + @Override + public boolean isFollowRedirect() { + return false; + } + + @Override + public HttpClientRequest responseTimeout(Duration duration) { + return null; + } + + @Override + public Context currentContext() { + return null; + } + + @Override + public ContextView currentContextView() { + return null; + } + + @Override + public String[] redirectedFrom() { + return new String[0]; + } + + @Override + public HttpHeaders requestHeaders() { + return new DefaultHttpHeaders(); + } + + @Override + public String resourceUrl() { + return null; + } + + @Override + public Map> cookies() { + return null; + } + + @Override + public boolean isKeepAlive() { + return false; + } + + @Override + public boolean isWebsocket() { + return false; + } + + @Override + public HttpMethod method() { + return null; + } + + @Override + public String path() { + return HttpClientRequest.super.path(); + } + + @Override + public String fullPath() { + return null; + } + + @Override + public String requestId() { + return null; + } + + @Override + public String uri() { + return null; + } + + @Override + public HttpVersion version() { + return null; + } +} diff --git a/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-4.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v412x/MockClientResponse.java b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-4.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v412x/MockClientResponse.java new file mode 100644 index 0000000000..a2381d0b15 --- /dev/null +++ b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-4.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v412x/MockClientResponse.java @@ -0,0 +1,120 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.spring.cloud.gateway.v412x; + +import io.netty.handler.codec.http.HttpHeaders; +import io.netty.handler.codec.http.HttpMethod; +import io.netty.handler.codec.http.HttpResponseStatus; +import io.netty.handler.codec.http.HttpVersion; +import io.netty.handler.codec.http.cookie.Cookie; +import reactor.core.publisher.Mono; +import reactor.netty.http.client.HttpClientResponse; +import reactor.util.context.Context; +import reactor.util.context.ContextView; + +import java.util.Map; +import java.util.Set; + +public class MockClientResponse implements HttpClientResponse { + + @Override + public HttpHeaders responseHeaders() { + return null; + } + + @Override + public HttpResponseStatus status() { + return HttpResponseStatus.OK; + } + + @Override + public Mono trailerHeaders() { + return null; + } + + @Override + public Context currentContext() { + return null; + } + + @Override + public ContextView currentContextView() { + return null; + } + + @Override + public String[] redirectedFrom() { + return new String[0]; + } + + @Override + public HttpHeaders requestHeaders() { + return null; + } + + @Override + public String resourceUrl() { + return null; + } + + @Override + public Map> cookies() { + return null; + } + + @Override + public boolean isKeepAlive() { + return false; + } + + @Override + public boolean isWebsocket() { + return false; + } + + @Override + public HttpMethod method() { + return null; + } + + @Override + public String path() { + return HttpClientResponse.super.path(); + } + + @Override + public String fullPath() { + return null; + } + + @Override + public String requestId() { + return null; + } + + @Override + public String uri() { + return null; + } + + @Override + public HttpVersion version() { + return null; + } +} diff --git a/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-4.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v412x/NettyRoutingFilterV412InterceptorTest.java b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-4.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v412x/NettyRoutingFilterV412InterceptorTest.java new file mode 100644 index 0000000000..75232fcc1c --- /dev/null +++ b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-4.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v412x/NettyRoutingFilterV412InterceptorTest.java @@ -0,0 +1,222 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.spring.cloud.gateway.v412x; + +import org.apache.skywalking.apm.agent.core.context.ContextManager; +import org.apache.skywalking.apm.agent.core.context.ContextSnapshot; +import org.apache.skywalking.apm.agent.core.context.trace.AbstractSpan; +import org.apache.skywalking.apm.agent.core.context.trace.AbstractTracingSpan; +import org.apache.skywalking.apm.agent.core.context.trace.SpanLayer; +import org.apache.skywalking.apm.agent.core.context.trace.TraceSegment; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; +import org.apache.skywalking.apm.agent.test.helper.SegmentHelper; +import org.apache.skywalking.apm.agent.test.tools.AgentServiceRule; +import org.apache.skywalking.apm.agent.test.tools.SegmentStorage; +import org.apache.skywalking.apm.agent.test.tools.SegmentStoragePoint; +import org.apache.skywalking.apm.agent.test.tools.SpanAssert; +import org.apache.skywalking.apm.agent.test.tools.TracingSegmentRunner; +import org.apache.skywalking.apm.network.trace.component.ComponentsDefine; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; +import org.springframework.context.ApplicationContext; +import org.springframework.context.i18n.LocaleContext; +import org.springframework.http.codec.multipart.Part; +import org.springframework.http.server.reactive.ServerHttpRequest; +import org.springframework.http.server.reactive.ServerHttpResponse; +import org.springframework.util.MultiValueMap; +import org.springframework.web.server.ServerWebExchange; +import org.springframework.web.server.WebSession; +import reactor.core.publisher.Mono; + +import java.security.Principal; +import java.time.Instant; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Function; + +@RunWith(TracingSegmentRunner.class) +public class NettyRoutingFilterV412InterceptorTest { + private static class ServerWebExchangeEnhancedInstance implements ServerWebExchange, EnhancedInstance { + private ContextSnapshot snapshot; + Map attributes = new HashMap<>(); + + @Override + public Object getSkyWalkingDynamicField() { + return snapshot; + } + + @Override + public void setSkyWalkingDynamicField(Object value) { + this.snapshot = (ContextSnapshot) value; + } + + @Override + public ServerHttpRequest getRequest() { + return null; + } + + @Override + public ServerHttpResponse getResponse() { + return null; + } + + @Override + public Map getAttributes() { + return attributes; + } + + @Override + public Mono getSession() { + return null; + } + + @Override + public Mono getPrincipal() { + return null; + } + + @Override + public Mono> getFormData() { + return null; + } + + @Override + public Mono> getMultipartData() { + return null; + } + + @Override + public LocaleContext getLocaleContext() { + return null; + } + + @Override + public ApplicationContext getApplicationContext() { + return null; + } + + @Override + public boolean isNotModified() { + return false; + } + + @Override + public boolean checkNotModified(Instant instant) { + return false; + } + + @Override + public boolean checkNotModified(String s) { + return false; + } + + @Override + public boolean checkNotModified(String s, Instant instant) { + return false; + } + + @Override + public String transformUrl(String s) { + return null; + } + + @Override + public void addUrlTransformer(Function function) { + + } + + @Override + public String getLogPrefix() { + return null; + } + } + + private final ServerWebExchangeEnhancedInstance enhancedInstance = new ServerWebExchangeEnhancedInstance(); + private final NettyRoutingFilterV412Interceptor interceptor = new NettyRoutingFilterV412Interceptor(); + @Rule + public AgentServiceRule serviceRule = new AgentServiceRule(); + @Rule + public MockitoRule rule = MockitoJUnit.rule(); + + @SegmentStoragePoint + private SegmentStorage segmentStorage; + + @Before + public void setUp() throws Exception { + } + + @Test + public void testWithNullDynamicField() throws Throwable { + interceptor.beforeMethod(null, null, new Object[]{enhancedInstance}, null, null); + interceptor.afterMethod(null, null, null, null, null); + // no more need this, span was stopped at interceptor#afterMethod + // ContextManager.stopSpan(); + final List traceSegments = segmentStorage.getTraceSegments(); + Assert.assertEquals(traceSegments.size(), 1); + final List spans = SegmentHelper.getSpans(traceSegments.get(0)); + Assert.assertNotNull(spans); + Assert.assertEquals(1, spans.size()); + SpanAssert.assertComponent(spans.get(0), ComponentsDefine.SPRING_CLOUD_GATEWAY); + } + + @Test + public void testWithContextSnapshot() throws Throwable { + final AbstractSpan entrySpan = ContextManager.createEntrySpan("/get", null); + SpanLayer.asHttp(entrySpan); + entrySpan.setComponent(ComponentsDefine.SPRING_WEBFLUX); + enhancedInstance.setSkyWalkingDynamicField(ContextManager.capture()); + interceptor.beforeMethod(null, null, new Object[]{enhancedInstance}, null, null); + interceptor.afterMethod(null, null, null, null, null); + // no more need this, span was stopped at interceptor#afterMethod + // ContextManager.stopSpan(); + ContextManager.stopSpan(entrySpan); + final List traceSegments = segmentStorage.getTraceSegments(); + Assert.assertEquals(traceSegments.size(), 1); + final List spans = SegmentHelper.getSpans(traceSegments.get(0)); + Assert.assertNotNull(spans); + Assert.assertEquals(spans.size(), 2); + SpanAssert.assertComponent(spans.get(0), ComponentsDefine.SPRING_CLOUD_GATEWAY); + SpanAssert.assertComponent(spans.get(1), ComponentsDefine.SPRING_WEBFLUX); + SpanAssert.assertLayer(spans.get(1), SpanLayer.HTTP); + } + + private static final String NETTY_ROUTING_FILTER_TRACED_ATTR = + NettyRoutingFilterV412Interceptor.class.getName() + ".isTraced"; + + @Test + public void testIsTraced() throws Throwable { + interceptor.beforeMethod(null, null, new Object[]{enhancedInstance}, null, null); + interceptor.afterMethod(null, null, null, null, null); + Assert.assertEquals(enhancedInstance.getAttributes().get(NETTY_ROUTING_FILTER_TRACED_ATTR), true); + Assert.assertFalse(ContextManager.isActive()); + + // no more need this, span was stopped at interceptor#afterMethod + // ContextManager.stopSpan(); + + interceptor.beforeMethod(null, null, new Object[]{enhancedInstance}, null, null); + interceptor.afterMethod(null, null, null, null, null); + Assert.assertEquals(enhancedInstance.getAttributes().get(NETTY_ROUTING_FILTER_TRACED_ATTR), true); + } +} diff --git a/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-4.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v412x/NettyRoutingGetHttpClientV412InterceptorTest.java b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-4.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v412x/NettyRoutingGetHttpClientV412InterceptorTest.java new file mode 100644 index 0000000000..5f3f8811ae --- /dev/null +++ b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-4.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v412x/NettyRoutingGetHttpClientV412InterceptorTest.java @@ -0,0 +1,115 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.spring.cloud.gateway.v412x; + +import org.apache.skywalking.apm.agent.core.context.ContextManager; +import org.apache.skywalking.apm.agent.core.context.trace.AbstractSpan; +import org.apache.skywalking.apm.agent.core.context.trace.SpanLayer; +import org.apache.skywalking.apm.agent.core.context.trace.TraceSegment; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; +import org.apache.skywalking.apm.agent.test.tools.AgentServiceRule; +import org.apache.skywalking.apm.agent.test.tools.SegmentStorage; +import org.apache.skywalking.apm.agent.test.tools.SegmentStoragePoint; +import org.apache.skywalking.apm.agent.test.tools.TracingSegmentRunner; +import org.apache.skywalking.apm.network.trace.component.ComponentsDefine; +import org.apache.skywalking.apm.plugin.spring.cloud.gateway.v4x.define.EnhanceObjectCache; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +@RunWith(TracingSegmentRunner.class) +public class NettyRoutingGetHttpClientV412InterceptorTest { + private final static String ENTRY_OPERATION_NAME = "/get"; + private final NettyRoutingGetHttpClientV412Interceptor interceptor = new NettyRoutingGetHttpClientV412Interceptor(); + private final EnhancedInstance enhancedInstance = new EnhancedInstance() { + private EnhanceObjectCache enhanceObjectCache; + + @Override + public Object getSkyWalkingDynamicField() { + return enhanceObjectCache; + } + + @Override + public void setSkyWalkingDynamicField(Object value) { + this.enhanceObjectCache = (EnhanceObjectCache) value; + } + }; + private final EnhancedInstance retEnhancedInstance = new EnhancedInstance() { + private EnhanceObjectCache enhanceObjectCache; + @Override + public Object getSkyWalkingDynamicField() { + return enhanceObjectCache; + } + + @Override + public void setSkyWalkingDynamicField(Object value) { + this.enhanceObjectCache = (EnhanceObjectCache) value; + } + }; + + @Rule + public AgentServiceRule serviceRule = new AgentServiceRule(); + @Rule + public MockitoRule rule = MockitoJUnit.rule(); + + @SegmentStoragePoint + private SegmentStorage segmentStorage; + private AbstractSpan entrySpan; + + @Before + public void setUp() throws Exception { + } + + @Test + public void testWithContextIsActive() throws Throwable { + entrySpan = ContextManager.createEntrySpan(ENTRY_OPERATION_NAME, null); + entrySpan.setLayer(SpanLayer.HTTP); + entrySpan.setComponent(ComponentsDefine.SPRING_WEBFLUX); + interceptor.afterMethod(enhancedInstance, null, null, null, retEnhancedInstance); + assertNotNull(retEnhancedInstance.getSkyWalkingDynamicField()); + assertTrue(retEnhancedInstance.getSkyWalkingDynamicField() instanceof EnhanceObjectCache); + EnhanceObjectCache enhanceObjectCache = (EnhanceObjectCache) retEnhancedInstance.getSkyWalkingDynamicField(); + assertNotNull(enhanceObjectCache.getContextSnapshot()); + final List traceSegments = segmentStorage.getTraceSegments(); + assertEquals(traceSegments.size(), 0); + if (ContextManager.isActive()) { + ContextManager.stopSpan(); + } + } + + @Test + public void testWithContextNotActive() throws Throwable { + interceptor.afterMethod(enhancedInstance, null, null, null, retEnhancedInstance); + assertNull(retEnhancedInstance.getSkyWalkingDynamicField()); + final List traceSegments = segmentStorage.getTraceSegments(); + assertEquals(traceSegments.size(), 0); + if (ContextManager.isActive()) { + ContextManager.stopSpan(); + } + } +} diff --git a/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-4.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v4x/GatewayFilterInterceptorTest.java b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-4.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v4x/GatewayFilterInterceptorTest.java new file mode 100644 index 0000000000..d59c63cd17 --- /dev/null +++ b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-4.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v4x/GatewayFilterInterceptorTest.java @@ -0,0 +1,332 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.spring.cloud.gateway.v4x; + +import org.apache.skywalking.apm.agent.core.context.ContextManager; +import org.apache.skywalking.apm.agent.core.context.ContextSnapshot; +import org.apache.skywalking.apm.agent.core.context.trace.AbstractSpan; +import org.apache.skywalking.apm.agent.core.context.trace.AbstractTracingSpan; +import org.apache.skywalking.apm.agent.core.context.trace.SpanLayer; +import org.apache.skywalking.apm.agent.core.context.trace.TraceSegment; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; +import org.apache.skywalking.apm.agent.test.helper.SegmentHelper; +import org.apache.skywalking.apm.agent.test.tools.AgentServiceRule; +import org.apache.skywalking.apm.agent.test.tools.SegmentStorage; +import org.apache.skywalking.apm.agent.test.tools.SegmentStoragePoint; +import org.apache.skywalking.apm.agent.test.tools.SpanAssert; +import org.apache.skywalking.apm.agent.test.tools.TracingSegmentRunner; +import org.apache.skywalking.apm.network.trace.component.ComponentsDefine; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; +import org.springframework.context.ApplicationContext; +import org.springframework.context.i18n.LocaleContext; +import org.springframework.http.codec.multipart.Part; +import org.springframework.http.server.reactive.ServerHttpRequest; +import org.springframework.http.server.reactive.ServerHttpResponse; +import org.springframework.util.MultiValueMap; +import org.springframework.web.server.ServerWebExchange; +import org.springframework.web.server.WebSession; +import reactor.core.publisher.Mono; + +import java.security.Principal; +import java.time.Instant; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Function; + +@RunWith(TracingSegmentRunner.class) +public class GatewayFilterInterceptorTest { + + private static final String GATEWAY_FILTER_INTERCEPTOR_LOCAL_SPAN_OPERATION_NAME = "SpringCloudGateway/GatewayFilter"; + + private static class ServerWebExchangeEnhancedInstance implements ServerWebExchange, EnhancedInstance { + private ContextSnapshot snapshot; + Map attributes = new HashMap<>(); + + @Override + public Object getSkyWalkingDynamicField() { + return snapshot; + } + + @Override + public void setSkyWalkingDynamicField(Object value) { + this.snapshot = (ContextSnapshot) value; + } + + @Override + public ServerHttpRequest getRequest() { + return null; + } + + @Override + public ServerHttpResponse getResponse() { + return null; + } + + @Override + public Map getAttributes() { + return attributes; + } + + @Override + public Mono getSession() { + return null; + } + + @Override + public Mono getPrincipal() { + return null; + } + + @Override + public Mono> getFormData() { + return null; + } + + @Override + public Mono> getMultipartData() { + return null; + } + + @Override + public LocaleContext getLocaleContext() { + return null; + } + + @Override + public ApplicationContext getApplicationContext() { + return null; + } + + @Override + public boolean isNotModified() { + return false; + } + + @Override + public boolean checkNotModified(Instant instant) { + return false; + } + + @Override + public boolean checkNotModified(String s) { + return false; + } + + @Override + public boolean checkNotModified(String s, Instant instant) { + return false; + } + + @Override + public String transformUrl(String s) { + return null; + } + + @Override + public void addUrlTransformer(Function function) { + + } + + @Override + public String getLogPrefix() { + return null; + } + } + + private final ServerWebExchangeEnhancedInstance enhancedInstance = new ServerWebExchangeEnhancedInstance(); + private final GatewayFilterInterceptor interceptor = new GatewayFilterInterceptor(); + @Rule + public AgentServiceRule serviceRule = new AgentServiceRule(); + @Rule + public MockitoRule rule = MockitoJUnit.rule(); + + @SegmentStoragePoint + private SegmentStorage segmentStorage; + + @Before + public void setUp() throws Exception { + } + + private final ServerWebExchange exchange = new ServerWebExchange() { + Map attributes = new HashMap<>(); + @Override + public ServerHttpRequest getRequest() { + return null; + } + + @Override + public ServerHttpResponse getResponse() { + return null; + } + + @Override + public Map getAttributes() { + return attributes; + } + + @Override + public Mono getSession() { + return null; + } + + @Override + public Mono getPrincipal() { + return null; + } + + @Override + public Mono> getFormData() { + return null; + } + + @Override + public Mono> getMultipartData() { + return null; + } + + @Override + public LocaleContext getLocaleContext() { + return null; + } + + @Override + public ApplicationContext getApplicationContext() { + return null; + } + + @Override + public boolean isNotModified() { + return false; + } + + @Override + public boolean checkNotModified(Instant instant) { + return false; + } + + @Override + public boolean checkNotModified(String s) { + return false; + } + + @Override + public boolean checkNotModified(String s, Instant instant) { + return false; + } + + @Override + public String transformUrl(String s) { + return null; + } + + @Override + public void addUrlTransformer(Function function) { + + } + + @Override + public String getLogPrefix() { + return null; + } + }; + + @Test + public void testInterceptOnlyOnce() throws Throwable { + interceptor.beforeMethod(null, null, new Object[]{exchange}, null, null); + Assert.assertTrue(ContextManager.isActive()); + AbstractSpan activeSpan = ContextManager.activeSpan(); + Assert.assertTrue(activeSpan instanceof AbstractTracingSpan); + Assert.assertEquals(activeSpan.getOperationName(), GATEWAY_FILTER_INTERCEPTOR_LOCAL_SPAN_OPERATION_NAME); + + interceptor.afterMethod(null, null, null, null, null); + Assert.assertFalse(ContextManager.isActive()); + final List traceSegments = segmentStorage.getTraceSegments(); + Assert.assertEquals(traceSegments.size(), 1); + final List spans = SegmentHelper.getSpans(traceSegments.get(0)); + Assert.assertNotNull(spans); + Assert.assertEquals(spans.size(), 1); + SpanAssert.assertComponent(spans.get(0), ComponentsDefine.SPRING_CLOUD_GATEWAY); + + } + + @Test + public void testNestedInterception() throws Throwable { + interceptor.beforeMethod(null, null, new Object[]{enhancedInstance}, null, null); + Assert.assertTrue(ContextManager.isActive()); + + interceptor.beforeMethod(null, null, new Object[]{enhancedInstance}, null, null); + Assert.assertTrue(ContextManager.isActive()); + + interceptor.afterMethod(null, null, null, null, null); + Assert.assertTrue(ContextManager.isActive()); + + interceptor.afterMethod(null, null, null, null, null); + Assert.assertFalse(ContextManager.isActive()); + + final List traceSegments = segmentStorage.getTraceSegments(); + Assert.assertEquals(traceSegments.size(), 1); + final List spans = SegmentHelper.getSpans(traceSegments.get(0)); + Assert.assertNotNull(spans); + Assert.assertEquals(spans.size(), 1); + SpanAssert.assertComponent(spans.get(0), ComponentsDefine.SPRING_CLOUD_GATEWAY); + } + + @Test + public void testWithNullDynamicField() throws Throwable { + interceptor.beforeMethod(null, null, new Object[]{enhancedInstance}, null, null); + interceptor.afterMethod(null, null, null, null, null); + // no more need this, span was stopped at interceptor#afterMethod + // ContextManager.stopSpan(); + final List traceSegments = segmentStorage.getTraceSegments(); + Assert.assertEquals(traceSegments.size(), 1); + final List spans = SegmentHelper.getSpans(traceSegments.get(0)); + Assert.assertNotNull(spans); + Assert.assertEquals(spans.size(), 1); + SpanAssert.assertComponent(spans.get(0), ComponentsDefine.SPRING_CLOUD_GATEWAY); + } + + @Test + public void testWithContextSnapshot() throws Throwable { + final AbstractSpan entrySpan = ContextManager.createEntrySpan("/get", null); + SpanLayer.asHttp(entrySpan); + entrySpan.setComponent(ComponentsDefine.SPRING_WEBFLUX); + enhancedInstance.setSkyWalkingDynamicField(ContextManager.capture()); + interceptor.beforeMethod(null, null, new Object[]{enhancedInstance}, null, null); + interceptor.afterMethod(null, null, null, null, null); + // no more need this, span was stopped at interceptor#afterMethod + // ContextManager.stopSpan(); + ContextManager.stopSpan(entrySpan); + final List traceSegments = segmentStorage.getTraceSegments(); + Assert.assertEquals(traceSegments.size(), 1); + final List spans = SegmentHelper.getSpans(traceSegments.get(0)); + Assert.assertNotNull(spans); + Assert.assertEquals(spans.size(), 2); + SpanAssert.assertComponent(spans.get(0), ComponentsDefine.SPRING_CLOUD_GATEWAY); + SpanAssert.assertComponent(spans.get(1), ComponentsDefine.SPRING_WEBFLUX); + SpanAssert.assertLayer(spans.get(1), SpanLayer.HTTP); + } + +} diff --git a/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-4.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v4x/NettyRoutingFilterInterceptorTest.java b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-4.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v4x/NettyRoutingFilterInterceptorTest.java index 25bbffb7cc..9eab1fdd36 100644 --- a/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-4.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v4x/NettyRoutingFilterInterceptorTest.java +++ b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-4.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v4x/NettyRoutingFilterInterceptorTest.java @@ -170,7 +170,8 @@ public void setUp() throws Exception { public void testWithNullDynamicField() throws Throwable { interceptor.beforeMethod(null, null, new Object[]{enhancedInstance}, null, null); interceptor.afterMethod(null, null, null, null, null); - ContextManager.stopSpan(); + // no more need this, span was stopped at interceptor#afterMethod + // ContextManager.stopSpan(); final List traceSegments = segmentStorage.getTraceSegments(); Assert.assertEquals(traceSegments.size(), 1); final List spans = SegmentHelper.getSpans(traceSegments.get(0)); @@ -187,7 +188,8 @@ public void testWithContextSnapshot() throws Throwable { enhancedInstance.setSkyWalkingDynamicField(ContextManager.capture()); interceptor.beforeMethod(null, null, new Object[]{enhancedInstance}, null, null); interceptor.afterMethod(null, null, null, null, null); - ContextManager.stopSpan(); + // no more need this, span was stopped at interceptor#afterMethod + // ContextManager.stopSpan(); ContextManager.stopSpan(entrySpan); final List traceSegments = segmentStorage.getTraceSegments(); Assert.assertEquals(traceSegments.size(), 1); @@ -207,9 +209,10 @@ public void testIsTraced() throws Throwable { interceptor.beforeMethod(null, null, new Object[]{enhancedInstance}, null, null); interceptor.afterMethod(null, null, null, null, null); Assert.assertEquals(enhancedInstance.getAttributes().get(NETTY_ROUTING_FILTER_TRACED_ATTR), true); - Assert.assertNotNull(ContextManager.activeSpan()); + Assert.assertFalse(ContextManager.isActive()); - ContextManager.stopSpan(); + // no more need this, span was stopped at interceptor#afterMethod + // ContextManager.stopSpan(); interceptor.beforeMethod(null, null, new Object[]{enhancedInstance}, null, null); interceptor.afterMethod(null, null, null, null, null); diff --git a/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/pom.xml b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/pom.xml index 0741114c6b..8e5547689c 100644 --- a/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/pom.xml +++ b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/pom.xml @@ -23,7 +23,7 @@ org.apache.skywalking optional-spring-plugins - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT optional-spring-cloud @@ -40,7 +40,6 @@ UTF-8 - /../.. diff --git a/apm-sniffer/optional-plugins/optional-spring-plugins/pom.xml b/apm-sniffer/optional-plugins/optional-spring-plugins/pom.xml index 1ae8f4983f..3b34789ab4 100644 --- a/apm-sniffer/optional-plugins/optional-spring-plugins/pom.xml +++ b/apm-sniffer/optional-plugins/optional-spring-plugins/pom.xml @@ -21,7 +21,7 @@ optional-plugins org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 pom @@ -39,7 +39,6 @@ - /.. diff --git a/apm-sniffer/optional-plugins/optional-spring-plugins/resttemplate-6.x-plugin/pom.xml b/apm-sniffer/optional-plugins/optional-spring-plugins/resttemplate-6.x-plugin/pom.xml index 4239b8e95b..8fc5797da0 100644 --- a/apm-sniffer/optional-plugins/optional-spring-plugins/resttemplate-6.x-plugin/pom.xml +++ b/apm-sniffer/optional-plugins/optional-spring-plugins/resttemplate-6.x-plugin/pom.xml @@ -20,7 +20,7 @@ optional-spring-plugins org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/optional-plugins/optional-spring-plugins/spring-annotation-plugin/pom.xml b/apm-sniffer/optional-plugins/optional-spring-plugins/spring-annotation-plugin/pom.xml index 93e3d73346..367a58c61b 100644 --- a/apm-sniffer/optional-plugins/optional-spring-plugins/spring-annotation-plugin/pom.xml +++ b/apm-sniffer/optional-plugins/optional-spring-plugins/spring-annotation-plugin/pom.xml @@ -21,7 +21,7 @@ optional-spring-plugins org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 jar diff --git a/apm-sniffer/optional-plugins/optional-spring-plugins/spring-tx-plugin/pom.xml b/apm-sniffer/optional-plugins/optional-spring-plugins/spring-tx-plugin/pom.xml index deb4bb43fc..74f0694de2 100644 --- a/apm-sniffer/optional-plugins/optional-spring-plugins/spring-tx-plugin/pom.xml +++ b/apm-sniffer/optional-plugins/optional-spring-plugins/spring-tx-plugin/pom.xml @@ -21,7 +21,7 @@ optional-spring-plugins org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 jar diff --git a/apm-sniffer/optional-plugins/optional-spring-plugins/spring-webflux-5.x-plugin/pom.xml b/apm-sniffer/optional-plugins/optional-spring-plugins/spring-webflux-5.x-plugin/pom.xml index f82260420f..e597375432 100644 --- a/apm-sniffer/optional-plugins/optional-spring-plugins/spring-webflux-5.x-plugin/pom.xml +++ b/apm-sniffer/optional-plugins/optional-spring-plugins/spring-webflux-5.x-plugin/pom.xml @@ -21,7 +21,7 @@ optional-spring-plugins org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/optional-plugins/optional-spring-plugins/spring-webflux-6.x-plugin/pom.xml b/apm-sniffer/optional-plugins/optional-spring-plugins/spring-webflux-6.x-plugin/pom.xml index b8e7c79207..98c5f9bc71 100644 --- a/apm-sniffer/optional-plugins/optional-spring-plugins/spring-webflux-6.x-plugin/pom.xml +++ b/apm-sniffer/optional-plugins/optional-spring-plugins/spring-webflux-6.x-plugin/pom.xml @@ -21,7 +21,7 @@ optional-spring-plugins org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/optional-plugins/pom.xml b/apm-sniffer/optional-plugins/pom.xml index ad5f4d42ec..c1d0d8adb7 100644 --- a/apm-sniffer/optional-plugins/pom.xml +++ b/apm-sniffer/optional-plugins/pom.xml @@ -21,7 +21,7 @@ java-agent-sniffer org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 @@ -32,10 +32,7 @@ ${shade.package}.${shade.net.bytebuddy.source} UTF-8 - - ${project.build.directory}${sdk.plugin.related.dir}/../../../../skywalking-agent - - ${agent.package.dest.dir}/optional-plugins + ${maven.multiModuleProjectDirectory}/skywalking-agent/optional-plugins 1.0b3 1.8.1 @@ -59,6 +56,7 @@ trace-sampler-cpu-policy-plugin nacos-client-2.x-plugin netty-http-4.1.x-plugin + caffeine-3.x-plugin diff --git a/apm-sniffer/optional-plugins/quartz-scheduler-2.x-plugin/pom.xml b/apm-sniffer/optional-plugins/quartz-scheduler-2.x-plugin/pom.xml index 780020b8ea..acc3b85a35 100644 --- a/apm-sniffer/optional-plugins/quartz-scheduler-2.x-plugin/pom.xml +++ b/apm-sniffer/optional-plugins/quartz-scheduler-2.x-plugin/pom.xml @@ -23,7 +23,7 @@ org.apache.skywalking optional-plugins - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT apm-quartz-scheduler-2.x-plugin diff --git a/apm-sniffer/optional-plugins/sentinel-1.x-plugin/pom.xml b/apm-sniffer/optional-plugins/sentinel-1.x-plugin/pom.xml index 841623347e..fd4abb4af5 100644 --- a/apm-sniffer/optional-plugins/sentinel-1.x-plugin/pom.xml +++ b/apm-sniffer/optional-plugins/sentinel-1.x-plugin/pom.xml @@ -21,7 +21,7 @@ org.apache.skywalking optional-plugins - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/optional-plugins/shenyu-2.4.x-plugin/pom.xml b/apm-sniffer/optional-plugins/shenyu-2.4.x-plugin/pom.xml index 3d1c99c565..7f712e7582 100644 --- a/apm-sniffer/optional-plugins/shenyu-2.4.x-plugin/pom.xml +++ b/apm-sniffer/optional-plugins/shenyu-2.4.x-plugin/pom.xml @@ -21,7 +21,7 @@ optional-plugins org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 @@ -63,7 +63,6 @@ org.apache.maven.plugins maven-shade-plugin - 3.2.4 package diff --git a/apm-sniffer/optional-plugins/trace-ignore-plugin/pom.xml b/apm-sniffer/optional-plugins/trace-ignore-plugin/pom.xml index d71198b1c1..ec9d193fc6 100644 --- a/apm-sniffer/optional-plugins/trace-ignore-plugin/pom.xml +++ b/apm-sniffer/optional-plugins/trace-ignore-plugin/pom.xml @@ -20,7 +20,7 @@ optional-plugins org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/optional-plugins/trace-sampler-cpu-policy-plugin/pom.xml b/apm-sniffer/optional-plugins/trace-sampler-cpu-policy-plugin/pom.xml index 1e0bc3dab9..cc4f683ed6 100644 --- a/apm-sniffer/optional-plugins/trace-sampler-cpu-policy-plugin/pom.xml +++ b/apm-sniffer/optional-plugins/trace-sampler-cpu-policy-plugin/pom.xml @@ -20,7 +20,7 @@ optional-plugins org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/optional-plugins/zookeeper-3.4.x-plugin/pom.xml b/apm-sniffer/optional-plugins/zookeeper-3.4.x-plugin/pom.xml index a3b5e0e0d1..530f490fd5 100644 --- a/apm-sniffer/optional-plugins/zookeeper-3.4.x-plugin/pom.xml +++ b/apm-sniffer/optional-plugins/zookeeper-3.4.x-plugin/pom.xml @@ -22,7 +22,7 @@ org.apache.skywalking optional-plugins - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT apm-zookeeper-3.4.x-plugin diff --git a/apm-sniffer/optional-reporter-plugins/kafka-config-extension/pom.xml b/apm-sniffer/optional-reporter-plugins/kafka-config-extension/pom.xml index 53927fe911..52374f2631 100644 --- a/apm-sniffer/optional-reporter-plugins/kafka-config-extension/pom.xml +++ b/apm-sniffer/optional-reporter-plugins/kafka-config-extension/pom.xml @@ -20,7 +20,7 @@ optional-reporter-plugins org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 diff --git a/apm-sniffer/optional-reporter-plugins/kafka-reporter-plugin/pom.xml b/apm-sniffer/optional-reporter-plugins/kafka-reporter-plugin/pom.xml index 73d542bb39..127e923448 100644 --- a/apm-sniffer/optional-reporter-plugins/kafka-reporter-plugin/pom.xml +++ b/apm-sniffer/optional-reporter-plugins/kafka-reporter-plugin/pom.xml @@ -21,7 +21,7 @@ optional-reporter-plugins org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 @@ -137,7 +137,6 @@ org.apache.maven.plugins maven-dependency-plugin - ${maven-dependency-plugin.version} copy diff --git a/apm-sniffer/optional-reporter-plugins/pom.xml b/apm-sniffer/optional-reporter-plugins/pom.xml index feb2ec0daf..13b9fc86f7 100644 --- a/apm-sniffer/optional-reporter-plugins/pom.xml +++ b/apm-sniffer/optional-reporter-plugins/pom.xml @@ -21,7 +21,7 @@ java-agent-sniffer org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 @@ -36,13 +36,12 @@ UTF-8 - ${project.build.directory}/../../../../skywalking-agent - ${agent.package.dest.dir}/optional-reporter-plugins + ${maven.multiModuleProjectDirectory}/skywalking-agent/optional-reporter-plugins 1.0b3 1.8.1 - 2.4.1 + 3.9.1 2.4.6.RELEASE diff --git a/apm-sniffer/pom.xml b/apm-sniffer/pom.xml index c9d2dbdaf4..94ea5a16bd 100644 --- a/apm-sniffer/pom.xml +++ b/apm-sniffer/pom.xml @@ -21,7 +21,7 @@ java-agent org.apache.skywalking - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT 4.0.0 @@ -38,6 +38,7 @@ optional-plugins optional-reporter-plugins bytebuddy-patch + expired-plugins diff --git a/changes/changes-9.2.0.md b/changes/changes-9.2.0.md new file mode 100644 index 0000000000..a5eecc0d2d --- /dev/null +++ b/changes/changes-9.2.0.md @@ -0,0 +1,27 @@ +Changes by Version +================== +Release Notes. + +9.2.0 +------------------ + +* Fix NoSuchMethodError in mvc-annotation-commons and change deprecated method. +* Fix forkjoinpool plugin in JDK11. +* Support for tracing spring-cloud-gateway 4.x in gateway-4.x-plugin. +* Fix re-transform bug when plugin enhanced class proxy parent method. +* Fix error HTTP status codes not recording as SLA failures in Vert.x plugins. +* Support for HttpExchange request tracing. +* Support tracing for async producing, batch sync consuming, and batch async consuming in rocketMQ-client-java-5.x-plugin. +* Convert the Redisson span into an async span. +* Rename system env name from `sw_plugin_kafka_producer_config` to `SW_PLUGIN_KAFKA_PRODUCER_CONFIG`. +* Support for ActiveMQ-Artemis messaging tracing. +* Archive the expired plugins `impala-jdbc-2.6.x-plugin`. +* Fix a bug in Spring Cloud Gateway if HttpClientFinalizer#send does not invoke, the span created at NettyRoutingFilterInterceptor can not stop. +* Fix not tracing in HttpClient v5 when HttpHost(arg[0]) is null but `RoutingSupport#determineHost` works. +* Support across thread tracing for SOFA-RPC. +* Update Jedis 4.x plugin to support Sharding and Cluster models. + +#### Documentation +* Update docs to describe `expired-plugins`. + +All issues and pull requests are [here](https://github.com/apache/skywalking/milestone/204?closed=1) diff --git a/changes/changes-9.3.0.md b/changes/changes-9.3.0.md new file mode 100644 index 0000000000..304feb01ae --- /dev/null +++ b/changes/changes-9.3.0.md @@ -0,0 +1,28 @@ +Changes by Version +================== +Release Notes. + +9.3.0 +------------------ + +* Remove `idleCount` tag in Alibaba Druid meter plugin. +* Fix NPE in handleMethodException method of apm-jdk-threadpool-plugin. +* Support for C3P0 connection pool tracing. +* Use a daemon thread to flush logs. +* Fix typos in `URLParser`. +* Add support for `Derby`/`Sybase`/`SQLite`/`DB2`/`OceanBase` jdbc url format in `URLParser`. +* Optimize spring-plugins:scheduled-annotation-plugin compatibility about Spring 6.1.x support. +* Add a forceIgnoring mechanism in a CROSS_THREAD scenario. +* Fix NPE in Redisson plugin since Redisson 3.20.0. +* Support for showing batch command details and ignoring PING commands in Redisson plugin. +* Fix peer value of Master-Slave mode in Redisson plugin. +* Support for tracing the callbacks of asynchronous methods in elasticsearch-6.x-plugin/elasticsearch-7.x-plugin. +* Fixed the invalid issue in the isInterface method in PluginFinder. +* Fix the opentracing toolkit SPI config +* Improve 4x performance of ContextManagerExtendService.createTraceContext() +* Add a plugin that supports the Solon framework. +* Fixed issues in the MySQL component where the executeBatch method could result in empty SQL statements. +* Support kafka-clients-3.7.x. + +All issues and pull requests are [here](https://github.com/apache/skywalking/milestone/213?closed=1) + diff --git a/changes/changes-9.4.0.md b/changes/changes-9.4.0.md new file mode 100644 index 0000000000..c3fa1a4b7e --- /dev/null +++ b/changes/changes-9.4.0.md @@ -0,0 +1,33 @@ +Changes by Version +================== +Release Notes. + +9.4.0 +------------------ + +* Upgrade nats plugin to support 2.16.5 +* Add agent self-observability. +* Fix intermittent ClassCircularityError by preloading ThreadLocalRandom since ByteBuddy 1.12.11 +* Add witness class/method for resteasy-server plugin(v3/v4/v6) +* Add async-profiler feature for performance analysis. This requires OAP server 10.2.0 +* Support db.instance tag,db.collection tag and AggregateOperation span for mongodb plugin(3.x/4.x) +* Improve CustomizeConfiguration by avoiding repeatedly resolve file config +* Add empty judgment for constructorInterceptPoint +* Bump up gRPC to 1.68.1 +* Bump up netty to 4.1.115.Final +* Fix the `CreateAopProxyInterceptor` in the Spring core-patch to prevent it from changing the implementation of the + Spring AOP proxy +* Support Tracing for GlobalFilter and GatewayFilter in Spring Gateway +* [doc] Enhance Custom Trace Ignoring Plugin document about conflicts with the plugin of **sampler plugin with CPU + policy** +* [doc] Add Spring Gateway Plugin document +* [doc] Add 4 menu items guiding users to find important notices for Spring Annotation Plugin, Custom Trace Ignoring + Plugin, Kotlin Coroutine Plugin, and Spring Gateway Plugin +* Change context and parent entry span propagation mechanism from gRPC ThreadLocal context to SkyWalking native dynamic + field as new propagation mechanism, to better support async scenarios. +* Add Caffeine plugin as optional. +* Add Undertow 2.1.7.final+ worker thread pool metrics. +* Support for tracking in spring gateway versions 4.1.2 and above. +* Fix `ConsumeDriver` running status concurrency issues. + +All issues and pull requests are [here](https://github.com/apache/skywalking/milestone/222?closed=1) \ No newline at end of file diff --git a/changes/changes-9.5.0.md b/changes/changes-9.5.0.md new file mode 100644 index 0000000000..01ff913873 --- /dev/null +++ b/changes/changes-9.5.0.md @@ -0,0 +1,26 @@ +Changes by Version +================== +Release Notes. + +9.5.0 +------------------ + +* Add the virtual thread executor plugin +* Fix Conflicts apm-jdk-threadpool-plugin conflicts with apm-jdk-forkjoinpool-plugin +* Fix NPE in hikaricp-plugin if JDBC URL is not set +* Agent kernel services could be not-booted-yet as ServiceManager#INSTANCE#boot executed after agent transfer + initialization. Delay so11y metrics#build when the services are not ready to avoid MeterService status is not + initialized. +* Fix retransform failure when enhancing both parent and child classes. +* Add support for `dameng(DM)` JDBC url format in `URLParser`. +* Fix RabbitMQ Consumer could not receive handleCancelOk callback. +* Support for tracking in lettuce versions 6.5.x and above. +* Upgrade byte-buddy version to 1.17.6. +* Support gRPC 1.59.x and 1.70.x server interceptor trace +* Fix the `CreateAopProxyInterceptor` in the Spring core-patch changes the AOP proxy type when a class is + enhanced by both SkyWalking and Spring AOP. +* Build: Centralized plugin version management in the root POM and remove redundant declarations. +* Support Spring Cloud Gateway 4.3.x. + +All issues and pull requests are [here](https://github.com/apache/skywalking/milestone/236?closed=1) + diff --git a/changes/changes-9.6.0.md b/changes/changes-9.6.0.md new file mode 100644 index 0000000000..74a4739aaa --- /dev/null +++ b/changes/changes-9.6.0.md @@ -0,0 +1,33 @@ +Changes by Version +================== +Release Notes. + +9.6.0 +------------------ + +* Add CLAUDE.md for AI assistant guidance. +* Bump up agent-oap protocol to latest(16c51358ebcf42629bf4ffdf952253971f20eb25). +* Bump up gRPC to v1.74.0. +* Bump up netty to v4.1.124.Final. +* Bump up GSON to v2.13.1. +* Bump up guava to v32.1.3. +* Bump up oap to the 10.3-dev.latest(dc8740d4757b35374283c4850a9a080e40f0eb79) in e2e. +* Bump up cli to the 0.15.0-dev.latest(77b4c49e89c9c000278f44e62729d534f2ec842e) in e2e. +* Bump up apache parent pom to v35. +* Update Maven to 3.6.3 in mvnw. +* Fix OOM due to too many span logs. +* Fix ClassLoader cache OOM issue with WeakHashMap. +* Fix Jetty client cannot receive the HTTP response body. +* Eliminate repeated code with HttpServletRequestWrapper in mvc-annotation-commons. +* Add the jdk httpclient plugin. +* Fix Gateway 2.0.x plugin not activated for spring-cloud-starter-gateway 2.0.0.RELEASE. +* Support kafka-clients-3.9.x intercept. +* Upgrade kafka-clients version in optional-reporter-plugins to 3.9.1. +* Fix AbstractLogger replaceParam when the replaced string contains a replacement marker. +* Fix `JDBCPluginConfig.Plugin.JDBC.SQL_BODY_MAX_LENGTH` was not working in some plugins. +* Bump up Lombok to v1.18.42 to adopt JDK25 compiling. +* Add `eclipse-temurin:25-jre` as another base image. +* Add JDK25 plugin tests for Spring 6. +* Ignore classes starting with "sun.nio.cs" in bytebuddy due to potential class loading deadlock. + +All issues and pull requests are [here](https://github.com/apache/skywalking/milestone/242?closed=1) diff --git a/dist-material/LICENSE b/dist-material/LICENSE index a5c6f781cf..a7cb7efaeb 100755 --- a/dist-material/LICENSE +++ b/dist-material/LICENSE @@ -215,13 +215,14 @@ Apache 2.0 licenses The following components are provided under the Apache License. See project link for details. The text of each license is the standard Apache 2.0 license. - raphw (byte-buddy) 1.14.9: http://bytebuddy.net/ , Apache 2.0 - Google: grpc-java 1.53.0: https://github.com/grpc/grpc-java, Apache 2.0 - Google: gson 2.8.9: https://github.com/google/gson , Apache 2.0 - Google: proto-google-common-protos 2.0.1: https://github.com/googleapis/googleapis , Apache 2.0 + raphw (byte-buddy) 1.17.6: http://bytebuddy.net/ , Apache 2.0 + Google: grpc-java 1.74.0: https://github.com/grpc/grpc-java, Apache 2.0 + Google: gson 2.13.1: https://github.com/google/gson , Apache 2.0 + Google: proto-google-common-protos 2.59.2: https://github.com/googleapis/googleapis , Apache 2.0 Google: jsr305 3.0.2: http://central.maven.org/maven2/com/google/code/findbugs/jsr305/3.0.0/jsr305-3.0.0.pom , Apache 2.0 - Google: guava 32.0.1: https://github.com/google/guava , Apache 2.0 - netty 4.1.100: https://github.com/netty/netty/blob/4.1/LICENSE.txt, Apache 2.0 + Google: guava 32.1.3: https://github.com/google/guava , Apache 2.0 + netty 4.1.124: https://github.com/netty/netty/blob/4.1/LICENSE.txt, Apache 2.0 + async-profiler 3.0: https://github.com/async-profiler/async-profiler/blob/v3.0/LICENSE, Apache 2.0 ======================================================================== BSD licenses diff --git a/docs/en/contribution/compiling.md b/docs/en/contribution/compiling.md index d1507cc47a..da69b48a91 100644 --- a/docs/en/contribution/compiling.md +++ b/docs/en/contribution/compiling.md @@ -1,7 +1,7 @@ # Compiling project This document will help you compile and build a project in your maven and set your IDE. -Prepare JDK 17 or 21. +Prepare JDK 17, 21 or 25. * If you clone codes from https://github.com/apache/skywalking-java ```shell diff --git a/docs/en/contribution/release-java-agent.md b/docs/en/contribution/release-java-agent.md index f0149636da..567cf81615 100644 --- a/docs/en/contribution/release-java-agent.md +++ b/docs/en/contribution/release-java-agent.md @@ -276,7 +276,7 @@ export NAME=skywalking-java-agent export HUB=apache export TAG=$SW_VERSION -make docker.push.alpine docker.push.java8 docker.push.java11 docker.push.java17 docker.push.java21 +make docker.push.alpine docker.push.java8 docker.push.java11 docker.push.java17 docker.push.java21 docker.push.java25 ``` ## Clean up the old releases diff --git a/docs/en/setup/service-agent/java-agent/Agent-self-observability.md b/docs/en/setup/service-agent/java-agent/Agent-self-observability.md new file mode 100644 index 0000000000..7efa7ee1b4 --- /dev/null +++ b/docs/en/setup/service-agent/java-agent/Agent-self-observability.md @@ -0,0 +1,16 @@ +# Agent Self Observability +The Java Agent self-observability feature is built-in and used to measure the tracing performance and error statistics of plugins. + +It reports meters to SkyWalking oap through native meter protocol, OAP receives and analyzes meters, +which are ultimately presented on the [Java Agent self-observability dashboard](https://skywalking.apache.org/docs/main/next/en/setup/backend/dashboards-so11y-java-agent/). + +***Note: Java Agent self-observability dashboard is available since OAP 10.1.0*** + +# Details of agent so11y meters +- `created_tracing_context_counter` - Counter. The number of created tracing contexts. This includes a label=created_by(value=sampler,propagated). `created_by=propagated` means the agent created the context due to downstream service added sw8 header to trigger force sampling. `created_by=sampler` means the agent created this context by local sampler no matter which policy it uses. +- `finished_tracing_context_counter` - Counter. The number of finished contexts. The gap between `finished_tracing_context_counter` and `created_tracing_context_counter` should be relatively stable, otherwise, the memory cost would be increased. +- `created_ignored_context_counter` and `finished_ignored_context_counter`. Same concepts like `*_tracing_context_counter`. +- `interceptor_error_counter` - Counter. The number of errors happened in the interceptor logic, with `label=plugin_name, inter_type(constructor, inst, static)`. We don't add interceptor names into labels in case of OOM. The number of plugins is only dozens, it is predictable, but the number of interceptors will be hundreds. +- `possible_leaked_context_counter` - Counter. The number of detected leaked contexts. It should include the `label=source(value=tracing, ignore)`. When `source=tracing`, it is today's shadow tracing context. But now, it is measured. +- `tracing_context_performance` - Histogram. For successfully finished tracing context, it measures every interceptor's time cost(by using nanoseconds), the buckets of the histogram are {1000, 10000, 50000, 100000, 300000, 500000, + 1000000, 5000000, 10000000, 20000000, 50000000, 100000000}ns. This provides the performance behavior for the tracing operations. \ No newline at end of file diff --git a/docs/en/setup/service-agent/java-agent/Application-toolkit-log4j-1.x.md b/docs/en/setup/service-agent/java-agent/Application-toolkit-log4j-1.x.md index b180d164ae..2768fa4a72 100644 --- a/docs/en/setup/service-agent/java-agent/Application-toolkit-log4j-1.x.md +++ b/docs/en/setup/service-agent/java-agent/Application-toolkit-log4j-1.x.md @@ -14,7 +14,7 @@ log4j.appender.CONSOLE.layout=org.apache.skywalking.apm.toolkit.log.log4j.v1.x.TraceIdPatternLayout ``` -* set `%T` in `layout.ConversionPattern` ( In 2.0-2016, you should use %x, [Why change?](https://github.com/wu-sheng/sky-walking/issues/77) ) +* set `%T` in `layout.ConversionPattern` ( In 2.0-2016, you should use %x, [Why change?](https://github.com/apache/skywalking/issues/77) ) ```properties log4j.appender.CONSOLE.layout.ConversionPattern=%d [%T] %-5p %c{1}:%L - %m%n ``` diff --git a/docs/en/setup/service-agent/java-agent/Bootstrap-plugins.md b/docs/en/setup/service-agent/java-agent/Bootstrap-plugins.md index 10dd955a9c..f03a4e21a7 100644 --- a/docs/en/setup/service-agent/java-agent/Bootstrap-plugins.md +++ b/docs/en/setup/service-agent/java-agent/Bootstrap-plugins.md @@ -7,6 +7,8 @@ Now, we have the following known bootstrap plugins. * Plugin of JDK Callable and Runnable. Agent is compatible with JDK 1.8+ * Plugin of JDK ThreadPoolExecutor. Agent is compatible with JDK 1.8+ * Plugin of JDK ForkJoinPool. Agent is compatible with JDK 1.8+ +* Plugin of JDK VirtualThreadExecutor. Agent is compatible with JDK 21+ +* Plugin of JDK HttpClient. Agent is compatible with JDK 11+ ### HttpURLConnection Plugin Notice The plugin of JDK HttpURLConnection depended on `sun.net.*`. When using Java 9+, You should add some JVM options as follows: diff --git a/docs/en/setup/service-agent/java-agent/Java-Plugin-Development-Guide.md b/docs/en/setup/service-agent/java-agent/Java-Plugin-Development-Guide.md index 2aa6d148c3..0498f2011d 100644 --- a/docs/en/setup/service-agent/java-agent/Java-Plugin-Development-Guide.md +++ b/docs/en/setup/service-agent/java-agent/Java-Plugin-Development-Guide.md @@ -11,7 +11,7 @@ We also provide the [plugin test tool](#plugin-test-tool) to verify the data col ## Concepts ### Span The span is an important and recognized concept in the distributed tracing system. Learn about the **span** from the -[Google Dapper Paper](https://research.google.com/pubs/pub36356.html) and +[Google Dapper Paper](https://research.google/pubs/dapper-a-large-scale-distributed-systems-tracing-infrastructure/) and [OpenTracing](http://opentracing.io) SkyWalking has supported OpenTracing and OpenTracing-Java API since 2017. Our concepts of the span are similar to that of the Google Dapper Paper and OpenTracing. We have also extended the span. diff --git a/docs/en/setup/service-agent/java-agent/Optional-plugins.md b/docs/en/setup/service-agent/java-agent/Optional-plugins.md index 5c7a7f7976..b8ab3524f9 100644 --- a/docs/en/setup/service-agent/java-agent/Optional-plugins.md +++ b/docs/en/setup/service-agent/java-agent/Optional-plugins.md @@ -1,15 +1,19 @@ # Optional Plugins -Java agent plugins are all pluggable. Optional plugins could be provided in `optional-plugins` folder under agent or 3rd party repositories. +Java agent plugins are all pluggable. Optional plugins could be provided in `optional-plugins` and `expired-plugins` folder under agent or 3rd party repositories. For using these plugins, you need to put the target plugin jar file into `/plugins`. -Now, we have the following known optional plugins. -* [Plugin of tracing Spring annotation beans](agent-optional-plugins/Spring-annotation-plugin.md) -* [Plugin of tracing Oracle and Resin](agent-optional-plugins/Oracle-Resin-plugins.md) +Now, we have the following known 2 kinds of optional plugins. + +## Optional Level 2 Plugins +These plugins affect the performance or must be used under some conditions, from experiences. +So only released in `/optional-plugins` or `/bootstrap-plugins`, copy to `/plugins` in order to make them work. + +* Plugin of [tracing Spring annotation beans](agent-optional-plugins/Spring-annotation-plugin.md) * [Filter traces through specified endpoint name patterns](agent-optional-plugins/trace-ignore-plugin.md) * Plugin of Gson serialization lib in optional plugin folder. * Plugin of Zookeeper 3.4.x in optional plugin folder. The reason of being optional plugin is, many business irrelevant traces are generated, which cause extra payload to agents and backends. At the same time, those traces may be just heartbeat(s). * [Customize enhance](Customize-enhance-trace.md) Trace methods based on description files, rather than write plugin or change source codes. -* Plugin of Spring Cloud Gateway 2.x and 3.x and 4.x in optional plugin folder. Please only activate this plugin when you install agent in Spring Gateway. +* Plugin of [Spring Cloud Gateway 2.x and 3.x and 4.x](agent-optional-plugins/spring-gateway.md) in optional plugin folder. Please only activate this plugin when you install agent in Spring Gateway. * Plugin of Spring Transaction in optional plugin folder. The reason of being optional plugin is, many local span are generated, which also spend more CPU, memory and network. * [Plugin of Kotlin coroutine](agent-optional-plugins/Kotlin-Coroutine-plugin.md) provides the tracing across coroutines automatically. As it will add local spans to all across routines scenarios, Please assess the performance impact. * Plugin of quartz-scheduler-2.x in the optional plugin folder. The reason for being an optional plugin is, many task scheduling systems are based on quartz-scheduler, this will cause duplicate tracing and link different sub-tasks as they share the same quartz level trigger, such as ElasticJob. @@ -21,7 +25,23 @@ Now, we have the following known optional plugins. * Plugin of fastjson serialization lib in optional plugin folder. * Plugin of jackson serialization lib in optional plugin folder. * Plugin of Apache ShenYu(incubating) Gateway 2.4.x in optional plugin folder. Please only activate this plugin when you install agent in Apache ShenYu Gateway. -* Plugin of trace sampler CPU policy in the optional plugin folder. Please only activate this plugin when you need to disable trace collecting when the agent process CPU usage is too high(over threshold). +* Plugin of sampler plugin with CPU policy in the optional plugin folder. Please only activate this plugin when you need to disable trace collecting when the agent process CPU usage is too high(over threshold). * Plugin for Spring 6.x and RestTemplate 6.x are in the optional plugin folder. Spring 6 requires Java 17 but SkyWalking is still compatible with Java 8. So, we put it in the optional plugin folder. * Plugin of nacos-client 2.x lib in optional plugin folder. The reason is many business irrelevant traces are generated, which cause extra payload to agents and backends, also spend more CPU, memory and network. -* Plugin of netty-http 4.1.x lib in optional plugin folder. The reason is some frameworks use Netty HTTP as kernel, which could double the unnecessary spans and create incorrect RPC relative metrics. \ No newline at end of file +* Plugin of netty-http 4.1.x lib in optional plugin folder. The reason is some frameworks use Netty HTTP as kernel, which could double the unnecessary spans and create incorrect RPC relative metrics. + +### Notice due to Licence Restrictions +These plugins can't be provided in Apache release because of Oracle and Resin Licenses. +If you want to know details, please read [Apache license legal document](https://www.apache.org/legal/resolved.html) + +Due to license incompatibilities/restrictions these plugins are hosted and released in 3rd part repository, +go to [OpenSkywalking java plugin extension repository](https://github.com/OpenSkywalking/java-plugin-extensions) to get these. + +## Optional Level 3 Plugins. Expired Plugins +These plugins are not tested in the CI/CD pipeline, as the previous added tests are not able to run according to the latest +CI/CD infrastructure limitations, lack of maintenance, or dependencies/images not available(e.g. removed from DockerHub). + +**Warning, there is no guarantee of working and maintenance. The committer team may remove them from the agent package +in the future without further notice.** + +* Plugin of Spring Impala 2.6.x was tested through parrot-stream released images. The images are not available since Mar. 2024. This plugin is expired due to lack of testing. \ No newline at end of file diff --git a/docs/en/setup/service-agent/java-agent/Plugin-list.md b/docs/en/setup/service-agent/java-agent/Plugin-list.md index 16136fce70..3764ae9716 100644 --- a/docs/en/setup/service-agent/java-agent/Plugin-list.md +++ b/docs/en/setup/service-agent/java-agent/Plugin-list.md @@ -47,15 +47,21 @@ - influxdb-2.x - jackson-2.x - jdk-http-plugin +- jdk-httpclient-plugin - jdk-threading-plugin +- jdk-virtual-thread-executor-plugin - jedis-2.x-3.x - jedis-4.x - jetty-client-9.0 - jetty-client-9.x - jetty-server-9.x - kafka-0.11.x/1.x/2.x +- kafka-3.7.x +- kafka-3.9.x - kotlin-coroutine -- lettuce-5.x +- lettuce-common +- lettuce-5.x-6.4.x +- lettuce-6.5.x - light4j - mariadb-2.x - micrometer-1.10.x @@ -97,6 +103,7 @@ - sharding-sphere-5.0.0 - sofarpc - solrj-7.x +- spring-ai-1.x - spring-annotation - spring-async-annotation-5.x - spring-cloud-feign-1.x @@ -107,6 +114,7 @@ - spring-core-patch - spring-kafka-1.x - spring-kafka-2.x +- spring-rabbitmq - spring-mvc-annotation - spring-mvc-annotation-3.x - spring-mvc-annotation-4.x @@ -161,7 +169,7 @@ - hutool-http-5.x - micronaut-http-client-3.2.x-3.6.x - micronaut-http-server-3.2.x-3.6.x -- nats-client-2.14.x-2.15.x +- nats-client-2.14.x-2.16.5 - impala-jdbc-2.6.x - jdk-forkjoinpool-plugin - jetty-thread-pool @@ -176,3 +184,6 @@ - spring-webflux-6.x - spring-webflux-6.x-webclient - activemq-artemis-jakarta-client-2.x +- c3p0-0.9.x +- solon-2.x +- caffeine-3.x diff --git a/docs/en/setup/service-agent/java-agent/Plugin-test.md b/docs/en/setup/service-agent/java-agent/Plugin-test.md index e67c365672..e9dabb7515 100644 --- a/docs/en/setup/service-agent/java-agent/Plugin-test.md +++ b/docs/en/setup/service-agent/java-agent/Plugin-test.md @@ -38,6 +38,10 @@ The test case project must be packaged as `project-name.war` by using `mvn packa Take the following test project as an example * [spring-4.3.x-scenario](../../../../../test/plugin/scenarios/spring-4.3.x-scenario) +Note on `tomcat:10.1.50-jdk25-temurin` image: + +Starting from JDK 22, the Tomcat community removed `curl` from the official Tomcat JDK images. Since `curl` is required by our plugin test framework, we provide a custom `tomcat-jdk25` image built with `curl` installed. +You can refer to the [tomcat-jdk25-dockerFile](../../../../../.github/workflows/Dockerfile-tomcat-jdk25-withCurl) to build the image for local plugin tests. ## Test project hierarchical structure The test case is an independent maven project, and it must be packaged as a war tar ball or zip file, depending on the chosen base image. Also, two external accessible endpoints usually two URLs) are required. @@ -107,7 +111,7 @@ File Name | Descriptions | startScript | Path of the start up script. Required in `type: jvm` only. | runningMode | Running mode with the optional plugin, options, `default`(default), `with_optional`, or `with_bootstrap`. | withPlugins | Plugin selector rule, e.g.:`apm-spring-annotation-plugin-*.jar`. Required for `runningMode=with_optional` or `runningMode=with_bootstrap`. -| environment | Same as `docker-compose#environment`. +| environment | Same as `docker-compose#environment` and also used as `docker run` environment variables. | depends_on | Same as `docker-compose#depends_on`. | dependencies | Same as `docker-compose#services`, `image`, `links`, `hostname`, `command`, `environment` and `depends_on` are supported. @@ -682,6 +686,7 @@ You can run `python3 tools/select-group.py` to see which file contains the least If a test case required to run in JDK 17 environment, please add you test case into file `plugins-jdk17-test..yaml`. If a test case required to run in JDK 21 environment, please add you test case into file `plugins-jdk21-test..yaml`. +If a test case required to run in JDK 25 environment, please add you test case into file `plugins-jdk25-test..yaml`. ```yaml jobs: diff --git a/docs/en/setup/service-agent/java-agent/README.md b/docs/en/setup/service-agent/java-agent/README.md index 139fb50a5a..d829b741fc 100755 --- a/docs/en/setup/service-agent/java-agent/README.md +++ b/docs/en/setup/service-agent/java-agent/README.md @@ -1,6 +1,6 @@ # Setup java agent -1. Agent is available for JDK 8 - 21. +1. Agent is available for JDK 8 - 25. 1. Find `agent` folder in SkyWalking release package 1. Set `agent.service_name` in `config/agent.config`. Could be any String in English. 1. Set `collector.backend_service` in `config/agent.config`. Default point to `127.0.0.1:11800`, only works for local @@ -33,6 +33,10 @@ package looks like this. +-- bootstrap-plugins jdk-http-plugin.jar ..... + +-- expired-plugins + # Expired plugins are moved to this folder. No guarantee of working and maintenance. + apm-impala-2.6.x-plugin.jar + ..... +-- logs skywalking-agent.jar ``` diff --git a/docs/en/setup/service-agent/java-agent/Supported-list.md b/docs/en/setup/service-agent/java-agent/Supported-list.md index 9e69ad72b4..1645c43b4e 100644 --- a/docs/en/setup/service-agent/java-agent/Supported-list.md +++ b/docs/en/setup/service-agent/java-agent/Supported-list.md @@ -12,12 +12,12 @@ metrics based on the tracing data. * Spring MVC 6.x (Optional²) * [Nutz Web Framework](https://github.com/nutzam/nutz) 1.x * [Struts2 MVC](http://struts.apache.org/) 2.3.x -> 2.5.x - * Resin 3 (Optional¹) - * Resin 4 (Optional¹) + * Resin 3 (Optional¹), See [SkySPM Plugin Repository](https://github.com/SkyAPM/java-plugin-extensions) + * Resin 4 (Optional¹), See [SkySPM Plugin Repository](https://github.com/SkyAPM/java-plugin-extensions) * [Jetty Server](http://www.eclipse.org/jetty/) 9.x -> 11.x - * [Spring WebFlux](https://docs.spring.io/spring/docs/current/spring-framework-reference/web-reactive.html) 5.x (Optional¹) -> 6.x (Optional¹) + * [Spring WebFlux](https://docs.spring.io/spring/docs/current/spring-framework-reference/web-reactive.html) 5.x (Optional²) -> 6.x (Optional²) * [Undertow](http://undertow.io/) 1.3.0.Final -> 2.0.27.Final - * [RESTEasy](https://resteasy.github.io/) 3.1.0.Final -> 6.2.4.Final + * [RESTEasy](https://resteasy.dev/) 3.1.0.Final -> 6.2.4.Final * [Play Framework](https://www.playframework.com/) 2.6.x -> 2.8.x * [Light4J Microservices Framework](https://doc.networknt.com/) 1.6.x -> 2.x * [Netty SocketIO](https://github.com/mrniko/netty-socketio) 1.x @@ -26,6 +26,7 @@ metrics based on the tracing data. * [Grizzly](https://github.com/eclipse-ee4j/grizzly) 2.3.x -> 4.x * [WebSphere Liberty](https://github.com/OpenLiberty/open-liberty) 23.x * [Netty HTTP](https://github.com/netty/netty) 4.1.x (Optional²) + * [Solon](https://github.com/opensolon/solon) 2.7.x -> 2.8.x * HTTP Client * [Feign](https://github.com/OpenFeign/feign) 9.x * [Netflix Spring Cloud Feign](https://github.com/spring-cloud/spring-cloud-openfeign) 1.1.x -> 2.x @@ -41,11 +42,11 @@ metrics based on the tracing data. * [Hutool-http](https://www.hutool.cn/) client 5.x * [Micronaut HTTP Client](https://github.com/micronaut-projects/micronaut-core) 3.2.x -> 3.6.x * HTTP Gateway - * [Spring Cloud Gateway](https://spring.io/projects/spring-cloud-gateway) 2.0.2.RELEASE -> 4.1.x (Optional²) + * [Spring Cloud Gateway](https://spring.io/projects/spring-cloud-gateway) 2.0.2.RELEASE -> 4.3.x (Optional²) * [Apache ShenYu](https://shenyu.apache.org) (Rich protocol support: `HTTP`,`Spring Cloud`,`gRPC`,`Dubbo`,`SOFARPC`,`Motan`,`Tars`) 2.4.x (Optional²) * JDBC * Mysql Driver 5.x, 6.x, 8.x - * Oracle Driver (Optional¹) + * Oracle Driver (Optional¹), See [SkySPM Plugin Repository](https://github.com/SkyAPM/java-plugin-extensions) * H2 Driver 1.3.x -> 1.4.x * [ShardingSphere](https://github.com/apache/shardingsphere) 3.0.0, 4.0.0, 4.0.1, 4.1.0, 4.1.1, 5.0.0 * PostgreSQL Driver 8.x, 9.x, 42.x @@ -55,7 +56,7 @@ metrics based on the tracing data. * [Mssql-jdbc](https://github.com/microsoft/mssql-jdbc) 6.x -> 8.x * [ClickHouse-jdbc](https://github.com/ClickHouse/clickhouse-jdbc) 0.3.x * [Apache-Kylin-Jdbc](https://github.com/apache/kylin.git) 2.6.x -> 3.x -> 4.x - * [Impala-jdbc](https://www.cloudera.com/downloads/connectors/impala/jdbc/2-6-29.html) 2.6.x + * [Impala-jdbc](https://www.cloudera.com/downloads/connectors/impala/jdbc/2-6-29.html) 2.6.x (Optional³) * RPC Frameworks * [Dubbo](https://github.com/alibaba/dubbo) 2.5.4 -> 2.6.0 * [Dubbox](https://github.com/dangdangdotcom/dubbox) 2.8.4 @@ -75,20 +76,21 @@ metrics based on the tracing data. * MQ * [RocketMQ](https://github.com/apache/rocketmq) 3.x-> 5.x * [RocketMQ-gRPC](http://github.com/apache/rocketmq-clients) 5.x - * [Kafka](http://kafka.apache.org) 0.11.0.0 -> 3.2.3 + * [Kafka](http://kafka.apache.org) 0.11.0.0 -> 3.9.1 * [Spring-Kafka](https://github.com/spring-projects/spring-kafka) Spring Kafka Consumer 1.3.x -> 2.3.x (2.0.x and 2.1.x not tested and not recommended by [the official document](https://spring.io/projects/spring-kafka)) * [ActiveMQ](https://github.com/apache/activemq) 5.10.0 -> 5.15.4 * [RabbitMQ](https://www.rabbitmq.com/) 3.x-> 5.x + * [Spring-RabbitMQ](https://github.com/spring-projects/spring-amqp) 2.x -> 4.x * [Pulsar](http://pulsar.apache.org) 2.2.x -> 2.9.x - * [NATS](https://github.com/nats-io/nats.java) 2.14.x -> 2.15.x + * [NATS](https://github.com/nats-io/nats.java) 2.14.x -> 2.16.5 * [ActiveMQ-Artemis](https://github.com/apache/activemq) 2.30.0 -> 2.31.2 - * Aliyun ONS 1.x (Optional¹) + * Aliyun ONS 1.x (Optional¹), See [SkySPM Plugin Repository](https://github.com/SkyAPM/java-plugin-extensions) * NoSQL * [aerospike](https://github.com/aerospike/aerospike-client-java) 3.x -> 6.x * Redis * [Jedis](https://github.com/xetorthio/jedis) 2.x-4.x - * [Redisson](https://github.com/redisson/redisson) Easy Java Redis client 3.5.2+ - * [Lettuce](https://github.com/lettuce-io/lettuce-core) 5.x + * [Redisson](https://github.com/redisson/redisson) Easy Java Redis client 3.5.0 -> 3.30.0 + * [Lettuce](https://github.com/lettuce-io/lettuce-core) 5.x -> 6.7.1 * [MongoDB Java Driver](https://github.com/mongodb/mongo-java-driver) 2.13-2.14, 3.4.0-3.12.7, 4.0.0-4.1.0 * Memcached Client * [Spymemcached](https://github.com/couchbase/spymemcached) 2.x @@ -98,7 +100,7 @@ metrics based on the tracing data. * [transport-client](https://github.com/elastic/elasticsearch/tree/v6.2.3/client/transport) 6.2.3-6.8.4 * [transport-client](https://github.com/elastic/elasticsearch/tree/7.0/client/transport) 7.0.0-7.5.2 * [rest-high-level-client](https://www.elastic.co/guide/en/elasticsearch/client/java-rest/6.7/index.html) 6.7.1-6.8.4 - * [rest-high-level-client](https://www.elastic.co/guide/en/elasticsearch/client/java-rest/7.0/java-rest-high.html) 7.0.0-7.5.2 + * [rest-high-level-client](https://www.elastic.co/guide/en/elasticsearch/client/java-rest/7.0/java-rest-high.html) 7.x * [Solr](https://github.com/apache/solr/) * [SolrJ](https://github.com/apache/solr/tree/main/solr/solrj) 7.x * [Cassandra](https://github.com/apache/cassandra) 3.x @@ -107,6 +109,7 @@ metrics based on the tracing data. * [hbase-client](https://github.com/apache/hbase) HTable 1.0.0-2.4.2 * Neo4j * [Neo4j-java](https://neo4j.com/docs/java-manual/current/) 4.x + * [Couchbase Java SDK](https://docs.couchbase.com/java-sdk/current/hello-world/overview.html) 3.8.3 (Optional¹), See [leralik's Plugin Repository](https://github.com/leralik/skywalking-java-agent-couchbase-plugin) * Service Discovery * [Netflix Eureka](https://github.com/Netflix/eureka) * Distributed Coordination @@ -120,7 +123,7 @@ metrics based on the tracing data. * Scheduler * [Elastic Job](https://github.com/elasticjob/elastic-job) 2.x * [Apache ShardingSphere-Elasticjob](https://github.com/apache/shardingsphere-elasticjob) 3.x - * [Spring @Scheduled](https://github.com/spring-projects/spring-framework) 3.1+ + * [Spring @Scheduled](https://github.com/spring-projects/spring-framework) 3.1.x -> 6.1.x * [Quartz Scheduler](https://github.com/quartz-scheduler/quartz) 2.x (Optional²) * [XXL Job](https://github.com/xuxueli/xxl-job) 2.x * OpenTracing community supported @@ -138,8 +141,9 @@ metrics based on the tracing data. * JRE Callable and Runnable (Optional²) * JRE ForkJoinPool (Optional²) * Cache - * [Ehcache](https://www.ehcache.org/) 2.x + * [Ehcache](https://www.ehcache.org/) 2.x (Optional²) * [GuavaCache](https://github.com/google/guava) 18.x -> 23.x (Optional²) + * [Caffeine](https://github.com/ben-manes/caffeine) 3.x (Optional²) * Kotlin * [Coroutine](https://kotlinlang.org/docs/coroutines-overview.html) 1.0.1 -> 1.3.x (Optional²) * GraphQL @@ -148,6 +152,7 @@ metrics based on the tracing data. * [Apache Commons DBCP](https://github.com/apache/commons-dbcp) 2.x * [Alibaba Druid](https://github.com/alibaba/druid) 1.x * [HikariCP](https://github.com/brettwooldridge/HikariCP) 3.x -> 4.x + * [C3P0](https://github.com/swaldman/c3p0) 0.9.0 -> 0.10.0 * Logging Framework * [log4j](https://github.com/apache/log4j) 2.x * [log4j2](https://github.com/apache/logging-log4j2) 1.2.x @@ -156,6 +161,8 @@ metrics based on the tracing data. * [MyBatis](https://github.com/mybatis/mybatis-3) 3.4.x -> 3.5.x * Event * [GuavaEventBus](https://github.com/google/guava) 19.x -> 31.x-jre +* GenAI + * [spring-ai](https://github.com/spring-projects/spring-ai) 1.x (OpenAI, Azure OpenAI, Anthropic, Amazon Bedrock, Google GenAI, Google VertexAI, DeepSeek, Mistral AI, ZhiPu AI, MiniMax, Ollama, Cohere, HuggingFace) # Meter Plugins The meter plugin provides the advanced metrics collections, which are not a part of tracing. @@ -166,8 +173,34 @@ The meter plugin provides the advanced metrics collections, which are not a part * [Dubbo](https://github.com/apache/dubbo) 2.5.x -> 2.7.x * [Jetty](https://github.com/eclipse/jetty.project) 9.1.x -> 11.x * [Grizzly](https://github.com/eclipse-ee4j/grizzly) 2.3.x -> 4.x +* Connection Pool + * Supported JDBC drviers + * [MySQL](https://www.mysql.com/) + * [Oracle](https://www.oracle.com/) + * [H2](https://h2database.com/html/main.html) + * [PostgreSQL](https://www.postgresql.org/) + * [MariaDB](https://mariadb.org/) + * [SQL Server](https://www.microsoft.com/en-us/sql-server/) + * [Apache Kylin](https://kylin.apache.org/) + * [Impala](https://impala.apache.org/) + * [ClickHouse](https://clickhouse.com/) + * [Derby](https://db.apache.org/derby/) + * [SQLite](https://www.sqlite.org/index.html) + * [DB2](https://www.ibm.com/products/db2/database) + * Sybase + * [OceanBase](https://www.oceanbase.com/) + * [DaMeng(DM)](https://www.dameng.com/) + * Supported Connection Pool Frameworks + * [Apache Commons DBCP](https://github.com/apache/commons-dbcp) 2.x + * [Alibaba Druid](https://github.com/alibaba/druid) 1.x + * [HikariCP](https://github.com/brettwooldridge/HikariCP) 3.x -> 4.x + * [C3P0](https://github.com/swaldman/c3p0) 0.9.0 -> 0.10.0 + ___ ¹Due to license incompatibilities/restrictions these plugins are hosted and released in 3rd part repository, - go to [SkyAPM java plugin extension repository](https://github.com/SkyAPM/java-plugin-extensions) to get these. + the address following the plugin can help you to get it. ²These plugins affect the performance or must be used under some conditions, from experiences. So only released in `/optional-plugins` or `/bootstrap-plugins`, copy to `/plugins` in order to make them work. + +³These plugins are not tested in the CI/CD pipeline, as the previous added tests are not able to run according to the latest +CI/CD infrastructure limitations, lack of maintenance, or dependencies/images not available(e.g. removed from DockerHub). diff --git a/docs/en/setup/service-agent/java-agent/advanced-reporters.md b/docs/en/setup/service-agent/java-agent/advanced-reporters.md index 44264d4e9f..7ecede0190 100644 --- a/docs/en/setup/service-agent/java-agent/advanced-reporters.md +++ b/docs/en/setup/service-agent/java-agent/advanced-reporters.md @@ -1,5 +1,5 @@ # Advanced Reporters -The advanced report provides an alternative way to submit the agent collected data to the backend. All of them are in the `optional-reporter-plugins` folder, move the one you needed into the `reporter-plugins` folder for the activation. **Notice, don't try to activate multiple reporters, that could cause unexpected fatal errors.** +The advanced report provides an alternative way to submit the agent collected data to the backend. All of them are in the `optional-reporter-plugins` folder, move the one you needed into the `plugins` folder for the activation. **Notice, don't try to activate multiple reporters, that could cause unexpected fatal errors.** ## Kafka Reporter The Kafka reporter plugin support report traces, JVM metrics, Instance Properties, and profiled snapshots to Kafka cluster, which is disabled in default. Move the jar of the plugin, `kafka-reporter-plugin-x.y.z.jar`, from `agent/optional-reporter-plugins` to `agent/plugins` for activating. diff --git a/docs/en/setup/service-agent/java-agent/agent-optional-plugins/Oracle-Resin-plugins.md b/docs/en/setup/service-agent/java-agent/agent-optional-plugins/Oracle-Resin-plugins.md deleted file mode 100644 index 145ef50133..0000000000 --- a/docs/en/setup/service-agent/java-agent/agent-optional-plugins/Oracle-Resin-plugins.md +++ /dev/null @@ -1,6 +0,0 @@ -## Oracle and Resin plugins -These plugins can't be provided in Apache release because of Oracle and Resin Licenses. -If you want to know details, please read [Apache license legal document](https://www.apache.org/legal/resolved.html) - -Due to license incompatibilities/restrictions these plugins are hosted and released in 3rd part repository, -go to [OpenSkywalking java plugin extension repository](https://github.com/OpenSkywalking/java-plugin-extensions) to get these. diff --git a/docs/en/setup/service-agent/java-agent/agent-optional-plugins/Spring-annotation-plugin.md b/docs/en/setup/service-agent/java-agent/agent-optional-plugins/Spring-annotation-plugin.md index 8f2336adee..ddf088ca11 100644 --- a/docs/en/setup/service-agent/java-agent/agent-optional-plugins/Spring-annotation-plugin.md +++ b/docs/en/setup/service-agent/java-agent/agent-optional-plugins/Spring-annotation-plugin.md @@ -1,4 +1,4 @@ -## Spring annotation plugin +# Spring annotation plugin This plugin allows to trace all methods of beans in Spring context, which are annotated with `@Bean`, `@Service`, `@Component` and `@Repository`. diff --git a/docs/en/setup/service-agent/java-agent/agent-optional-plugins/spring-gateway.md b/docs/en/setup/service-agent/java-agent/agent-optional-plugins/spring-gateway.md new file mode 100644 index 0000000000..6b84d91bec --- /dev/null +++ b/docs/en/setup/service-agent/java-agent/agent-optional-plugins/spring-gateway.md @@ -0,0 +1,96 @@ +# Spring Gateway Plugin + +Spring Gateway Plugin only support Spring Gateway 2.x, 3.x and 4.x. It has capabilities to create entry spans for +incoming calls, continue tracing context propagation in Spring Gateway and create exit spans for outgoing calls. + +About the filter extension of Gateway, it provides automatically support as much as possible, including GlobalFilter and GatewayFilter +support. However, the filter extension by using `chain.filter(exchange).then(...)` is not able to transparently. + +## Supported Auto-Instrument Filters + +```java +@Component +public class Filter1 implements GlobalFilter, Ordered { + + private static final Logger log = LoggerFactory.getLogger(Filter1.class); + @Override + public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { + // Available trace context + // For log framework integration(trace context log output) and manual trace context usage. + String traceId = TraceContext.traceId(); + log.info("available traceId: {}", traceId); + + String segmentId = TraceContext.segmentId(); + log.info("available segmentId: {}", segmentId); + + int spanId = TraceContext.spanId(); + log.info("available spanId: {}", spanId); + + return chain.filter(exchange); + } + @Override + public int getOrder() { + return -100; + } +} +``` +```java +@Component +public class GatewayFilter1 implements GatewayFilter { + + private static final Logger log = LoggerFactory.getLogger(GatewayFilter1.class); + @Override + public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { + // Available trace context + log.info("gatewayFilter1 running"); + return chain.filter(exchange); + } +} +``` + +## Unsupported Auto-Instrument Filters +Typically, in the following case, you need to read via [Webflux Tracing Assistant APIs](../Application-toolkit-webflux.md) to get the trace context. + +```java +@Component +public class UnsupportedFilter implements GlobalFilter, Ordered { + private static final Logger log = LoggerFactory.getLogger(UnsupportedFilter.class); + @Override + public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { + String traceId = TraceContext.traceId(); + // Trace ID is available as it's in the GlobalFilter. + log.info("available traceId: {}", traceId); + + String segmentId = TraceContext.segmentId(); + // Segment ID is available as it's in the GlobalFilter. + log.info("available segmentId: {}", segmentId); + + int spanId = TraceContext.spanId(); + // Span ID is available as it's in the GlobalFilter. + log.info("available spanId: {}", spanId); + + return chain.filter(exchange).then(Mono.fromRunnable(() -> { + // Trace ID/context is not available, N/A in the all following logs. + // The trace context is not available in the then-closure. + // Only webflux assistant API can get the trace context. + String traceId2 = WebFluxSkyWalkingTraceContext.traceId(exchange); + // Trace ID is not available, N/A in the logs. + log.info("unavailable in then-closure, available traceId: {} through webflux assistant API", traceId2); + + String segmentId2 = WebFluxSkyWalkingTraceContext.segmentId(exchange); + // Segment ID is not available, N/A in the logs. + log.info("unavailable in then-closure, available segmentId: {} through webflux assistant API", segmentId2); + + int spanId2 = WebFluxSkyWalkingTraceContext.spanId(exchange); + // Span ID is not available, N/A in the logs. + log.info("unavailable in then-closure, available spanId: {} through webflux assistant API", spanId2); + })); + } + + @Override + public int getOrder() { + return 10; + } +} + +``` \ No newline at end of file diff --git a/docs/en/setup/service-agent/java-agent/agent-optional-plugins/trace-ignore-plugin.md b/docs/en/setup/service-agent/java-agent/agent-optional-plugins/trace-ignore-plugin.md index dfaf2a1bed..cc42b54602 100644 --- a/docs/en/setup/service-agent/java-agent/agent-optional-plugins/trace-ignore-plugin.md +++ b/docs/en/setup/service-agent/java-agent/agent-optional-plugins/trace-ignore-plugin.md @@ -1,4 +1,4 @@ -## Support custom trace ignore +# Support Custom Trace Ignoring Here is an optional plugin `apm-trace-ignore-plugin` **Notice:** @@ -18,3 +18,5 @@ There are two ways to configure ignore patterns. Settings through system env has trace.ignore_path=/your/path/1/**,/your/path/2/** ``` +## Conflicts Notice +Due to the mechanism sharing, this plugin has conflicts with the plugin of **sampler plugin with CPU policy**(`trace-sampler-cpu-policy-plugin-*.jar`) in the optional plugin folder. \ No newline at end of file diff --git a/docs/en/setup/service-agent/java-agent/configurations.md b/docs/en/setup/service-agent/java-agent/configurations.md index 4126407d18..ddc14a145e 100644 --- a/docs/en/setup/service-agent/java-agent/configurations.md +++ b/docs/en/setup/service-agent/java-agent/configurations.md @@ -107,15 +107,15 @@ This is the properties list supported in `agent/config/agent.config`. | `plugin.lettuce.trace_redis_parameters` | If set to true, the parameters of Redis commands would be collected by Lettuce agent. | SW_PLUGIN_LETTUCE_TRACE_REDIS_PARAMETERS | `false` | | `plugin.lettuce.redis_parameter_max_length` | If set to positive number and `plugin.lettuce.trace_redis_parameters` is set to `true`, Redis command parameters would be collected and truncated to this length. | SW_PLUGIN_LETTUCE_REDIS_PARAMETER_MAX_LENGTH | `128` | | `plugin.lettuce.operation_mapping_write` | Specify which command should be converted to `write` operation | SW_PLUGIN_LETTUCE_OPERATION_MAPPING_WRITE | | -| `plugin.lettuce.operation_mapping_read ` | Specify which command should be converted to `read` operation | SW_PLUGIN_LETTUCE_OPERATION_MAPPING_READ | Referenc [Lettuce-5.x-plugin](https://github.com/apache/skywalking-java/blob/main/apm-sniffer/apm-sdk-plugin/lettuce-5.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/lettuce/v5/LettucePluginConfig.java) | +| `plugin.lettuce.operation_mapping_read ` | Specify which command should be converted to `read` operation | SW_PLUGIN_LETTUCE_OPERATION_MAPPING_READ | Reference [Lettuce-5.x-plugin](https://github.com/apache/skywalking-java/blob/main/apm-sniffer/apm-sdk-plugin/lettuce-plugins/lettuce-common/src/main/java/org/apache/skywalking/apm/plugin/lettuce/common/LettucePluginConfig.java) | | `plugin.jedis.trace_redis_parameters` | If set to true, the parameters of Redis commands would be collected by Jedis agent. | SW_PLUGIN_JEDIS_TRACE_REDIS_PARAMETERS | `false` | | `plugin.jedis.redis_parameter_max_length` | If set to positive number and `plugin.jedis.trace_redis_parameters` is set to `true`, Redis command parameters would be collected and truncated to this length. | SW_PLUGIN_JEDIS_REDIS_PARAMETER_MAX_LENGTH | `128` | | `plugin.jedis.operation_mapping_write` | Specify which command should be converted to `write` operation | SW_PLUGIN_JEDIS_OPERATION_MAPPING_WRITE | | -| `plugin.jedis.operation_mapping_read ` | Specify which command should be converted to `read` operation | SW_PLUGIN_JEDIS_OPERATION_MAPPING_READ | Referenc [Jedis-4.x-plugin](https://github.com/apache/skywalking-java/blob/main/apm-sniffer/apm-sdk-plugin/jedis-plugins/jedis-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jedis/v4/JedisPluginConfig.java) [jedis-2.x-3.x-plugin](https://github.com/apache/skywalking-java/blob/main/apm-sniffer/apm-sdk-plugin/jedis-plugins/jedis-2.x-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jedis/v3/JedisPluginConfig.java) | +| `plugin.jedis.operation_mapping_read ` | Specify which command should be converted to `read` operation | SW_PLUGIN_JEDIS_OPERATION_MAPPING_READ | Reference [Jedis-4.x-plugin](https://github.com/apache/skywalking-java/blob/main/apm-sniffer/apm-sdk-plugin/jedis-plugins/jedis-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jedis/v4/JedisPluginConfig.java) [jedis-2.x-3.x-plugin](https://github.com/apache/skywalking-java/blob/main/apm-sniffer/apm-sdk-plugin/jedis-plugins/jedis-2.x-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jedis/v3/JedisPluginConfig.java) | | `plugin.redisson.trace_redis_parameters` | If set to true, the parameters of Redis commands would be collected by Redisson agent. | SW_PLUGIN_REDISSON_TRACE_REDIS_PARAMETERS | `false` | | `plugin.redisson.redis_parameter_max_length` | If set to positive number and `plugin.redisson.trace_redis_parameters` is set to `true`, Redis command parameters would be collected and truncated to this length. | SW_PLUGIN_REDISSON_REDIS_PARAMETER_MAX_LENGTH | `128` | | `plugin.redisson.operation_mapping_write` | Specify which command should be converted to `write` operation | SW_PLUGIN_REDISSON_OPERATION_MAPPING_WRITE | | -| `plugin.redisson.operation_mapping_read ` | Specify which command should be converted to `read` operation | SW_PLUGIN_REDISSON_OPERATION_MAPPING_READ | Referenc [Redisson-3.x-plugin](https://github.com/apache/skywalking-java/blob/main/apm-sniffer/apm-sdk-plugin/redisson-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/redisson/v3/RedissonPluginConfig.java) | +| `plugin.redisson.operation_mapping_read ` | Specify which command should be converted to `read` operation | SW_PLUGIN_REDISSON_OPERATION_MAPPING_READ | Reference [Redisson-3.x-plugin](https://github.com/apache/skywalking-java/blob/main/apm-sniffer/apm-sdk-plugin/redisson-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/redisson/v3/RedissonPluginConfig.java) | | `plugin.neo4j.trace_cypher_parameters` | If set to true, the parameters of the cypher would be collected. | SW_PLUGIN_NEO4J_TRACE_CYPHER_PARAMETERS | `false` | | `plugin.neo4j.cypher_parameters_max_length` | If set to positive number, the `db.cypher.parameters` would be truncated to this length, otherwise it would be completely saved, which may cause performance problem. | SW_PLUGIN_NEO4J_CYPHER_PARAMETERS_MAX_LENGTH | `512` | | `plugin.neo4j.cypher_body_max_length` | If set to positive number, the `db.statement` would be truncated to this length, otherwise it would be completely saved, which may cause performance problem. | SW_PLUGIN_NEO4J_CYPHER_BODY_MAX_LENGTH | `2048` | @@ -126,14 +126,18 @@ This is the properties list supported in `agent/config/agent.config`. | `plugin.memcached.operation_mapping_read` | Specify which command should be converted to `read` operation | SW_PLUGIN_MEMCACHED_OPERATION_MAPPING_READ | `set,add,replace,append,prepend,cas,delete,touch,incr,decr` | | `plugin.ehcache.operation_mapping_write` | Specify which command should be converted to `write` operation | SW_PLUGIN_EHCACHE_OPERATION_MAPPING_WRITE | `get,getAll,getQuiet,getKeys,getKeysWithExpiryCheck,getKeysNoDuplicateCheck,releaseRead,tryRead,getWithLoader,getAll,loadAll,getAllWithLoader` | | `plugin.ehcache.operation_mapping_read` | Specify which command should be converted to `read` operation | SW_PLUGIN_EHCACHE_OPERATION_MAPPING_READ | `tryRemoveImmediately,remove,removeAndReturnElement,removeAll,removeQuiet,removeWithWriter,put,putAll,replace,removeQuiet,removeWithWriter,removeElement,removeAll,putWithWriter,putQuiet,putIfAbsent,putIfAbsent` | -| `plugin.guavacache.operation_mapping_write` | Specify which command should be converted to `write` operation | SW_PLUGIN_GUAVACACHE_OPERATION_MAPPING_WRITE | `getIfPresent,get,getAllPresent,size` | -| `plugin.guavacache.operation_mapping_read` | Specify which command should be converted to `read` operation | SW_PLUGIN_GUAVACACHE_OPERATION_MAPPING_READ | `put,putAll,invalidate,invalidateAll,invalidateAll,cleanUp` -| `plugin.nettyhttp.collect_request_body` | This config item controls that whether the Netty-http plugin should collect the http body of the request. | SW_PLUGIN_NETTY_HTTP_COLLECT_REQUEST_BODY | `false` | -| `plugin.nettyhttp.filter_length_limit` | When `COLLECT_REQUEST_BODY` is enabled, how many characters to keep and send to the OAP backend, use negative values to keep and send the complete body. | SW_PLUGIN_NETTY_HTTP_FILTER_LENGTH_LIMIT | `1024` | -| `plugin.nettyhttp.supported_content_types_prefix` | When `COLLECT_REQUEST_BODY` is enabled and content-type start with `HTTP_SUPPORTED_CONTENT_TYPES_PREFIX`, collect the body of the request , multiple paths should be separated by `,` | SW_PLUGIN_NETTY_HTTP_SUPPORTED_CONTENT_TYPES_PREFIX | `application/json,text/` | -| `plugin.rocketmqclient.collect_message_keys` | If set to true, the keys of messages would be collected by the plugin for RocketMQ Java client. -| `plugin.rocketmqclient.collect_message_tags` | If set to true, the tags of messages would be collected by the plugin for RocketMQ Java client. -| +| `plugin.guavacache.operation_mapping_write` | Specify which command should be converted to `write` operation | SW_PLUGIN_GUAVACACHE_OPERATION_MAPPING_WRITE | `put,putAll,invalidate,invalidateAll,invalidateAll,cleanUp` | +| `plugin.guavacache.operation_mapping_read` | Specify which command should be converted to `read` operation | SW_PLUGIN_GUAVACACHE_OPERATION_MAPPING_READ | `getIfPresent,get,getAllPresent,size` | +| `plugin.nettyhttp.collect_request_body` | This config item controls that whether the Netty-http plugin should collect the http body of the request. | SW_PLUGIN_NETTY_HTTP_COLLECT_REQUEST_BODY | `false` | +| `plugin.nettyhttp.filter_length_limit` | When `COLLECT_REQUEST_BODY` is enabled, how many characters to keep and send to the OAP backend, use negative values to keep and send the complete body. | SW_PLUGIN_NETTY_HTTP_FILTER_LENGTH_LIMIT | `1024` | +| `plugin.nettyhttp.supported_content_types_prefix` | When `COLLECT_REQUEST_BODY` is enabled and content-type start with `HTTP_SUPPORTED_CONTENT_TYPES_PREFIX`, collect the body of the request , multiple paths should be separated by `,` | SW_PLUGIN_NETTY_HTTP_SUPPORTED_CONTENT_TYPES_PREFIX | `application/json,text/` | +| `plugin.rocketmqclient.collect_message_keys` | If set to true, the keys of messages would be collected by the plugin for RocketMQ Java client. | | | +| `plugin.rocketmqclient.collect_message_tags` | If set to true, the tags of messages would be collected by the plugin for RocketMQ Java client. | | | +| `plugin.solon.http_params_length_threshold` | Define the max length of collected HTTP parameters. The default value(=0) means not collecting. | SW_PLUGIN_SOLON_HTTP_PARAMS_LENGTH_THRESHOLD | `0` | +| `plugin.solon.include_http_headers` | It controls what header data should be collected, values must be in lower case, if empty, no header data will be collected. default is empty. | SW_PLUGIN_SOLON_INCLUDE_HTTP_HEADERS | ``(No header would be collected) | +| `plugin.solon.http_body_length_threshold` | Define the max length of collected HTTP body. The default value(=0) means not collecting. | SW_PLUGIN_SOLON_HTTP_BODY_LENGTH_THRESHOLD | `0` | +| `plugin.caffeine.operation_mapping_write` | Specify which command should be converted to `write` operation | SW_PLUGIN_EHCACHE_OPERATION_MAPPING_WRITE | `put,putAll,remove,clear` | +| `plugin.caffeine.operation_mapping_read` | Specify which command should be converted to `read` operation | SW_PLUGIN_EHCACHE_OPERATION_MAPPING_READ | `getIfPresent,getAllPresent,computeIfAbsent` | # Reset Collection/Map type configurations as empty collection. diff --git a/docs/menu.yml b/docs/menu.yml index 3964b059be..7ba331d586 100644 --- a/docs/menu.yml +++ b/docs/menu.yml @@ -74,12 +74,24 @@ catalog: path: "/en/setup/service-agent/java-agent/application-toolkit-logback-1.x/" - name: "Logic Endpoint" path: "/en/setup/service-agent/java-agent/logic-endpoint" + - name: "Agent Self Observability" + path: "/en/setup/service-agent/java-agent/Agent-self-observability" - name: "Plugins" catalog: - name: "Supported middleware, framework and library" path: "/en/setup/service-agent/java-agent/Supported-list" - name: "Optional Plugins" - path: "/en/setup/service-agent/java-agent/Optional-plugins" + catalog: + - name: "Plugin Manifest" + path: "/en/setup/service-agent/java-agent/Optional-plugins" + - name: "Spring Annotation Plugin" + path: "/en/setup/service-agent/java-agent/agent-optional-plugins/Spring-annotation-plugin" + - name: "Spring Gateway" + path: "/en/setup/service-agent/java-agent/agent-optional-plugins/spring-gateway" + - name: "Custom Trace Ignoring" + path: "/en/setup/service-agent/java-agent/agent-optional-plugins/trace-ignore-plugin" + - name: "Kotlin Coroutine" + path: "/en/setup/service-agent/java-agent/agent-optional-plugins/Kotlin-Coroutine-plugin" - name: "Bootstrap/JVM class plugin" path: "/en/setup/service-agent/java-agent/Bootstrap-plugins" - name: "Agent Configuration Properties" @@ -88,7 +100,9 @@ catalog: path: "/en/setup/service-agent/java-agent/configuration-discovery" - name: "Advanced reporters" path: "/en/setup/service-agent/java-agent/advanced-reporters" - - name: "Plugin development guide" + - name: "Plugin Test Guide" + path: "/en/setup/service-agent/java-agent/Plugin-test/" + - name: "Plugin Development Guide" path: "/en/setup/service-agent/java-agent/java-plugin-development-guide/" - name: "Java Agent Performance Test" path: "https://skyapmtest.github.io/Agent-Benchmarks/" diff --git a/pom.xml b/pom.xml index e5b629f0e0..b675688a4a 100755 --- a/pom.xml +++ b/pom.xml @@ -22,12 +22,12 @@ org.apache.skywalking java-agent - 9.2.0-SNAPSHOT + 9.7.0-SNAPSHOT org.apache apache - 21 + 35 @@ -78,25 +78,33 @@ UTF-8 + 8 + 8 + 8 8 6.18 4.12 5.0.0 2.0.2 - 1.18.30 + 1.18.42 - 1.14.9 - 1.53.0 - 4.1.100.Final - 2.8.9 - 1.6.2 + 1.17.6 + 1.74.0 + 4.1.124.Final + 2.13.1 + + + 32.1.3-jre + + 1.7.1 0.6.1 - 3.21.7 - 1.53.0 - 2.0.48.Final + 3.25.5 + 1.74.0 + 2.0.70.Final 1.3.2 3.1 + 3.0 6.0.53 @@ -111,18 +119,19 @@ 2.22.0 3.2.0 3.1.0 - 3.1.1 + 3.3.0 3.0.0-M2 3.10.1 3.1.0 3.0.1 + 3.0.0-M8 2.5 4.3.0 3.2.1 1.33 1.5 true - + 0.46.0 @@ -260,6 +269,28 @@ ${jmh.version} test + + tools.profiler + async-profiler + ${async-profiler.version} + + + io.netty + netty-bom + ${netty.version} + pom + import + + + io.netty + netty-tcnative-boringssl-static + ${netty-tcnative-boringssl-static.version} + + + io.netty + netty-tcnative-classes + ${netty-tcnative-boringssl-static.version} + @@ -307,6 +338,32 @@ %a-%t-%i + + maven-enforcer-plugin + ${maven-enforcer-plugin.version} + + + maven-compiler-plugin + ${maven-compiler-plugin.version} + + + maven-resources-plugin + ${maven-resource-plugin.version} + + + maven-source-plugin + ${maven-source-plugin.version} + + + org.apache.maven.plugins + maven-surefire-plugin + ${maven-surefire-plugin.version} + + + org.apache.maven.plugins + maven-dependency-plugin + ${maven-dependency-plugin.version} + @@ -325,7 +382,6 @@ maven-enforcer-plugin - ${maven-enforcer-plugin.version} enforce-java @@ -349,16 +405,21 @@ maven-compiler-plugin - ${maven-compiler-plugin.version} ${compiler.version} ${compiler.version} ${project.build.sourceEncoding} + + + org.projectlombok + lombok + ${lombok.version} + + maven-resources-plugin - ${maven-resource-plugin.version} ${project.build.sourceEncoding} @@ -373,7 +434,6 @@ maven-source-plugin - ${maven-source-plugin.version} attach-sources @@ -390,7 +450,7 @@ ${maven.multiModuleProjectDirectory}/apm-checkstyle/checkStyle.xml ${maven.multiModuleProjectDirectory}/apm-checkstyle/CHECKSTYLE_HEAD - UTF-8 + ${project.build.sourceEncoding} true true ${checkstyle.fails.on.error} @@ -448,7 +508,6 @@ org.apache.maven.plugins maven-surefire-plugin - 3.0.0-M8 1 false diff --git a/test/e2e/base/base-compose.yml b/test/e2e/base/base-compose.yml index e6b4b70c4b..f25eb8ce25 100644 --- a/test/e2e/base/base-compose.yml +++ b/test/e2e/base/base-compose.yml @@ -18,7 +18,7 @@ version: '2.1' services: oap: - image: ghcr.io/apache/skywalking/oap:1730f2c84bbd4da999ec2c74d1c26db31d5a0d24 + image: "ghcr.io/apache/skywalking/oap:${SW_OAP_COMMIT}" expose: - 11800 - 12800 @@ -26,7 +26,23 @@ services: - e2e restart: on-failure healthcheck: - test: ["CMD", "sh", "-c", "nc -zn 127.0.0.1 11800"] + test: [ "CMD", "bash", "-c", "cat < /dev/null > /dev/tcp/127.0.0.1/11800" ] + interval: 5s + timeout: 60s + retries: 120 + environment: + SW_STORAGE_BANYANDB_TARGETS: banyandb:17912 + + banyandb: + image: "ghcr.io/apache/skywalking-banyandb:${SW_BANYANDB_COMMIT}" + networks: + - e2e + ports: + - 17912:17912 + - 17913:17913 + command: standalone --stream-root-path /tmp/stream-data --measure-root-path /tmp/measure-data --measure-metadata-cache-wait-duration 1m --stream-metadata-cache-wait-duration 1m + healthcheck: + test: [ "CMD", "sh", "-c", "nc -nz 127.0.0.1 17912" ] interval: 5s timeout: 60s retries: 120 diff --git a/test/e2e/base/pom.xml b/test/e2e/base/pom.xml index 9ae8d9e70e..63b6a687f2 100644 --- a/test/e2e/base/pom.xml +++ b/test/e2e/base/pom.xml @@ -51,7 +51,7 @@ ${java.version} ${java.version} UTF-8 - 2.2.5.RELEASE + 2.7.18 30.1.1-jre 1.18.20 diff --git a/test/e2e/case/expected/event-list.yml b/test/e2e/case/expected/event-list.yml index e5a19bfe07..e020869abc 100644 --- a/test/e2e/case/expected/event-list.yml +++ b/test/e2e/case/expected/event-list.yml @@ -30,5 +30,5 @@ events: {{- end }} starttime: {{ gt .starttime 0 }} endtime: {{ gt .endtime 0 }} -{{- end }} -total: {{ gt .total 0 }} \ No newline at end of file + layer: "GENERAL" +{{- end }} \ No newline at end of file diff --git a/test/e2e/case/expected/logs-list.yml b/test/e2e/case/expected/logs-list.yml index 23de580d25..2e02cbd076 100644 --- a/test/e2e/case/expected/logs-list.yml +++ b/test/e2e/case/expected/logs-list.yml @@ -36,4 +36,5 @@ logs: value: {{ notEmpty .value }} {{- end }} {{- end }} -total: {{ gt .total 0 }} +debuggingtrace: null +errorreason: null \ No newline at end of file diff --git a/test/e2e/case/expected/metrics-has-value.yml b/test/e2e/case/expected/metrics-has-value.yml index 27ae47c8ff..baeae56d1c 100644 --- a/test/e2e/case/expected/metrics-has-value.yml +++ b/test/e2e/case/expected/metrics-has-value.yml @@ -13,9 +13,13 @@ # See the License for the specific language governing permissions and # limitations under the License. -{{- contains . }} + {{- contains . }} - key: {{ notEmpty .key }} - value: {{ ge .value 0 }} + value: + value: 0 + isemptyvalue: true - key: {{ notEmpty .key }} - value: {{ ge .value 1 }} -{{- end }} \ No newline at end of file + value: + value: {{ ge .value.value 1 }} + isemptyvalue: false + {{- end }} \ No newline at end of file diff --git a/test/e2e/case/expected/service.yml b/test/e2e/case/expected/service.yml index 129f9d3e8b..e60a3313db 100644 --- a/test/e2e/case/expected/service.yml +++ b/test/e2e/case/expected/service.yml @@ -13,9 +13,28 @@ # See the License for the specific language governing permissions and # limitations under the License. +{{- contains . }} - id: {{ b64enc "e2e-service-provider" }}.1 name: e2e-service-provider group: "" + shortname: e2e-service-provider + normal: true + layers: + - SO11Y_JAVA_AGENT + - GENERAL - id: {{ b64enc "e2e-service-consumer" }}.1 name: e2e-service-consumer - group: "" \ No newline at end of file + group: "" + shortname: e2e-service-consumer + normal: true + layers: + - SO11Y_JAVA_AGENT + - GENERAL +- id: {{ b64enc "localhost:-1" }}.0 + name: localhost:-1 + group: "" + shortname: localhost:-1 + normal: false + layers: + - VIRTUAL_DATABASE +{{- end }} \ No newline at end of file diff --git a/test/e2e/case/expected/trace-info-detail.yml b/test/e2e/case/expected/trace-info-detail.yml index f50b094f53..fad2fdbe70 100644 --- a/test/e2e/case/expected/trace-info-detail.yml +++ b/test/e2e/case/expected/trace-info-detail.yml @@ -13,6 +13,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +debuggingtrace: null spans: {{- contains .spans}} - traceid: {{ notEmpty .traceid }} @@ -46,4 +47,5 @@ spans: value: '200' {{- end }} logs: [] + attachedevents: [] {{- end }} diff --git a/test/e2e/case/expected/trace-users-detail.yml b/test/e2e/case/expected/trace-users-detail.yml index 3a1030eea6..ef0d1a39cf 100644 --- a/test/e2e/case/expected/trace-users-detail.yml +++ b/test/e2e/case/expected/trace-users-detail.yml @@ -13,6 +13,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +debuggingtrace: null spans: {{- contains .spans}} - traceid: {{ notEmpty .traceid }} @@ -40,4 +41,5 @@ spans: value: '200' {{- end }} logs: [] + attachedevents: [] {{- end }} diff --git a/test/e2e/case/expected/traces-list.yml b/test/e2e/case/expected/traces-list.yml index bed95518e2..8996767757 100644 --- a/test/e2e/case/expected/traces-list.yml +++ b/test/e2e/case/expected/traces-list.yml @@ -13,17 +13,15 @@ # See the License for the specific language governing permissions and # limitations under the License. +debuggingtrace: null traces: {{- contains .traces }} - - segmentid: {{ notEmpty .segmentid }} - endpointnames: - {{- contains .endpointnames }} - - POST:/info - {{- end }} - duration: {{ ge .duration 0 }} - start: "{{ notEmpty .start}}" - iserror: false - traceids: - - {{ (index .traceids 0) }} +- segmentid: {{ notEmpty .segmentid }} + endpointnames: + - POST:/info + duration: {{ ge .duration 0 }} + start: "{{ notEmpty .start}}" + iserror: false + traceids: + - {{ index .traceids 0 }} {{- end }} -total: {{ gt .total 0 }} diff --git a/test/e2e/case/grpc/docker-compose.yml b/test/e2e/case/grpc/docker-compose.yml index 621587bd60..f79160cb8c 100644 --- a/test/e2e/case/grpc/docker-compose.yml +++ b/test/e2e/case/grpc/docker-compose.yml @@ -23,6 +23,13 @@ services: ports: - 12800 + banyandb: + extends: + file: ../../base/base-compose.yml + service: banyandb + ports: + - 17912 + provider: extends: file: ../../base/base-compose.yml diff --git a/test/e2e/case/kafka/docker-compose.yml b/test/e2e/case/kafka/docker-compose.yml index 3760fd44fb..4f25a14cd6 100644 --- a/test/e2e/case/kafka/docker-compose.yml +++ b/test/e2e/case/kafka/docker-compose.yml @@ -32,7 +32,7 @@ services: retries: 120 broker-a: - image: bitnami/kafka:2.4.1 + image: bitnamilegacy/kafka:2.4.1 hostname: broker-a expose: - 9092 @@ -52,7 +52,7 @@ services: retries: 120 broker-b: - image: bitnami/kafka:2.4.1 + image: bitnamilegacy/kafka:2.4.1 hostname: broker-b expose: - 9092 @@ -89,6 +89,13 @@ services: broker-b: condition: service_healthy + banyandb: + extends: + file: ../../base/base-compose.yml + service: banyandb + ports: + - 17912 + kafkaprovider: extends: file: ../../base/base-compose.yml diff --git a/test/e2e/script/env b/test/e2e/script/env index e57ec37b21..01f311a60b 100644 --- a/test/e2e/script/env +++ b/test/e2e/script/env @@ -13,4 +13,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -SW_CTL_COMMIT=b90255132f916f53eb90955cc8a6445b03a4bec3 +SW_CTL_COMMIT=77b4c49e89c9c000278f44e62729d534f2ec842e +SW_OAP_COMMIT=dc8740d4757b35374283c4850a9a080e40f0eb79 +SW_BANYANDB_COMMIT=7e5b2d0404e8ad6d5835eee6fe589a2544d0decb \ No newline at end of file diff --git a/test/e2e/script/prepare/install-swctl.sh b/test/e2e/script/prepare/install-swctl.sh index 587541f7ee..d435f5ad92 100644 --- a/test/e2e/script/prepare/install-swctl.sh +++ b/test/e2e/script/prepare/install-swctl.sh @@ -22,12 +22,18 @@ BASE_DIR=$1 BIN_DIR=$2 -set -ex - -if ! command -v swctl &> /dev/null; then +install_swctl() { mkdir -p $BASE_DIR/swctl && cd $BASE_DIR/swctl curl -kLo skywalking-cli.tar.gz https://github.com/apache/skywalking-cli/archive/${SW_CTL_COMMIT}.tar.gz tar -zxf skywalking-cli.tar.gz --strip=1 - utype=$(uname | awk '{print tolower($0)}') - make $utype && mv bin/swctl-*-$utype-amd64 $BIN_DIR/swctl -fi \ No newline at end of file + VERSION=${SW_CTL_COMMIT} make install DESTDIR=$BIN_DIR +} + +if ! command -v swctl &> /dev/null; then + echo "swctl is not installed" + install_swctl +elif ! swctl --version | grep -q "${SW_CTL_COMMIT::7}"; then + # Check if the installed version is correct + echo "swctl is already installed, but version is not ${SW_CTL_COMMIT}, will re-install it" + install_swctl +fi diff --git a/test/plugin/agent-test-tools/pom.xml b/test/plugin/agent-test-tools/pom.xml index 374263e7f4..13a08a39da 100644 --- a/test/plugin/agent-test-tools/pom.xml +++ b/test/plugin/agent-test-tools/pom.xml @@ -35,7 +35,7 @@ pom - cf62c1b733fe2861229201a67b9cc0075ac3e236 + 7220c715d280cf0d7421f17bbc8c8de57249914d ${project.basedir}/target/agent-test-tools https://github.com/apache/skywalking-agent-test-tool.git diff --git a/test/plugin/containers/jvm-container/pom.xml b/test/plugin/containers/jvm-container/pom.xml index 65d4045469..e71083c86e 100644 --- a/test/plugin/containers/jvm-container/pom.xml +++ b/test/plugin/containers/jvm-container/pom.xml @@ -53,7 +53,7 @@ chmod +x /usr/local/skywalking/run.sh tar -xvf ../tools/skywalking-mock-collector.tar.gz -C ../tools apt-get update -y - apt-get install -y unzip + apt-get install -y unzip bash rm -rf /var/lib/apt/lists/* ["/usr/local/skywalking/run.sh"] diff --git a/test/plugin/containers/jvm-container/src/main/docker/run.sh b/test/plugin/containers/jvm-container/src/main/docker/run.sh index 9d1327c621..3a70fe4930 100644 --- a/test/plugin/containers/jvm-container/src/main/docker/run.sh +++ b/test/plugin/containers/jvm-container/src/main/docker/run.sh @@ -24,7 +24,10 @@ exitOnError() { } exitAndClean() { - [[ -n $DEBUG_MODE ]] && exit $1; + if [[ -n $DEBUG_MODE ]]; then + chmod -R a+r ${SCENARIO_HOME} 2>/dev/null + exit $1 + fi [[ -f ${SCENARIO_HOME}/data/actualData.yaml ]] && rm -rf ${SCENARIO_HOME}/data/actualData.yaml [[ -d ${LOGS_HOME} ]] && rm -rf ${LOGS_HOME} @@ -84,7 +87,7 @@ export agent_opts=" -Dskywalking.meter.report_interval=1 -Xms256m -Xmx256m ${agent_opts}" -bash /var/run/${SCENARIO_NAME}/${SCENARIO_START_SCRIPT} 1>${LOGS_HOME}/scenario.out & +bash /var/run/${SCENARIO_NAME}/${SCENARIO_START_SCRIPT} >${LOGS_HOME}/scenario.out 2>&1 & sleep 5 healthCheck ${SCENARIO_HEALTH_CHECK_URL} diff --git a/test/plugin/pom.xml b/test/plugin/pom.xml index 0b3033000e..b42f4a69b7 100644 --- a/test/plugin/pom.xml +++ b/test/plugin/pom.xml @@ -34,6 +34,18 @@ containers + + + set-compiler-release + + [9,) + + + ${java.version} + + + + 8 ${java.version} @@ -47,7 +59,7 @@ 1.18.20 1.24 - 3.8.1 + 3.14.0 true diff --git a/test/plugin/run.sh b/test/plugin/run.sh index 47a87de456..164eebfe6d 100755 --- a/test/plugin/run.sh +++ b/test/plugin/run.sh @@ -159,6 +159,8 @@ remove_dir() { start_stamp=`date +%s` parse_commandline "$@" +echo "Scenario=$scenario_name JavaImage=$base_image_java TomcatImage=$base_image_tomcat" + if [[ "$cleanup" == "on" ]]; then do_cleanup [[ -z "${scenario_name}" ]] && exit 0 diff --git a/test/plugin/runner-helper/src/main/resources/compose-start-script.template b/test/plugin/runner-helper/src/main/resources/compose-start-script.template index 6ee08de4b5..c94e415bf2 100644 --- a/test/plugin/runner-helper/src/main/resources/compose-start-script.template +++ b/test/plugin/runner-helper/src/main/resources/compose-start-script.template @@ -26,20 +26,51 @@ docker_container_name="${docker_container_name}_1" <#noparse> container_name="${project_name}_${docker_container_name}" -docker-compose -p ${project_name} -f ${compose_file} up -d + +# Diagnostic: log docker compose version +docker compose version >&2 || true + +echo "=== Starting docker compose ===" >&2 +compose_output=$(docker compose -p ${project_name} -f ${compose_file} up -d 2>&1) +compose_exit=$? +echo "${compose_output}" >&2 +if [[ ${compose_exit} -ne 0 ]]; then + echo "docker compose up exited with code ${compose_exit}" >&2 +fi container_id=`docker ps -qf "name=${container_name}"` if [[ -z "${container_id}" ]]; then - echo "docker startup failure!" >&2 + echo "docker startup failure! container_name=${container_name} not found." >&2 + echo "=== docker ps -a ===" >&2 + docker ps -a >&2 || true + echo "=== docker compose logs ===" >&2 + docker compose -p ${project_name} -f ${compose_file} logs 2>&1 >&2 || true + # Create container.log with diagnostic info + mkdir -p ${SCENARIO_HOME}/logs + { + echo "=== CONTAINER STARTUP FAILURE ===" + echo "container_name=${container_name} was not found by docker ps" + echo "compose_exit_code=${compose_exit}" + echo "compose_output:" + echo "${compose_output}" + echo "=== docker ps -a ===" + docker ps -a 2>&1 || true + echo "=== docker compose logs ===" + docker compose -p ${project_name} -f ${compose_file} logs 2>&1 || true + } >${SCENARIO_HOME}/logs/container.log + docker compose -p ${project_name} -f ${compose_file} kill 2>/dev/null || true + docker compose -p ${project_name} -f ${compose_file} rm -f 2>/dev/null || true status=1 else status=`docker wait ${container_id}` [[ $status -ne 0 ]] && docker logs ${container_id} >&2 + # Recreate logs dir in case the container removed it via volume mount + mkdir -p ${SCENARIO_HOME}/logs docker logs ${container_id} >${SCENARIO_HOME}/logs/container.log - docker-compose -p ${project_name} -f ${compose_file} kill - docker-compose -p ${project_name} -f ${compose_file} rm -f + docker compose -p ${project_name} -f ${compose_file} kill + docker compose -p ${project_name} -f ${compose_file} rm -f fi diff --git a/test/plugin/runner-helper/src/main/resources/container-start-script.template b/test/plugin/runner-helper/src/main/resources/container-start-script.template index 108fb0bf20..97ee284649 100644 --- a/test/plugin/runner-helper/src/main/resources/container-start-script.template +++ b/test/plugin/runner-helper/src/main/resources/container-start-script.template @@ -33,6 +33,11 @@ docker run -d \ <#if debug_mode??> --env DEBUG_MODE=${debug_mode} \ + <#if environments??> + <#list environments as env> + --env ${env} \ + + -v ${agent_home}:/usr/local/skywalking/scenario/agent \ -v ${scenario_home}:/usr/local/skywalking/scenario \ -v ${jacoco_home}:/jacoco \ @@ -42,13 +47,25 @@ sleep 3 container_name=`docker ps -aqf "name=${docker_container_name}"` <#noparse> -status=$(docker wait ${container_name}) - if [[ -z ${container_name} ]]; then echo "docker startup failure!" >&2 + echo "=== docker ps -a ===" >&2 + docker ps -a >&2 || true + # Create container.log with diagnostic info + mkdir -p ${SCENARIO_HOME}/logs + { + echo "=== CONTAINER STARTUP FAILURE ===" + echo "Container not found by docker ps" + echo "=== docker ps -a ===" + docker ps -a 2>&1 || true + } >${SCENARIO_HOME}/logs/container.log status=1 else + status=$(docker wait ${container_name}) + [[ $status -ne 0 ]] && docker logs ${container_name} >&2 + # Recreate logs dir in case the container removed it via volume mount + mkdir -p ${SCENARIO_HOME}/logs docker logs ${container_name} >${SCENARIO_HOME}/logs/container.log docker container rm -f $container_name fi diff --git a/test/plugin/scenarios/oracle-scenario/bin/startup.sh b/test/plugin/scenarios/agent-so11y-scenario/bin/startup.sh similarity index 92% rename from test/plugin/scenarios/oracle-scenario/bin/startup.sh rename to test/plugin/scenarios/agent-so11y-scenario/bin/startup.sh index a8f932fa0d..e4140338be 100644 --- a/test/plugin/scenarios/oracle-scenario/bin/startup.sh +++ b/test/plugin/scenarios/agent-so11y-scenario/bin/startup.sh @@ -18,4 +18,4 @@ home="$(cd "$(dirname $0)"; pwd)" -java -jar ${agent_opts} ${home}/../libs/oracle-scenario.jar & \ No newline at end of file +java -jar ${agent_opts} ${home}/../libs/agent-so11y-scenario.jar & \ No newline at end of file diff --git a/test/plugin/scenarios/agent-so11y-scenario/config/expectedData.yaml b/test/plugin/scenarios/agent-so11y-scenario/config/expectedData.yaml new file mode 100644 index 0000000000..8e044e041f --- /dev/null +++ b/test/plugin/scenarios/agent-so11y-scenario/config/expectedData.yaml @@ -0,0 +1,177 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +segmentItems: +- serviceName: agent-so11y-scenario + segmentSize: gt 1 + segments: + - segmentId: not null + spans: + - operationName: H2/JDBC/PreparedStatement/execute + parentSpanId: 0 + spanId: 1 + spanLayer: Database + startTime: gt 0 + endTime: gt 0 + componentId: 32 + isError: false + spanType: Exit + peer: localhost:-1 + tags: + - {key: db.type, value: H2} + - {key: db.instance, value: test} + - key: db.statement + value: "CREATE TABLE test_007(\nid VARCHAR(1) PRIMARY KEY, \nvalue VARCHAR(1)\ + \ NOT NULL)" + skipAnalysis: 'false' + - operationName: H2/JDBC/CallableStatement/execute + parentSpanId: 0 + spanId: 2 + spanLayer: Database + startTime: gt 0 + endTime: gt 0 + componentId: 32 + isError: false + spanType: Exit + peer: localhost:-1 + tags: + - {key: db.type, value: H2} + - {key: db.instance, value: test} + - {key: db.statement, value: 'INSERT INTO test_007(id, value) VALUES(?,?)'} + skipAnalysis: 'false' + - operationName: H2/JDBC/Statement/execute + parentSpanId: 0 + spanId: 3 + spanLayer: Database + startTime: gt 0 + endTime: gt 0 + componentId: 32 + isError: false + spanType: Exit + peer: localhost:-1 + tags: + - {key: db.type, value: H2} + - {key: db.instance, value: test} + - {key: db.statement, value: DROP table test_007} + skipAnalysis: 'false' + - operationName: /agent-so11y-scenario/case/ignore.html + parentSpanId: 0 + spanId: 4 + spanLayer: Http + startTime: gt 0 + endTime: gt 0 + componentId: 66 + isError: false + spanType: Exit + peer: localhost:8080 + tags: + - {key: http.method, value: GET} + - {key: url, value: 'http://localhost:8080/agent-so11y-scenario/case/ignore.html'} + - {key: http.status_code, value: '200'} + skipAnalysis: 'false' + - operationName: /agent-so11y-scenario/case/propagated + parentSpanId: 0 + spanId: 5 + spanLayer: Http + startTime: gt 0 + endTime: gt 0 + componentId: 66 + isError: false + spanType: Exit + peer: localhost:8080 + tags: + - {key: http.method, value: GET} + - {key: url, value: 'http://localhost:8080/agent-so11y-scenario/case/propagated'} + - {key: http.status_code, value: '200'} + skipAnalysis: 'false' + - operationName: GET:/agent-so11y-scenario/case/agent-so11y-scenario + parentSpanId: -1 + spanId: 0 + spanLayer: Http + startTime: gt 0 + endTime: gt 0 + componentId: 1 + isError: false + spanType: Entry + peer: '' + tags: + - {key: url, value: 'http://localhost:8080/agent-so11y-scenario/case/agent-so11y-scenario'} + - {key: http.method, value: GET} + - {key: http.status_code, value: '200'} + skipAnalysis: 'false' + - segmentId: not null + spans: + - operationName: GET:/agent-so11y-scenario/case/propagated + parentSpanId: -1 + spanId: 0 + spanLayer: Http + startTime: gt 0 + endTime: gt 0 + componentId: 1 + isError: false + spanType: Entry + peer: '' + tags: + - {key: url, value: 'http://localhost:8080/agent-so11y-scenario/case/propagated'} + - {key: http.method, value: GET} + - {key: http.status_code, value: '200'} + skipAnalysis: 'false' + refs: + - {parentEndpoint: 'GET:/agent-so11y-scenario/case/agent-so11y-scenario', networkAddress: 'localhost:8080', + refType: CrossProcess, parentSpanId: 5, parentTraceSegmentId: not null, + parentServiceInstance: not null, parentService: agent-so11y-scenario, + traceId: not null} + +meterItems: +- serviceName: agent-so11y-scenario + meterSize: ge 9 + meters: + - meterId: + name: created_tracing_context_counter + tags: + - {name: created_by, value: propagated} + singleValue: 1.0 + - meterId: + name: created_tracing_context_counter + tags: + - {name: created_by, value: sampler} + singleValue: 1.0 + - meterId: + name: finished_tracing_context_counter + tags: [] + singleValue: 2.0 + - meterId: + name: created_ignored_context_counter + tags: + - {name: created_by, value: propagated} + singleValue: 1.0 + - meterId: + name: created_ignored_context_counter + tags: + - {name: created_by, value: sampler} + singleValue: 1.0 + - meterId: + name: finished_ignored_context_counter + tags: [] + singleValue: 2.0 + - meterId: + name: tracing_context_performance + tags: [] + histogramBuckets: [0.0, 1000.0, 10000.0, 50000.0, 100000.0, 300000.0, 500000.0, 1000000.0, 5000000.0, 1.0E7, 2.0E7, 5.0E7, 1.0E8] + - meterId: + name: possible_leaked_context_counter + tags: + - {name: source, value: tracing} + singleValue: 1.0 diff --git a/test/plugin/scenarios/agent-so11y-scenario/configuration.yml b/test/plugin/scenarios/agent-so11y-scenario/configuration.yml new file mode 100644 index 0000000000..7d9a31395c --- /dev/null +++ b/test/plugin/scenarios/agent-so11y-scenario/configuration.yml @@ -0,0 +1,25 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +type: jvm +entryService: http://localhost:8080/agent-so11y-scenario/case/agent-so11y-scenario +healthCheck: http://localhost:8080/agent-so11y-scenario/case/healthCheck.html +runningMode: with_bootstrap +withPlugins: apm-jdk-http-plugin-*.jar +startScript: ./bin/startup.sh +environment: + - SW_AGENT_SPAN_LIMIT=6 + - SW_METER_REPORT_INTERVAL=1 diff --git a/test/plugin/scenarios/impala-jdbc-2.6.x-scenario/pom.xml b/test/plugin/scenarios/agent-so11y-scenario/pom.xml similarity index 82% rename from test/plugin/scenarios/impala-jdbc-2.6.x-scenario/pom.xml rename to test/plugin/scenarios/agent-so11y-scenario/pom.xml index 30223782b0..39095c57ad 100644 --- a/test/plugin/scenarios/impala-jdbc-2.6.x-scenario/pom.xml +++ b/test/plugin/scenarios/agent-so11y-scenario/pom.xml @@ -21,7 +21,7 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> org.apache.skywalking.apm.testcase - impala-jdbc-2.6.x-scenario + agent-so11y-scenario 1.0.0 jar @@ -30,14 +30,14 @@ UTF-8 1.8 - 2.6.26.1031 - ${test.framework.version} - 2.1.6.RELEASE + 3.8.1 + 1.0.0 + 1.4.177 + 2.5.6 1.18.20 - 2.6.26.1031 - skywalking-impala-jdbc-2.6.x-scenario + skywalking-agent-so11y-scenario @@ -52,11 +52,6 @@ - - Impala - ImpalaJDBC42 - ${test.framework.version} - org.springframework.boot spring-boot-starter-web @@ -77,19 +72,16 @@ ${lombok.version} provided - - - - - - cloudera.repo - https://repository.cloudera.com/artifactory/cloudera-repos/ - - + + com.h2database + h2 + ${h2.version} + + - impala-jdbc-2.6.x-scenario + agent-so11y-scenario org.springframework.boot @@ -105,6 +97,7 @@ maven-compiler-plugin + ${maven-compiler-plugin.version} ${compiler.version} ${compiler.version} diff --git a/test/plugin/scenarios/oracle-scenario/src/main/assembly/assembly.xml b/test/plugin/scenarios/agent-so11y-scenario/src/main/assembly/assembly.xml similarity index 94% rename from test/plugin/scenarios/oracle-scenario/src/main/assembly/assembly.xml rename to test/plugin/scenarios/agent-so11y-scenario/src/main/assembly/assembly.xml index 9e4db94d75..abd3f9e799 100644 --- a/test/plugin/scenarios/oracle-scenario/src/main/assembly/assembly.xml +++ b/test/plugin/scenarios/agent-so11y-scenario/src/main/assembly/assembly.xml @@ -33,7 +33,7 @@ - ${project.build.directory}/oracle-scenario.jar + ${project.build.directory}/agent-so11y-scenario.jar ./libs 0775 diff --git a/test/plugin/scenarios/agent-so11y-scenario/src/main/java/org/apache/skywalking/apm/testcase/h2/Application.java b/test/plugin/scenarios/agent-so11y-scenario/src/main/java/org/apache/skywalking/apm/testcase/h2/Application.java new file mode 100644 index 0000000000..e063f8d641 --- /dev/null +++ b/test/plugin/scenarios/agent-so11y-scenario/src/main/java/org/apache/skywalking/apm/testcase/h2/Application.java @@ -0,0 +1,30 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.testcase.h2; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class Application { + + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } +} diff --git a/test/plugin/scenarios/agent-so11y-scenario/src/main/java/org/apache/skywalking/apm/testcase/h2/controller/CaseController.java b/test/plugin/scenarios/agent-so11y-scenario/src/main/java/org/apache/skywalking/apm/testcase/h2/controller/CaseController.java new file mode 100644 index 0000000000..ee8760cb50 --- /dev/null +++ b/test/plugin/scenarios/agent-so11y-scenario/src/main/java/org/apache/skywalking/apm/testcase/h2/controller/CaseController.java @@ -0,0 +1,76 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.testcase.h2.controller; + +import java.net.URL; +import lombok.extern.log4j.Log4j2; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/case") +@Log4j2 +public class CaseController { + + private static final String SUCCESS = "Success"; + + private static final String CREATE_TABLE_SQL = "CREATE TABLE test_007(\n" + + "id VARCHAR(1) PRIMARY KEY, \n" + + "value VARCHAR(1) NOT NULL)"; + private static final String INSERT_DATA_SQL = "INSERT INTO test_007(id, value) VALUES(?,?)"; + private static final String DROP_TABLE_SQL = "DROP table test_007"; + + @RequestMapping("/healthCheck.html") + @ResponseBody + public String healthCheck() { + // your codes + return SUCCESS; + } + + @RequestMapping("/agent-so11y-scenario") + @ResponseBody + public String testcase() throws Exception { + try (SQLExecutor sqlExecute = new SQLExecutor()) { + sqlExecute.createTable(CREATE_TABLE_SQL); + sqlExecute.insertData(INSERT_DATA_SQL, "1", "1"); + sqlExecute.dropTable(DROP_TABLE_SQL); + + // test bootstrap plugin & ignore context counter + new URL("http://localhost:8080/agent-so11y-scenario/case/ignore.html").getContent(); + new URL("http://localhost:8080/agent-so11y-scenario/case/propagated").getContent(); + } catch (Exception e) { + log.error("Failed to execute sql.", e); + throw e; + } + return SUCCESS; + } + + @RequestMapping("/ignore.html") + @ResponseBody + public String ignorePath() { + return SUCCESS; + } + + @RequestMapping("/propagated") + @ResponseBody + public String propagated() { + return SUCCESS; + } +} diff --git a/test/plugin/scenarios/impala-jdbc-2.6.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/impalajdbc/SQLExecutor.java b/test/plugin/scenarios/agent-so11y-scenario/src/main/java/org/apache/skywalking/apm/testcase/h2/controller/SQLExecutor.java similarity index 53% rename from test/plugin/scenarios/impala-jdbc-2.6.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/impalajdbc/SQLExecutor.java rename to test/plugin/scenarios/agent-so11y-scenario/src/main/java/org/apache/skywalking/apm/testcase/h2/controller/SQLExecutor.java index ce8155ee39..1f6e3b3463 100644 --- a/test/plugin/scenarios/impala-jdbc-2.6.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/impalajdbc/SQLExecutor.java +++ b/test/plugin/scenarios/agent-so11y-scenario/src/main/java/org/apache/skywalking/apm/testcase/h2/controller/SQLExecutor.java @@ -16,48 +16,50 @@ * */ -package org.apache.skywalking.apm.testcase.impalajdbc; +package org.apache.skywalking.apm.testcase.h2.controller; +import java.sql.CallableStatement; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.SQLException; import java.sql.Statement; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; public class SQLExecutor implements AutoCloseable { - private static final Logger LOGGER = LogManager.getLogger(SQLExecutor.class); + private static final String URL = "jdbc:h2:mem:test"; + private static final String USERNAME = "root"; + private static final String PASSWORD = "root"; private Connection connection; - private static final String STATEMENT_CREATE_TABLE_SQL = "CREATE TABLE IF NOT EXISTS default.impala_test (test_id BIGINT, test_name STRING);"; - private static final String IMPALA_DRIVER = "com.cloudera.impala.jdbc.Driver"; public SQLExecutor() throws SQLException { try { - Class.forName(IMPALA_DRIVER); - } catch (ClassNotFoundException ex) { - LOGGER.error(ex); + Class.forName("org.h2.Driver"); + } catch (ClassNotFoundException e) { + // } - connection = DriverManager.getConnection(ImpalaJdbcConfig.getUrl()); - connection.createStatement().execute(STATEMENT_CREATE_TABLE_SQL); + connection = DriverManager.getConnection(URL, USERNAME, PASSWORD); } - public void execute(String sql) throws SQLException { - Statement statement = connection.createStatement(); - statement.execute(sql); + public void createTable(String sql) throws SQLException { + PreparedStatement preparedStatement = connection.prepareStatement(sql); + preparedStatement.execute(); + preparedStatement.close(); } - public void queryData(String sql) throws SQLException { - PreparedStatement preparedStatement = connection.prepareStatement(sql); - preparedStatement.executeQuery(); + public void insertData(String sql, String id, String value) throws SQLException { + CallableStatement preparedStatement = connection.prepareCall(sql); + preparedStatement.setString(1, id); + preparedStatement.setString(2, value); + preparedStatement.execute(); + preparedStatement.close(); } - public void queryData(String sql, int id) throws SQLException { - PreparedStatement preparedStatement = connection.prepareStatement(sql); - preparedStatement.setInt(1, id); - preparedStatement.executeQuery(); + public void dropTable(String sql) throws SQLException { + Statement preparedStatement = connection.createStatement(); + preparedStatement.execute(sql); + preparedStatement.close(); } public void closeConnection() throws SQLException { @@ -70,8 +72,4 @@ public void closeConnection() throws SQLException { public void close() throws Exception { closeConnection(); } - - public Connection getConnection() { - return connection; - } } diff --git a/test/plugin/scenarios/oracle-scenario/src/main/resources/application.yaml b/test/plugin/scenarios/agent-so11y-scenario/src/main/resources/application.yaml similarity index 95% rename from test/plugin/scenarios/oracle-scenario/src/main/resources/application.yaml rename to test/plugin/scenarios/agent-so11y-scenario/src/main/resources/application.yaml index 3bf1f53f66..8e987fed1a 100644 --- a/test/plugin/scenarios/oracle-scenario/src/main/resources/application.yaml +++ b/test/plugin/scenarios/agent-so11y-scenario/src/main/resources/application.yaml @@ -18,6 +18,6 @@ server: port: 8080 servlet: - context-path: /oracle-scenario + context-path: /agent-so11y-scenario logging: config: classpath:log4j2.xml \ No newline at end of file diff --git a/test/plugin/scenarios/impala-jdbc-2.6.x-scenario/src/main/resources/log4j2.xml b/test/plugin/scenarios/agent-so11y-scenario/src/main/resources/log4j2.xml similarity index 100% rename from test/plugin/scenarios/impala-jdbc-2.6.x-scenario/src/main/resources/log4j2.xml rename to test/plugin/scenarios/agent-so11y-scenario/src/main/resources/log4j2.xml diff --git a/test/plugin/scenarios/oracle-scenario/support-version.list b/test/plugin/scenarios/agent-so11y-scenario/support-version.list similarity index 98% rename from test/plugin/scenarios/oracle-scenario/support-version.list rename to test/plugin/scenarios/agent-so11y-scenario/support-version.list index 58b2e2f6dd..293331ad63 100644 --- a/test/plugin/scenarios/oracle-scenario/support-version.list +++ b/test/plugin/scenarios/agent-so11y-scenario/support-version.list @@ -14,4 +14,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -10.2.0.4.0 +1.0.0 diff --git a/test/plugin/scenarios/apm-toolkit-trace-scenario/config/expectedData.yaml b/test/plugin/scenarios/apm-toolkit-trace-scenario/config/expectedData.yaml index ab105a82ab..8c6b47877a 100644 --- a/test/plugin/scenarios/apm-toolkit-trace-scenario/config/expectedData.yaml +++ b/test/plugin/scenarios/apm-toolkit-trace-scenario/config/expectedData.yaml @@ -492,7 +492,7 @@ segmentItems: skipAnalysis: 'true' meterItems: - serviceName: apm-toolkit-trace-scenario - meterSize: 8 + meterSize: ge 8 meters: - meterId: name: test_counter diff --git a/test/plugin/scenarios/impala-jdbc-2.6.x-scenario/bin/startup.sh b/test/plugin/scenarios/c3p0-0.9.0.x-0.9.1.x-scenario/bin/startup.sh old mode 100644 new mode 100755 similarity index 87% rename from test/plugin/scenarios/impala-jdbc-2.6.x-scenario/bin/startup.sh rename to test/plugin/scenarios/c3p0-0.9.0.x-0.9.1.x-scenario/bin/startup.sh index 872fc138ca..ec6c3c19a5 --- a/test/plugin/scenarios/impala-jdbc-2.6.x-scenario/bin/startup.sh +++ b/test/plugin/scenarios/c3p0-0.9.0.x-0.9.1.x-scenario/bin/startup.sh @@ -18,4 +18,4 @@ home="$(cd "$(dirname $0)"; pwd)" -java -jar -Dskywalking.plugin.jdbc.trace_sql_parameters=true ${agent_opts} ${home}/../libs/impala-jdbc-2.6.x-scenario.jar & \ No newline at end of file +java -jar ${agent_opts} ${home}/../libs/c3p0-0.9.0.x-0.9.1.x-scenario.jar & \ No newline at end of file diff --git a/test/plugin/scenarios/c3p0-0.9.0.x-0.9.1.x-scenario/config/expectedData.yaml b/test/plugin/scenarios/c3p0-0.9.0.x-0.9.1.x-scenario/config/expectedData.yaml new file mode 100755 index 0000000000..631a5bb4c3 --- /dev/null +++ b/test/plugin/scenarios/c3p0-0.9.0.x-0.9.1.x-scenario/config/expectedData.yaml @@ -0,0 +1,288 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +segmentItems: +- serviceName: c3p0-0.9.0.x-0.9.1.x-scenario + segmentSize: nq 0 + segments: + - segmentId: not null + spans: + - operationName: C3P0/Connection/getConnection + parentSpanId: 0 + spanId: 1 + startTime: gt 0 + endTime: gt 0 + componentId: 152 + isError: false + spanType: Local + peer: '' + skipAnalysis: false + - operationName: Mysql/JDBC/Statement/execute + parentSpanId: 0 + spanId: 2 + startTime: gt 0 + endTime: gt 0 + componentId: 33 + isError: false + spanType: Exit + peer: mysql-server:3306 + skipAnalysis: false + tags: + - {key: db.type, value: Mysql} + - {key: db.instance, value: test} + - key: db.statement + value: "CREATE TABLE test_C3P0(\nid VARCHAR(1) PRIMARY KEY, \nvalue VARCHAR(1)\ + \ NOT NULL)" + - operationName: C3P0/Connection/close + parentSpanId: 0 + spanId: 3 + startTime: gt 0 + endTime: gt 0 + componentId: 152 + isError: false + spanType: Local + peer: '' + skipAnalysis: false + - operationName: C3P0/Connection/getConnection + parentSpanId: 0 + spanId: 4 + startTime: gt 0 + endTime: gt 0 + componentId: 152 + isError: false + spanType: Local + peer: '' + skipAnalysis: false + - operationName: Mysql/JDBC/Statement/execute + parentSpanId: 0 + spanId: 5 + startTime: gt 0 + endTime: gt 0 + componentId: 33 + isError: false + spanType: Exit + peer: mysql-server:3306 + skipAnalysis: false + tags: + - {key: db.type, value: Mysql} + - {key: db.instance, value: test} + - key: db.statement + value: 'INSERT INTO test_C3P0(id, value) VALUES(1,1)' + - operationName: C3P0/Connection/close + parentSpanId: 0 + spanId: 6 + startTime: gt 0 + endTime: gt 0 + componentId: 152 + isError: false + spanType: Local + peer: '' + skipAnalysis: false + - operationName: C3P0/Connection/getConnection + parentSpanId: 0 + spanId: 7 + startTime: gt 0 + endTime: gt 0 + componentId: 152 + isError: false + spanType: Local + peer: '' + skipAnalysis: false + - operationName: Mysql/JDBC/Statement/execute + parentSpanId: 0 + spanId: 8 + startTime: gt 0 + endTime: gt 0 + componentId: 33 + isError: false + spanType: Exit + peer: mysql-server:3306 + skipAnalysis: false + tags: + - {key: db.type, value: Mysql} + - {key: db.instance, value: test} + - key: db.statement + value: 'SELECT id, value FROM test_C3P0 WHERE id=1' + - operationName: C3P0/Connection/close + parentSpanId: 0 + spanId: 9 + startTime: gt 0 + endTime: gt 0 + componentId: 152 + isError: false + spanType: Local + peer: '' + skipAnalysis: false + - operationName: C3P0/Connection/getConnection + parentSpanId: 0 + spanId: 10 + startTime: gt 0 + endTime: gt 0 + componentId: 152 + isError: false + spanType: Local + peer: '' + skipAnalysis: false + - operationName: Mysql/JDBC/Statement/execute + parentSpanId: 0 + spanId: 11 + startTime: gt 0 + endTime: gt 0 + componentId: 33 + isError: false + spanType: Exit + peer: mysql-server:3306 + skipAnalysis: false + tags: + - {key: db.type, value: Mysql} + - {key: db.instance, value: test} + - key: db.statement + value: "DELETE FROM test_C3P0 WHERE id=1" + - operationName: C3P0/Connection/close + parentSpanId: 0 + spanId: 12 + startTime: gt 0 + endTime: gt 0 + componentId: 152 + isError: false + spanType: Local + peer: '' + skipAnalysis: false + - operationName: C3P0/Connection/getConnection + parentSpanId: 0 + spanId: 13 + startTime: gt 0 + endTime: gt 0 + componentId: 152 + isError: false + spanType: Local + peer: '' + skipAnalysis: false + - operationName: Mysql/JDBC/Statement/execute + parentSpanId: 0 + spanId: 14 + startTime: gt 0 + endTime: gt 0 + componentId: 33 + isError: false + spanType: Exit + peer: mysql-server:3306 + skipAnalysis: false + tags: + - {key: db.type, value: Mysql} + - {key: db.instance, value: test} + - key: db.statement + value: "DROP table test_C3P0" + - operationName: C3P0/Connection/close + parentSpanId: 0 + spanId: 15 + startTime: gt 0 + endTime: gt 0 + componentId: 152 + isError: false + spanType: Local + peer: '' + skipAnalysis: false + - operationName: GET:/c3p0-0.9.0.x-0.9.1.x-scenario/case/c3p0-0.9.0.x-0.9.1.x-scenario + parentSpanId: -1 + spanId: 0 + spanLayer: Http + startTime: gt 0 + endTime: gt 0 + componentId: 1 + isError: false + spanType: Entry + peer: '' + skipAnalysis: false + tags: + - {key: url, value: 'http://localhost:8080/c3p0-0.9.0.x-0.9.1.x-scenario/case/c3p0-0.9.0.x-0.9.1.x-scenario'} + - {key: http.method, value: GET} + - {key: http.status_code, value: '200'} +meterItems: +- serviceName: c3p0-0.9.0.x-0.9.1.x-scenario + meterSize: ge 12 + meters: + - meterId: + name: datasource + tags: + - {name: name, value: test_mysql-server:3306} + - {name: status, value: initialPoolSize} + singleValue: ge 0 + - meterId: + name: datasource + tags: + - {name: name, value: test_mysql-server:3306} + - {name: status, value: maxIdleTime} + singleValue: ge 0 + - meterId: + name: datasource + tags: + - {name: name, value: test_mysql-server:3306} + - {name: status, value: maxPoolSize} + singleValue: ge 0 + - meterId: + name: datasource + tags: + - {name: name, value: test_mysql-server:3306} + - {name: status, value: minPoolSize} + singleValue: ge -1 + - meterId: + name: datasource + tags: + - {name: name, value: test_mysql-server:3306} + - {name: status, value: numBusyConnections} + singleValue: ge 0 + - meterId: + name: datasource + tags: + - {name: name, value: test_mysql-server:3306} + - {name: status, value: numIdleConnections} + singleValue: ge 0 + - meterId: + name: datasource + tags: + - {name: name, value: test_mysql-server:3306} + - {name: status, value: numTotalConnections} + singleValue: ge 0 + - meterId: + name: thread_pool + tags: + - {name: metric_type, value: core_pool_size} + - {name: pool_name, value: tomcat_execute_pool} + singleValue: ge 1 + - meterId: + name: thread_pool + tags: + - {name: metric_type, value: max_pool_size} + - {name: pool_name, value: tomcat_execute_pool} + singleValue: ge 1 + - meterId: + name: thread_pool + tags: + - {name: metric_type, value: pool_size} + - {name: pool_name, value: tomcat_execute_pool} + singleValue: ge 0 + - meterId: + name: thread_pool + tags: + - {name: metric_type, value: active_size} + - {name: pool_name, value: tomcat_execute_pool} + singleValue: ge 0 + - meterId: + name: thread_pool + tags: + - {name: metric_type, value: queue_size} + - {name: pool_name, value: tomcat_execute_pool} + singleValue: ge 0 diff --git a/test/plugin/scenarios/c3p0-0.9.0.x-0.9.1.x-scenario/configuration.yml b/test/plugin/scenarios/c3p0-0.9.0.x-0.9.1.x-scenario/configuration.yml new file mode 100755 index 0000000000..d05855afe4 --- /dev/null +++ b/test/plugin/scenarios/c3p0-0.9.0.x-0.9.1.x-scenario/configuration.yml @@ -0,0 +1,32 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +type: jvm +entryService: http://localhost:8080/c3p0-0.9.0.x-0.9.1.x-scenario/case/c3p0-0.9.0.x-0.9.1.x-scenario +healthCheck: http://localhost:8080/c3p0-0.9.0.x-0.9.1.x-scenario/case/healthCheck +startScript: ./bin/startup.sh +environment: +depends_on: + - mysql-server +dependencies: + mysql-server: + image: mysql:5.7 + hostname: mysql-server + expose: + - "3306" + environment: + - MYSQL_ROOT_PASSWORD=root + - MYSQL_DATABASE=test diff --git a/test/plugin/scenarios/oracle-scenario/pom.xml b/test/plugin/scenarios/c3p0-0.9.0.x-0.9.1.x-scenario/pom.xml old mode 100644 new mode 100755 similarity index 88% rename from test/plugin/scenarios/oracle-scenario/pom.xml rename to test/plugin/scenarios/c3p0-0.9.0.x-0.9.1.x-scenario/pom.xml index ed087a9cca..0a3408ade0 --- a/test/plugin/scenarios/oracle-scenario/pom.xml +++ b/test/plugin/scenarios/c3p0-0.9.0.x-0.9.1.x-scenario/pom.xml @@ -21,7 +21,7 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> org.apache.skywalking.apm.testcase - oracle-scenario + c3p0-0.9.0.x-0.9.1.x-scenario 1.0.0 jar @@ -31,13 +31,12 @@ UTF-8 1.8 3.8.1 - - 10.2.0.4.0 - + 0.9.1.2 + ${test.framework.version} 2.1.6.RELEASE - skywalking-oracle-scenario + skywalking-c3p0-0.9.0.x-0.9.1.x-scenario @@ -52,11 +51,6 @@ - - com.oracle - ojdbc14 - ${test.framework.version} - org.springframework.boot spring-boot-starter-web @@ -71,10 +65,22 @@ org.springframework.boot spring-boot-starter-log4j2 + + + mysql + mysql-connector-java + 5.1.25 + + + + c3p0 + c3p0 + ${test.framework.version} + - oracle-scenario + c3p0-0.9.0.x-0.9.1.x-scenario org.springframework.boot @@ -118,4 +124,4 @@ - + \ No newline at end of file diff --git a/test/plugin/scenarios/c3p0-0.9.0.x-0.9.1.x-scenario/src/main/assembly/assembly.xml b/test/plugin/scenarios/c3p0-0.9.0.x-0.9.1.x-scenario/src/main/assembly/assembly.xml new file mode 100755 index 0000000000..6e2d37867c --- /dev/null +++ b/test/plugin/scenarios/c3p0-0.9.0.x-0.9.1.x-scenario/src/main/assembly/assembly.xml @@ -0,0 +1,41 @@ + + + + + zip + + + + + ./bin + 0775 + + + + + + ${project.build.directory}/c3p0-0.9.0.x-0.9.1.x-scenario.jar + ./libs + 0775 + + + diff --git a/test/plugin/scenarios/impala-jdbc-2.6.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/impalajdbc/Application.java b/test/plugin/scenarios/c3p0-0.9.0.x-0.9.1.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/c3p0/c3p0/Application.java old mode 100644 new mode 100755 similarity index 95% rename from test/plugin/scenarios/impala-jdbc-2.6.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/impalajdbc/Application.java rename to test/plugin/scenarios/c3p0-0.9.0.x-0.9.1.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/c3p0/c3p0/Application.java index 4f85f6924f..2accc46e0f --- a/test/plugin/scenarios/impala-jdbc-2.6.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/impalajdbc/Application.java +++ b/test/plugin/scenarios/c3p0-0.9.0.x-0.9.1.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/c3p0/c3p0/Application.java @@ -16,7 +16,7 @@ * */ -package org.apache.skywalking.apm.testcase.impalajdbc; +package org.apache.skywalking.apm.testcase.c3p0.c3p0; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; diff --git a/test/plugin/scenarios/impala-jdbc-2.6.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/impalajdbc/ImpalaJdbcConfig.java b/test/plugin/scenarios/c3p0-0.9.0.x-0.9.1.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/c3p0/c3p0/MysqlConfig.java old mode 100644 new mode 100755 similarity index 66% rename from test/plugin/scenarios/impala-jdbc-2.6.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/impalajdbc/ImpalaJdbcConfig.java rename to test/plugin/scenarios/c3p0-0.9.0.x-0.9.1.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/c3p0/c3p0/MysqlConfig.java index 68d4d1d603..bdc0102b61 --- a/test/plugin/scenarios/impala-jdbc-2.6.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/impalajdbc/ImpalaJdbcConfig.java +++ b/test/plugin/scenarios/c3p0-0.9.0.x-0.9.1.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/c3p0/c3p0/MysqlConfig.java @@ -16,7 +16,7 @@ * */ -package org.apache.skywalking.apm.testcase.impalajdbc; +package org.apache.skywalking.apm.testcase.c3p0.c3p0; import java.io.IOException; import java.io.InputStream; @@ -24,23 +24,34 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -public class ImpalaJdbcConfig { - private static final Logger LOGGER = LogManager.getLogger(ImpalaJdbcConfig.class); +public class MysqlConfig { + private static Logger LOGGER = LogManager.getLogger(MysqlConfig.class); private static String URL; + private static String USER_NAME; + private static String PASSWORD; static { - InputStream inputStream = ImpalaJdbcConfig.class.getClassLoader().getResourceAsStream("jdbc.properties"); + InputStream inputStream = MysqlConfig.class.getClassLoader().getResourceAsStream("jdbc.properties"); Properties properties = new Properties(); try { properties.load(inputStream); } catch (IOException e) { LOGGER.error("Failed to load config", e); } - - URL = properties.getProperty("impala.url"); + URL = properties.getProperty("mysql.url"); + USER_NAME = properties.getProperty("mysql.username"); + PASSWORD = properties.getProperty("mysql.password"); } public static String getUrl() { return URL; } + + public static String getUserName() { + return USER_NAME; + } + + public static String getPassword() { + return PASSWORD; + } } diff --git a/test/plugin/scenarios/c3p0-0.9.0.x-0.9.1.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/c3p0/c3p0/controller/CaseController.java b/test/plugin/scenarios/c3p0-0.9.0.x-0.9.1.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/c3p0/c3p0/controller/CaseController.java new file mode 100755 index 0000000000..447f843413 --- /dev/null +++ b/test/plugin/scenarios/c3p0-0.9.0.x-0.9.1.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/c3p0/c3p0/controller/CaseController.java @@ -0,0 +1,58 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.testcase.c3p0.c3p0.controller; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.apache.skywalking.apm.testcase.c3p0.c3p0.service.CaseService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/case") +public class CaseController { + + @Autowired + CaseService caseService; + + private static final Logger LOGGER = LogManager.getLogger(CaseController.class); + + private static final String SUCCESS = "Success"; + + @RequestMapping("/c3p0-0.9.0.x-0.9.1.x-scenario") + @ResponseBody + public String testcase() throws Exception { + try { + caseService.testCase(); + } catch (Exception e) { + LOGGER.error("Failed to execute sql.", e); + throw e; + } + return SUCCESS; + } + + @RequestMapping("/healthCheck") + @ResponseBody + public String healthCheck() throws Exception { + return SUCCESS; + } + +} diff --git a/test/plugin/scenarios/c3p0-0.9.0.x-0.9.1.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/c3p0/c3p0/service/CaseService.java b/test/plugin/scenarios/c3p0-0.9.0.x-0.9.1.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/c3p0/c3p0/service/CaseService.java new file mode 100644 index 0000000000..ee2c38ed21 --- /dev/null +++ b/test/plugin/scenarios/c3p0-0.9.0.x-0.9.1.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/c3p0/c3p0/service/CaseService.java @@ -0,0 +1,72 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.testcase.c3p0.c3p0.service; + +import com.mchange.v2.c3p0.ComboPooledDataSource; +import java.sql.Connection; +import java.sql.SQLException; +import java.sql.Statement; +import org.apache.skywalking.apm.testcase.c3p0.c3p0.MysqlConfig; +import org.springframework.stereotype.Service; + +@Service +public class CaseService { + + public static ComboPooledDataSource DS; + private static final String CREATE_TABLE_SQL = "CREATE TABLE test_C3P0(\n" + "id VARCHAR(1) PRIMARY KEY, \n" + "value VARCHAR(1) NOT NULL)"; + private static final String INSERT_DATA_SQL = "INSERT INTO test_C3P0(id, value) VALUES(1,1)"; + private static final String QUERY_DATA_SQL = "SELECT id, value FROM test_C3P0 WHERE id=1"; + private static final String DELETE_DATA_SQL = "DELETE FROM test_C3P0 WHERE id=1"; + private static final String DROP_TABLE_SQL = "DROP table test_C3P0"; + + static { + try { + DS = new ComboPooledDataSource(); + DS.setDriverClass("com.mysql.jdbc.Driver"); + DS.setJdbcUrl(MysqlConfig.getUrl()); + DS.setUser(MysqlConfig.getUserName()); + DS.setPassword(MysqlConfig.getPassword()); + DS.setAcquireIncrement(1); + DS.setInitialPoolSize(5); + DS.setMinPoolSize(1); + DS.setMaxIdleTime(10); + DS.setTestConnectionOnCheckin(false); + DS.setTestConnectionOnCheckout(false); + } catch (Exception e) { + e.printStackTrace(); + } + } + + public void testCase() { + sqlExecutor(DS, CREATE_TABLE_SQL); + sqlExecutor(DS, INSERT_DATA_SQL); + sqlExecutor(DS, QUERY_DATA_SQL); + sqlExecutor(DS, DELETE_DATA_SQL); + sqlExecutor(DS, DROP_TABLE_SQL); + } + + public void sqlExecutor(ComboPooledDataSource dataSource, String sql) { + try (Connection conn = dataSource.getConnection();) { + Statement statement = conn.createStatement(); + statement.execute(sql); + } catch (SQLException e) { + e.printStackTrace(); + } + } +} diff --git a/test/plugin/scenarios/c3p0-0.9.0.x-0.9.1.x-scenario/src/main/resources/application.yaml b/test/plugin/scenarios/c3p0-0.9.0.x-0.9.1.x-scenario/src/main/resources/application.yaml new file mode 100755 index 0000000000..66a7a51fd1 --- /dev/null +++ b/test/plugin/scenarios/c3p0-0.9.0.x-0.9.1.x-scenario/src/main/resources/application.yaml @@ -0,0 +1,23 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# +server: + port: 8080 + servlet: + context-path: /c3p0-0.9.0.x-0.9.1.x-scenario +logging: + config: classpath:log4j2.xml \ No newline at end of file diff --git a/test/plugin/scenarios/c3p0-0.9.0.x-0.9.1.x-scenario/src/main/resources/jdbc.properties b/test/plugin/scenarios/c3p0-0.9.0.x-0.9.1.x-scenario/src/main/resources/jdbc.properties new file mode 100755 index 0000000000..8051777cce --- /dev/null +++ b/test/plugin/scenarios/c3p0-0.9.0.x-0.9.1.x-scenario/src/main/resources/jdbc.properties @@ -0,0 +1,19 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +mysql.url=jdbc:mysql://mysql-server:3306/test?serverTimezone=CST&useLocalSessionState=true +mysql.username=root +mysql.password=root \ No newline at end of file diff --git a/test/plugin/scenarios/oracle-scenario/src/main/resources/log4j2.xml b/test/plugin/scenarios/c3p0-0.9.0.x-0.9.1.x-scenario/src/main/resources/log4j2.xml old mode 100644 new mode 100755 similarity index 100% rename from test/plugin/scenarios/oracle-scenario/src/main/resources/log4j2.xml rename to test/plugin/scenarios/c3p0-0.9.0.x-0.9.1.x-scenario/src/main/resources/log4j2.xml diff --git a/test/plugin/scenarios/impala-jdbc-2.6.x-scenario/src/main/resources/jdbc.properties b/test/plugin/scenarios/c3p0-0.9.0.x-0.9.1.x-scenario/support-version.list old mode 100644 new mode 100755 similarity index 94% rename from test/plugin/scenarios/impala-jdbc-2.6.x-scenario/src/main/resources/jdbc.properties rename to test/plugin/scenarios/c3p0-0.9.0.x-0.9.1.x-scenario/support-version.list index 988356c8f8..730308cbca --- a/test/plugin/scenarios/impala-jdbc-2.6.x-scenario/src/main/resources/jdbc.properties +++ b/test/plugin/scenarios/c3p0-0.9.0.x-0.9.1.x-scenario/support-version.list @@ -14,4 +14,5 @@ # See the License for the specific language governing permissions and # limitations under the License. -impala.url=jdbc:impala://impala-server:21050 +0.9.0.4 +0.9.1.2 \ No newline at end of file diff --git a/test/plugin/scenarios/c3p0-0.9.2.x-0.10.x-scenario/bin/startup.sh b/test/plugin/scenarios/c3p0-0.9.2.x-0.10.x-scenario/bin/startup.sh new file mode 100755 index 0000000000..9ee496ba82 --- /dev/null +++ b/test/plugin/scenarios/c3p0-0.9.2.x-0.10.x-scenario/bin/startup.sh @@ -0,0 +1,21 @@ +#!/bin/bash +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +home="$(cd "$(dirname $0)"; pwd)" + +java -jar ${agent_opts} ${home}/../libs/c3p0-0.9.2.x-0.10.x-scenario.jar & \ No newline at end of file diff --git a/test/plugin/scenarios/c3p0-0.9.2.x-0.10.x-scenario/config/expectedData.yaml b/test/plugin/scenarios/c3p0-0.9.2.x-0.10.x-scenario/config/expectedData.yaml new file mode 100755 index 0000000000..35d6453291 --- /dev/null +++ b/test/plugin/scenarios/c3p0-0.9.2.x-0.10.x-scenario/config/expectedData.yaml @@ -0,0 +1,288 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +segmentItems: +- serviceName: c3p0-0.9.2.x-0.10.x-scenario + segmentSize: nq 0 + segments: + - segmentId: not null + spans: + - operationName: C3P0/Connection/getConnection + parentSpanId: 0 + spanId: 1 + startTime: gt 0 + endTime: gt 0 + componentId: 152 + isError: false + spanType: Local + peer: '' + skipAnalysis: false + - operationName: Mysql/JDBC/Statement/execute + parentSpanId: 0 + spanId: 2 + startTime: gt 0 + endTime: gt 0 + componentId: 33 + isError: false + spanType: Exit + peer: mysql-server:3306 + skipAnalysis: false + tags: + - {key: db.type, value: Mysql} + - {key: db.instance, value: test} + - key: db.statement + value: "CREATE TABLE test_C3P0(\nid VARCHAR(1) PRIMARY KEY, \nvalue VARCHAR(1)\ + \ NOT NULL)" + - operationName: C3P0/Connection/close + parentSpanId: 0 + spanId: 3 + startTime: gt 0 + endTime: gt 0 + componentId: 152 + isError: false + spanType: Local + peer: '' + skipAnalysis: false + - operationName: C3P0/Connection/getConnection + parentSpanId: 0 + spanId: 4 + startTime: gt 0 + endTime: gt 0 + componentId: 152 + isError: false + spanType: Local + peer: '' + skipAnalysis: false + - operationName: Mysql/JDBC/Statement/execute + parentSpanId: 0 + spanId: 5 + startTime: gt 0 + endTime: gt 0 + componentId: 33 + isError: false + spanType: Exit + peer: mysql-server:3306 + skipAnalysis: false + tags: + - {key: db.type, value: Mysql} + - {key: db.instance, value: test} + - key: db.statement + value: 'INSERT INTO test_C3P0(id, value) VALUES(1,1)' + - operationName: C3P0/Connection/close + parentSpanId: 0 + spanId: 6 + startTime: gt 0 + endTime: gt 0 + componentId: 152 + isError: false + spanType: Local + peer: '' + skipAnalysis: false + - operationName: C3P0/Connection/getConnection + parentSpanId: 0 + spanId: 7 + startTime: gt 0 + endTime: gt 0 + componentId: 152 + isError: false + spanType: Local + peer: '' + skipAnalysis: false + - operationName: Mysql/JDBC/Statement/execute + parentSpanId: 0 + spanId: 8 + startTime: gt 0 + endTime: gt 0 + componentId: 33 + isError: false + spanType: Exit + peer: mysql-server:3306 + skipAnalysis: false + tags: + - {key: db.type, value: Mysql} + - {key: db.instance, value: test} + - key: db.statement + value: 'SELECT id, value FROM test_C3P0 WHERE id=1' + - operationName: C3P0/Connection/close + parentSpanId: 0 + spanId: 9 + startTime: gt 0 + endTime: gt 0 + componentId: 152 + isError: false + spanType: Local + peer: '' + skipAnalysis: false + - operationName: C3P0/Connection/getConnection + parentSpanId: 0 + spanId: 10 + startTime: gt 0 + endTime: gt 0 + componentId: 152 + isError: false + spanType: Local + peer: '' + skipAnalysis: false + - operationName: Mysql/JDBC/Statement/execute + parentSpanId: 0 + spanId: 11 + startTime: gt 0 + endTime: gt 0 + componentId: 33 + isError: false + spanType: Exit + peer: mysql-server:3306 + skipAnalysis: false + tags: + - {key: db.type, value: Mysql} + - {key: db.instance, value: test} + - key: db.statement + value: "DELETE FROM test_C3P0 WHERE id=1" + - operationName: C3P0/Connection/close + parentSpanId: 0 + spanId: 12 + startTime: gt 0 + endTime: gt 0 + componentId: 152 + isError: false + spanType: Local + peer: '' + skipAnalysis: false + - operationName: C3P0/Connection/getConnection + parentSpanId: 0 + spanId: 13 + startTime: gt 0 + endTime: gt 0 + componentId: 152 + isError: false + spanType: Local + peer: '' + skipAnalysis: false + - operationName: Mysql/JDBC/Statement/execute + parentSpanId: 0 + spanId: 14 + startTime: gt 0 + endTime: gt 0 + componentId: 33 + isError: false + spanType: Exit + peer: mysql-server:3306 + skipAnalysis: false + tags: + - {key: db.type, value: Mysql} + - {key: db.instance, value: test} + - key: db.statement + value: "DROP table test_C3P0" + - operationName: C3P0/Connection/close + parentSpanId: 0 + spanId: 15 + startTime: gt 0 + endTime: gt 0 + componentId: 152 + isError: false + spanType: Local + peer: '' + skipAnalysis: false + - operationName: GET:/c3p0-0.9.2.x-0.10.x-scenario/case/c3p0-0.9.2.x-0.10.x-scenario + parentSpanId: -1 + spanId: 0 + spanLayer: Http + startTime: gt 0 + endTime: gt 0 + componentId: 1 + isError: false + spanType: Entry + peer: '' + skipAnalysis: false + tags: + - {key: url, value: 'http://localhost:8080/c3p0-0.9.2.x-0.10.x-scenario/case/c3p0-0.9.2.x-0.10.x-scenario'} + - {key: http.method, value: GET} + - {key: http.status_code, value: '200'} +meterItems: +- serviceName: c3p0-0.9.2.x-0.10.x-scenario + meterSize: ge 12 + meters: + - meterId: + name: datasource + tags: + - {name: name, value: test_mysql-server:3306} + - {name: status, value: initialPoolSize} + singleValue: ge 0 + - meterId: + name: datasource + tags: + - {name: name, value: test_mysql-server:3306} + - {name: status, value: maxIdleTime} + singleValue: ge 0 + - meterId: + name: datasource + tags: + - {name: name, value: test_mysql-server:3306} + - {name: status, value: maxPoolSize} + singleValue: ge 0 + - meterId: + name: datasource + tags: + - {name: name, value: test_mysql-server:3306} + - {name: status, value: minPoolSize} + singleValue: ge -1 + - meterId: + name: datasource + tags: + - {name: name, value: test_mysql-server:3306} + - {name: status, value: numBusyConnections} + singleValue: ge 0 + - meterId: + name: datasource + tags: + - {name: name, value: test_mysql-server:3306} + - {name: status, value: numIdleConnections} + singleValue: ge 0 + - meterId: + name: datasource + tags: + - {name: name, value: test_mysql-server:3306} + - {name: status, value: numTotalConnections} + singleValue: ge 0 + - meterId: + name: thread_pool + tags: + - {name: metric_type, value: core_pool_size} + - {name: pool_name, value: tomcat_execute_pool} + singleValue: ge 1 + - meterId: + name: thread_pool + tags: + - {name: metric_type, value: max_pool_size} + - {name: pool_name, value: tomcat_execute_pool} + singleValue: ge 1 + - meterId: + name: thread_pool + tags: + - {name: metric_type, value: pool_size} + - {name: pool_name, value: tomcat_execute_pool} + singleValue: ge 0 + - meterId: + name: thread_pool + tags: + - {name: metric_type, value: active_size} + - {name: pool_name, value: tomcat_execute_pool} + singleValue: ge 0 + - meterId: + name: thread_pool + tags: + - {name: metric_type, value: queue_size} + - {name: pool_name, value: tomcat_execute_pool} + singleValue: ge 0 diff --git a/test/plugin/scenarios/c3p0-0.9.2.x-0.10.x-scenario/configuration.yml b/test/plugin/scenarios/c3p0-0.9.2.x-0.10.x-scenario/configuration.yml new file mode 100755 index 0000000000..1fb61085e6 --- /dev/null +++ b/test/plugin/scenarios/c3p0-0.9.2.x-0.10.x-scenario/configuration.yml @@ -0,0 +1,32 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +type: jvm +entryService: http://localhost:8080/c3p0-0.9.2.x-0.10.x-scenario/case/c3p0-0.9.2.x-0.10.x-scenario +healthCheck: http://localhost:8080/c3p0-0.9.2.x-0.10.x-scenario/case/healthCheck +startScript: ./bin/startup.sh +environment: +depends_on: + - mysql-server +dependencies: + mysql-server: + image: mysql:5.7 + hostname: mysql-server + expose: + - "3306" + environment: + - MYSQL_ROOT_PASSWORD=root + - MYSQL_DATABASE=test diff --git a/test/plugin/scenarios/c3p0-0.9.2.x-0.10.x-scenario/pom.xml b/test/plugin/scenarios/c3p0-0.9.2.x-0.10.x-scenario/pom.xml new file mode 100755 index 0000000000..dc4e7cf830 --- /dev/null +++ b/test/plugin/scenarios/c3p0-0.9.2.x-0.10.x-scenario/pom.xml @@ -0,0 +1,127 @@ + + + + + org.apache.skywalking.apm.testcase + c3p0-0.9.2.x-0.10.x-scenario + 1.0.0 + jar + + 4.0.0 + + + UTF-8 + 1.8 + 3.8.1 + 0.9.5.5 + ${test.framework.version} + 2.1.6.RELEASE + + + skywalking-c3p0-0.9.2.x-0.10.x-scenario + + + + + org.springframework.boot + spring-boot-dependencies + ${spring.boot.version} + pom + import + + + + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-logging + + + + + org.springframework.boot + spring-boot-starter-log4j2 + + + + mysql + mysql-connector-java + 5.1.25 + + + + com.mchange + c3p0 + ${test.framework.version} + + + + + c3p0-0.9.2.x-0.10.x-scenario + + + org.springframework.boot + spring-boot-maven-plugin + ${spring.boot.version} + + + + repackage + + + + + + maven-compiler-plugin + ${maven-compiler-plugin.version} + + ${compiler.version} + ${compiler.version} + ${project.build.sourceEncoding} + + + + org.apache.maven.plugins + maven-assembly-plugin + + + assemble + package + + single + + + + src/main/assembly/assembly.xml + + ./target/ + + + + + + + \ No newline at end of file diff --git a/test/plugin/scenarios/c3p0-0.9.2.x-0.10.x-scenario/src/main/assembly/assembly.xml b/test/plugin/scenarios/c3p0-0.9.2.x-0.10.x-scenario/src/main/assembly/assembly.xml new file mode 100755 index 0000000000..724b0f4a36 --- /dev/null +++ b/test/plugin/scenarios/c3p0-0.9.2.x-0.10.x-scenario/src/main/assembly/assembly.xml @@ -0,0 +1,41 @@ + + + + + zip + + + + + ./bin + 0775 + + + + + + ${project.build.directory}/c3p0-0.9.2.x-0.10.x-scenario.jar + ./libs + 0775 + + + diff --git a/test/plugin/scenarios/c3p0-0.9.2.x-0.10.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/c3p0/mchange/Application.java b/test/plugin/scenarios/c3p0-0.9.2.x-0.10.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/c3p0/mchange/Application.java new file mode 100755 index 0000000000..6f88c48cbc --- /dev/null +++ b/test/plugin/scenarios/c3p0-0.9.2.x-0.10.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/c3p0/mchange/Application.java @@ -0,0 +1,34 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.testcase.c3p0.mchange; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class Application { + + public static void main(String[] args) { + try { + SpringApplication.run(Application.class, args); + } catch (Exception e) { + // Never do this + } + } +} diff --git a/test/plugin/scenarios/c3p0-0.9.2.x-0.10.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/c3p0/mchange/MysqlConfig.java b/test/plugin/scenarios/c3p0-0.9.2.x-0.10.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/c3p0/mchange/MysqlConfig.java new file mode 100755 index 0000000000..8762486f35 --- /dev/null +++ b/test/plugin/scenarios/c3p0-0.9.2.x-0.10.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/c3p0/mchange/MysqlConfig.java @@ -0,0 +1,57 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.testcase.c3p0.mchange; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Properties; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public class MysqlConfig { + private static Logger LOGGER = LogManager.getLogger(MysqlConfig.class); + private static String URL; + private static String USER_NAME; + private static String PASSWORD; + + static { + InputStream inputStream = MysqlConfig.class.getClassLoader().getResourceAsStream("jdbc.properties"); + Properties properties = new Properties(); + try { + properties.load(inputStream); + } catch (IOException e) { + LOGGER.error("Failed to load config", e); + } + URL = properties.getProperty("mysql.url"); + USER_NAME = properties.getProperty("mysql.username"); + PASSWORD = properties.getProperty("mysql.password"); + } + + public static String getUrl() { + return URL; + } + + public static String getUserName() { + return USER_NAME; + } + + public static String getPassword() { + return PASSWORD; + } +} diff --git a/test/plugin/scenarios/c3p0-0.9.2.x-0.10.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/c3p0/mchange/controller/CaseController.java b/test/plugin/scenarios/c3p0-0.9.2.x-0.10.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/c3p0/mchange/controller/CaseController.java new file mode 100755 index 0000000000..c9d8432531 --- /dev/null +++ b/test/plugin/scenarios/c3p0-0.9.2.x-0.10.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/c3p0/mchange/controller/CaseController.java @@ -0,0 +1,58 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.testcase.c3p0.mchange.controller; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.apache.skywalking.apm.testcase.c3p0.mchange.service.CaseService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/case") +public class CaseController { + + @Autowired + CaseService caseService; + + private static final Logger LOGGER = LogManager.getLogger(CaseController.class); + + private static final String SUCCESS = "Success"; + + @RequestMapping("/c3p0-0.9.2.x-0.10.x-scenario") + @ResponseBody + public String testcase() throws Exception { + try { + caseService.testCase(); + } catch (Exception e) { + LOGGER.error("Failed to execute sql.", e); + throw e; + } + return SUCCESS; + } + + @RequestMapping("/healthCheck") + @ResponseBody + public String healthCheck() throws Exception { + return SUCCESS; + } + +} diff --git a/test/plugin/scenarios/c3p0-0.9.2.x-0.10.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/c3p0/mchange/service/CaseService.java b/test/plugin/scenarios/c3p0-0.9.2.x-0.10.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/c3p0/mchange/service/CaseService.java new file mode 100644 index 0000000000..3d493b2a27 --- /dev/null +++ b/test/plugin/scenarios/c3p0-0.9.2.x-0.10.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/c3p0/mchange/service/CaseService.java @@ -0,0 +1,73 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.testcase.c3p0.mchange.service; + +import com.mchange.v2.c3p0.ComboPooledDataSource; +import java.sql.Connection; +import java.sql.SQLException; +import java.sql.Statement; +import org.apache.skywalking.apm.testcase.c3p0.mchange.MysqlConfig; +import org.springframework.stereotype.Service; + +@Service +public class CaseService { + + public static ComboPooledDataSource DS; + private static final String CREATE_TABLE_SQL = "CREATE TABLE test_C3P0(\n" + "id VARCHAR(1) PRIMARY KEY, \n" + "value VARCHAR(1) NOT NULL)"; + private static final String INSERT_DATA_SQL = "INSERT INTO test_C3P0(id, value) VALUES(1,1)"; + private static final String QUERY_DATA_SQL = "SELECT id, value FROM test_C3P0 WHERE id=1"; + private static final String DELETE_DATA_SQL = "DELETE FROM test_C3P0 WHERE id=1"; + private static final String DROP_TABLE_SQL = "DROP table test_C3P0"; + + static { + try { + DS = new ComboPooledDataSource(); + DS.setDriverClass("com.mysql.jdbc.Driver"); + DS.setJdbcUrl(MysqlConfig.getUrl()); + DS.setUser(MysqlConfig.getUserName()); + DS.setPassword(MysqlConfig.getPassword()); + DS.setAcquireIncrement(1); + DS.setInitialPoolSize(5); + DS.setMinPoolSize(1); + DS.setMaxIdleTime(10); + DS.setTestConnectionOnCheckin(false); + DS.setTestConnectionOnCheckout(false); + + } catch (Exception e) { + e.printStackTrace(); + } + } + + public void testCase() { + sqlExecutor(DS, CREATE_TABLE_SQL); + sqlExecutor(DS, INSERT_DATA_SQL); + sqlExecutor(DS, QUERY_DATA_SQL); + sqlExecutor(DS, DELETE_DATA_SQL); + sqlExecutor(DS, DROP_TABLE_SQL); + } + + public void sqlExecutor(ComboPooledDataSource dataSource, String sql) { + try (Connection conn = dataSource.getConnection();) { + Statement statement = conn.createStatement(); + statement.execute(sql); + } catch (SQLException e) { + e.printStackTrace(); + } + } +} diff --git a/test/plugin/scenarios/impala-jdbc-2.6.x-scenario/src/main/resources/application.yaml b/test/plugin/scenarios/c3p0-0.9.2.x-0.10.x-scenario/src/main/resources/application.yaml old mode 100644 new mode 100755 similarity index 94% rename from test/plugin/scenarios/impala-jdbc-2.6.x-scenario/src/main/resources/application.yaml rename to test/plugin/scenarios/c3p0-0.9.2.x-0.10.x-scenario/src/main/resources/application.yaml index 786ec3440e..88dfae1b32 --- a/test/plugin/scenarios/impala-jdbc-2.6.x-scenario/src/main/resources/application.yaml +++ b/test/plugin/scenarios/c3p0-0.9.2.x-0.10.x-scenario/src/main/resources/application.yaml @@ -18,6 +18,6 @@ server: port: 8080 servlet: - context-path: /impala-jdbc-2.6.x-scenario + context-path: /c3p0-0.9.2.x-0.10.x-scenario logging: config: classpath:log4j2.xml \ No newline at end of file diff --git a/test/plugin/scenarios/c3p0-0.9.2.x-0.10.x-scenario/src/main/resources/jdbc.properties b/test/plugin/scenarios/c3p0-0.9.2.x-0.10.x-scenario/src/main/resources/jdbc.properties new file mode 100755 index 0000000000..8051777cce --- /dev/null +++ b/test/plugin/scenarios/c3p0-0.9.2.x-0.10.x-scenario/src/main/resources/jdbc.properties @@ -0,0 +1,19 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +mysql.url=jdbc:mysql://mysql-server:3306/test?serverTimezone=CST&useLocalSessionState=true +mysql.username=root +mysql.password=root \ No newline at end of file diff --git a/test/plugin/scenarios/c3p0-0.9.2.x-0.10.x-scenario/src/main/resources/log4j2.xml b/test/plugin/scenarios/c3p0-0.9.2.x-0.10.x-scenario/src/main/resources/log4j2.xml new file mode 100755 index 0000000000..9849ed5a8a --- /dev/null +++ b/test/plugin/scenarios/c3p0-0.9.2.x-0.10.x-scenario/src/main/resources/log4j2.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/plugin/scenarios/c3p0-0.9.2.x-0.10.x-scenario/support-version.list b/test/plugin/scenarios/c3p0-0.9.2.x-0.10.x-scenario/support-version.list new file mode 100755 index 0000000000..9405ba41cc --- /dev/null +++ b/test/plugin/scenarios/c3p0-0.9.2.x-0.10.x-scenario/support-version.list @@ -0,0 +1,19 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +0.10.0 +0.9.5.5 +0.9.2.1 \ No newline at end of file diff --git a/test/plugin/scenarios/caffeine-3.x-scenario/bin/startup.sh b/test/plugin/scenarios/caffeine-3.x-scenario/bin/startup.sh new file mode 100755 index 0000000000..01883d186d --- /dev/null +++ b/test/plugin/scenarios/caffeine-3.x-scenario/bin/startup.sh @@ -0,0 +1,21 @@ +#!/bin/bash +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +home="$(cd "$(dirname $0)"; pwd)" + +java -jar ${agent_opts} ${home}/../libs/caffeine-3.x-scenario.jar & \ No newline at end of file diff --git a/test/plugin/scenarios/caffeine-3.x-scenario/config/expectedData.yaml b/test/plugin/scenarios/caffeine-3.x-scenario/config/expectedData.yaml new file mode 100755 index 0000000000..133ede4755 --- /dev/null +++ b/test/plugin/scenarios/caffeine-3.x-scenario/config/expectedData.yaml @@ -0,0 +1,163 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +segmentItems: +- serviceName: caffeine-3.x-scenario + segmentSize: nq 0 + segments: + - segmentId: not null + spans: + - operationName: Caffeine/put + parentSpanId: 0 + spanId: 1 + spanLayer: Cache + startTime: nq 0 + endTime: nq 0 + componentId: 160 + isError: false + spanType: Local + peer: '' + tags: + - { key: cache.type, value: Caffeine } + - { key: cache.cmd, value: put } + - { key: cache.op, value: write } + - { key: cache.key, value: "1" } + skipAnalysis: 'false' + - operationName: Caffeine/putAll + parentSpanId: 0 + spanId: 2 + spanLayer: Cache + startTime: nq 0 + endTime: nq 0 + componentId: 160 + isError: false + spanType: Local + peer: '' + tags: + - { key: cache.type, value: Caffeine } + - { key: cache.cmd, value: putAll } + - { key: cache.op, value: write } + - { key: cache.key, value: "2" } + skipAnalysis: 'false' + - operationName: Caffeine/computeIfAbsent + parentSpanId: 0 + spanId: 3 + spanLayer: Cache + startTime: nq 0 + endTime: nq 0 + componentId: 160 + isError: false + spanType: Local + peer: '' + tags: + - { key: cache.type, value: Caffeine } + - { key: cache.cmd, value: computeIfAbsent } + - { key: cache.op, value: read } + - { key: cache.key, value: "1" } + - operationName: Caffeine/getIfPresent + parentSpanId: 0 + spanId: 4 + spanLayer: Cache + startTime: nq 0 + endTime: nq 0 + componentId: 160 + isError: false + spanType: Local + peer: '' + tags: + - { key: cache.type, value: Caffeine } + - { key: cache.cmd, value: getIfPresent } + - { key: cache.op, value: read } + - { key: cache.key, value: "1" } + skipAnalysis: 'false' + - operationName: Caffeine/getAllPresent + parentSpanId: 0 + spanId: 5 + spanLayer: Cache + startTime: nq 0 + endTime: nq 0 + componentId: 160 + isError: false + spanType: Local + peer: '' + skipAnalysis: 'false' + tags: + - { key: cache.type, value: Caffeine } + - { key: cache.cmd, value: getAllPresent } + - { key: cache.op, value: read } + - { key: cache.key, value: '2' } + - operationName: Caffeine/remove + parentSpanId: 0 + spanId: 6 + spanLayer: Cache + startTime: nq 0 + endTime: nq 0 + componentId: 160 + isError: false + spanType: Local + peer: '' + skipAnalysis: 'false' + tags: + - { key: cache.type, value: Caffeine } + - { key: cache.cmd, value: remove } + - { key: cache.op, value: write } + - { key: cache.key, value: '1' } + - operationName: Caffeine/remove + parentSpanId: 0 + spanId: 7 + spanLayer: Cache + startTime: nq 0 + endTime: nq 0 + componentId: 160 + isError: false + spanType: Local + peer: '' + skipAnalysis: 'false' + tags: + - { key: cache.type, value: Caffeine } + - { key: cache.cmd, value: remove } + - { key: cache.op, value: write } + - { key: cache.key, value: '2' } + - operationName: Caffeine/clear + parentSpanId: 0 + spanId: 8 + spanLayer: Cache + startTime: nq 0 + endTime: nq 0 + componentId: 160 + isError: false + spanType: Local + peer: '' + skipAnalysis: 'false' + tags: + - { key: cache.type, value: Caffeine } + - { key: cache.cmd, value: clear } + - { key: cache.op, value: write } + - operationName: GET:/caffeine-3.x-scenario/case/caffeine + parentSpanId: -1 + spanId: 0 + spanLayer: Http + startTime: nq 0 + endTime: nq 0 + componentId: 1 + isError: false + spanType: Entry + peer: '' + tags: + - { key: url, value: 'http://localhost:8080/caffeine-3.x-scenario/case/caffeine' } + - { key: http.method, value: GET } + - { key: http.status_code, value: '200' } + skipAnalysis: 'false' diff --git a/test/plugin/scenarios/caffeine-3.x-scenario/configuration.yml b/test/plugin/scenarios/caffeine-3.x-scenario/configuration.yml new file mode 100755 index 0000000000..00208eefa9 --- /dev/null +++ b/test/plugin/scenarios/caffeine-3.x-scenario/configuration.yml @@ -0,0 +1,22 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +type: jvm +entryService: http://localhost:8080/caffeine-3.x-scenario/case/caffeine +healthCheck: http://localhost:8080/caffeine-3.x-scenario/case/healthCheck +startScript: ./bin/startup.sh +runningMode: with_optional +withPlugins: apm-caffeine-3.x-plugin-*.jar \ No newline at end of file diff --git a/test/plugin/scenarios/caffeine-3.x-scenario/pom.xml b/test/plugin/scenarios/caffeine-3.x-scenario/pom.xml new file mode 100755 index 0000000000..a627109082 --- /dev/null +++ b/test/plugin/scenarios/caffeine-3.x-scenario/pom.xml @@ -0,0 +1,111 @@ + + + + + org.apache.skywalking.apm.testcase + caffeine-3.x-scenario + 1.0.0 + jar + + 4.0.0 + + + 17 + UTF-8 + 3.8.1 + 6.0.2 + ${test.framework.version} + 3.0.0 + 3.1.8 + + + skywalking-caffeine-3.x-scenario + + + + + org.springframework.boot + spring-boot-dependencies + ${spring.boot.version} + pom + import + + + + + + + org.springframework.boot + spring-boot-starter-web + + + com.github.ben-manes.caffeine + caffeine + ${caffeine.version} + + + + + caffeine-3.x-scenario + + + org.springframework.boot + spring-boot-maven-plugin + ${spring.boot.version} + + + + repackage + + + + + + maven-compiler-plugin + ${maven-compiler-plugin.version} + + ${compiler.version} + ${compiler.version} + ${project.build.sourceEncoding} + + + + org.apache.maven.plugins + maven-assembly-plugin + + + assemble + package + + single + + + + src/main/assembly/assembly.xml + + ./target/ + + + + + + + \ No newline at end of file diff --git a/test/plugin/scenarios/impala-jdbc-2.6.x-scenario/src/main/assembly/assembly.xml b/test/plugin/scenarios/caffeine-3.x-scenario/src/main/assembly/assembly.xml old mode 100644 new mode 100755 similarity index 94% rename from test/plugin/scenarios/impala-jdbc-2.6.x-scenario/src/main/assembly/assembly.xml rename to test/plugin/scenarios/caffeine-3.x-scenario/src/main/assembly/assembly.xml index 8580373fc9..382a68db61 --- a/test/plugin/scenarios/impala-jdbc-2.6.x-scenario/src/main/assembly/assembly.xml +++ b/test/plugin/scenarios/caffeine-3.x-scenario/src/main/assembly/assembly.xml @@ -33,7 +33,7 @@ - ${project.build.directory}/impala-jdbc-2.6.x-scenario.jar + ${project.build.directory}/caffeine-3.x-scenario.jar ./libs 0775 diff --git a/test/plugin/scenarios/caffeine-3.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/caffeine/Application.java b/test/plugin/scenarios/caffeine-3.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/caffeine/Application.java new file mode 100644 index 0000000000..a4739cfbfe --- /dev/null +++ b/test/plugin/scenarios/caffeine-3.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/caffeine/Application.java @@ -0,0 +1,30 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.testcase.caffeine; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class Application { + + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } +} diff --git a/test/plugin/scenarios/caffeine-3.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/caffeine/controller/CaffeineController.java b/test/plugin/scenarios/caffeine-3.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/caffeine/controller/CaffeineController.java new file mode 100644 index 0000000000..f8bad1d288 --- /dev/null +++ b/test/plugin/scenarios/caffeine-3.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/caffeine/controller/CaffeineController.java @@ -0,0 +1,56 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.testcase.caffeine.controller; + +import com.github.benmanes.caffeine.cache.Cache; +import com.github.benmanes.caffeine.cache.Caffeine; +import java.util.HashMap; +import java.util.Map; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class CaffeineController { + + Cache caffeine = Caffeine.newBuilder().build(); + + @GetMapping("/case/caffeine") + public void testCase() { + try { + Map data = new HashMap<>(); + data.put("2", "value-2"); + + // put operations + caffeine.put("1", "value-1"); + caffeine.putAll(data); + + // get operations + caffeine.get("1", o -> "default"); + caffeine.getIfPresent("1"); + caffeine.getAllPresent(data.keySet()); + + // delete operations + caffeine.invalidate("1"); + caffeine.invalidateAll(data.keySet()); + caffeine.invalidateAll(); + } catch (Exception e) { + throw e; + } + } +} diff --git a/test/plugin/scenarios/caffeine-3.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/caffeine/controller/HeartbeatController.java b/test/plugin/scenarios/caffeine-3.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/caffeine/controller/HeartbeatController.java new file mode 100644 index 0000000000..9dbc01dc77 --- /dev/null +++ b/test/plugin/scenarios/caffeine-3.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/caffeine/controller/HeartbeatController.java @@ -0,0 +1,31 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.testcase.caffeine.controller; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class HeartbeatController { + + @GetMapping("/case/healthCheck") + public String healthCheck() { + return "success"; + } +} diff --git a/test/plugin/scenarios/caffeine-3.x-scenario/src/main/resources/application.yaml b/test/plugin/scenarios/caffeine-3.x-scenario/src/main/resources/application.yaml new file mode 100755 index 0000000000..ac1fb9a734 --- /dev/null +++ b/test/plugin/scenarios/caffeine-3.x-scenario/src/main/resources/application.yaml @@ -0,0 +1,21 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# +server: + port: 8080 + servlet: + context-path: /caffeine-3.x-scenario \ No newline at end of file diff --git a/test/plugin/scenarios/nats-2.14.x-2.15.x-scenario/support-version.list b/test/plugin/scenarios/caffeine-3.x-scenario/support-version.list old mode 100644 new mode 100755 similarity index 98% rename from test/plugin/scenarios/nats-2.14.x-2.15.x-scenario/support-version.list rename to test/plugin/scenarios/caffeine-3.x-scenario/support-version.list index 9798fc44d8..0c44c26905 --- a/test/plugin/scenarios/nats-2.14.x-2.15.x-scenario/support-version.list +++ b/test/plugin/scenarios/caffeine-3.x-scenario/support-version.list @@ -14,5 +14,5 @@ # See the License for the specific language governing permissions and # limitations under the License. -2.15.6 -2.14.2 \ No newline at end of file +3.0.0 +3.1.8 \ No newline at end of file diff --git a/test/plugin/scenarios/dbcp-2.x-scenario/config/expectedData.yaml b/test/plugin/scenarios/dbcp-2.x-scenario/config/expectedData.yaml index 7e21e55dd6..4e3cb7c03a 100755 --- a/test/plugin/scenarios/dbcp-2.x-scenario/config/expectedData.yaml +++ b/test/plugin/scenarios/dbcp-2.x-scenario/config/expectedData.yaml @@ -224,7 +224,7 @@ segmentItems: - {key: http.status_code, value: '200'} meterItems: - serviceName: dbcp-2.x-scenario - meterSize: 12 + meterSize: ge 12 meters: - meterId: name: datasource diff --git a/test/plugin/scenarios/druid-1.x-scenario/config/expectedData.yaml b/test/plugin/scenarios/druid-1.x-scenario/config/expectedData.yaml index 79b64a8ab5..fb32950056 100644 --- a/test/plugin/scenarios/druid-1.x-scenario/config/expectedData.yaml +++ b/test/plugin/scenarios/druid-1.x-scenario/config/expectedData.yaml @@ -206,7 +206,7 @@ segmentItems: - {key: http.status_code, value: '200'} meterItems: - serviceName: druid-1.x-scenario - meterSize: 14 + meterSize: ge 13 meters: - meterId: name: datasource @@ -220,12 +220,6 @@ meterItems: - {name: name, value: test_mysql-server:3306} - {name: status, value: poolingCount} singleValue: ge 0 - - meterId: - name: datasource - tags: - - {name: name, value: test_mysql-server:3306} - - {name: status, value: idleCount} - singleValue: ge 0 - meterId: name: datasource tags: diff --git a/test/plugin/scenarios/elasticsearch-7.x-scenario/config/expectedData.yaml b/test/plugin/scenarios/elasticsearch-7.x-scenario/config/expectedData.yaml index af47b1b27e..33f33e7573 100644 --- a/test/plugin/scenarios/elasticsearch-7.x-scenario/config/expectedData.yaml +++ b/test/plugin/scenarios/elasticsearch-7.x-scenario/config/expectedData.yaml @@ -15,7 +15,7 @@ # limitations under the License. segmentItems: - serviceName: elasticsearch-7.x-scenario - segmentSize: ge 1 + segmentSize: ge 10 segments: - segmentId: not null spans: @@ -23,303 +23,650 @@ segmentItems: parentSpanId: 0 spanId: 1 spanLayer: Database - startTime: nq 0 - endTime: nq 0 + startTime: not null + endTime: not null componentId: 77 isError: false spanType: Exit peer: not null + skipAnalysis: false tags: - - { key: db.type, value: Elasticsearch } - skipAnalysis: 'false' + - {key: db.type, value: Elasticsearch} + - operationName: HEAD:/elasticsearch-case/case/healthCheck + parentSpanId: -1 + spanId: 0 + spanLayer: Http + startTime: not null + endTime: not null + componentId: 1 + isError: false + spanType: Entry + peer: '' + skipAnalysis: false + tags: + - {key: url, value: 'http://localhost:8080/elasticsearch-case/case/healthCheck'} + - {key: http.method, value: HEAD} + - {key: http.status_code, value: '200'} + - segmentId: not null + spans: + - operationName: Elasticsearch/CreateRequest/onResponse + parentSpanId: -1 + spanId: 0 + spanLayer: Database + startTime: not null + endTime: not null + componentId: 77 + isError: false + spanType: Local + peer: '' + skipAnalysis: false + tags: + - {key: db.type, value: Elasticsearch} + - {key: transmission.latency, value: not null} + refs: + - {parentEndpoint: 'GET:/elasticsearch-case/case/elasticsearch', networkAddress: '', + refType: CrossThread, parentSpanId: 5, parentTraceSegmentId: not null, + parentServiceInstance: not null, parentService: elasticsearch-7.x-scenario, + traceId: not null} + - segmentId: not null + spans: + - operationName: Elasticsearch/IndexRequest/onResponse + parentSpanId: -1 + spanId: 0 + spanLayer: Database + startTime: not null + endTime: not null + componentId: 77 + isError: false + spanType: Local + peer: '' + skipAnalysis: false + tags: + - {key: db.type, value: Elasticsearch} + - {key: transmission.latency, value: not null} + refs: + - {parentEndpoint: 'GET:/elasticsearch-case/case/elasticsearch', networkAddress: '', + refType: CrossThread, parentSpanId: 8, parentTraceSegmentId: not null, + parentServiceInstance: not null, parentService: elasticsearch-7.x-scenario, + traceId: not null} + - segmentId: not null + spans: + - operationName: Elasticsearch/RefreshRequest/onResponse + parentSpanId: -1 + spanId: 0 + spanLayer: Database + startTime: not null + endTime: not null + componentId: 77 + isError: false + spanType: Local + peer: '' + skipAnalysis: false + tags: + - {key: db.type, value: Elasticsearch} + - {key: transmission.latency, value: not null} + refs: + - {parentEndpoint: 'GET:/elasticsearch-case/case/elasticsearch', networkAddress: '', + refType: CrossThread, parentSpanId: 9, parentTraceSegmentId: not null, + parentServiceInstance: not null, parentService: elasticsearch-7.x-scenario, + traceId: not null} + - segmentId: not null + spans: + - operationName: Elasticsearch/GetRequest/onResponse + parentSpanId: -1 + spanId: 0 + spanLayer: Database + startTime: not null + endTime: not null + componentId: 77 + isError: false + spanType: Local + peer: '' + skipAnalysis: false + tags: + - {key: db.type, value: Elasticsearch} + - {key: transmission.latency, value: not null} + refs: + - {parentEndpoint: 'GET:/elasticsearch-case/case/elasticsearch', networkAddress: '', + refType: CrossThread, parentSpanId: 13, parentTraceSegmentId: not null, + parentServiceInstance: not null, parentService: elasticsearch-7.x-scenario, + traceId: not null} + - segmentId: not null + spans: + - operationName: Elasticsearch/SearchRequest/onResponse + parentSpanId: -1 + spanId: 0 + spanLayer: Database + startTime: not null + endTime: not null + componentId: 77 + isError: false + spanType: Local + peer: '' + skipAnalysis: false + tags: + - {key: db.type, value: Elasticsearch} + - {key: transmission.latency, value: not null} + refs: + - {parentEndpoint: 'GET:/elasticsearch-case/case/elasticsearch', networkAddress: '', + refType: CrossThread, parentSpanId: 15, parentTraceSegmentId: not null, + parentServiceInstance: not null, parentService: elasticsearch-7.x-scenario, + traceId: not null} + - segmentId: not null + spans: + - operationName: Elasticsearch/UpdateRequest/onResponse + parentSpanId: -1 + spanId: 0 + spanLayer: Database + startTime: not null + endTime: not null + componentId: 77 + isError: false + spanType: Local + peer: '' + skipAnalysis: false + tags: + - {key: db.type, value: Elasticsearch} + - {key: transmission.latency, value: not null} + refs: + - {parentEndpoint: 'GET:/elasticsearch-case/case/elasticsearch', networkAddress: '', + refType: CrossThread, parentSpanId: 17, parentTraceSegmentId: not null, + parentServiceInstance: not null, parentService: elasticsearch-7.x-scenario, + traceId: not null} + - segmentId: not null + spans: + - operationName: Elasticsearch/AnalyzeRequest/onResponse + parentSpanId: -1 + spanId: 0 + spanLayer: Database + startTime: not null + endTime: not null + componentId: 77 + isError: false + spanType: Local + peer: '' + skipAnalysis: false + tags: + - {key: db.type, value: Elasticsearch} + - {key: transmission.latency, value: not null} + refs: + - {parentEndpoint: 'GET:/elasticsearch-case/case/elasticsearch', networkAddress: '', + refType: CrossThread, parentSpanId: 19, parentTraceSegmentId: not null, + parentServiceInstance: not null, parentService: elasticsearch-7.x-scenario, + traceId: not null} + - segmentId: not null + spans: + - operationName: Elasticsearch/DeleteRequest/onResponse + parentSpanId: -1 + spanId: 0 + spanLayer: Database + startTime: not null + endTime: not null + componentId: 77 + isError: false + spanType: Local + peer: '' + skipAnalysis: false + tags: + - {key: db.type, value: Elasticsearch} + - {key: transmission.latency, value: not null} + refs: + - {parentEndpoint: 'GET:/elasticsearch-case/case/elasticsearch', networkAddress: '', + refType: CrossThread, parentSpanId: 21, parentTraceSegmentId: not null, + parentServiceInstance: not null, parentService: elasticsearch-7.x-scenario, + traceId: not null} + - segmentId: not null + spans: + - operationName: Elasticsearch/Health + parentSpanId: 0 + spanId: 1 + spanLayer: Database + startTime: not null + endTime: not null + componentId: 77 + isError: false + spanType: Exit + peer: not null + skipAnalysis: false + tags: + - {key: db.type, value: Elasticsearch} - operationName: Elasticsearch/GetSettings parentSpanId: 0 spanId: 2 spanLayer: Database - startTime: nq 0 - endTime: nq 0 + startTime: not null + endTime: not null componentId: 77 isError: false spanType: Exit peer: not null + skipAnalysis: false tags: - - { key: db.type, value: Elasticsearch } - skipAnalysis: 'false' + - {key: db.type, value: Elasticsearch} - operationName: Elasticsearch/PutSettings parentSpanId: 0 spanId: 3 spanLayer: Database - startTime: nq 0 - endTime: nq 0 + startTime: not null + endTime: not null componentId: 77 isError: false spanType: Exit peer: not null + skipAnalysis: false tags: - - { key: db.type, value: Elasticsearch } - - { key: db.statement, value: not null } - skipAnalysis: 'false' + - {key: db.type, value: Elasticsearch} + - {key: db.statement, value: not null} - operationName: Elasticsearch/CreateRequest parentSpanId: 0 spanId: 4 spanLayer: Database - startTime: nq 0 - endTime: nq 0 + startTime: not null + endTime: not null componentId: 77 isError: false spanType: Exit peer: not null + skipAnalysis: false tags: - - { key: db.type, value: Elasticsearch } - - { key: db.instance, value: not null } - skipAnalysis: 'false' - - operationName: Elasticsearch/IndexRequest + - {key: db.type, value: Elasticsearch} + - {key: db.instance, value: not null} + - operationName: Elasticsearch/CreateRequest parentSpanId: 0 spanId: 5 spanLayer: Database - startTime: nq 0 - endTime: nq 0 + startTime: not null + endTime: not null componentId: 77 isError: false spanType: Exit peer: not null + skipAnalysis: false tags: - - { key: db.type, value: Elasticsearch } - - { key: db.instance, value: not null } - - { key: db.statement, value: not null } - skipAnalysis: 'false' - - operationName: Elasticsearch/RefreshRequest + - {key: db.type, value: Elasticsearch} + - {key: db.instance, value: not null} + - operationName: Elasticsearch/IndexRequest parentSpanId: 0 spanId: 6 spanLayer: Database - startTime: nq 0 - endTime: nq 0 + startTime: not null + endTime: not null componentId: 77 isError: false spanType: Exit peer: not null + skipAnalysis: false tags: - - { key: db.type, value: Elasticsearch } - - { key: db.instance, value: not null } - skipAnalysis: 'false' - - operationName: Elasticsearch/GetRequest + - {key: db.type, value: Elasticsearch} + - {key: db.instance, value: not null} + - {key: db.statement, value: not null} + - operationName: Elasticsearch/RefreshRequest parentSpanId: 0 spanId: 7 spanLayer: Database - startTime: nq 0 - endTime: nq 0 + startTime: not null + endTime: not null componentId: 77 isError: false spanType: Exit peer: not null + skipAnalysis: false tags: - - { key: db.type, value: Elasticsearch } - - { key: db.instance, value: not null } - - { key: db.statement, value: not null } - skipAnalysis: 'false' - - operationName: Elasticsearch/SearchRequest + - {key: db.type, value: Elasticsearch} + - {key: db.instance, value: not null} + - operationName: Elasticsearch/IndexRequest parentSpanId: 0 spanId: 8 spanLayer: Database - startTime: nq 0 - endTime: nq 0 + startTime: not null + endTime: not null componentId: 77 isError: false spanType: Exit peer: not null + skipAnalysis: false tags: - - { key: db.type, value: Elasticsearch } - - { key: db.instance, value: not null } - - { key: db.statement, value: not null } - skipAnalysis: 'false' - - operationName: Elasticsearch/UpdateRequest + - {key: db.type, value: Elasticsearch} + - {key: db.instance, value: not null} + - {key: db.statement, value: not null} + - operationName: Elasticsearch/RefreshRequest parentSpanId: 0 spanId: 9 spanLayer: Database - startTime: nq 0 - endTime: nq 0 + startTime: not null + endTime: not null componentId: 77 isError: false spanType: Exit peer: not null + skipAnalysis: false tags: - - { key: db.type, value: Elasticsearch } - - { key: db.instance, value: not null } - - { key: db.statement, value: not null } - skipAnalysis: 'false' - - operationName: Elasticsearch/AnalyzeRequest + - {key: db.type, value: Elasticsearch} + - {key: db.instance, value: not null} + - operationName: Elasticsearch/RefreshRequest parentSpanId: 0 spanId: 10 spanLayer: Database - startTime: nq 0 - endTime: nq 0 + startTime: not null + endTime: not null componentId: 77 isError: false spanType: Exit peer: not null + skipAnalysis: false tags: - - { key: db.type, value: Elasticsearch } - - { key: analyzer, value: not null } - - { key: db.statement, value: not null } - skipAnalysis: 'false' - - operationName: Elasticsearch/DeleteRequest + - {key: db.type, value: Elasticsearch} + - {key: db.instance, value: not null} + - operationName: Elasticsearch/RefreshRequest parentSpanId: 0 spanId: 11 spanLayer: Database - startTime: nq 0 - endTime: nq 0 + startTime: not null + endTime: not null componentId: 77 isError: false spanType: Exit peer: not null + skipAnalysis: false tags: - - { key: db.type, value: Elasticsearch } - - { key: db.instance, value: not null } - skipAnalysis: 'false' - - operationName: Elasticsearch/IndexRequest + - {key: db.type, value: Elasticsearch} + - {key: db.instance, value: not null} + - operationName: Elasticsearch/GetRequest parentSpanId: 0 spanId: 12 spanLayer: Database - startTime: nq 0 - endTime: nq 0 + startTime: not null + endTime: not null + componentId: 77 + isError: false + spanType: Exit + peer: not null + skipAnalysis: false + tags: + - {key: db.type, value: Elasticsearch} + - {key: db.instance, value: not null} + - {key: db.statement, value: not null} + - operationName: Elasticsearch/GetRequest + parentSpanId: 0 + spanId: 13 + spanLayer: Database + startTime: not null + endTime: not null + componentId: 77 + isError: false + spanType: Exit + peer: not null + skipAnalysis: false + tags: + - {key: db.type, value: Elasticsearch} + - {key: db.instance, value: not null} + - {key: db.statement, value: not null} + - operationName: Elasticsearch/SearchRequest + parentSpanId: 0 + spanId: 14 + spanLayer: Database + startTime: not null + endTime: not null + componentId: 77 + isError: false + spanType: Exit + peer: not null + skipAnalysis: false + tags: + - {key: db.type, value: Elasticsearch} + - {key: db.instance, value: not null} + - {key: db.statement, value: not null} + - operationName: Elasticsearch/SearchRequest + parentSpanId: 0 + spanId: 15 + spanLayer: Database + startTime: not null + endTime: not null + componentId: 77 + isError: false + spanType: Exit + peer: not null + skipAnalysis: false + tags: + - {key: db.type, value: Elasticsearch} + - {key: db.instance, value: not null} + - {key: db.statement, value: not null} + - operationName: Elasticsearch/UpdateRequest + parentSpanId: 0 + spanId: 16 + spanLayer: Database + startTime: not null + endTime: not null + componentId: 77 + isError: false + spanType: Exit + peer: not null + skipAnalysis: false + tags: + - {key: db.type, value: Elasticsearch} + - {key: db.instance, value: not null} + - {key: db.statement, value: not null} + - operationName: Elasticsearch/UpdateRequest + parentSpanId: 0 + spanId: 17 + spanLayer: Database + startTime: not null + endTime: not null + componentId: 77 + isError: false + spanType: Exit + peer: not null + skipAnalysis: false + tags: + - {key: db.type, value: Elasticsearch} + - {key: db.instance, value: not null} + - {key: db.statement, value: not null} + - operationName: Elasticsearch/AnalyzeRequest + parentSpanId: 0 + spanId: 18 + spanLayer: Database + startTime: not null + endTime: not null + componentId: 77 + isError: false + spanType: Exit + peer: not null + skipAnalysis: false + tags: + - {key: db.type, value: Elasticsearch} + - {key: analyzer, value: not null} + - {key: db.statement, value: not null} + - operationName: Elasticsearch/AnalyzeRequest + parentSpanId: 0 + spanId: 19 + spanLayer: Database + startTime: not null + endTime: not null + componentId: 77 + isError: false + spanType: Exit + peer: not null + skipAnalysis: false + tags: + - {key: db.type, value: Elasticsearch} + - {key: analyzer, value: not null} + - {key: db.statement, value: not null} + - operationName: Elasticsearch/DeleteRequest + parentSpanId: 0 + spanId: 20 + spanLayer: Database + startTime: not null + endTime: not null + componentId: 77 + isError: false + spanType: Exit + peer: not null + skipAnalysis: false + tags: + - {key: db.type, value: Elasticsearch} + - {key: db.instance, value: not null} + - operationName: Elasticsearch/DeleteRequest + parentSpanId: 0 + spanId: 21 + spanLayer: Database + startTime: not null + endTime: not null + componentId: 77 + isError: false + spanType: Exit + peer: not null + skipAnalysis: false + tags: + - {key: db.type, value: Elasticsearch} + - {key: db.instance, value: not null} + - operationName: Elasticsearch/IndexRequest + parentSpanId: 0 + spanId: 22 + spanLayer: Database + startTime: not null + endTime: not null componentId: 48 isError: false spanType: Exit peer: not null + skipAnalysis: false tags: - - { key: db.type, value: Elasticsearch } - - { key: db.instance, value: not null } - - { key: node.address, value: not null } - - { key: es.indices, value: not null } - - { key: es.types, value: not null } - - { key: db.statement, value: not null } - skipAnalysis: 'false' + - {key: db.type, value: Elasticsearch} + - {key: db.instance, value: not null} + - {key: node.address, value: not null} + - {key: es.indices, value: not null} + - {key: es.types, value: not null} + - {key: db.statement, value: not null} - operationName: Elasticsearch/actionGet parentSpanId: 0 - spanId: 13 - startTime: nq 0 - endTime: nq 0 + spanId: 23 + spanLayer: Unknown + startTime: not null + endTime: not null componentId: 48 isError: false spanType: Local + peer: '' + skipAnalysis: false tags: - - { key: db.type, value: Elasticsearch } - - { key: db.statement, value: not null } - skipAnalysis: 'false' + - {key: db.type, value: Elasticsearch} + - {key: db.statement, value: not null} - operationName: Elasticsearch/GetRequest parentSpanId: 0 - spanId: 14 + spanId: 24 spanLayer: Database - startTime: nq 0 - endTime: nq 0 + startTime: not null + endTime: not null componentId: 48 isError: false spanType: Exit peer: not null + skipAnalysis: false tags: - - { key: db.type, value: Elasticsearch } - - { key: db.instance, value: not null } - - { key: node.address, value: not null } - - { key: es.indices, value: not null } - - { key: es.types, value: not null } - - { key: db.statement, value: not null } - skipAnalysis: 'false' + - {key: db.type, value: Elasticsearch} + - {key: db.instance, value: not null} + - {key: node.address, value: not null} + - {key: es.indices, value: not null} + - {key: es.types, value: not null} + - {key: db.statement, value: not null} - operationName: Elasticsearch/SearchRequest parentSpanId: 0 - spanId: 15 + spanId: 25 spanLayer: Database - startTime: nq 0 - endTime: nq 0 + startTime: not null + endTime: not null componentId: 48 isError: false spanType: Exit peer: not null + skipAnalysis: false tags: - - { key: db.type, value: Elasticsearch } - - { key: db.instance, value: not null } - - { key: node.address, value: not null } - - { key: es.indices, value: not null } - - { key: es.types, value: not null } - - { key: db.statement, value: not null } - skipAnalysis: 'false' + - {key: db.type, value: Elasticsearch} + - {key: db.instance, value: not null} + - {key: node.address, value: not null} + - {key: es.indices, value: not null} + - {key: es.types, value: not null} + - {key: db.statement, value: not null} - operationName: Elasticsearch/actionGet parentSpanId: 0 - spanId: 16 - startTime: nq 0 - endTime: nq 0 + spanId: 26 + spanLayer: Unknown + startTime: not null + endTime: not null componentId: 48 isError: false spanType: Local + peer: '' + skipAnalysis: false tags: - {key: db.type, value: Elasticsearch} - - {key: es.took_millis, value: not null } - - {key: es.total_hits, value: not null } - - {key: db.statement, value: not null } - skipAnalysis: 'false' + - {key: es.took_millis, value: not null} + - {key: es.total_hits, value: not null} + - {key: db.statement, value: not null} - operationName: Elasticsearch/UpdateRequest parentSpanId: 0 - spanId: 17 + spanId: 27 spanLayer: Database - startTime: nq 0 - endTime: nq 0 + startTime: not null + endTime: not null componentId: 48 isError: false spanType: Exit peer: not null + skipAnalysis: false tags: - - { key: db.type, value: Elasticsearch } - - { key: db.instance, value: not null } - - { key: node.address, value: not null } - - { key: es.indices, value: not null } - - { key: es.types, value: not null } - - { key: db.statement, value: not null } - skipAnalysis: 'false' + - {key: db.type, value: Elasticsearch} + - {key: db.instance, value: not null} + - {key: node.address, value: not null} + - {key: es.indices, value: not null} + - {key: es.types, value: not null} + - {key: db.statement, value: not null} - operationName: Elasticsearch/DeleteRequest parentSpanId: 0 - spanId: 18 + spanId: 28 spanLayer: Database - startTime: nq 0 - endTime: nq 0 + startTime: not null + endTime: not null componentId: 48 isError: false spanType: Exit peer: not null + skipAnalysis: false tags: - - { key: db.type, value: Elasticsearch } - - { key: db.instance, value: not null } - - { key: node.address, value: not null } - - { key: es.indices, value: not null } - - { key: es.types, value: not null } - - { key: db.statement, value: not null } - skipAnalysis: 'false' + - {key: db.type, value: Elasticsearch} + - {key: db.instance, value: not null} + - {key: node.address, value: not null} + - {key: es.indices, value: not null} + - {key: es.types, value: not null} + - {key: db.statement, value: not null} - operationName: Elasticsearch/DeleteIndexRequest parentSpanId: 0 - spanId: 19 + spanId: 29 spanLayer: Database - startTime: nq 0 - endTime: nq 0 + startTime: not null + endTime: not null componentId: 48 isError: false spanType: Exit peer: not null + skipAnalysis: false tags: - - { key: db.type, value: Elasticsearch } - - { key: db.instance, value: not null } - - { key: node.address, value: not null } - - { key: es.indices, value: not null } - skipAnalysis: 'false' + - {key: db.type, value: Elasticsearch} + - {key: db.instance, value: not null} + - {key: node.address, value: not null} + - {key: es.indices, value: not null} - operationName: GET:/elasticsearch-case/case/elasticsearch parentSpanId: -1 spanId: 0 spanLayer: Http - startTime: nq 0 - endTime: nq 0 + startTime: not null + endTime: not null componentId: 1 isError: false spanType: Entry peer: '' + skipAnalysis: false tags: - - { key: url, value: 'http://localhost:8080/elasticsearch-case/case/elasticsearch' } - - { key: http.method, value: GET } + - {key: url, value: 'http://localhost:8080/elasticsearch-case/case/elasticsearch'} + - {key: http.method, value: GET} - {key: http.status_code, value: '200'} - skipAnalysis: 'false' diff --git a/test/plugin/scenarios/elasticsearch-7.x-scenario/configuration.yml b/test/plugin/scenarios/elasticsearch-7.x-scenario/configuration.yml index 853e67498e..aa43501041 100644 --- a/test/plugin/scenarios/elasticsearch-7.x-scenario/configuration.yml +++ b/test/plugin/scenarios/elasticsearch-7.x-scenario/configuration.yml @@ -22,7 +22,7 @@ environment: - elasticsearch.server=elasticsearch-server-7.x:9200 dependencies: elasticsearch-server-7.x: - image: elasticsearch:${CASE_SERVER_IMAGE_VERSION} + image: docker.elastic.co/elasticsearch/elasticsearch:${CASE_SERVER_IMAGE_VERSION} hostname: elasticsearch-server-7.x removeOnExit: true expose: @@ -30,6 +30,5 @@ dependencies: environment: - cluster.name=docker-node - xpack.security.enabled=false - - bootstrap.memory_lock=true - - "ES_JAVA_OPTS=-Xms256m -Xmx256m" + - "ES_JAVA_OPTS=-Xms512m -Xmx512m" - discovery.type=single-node diff --git a/test/plugin/scenarios/elasticsearch-7.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/elasticsearch/RestHighLevelClientCase.java b/test/plugin/scenarios/elasticsearch-7.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/elasticsearch/RestHighLevelClientCase.java index ce44956346..a0314fae32 100644 --- a/test/plugin/scenarios/elasticsearch-7.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/elasticsearch/RestHighLevelClientCase.java +++ b/test/plugin/scenarios/elasticsearch-7.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/elasticsearch/RestHighLevelClientCase.java @@ -22,9 +22,11 @@ import java.util.HashMap; import java.util.Map; import java.util.UUID; +import java.util.concurrent.CountDownLatch; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.apache.skywalking.apm.testcase.elasticsearch.controller.CaseController; +import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.admin.cluster.health.ClusterHealthRequest; import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse; import org.elasticsearch.action.admin.cluster.settings.ClusterGetSettingsRequest; @@ -33,6 +35,7 @@ import org.elasticsearch.action.admin.cluster.settings.ClusterUpdateSettingsResponse; import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest; import org.elasticsearch.action.admin.indices.refresh.RefreshRequest; +import org.elasticsearch.action.admin.indices.refresh.RefreshResponse; import org.elasticsearch.action.get.GetRequest; import org.elasticsearch.action.get.GetResponse; import org.elasticsearch.action.index.IndexRequest; @@ -85,6 +88,7 @@ public String healthCheck() throws Exception { public String elasticsearch() throws Exception { String indexName = UUID.randomUUID().toString(); + String indexName2 = UUID.randomUUID().toString(); try { // health health(); @@ -97,27 +101,35 @@ public String elasticsearch() throws Exception { // create createIndex(indexName); + createIndexAsync(indexName2); // index index(indexName); + indexAsync(indexName2); // refresh client.indices().refresh(new RefreshRequest(indexName), RequestOptions.DEFAULT); + client.indices().refresh(new RefreshRequest(indexName2), RequestOptions.DEFAULT); // get get(indexName); + getAsync(indexName2); // search search(indexName); + searchAsync(indexName2); // update update(indexName); + updateAsync(indexName2); // analyze analyze(indexName); + analyzeAsync(indexName2); // delete delete(indexName); + deleteAsync(indexName2); } finally { if (null != client) { client.close(); @@ -189,6 +201,41 @@ private void createIndex(String indexName) throws IOException { } } + private void createIndexAsync(String indexName) throws InterruptedException { + CreateIndexRequest request = new CreateIndexRequest(indexName); + Map mapping = new HashMap<>(); + Map mappingProperties = new HashMap<>(); + Map mappingPropertiesAuthor = new HashMap<>(); + mappingPropertiesAuthor.put("type", "keyword"); + mappingProperties.put("author", mappingPropertiesAuthor); + Map mappingPropertiesTitle = new HashMap<>(); + mappingPropertiesTitle.put("type", "keyword"); + mappingProperties.put("title", mappingPropertiesTitle); + mapping.put("properties", mappingProperties); + request.mapping(mapping); + + request.settings(Settings.builder().put("index.number_of_shards", 1).put("index.number_of_replicas", 0)); + + final CountDownLatch latch = new CountDownLatch(1); + client.indices().createAsync(request, RequestOptions.DEFAULT, new ActionListener() { + @Override + public void onResponse(final CreateIndexResponse createIndexResponse) { + latch.countDown(); + if (!createIndexResponse.isAcknowledged()) { + String message = "elasticsearch create index fail."; + LOGGER.error(message); + throw new RuntimeException(message); + } + } + + @Override + public void onFailure(final Exception e) { + latch.countDown(); + } + }); + latch.await(); + } + private void index(String indexName) throws IOException { Map source = new HashMap<>(); source.put("author", "Marker"); @@ -201,6 +248,49 @@ private void index(String indexName) throws IOException { LOGGER.error(message); throw new RuntimeException(message); } + client.indices().refresh(new RefreshRequest(indexName), RequestOptions.DEFAULT); + } + + private void indexAsync(String indexName) throws InterruptedException { + Map source = new HashMap<>(); + source.put("author", "Marker"); + source.put("title", "Java programing."); + IndexRequest indexRequest = new IndexRequest(indexName).id("1").source(source); + + final CountDownLatch latch = new CountDownLatch(2); + client.indexAsync(indexRequest, RequestOptions.DEFAULT, + new ActionListener() { + @Override + public void onResponse(final IndexResponse indexResponse) { + latch.countDown(); + if (indexResponse.status().getStatus() >= 400) { + String message = "elasticsearch index data fail."; + LOGGER.error(message); + throw new RuntimeException(message); + } + } + + @Override + public void onFailure(final Exception e) { + latch.countDown(); + } + } + ); + + client.indices().refreshAsync(new RefreshRequest(indexName), RequestOptions.DEFAULT, + new ActionListener() { + @Override + public void onResponse(final RefreshResponse refreshResponse) { + latch.countDown(); + } + + @Override + public void onFailure(final Exception e) { + latch.countDown(); + } + } + ); + latch.await(); } private void get(String indexName) throws IOException { @@ -214,6 +304,31 @@ private void get(String indexName) throws IOException { } } + private void getAsync(String indexName) throws InterruptedException { + GetRequest getRequest = new GetRequest(indexName, "1"); + final CountDownLatch latch = new CountDownLatch(1); + client.getAsync(getRequest, RequestOptions.DEFAULT, + new ActionListener() { + @Override + public void onResponse(final GetResponse getResponse) { + latch.countDown(); + if (!getResponse.isExists()) { + String message = "elasticsearch get data fail."; + LOGGER.error(message); + throw new RuntimeException(message); + } + } + + @Override + public void onFailure(final Exception e) { + latch.countDown(); + throw new RuntimeException(e); + } + } + ); + latch.await(); + } + private void update(String indexName) throws IOException { UpdateRequest request = new UpdateRequest(indexName, "1"); Map parameters = singletonMap("title", "c++ programing."); @@ -228,6 +343,33 @@ private void update(String indexName) throws IOException { } } + private void updateAsync(String indexName) throws InterruptedException { + UpdateRequest request = new UpdateRequest(indexName, "1"); + Map parameters = singletonMap("title", "c++ programing."); + Script inline = new Script(ScriptType.INLINE, "painless", "ctx._source.title = params.title", parameters); + request.script(inline); + + final CountDownLatch latch = new CountDownLatch(1); + client.updateAsync(request, RequestOptions.DEFAULT, new ActionListener() { + @Override + public void onResponse(final UpdateResponse updateResponse) { + latch.countDown(); + if (updateResponse.getVersion() != 2) { + String message = "elasticsearch update data fail."; + LOGGER.error(message); + throw new RuntimeException(message); + } + } + + @Override + public void onFailure(final Exception e) { + latch.countDown(); + throw new RuntimeException(e); + } + }); + latch.await(); + } + private void analyze(String indexName) throws IOException { AnalyzeRequest analyzeRequest = AnalyzeRequest.withIndexAnalyzer(indexName, null, "SkyWalking"); AnalyzeResponse analyzeResponse = client.indices().analyze(analyzeRequest, RequestOptions.DEFAULT); @@ -238,6 +380,30 @@ private void analyze(String indexName) throws IOException { } } + private void analyzeAsync(String indexName) throws InterruptedException { + AnalyzeRequest analyzeRequest = AnalyzeRequest.withIndexAnalyzer(indexName, null, "SkyWalking"); + final CountDownLatch latch = new CountDownLatch(1); + client.indices().analyzeAsync( + analyzeRequest, RequestOptions.DEFAULT, new ActionListener() { + @Override + public void onResponse(final AnalyzeResponse analyzeResponse) { + latch.countDown(); + if (null == analyzeResponse.getTokens() || analyzeResponse.getTokens().size() < 1) { + String message = "elasticsearch analyze index fail."; + LOGGER.error(message); + throw new RuntimeException(message); + } + } + + @Override + public void onFailure(final Exception e) { + latch.countDown(); + LOGGER.error(e); + } + }); + latch.await(); + } + private void delete(String indexName) throws IOException { DeleteIndexRequest request = new DeleteIndexRequest(indexName); AcknowledgedResponse deleteIndexResponse = client.indices().delete(request, RequestOptions.DEFAULT); @@ -248,6 +414,29 @@ private void delete(String indexName) throws IOException { } } + private void deleteAsync(String indexName) throws InterruptedException { + DeleteIndexRequest request = new DeleteIndexRequest(indexName); + final CountDownLatch latch = new CountDownLatch(1); + client.indices().deleteAsync(request, RequestOptions.DEFAULT, new ActionListener() { + @Override + public void onResponse(final AcknowledgedResponse acknowledgedResponse) { + latch.countDown(); + if (!acknowledgedResponse.isAcknowledged()) { + String message = "elasticsearch delete index fail."; + LOGGER.error(message); + throw new RuntimeException(message); + } + } + + @Override + public void onFailure(final Exception e) { + latch.countDown(); + throw new RuntimeException(e); + } + }); + latch.await(); + } + private void search(String indexName) throws IOException { SearchSourceBuilder sourceBuilder = new SearchSourceBuilder(); sourceBuilder.query(QueryBuilders.termQuery("author", "Marker")); @@ -266,4 +455,35 @@ private void search(String indexName) throws IOException { throw new RuntimeException(message); } } + + private void searchAsync(String indexName) throws InterruptedException { + SearchSourceBuilder sourceBuilder = new SearchSourceBuilder(); + sourceBuilder.query(QueryBuilders.termQuery("author", "Marker")); + sourceBuilder.from(0); + sourceBuilder.size(10); + + SearchRequest searchRequest = new SearchRequest(); + searchRequest.indices(indexName); + searchRequest.source(sourceBuilder); + final CountDownLatch latch = new CountDownLatch(1); + client.searchAsync(searchRequest, RequestOptions.DEFAULT, new ActionListener() { + @Override + public void onResponse(final SearchResponse searchResponse) { + latch.countDown(); + int length = searchResponse.getHits().getHits().length; + if (!(length > 0)) { + String message = "elasticsearch search data fail."; + LOGGER.error(message); + throw new RuntimeException(message); + } + } + + @Override + public void onFailure(final Exception e) { + latch.countDown(); + LOGGER.error(e); + } + }); + latch.await(); + } } diff --git a/test/plugin/scenarios/elasticsearch-7.x-scenario/support-version.list b/test/plugin/scenarios/elasticsearch-7.x-scenario/support-version.list index 54e1c1de5b..e5b98548c6 100644 --- a/test/plugin/scenarios/elasticsearch-7.x-scenario/support-version.list +++ b/test/plugin/scenarios/elasticsearch-7.x-scenario/support-version.list @@ -14,6 +14,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +# 7.0.0 - 7.17.12 have been tested, and 7.0.0 - 7.2.1 not included in the test 7.3.2 7.4.2 7.5.2 @@ -26,5 +27,4 @@ 7.12.1 7.13.4 7.14.2 -7.16.3 -7.17.12 \ No newline at end of file +7.17.21 \ No newline at end of file diff --git a/test/plugin/scenarios/elasticsearch-rest-high-level-6.x-scenario/configuration.yml b/test/plugin/scenarios/elasticsearch-rest-high-level-6.x-scenario/configuration.yml index bab1c5a875..72c856d395 100644 --- a/test/plugin/scenarios/elasticsearch-rest-high-level-6.x-scenario/configuration.yml +++ b/test/plugin/scenarios/elasticsearch-rest-high-level-6.x-scenario/configuration.yml @@ -20,7 +20,7 @@ environment: - elasticsearch.server=elasticsearch-server-6.x:9200 dependencies: elasticsearch-server-6.x: - image: elasticsearch:${CASE_SERVER_IMAGE_VERSION} + image: docker.elastic.co/elasticsearch/elasticsearch:${CASE_SERVER_IMAGE_VERSION} hostname: elasticsearch-server-6.x removeOnExit: true expose: diff --git a/test/plugin/scenarios/elasticsearch-transport-6.x-scenario/configuration.yml b/test/plugin/scenarios/elasticsearch-transport-6.x-scenario/configuration.yml index cafc4a0571..a268b52e4c 100644 --- a/test/plugin/scenarios/elasticsearch-transport-6.x-scenario/configuration.yml +++ b/test/plugin/scenarios/elasticsearch-transport-6.x-scenario/configuration.yml @@ -20,7 +20,7 @@ environment: - elasticsearch.server=elasticsearch-server-6.x:9200 dependencies: elasticsearch-server-6.x: - image: bitnami/elasticsearch:${CASE_SERVER_IMAGE_VERSION} + image: bitnamilegacy/elasticsearch:${CASE_SERVER_IMAGE_VERSION} hostname: elasticsearch-server-6.x removeOnExit: true expose: diff --git a/test/plugin/scenarios/gateway-2.0.x-scenario/config/expectedData.yaml b/test/plugin/scenarios/gateway-2.0.x-scenario/config/expectedData.yaml index 8373df690b..d20a2486bc 100644 --- a/test/plugin/scenarios/gateway-2.0.x-scenario/config/expectedData.yaml +++ b/test/plugin/scenarios/gateway-2.0.x-scenario/config/expectedData.yaml @@ -34,8 +34,8 @@ segmentItems: - {key: http.method, value: GET} - {key: http.status_code, value: '200'} refs: - - {parentEndpoint: SpringCloudGateway/RoutingFilter, networkAddress: 'localhost:18070', - refType: CrossProcess, parentSpanId: 1, parentTraceSegmentId: not null, parentServiceInstance: not + - {parentEndpoint: SpringCloudGateway/GatewayFilter, networkAddress: 'localhost:18070', + refType: CrossProcess, parentSpanId: 2, parentTraceSegmentId: not null, parentServiceInstance: not null, parentService: not null, traceId: not null} skipAnalysis: 'false' - serviceName: gateway-projectA-scenario @@ -61,8 +61,8 @@ segmentItems: - segmentId: not null spans: - operationName: SpringCloudGateway/sendRequest - parentSpanId: 0 - spanId: 1 + parentSpanId: 1 + spanId: 2 spanLayer: Http startTime: nq 0 endTime: nq 0 @@ -75,8 +75,8 @@ segmentItems: - {key: http.status_code, value: '200'} skipAnalysis: 'false' - operationName: SpringCloudGateway/RoutingFilter - parentSpanId: -1 - spanId: 0 + parentSpanId: 0 + spanId: 1 startTime: nq 0 endTime: nq 0 componentId: 61 @@ -88,3 +88,17 @@ segmentItems: parentSpanId: 0, parentTraceSegmentId: not null, parentServiceInstance: not null, parentService: not null, traceId: not null} skipAnalysis: 'false' + - operationName: SpringCloudGateway/GatewayFilter + parentSpanId: -1 + spanId: 0 + startTime: nq 0 + endTime: nq 0 + componentId: 61 + isError: false + spanType: Local + peer: '' + refs: + - {parentEndpoint: '/provider/b/testcase', networkAddress: '', refType: CrossThread, + parentSpanId: 0, parentTraceSegmentId: not null, parentServiceInstance: not + null, parentService: not null, traceId: not null} + skipAnalysis: 'false' diff --git a/test/plugin/scenarios/gateway-2.0.x-scenario/gateway-projectA-scenario/pom.xml b/test/plugin/scenarios/gateway-2.0.x-scenario/gateway-projectA-scenario/pom.xml index fc1fb5e6d6..446f5e9f28 100644 --- a/test/plugin/scenarios/gateway-2.0.x-scenario/gateway-projectA-scenario/pom.xml +++ b/test/plugin/scenarios/gateway-2.0.x-scenario/gateway-projectA-scenario/pom.xml @@ -34,6 +34,11 @@ spring-cloud-starter-gateway ${test.framework.version} + + io.projectreactor + reactor-core + 3.1.7.RELEASE + diff --git a/test/plugin/scenarios/gateway-2.0.x-scenario/support-version.list b/test/plugin/scenarios/gateway-2.0.x-scenario/support-version.list index 8e6ebcf1ab..bb0a110c46 100644 --- a/test/plugin/scenarios/gateway-2.0.x-scenario/support-version.list +++ b/test/plugin/scenarios/gateway-2.0.x-scenario/support-version.list @@ -14,4 +14,5 @@ # See the License for the specific language governing permissions and # limitations under the License. -2.0.4.RELEASE \ No newline at end of file +2.0.4.RELEASE +2.0.0.RELEASE \ No newline at end of file diff --git a/test/plugin/scenarios/gateway-2.1.x-scenario/config/expectedData.yaml b/test/plugin/scenarios/gateway-2.1.x-scenario/config/expectedData.yaml index c312b744ab..7eecbf2365 100644 --- a/test/plugin/scenarios/gateway-2.1.x-scenario/config/expectedData.yaml +++ b/test/plugin/scenarios/gateway-2.1.x-scenario/config/expectedData.yaml @@ -54,8 +54,8 @@ segmentItems: - {key: http.method, value: GET} - {key: http.status_code, value: '200'} refs: - - {parentEndpoint: SpringCloudGateway/RoutingFilter, networkAddress: 'localhost:18070', - refType: CrossProcess, parentSpanId: 1, parentTraceSegmentId: not null, parentServiceInstance: not + - {parentEndpoint: SpringCloudGateway/GatewayFilter, networkAddress: 'localhost:18070', + refType: CrossProcess, parentSpanId: 2, parentTraceSegmentId: not null, parentServiceInstance: not null, parentService: not null, traceId: not null} skipAnalysis: 'false' - serviceName: gateway-projectA-scenario @@ -76,8 +76,8 @@ segmentItems: - segmentId: not null spans: - operationName: SpringCloudGateway/sendRequest - parentSpanId: 0 - spanId: 1 + parentSpanId: 1 + spanId: 2 spanLayer: Http startTime: nq 0 endTime: nq 0 @@ -95,8 +95,8 @@ segmentItems: - { key: message, value: not null } - { key: stack, value: not null } - operationName: SpringCloudGateway/RoutingFilter - parentSpanId: -1 - spanId: 0 + parentSpanId: 0 + spanId: 1 startTime: nq 0 endTime: nq 0 componentId: 61 @@ -106,11 +106,23 @@ segmentItems: parentSpanId: 0, parentTraceSegmentId: not null, parentServiceInstance: not null, parentService: not null, traceId: not null } skipAnalysis: 'false' + - operationName: SpringCloudGateway/GatewayFilter + parentSpanId: -1 + spanId: 0 + startTime: nq 0 + endTime: nq 0 + componentId: 61 + spanType: Local + refs: + - { parentEndpoint: '/provider/timeout/error', networkAddress: '', refType: CrossThread, + parentSpanId: 0, parentTraceSegmentId: not null, parentServiceInstance: not + null, parentService: not null, traceId: not null } + skipAnalysis: 'false' - segmentId: not null spans: - operationName: SpringCloudGateway/sendRequest - parentSpanId: 0 - spanId: 1 + parentSpanId: 1 + spanId: 2 spanLayer: Http startTime: nq 0 endTime: nq 0 @@ -123,8 +135,8 @@ segmentItems: - {key: http.status_code, value: '200'} skipAnalysis: 'false' - operationName: SpringCloudGateway/RoutingFilter - parentSpanId: -1 - spanId: 0 + parentSpanId: 0 + spanId: 1 startTime: nq 0 endTime: nq 0 componentId: 61 @@ -136,6 +148,20 @@ segmentItems: parentSpanId: 0, parentTraceSegmentId: not null, parentServiceInstance: not null, parentService: not null, traceId: not null} skipAnalysis: 'false' + - operationName: SpringCloudGateway/GatewayFilter + parentSpanId: -1 + spanId: 0 + startTime: nq 0 + endTime: nq 0 + componentId: 61 + isError: false + spanType: Local + peer: '' + refs: + - {parentEndpoint: '/provider/b/testcase', networkAddress: '', refType: CrossThread, + parentSpanId: 0, parentTraceSegmentId: not null, parentServiceInstance: not + null, parentService: not null, traceId: not null} + skipAnalysis: 'false' - segmentId: not null spans: - operationName: /provider/b/testcase diff --git a/test/plugin/scenarios/gateway-3.x-filter-context-scenario/config/expectedData.yaml b/test/plugin/scenarios/gateway-3.x-filter-context-scenario/config/expectedData.yaml index 61852cba38..6a052d28fd 100644 --- a/test/plugin/scenarios/gateway-3.x-filter-context-scenario/config/expectedData.yaml +++ b/test/plugin/scenarios/gateway-3.x-filter-context-scenario/config/expectedData.yaml @@ -43,8 +43,8 @@ segmentItems: - {key: http.method, value: GET} - {key: http.status_code, value: '200'} refs: - - {parentEndpoint: SpringCloudGateway/RoutingFilter, networkAddress: 'localhost:18070', - refType: CrossProcess, parentSpanId: 1, parentTraceSegmentId: not null, parentServiceInstance: not + - {parentEndpoint: SpringCloudGateway/GatewayFilter, networkAddress: 'localhost:18070', + refType: CrossProcess, parentSpanId: 2, parentTraceSegmentId: not null, parentServiceInstance: not null, parentService: not null, traceId: not null} skipAnalysis: 'false' - serviceName: gateway-projectA-scenario diff --git a/test/plugin/scenarios/gateway-3.x-scenario/config/expectedData.yaml b/test/plugin/scenarios/gateway-3.x-scenario/config/expectedData.yaml index 70df54e93f..7a883f6261 100644 --- a/test/plugin/scenarios/gateway-3.x-scenario/config/expectedData.yaml +++ b/test/plugin/scenarios/gateway-3.x-scenario/config/expectedData.yaml @@ -54,8 +54,8 @@ segmentItems: - {key: http.method, value: GET} - {key: http.status_code, value: '200'} refs: - - {parentEndpoint: SpringCloudGateway/RoutingFilter, networkAddress: 'localhost:18070', - refType: CrossProcess, parentSpanId: 1, parentTraceSegmentId: not null, parentServiceInstance: not + - {parentEndpoint: SpringCloudGateway/GatewayFilter, networkAddress: 'localhost:18070', + refType: CrossProcess, parentSpanId: 2, parentTraceSegmentId: not null, parentServiceInstance: not null, parentService: not null, traceId: not null} skipAnalysis: 'false' - serviceName: gateway-projectA-scenario @@ -76,8 +76,8 @@ segmentItems: - segmentId: not null spans: - operationName: SpringCloudGateway/sendRequest - parentSpanId: 0 - spanId: 1 + parentSpanId: 1 + spanId: 2 spanLayer: Http startTime: nq 0 endTime: nq 0 @@ -95,8 +95,8 @@ segmentItems: - { key: message, value: not null } - { key: stack, value: not null} - operationName: SpringCloudGateway/RoutingFilter - parentSpanId: -1 - spanId: 0 + parentSpanId: 0 + spanId: 1 startTime: nq 0 endTime: nq 0 componentId: 61 @@ -106,11 +106,23 @@ segmentItems: parentSpanId: 0, parentTraceSegmentId: not null, parentServiceInstance: not null, parentService: not null, traceId: not null } skipAnalysis: 'false' + - operationName: SpringCloudGateway/GatewayFilter + parentSpanId: -1 + spanId: 0 + startTime: nq 0 + endTime: nq 0 + componentId: 61 + spanType: Local + refs: + - { parentEndpoint: '/provider/timeout/error', networkAddress: '', refType: CrossThread, + parentSpanId: 0, parentTraceSegmentId: not null, parentServiceInstance: not + null, parentService: not null, traceId: not null } + skipAnalysis: 'false' - segmentId: not null spans: - operationName: SpringCloudGateway/sendRequest - parentSpanId: 0 - spanId: 1 + parentSpanId: 1 + spanId: 2 spanLayer: Http startTime: nq 0 endTime: nq 0 @@ -123,8 +135,8 @@ segmentItems: - {key: http.status_code, value: '200'} skipAnalysis: 'false' - operationName: SpringCloudGateway/RoutingFilter - parentSpanId: -1 - spanId: 0 + parentSpanId: 0 + spanId: 1 startTime: nq 0 endTime: nq 0 componentId: 61 @@ -136,6 +148,20 @@ segmentItems: parentSpanId: 0, parentTraceSegmentId: not null, parentServiceInstance: not null, parentService: not null, traceId: not null} skipAnalysis: 'false' + - operationName: SpringCloudGateway/GatewayFilter + parentSpanId: -1 + spanId: 0 + startTime: nq 0 + endTime: nq 0 + componentId: 61 + isError: false + spanType: Local + peer: '' + refs: + - {parentEndpoint: '/provider/b/testcase', networkAddress: '', refType: CrossThread, + parentSpanId: 0, parentTraceSegmentId: not null, parentServiceInstance: not + null, parentService: not null, traceId: not null} + skipAnalysis: 'false' - segmentId: not null spans: - operationName: /provider/b/testcase diff --git a/test/plugin/scenarios/gateway-4.1.2.x-scenario/config/expectedData.yaml b/test/plugin/scenarios/gateway-4.1.2.x-scenario/config/expectedData.yaml new file mode 100644 index 0000000000..fe66b460d6 --- /dev/null +++ b/test/plugin/scenarios/gateway-4.1.2.x-scenario/config/expectedData.yaml @@ -0,0 +1,155 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +segmentItems: +- serviceName: gateway-projectB-scenario + segmentSize: nq 0 + segments: + - segmentId: not null + spans: + - operationName: /provider/timeout/error + parentSpanId: 0 + spanId: 1 + isError: true + spanType: Exit + tags: + - { key: url, value: not null } + - { key: http.method, value: GET } + - { key: http.status_code, value: '500' } + logs: + - logEvent: + - { key: event, value: error } + - { key: error.kind, value: not null } + - { key: message, value: not null } + - {key: stack, value: not null} + - logEvent: + - { key: event, value: error } + - { key: error.kind, value: not null } + - { key: message, value: not null } + - { key: stack, value: not null } + - operationName: GET:/provider/b/testcase + parentSpanId: -1 + spanId: 0 + spanLayer: Http + startTime: nq 0 + endTime: nq 0 + componentId: 14 + isError: false + spanType: Entry + peer: '' + tags: + - {key: url, value: not null} + - {key: http.method, value: GET} + - {key: http.status_code, value: '200'} + refs: + - {parentEndpoint: SpringCloudGateway/send, networkAddress: 'localhost:18070', + refType: CrossProcess, parentSpanId: 1, parentTraceSegmentId: not null, parentServiceInstance: not + null, parentService: not null, traceId: not null} + skipAnalysis: 'false' +- serviceName: gateway-projectA-scenario + segmentSize: nq 0 + segments: + - segmentId: not null + spans: + - operationName: /provider/timeout/error + parentSpanId: -1 + spanId: 0 + spanLayer: Http + isError: true + spanType: Entry + tags: + - {key: url, value: 'http://localhost:8080/provider/timeout/error' } + - {key: http.method, value: GET} + - {key: http.status_code, value: '500'} + - segmentId: not null + spans: + - operationName: SpringCloudGateway/sendRequest + parentSpanId: 0 + spanId: 1 + spanLayer: Http + startTime: nq 0 + endTime: nq 0 + componentId: 61 + isError: true + spanType: Exit + peer: 1.2.3.4:18070 + skipAnalysis: false + tags: + - { key: url, value: not null } + logs: + - logEvent: + - { key: event, value: error } + - { key: error.kind, value: not null } + - { key: message, value: not null } + - { key: stack, value: not null} + - operationName: SpringCloudGateway/send + parentSpanId: -1 + spanId: 0 + startTime: nq 0 + endTime: nq 0 + componentId: 61 + spanType: Local + refs: + - { parentEndpoint: 'SpringCloudGateway/GatewayFilter', networkAddress: '', refType: CrossThread, + parentSpanId: 1, parentTraceSegmentId: not null, parentServiceInstance: not + null, parentService: not null, traceId: not null } + skipAnalysis: 'false' + - segmentId: not null + spans: + - operationName: SpringCloudGateway/sendRequest + parentSpanId: 0 + spanId: 1 + spanLayer: Http + startTime: nq 0 + endTime: nq 0 + componentId: 61 + isError: false + spanType: Exit + peer: 'localhost:18070' + tags: + - {key: url, value: not null} + - {key: http.status_code, value: '200'} + skipAnalysis: 'false' + - operationName: SpringCloudGateway/send + parentSpanId: -1 + spanId: 0 + startTime: nq 0 + endTime: nq 0 + componentId: 61 + isError: false + spanType: Local + peer: '' + refs: + - {parentEndpoint: 'SpringCloudGateway/GatewayFilter', networkAddress: '', refType: CrossThread, + parentSpanId: 1, parentTraceSegmentId: not null, parentServiceInstance: not + null, parentService: not null, traceId: not null} + skipAnalysis: 'false' + - segmentId: not null + spans: + - operationName: /provider/b/testcase + parentSpanId: -1 + spanId: 0 + spanLayer: Http + startTime: nq 0 + endTime: nq 0 + componentId: 67 + isError: false + spanType: Entry + peer: not null + tags: + - { key: url, value: 'http://localhost:8080/provider/b/testcase' } + - { key: http.method, value: GET } + - { key: http.status_code, value: '200' } + skipAnalysis: 'false' diff --git a/test/plugin/scenarios/gateway-4.1.2.x-scenario/configuration.yml b/test/plugin/scenarios/gateway-4.1.2.x-scenario/configuration.yml new file mode 100644 index 0000000000..a958b8d680 --- /dev/null +++ b/test/plugin/scenarios/gateway-4.1.2.x-scenario/configuration.yml @@ -0,0 +1,22 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +type: jvm +entryService: http://localhost:8080/provider/b/testcase +healthCheck: http://localhost:8080/provider/b/healthCheck +startScript: ./bin/startup.sh +runningMode: with_optional +withPlugins: apm-spring-cloud-gateway-4.x-plugin-*.jar diff --git a/test/plugin/scenarios/gateway-4.1.2.x-scenario/gateway-dist/bin/startup.sh b/test/plugin/scenarios/gateway-4.1.2.x-scenario/gateway-dist/bin/startup.sh new file mode 100644 index 0000000000..0d28675632 --- /dev/null +++ b/test/plugin/scenarios/gateway-4.1.2.x-scenario/gateway-dist/bin/startup.sh @@ -0,0 +1,24 @@ +#!/bin/bash +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +home="$(cd "$(dirname $0)"; pwd)" + +java -jar ${agent_opts} "-Dskywalking.agent.service_name=gateway-projectA-scenario" ${home}/../libs/gateway-projectA-scenario.jar & +sleep 1 + +java -jar ${agent_opts} "-Dskywalking.agent.service_name=gateway-projectB-scenario" ${home}/../libs/gateway-projectB-scenario.jar & diff --git a/test/plugin/scenarios/gateway-4.1.2.x-scenario/gateway-dist/pom.xml b/test/plugin/scenarios/gateway-4.1.2.x-scenario/gateway-dist/pom.xml new file mode 100644 index 0000000000..8e7d699cd7 --- /dev/null +++ b/test/plugin/scenarios/gateway-4.1.2.x-scenario/gateway-dist/pom.xml @@ -0,0 +1,54 @@ + + + + + org.apache.skywalking + gateway-4.1.2.x-scenario + 5.0.0 + + 4.0.0 + + gateway-dist + + + + + org.apache.maven.plugins + maven-assembly-plugin + + + assemble + package + + single + + + + src/main/assembly/assembly.xml + + ../target/ + + + + + + + diff --git a/test/plugin/scenarios/gateway-4.1.2.x-scenario/gateway-dist/src/main/assembly/assembly.xml b/test/plugin/scenarios/gateway-4.1.2.x-scenario/gateway-dist/src/main/assembly/assembly.xml new file mode 100644 index 0000000000..00d04e28e0 --- /dev/null +++ b/test/plugin/scenarios/gateway-4.1.2.x-scenario/gateway-dist/src/main/assembly/assembly.xml @@ -0,0 +1,46 @@ + + + + + zip + + + + + ./bin + 0775 + + + + + + ../gateway-projectA-scenario/target/gateway-projectA-scenario.jar + ./libs + 0775 + + + ../gateway-projectB-scenario/target/gateway-projectB-scenario.jar + ./libs + 0775 + + + \ No newline at end of file diff --git a/test/plugin/scenarios/gateway-4.1.2.x-scenario/gateway-projectA-scenario/pom.xml b/test/plugin/scenarios/gateway-4.1.2.x-scenario/gateway-projectA-scenario/pom.xml new file mode 100644 index 0000000000..ad7b0f6f9b --- /dev/null +++ b/test/plugin/scenarios/gateway-4.1.2.x-scenario/gateway-projectA-scenario/pom.xml @@ -0,0 +1,56 @@ + + + + + org.apache.skywalking + gateway-4.1.2.x-scenario + 5.0.0 + + 4.0.0 + + gateway-projectA-scenario + + + + org.springframework.cloud + spring-cloud-starter-gateway + ${test.framework.version} + + + + + gateway-projectA-scenario + + + org.springframework.boot + spring-boot-maven-plugin + 3.0.0 + + + + repackage + + + + + + + diff --git a/test/plugin/scenarios/gateway-4.1.2.x-scenario/gateway-projectA-scenario/src/main/java/test/apache/skywalking/apm/testcase/sc/gateway/projectA/ApiKeyResolver.java b/test/plugin/scenarios/gateway-4.1.2.x-scenario/gateway-projectA-scenario/src/main/java/test/apache/skywalking/apm/testcase/sc/gateway/projectA/ApiKeyResolver.java new file mode 100644 index 0000000000..40517d1d3f --- /dev/null +++ b/test/plugin/scenarios/gateway-4.1.2.x-scenario/gateway-projectA-scenario/src/main/java/test/apache/skywalking/apm/testcase/sc/gateway/projectA/ApiKeyResolver.java @@ -0,0 +1,31 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package test.apache.skywalking.apm.testcase.sc.gateway.projectA; + +import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver; +import org.springframework.stereotype.Component; +import org.springframework.web.server.ServerWebExchange; +import reactor.core.publisher.Mono; + +@Component +public class ApiKeyResolver implements KeyResolver { + + public Mono resolve(ServerWebExchange exchange) { + return Mono.just(exchange.getRequest().getPath().value()); + } +} diff --git a/test/plugin/scenarios/gateway-4.1.2.x-scenario/gateway-projectA-scenario/src/main/java/test/apache/skywalking/apm/testcase/sc/gateway/projectA/Application.java b/test/plugin/scenarios/gateway-4.1.2.x-scenario/gateway-projectA-scenario/src/main/java/test/apache/skywalking/apm/testcase/sc/gateway/projectA/Application.java new file mode 100644 index 0000000000..68eb95b0c6 --- /dev/null +++ b/test/plugin/scenarios/gateway-4.1.2.x-scenario/gateway-projectA-scenario/src/main/java/test/apache/skywalking/apm/testcase/sc/gateway/projectA/Application.java @@ -0,0 +1,29 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package test.apache.skywalking.apm.testcase.sc.gateway.projectA; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class Application { + + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } +} diff --git a/test/plugin/scenarios/gateway-4.1.2.x-scenario/gateway-projectA-scenario/src/main/java/test/apache/skywalking/apm/testcase/sc/gateway/projectA/Test1Filter.java b/test/plugin/scenarios/gateway-4.1.2.x-scenario/gateway-projectA-scenario/src/main/java/test/apache/skywalking/apm/testcase/sc/gateway/projectA/Test1Filter.java new file mode 100644 index 0000000000..af891eae20 --- /dev/null +++ b/test/plugin/scenarios/gateway-4.1.2.x-scenario/gateway-projectA-scenario/src/main/java/test/apache/skywalking/apm/testcase/sc/gateway/projectA/Test1Filter.java @@ -0,0 +1,38 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package test.apache.skywalking.apm.testcase.sc.gateway.projectA; + +import org.springframework.cloud.gateway.filter.GatewayFilterChain; +import org.springframework.cloud.gateway.filter.GlobalFilter; +import org.springframework.core.Ordered; +import org.springframework.http.server.reactive.ServerHttpRequest; +import org.springframework.web.server.ServerWebExchange; +import reactor.core.publisher.Mono; + +public class Test1Filter implements GlobalFilter, Ordered { + @Override + public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { + ServerHttpRequest buildRequest = exchange.getRequest().mutate().build(); + return chain.filter(exchange.mutate().request(buildRequest).build()); + } + + @Override + public int getOrder() { + return 0; + } +} diff --git a/test/plugin/scenarios/gateway-4.1.2.x-scenario/gateway-projectA-scenario/src/main/java/test/apache/skywalking/apm/testcase/sc/gateway/projectA/Test2Filter.java b/test/plugin/scenarios/gateway-4.1.2.x-scenario/gateway-projectA-scenario/src/main/java/test/apache/skywalking/apm/testcase/sc/gateway/projectA/Test2Filter.java new file mode 100644 index 0000000000..d3a5846bd5 --- /dev/null +++ b/test/plugin/scenarios/gateway-4.1.2.x-scenario/gateway-projectA-scenario/src/main/java/test/apache/skywalking/apm/testcase/sc/gateway/projectA/Test2Filter.java @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package test.apache.skywalking.apm.testcase.sc.gateway.projectA; + +import org.springframework.cloud.gateway.filter.GatewayFilterChain; +import org.springframework.cloud.gateway.filter.GlobalFilter; +import org.springframework.core.Ordered; +import org.springframework.http.server.reactive.ServerHttpRequest; +import org.springframework.web.server.ServerWebExchange; +import reactor.core.publisher.Mono; + +public class Test2Filter implements GlobalFilter, Ordered { + + @Override + public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { + ServerHttpRequest buildRequest = exchange.getRequest().mutate().build(); + return chain.filter(exchange.mutate().request(buildRequest).build()); + } + + @Override + public int getOrder() { + return 1; + } +} diff --git a/test/plugin/scenarios/gateway-4.1.2.x-scenario/gateway-projectA-scenario/src/main/java/test/apache/skywalking/apm/testcase/sc/gateway/projectA/TestFilterConfig.java b/test/plugin/scenarios/gateway-4.1.2.x-scenario/gateway-projectA-scenario/src/main/java/test/apache/skywalking/apm/testcase/sc/gateway/projectA/TestFilterConfig.java new file mode 100644 index 0000000000..29f9627bdf --- /dev/null +++ b/test/plugin/scenarios/gateway-4.1.2.x-scenario/gateway-projectA-scenario/src/main/java/test/apache/skywalking/apm/testcase/sc/gateway/projectA/TestFilterConfig.java @@ -0,0 +1,35 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package test.apache.skywalking.apm.testcase.sc.gateway.projectA; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class TestFilterConfig { + + @Bean + public Test1Filter test1Filter() { + return new Test1Filter(); + } + + @Bean + public Test2Filter test2Filter() { + return new Test2Filter(); + } +} diff --git a/test/plugin/scenarios/gateway-4.1.2.x-scenario/gateway-projectA-scenario/src/main/resources/application.yml b/test/plugin/scenarios/gateway-4.1.2.x-scenario/gateway-projectA-scenario/src/main/resources/application.yml new file mode 100644 index 0000000000..7639e7ce8a --- /dev/null +++ b/test/plugin/scenarios/gateway-4.1.2.x-scenario/gateway-projectA-scenario/src/main/resources/application.yml @@ -0,0 +1,33 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +server: + port: 8080 +spring: + cloud: + gateway: + httpclient: + connect-timeout: 2000 + routes: + - id: provider_route + uri: http://localhost:18070 + predicates: + - Path=/provider/b/* + - id: provider_timeout + uri: http://1.2.3.4:18070 + predicates: + - Path=/provider/timeout/* diff --git a/test/plugin/scenarios/gateway-4.1.2.x-scenario/gateway-projectB-scenario/pom.xml b/test/plugin/scenarios/gateway-4.1.2.x-scenario/gateway-projectB-scenario/pom.xml new file mode 100644 index 0000000000..6669207a94 --- /dev/null +++ b/test/plugin/scenarios/gateway-4.1.2.x-scenario/gateway-projectB-scenario/pom.xml @@ -0,0 +1,57 @@ + + + + + gateway-4.1.2.x-scenario + org.apache.skywalking + 5.0.0 + + 4.0.0 + + gateway-projectB-scenario + + + + org.springframework.boot + spring-boot-starter-web + 2.1.0.RELEASE + + + + + gateway-projectB-scenario + + + org.springframework.boot + spring-boot-maven-plugin + 1.5.9.RELEASE + + + + repackage + + + + + + + + \ No newline at end of file diff --git a/test/plugin/scenarios/gateway-4.1.2.x-scenario/gateway-projectB-scenario/src/main/java/test/apache/skywalking/apm/testcase/sc/gateway/projectB/Application.java b/test/plugin/scenarios/gateway-4.1.2.x-scenario/gateway-projectB-scenario/src/main/java/test/apache/skywalking/apm/testcase/sc/gateway/projectB/Application.java new file mode 100644 index 0000000000..51c94655c7 --- /dev/null +++ b/test/plugin/scenarios/gateway-4.1.2.x-scenario/gateway-projectB-scenario/src/main/java/test/apache/skywalking/apm/testcase/sc/gateway/projectB/Application.java @@ -0,0 +1,31 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package test.apache.skywalking.apm.testcase.sc.gateway.projectB; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.ComponentScan; + +@SpringBootApplication +@ComponentScan(value = {"test.apache.skywalking.apm.testcase.sc.gateway.projectB.controller"}) +public class Application { + + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } +} diff --git a/test/plugin/scenarios/gateway-4.1.2.x-scenario/gateway-projectB-scenario/src/main/java/test/apache/skywalking/apm/testcase/sc/gateway/projectB/controller/TestController.java b/test/plugin/scenarios/gateway-4.1.2.x-scenario/gateway-projectB-scenario/src/main/java/test/apache/skywalking/apm/testcase/sc/gateway/projectB/controller/TestController.java new file mode 100644 index 0000000000..923d7fd7ff --- /dev/null +++ b/test/plugin/scenarios/gateway-4.1.2.x-scenario/gateway-projectB-scenario/src/main/java/test/apache/skywalking/apm/testcase/sc/gateway/projectB/controller/TestController.java @@ -0,0 +1,40 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package test.apache.skywalking.apm.testcase.sc.gateway.projectB.controller; + +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.client.RestTemplate; + +@RestController +public class TestController { + + @RequestMapping("/provider/b/testcase") + public String testcase() { + try { + new RestTemplate().getForEntity("http://localhost:8080/provider/timeout/error", String.class); + } catch (Exception e) { + } + return "1"; + } + + @RequestMapping("/provider/b/healthCheck") + public String healthCheck() { + return "Success"; + } +} diff --git a/test/plugin/scenarios/gateway-4.1.2.x-scenario/gateway-projectB-scenario/src/main/resources/application.properties b/test/plugin/scenarios/gateway-4.1.2.x-scenario/gateway-projectB-scenario/src/main/resources/application.properties new file mode 100644 index 0000000000..cac2c4d5a1 --- /dev/null +++ b/test/plugin/scenarios/gateway-4.1.2.x-scenario/gateway-projectB-scenario/src/main/resources/application.properties @@ -0,0 +1,17 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +server.port=18070 \ No newline at end of file diff --git a/test/plugin/scenarios/gateway-4.1.2.x-scenario/pom.xml b/test/plugin/scenarios/gateway-4.1.2.x-scenario/pom.xml new file mode 100644 index 0000000000..41b9e64c15 --- /dev/null +++ b/test/plugin/scenarios/gateway-4.1.2.x-scenario/pom.xml @@ -0,0 +1,59 @@ + + + + 4.0.0 + + org.apache.skywalking + gateway-4.1.2.x-scenario + pom + 5.0.0 + + gateway-projectA-scenario + gateway-projectB-scenario + gateway-dist + + + skywalking-gateway-4.1.2.x-scenario + + + UTF-8 + 1.8 + 3.8.1 + 4.1.2 + ${test.framework.version} + + + + gateway-4.1.2.x-scenario + + + maven-compiler-plugin + ${maven-compiler-plugin.version} + + ${compiler.version} + ${compiler.version} + ${project.build.sourceEncoding} + + + + + + diff --git a/test/plugin/scenarios/gateway-4.1.2.x-scenario/support-version.list b/test/plugin/scenarios/gateway-4.1.2.x-scenario/support-version.list new file mode 100644 index 0000000000..347dda15d0 --- /dev/null +++ b/test/plugin/scenarios/gateway-4.1.2.x-scenario/support-version.list @@ -0,0 +1,19 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +4.1.2 +4.1.6 +4.2.0 \ No newline at end of file diff --git a/test/plugin/scenarios/gateway-4.3.x-scenario/config/expectedData.yaml b/test/plugin/scenarios/gateway-4.3.x-scenario/config/expectedData.yaml new file mode 100644 index 0000000000..94488a65aa --- /dev/null +++ b/test/plugin/scenarios/gateway-4.3.x-scenario/config/expectedData.yaml @@ -0,0 +1,189 @@ + # Licensed to the Apache Software Foundation (ASF) under one + # or more contributor license agreements. See the NOTICE file + # distributed with this work for additional information + # regarding copyright ownership. The ASF licenses this file + # to you under the Apache License, Version 2.0 (the + # "License"); you may not use this file except in compliance + # with the License. You may obtain a copy of the License at + # + # http://www.apache.org/licenses/LICENSE-2.0 + # + # Unless required by applicable law or agreed to in writing, software + # distributed under the License is distributed on an "AS IS" BASIS, + # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + # See the License for the specific language governing permissions and + # limitations under the License. + + ignoreSegmentNames: + - HEAD:/provider/b/healthCheck + - /provider/b/healthCheck + + segmentItems: + - serviceName: gateway-projectB-scenario + segmentSize: nq 0 + segments: + - segmentId: not null + spans: + - operationName: GET:/provider/b/testcase + parentSpanId: -1 + spanId: 0 + spanLayer: Http + startTime: nq 0 + endTime: nq 0 + componentId: nq 0 + isError: false + spanType: Entry + peer: '' + tags: + - {key: url, value: not null} + - {key: http.method, value: GET} + - {key: http.status_code, value: '200'} + refs: + - {parentEndpoint: SpringCloudGateway/GatewayFilter, networkAddress: not null, + refType: CrossProcess, parentSpanId: 2, parentTraceSegmentId: not null, parentServiceInstance: not + null, parentService: not null, traceId: not null} + skipAnalysis: 'false' + + - serviceName: gateway-projectA-scenario + segmentSize: nq 0 + segments: + # -------------------------------------------------------- + # Case 1: Timeout Error + # -------------------------------------------------------- + # A. Entry Span + - segmentId: not null + spans: + - operationName: /provider/timeout/error + parentSpanId: -1 + spanId: 0 + spanLayer: Http + startTime: nq 0 + endTime: nq 0 + componentId: nq 0 + isError: true + spanType: Entry + peer: '' + tags: + - {key: url, value: not null } + - {key: http.method, value: GET} + - {key: http.status_code, value: '500'} + skipAnalysis: 'false' + + # B. Exit Span (Contains Logs) + - segmentId: not null + spans: + - operationName: SpringCloudGateway/sendRequest + parentSpanId: 1 + spanId: 2 + spanLayer: Http + startTime: nq 0 + endTime: nq 0 + componentId: nq 0 + isError: true + spanType: Exit + peer: not null + skipAnalysis: 'false' + tags: + - { key: url, value: not null } + logs: + - logEvent: + - { key: event, value: error } + - { key: error.kind, value: not null } + - { key: message, value: not null } + - { key: stack, value: not null} + - operationName: SpringCloudGateway/RoutingFilter + parentSpanId: 0 + spanId: 1 + startTime: nq 0 + endTime: nq 0 + componentId: nq 0 + isError: false + spanType: Local + peer: '' + skipAnalysis: 'false' + refs: + - { parentEndpoint: '/provider/timeout/error', networkAddress: '', refType: CrossThread, + parentSpanId: 0, parentTraceSegmentId: not null, parentServiceInstance: not + null, parentService: not null, traceId: not null } + - operationName: SpringCloudGateway/GatewayFilter + parentSpanId: -1 + spanId: 0 + startTime: nq 0 + endTime: nq 0 + componentId: nq 0 + isError: false + spanType: Local + peer: '' + skipAnalysis: 'false' + refs: + - { parentEndpoint: '/provider/timeout/error', networkAddress: '', refType: CrossThread, + parentSpanId: 0, parentTraceSegmentId: not null, parentServiceInstance: not + null, parentService: not null, traceId: not null } + + # -------------------------------------------------------- + # Case 2: Success Case + # -------------------------------------------------------- + # A. Entry Span + - segmentId: not null + spans: + - operationName: /provider/b/testcase + parentSpanId: -1 + spanId: 0 + spanLayer: Http + startTime: nq 0 + endTime: nq 0 + componentId: nq 0 + isError: false + spanType: Entry + peer: '' + tags: + - { key: url, value: not null } + - { key: http.method, value: GET } + - { key: http.status_code, value: '200' } + skipAnalysis: 'false' + + # B. Exit Span + - segmentId: not null + spans: + - operationName: SpringCloudGateway/sendRequest + parentSpanId: 1 + spanId: 2 + spanLayer: Http + startTime: nq 0 + endTime: nq 0 + componentId: nq 0 + isError: false + spanType: Exit + peer: not null + tags: + - {key: url, value: not null} + - {key: http.status_code, value: '200'} + skipAnalysis: 'false' + - operationName: SpringCloudGateway/RoutingFilter + parentSpanId: 0 + spanId: 1 + startTime: nq 0 + endTime: nq 0 + componentId: nq 0 + isError: false + spanType: Local + peer: '' + skipAnalysis: 'false' + refs: + - {parentEndpoint: '/provider/b/testcase', networkAddress: '', refType: CrossThread, + parentSpanId: 0, parentTraceSegmentId: not null, parentServiceInstance: not + null, parentService: not null, traceId: not null} + - operationName: SpringCloudGateway/GatewayFilter + parentSpanId: -1 + spanId: 0 + startTime: nq 0 + endTime: nq 0 + componentId: nq 0 + isError: false + spanType: Local + peer: '' + skipAnalysis: 'false' + refs: + - {parentEndpoint: '/provider/b/testcase', networkAddress: '', refType: CrossThread, + parentSpanId: 0, parentTraceSegmentId: not null, parentServiceInstance: not + null, parentService: not null, traceId: not null} diff --git a/test/plugin/scenarios/gateway-4.3.x-scenario/configuration.yml b/test/plugin/scenarios/gateway-4.3.x-scenario/configuration.yml new file mode 100644 index 0000000000..49d08ed8ef --- /dev/null +++ b/test/plugin/scenarios/gateway-4.3.x-scenario/configuration.yml @@ -0,0 +1,23 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +type: jvm +entryService: http://localhost:8080/provider/b/testcase +healthCheck: http://localhost:8080/provider/b/healthCheck +startScript: ./bin/startup.sh +runningMode: with_optional +withPlugins: apm-spring-cloud-gateway-4.x-plugin-*.jar + diff --git a/test/plugin/scenarios/gateway-4.3.x-scenario/gateway-dist/bin/startup.sh b/test/plugin/scenarios/gateway-4.3.x-scenario/gateway-dist/bin/startup.sh new file mode 100644 index 0000000000..0d28675632 --- /dev/null +++ b/test/plugin/scenarios/gateway-4.3.x-scenario/gateway-dist/bin/startup.sh @@ -0,0 +1,24 @@ +#!/bin/bash +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +home="$(cd "$(dirname $0)"; pwd)" + +java -jar ${agent_opts} "-Dskywalking.agent.service_name=gateway-projectA-scenario" ${home}/../libs/gateway-projectA-scenario.jar & +sleep 1 + +java -jar ${agent_opts} "-Dskywalking.agent.service_name=gateway-projectB-scenario" ${home}/../libs/gateway-projectB-scenario.jar & diff --git a/test/plugin/scenarios/gateway-4.3.x-scenario/gateway-dist/pom.xml b/test/plugin/scenarios/gateway-4.3.x-scenario/gateway-dist/pom.xml new file mode 100644 index 0000000000..45b02e8800 --- /dev/null +++ b/test/plugin/scenarios/gateway-4.3.x-scenario/gateway-dist/pom.xml @@ -0,0 +1,57 @@ + + + + + org.apache.skywalking + gateway-4.3.x-scenario + 5.0.0 + + 4.0.0 + + gateway-dist + + + gateway-4.3.x-scenario + + + org.apache.maven.plugins + maven-assembly-plugin + + + assemble + package + + single + + + gateway-4.3.x-scenario + false + + src/main/assembly/assembly.xml + + ../target/ + + + + + + + diff --git a/test/plugin/scenarios/gateway-4.3.x-scenario/gateway-dist/src/main/assembly/assembly.xml b/test/plugin/scenarios/gateway-4.3.x-scenario/gateway-dist/src/main/assembly/assembly.xml new file mode 100644 index 0000000000..127200bdc8 --- /dev/null +++ b/test/plugin/scenarios/gateway-4.3.x-scenario/gateway-dist/src/main/assembly/assembly.xml @@ -0,0 +1,47 @@ + + + + gateway-dist + + zip + + + + + ./bin + 0775 + + + + + + ../gateway-projectA-scenario/target/gateway-projectA-scenario.jar + ./libs + 0775 + + + ../gateway-projectB-scenario/target/gateway-projectB-scenario.jar + ./libs + 0775 + + + \ No newline at end of file diff --git a/test/plugin/scenarios/gateway-4.3.x-scenario/gateway-projectA-scenario/pom.xml b/test/plugin/scenarios/gateway-4.3.x-scenario/gateway-projectA-scenario/pom.xml new file mode 100644 index 0000000000..a3009bea02 --- /dev/null +++ b/test/plugin/scenarios/gateway-4.3.x-scenario/gateway-projectA-scenario/pom.xml @@ -0,0 +1,56 @@ + + + + + org.apache.skywalking + gateway-4.3.x-scenario + 5.0.0 + + 4.0.0 + + gateway-projectA-scenario + + + + org.springframework.cloud + spring-cloud-starter-gateway + ${test.framework.version} + + + + + gateway-projectA-scenario + + + org.springframework.boot + spring-boot-maven-plugin + 3.0.0 + + + + repackage + + + + + + + diff --git a/test/plugin/scenarios/gateway-4.3.x-scenario/gateway-projectA-scenario/src/main/java/test/apache/skywalking/apm/testcase/sc/gateway/projectA/ApiKeyResolver.java b/test/plugin/scenarios/gateway-4.3.x-scenario/gateway-projectA-scenario/src/main/java/test/apache/skywalking/apm/testcase/sc/gateway/projectA/ApiKeyResolver.java new file mode 100644 index 0000000000..40517d1d3f --- /dev/null +++ b/test/plugin/scenarios/gateway-4.3.x-scenario/gateway-projectA-scenario/src/main/java/test/apache/skywalking/apm/testcase/sc/gateway/projectA/ApiKeyResolver.java @@ -0,0 +1,31 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package test.apache.skywalking.apm.testcase.sc.gateway.projectA; + +import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver; +import org.springframework.stereotype.Component; +import org.springframework.web.server.ServerWebExchange; +import reactor.core.publisher.Mono; + +@Component +public class ApiKeyResolver implements KeyResolver { + + public Mono resolve(ServerWebExchange exchange) { + return Mono.just(exchange.getRequest().getPath().value()); + } +} diff --git a/test/plugin/scenarios/gateway-4.3.x-scenario/gateway-projectA-scenario/src/main/java/test/apache/skywalking/apm/testcase/sc/gateway/projectA/Application.java b/test/plugin/scenarios/gateway-4.3.x-scenario/gateway-projectA-scenario/src/main/java/test/apache/skywalking/apm/testcase/sc/gateway/projectA/Application.java new file mode 100644 index 0000000000..68eb95b0c6 --- /dev/null +++ b/test/plugin/scenarios/gateway-4.3.x-scenario/gateway-projectA-scenario/src/main/java/test/apache/skywalking/apm/testcase/sc/gateway/projectA/Application.java @@ -0,0 +1,29 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package test.apache.skywalking.apm.testcase.sc.gateway.projectA; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class Application { + + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } +} diff --git a/test/plugin/scenarios/gateway-4.3.x-scenario/gateway-projectA-scenario/src/main/java/test/apache/skywalking/apm/testcase/sc/gateway/projectA/Test1Filter.java b/test/plugin/scenarios/gateway-4.3.x-scenario/gateway-projectA-scenario/src/main/java/test/apache/skywalking/apm/testcase/sc/gateway/projectA/Test1Filter.java new file mode 100644 index 0000000000..af891eae20 --- /dev/null +++ b/test/plugin/scenarios/gateway-4.3.x-scenario/gateway-projectA-scenario/src/main/java/test/apache/skywalking/apm/testcase/sc/gateway/projectA/Test1Filter.java @@ -0,0 +1,38 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package test.apache.skywalking.apm.testcase.sc.gateway.projectA; + +import org.springframework.cloud.gateway.filter.GatewayFilterChain; +import org.springframework.cloud.gateway.filter.GlobalFilter; +import org.springframework.core.Ordered; +import org.springframework.http.server.reactive.ServerHttpRequest; +import org.springframework.web.server.ServerWebExchange; +import reactor.core.publisher.Mono; + +public class Test1Filter implements GlobalFilter, Ordered { + @Override + public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { + ServerHttpRequest buildRequest = exchange.getRequest().mutate().build(); + return chain.filter(exchange.mutate().request(buildRequest).build()); + } + + @Override + public int getOrder() { + return 0; + } +} diff --git a/test/plugin/scenarios/gateway-4.3.x-scenario/gateway-projectA-scenario/src/main/java/test/apache/skywalking/apm/testcase/sc/gateway/projectA/Test2Filter.java b/test/plugin/scenarios/gateway-4.3.x-scenario/gateway-projectA-scenario/src/main/java/test/apache/skywalking/apm/testcase/sc/gateway/projectA/Test2Filter.java new file mode 100644 index 0000000000..d3a5846bd5 --- /dev/null +++ b/test/plugin/scenarios/gateway-4.3.x-scenario/gateway-projectA-scenario/src/main/java/test/apache/skywalking/apm/testcase/sc/gateway/projectA/Test2Filter.java @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package test.apache.skywalking.apm.testcase.sc.gateway.projectA; + +import org.springframework.cloud.gateway.filter.GatewayFilterChain; +import org.springframework.cloud.gateway.filter.GlobalFilter; +import org.springframework.core.Ordered; +import org.springframework.http.server.reactive.ServerHttpRequest; +import org.springframework.web.server.ServerWebExchange; +import reactor.core.publisher.Mono; + +public class Test2Filter implements GlobalFilter, Ordered { + + @Override + public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { + ServerHttpRequest buildRequest = exchange.getRequest().mutate().build(); + return chain.filter(exchange.mutate().request(buildRequest).build()); + } + + @Override + public int getOrder() { + return 1; + } +} diff --git a/test/plugin/scenarios/gateway-4.3.x-scenario/gateway-projectA-scenario/src/main/java/test/apache/skywalking/apm/testcase/sc/gateway/projectA/TestFilterConfig.java b/test/plugin/scenarios/gateway-4.3.x-scenario/gateway-projectA-scenario/src/main/java/test/apache/skywalking/apm/testcase/sc/gateway/projectA/TestFilterConfig.java new file mode 100644 index 0000000000..29f9627bdf --- /dev/null +++ b/test/plugin/scenarios/gateway-4.3.x-scenario/gateway-projectA-scenario/src/main/java/test/apache/skywalking/apm/testcase/sc/gateway/projectA/TestFilterConfig.java @@ -0,0 +1,35 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package test.apache.skywalking.apm.testcase.sc.gateway.projectA; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class TestFilterConfig { + + @Bean + public Test1Filter test1Filter() { + return new Test1Filter(); + } + + @Bean + public Test2Filter test2Filter() { + return new Test2Filter(); + } +} diff --git a/test/plugin/scenarios/gateway-4.3.x-scenario/gateway-projectA-scenario/src/main/resources/application.yml b/test/plugin/scenarios/gateway-4.3.x-scenario/gateway-projectA-scenario/src/main/resources/application.yml new file mode 100644 index 0000000000..7639e7ce8a --- /dev/null +++ b/test/plugin/scenarios/gateway-4.3.x-scenario/gateway-projectA-scenario/src/main/resources/application.yml @@ -0,0 +1,33 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +server: + port: 8080 +spring: + cloud: + gateway: + httpclient: + connect-timeout: 2000 + routes: + - id: provider_route + uri: http://localhost:18070 + predicates: + - Path=/provider/b/* + - id: provider_timeout + uri: http://1.2.3.4:18070 + predicates: + - Path=/provider/timeout/* diff --git a/test/plugin/scenarios/gateway-4.3.x-scenario/gateway-projectB-scenario/pom.xml b/test/plugin/scenarios/gateway-4.3.x-scenario/gateway-projectB-scenario/pom.xml new file mode 100644 index 0000000000..2e1b7cb3d7 --- /dev/null +++ b/test/plugin/scenarios/gateway-4.3.x-scenario/gateway-projectB-scenario/pom.xml @@ -0,0 +1,57 @@ + + + + + gateway-4.3.x-scenario + org.apache.skywalking + 5.0.0 + + 4.0.0 + + gateway-projectB-scenario + + + + org.springframework.boot + spring-boot-starter-web + 2.1.0.RELEASE + + + + + gateway-projectB-scenario + + + org.springframework.boot + spring-boot-maven-plugin + 1.5.9.RELEASE + + + + repackage + + + + + + + + \ No newline at end of file diff --git a/test/plugin/scenarios/gateway-4.3.x-scenario/gateway-projectB-scenario/src/main/java/test/apache/skywalking/apm/testcase/sc/gateway/projectB/Application.java b/test/plugin/scenarios/gateway-4.3.x-scenario/gateway-projectB-scenario/src/main/java/test/apache/skywalking/apm/testcase/sc/gateway/projectB/Application.java new file mode 100644 index 0000000000..51c94655c7 --- /dev/null +++ b/test/plugin/scenarios/gateway-4.3.x-scenario/gateway-projectB-scenario/src/main/java/test/apache/skywalking/apm/testcase/sc/gateway/projectB/Application.java @@ -0,0 +1,31 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package test.apache.skywalking.apm.testcase.sc.gateway.projectB; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.ComponentScan; + +@SpringBootApplication +@ComponentScan(value = {"test.apache.skywalking.apm.testcase.sc.gateway.projectB.controller"}) +public class Application { + + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } +} diff --git a/test/plugin/scenarios/gateway-4.3.x-scenario/gateway-projectB-scenario/src/main/java/test/apache/skywalking/apm/testcase/sc/gateway/projectB/controller/TestController.java b/test/plugin/scenarios/gateway-4.3.x-scenario/gateway-projectB-scenario/src/main/java/test/apache/skywalking/apm/testcase/sc/gateway/projectB/controller/TestController.java new file mode 100644 index 0000000000..923d7fd7ff --- /dev/null +++ b/test/plugin/scenarios/gateway-4.3.x-scenario/gateway-projectB-scenario/src/main/java/test/apache/skywalking/apm/testcase/sc/gateway/projectB/controller/TestController.java @@ -0,0 +1,40 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package test.apache.skywalking.apm.testcase.sc.gateway.projectB.controller; + +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.client.RestTemplate; + +@RestController +public class TestController { + + @RequestMapping("/provider/b/testcase") + public String testcase() { + try { + new RestTemplate().getForEntity("http://localhost:8080/provider/timeout/error", String.class); + } catch (Exception e) { + } + return "1"; + } + + @RequestMapping("/provider/b/healthCheck") + public String healthCheck() { + return "Success"; + } +} diff --git a/test/plugin/scenarios/gateway-4.3.x-scenario/gateway-projectB-scenario/src/main/resources/application.properties b/test/plugin/scenarios/gateway-4.3.x-scenario/gateway-projectB-scenario/src/main/resources/application.properties new file mode 100644 index 0000000000..cac2c4d5a1 --- /dev/null +++ b/test/plugin/scenarios/gateway-4.3.x-scenario/gateway-projectB-scenario/src/main/resources/application.properties @@ -0,0 +1,17 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +server.port=18070 \ No newline at end of file diff --git a/test/plugin/scenarios/gateway-4.3.x-scenario/pom.xml b/test/plugin/scenarios/gateway-4.3.x-scenario/pom.xml new file mode 100644 index 0000000000..a91ec5a97d --- /dev/null +++ b/test/plugin/scenarios/gateway-4.3.x-scenario/pom.xml @@ -0,0 +1,79 @@ + + + + 4.0.0 + + + org.springframework.boot + spring-boot-starter-parent + 3.2.5 + + + + org.apache.skywalking + gateway-4.3.x-scenario + pom + 5.0.0 + + gateway-projectA-scenario + gateway-projectB-scenario + gateway-dist + + + skywalking-gateway-4.3.x-scenario + + + UTF-8 + 17 + 2023.0.0 + 3.11.0 + 4.3.3 + ${test.framework.version} + + + + + + org.springframework.cloud + spring-cloud-dependencies + ${spring-cloud.version} + pom + import + + + + + + gateway-4.3.x-scenario + + + maven-compiler-plugin + ${maven-compiler-plugin.version} + + ${java.version} + ${java.version} + ${project.build.sourceEncoding} + + + + + + diff --git a/test/plugin/scenarios/gateway-4.3.x-scenario/support-version.list b/test/plugin/scenarios/gateway-4.3.x-scenario/support-version.list new file mode 100644 index 0000000000..ab039a217d --- /dev/null +++ b/test/plugin/scenarios/gateway-4.3.x-scenario/support-version.list @@ -0,0 +1,17 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +4.3.3 \ No newline at end of file diff --git a/test/plugin/scenarios/gateway-4.x-scenario/config/expectedData.yaml b/test/plugin/scenarios/gateway-4.x-scenario/config/expectedData.yaml index 70df54e93f..7a883f6261 100644 --- a/test/plugin/scenarios/gateway-4.x-scenario/config/expectedData.yaml +++ b/test/plugin/scenarios/gateway-4.x-scenario/config/expectedData.yaml @@ -54,8 +54,8 @@ segmentItems: - {key: http.method, value: GET} - {key: http.status_code, value: '200'} refs: - - {parentEndpoint: SpringCloudGateway/RoutingFilter, networkAddress: 'localhost:18070', - refType: CrossProcess, parentSpanId: 1, parentTraceSegmentId: not null, parentServiceInstance: not + - {parentEndpoint: SpringCloudGateway/GatewayFilter, networkAddress: 'localhost:18070', + refType: CrossProcess, parentSpanId: 2, parentTraceSegmentId: not null, parentServiceInstance: not null, parentService: not null, traceId: not null} skipAnalysis: 'false' - serviceName: gateway-projectA-scenario @@ -76,8 +76,8 @@ segmentItems: - segmentId: not null spans: - operationName: SpringCloudGateway/sendRequest - parentSpanId: 0 - spanId: 1 + parentSpanId: 1 + spanId: 2 spanLayer: Http startTime: nq 0 endTime: nq 0 @@ -95,8 +95,8 @@ segmentItems: - { key: message, value: not null } - { key: stack, value: not null} - operationName: SpringCloudGateway/RoutingFilter - parentSpanId: -1 - spanId: 0 + parentSpanId: 0 + spanId: 1 startTime: nq 0 endTime: nq 0 componentId: 61 @@ -106,11 +106,23 @@ segmentItems: parentSpanId: 0, parentTraceSegmentId: not null, parentServiceInstance: not null, parentService: not null, traceId: not null } skipAnalysis: 'false' + - operationName: SpringCloudGateway/GatewayFilter + parentSpanId: -1 + spanId: 0 + startTime: nq 0 + endTime: nq 0 + componentId: 61 + spanType: Local + refs: + - { parentEndpoint: '/provider/timeout/error', networkAddress: '', refType: CrossThread, + parentSpanId: 0, parentTraceSegmentId: not null, parentServiceInstance: not + null, parentService: not null, traceId: not null } + skipAnalysis: 'false' - segmentId: not null spans: - operationName: SpringCloudGateway/sendRequest - parentSpanId: 0 - spanId: 1 + parentSpanId: 1 + spanId: 2 spanLayer: Http startTime: nq 0 endTime: nq 0 @@ -123,8 +135,8 @@ segmentItems: - {key: http.status_code, value: '200'} skipAnalysis: 'false' - operationName: SpringCloudGateway/RoutingFilter - parentSpanId: -1 - spanId: 0 + parentSpanId: 0 + spanId: 1 startTime: nq 0 endTime: nq 0 componentId: 61 @@ -136,6 +148,20 @@ segmentItems: parentSpanId: 0, parentTraceSegmentId: not null, parentServiceInstance: not null, parentService: not null, traceId: not null} skipAnalysis: 'false' + - operationName: SpringCloudGateway/GatewayFilter + parentSpanId: -1 + spanId: 0 + startTime: nq 0 + endTime: nq 0 + componentId: 61 + isError: false + spanType: Local + peer: '' + refs: + - {parentEndpoint: '/provider/b/testcase', networkAddress: '', refType: CrossThread, + parentSpanId: 0, parentTraceSegmentId: not null, parentServiceInstance: not + null, parentService: not null, traceId: not null} + skipAnalysis: 'false' - segmentId: not null spans: - operationName: /provider/b/testcase diff --git a/test/plugin/scenarios/gateway-4.x-scenario/support-version.list b/test/plugin/scenarios/gateway-4.x-scenario/support-version.list index ff90e11c58..d69db104f9 100644 --- a/test/plugin/scenarios/gateway-4.x-scenario/support-version.list +++ b/test/plugin/scenarios/gateway-4.x-scenario/support-version.list @@ -16,4 +16,5 @@ 4.0.0 4.0.8 -4.1.0 \ No newline at end of file +4.1.0 +4.1.1 \ No newline at end of file diff --git a/test/plugin/scenarios/grizzly-2.3.x-4.x-workthreadpool-scenario/config/expectedData.yaml b/test/plugin/scenarios/grizzly-2.3.x-4.x-workthreadpool-scenario/config/expectedData.yaml index da0f47c6ab..a635342f8b 100644 --- a/test/plugin/scenarios/grizzly-2.3.x-4.x-workthreadpool-scenario/config/expectedData.yaml +++ b/test/plugin/scenarios/grizzly-2.3.x-4.x-workthreadpool-scenario/config/expectedData.yaml @@ -15,7 +15,7 @@ # limitations under the License. meterItems: - serviceName: grizzly-2.3.x-4.x-workthreadpool-scenario - meterSize: 4 + meterSize: ge 4 meters: - meterId: name: thread_pool diff --git a/test/plugin/scenarios/grpc-1.30.x-1.39.x-scenario/bin/startup.sh b/test/plugin/scenarios/grpc-1.30.x-1.39.x-scenario/bin/startup.sh new file mode 100644 index 0000000000..aeac2a7b77 --- /dev/null +++ b/test/plugin/scenarios/grpc-1.30.x-1.39.x-scenario/bin/startup.sh @@ -0,0 +1,21 @@ +#!/bin/bash +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +home="$(cd "$(dirname $0)"; pwd)" + +java -jar ${agent_opts} ${home}/../libs/grpc-1.30.x-1.39.x-scenario.jar & diff --git a/test/plugin/scenarios/grpc-1.30.x-1.39.x-scenario/config/expectedData.yaml b/test/plugin/scenarios/grpc-1.30.x-1.39.x-scenario/config/expectedData.yaml new file mode 100644 index 0000000000..b4551acf3e --- /dev/null +++ b/test/plugin/scenarios/grpc-1.30.x-1.39.x-scenario/config/expectedData.yaml @@ -0,0 +1,458 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +segmentItems: + - serviceName: grpc-1.30.x-1.39.x-scenario + segmentSize: gt 10 + segments: + - segmentId: not null + spans: + - operationName: GreeterBlocking.sayHello + parentSpanId: -1 + spanId: 0 + spanLayer: RPCFramework + startTime: nq 0 + endTime: nq 0 + componentId: 23 + isError: false + spanType: Entry + peer: '' + refs: + - {parentEndpoint: GET:/grpc-1.30.x-1.39.x-scenario/case/grpc-1.30.x-1.39.x-scenario, networkAddress: '127.0.0.1:18080', + refType: CrossProcess, parentSpanId: 2, parentTraceSegmentId: not null, parentServiceInstance: not + null, parentService: grpc-1.30.x-1.39.x-scenario, traceId: not null} + skipAnalysis: 'false' + - segmentId: not null + spans: + - operationName: Greeter.sayHello/client/Request/onMessage + parentSpanId: -1 + spanId: 0 + spanLayer: RPCFramework + startTime: nq 0 + endTime: nq 0 + componentId: 23 + isError: false + spanType: Local + peer: '' + refs: + - {parentEndpoint: GET:/grpc-1.30.x-1.39.x-scenario/case/grpc-1.30.x-1.39.x-scenario, networkAddress: '', refType: CrossThread, + parentSpanId: 1, parentTraceSegmentId: not null, parentServiceInstance: not + null, parentService: not null, traceId: not null} + skipAnalysis: 'false' + - segmentId: not null + spans: + - operationName: Greeter.sayHello/client/Request/onMessage + parentSpanId: -1 + spanId: 0 + spanLayer: RPCFramework + startTime: nq 0 + endTime: nq 0 + componentId: 23 + isError: false + spanType: Local + peer: '' + refs: + - {parentEndpoint: GET:/grpc-1.30.x-1.39.x-scenario/case/grpc-1.30.x-1.39.x-scenario, networkAddress: '', refType: CrossThread, + parentSpanId: 1, parentTraceSegmentId: not null, parentServiceInstance: not + null, parentService: grpc-1.30.x-1.39.x-scenario, traceId: not null} + skipAnalysis: 'false' + - segmentId: not null + spans: + - operationName: Greeter.sayHello/client/Request/onComplete + parentSpanId: -1 + spanId: 0 + spanLayer: RPCFramework + startTime: nq 0 + endTime: nq 0 + componentId: 23 + isError: false + spanType: Local + peer: '' + refs: + - {parentEndpoint: GET:/grpc-1.30.x-1.39.x-scenario/case/grpc-1.30.x-1.39.x-scenario, networkAddress: '', refType: CrossThread, + parentSpanId: 1, parentTraceSegmentId: not null, parentServiceInstance: not + null, parentService: grpc-1.30.x-1.39.x-scenario, traceId: not null} + skipAnalysis: 'false' + - segmentId: not null + spans: + - operationName: Greeter.sayHello + parentSpanId: -1 + spanId: 0 + spanLayer: RPCFramework + startTime: nq 0 + endTime: nq 0 + componentId: 23 + isError: false + spanType: Entry + peer: '' + refs: + - {parentEndpoint: GET:/grpc-1.30.x-1.39.x-scenario/case/grpc-1.30.x-1.39.x-scenario, networkAddress: '127.0.0.1:18080', + refType: CrossProcess, parentSpanId: 1, parentTraceSegmentId: not null, parentServiceInstance: not + null, parentService: grpc-1.30.x-1.39.x-scenario, traceId: not null} + skipAnalysis: 'false' + - segmentId: not null + spans: + - operationName: Greeter.sayHello/server/Response/onMessage + parentSpanId: 0 + spanId: 1 + spanLayer: RPCFramework + startTime: nq 0 + endTime: nq 0 + componentId: 23 + isError: false + spanType: Local + peer: '' + skipAnalysis: 'false' + - operationName: Greeter.sayHello/server/Request/onMessage + parentSpanId: -1 + spanId: 0 + spanLayer: RPCFramework + startTime: nq 0 + endTime: nq 0 + componentId: 23 + isError: false + spanType: Local + peer: '' + refs: + - {parentEndpoint: Greeter.sayHello, networkAddress: '', refType: CrossThread, + parentSpanId: 0, parentTraceSegmentId: not null, parentServiceInstance: not + null, parentService: not null, traceId: not null} + skipAnalysis: 'false' + - segmentId: not null + spans: + - operationName: Greeter.sayHello/client/Response/onMessage + parentSpanId: -1 + spanId: 0 + spanLayer: RPCFramework + startTime: nq 0 + endTime: nq 0 + componentId: 23 + isError: false + spanType: Local + peer: '' + refs: + - {parentEndpoint: GET:/grpc-1.30.x-1.39.x-scenario/case/grpc-1.30.x-1.39.x-scenario, networkAddress: '', refType: CrossThread, + parentSpanId: 1, parentTraceSegmentId: not null, parentServiceInstance: not + null, parentService: not null, traceId: not null} + skipAnalysis: 'false' + - segmentId: not null + spans: + - operationName: GreeterBlocking.sayHello/server/Response/onClose + parentSpanId: 0 + spanId: 1 + spanLayer: RPCFramework + startTime: nq 0 + endTime: nq 0 + componentId: 23 + isError: false + spanType: Local + peer: '' + tags: + - {key: rpc.status_code, value: OK} + skipAnalysis: 'false' + - operationName: GreeterBlocking.sayHello/server/Request/onHalfClose + parentSpanId: -1 + spanId: 0 + spanLayer: RPCFramework + startTime: nq 0 + endTime: nq 0 + componentId: 23 + isError: false + spanType: Local + peer: '' + refs: + - {parentEndpoint: GreeterBlocking.sayHello, networkAddress: '', refType: CrossThread, + parentSpanId: 0, parentTraceSegmentId: not null, parentServiceInstance: not + null, parentService: grpc-1.30.x-1.39.x-scenario, traceId: not null} + skipAnalysis: 'false' + - segmentId: not null + spans: + - operationName: Greeter.sayHello/server/Response/onMessage + parentSpanId: 0 + spanId: 1 + spanLayer: RPCFramework + startTime: nq 0 + endTime: nq 0 + componentId: 23 + isError: false + spanType: Local + peer: '' + skipAnalysis: 'false' + - operationName: Greeter.sayHello/server/Request/onMessage + parentSpanId: -1 + spanId: 0 + spanLayer: RPCFramework + startTime: nq 0 + endTime: nq 0 + componentId: 23 + isError: false + spanType: Local + peer: '' + refs: + - {parentEndpoint: Greeter.sayHello, networkAddress: '', refType: CrossThread, + parentSpanId: 0, parentTraceSegmentId: not null, parentServiceInstance: not + null, parentService: grpc-1.30.x-1.39.x-scenario, traceId: not null} + skipAnalysis: 'false' + - segmentId: not null + spans: + - operationName: Greeter.sayHello/server/Response/onClose + parentSpanId: 0 + spanId: 1 + spanLayer: RPCFramework + startTime: nq 0 + endTime: nq 0 + componentId: 23 + isError: false + spanType: Local + peer: '' + tags: + - {key: rpc.status_code, value: OK} + skipAnalysis: 'false' + - operationName: Greeter.sayHello/server/Request/onHalfClose + parentSpanId: -1 + spanId: 0 + spanLayer: RPCFramework + startTime: nq 0 + endTime: nq 0 + componentId: 23 + isError: false + spanType: Local + peer: '' + refs: + - {parentEndpoint: Greeter.sayHello, networkAddress: '', refType: CrossThread, + parentSpanId: 0, parentTraceSegmentId: not null, parentServiceInstance: not + null, parentService: grpc-1.30.x-1.39.x-scenario, traceId: not null} + skipAnalysis: 'false' + - segmentId: not null + spans: + - operationName: Greeter.sayHello/client/Response/onMessage + parentSpanId: -1 + spanId: 0 + spanLayer: RPCFramework + startTime: nq 0 + endTime: nq 0 + componentId: 23 + isError: false + spanType: Local + peer: '' + refs: + - {parentEndpoint: GET:/grpc-1.30.x-1.39.x-scenario/case/grpc-1.30.x-1.39.x-scenario, networkAddress: '', refType: CrossThread, + parentSpanId: 1, parentTraceSegmentId: not null, parentServiceInstance: not + null, parentService: grpc-1.30.x-1.39.x-scenario, traceId: not null} + skipAnalysis: 'false' + - segmentId: not null + spans: + - operationName: Greeter.sayHello/client/Response/onClose + parentSpanId: -1 + spanId: 0 + spanLayer: RPCFramework + startTime: nq 0 + endTime: nq 0 + componentId: 23 + isError: false + spanType: Local + peer: '' + refs: + - {parentEndpoint: GET:/grpc-1.30.x-1.39.x-scenario/case/grpc-1.30.x-1.39.x-scenario, networkAddress: '', refType: CrossThread, + parentSpanId: 1, parentTraceSegmentId: not null, parentServiceInstance: not + null, parentService: grpc-1.30.x-1.39.x-scenario, traceId: not null} + skipAnalysis: 'false' + - segmentId: not null + spans: + - operationName: GreeterBlockingError.sayHello + parentSpanId: -1 + spanId: 0 + spanLayer: RPCFramework + startTime: nq 0 + endTime: nq 0 + componentId: 23 + isError: false + spanType: Entry + peer: '' + refs: + - {parentEndpoint: GET:/grpc-1.30.x-1.39.x-scenario/case/grpc-1.30.x-1.39.x-scenario, networkAddress: '127.0.0.1:18080', + refType: CrossProcess, parentSpanId: 5, parentTraceSegmentId: not null, parentServiceInstance: not + null, parentService: grpc-1.30.x-1.39.x-scenario, traceId: not null} + skipAnalysis: 'false' + - segmentId: not null + spans: + - operationName: GreeterBlockingError.sayHello/server/Response/onClose + parentSpanId: 0 + spanId: 1 + spanLayer: RPCFramework + startTime: nq 0 + endTime: nq 0 + componentId: 23 + isError: true + spanType: Local + peer: '' + tags: + - {key: rpc.status_code, value: UNKNOWN} + logs: + - logEvent: + - {key: event, value: error} + - {key: error.kind, value: java.lang.Exception} + - {key: message, value: ''} + - {key: stack, value: not null} + skipAnalysis: 'false' + - operationName: GreeterBlockingError.sayHello/server/Request/onHalfClose + parentSpanId: -1 + spanId: 0 + spanLayer: RPCFramework + startTime: nq 0 + endTime: nq 0 + componentId: 23 + isError: false + spanType: Local + peer: '' + refs: + - {parentEndpoint: GreeterBlockingError.sayHello, networkAddress: '', refType: CrossThread, + parentSpanId: 0, parentTraceSegmentId: not null, parentServiceInstance: not + null, parentService: grpc-1.30.x-1.39.x-scenario, traceId: not null} + skipAnalysis: 'false' + - segmentId: not null + spans: + - operationName: Greeter.sayHello + parentSpanId: 0 + spanId: 1 + spanLayer: RPCFramework + startTime: nq 0 + endTime: nq 0 + componentId: 23 + isError: false + spanType: Exit + peer: '127.0.0.1:18080' + skipAnalysis: 'false' + - operationName: GreeterBlocking.sayHello/client/Request/onComplete + parentSpanId: 2 + spanId: 3 + spanLayer: RPCFramework + startTime: nq 0 + endTime: nq 0 + componentId: 23 + isError: false + spanType: Local + peer: '' + skipAnalysis: 'false' + - operationName: GreeterBlocking.sayHello/client/Response/onClose + parentSpanId: 2 + spanId: 4 + spanLayer: RPCFramework + startTime: nq 0 + endTime: nq 0 + componentId: 23 + isError: false + spanType: Local + peer: '' + skipAnalysis: 'false' + - operationName: GreeterBlocking.sayHello + parentSpanId: 0 + panId: 2 + spanLayer: RPCFramework + startTime: nq 0 + endTime: nq 0 + componentId: 23 + isError: false + spanType: Exit + peer: '127.0.0.1:18080' + skipAnalysis: 'false' + - operationName: GreeterBlockingError.sayHello/client/Request/onComplete + parentSpanId: 5 + spanId: 6 + spanLayer: RPCFramework + startTime: nq 0 + endTime: nq 0 + componentId: 23 + isError: false + spanType: Local + peer: '' + skipAnalysis: 'false' + - operationName: GreeterBlockingError.sayHello/client/Response/onClose + parentSpanId: 5 + spanId: 7 + spanLayer: RPCFramework + startTime: nq 0 + endTime: nq 0 + componentId: 23 + isError: true + spanType: Local + peer: '' + tags: + - {key: rpc.status_code, value: UNKNOWN} + logs: + - logEvent: + - {key: event, value: error} + - {key: error.kind, value: io.grpc.StatusRuntimeException} + - {key: message, value: UNKNOWN} + - {key: stack, value: not null} + skipAnalysis: 'false' + - operationName: GreeterBlockingError.sayHello/client/Request/onCancel + parentSpanId: 5 + spanId: 8 + spanLayer: RPCFramework + startTime: nq 0 + endTime: nq 0 + componentId: 23 + isError: true + spanType: Local + peer: '' + logs: + - logEvent: + - {key: event, value: error} + - {key: error.kind, value: io.grpc.StatusRuntimeException} + - {key: message, value: UNKNOWN} + - {key: stack, value: not null} + skipAnalysis: 'false' + - operationName: GreeterBlockingError.sayHello + parentSpanId: 0 + spanId: 5 + spanLayer: RPCFramework + startTime: nq 0 + endTime: nq 0 + componentId: 23 + isError: true + spanType: Exit + peer: 127.0.0.1:18080 + logs: + - logEvent: + - {key: event, value: error} + - {key: error.kind, value: io.grpc.StatusRuntimeException} + - {key: message, value: UNKNOWN} + - {key: stack, value: not null} + skipAnalysis: 'false' + - operationName: GET:/grpc-1.30.x-1.39.x-scenario/case/grpc-1.30.x-1.39.x-scenario + parentSpanId: -1 + spanId: 0 + spanLayer: Http + startTime: nq 0 + endTime: nq 0 + componentId: 1 + isError: true + spanType: Entry + peer: '' + tags: + - {key: url, value: 'http://localhost:8080/grpc-1.30.x-1.39.x-scenario/case/grpc-1.30.x-1.39.x-scenario'} + - {key: http.method, value: GET} + - {key: http.status_code, value: '500'} + logs: + - logEvent: + - {key: event, value: error} + - {key: error.kind, value: org.springframework.web.util.NestedServletException} + - {key: message, value: 'Request processing failed; nested exception is io.grpc.StatusRuntimeException: + UNKNOWN'} + - {key: stack, value: not null} + - logEvent: + - {key: forward-url, value: /grpc-1.30.x-1.39.x-scenario/error} + skipAnalysis: 'false' diff --git a/test/plugin/scenarios/grpc-1.30.x-1.39.x-scenario/configuration.yml b/test/plugin/scenarios/grpc-1.30.x-1.39.x-scenario/configuration.yml new file mode 100644 index 0000000000..932097e647 --- /dev/null +++ b/test/plugin/scenarios/grpc-1.30.x-1.39.x-scenario/configuration.yml @@ -0,0 +1,22 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +type: jvm +entryService: http://localhost:8080/grpc-1.30.x-1.39.x-scenario/case/grpc-1.30.x-1.39.x-scenario +healthCheck: http://localhost:8080/grpc-1.30.x-1.39.x-scenario/case/healthCheck +startScript: ./bin/startup.sh +environment: +dependencies: diff --git a/test/plugin/scenarios/grpc-1.30.x-1.39.x-scenario/pom.xml b/test/plugin/scenarios/grpc-1.30.x-1.39.x-scenario/pom.xml new file mode 100644 index 0000000000..de23b46190 --- /dev/null +++ b/test/plugin/scenarios/grpc-1.30.x-1.39.x-scenario/pom.xml @@ -0,0 +1,170 @@ + + + + + org.apache.skywalking.apm.testcase + grpc-1.30.x-1.39.x-scenario + 1.0.0 + jar + + 4.0.0 + + + UTF-8 + 1.8 + 3.8.1 + 1.6.2 + + 1.39.0 + + 2.1.6.RELEASE + + + skywalking-grpc-1.30.x-1.39.x-scenario + + + + + org.springframework.boot + spring-boot-dependencies + ${spring.boot.version} + pom + import + + + + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-logging + + + + + org.springframework.boot + spring-boot-starter-log4j2 + + + + + io.grpc + grpc-all + ${test.framework.version} + + + io.grpc + grpc-rls + + + + + + + + + grpc-1.30.x-1.39.x-scenario + + + kr.motd.maven + os-maven-plugin + ${os-maven-plugin.version} + + + initialize + + detect + + + + + + org.springframework.boot + spring-boot-maven-plugin + ${spring.boot.version} + + + + repackage + + + + + + maven-compiler-plugin + ${maven-compiler-plugin.version} + + ${compiler.version} + ${compiler.version} + ${project.build.sourceEncoding} + + + + org.apache.maven.plugins + maven-assembly-plugin + + + assemble + package + + single + + + + src/main/assembly/assembly.xml + + ./target/ + + + + + + org.xolstice.maven.plugins + protobuf-maven-plugin + 0.6.1 + + + com.google.protobuf:protoc:3.11.4:exe:${os.detected.classifier} + + grpc-java + io.grpc:protoc-gen-grpc-java:${test.framework.version}:exe:${os.detected.classifier} + + + + + + compile + compile-custom + + + + + + + diff --git a/test/plugin/scenarios/nats-2.14.x-2.15.x-scenario/src/main/assembly/assembly.xml b/test/plugin/scenarios/grpc-1.30.x-1.39.x-scenario/src/main/assembly/assembly.xml similarity index 96% rename from test/plugin/scenarios/nats-2.14.x-2.15.x-scenario/src/main/assembly/assembly.xml rename to test/plugin/scenarios/grpc-1.30.x-1.39.x-scenario/src/main/assembly/assembly.xml index 098c888d81..9ba3b28c36 100644 --- a/test/plugin/scenarios/nats-2.14.x-2.15.x-scenario/src/main/assembly/assembly.xml +++ b/test/plugin/scenarios/grpc-1.30.x-1.39.x-scenario/src/main/assembly/assembly.xml @@ -33,7 +33,7 @@ - ${project.build.directory}/nats-2.14.x-2.15.x-scenario.jar + ${project.build.directory}/grpc-1.30.x-1.39.x-scenario.jar ./libs 0775 diff --git a/test/plugin/scenarios/oracle-scenario/src/main/java/org/apache/skywalking/apm/testcase/oracle/Application.java b/test/plugin/scenarios/grpc-1.30.x-1.39.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/grpc/Application.java similarity index 95% rename from test/plugin/scenarios/oracle-scenario/src/main/java/org/apache/skywalking/apm/testcase/oracle/Application.java rename to test/plugin/scenarios/grpc-1.30.x-1.39.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/grpc/Application.java index 31bce49f55..052e0f7e3b 100644 --- a/test/plugin/scenarios/oracle-scenario/src/main/java/org/apache/skywalking/apm/testcase/oracle/Application.java +++ b/test/plugin/scenarios/grpc-1.30.x-1.39.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/grpc/Application.java @@ -16,7 +16,7 @@ * */ -package org.apache.skywalking.apm.testcase.oracle; +package org.apache.skywalking.apm.testcase.grpc; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; diff --git a/test/plugin/scenarios/grpc-1.30.x-1.39.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/grpc/consumr/ConsumerInterceptor.java b/test/plugin/scenarios/grpc-1.30.x-1.39.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/grpc/consumr/ConsumerInterceptor.java new file mode 100644 index 0000000000..e8a35d0359 --- /dev/null +++ b/test/plugin/scenarios/grpc-1.30.x-1.39.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/grpc/consumr/ConsumerInterceptor.java @@ -0,0 +1,109 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.testcase.grpc.consumr; + +import io.grpc.CallOptions; +import io.grpc.Channel; +import io.grpc.ClientCall; +import io.grpc.ClientInterceptor; +import io.grpc.ForwardingClientCall; +import io.grpc.ForwardingClientCallListener; +import io.grpc.Metadata; +import io.grpc.MethodDescriptor; +import io.grpc.Status; +import javax.annotation.Nullable; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public class ConsumerInterceptor implements ClientInterceptor { + + private static final Logger LOGGER = LogManager.getLogger(ConsumerInterceptor.class); + + @Override + public ClientCall interceptCall(MethodDescriptor descriptor, + CallOptions options, Channel channel) { + LOGGER.info("start interceptor!"); + LOGGER.info("method type: {}", descriptor.getType()); + return new ForwardingClientCall.SimpleForwardingClientCall(channel.newCall(descriptor, options)) { + @Override + public void start(Listener responseListener, Metadata headers) { + LOGGER.info("Peer: {}", channel.authority()); + LOGGER.info("Operation Name : {}", descriptor.getFullMethodName()); + Interceptor tracingResponseListener = new Interceptor(responseListener); + tracingResponseListener.contextSnapshot = "contextSnapshot"; + delegate().start(tracingResponseListener, headers); + } + + @Override + public void cancel(@Nullable String message, @Nullable Throwable cause) { + LOGGER.info("cancel"); + super.cancel(message, cause); + } + + @Override + public void halfClose() { + LOGGER.info("halfClose"); + super.halfClose(); + } + + @Override + public void sendMessage(REQ_T message) { + LOGGER.info("sendMessage ...."); + super.sendMessage(message); + } + }; + } + + private static class Interceptor extends ForwardingClientCallListener.SimpleForwardingClientCallListener { + private static final Logger LOGGER = LogManager.getLogger(Interceptor.class); + + private Object contextSnapshot; + + protected Interceptor(ClientCall.Listener delegate) { + super(delegate); + } + + @Override + public void onHeaders(Metadata headers) { + LOGGER.info("on Headers"); + for (String key : headers.keys()) { + LOGGER.info("Receive key: {}", key); + } + delegate().onHeaders(headers); + } + + @Override + public void onMessage(RESP_T message) { + LOGGER.info("contextSnapshot: {}", contextSnapshot); + delegate().onMessage(message); + } + + @Override + public void onClose(Status status, Metadata trailers) { + LOGGER.info("on close"); + delegate().onClose(status, trailers); + } + + @Override + public void onReady() { + LOGGER.info("on Ready"); + super.onReady(); + } + } +} diff --git a/test/plugin/scenarios/grpc-1.30.x-1.39.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/grpc/controller/CaseController.java b/test/plugin/scenarios/grpc-1.30.x-1.39.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/grpc/controller/CaseController.java new file mode 100644 index 0000000000..c97804e90b --- /dev/null +++ b/test/plugin/scenarios/grpc-1.30.x-1.39.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/grpc/controller/CaseController.java @@ -0,0 +1,137 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.testcase.grpc.controller; + +import io.grpc.ClientInterceptors; +import io.grpc.ManagedChannel; +import io.grpc.ManagedChannelBuilder; +import io.grpc.stub.ClientCallStreamObserver; +import io.grpc.stub.ClientResponseObserver; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; +import javax.annotation.PostConstruct; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.apache.skywalking.apm.testcase.grpc.consumr.ConsumerInterceptor; +import org.apache.skywalking.apm.testcase.grpc.proto.GreeterBlockingErrorGrpc; +import org.apache.skywalking.apm.testcase.grpc.proto.GreeterBlockingGrpc; +import org.apache.skywalking.apm.testcase.grpc.proto.GreeterGrpc; +import org.apache.skywalking.apm.testcase.grpc.proto.HelloRequest; +import org.apache.skywalking.apm.testcase.grpc.proto.HelloReply; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/case") +public class CaseController { + + private static final Logger LOGGER = LogManager.getLogger(CaseController.class); + + private static final String SUCCESS = "Success"; + + private final String grpcProviderHost = "127.0.0.1"; + private final int grpcProviderPort = 18080; + private ManagedChannel channel; + private GreeterGrpc.GreeterStub greeterStub; + private GreeterBlockingGrpc.GreeterBlockingBlockingStub greeterBlockingStub; + private GreeterBlockingErrorGrpc.GreeterBlockingErrorBlockingStub greeterBlockingErrorStub; + + @PostConstruct + public void up() { + channel = ManagedChannelBuilder.forAddress(grpcProviderHost, grpcProviderPort).usePlaintext().build(); + greeterStub = GreeterGrpc.newStub(ClientInterceptors.intercept(channel, new ConsumerInterceptor())); + greeterBlockingStub = GreeterBlockingGrpc.newBlockingStub(ClientInterceptors.intercept(channel, new ConsumerInterceptor())); + greeterBlockingErrorStub = GreeterBlockingErrorGrpc.newBlockingStub(ClientInterceptors.intercept(channel, new ConsumerInterceptor())); + } + + @RequestMapping("/grpc-1.30.x-1.39.x-scenario") + @ResponseBody + public String testcase() { + greetService(); + greetBlockingService(); + greetBlockingErrorService(); + return SUCCESS; + } + + @RequestMapping("/healthCheck") + @ResponseBody + public String healthCheck() { + // your codes + return SUCCESS; + } + + private static List names() { + return Arrays.asList("Sophia", "Jackson"); + } + + private void greetService() { + ClientResponseObserver helloReplyStreamObserver = new ClientResponseObserver() { + private ClientCallStreamObserver requestStream; + + @Override + public void beforeStart(ClientCallStreamObserver observer) { + this.requestStream = observer; + this.requestStream.setOnReadyHandler(new Runnable() { + Iterator iterator = names().iterator(); + + @Override + public void run() { + while (requestStream.isReady()) { + if (iterator.hasNext()) { + String name = iterator.next(); + HelloRequest request = HelloRequest.newBuilder().setName(name).build(); + requestStream.onNext(request); + } else { + requestStream.onCompleted(); + } + } + } + }); + } + + @Override + public void onNext(HelloReply reply) { + LOGGER.info("Receive an message from provider. message: {}", reply.getMessage()); + requestStream.request(1); + } + + public void onError(Throwable throwable) { + LOGGER.error("Failed to send data", throwable); + } + + public void onCompleted() { + LOGGER.info("All Done"); + } + }; + + greeterStub.sayHello(helloReplyStreamObserver); + } + + private void greetBlockingService() { + HelloRequest request = HelloRequest.newBuilder().setName("Sophia").build(); + greeterBlockingStub.sayHello(request); + } + + private void greetBlockingErrorService() { + HelloRequest request = HelloRequest.newBuilder().setName("Tony").build(); + greeterBlockingErrorStub.sayHello(request); + } +} diff --git a/test/plugin/scenarios/grpc-1.30.x-1.39.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/grpc/provider/ProviderConfiguration.java b/test/plugin/scenarios/grpc-1.30.x-1.39.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/grpc/provider/ProviderConfiguration.java new file mode 100644 index 0000000000..8541c403b1 --- /dev/null +++ b/test/plugin/scenarios/grpc-1.30.x-1.39.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/grpc/provider/ProviderConfiguration.java @@ -0,0 +1,44 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.testcase.grpc.provider; + +import io.grpc.Server; +import io.grpc.ServerBuilder; +import io.grpc.ServerInterceptors; +import org.apache.skywalking.apm.testcase.grpc.provider.interceptor.ProviderInterceptor; +import org.apache.skywalking.apm.testcase.grpc.provider.service.GreeterBlockingErrorServiceImpl; +import org.apache.skywalking.apm.testcase.grpc.provider.service.GreeterBlockingServiceImpl; +import org.apache.skywalking.apm.testcase.grpc.provider.service.GreeterServiceImpl; +import org.springframework.beans.factory.annotation.Configurable; +import org.springframework.context.annotation.Bean; +import org.springframework.stereotype.Component; + +@Configurable +@Component +public class ProviderConfiguration { + + @Bean(initMethod = "start", destroyMethod = "shutdown") + public Server server() { + return ServerBuilder.forPort(18080) + .addService(ServerInterceptors.intercept(new GreeterServiceImpl(), new ProviderInterceptor())) + .addService(ServerInterceptors.intercept(new GreeterBlockingServiceImpl(), new ProviderInterceptor())) + .addService(ServerInterceptors.intercept(new GreeterBlockingErrorServiceImpl(), new ProviderInterceptor())) + .build(); + } +} diff --git a/test/plugin/scenarios/grpc-1.30.x-1.39.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/grpc/provider/interceptor/ProviderInterceptor.java b/test/plugin/scenarios/grpc-1.30.x-1.39.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/grpc/provider/interceptor/ProviderInterceptor.java new file mode 100644 index 0000000000..26647b41a8 --- /dev/null +++ b/test/plugin/scenarios/grpc-1.30.x-1.39.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/grpc/provider/interceptor/ProviderInterceptor.java @@ -0,0 +1,96 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.testcase.grpc.provider.interceptor; + +import io.grpc.ForwardingServerCall; +import io.grpc.ForwardingServerCallListener; +import io.grpc.Metadata; +import io.grpc.ServerCall; +import io.grpc.ServerCallHandler; +import io.grpc.ServerInterceptor; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public class ProviderInterceptor implements ServerInterceptor { + private static final Logger LOGGER = LogManager.getLogger(ProviderInterceptor.class); + + @Override + public ServerCall.Listener interceptCall(ServerCall call, Metadata metadata, + ServerCallHandler handler) { + Map headerMap = new HashMap(); + for (String key : metadata.keys()) { + LOGGER.info("Receive key: {}", key); + if (!key.endsWith(Metadata.BINARY_HEADER_SUFFIX)) { + String value = metadata.get(Metadata.Key.of(key, Metadata.ASCII_STRING_MARSHALLER)); + + headerMap.put(key, value); + } + } + LOGGER.info("authority : {}", call.getAuthority()); + return new ForwardingServerCallListener.SimpleForwardingServerCallListener(handler.startCall(new ForwardingServerCall.SimpleForwardingServerCall(call) { + @Override + public void sendHeaders(Metadata responseHeaders) { + LOGGER.info("sendHeaders...."); + Metadata.Key headerKey = Metadata.Key.of("test-server", Metadata.ASCII_STRING_MARSHALLER); + responseHeaders.put(headerKey, "test-server"); + delegate().sendHeaders(responseHeaders); + } + + @Override + public void sendMessage(RESQ_T message) { + delegate().sendMessage(message); + } + + }, metadata)) { + @Override + public void onReady() { + LOGGER.info("onReady...."); + delegate().onReady(); + } + + @Override + public void onCancel() { + LOGGER.info("onCancel...."); + delegate().onCancel(); + } + + @Override + public void onComplete() { + LOGGER.info("onComplete...."); + delegate().onComplete(); + } + + @Override + public void onHalfClose() { + LOGGER.info("onHalfClose...."); + delegate().onHalfClose(); + } + + @Override + public void onMessage(REQ_T message) { + LOGGER.info("onMessage...."); + delegate().onMessage(message); + } + }; + } +} diff --git a/test/plugin/scenarios/grpc-1.30.x-1.39.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/grpc/provider/service/GreeterBlockingErrorServiceImpl.java b/test/plugin/scenarios/grpc-1.30.x-1.39.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/grpc/provider/service/GreeterBlockingErrorServiceImpl.java new file mode 100644 index 0000000000..3cd6394a48 --- /dev/null +++ b/test/plugin/scenarios/grpc-1.30.x-1.39.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/grpc/provider/service/GreeterBlockingErrorServiceImpl.java @@ -0,0 +1,31 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.testcase.grpc.provider.service; + +import io.grpc.stub.StreamObserver; +import org.apache.skywalking.apm.testcase.grpc.proto.GreeterBlockingErrorGrpc; +import org.apache.skywalking.apm.testcase.grpc.proto.HelloReply; +import org.apache.skywalking.apm.testcase.grpc.proto.HelloRequest; + +public class GreeterBlockingErrorServiceImpl extends GreeterBlockingErrorGrpc.GreeterBlockingErrorImplBase { + @Override + public void sayHello(HelloRequest request, StreamObserver responseObserver) { + responseObserver.onError(new Exception()); + } +} diff --git a/test/plugin/scenarios/grpc-1.30.x-1.39.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/grpc/provider/service/GreeterBlockingServiceImpl.java b/test/plugin/scenarios/grpc-1.30.x-1.39.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/grpc/provider/service/GreeterBlockingServiceImpl.java new file mode 100644 index 0000000000..2f156bc62f --- /dev/null +++ b/test/plugin/scenarios/grpc-1.30.x-1.39.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/grpc/provider/service/GreeterBlockingServiceImpl.java @@ -0,0 +1,32 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.testcase.grpc.provider.service; + +import io.grpc.stub.StreamObserver; +import org.apache.skywalking.apm.testcase.grpc.proto.GreeterBlockingGrpc; +import org.apache.skywalking.apm.testcase.grpc.proto.HelloReply; +import org.apache.skywalking.apm.testcase.grpc.proto.HelloRequest; + +public class GreeterBlockingServiceImpl extends GreeterBlockingGrpc.GreeterBlockingImplBase { + @Override + public void sayHello(HelloRequest request, StreamObserver responseObserver) { + responseObserver.onNext(HelloReply.newBuilder().setMessage("Hi," + request.getName()).build()); + responseObserver.onCompleted(); + } +} diff --git a/test/plugin/scenarios/grpc-1.30.x-1.39.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/grpc/provider/service/GreeterServiceImpl.java b/test/plugin/scenarios/grpc-1.30.x-1.39.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/grpc/provider/service/GreeterServiceImpl.java new file mode 100644 index 0000000000..5235d09318 --- /dev/null +++ b/test/plugin/scenarios/grpc-1.30.x-1.39.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/grpc/provider/service/GreeterServiceImpl.java @@ -0,0 +1,52 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.testcase.grpc.provider.service; + +import io.grpc.stub.StreamObserver; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.apache.skywalking.apm.testcase.grpc.proto.GreeterGrpc; +import org.apache.skywalking.apm.testcase.grpc.proto.HelloReply; +import org.apache.skywalking.apm.testcase.grpc.proto.HelloRequest; + +public class GreeterServiceImpl extends GreeterGrpc.GreeterImplBase { + + private static final Logger LOGGER = LogManager.getLogger(GreeterServiceImpl.class); + + @Override + public StreamObserver sayHello(final StreamObserver responseObserver) { + StreamObserver requestStreamObserver = new StreamObserver() { + + public void onNext(HelloRequest request) { + LOGGER.info("Receive an message from client. Message: {}", request.getName()); + responseObserver.onNext(HelloReply.newBuilder().setMessage("Hi," + request.getName()).build()); + } + + public void onError(Throwable throwable) { + responseObserver.onError(throwable); + } + + public void onCompleted() { + LOGGER.info("End the stream."); + responseObserver.onCompleted(); + } + }; + return requestStreamObserver; + } +} diff --git a/test/plugin/scenarios/grpc-1.30.x-1.39.x-scenario/src/main/proto/GreetService.proto b/test/plugin/scenarios/grpc-1.30.x-1.39.x-scenario/src/main/proto/GreetService.proto new file mode 100644 index 0000000000..fd5ba4a8f1 --- /dev/null +++ b/test/plugin/scenarios/grpc-1.30.x-1.39.x-scenario/src/main/proto/GreetService.proto @@ -0,0 +1,43 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +syntax = "proto3"; + +option java_multiple_files = true; +option java_package = "org.apache.skywalking.apm.testcase.grpc.proto"; + +service Greeter { + rpc SayHello (stream HelloRequest) returns (stream HelloReply) { + } +} + +service GreeterBlocking { + rpc SayHello (HelloRequest) returns (HelloReply) { + } +} +service GreeterBlockingError { + rpc SayHello (HelloRequest) returns (HelloReply) { + } +} + +message HelloRequest { + string name = 1; +} + +message HelloReply { + string message = 1; +} \ No newline at end of file diff --git a/test/plugin/scenarios/grpc-1.30.x-1.39.x-scenario/src/main/resources/application.yaml b/test/plugin/scenarios/grpc-1.30.x-1.39.x-scenario/src/main/resources/application.yaml new file mode 100644 index 0000000000..5f9c909a1b --- /dev/null +++ b/test/plugin/scenarios/grpc-1.30.x-1.39.x-scenario/src/main/resources/application.yaml @@ -0,0 +1,23 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# +server: + port: 8080 + servlet: + context-path: /grpc-1.30.x-1.39.x-scenario +logging: + config: classpath:log4j2.xml diff --git a/test/plugin/scenarios/grpc-1.30.x-1.39.x-scenario/src/main/resources/log4j2.xml b/test/plugin/scenarios/grpc-1.30.x-1.39.x-scenario/src/main/resources/log4j2.xml new file mode 100644 index 0000000000..9849ed5a8a --- /dev/null +++ b/test/plugin/scenarios/grpc-1.30.x-1.39.x-scenario/src/main/resources/log4j2.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/plugin/scenarios/grpc-1.30.x-1.39.x-scenario/support-version.list b/test/plugin/scenarios/grpc-1.30.x-1.39.x-scenario/support-version.list new file mode 100644 index 0000000000..5c01dfaf60 --- /dev/null +++ b/test/plugin/scenarios/grpc-1.30.x-1.39.x-scenario/support-version.list @@ -0,0 +1,31 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# INTERNAL: HTTP/2 error code: INTERNAL_ERROR Received Goaway occur in test cases 1.0.0 to 1.5.0 +# So these versions were not included in support-version.list. if you know what caused it, please help us. + +# Contains only the last version number of each minor version + +1.39.0 +1.38.0 +1.37.0 +1.36.0 +1.35.0 +1.34.0 +1.33.0 +1.32.1 +1.31.0 +1.30.0 diff --git a/test/plugin/scenarios/grpc-1.59.x-1.70.x-scenario/bin/startup.sh b/test/plugin/scenarios/grpc-1.59.x-1.70.x-scenario/bin/startup.sh new file mode 100644 index 0000000000..54d5003fe6 --- /dev/null +++ b/test/plugin/scenarios/grpc-1.59.x-1.70.x-scenario/bin/startup.sh @@ -0,0 +1,21 @@ +#!/bin/bash +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +home="$(cd "$(dirname $0)"; pwd)" + +java -jar ${agent_opts} ${home}/../libs/grpc-1.59.x-1.70.x-scenario.jar & diff --git a/test/plugin/scenarios/grpc-1.59.x-1.70.x-scenario/config/expectedData.yaml b/test/plugin/scenarios/grpc-1.59.x-1.70.x-scenario/config/expectedData.yaml new file mode 100644 index 0000000000..2cbf91a2ea --- /dev/null +++ b/test/plugin/scenarios/grpc-1.59.x-1.70.x-scenario/config/expectedData.yaml @@ -0,0 +1,458 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +segmentItems: + - serviceName: grpc-1.59.x-1.70.x-scenario + segmentSize: gt 10 + segments: + - segmentId: not null + spans: + - operationName: GreeterBlocking.sayHello + parentSpanId: -1 + spanId: 0 + spanLayer: RPCFramework + startTime: nq 0 + endTime: nq 0 + componentId: 23 + isError: false + spanType: Entry + peer: '' + refs: + - {parentEndpoint: GET:/grpc-1.59.x-1.70.x-scenario/case/grpc-1.59.x-1.70.x-scenario, networkAddress: '127.0.0.1:18080', + refType: CrossProcess, parentSpanId: 2, parentTraceSegmentId: not null, parentServiceInstance: not + null, parentService: grpc-1.59.x-1.70.x-scenario, traceId: not null} + skipAnalysis: 'false' + - segmentId: not null + spans: + - operationName: Greeter.sayHello/client/Request/onMessage + parentSpanId: -1 + spanId: 0 + spanLayer: RPCFramework + startTime: nq 0 + endTime: nq 0 + componentId: 23 + isError: false + spanType: Local + peer: '' + refs: + - {parentEndpoint: GET:/grpc-1.59.x-1.70.x-scenario/case/grpc-1.59.x-1.70.x-scenario, networkAddress: '', refType: CrossThread, + parentSpanId: 1, parentTraceSegmentId: not null, parentServiceInstance: not + null, parentService: not null, traceId: not null} + skipAnalysis: 'false' + - segmentId: not null + spans: + - operationName: Greeter.sayHello/client/Request/onMessage + parentSpanId: -1 + spanId: 0 + spanLayer: RPCFramework + startTime: nq 0 + endTime: nq 0 + componentId: 23 + isError: false + spanType: Local + peer: '' + refs: + - {parentEndpoint: GET:/grpc-1.59.x-1.70.x-scenario/case/grpc-1.59.x-1.70.x-scenario, networkAddress: '', refType: CrossThread, + parentSpanId: 1, parentTraceSegmentId: not null, parentServiceInstance: not + null, parentService: grpc-1.59.x-1.70.x-scenario, traceId: not null} + skipAnalysis: 'false' + - segmentId: not null + spans: + - operationName: Greeter.sayHello/client/Request/onComplete + parentSpanId: -1 + spanId: 0 + spanLayer: RPCFramework + startTime: nq 0 + endTime: nq 0 + componentId: 23 + isError: false + spanType: Local + peer: '' + refs: + - {parentEndpoint: GET:/grpc-1.59.x-1.70.x-scenario/case/grpc-1.59.x-1.70.x-scenario, networkAddress: '', refType: CrossThread, + parentSpanId: 1, parentTraceSegmentId: not null, parentServiceInstance: not + null, parentService: grpc-1.59.x-1.70.x-scenario, traceId: not null} + skipAnalysis: 'false' + - segmentId: not null + spans: + - operationName: Greeter.sayHello + parentSpanId: -1 + spanId: 0 + spanLayer: RPCFramework + startTime: nq 0 + endTime: nq 0 + componentId: 23 + isError: false + spanType: Entry + peer: '' + refs: + - {parentEndpoint: GET:/grpc-1.59.x-1.70.x-scenario/case/grpc-1.59.x-1.70.x-scenario, networkAddress: '127.0.0.1:18080', + refType: CrossProcess, parentSpanId: 1, parentTraceSegmentId: not null, parentServiceInstance: not + null, parentService: grpc-1.59.x-1.70.x-scenario, traceId: not null} + skipAnalysis: 'false' + - segmentId: not null + spans: + - operationName: Greeter.sayHello/server/Response/onMessage + parentSpanId: 0 + spanId: 1 + spanLayer: RPCFramework + startTime: nq 0 + endTime: nq 0 + componentId: 23 + isError: false + spanType: Local + peer: '' + skipAnalysis: 'false' + - operationName: Greeter.sayHello/server/Request/onMessage + parentSpanId: -1 + spanId: 0 + spanLayer: RPCFramework + startTime: nq 0 + endTime: nq 0 + componentId: 23 + isError: false + spanType: Local + peer: '' + refs: + - {parentEndpoint: Greeter.sayHello, networkAddress: '', refType: CrossThread, + parentSpanId: 0, parentTraceSegmentId: not null, parentServiceInstance: not + null, parentService: not null, traceId: not null} + skipAnalysis: 'false' + - segmentId: not null + spans: + - operationName: Greeter.sayHello/client/Response/onMessage + parentSpanId: -1 + spanId: 0 + spanLayer: RPCFramework + startTime: nq 0 + endTime: nq 0 + componentId: 23 + isError: false + spanType: Local + peer: '' + refs: + - {parentEndpoint: GET:/grpc-1.59.x-1.70.x-scenario/case/grpc-1.59.x-1.70.x-scenario, networkAddress: '', refType: CrossThread, + parentSpanId: 1, parentTraceSegmentId: not null, parentServiceInstance: not + null, parentService: not null, traceId: not null} + skipAnalysis: 'false' + - segmentId: not null + spans: + - operationName: GreeterBlocking.sayHello/server/Response/onClose + parentSpanId: 0 + spanId: 1 + spanLayer: RPCFramework + startTime: nq 0 + endTime: nq 0 + componentId: 23 + isError: false + spanType: Local + peer: '' + tags: + - {key: rpc.status_code, value: OK} + skipAnalysis: 'false' + - operationName: GreeterBlocking.sayHello/server/Request/onHalfClose + parentSpanId: -1 + spanId: 0 + spanLayer: RPCFramework + startTime: nq 0 + endTime: nq 0 + componentId: 23 + isError: false + spanType: Local + peer: '' + refs: + - {parentEndpoint: GreeterBlocking.sayHello, networkAddress: '', refType: CrossThread, + parentSpanId: 0, parentTraceSegmentId: not null, parentServiceInstance: not + null, parentService: grpc-1.59.x-1.70.x-scenario, traceId: not null} + skipAnalysis: 'false' + - segmentId: not null + spans: + - operationName: Greeter.sayHello/server/Response/onMessage + parentSpanId: 0 + spanId: 1 + spanLayer: RPCFramework + startTime: nq 0 + endTime: nq 0 + componentId: 23 + isError: false + spanType: Local + peer: '' + skipAnalysis: 'false' + - operationName: Greeter.sayHello/server/Request/onMessage + parentSpanId: -1 + spanId: 0 + spanLayer: RPCFramework + startTime: nq 0 + endTime: nq 0 + componentId: 23 + isError: false + spanType: Local + peer: '' + refs: + - {parentEndpoint: Greeter.sayHello, networkAddress: '', refType: CrossThread, + parentSpanId: 0, parentTraceSegmentId: not null, parentServiceInstance: not + null, parentService: grpc-1.59.x-1.70.x-scenario, traceId: not null} + skipAnalysis: 'false' + - segmentId: not null + spans: + - operationName: Greeter.sayHello/server/Response/onClose + parentSpanId: 0 + spanId: 1 + spanLayer: RPCFramework + startTime: nq 0 + endTime: nq 0 + componentId: 23 + isError: false + spanType: Local + peer: '' + tags: + - {key: rpc.status_code, value: OK} + skipAnalysis: 'false' + - operationName: Greeter.sayHello/server/Request/onHalfClose + parentSpanId: -1 + spanId: 0 + spanLayer: RPCFramework + startTime: nq 0 + endTime: nq 0 + componentId: 23 + isError: false + spanType: Local + peer: '' + refs: + - {parentEndpoint: Greeter.sayHello, networkAddress: '', refType: CrossThread, + parentSpanId: 0, parentTraceSegmentId: not null, parentServiceInstance: not + null, parentService: grpc-1.59.x-1.70.x-scenario, traceId: not null} + skipAnalysis: 'false' + - segmentId: not null + spans: + - operationName: Greeter.sayHello/client/Response/onMessage + parentSpanId: -1 + spanId: 0 + spanLayer: RPCFramework + startTime: nq 0 + endTime: nq 0 + componentId: 23 + isError: false + spanType: Local + peer: '' + refs: + - {parentEndpoint: GET:/grpc-1.59.x-1.70.x-scenario/case/grpc-1.59.x-1.70.x-scenario, networkAddress: '', refType: CrossThread, + parentSpanId: 1, parentTraceSegmentId: not null, parentServiceInstance: not + null, parentService: grpc-1.59.x-1.70.x-scenario, traceId: not null} + skipAnalysis: 'false' + - segmentId: not null + spans: + - operationName: Greeter.sayHello/client/Response/onClose + parentSpanId: -1 + spanId: 0 + spanLayer: RPCFramework + startTime: nq 0 + endTime: nq 0 + componentId: 23 + isError: false + spanType: Local + peer: '' + refs: + - {parentEndpoint: GET:/grpc-1.59.x-1.70.x-scenario/case/grpc-1.59.x-1.70.x-scenario, networkAddress: '', refType: CrossThread, + parentSpanId: 1, parentTraceSegmentId: not null, parentServiceInstance: not + null, parentService: grpc-1.59.x-1.70.x-scenario, traceId: not null} + skipAnalysis: 'false' + - segmentId: not null + spans: + - operationName: GreeterBlockingError.sayHello + parentSpanId: -1 + spanId: 0 + spanLayer: RPCFramework + startTime: nq 0 + endTime: nq 0 + componentId: 23 + isError: false + spanType: Entry + peer: '' + refs: + - {parentEndpoint: GET:/grpc-1.59.x-1.70.x-scenario/case/grpc-1.59.x-1.70.x-scenario, networkAddress: '127.0.0.1:18080', + refType: CrossProcess, parentSpanId: 5, parentTraceSegmentId: not null, parentServiceInstance: not + null, parentService: grpc-1.59.x-1.70.x-scenario, traceId: not null} + skipAnalysis: 'false' + - segmentId: not null + spans: + - operationName: GreeterBlockingError.sayHello/server/Response/onClose + parentSpanId: 0 + spanId: 1 + spanLayer: RPCFramework + startTime: nq 0 + endTime: nq 0 + componentId: 23 + isError: true + spanType: Local + peer: '' + tags: + - {key: rpc.status_code, value: UNKNOWN} + logs: + - logEvent: + - {key: event, value: error} + - {key: error.kind, value: java.lang.Exception} + - {key: message, value: ''} + - {key: stack, value: not null} + skipAnalysis: 'false' + - operationName: GreeterBlockingError.sayHello/server/Request/onHalfClose + parentSpanId: -1 + spanId: 0 + spanLayer: RPCFramework + startTime: nq 0 + endTime: nq 0 + componentId: 23 + isError: false + spanType: Local + peer: '' + refs: + - {parentEndpoint: GreeterBlockingError.sayHello, networkAddress: '', refType: CrossThread, + parentSpanId: 0, parentTraceSegmentId: not null, parentServiceInstance: not + null, parentService: grpc-1.59.x-1.70.x-scenario, traceId: not null} + skipAnalysis: 'false' + - segmentId: not null + spans: + - operationName: Greeter.sayHello + parentSpanId: 0 + spanId: 1 + spanLayer: RPCFramework + startTime: nq 0 + endTime: nq 0 + componentId: 23 + isError: false + spanType: Exit + peer: '127.0.0.1:18080' + skipAnalysis: 'false' + - operationName: GreeterBlocking.sayHello/client/Request/onComplete + parentSpanId: 2 + spanId: 3 + spanLayer: RPCFramework + startTime: nq 0 + endTime: nq 0 + componentId: 23 + isError: false + spanType: Local + peer: '' + skipAnalysis: 'false' + - operationName: GreeterBlocking.sayHello/client/Response/onClose + parentSpanId: 2 + spanId: 4 + spanLayer: RPCFramework + startTime: nq 0 + endTime: nq 0 + componentId: 23 + isError: false + spanType: Local + peer: '' + skipAnalysis: 'false' + - operationName: GreeterBlocking.sayHello + parentSpanId: 0 + panId: 2 + spanLayer: RPCFramework + startTime: nq 0 + endTime: nq 0 + componentId: 23 + isError: false + spanType: Exit + peer: '127.0.0.1:18080' + skipAnalysis: 'false' + - operationName: GreeterBlockingError.sayHello/client/Request/onComplete + parentSpanId: 5 + spanId: 6 + spanLayer: RPCFramework + startTime: nq 0 + endTime: nq 0 + componentId: 23 + isError: false + spanType: Local + peer: '' + skipAnalysis: 'false' + - operationName: GreeterBlockingError.sayHello/client/Response/onClose + parentSpanId: 5 + spanId: 7 + spanLayer: RPCFramework + startTime: nq 0 + endTime: nq 0 + componentId: 23 + isError: true + spanType: Local + peer: '' + tags: + - {key: rpc.status_code, value: UNKNOWN} + logs: + - logEvent: + - {key: event, value: error} + - {key: error.kind, value: io.grpc.StatusRuntimeException} + - {key: message, value: UNKNOWN} + - {key: stack, value: not null} + skipAnalysis: 'false' + - operationName: GreeterBlockingError.sayHello/client/Request/onCancel + parentSpanId: 5 + spanId: 8 + spanLayer: RPCFramework + startTime: nq 0 + endTime: nq 0 + componentId: 23 + isError: true + spanType: Local + peer: '' + logs: + - logEvent: + - {key: event, value: error} + - {key: error.kind, value: io.grpc.StatusRuntimeException} + - {key: message, value: UNKNOWN} + - {key: stack, value: not null} + skipAnalysis: 'false' + - operationName: GreeterBlockingError.sayHello + parentSpanId: 0 + spanId: 5 + spanLayer: RPCFramework + startTime: nq 0 + endTime: nq 0 + componentId: 23 + isError: true + spanType: Exit + peer: 127.0.0.1:18080 + logs: + - logEvent: + - {key: event, value: error} + - {key: error.kind, value: io.grpc.StatusRuntimeException} + - {key: message, value: UNKNOWN} + - {key: stack, value: not null} + skipAnalysis: 'false' + - operationName: GET:/grpc-1.59.x-1.70.x-scenario/case/grpc-1.59.x-1.70.x-scenario + parentSpanId: -1 + spanId: 0 + spanLayer: Http + startTime: nq 0 + endTime: nq 0 + componentId: 1 + isError: true + spanType: Entry + peer: '' + tags: + - {key: url, value: 'http://localhost:8080/grpc-1.59.x-1.70.x-scenario/case/grpc-1.59.x-1.70.x-scenario'} + - {key: http.method, value: GET} + - {key: http.status_code, value: '500'} + logs: + - logEvent: + - {key: event, value: error} + - {key: error.kind, value: org.springframework.web.util.NestedServletException} + - {key: message, value: 'Request processing failed; nested exception is io.grpc.StatusRuntimeException: + UNKNOWN'} + - {key: stack, value: not null} + - logEvent: + - {key: forward-url, value: /grpc-1.59.x-1.70.x-scenario/error} + skipAnalysis: 'false' diff --git a/test/plugin/scenarios/grpc-1.59.x-1.70.x-scenario/configuration.yml b/test/plugin/scenarios/grpc-1.59.x-1.70.x-scenario/configuration.yml new file mode 100644 index 0000000000..858c323d01 --- /dev/null +++ b/test/plugin/scenarios/grpc-1.59.x-1.70.x-scenario/configuration.yml @@ -0,0 +1,22 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +type: jvm +entryService: http://localhost:8080/grpc-1.59.x-1.70.x-scenario/case/grpc-1.59.x-1.70.x-scenario +healthCheck: http://localhost:8080/grpc-1.59.x-1.70.x-scenario/case/healthCheck +startScript: ./bin/startup.sh +environment: +dependencies: diff --git a/test/plugin/scenarios/grpc-1.59.x-1.70.x-scenario/pom.xml b/test/plugin/scenarios/grpc-1.59.x-1.70.x-scenario/pom.xml new file mode 100644 index 0000000000..0794a1cb21 --- /dev/null +++ b/test/plugin/scenarios/grpc-1.59.x-1.70.x-scenario/pom.xml @@ -0,0 +1,163 @@ + + + + + org.apache.skywalking.apm.testcase + grpc-1.59.x-1.70.x-scenario + 1.0.0 + jar + + 4.0.0 + + + UTF-8 + 1.8 + 3.8.1 + 1.6.2 + + 1.59.0 + + 2.1.6.RELEASE + + + skywalking-grpc-1.59.x-1.70.x-scenario + + + + + org.springframework.boot + spring-boot-dependencies + ${spring.boot.version} + pom + import + + + + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-logging + + + + + org.springframework.boot + spring-boot-starter-log4j2 + + + + + io.grpc + grpc-all + ${test.framework.version} + + + + + + grpc-1.59.x-1.70.x-scenario + + + kr.motd.maven + os-maven-plugin + ${os-maven-plugin.version} + + + initialize + + detect + + + + + + org.springframework.boot + spring-boot-maven-plugin + ${spring.boot.version} + + + + repackage + + + + + + maven-compiler-plugin + ${maven-compiler-plugin.version} + + ${compiler.version} + ${compiler.version} + ${project.build.sourceEncoding} + + + + org.apache.maven.plugins + maven-assembly-plugin + + + assemble + package + + single + + + + src/main/assembly/assembly.xml + + ./target/ + + + + + + org.xolstice.maven.plugins + protobuf-maven-plugin + 0.6.1 + + + com.google.protobuf:protoc:3.23.4:exe:${os.detected.classifier} + + grpc-java + io.grpc:protoc-gen-grpc-java:${test.framework.version}:exe:${os.detected.classifier} + + + + + + compile + compile-custom + + + + + + + diff --git a/test/plugin/scenarios/grpc-1.59.x-1.70.x-scenario/src/main/assembly/assembly.xml b/test/plugin/scenarios/grpc-1.59.x-1.70.x-scenario/src/main/assembly/assembly.xml new file mode 100644 index 0000000000..11d5b1f62c --- /dev/null +++ b/test/plugin/scenarios/grpc-1.59.x-1.70.x-scenario/src/main/assembly/assembly.xml @@ -0,0 +1,41 @@ + + + + + zip + + + + + ./bin + 0775 + + + + + + ${project.build.directory}/grpc-1.59.x-1.70.x-scenario.jar + ./libs + 0775 + + + diff --git a/test/plugin/scenarios/grpc-1.59.x-1.70.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/grpc/Application.java b/test/plugin/scenarios/grpc-1.59.x-1.70.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/grpc/Application.java new file mode 100644 index 0000000000..052e0f7e3b --- /dev/null +++ b/test/plugin/scenarios/grpc-1.59.x-1.70.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/grpc/Application.java @@ -0,0 +1,34 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.testcase.grpc; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class Application { + + public static void main(String[] args) { + try { + SpringApplication.run(Application.class, args); + } catch (Exception e) { + // Never do this + } + } +} diff --git a/test/plugin/scenarios/grpc-1.59.x-1.70.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/grpc/consumr/ConsumerInterceptor.java b/test/plugin/scenarios/grpc-1.59.x-1.70.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/grpc/consumr/ConsumerInterceptor.java new file mode 100644 index 0000000000..e8a35d0359 --- /dev/null +++ b/test/plugin/scenarios/grpc-1.59.x-1.70.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/grpc/consumr/ConsumerInterceptor.java @@ -0,0 +1,109 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.testcase.grpc.consumr; + +import io.grpc.CallOptions; +import io.grpc.Channel; +import io.grpc.ClientCall; +import io.grpc.ClientInterceptor; +import io.grpc.ForwardingClientCall; +import io.grpc.ForwardingClientCallListener; +import io.grpc.Metadata; +import io.grpc.MethodDescriptor; +import io.grpc.Status; +import javax.annotation.Nullable; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public class ConsumerInterceptor implements ClientInterceptor { + + private static final Logger LOGGER = LogManager.getLogger(ConsumerInterceptor.class); + + @Override + public ClientCall interceptCall(MethodDescriptor descriptor, + CallOptions options, Channel channel) { + LOGGER.info("start interceptor!"); + LOGGER.info("method type: {}", descriptor.getType()); + return new ForwardingClientCall.SimpleForwardingClientCall(channel.newCall(descriptor, options)) { + @Override + public void start(Listener responseListener, Metadata headers) { + LOGGER.info("Peer: {}", channel.authority()); + LOGGER.info("Operation Name : {}", descriptor.getFullMethodName()); + Interceptor tracingResponseListener = new Interceptor(responseListener); + tracingResponseListener.contextSnapshot = "contextSnapshot"; + delegate().start(tracingResponseListener, headers); + } + + @Override + public void cancel(@Nullable String message, @Nullable Throwable cause) { + LOGGER.info("cancel"); + super.cancel(message, cause); + } + + @Override + public void halfClose() { + LOGGER.info("halfClose"); + super.halfClose(); + } + + @Override + public void sendMessage(REQ_T message) { + LOGGER.info("sendMessage ...."); + super.sendMessage(message); + } + }; + } + + private static class Interceptor extends ForwardingClientCallListener.SimpleForwardingClientCallListener { + private static final Logger LOGGER = LogManager.getLogger(Interceptor.class); + + private Object contextSnapshot; + + protected Interceptor(ClientCall.Listener delegate) { + super(delegate); + } + + @Override + public void onHeaders(Metadata headers) { + LOGGER.info("on Headers"); + for (String key : headers.keys()) { + LOGGER.info("Receive key: {}", key); + } + delegate().onHeaders(headers); + } + + @Override + public void onMessage(RESP_T message) { + LOGGER.info("contextSnapshot: {}", contextSnapshot); + delegate().onMessage(message); + } + + @Override + public void onClose(Status status, Metadata trailers) { + LOGGER.info("on close"); + delegate().onClose(status, trailers); + } + + @Override + public void onReady() { + LOGGER.info("on Ready"); + super.onReady(); + } + } +} diff --git a/test/plugin/scenarios/grpc-1.59.x-1.70.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/grpc/controller/CaseController.java b/test/plugin/scenarios/grpc-1.59.x-1.70.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/grpc/controller/CaseController.java new file mode 100644 index 0000000000..2a26b99e96 --- /dev/null +++ b/test/plugin/scenarios/grpc-1.59.x-1.70.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/grpc/controller/CaseController.java @@ -0,0 +1,137 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.testcase.grpc.controller; + +import io.grpc.ClientInterceptors; +import io.grpc.ManagedChannel; +import io.grpc.ManagedChannelBuilder; +import io.grpc.stub.ClientCallStreamObserver; +import io.grpc.stub.ClientResponseObserver; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; +import javax.annotation.PostConstruct; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.apache.skywalking.apm.testcase.grpc.consumr.ConsumerInterceptor; +import org.apache.skywalking.apm.testcase.grpc.proto.GreeterBlockingErrorGrpc; +import org.apache.skywalking.apm.testcase.grpc.proto.GreeterBlockingGrpc; +import org.apache.skywalking.apm.testcase.grpc.proto.GreeterGrpc; +import org.apache.skywalking.apm.testcase.grpc.proto.HelloRequest; +import org.apache.skywalking.apm.testcase.grpc.proto.HelloReply; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/case") +public class CaseController { + + private static final Logger LOGGER = LogManager.getLogger(CaseController.class); + + private static final String SUCCESS = "Success"; + + private final String grpcProviderHost = "127.0.0.1"; + private final int grpcProviderPort = 18080; + private ManagedChannel channel; + private GreeterGrpc.GreeterStub greeterStub; + private GreeterBlockingGrpc.GreeterBlockingBlockingStub greeterBlockingStub; + private GreeterBlockingErrorGrpc.GreeterBlockingErrorBlockingStub greeterBlockingErrorStub; + + @PostConstruct + public void up() { + channel = ManagedChannelBuilder.forAddress(grpcProviderHost, grpcProviderPort).usePlaintext().build(); + greeterStub = GreeterGrpc.newStub(ClientInterceptors.intercept(channel, new ConsumerInterceptor())); + greeterBlockingStub = GreeterBlockingGrpc.newBlockingStub(ClientInterceptors.intercept(channel, new ConsumerInterceptor())); + greeterBlockingErrorStub = GreeterBlockingErrorGrpc.newBlockingStub(ClientInterceptors.intercept(channel, new ConsumerInterceptor())); + } + + @RequestMapping("/grpc-1.59.x-1.70.x-scenario") + @ResponseBody + public String testcase() { + greetService(); + greetBlockingService(); + greetBlockingErrorService(); + return SUCCESS; + } + + @RequestMapping("/healthCheck") + @ResponseBody + public String healthCheck() { + // your codes + return SUCCESS; + } + + private static List names() { + return Arrays.asList("Sophia", "Jackson"); + } + + private void greetService() { + ClientResponseObserver helloReplyStreamObserver = new ClientResponseObserver() { + private ClientCallStreamObserver requestStream; + + @Override + public void beforeStart(ClientCallStreamObserver observer) { + this.requestStream = observer; + this.requestStream.setOnReadyHandler(new Runnable() { + Iterator iterator = names().iterator(); + + @Override + public void run() { + while (requestStream.isReady()) { + if (iterator.hasNext()) { + String name = iterator.next(); + HelloRequest request = HelloRequest.newBuilder().setName(name).build(); + requestStream.onNext(request); + } else { + requestStream.onCompleted(); + } + } + } + }); + } + + @Override + public void onNext(HelloReply reply) { + LOGGER.info("Receive an message from provider. message: {}", reply.getMessage()); + requestStream.request(1); + } + + public void onError(Throwable throwable) { + LOGGER.error("Failed to send data", throwable); + } + + public void onCompleted() { + LOGGER.info("All Done"); + } + }; + + greeterStub.sayHello(helloReplyStreamObserver); + } + + private void greetBlockingService() { + HelloRequest request = HelloRequest.newBuilder().setName("Sophia").build(); + greeterBlockingStub.sayHello(request); + } + + private void greetBlockingErrorService() { + HelloRequest request = HelloRequest.newBuilder().setName("Tony").build(); + greeterBlockingErrorStub.sayHello(request); + } +} diff --git a/test/plugin/scenarios/grpc-1.59.x-1.70.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/grpc/provider/ProviderConfiguration.java b/test/plugin/scenarios/grpc-1.59.x-1.70.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/grpc/provider/ProviderConfiguration.java new file mode 100644 index 0000000000..8541c403b1 --- /dev/null +++ b/test/plugin/scenarios/grpc-1.59.x-1.70.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/grpc/provider/ProviderConfiguration.java @@ -0,0 +1,44 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.testcase.grpc.provider; + +import io.grpc.Server; +import io.grpc.ServerBuilder; +import io.grpc.ServerInterceptors; +import org.apache.skywalking.apm.testcase.grpc.provider.interceptor.ProviderInterceptor; +import org.apache.skywalking.apm.testcase.grpc.provider.service.GreeterBlockingErrorServiceImpl; +import org.apache.skywalking.apm.testcase.grpc.provider.service.GreeterBlockingServiceImpl; +import org.apache.skywalking.apm.testcase.grpc.provider.service.GreeterServiceImpl; +import org.springframework.beans.factory.annotation.Configurable; +import org.springframework.context.annotation.Bean; +import org.springframework.stereotype.Component; + +@Configurable +@Component +public class ProviderConfiguration { + + @Bean(initMethod = "start", destroyMethod = "shutdown") + public Server server() { + return ServerBuilder.forPort(18080) + .addService(ServerInterceptors.intercept(new GreeterServiceImpl(), new ProviderInterceptor())) + .addService(ServerInterceptors.intercept(new GreeterBlockingServiceImpl(), new ProviderInterceptor())) + .addService(ServerInterceptors.intercept(new GreeterBlockingErrorServiceImpl(), new ProviderInterceptor())) + .build(); + } +} diff --git a/test/plugin/scenarios/grpc-1.59.x-1.70.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/grpc/provider/interceptor/ProviderInterceptor.java b/test/plugin/scenarios/grpc-1.59.x-1.70.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/grpc/provider/interceptor/ProviderInterceptor.java new file mode 100644 index 0000000000..26647b41a8 --- /dev/null +++ b/test/plugin/scenarios/grpc-1.59.x-1.70.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/grpc/provider/interceptor/ProviderInterceptor.java @@ -0,0 +1,96 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.testcase.grpc.provider.interceptor; + +import io.grpc.ForwardingServerCall; +import io.grpc.ForwardingServerCallListener; +import io.grpc.Metadata; +import io.grpc.ServerCall; +import io.grpc.ServerCallHandler; +import io.grpc.ServerInterceptor; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public class ProviderInterceptor implements ServerInterceptor { + private static final Logger LOGGER = LogManager.getLogger(ProviderInterceptor.class); + + @Override + public ServerCall.Listener interceptCall(ServerCall call, Metadata metadata, + ServerCallHandler handler) { + Map headerMap = new HashMap(); + for (String key : metadata.keys()) { + LOGGER.info("Receive key: {}", key); + if (!key.endsWith(Metadata.BINARY_HEADER_SUFFIX)) { + String value = metadata.get(Metadata.Key.of(key, Metadata.ASCII_STRING_MARSHALLER)); + + headerMap.put(key, value); + } + } + LOGGER.info("authority : {}", call.getAuthority()); + return new ForwardingServerCallListener.SimpleForwardingServerCallListener(handler.startCall(new ForwardingServerCall.SimpleForwardingServerCall(call) { + @Override + public void sendHeaders(Metadata responseHeaders) { + LOGGER.info("sendHeaders...."); + Metadata.Key headerKey = Metadata.Key.of("test-server", Metadata.ASCII_STRING_MARSHALLER); + responseHeaders.put(headerKey, "test-server"); + delegate().sendHeaders(responseHeaders); + } + + @Override + public void sendMessage(RESQ_T message) { + delegate().sendMessage(message); + } + + }, metadata)) { + @Override + public void onReady() { + LOGGER.info("onReady...."); + delegate().onReady(); + } + + @Override + public void onCancel() { + LOGGER.info("onCancel...."); + delegate().onCancel(); + } + + @Override + public void onComplete() { + LOGGER.info("onComplete...."); + delegate().onComplete(); + } + + @Override + public void onHalfClose() { + LOGGER.info("onHalfClose...."); + delegate().onHalfClose(); + } + + @Override + public void onMessage(REQ_T message) { + LOGGER.info("onMessage...."); + delegate().onMessage(message); + } + }; + } +} diff --git a/test/plugin/scenarios/grpc-1.59.x-1.70.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/grpc/provider/service/GreeterBlockingErrorServiceImpl.java b/test/plugin/scenarios/grpc-1.59.x-1.70.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/grpc/provider/service/GreeterBlockingErrorServiceImpl.java new file mode 100644 index 0000000000..3cd6394a48 --- /dev/null +++ b/test/plugin/scenarios/grpc-1.59.x-1.70.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/grpc/provider/service/GreeterBlockingErrorServiceImpl.java @@ -0,0 +1,31 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.testcase.grpc.provider.service; + +import io.grpc.stub.StreamObserver; +import org.apache.skywalking.apm.testcase.grpc.proto.GreeterBlockingErrorGrpc; +import org.apache.skywalking.apm.testcase.grpc.proto.HelloReply; +import org.apache.skywalking.apm.testcase.grpc.proto.HelloRequest; + +public class GreeterBlockingErrorServiceImpl extends GreeterBlockingErrorGrpc.GreeterBlockingErrorImplBase { + @Override + public void sayHello(HelloRequest request, StreamObserver responseObserver) { + responseObserver.onError(new Exception()); + } +} diff --git a/test/plugin/scenarios/grpc-1.59.x-1.70.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/grpc/provider/service/GreeterBlockingServiceImpl.java b/test/plugin/scenarios/grpc-1.59.x-1.70.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/grpc/provider/service/GreeterBlockingServiceImpl.java new file mode 100644 index 0000000000..2f156bc62f --- /dev/null +++ b/test/plugin/scenarios/grpc-1.59.x-1.70.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/grpc/provider/service/GreeterBlockingServiceImpl.java @@ -0,0 +1,32 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.testcase.grpc.provider.service; + +import io.grpc.stub.StreamObserver; +import org.apache.skywalking.apm.testcase.grpc.proto.GreeterBlockingGrpc; +import org.apache.skywalking.apm.testcase.grpc.proto.HelloReply; +import org.apache.skywalking.apm.testcase.grpc.proto.HelloRequest; + +public class GreeterBlockingServiceImpl extends GreeterBlockingGrpc.GreeterBlockingImplBase { + @Override + public void sayHello(HelloRequest request, StreamObserver responseObserver) { + responseObserver.onNext(HelloReply.newBuilder().setMessage("Hi," + request.getName()).build()); + responseObserver.onCompleted(); + } +} diff --git a/test/plugin/scenarios/grpc-1.59.x-1.70.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/grpc/provider/service/GreeterServiceImpl.java b/test/plugin/scenarios/grpc-1.59.x-1.70.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/grpc/provider/service/GreeterServiceImpl.java new file mode 100644 index 0000000000..5235d09318 --- /dev/null +++ b/test/plugin/scenarios/grpc-1.59.x-1.70.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/grpc/provider/service/GreeterServiceImpl.java @@ -0,0 +1,52 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.testcase.grpc.provider.service; + +import io.grpc.stub.StreamObserver; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.apache.skywalking.apm.testcase.grpc.proto.GreeterGrpc; +import org.apache.skywalking.apm.testcase.grpc.proto.HelloReply; +import org.apache.skywalking.apm.testcase.grpc.proto.HelloRequest; + +public class GreeterServiceImpl extends GreeterGrpc.GreeterImplBase { + + private static final Logger LOGGER = LogManager.getLogger(GreeterServiceImpl.class); + + @Override + public StreamObserver sayHello(final StreamObserver responseObserver) { + StreamObserver requestStreamObserver = new StreamObserver() { + + public void onNext(HelloRequest request) { + LOGGER.info("Receive an message from client. Message: {}", request.getName()); + responseObserver.onNext(HelloReply.newBuilder().setMessage("Hi," + request.getName()).build()); + } + + public void onError(Throwable throwable) { + responseObserver.onError(throwable); + } + + public void onCompleted() { + LOGGER.info("End the stream."); + responseObserver.onCompleted(); + } + }; + return requestStreamObserver; + } +} diff --git a/test/plugin/scenarios/grpc-1.59.x-1.70.x-scenario/src/main/proto/GreetService.proto b/test/plugin/scenarios/grpc-1.59.x-1.70.x-scenario/src/main/proto/GreetService.proto new file mode 100644 index 0000000000..fd5ba4a8f1 --- /dev/null +++ b/test/plugin/scenarios/grpc-1.59.x-1.70.x-scenario/src/main/proto/GreetService.proto @@ -0,0 +1,43 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +syntax = "proto3"; + +option java_multiple_files = true; +option java_package = "org.apache.skywalking.apm.testcase.grpc.proto"; + +service Greeter { + rpc SayHello (stream HelloRequest) returns (stream HelloReply) { + } +} + +service GreeterBlocking { + rpc SayHello (HelloRequest) returns (HelloReply) { + } +} +service GreeterBlockingError { + rpc SayHello (HelloRequest) returns (HelloReply) { + } +} + +message HelloRequest { + string name = 1; +} + +message HelloReply { + string message = 1; +} \ No newline at end of file diff --git a/test/plugin/scenarios/grpc-1.59.x-1.70.x-scenario/src/main/resources/application.yaml b/test/plugin/scenarios/grpc-1.59.x-1.70.x-scenario/src/main/resources/application.yaml new file mode 100644 index 0000000000..37d6d01659 --- /dev/null +++ b/test/plugin/scenarios/grpc-1.59.x-1.70.x-scenario/src/main/resources/application.yaml @@ -0,0 +1,23 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# +server: + port: 8080 + servlet: + context-path: /grpc-1.59.x-1.70.x-scenario +logging: + config: classpath:log4j2.xml diff --git a/test/plugin/scenarios/grpc-1.59.x-1.70.x-scenario/src/main/resources/log4j2.xml b/test/plugin/scenarios/grpc-1.59.x-1.70.x-scenario/src/main/resources/log4j2.xml new file mode 100644 index 0000000000..9849ed5a8a --- /dev/null +++ b/test/plugin/scenarios/grpc-1.59.x-1.70.x-scenario/src/main/resources/log4j2.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/plugin/scenarios/grpc-1.59.x-1.70.x-scenario/support-version.list b/test/plugin/scenarios/grpc-1.59.x-1.70.x-scenario/support-version.list new file mode 100644 index 0000000000..dd10211b8e --- /dev/null +++ b/test/plugin/scenarios/grpc-1.59.x-1.70.x-scenario/support-version.list @@ -0,0 +1,35 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# INTERNAL: HTTP/2 error code: INTERNAL_ERROR Received Goaway occur in test cases 1.0.0 to 1.5.0 +# So these versions were not included in support-version.list. if you know what caused it, please help us. + +# Contains only the last version number of each minor version + +1.73.0 +1.72.0 +1.71.0 +1.70.0 +1.69.0 +1.68.0 +1.67.1 +1.66.0 +1.65.0 +1.64.0 +1.63.0 +1.62.2 +1.61.0 +1.60.0 diff --git a/test/plugin/scenarios/h2-scenario/config/expectedData.yaml b/test/plugin/scenarios/h2-scenario/config/expectedData.yaml index d06c487e8a..c1420faf08 100644 --- a/test/plugin/scenarios/h2-scenario/config/expectedData.yaml +++ b/test/plugin/scenarios/h2-scenario/config/expectedData.yaml @@ -98,7 +98,7 @@ segmentItems: skipAnalysis: 'false' meterItems: - serviceName: h2-scenario - meterSize: 5 + meterSize: ge 5 meters: - meterId: name: thread_pool diff --git a/test/plugin/scenarios/hikaricp-scenario/config/expectedData.yaml b/test/plugin/scenarios/hikaricp-scenario/config/expectedData.yaml index 75106a2a57..fbecf97a0a 100644 --- a/test/plugin/scenarios/hikaricp-scenario/config/expectedData.yaml +++ b/test/plugin/scenarios/hikaricp-scenario/config/expectedData.yaml @@ -206,7 +206,7 @@ segmentItems: - {key: http.status_code, value: '200'} meterItems: - serviceName: hikaricp-scenario - meterSize: 15 + meterSize: ge 15 meters: - meterId: name: datasource diff --git a/test/plugin/scenarios/impala-jdbc-2.6.x-scenario/config/expectedData.yaml b/test/plugin/scenarios/impala-jdbc-2.6.x-scenario/config/expectedData.yaml deleted file mode 100644 index c6bcc6455e..0000000000 --- a/test/plugin/scenarios/impala-jdbc-2.6.x-scenario/config/expectedData.yaml +++ /dev/null @@ -1,117 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -segmentItems: - - serviceName: impala-jdbc-2.6.x-scenario - segmentSize: ge 1 - segments: - - segmentId: not null - spans: - - operationName: Impala/JDBC/Statement/execute - parentSpanId: 0 - spanId: 1 - spanLayer: Database - startTime: nq 0 - endTime: nq 0 - componentId: 133 - isError: false - spanType: Exit - peer: impala-server:21050 - skipAnalysis: 'false' - tags: - - {key: db.type, value: Impala} - - { key: db.instance, value: '' } - - key: db.statement - value: "CREATE TABLE IF NOT EXISTS default.impala_test (test_id BIGINT, test_name STRING);" - - operationName: Impala/JDBC/Statement/execute - parentSpanId: 0 - spanId: 2 - spanLayer: Database - startTime: nq 0 - endTime: nq 0 - componentId: 133 - isError: false - spanType: Exit - peer: impala-server:21050 - skipAnalysis: 'false' - tags: - - {key: db.type, value: Impala} - - { key: db.instance, value: '' } - - key: db.statement - value: "INSERT INTO impala_test VALUES (123, 'test');" - - operationName: Impala/JDBC/PreparedStatement/executeQuery - parentSpanId: 0 - spanId: 3 - spanLayer: Database - startTime: nq 0 - endTime: nq 0 - componentId: 133 - isError: false - spanType: Exit - peer: impala-server:21050 - skipAnalysis: 'false' - tags: - - {key: db.type, value: Impala} - - { key: db.instance, value: '' } - - key: db.statement - value: "SELECT COUNT(*) FROM impala_test;" - - operationName: Impala/JDBC/PreparedStatement/executeQuery - parentSpanId: 0 - spanId: 4 - spanLayer: Database - startTime: nq 0 - endTime: nq 0 - componentId: 133 - isError: false - spanType: Exit - peer: impala-server:21050 - skipAnalysis: 'false' - tags: - - {key: db.type, value: Impala} - - { key: db.instance, value: '' } - - key: db.statement - value: 'SELECT COUNT(*) FROM impala_test WHERE test_id = ?;' - - { key: db.sql.parameters, value: '[123]'} - - operationName: Impala/JDBC/Connection/close - parentSpanId: 0 - spanId: 5 - spanLayer: Database - startTime: nq 0 - endTime: nq 0 - componentId: 133 - isError: false - spanType: Exit - peer: impala-server:21050 - skipAnalysis: 'false' - tags: - - {key: db.type, value: Impala} - - { key: db.instance, value: '' } - - { key: db.statement, value: '' } - - operationName: HEAD:/impala-jdbc-2.6.x-scenario/case/healthCheck - parentSpanId: -1 - spanId: 0 - startTime: nq 0 - endTime: nq 0 - spanLayer: Http - isError: false - spanType: Entry - peer: '' - componentId: 1 - tags: - - { key: url, value: 'http://localhost:8080/impala-jdbc-2.6.x-scenario/case/healthCheck' } - - { key: http.method, value: HEAD } - - { key: http.status_code, value: '200' } - logs: [ ] - skipAnalysis: 'false' diff --git a/test/plugin/scenarios/impala-jdbc-2.6.x-scenario/configuration.yml b/test/plugin/scenarios/impala-jdbc-2.6.x-scenario/configuration.yml deleted file mode 100644 index af968fbcd9..0000000000 --- a/test/plugin/scenarios/impala-jdbc-2.6.x-scenario/configuration.yml +++ /dev/null @@ -1,81 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -type: jvm -entryService: http://localhost:8080/impala-jdbc-2.6.x-scenario/case/impala-jdbc-2.6.x-scenario -healthCheck: http://localhost:8080/impala-jdbc-2.6.x-scenario/case/healthCheck -startScript: ./bin/startup.sh -environment: -depends_on: - - impala-server -dependencies: - postgres: - hostname: postgres - image: parrotstream/postgres:10.5 - environment: - - POSTGRES_PASSWORD=postgres - expose: - - 5432 - zookeeper: - hostname: zookeeper - image: parrotstream/zookeeper:latest - expose: - - 2181 - - 2888 - - 3888 - hadoop: - hostname: hadoop - image: parrotstream/hadoop:3.0.3 - links: - - zookeeper - expose: - - 9870 - - 9864 - - 9820 - - 8042 - - 8088 - - 8188 - - 19888 - hive: - hostname: hive - image: parrotstream/hive:1.1.0-cdh5.11.1 - environment: - - PGPASSWORD=postgres - links: - - hadoop - - zookeeper - - postgres - expose: - - 10000 - - 10001 - - 10002 - - 10003 - - 9083 - - 50111 - - 9999 - impala-server: - hostname: impala-server - image: parrotstream/impala:latest - links: - - hadoop - - hive - - zookeeper - expose: - - 21000 - - 21050 - - 25000 - - 25010 - - 25020 \ No newline at end of file diff --git a/test/plugin/scenarios/impala-jdbc-2.6.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/impalajdbc/controller/CaseController.java b/test/plugin/scenarios/impala-jdbc-2.6.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/impalajdbc/controller/CaseController.java deleted file mode 100644 index dca9840eb4..0000000000 --- a/test/plugin/scenarios/impala-jdbc-2.6.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/impalajdbc/controller/CaseController.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package org.apache.skywalking.apm.testcase.impalajdbc.controller; - -import lombok.extern.log4j.Log4j2; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.apache.skywalking.apm.testcase.impalajdbc.SQLExecutor; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.ResponseBody; -import org.springframework.web.bind.annotation.RestController; - -import java.io.IOException; -import java.net.InetSocketAddress; -import java.net.Socket; - -@RestController -@RequestMapping("/case") -@Log4j2 -public class CaseController { - - private static final Logger LOGGER = LogManager.getLogger(CaseController.class); - - private static final String SUCCESS = "Success"; - - private static final String STATEMENT_INSERT_DATA_SQL = "INSERT INTO impala_test VALUES (123, 'test');"; - private static final String STATEMENT_QUERY_DATA_SQL = "SELECT COUNT(*) FROM impala_test;"; - private static final String STATEMENT_QUERY_DATA_SQL_PARAM = "SELECT COUNT(*) FROM impala_test WHERE test_id = ?;"; - - @RequestMapping("/impala-jdbc-2.6.x-scenario") - @ResponseBody - public String testcase() throws Exception { - Thread.sleep(2000); - return SUCCESS; - } - - @RequestMapping("/healthCheck") - @ResponseBody - public String healthCheck() throws Exception { - if (!telnet("impala-server", 21050, 1000)) { // - Thread.sleep(5000); // WAIT UTIL CLIENT TIMEOUT - } else { - try (SQLExecutor sqlExecute = new SQLExecutor()) { - sqlExecute.execute(STATEMENT_INSERT_DATA_SQL); - sqlExecute.queryData(STATEMENT_QUERY_DATA_SQL); - sqlExecute.queryData(STATEMENT_QUERY_DATA_SQL_PARAM, 123); - } catch (Exception ex) { - LOGGER.error("Failed to execute sql.", ex); - throw ex; - } - } - return SUCCESS; - } - - private boolean telnet(String hostname, int port, int timeout) { - Socket socket = new Socket(); - boolean isConnected = false; - try { - socket.connect(new InetSocketAddress(hostname, port), timeout); - isConnected = socket.isConnected(); - } catch (IOException e) { - LOGGER.warn("connect to impala server failed"); - } finally { - try { - socket.close(); - } catch (IOException e) { - LOGGER.warn("close failed"); - } - } - return isConnected; - } -} diff --git a/test/plugin/scenarios/jdk-httpclient-scenario/bin/startup.sh b/test/plugin/scenarios/jdk-httpclient-scenario/bin/startup.sh new file mode 100644 index 0000000000..6c3e9e8b21 --- /dev/null +++ b/test/plugin/scenarios/jdk-httpclient-scenario/bin/startup.sh @@ -0,0 +1,21 @@ +#!/bin/bash +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +home="$(cd "$(dirname $0)"; pwd)" + +java -jar ${agent_opts} ${home}/../libs/jdk-httpclient-scenario.jar & \ No newline at end of file diff --git a/test/plugin/scenarios/jdk-httpclient-scenario/config/expectedData.yaml b/test/plugin/scenarios/jdk-httpclient-scenario/config/expectedData.yaml new file mode 100644 index 0000000000..f009e03a46 --- /dev/null +++ b/test/plugin/scenarios/jdk-httpclient-scenario/config/expectedData.yaml @@ -0,0 +1,111 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +segmentItems: + - serviceName: jdk-httpclient-scenario + segmentSize: gt 0 + segments: + - segmentId: not null + spans: + - operationName: POST:/user/login + parentSpanId: -1 + spanId: 0 + spanLayer: Http + startTime: not null + endTime: not null + componentId: 14 + isError: false + spanType: Entry + peer: '' + skipAnalysis: 'false' + tags: + - { key: url, value: not null } + - { key: http.method, value: POST } + - { key: http.status_code, value: '200' } + refs: + - { parentEndpoint: GET:/case/jdk-httpclient-scenario-case, networkAddress: 'localhost:8080', + refType: CrossProcess, parentSpanId: 1, parentTraceSegmentId: not null, parentServiceInstance: not + null, parentService: jdk-httpclient-scenario, traceId: not null } + - segmentId: not null + spans: + - operationName: POST:/user/asyncLogin + parentSpanId: -1 + spanId: 0 + spanLayer: Http + startTime: not null + endTime: not null + componentId: 14 + isError: false + spanType: Entry + peer: '' + skipAnalysis: 'false' + tags: + - { key: url, value: not null } + - { key: http.method, value: POST } + - { key: http.status_code, value: '200' } + refs: + - { parentEndpoint: GET:/case/jdk-httpclient-scenario-case, networkAddress: 'localhost:8080', + refType: CrossProcess, parentSpanId: 2, parentTraceSegmentId: not null, parentServiceInstance: not + null, parentService: jdk-httpclient-scenario, traceId: not null } + + - segmentId: not null + spans: + - operationName: POST:/jdk-httpclient-scenario/user/login + parentSpanId: 0 + spanId: 1 + spanLayer: Http + startTime: not null + endTime: not null + componentId: 66 + isError: false + spanType: Exit + peer: not null + skipAnalysis: 'false' + tags: + - { key: http.method, value: POST } + - { key: url, value: http://localhost:8080/jdk-httpclient-scenario/user/login } + - { key: http.status_code, value: '200' } + + - operationName: POST:/jdk-httpclient-scenario/user/asyncLogin + parentSpanId: 0 + spanId: 2 + spanLayer: Http + startTime: not null + endTime: not null + componentId: 66 + isError: false + spanType: Exit + peer: not null + skipAnalysis: 'false' + tags: + - { key: http.method, value: POST } + - { key: url, value: http://localhost:8080/jdk-httpclient-scenario/user/asyncLogin } + - { key: http.status_code, value: '200' } + + - operationName: GET:/case/jdk-httpclient-scenario-case + parentSpanId: -1 + spanId: 0 + spanLayer: Http + startTime: not null + endTime: not null + componentId: 14 + isError: false + spanType: Entry + peer: '' + tags: + - { key: url, value: http://localhost:8080/jdk-httpclient-scenario/case/jdk-httpclient-scenario-case } + - { key: http.method, value: GET } + - { key: http.status_code, value: '200' } + skipAnalysis: 'false' diff --git a/test/plugin/scenarios/jdk-httpclient-scenario/configuration.yml b/test/plugin/scenarios/jdk-httpclient-scenario/configuration.yml new file mode 100644 index 0000000000..a5fe9c2cb1 --- /dev/null +++ b/test/plugin/scenarios/jdk-httpclient-scenario/configuration.yml @@ -0,0 +1,22 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +type: jvm +entryService: http://localhost:8080/jdk-httpclient-scenario/case/jdk-httpclient-scenario-case +healthCheck: http://localhost:8080/jdk-httpclient-scenario/case/healthCheck +runningMode: with_bootstrap +withPlugins: apm-jdk-httpclient-plugin-*.jar +startScript: ./bin/startup.sh diff --git a/test/plugin/scenarios/jdk-httpclient-scenario/pom.xml b/test/plugin/scenarios/jdk-httpclient-scenario/pom.xml new file mode 100644 index 0000000000..406bb8f57d --- /dev/null +++ b/test/plugin/scenarios/jdk-httpclient-scenario/pom.xml @@ -0,0 +1,128 @@ + + + + 4.0.0 + + org.apache.skywalking + jdk-httpclient-scenario + 5.0.0 + + + UTF-8 + 11 + 3.8.1 + 2.7.15 + + + skywalking-jdk-httpclient-scenario + + + + org.springframework.boot + spring-boot-starter-web + 2.7.15 + + + + com.alibaba + fastjson + 1.2.83 + + + + + jdk-httpclient-scenario + + + org.springframework.boot + spring-boot-maven-plugin + ${spring.boot.version} + + + + repackage + + + + + + maven-compiler-plugin + ${maven-compiler-plugin.version} + + ${compiler.version} + ${compiler.version} + ${project.build.sourceEncoding} + + + + org.apache.maven.plugins + maven-assembly-plugin + + + assemble + package + + single + + + + src/main/assembly/assembly.xml + + ./target/ + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + + + attach-javadocs + + jar + + + + + false + + + + + org.apache.maven.plugins + maven-jar-plugin + + + empty-javadoc-jar + package + + jar + + + javadoc + ${basedir}/javadoc + + + + + + + \ No newline at end of file diff --git a/test/plugin/scenarios/jdk-httpclient-scenario/src/main/assembly/assembly.xml b/test/plugin/scenarios/jdk-httpclient-scenario/src/main/assembly/assembly.xml new file mode 100644 index 0000000000..3ab23573ab --- /dev/null +++ b/test/plugin/scenarios/jdk-httpclient-scenario/src/main/assembly/assembly.xml @@ -0,0 +1,41 @@ + + + + + zip + + + + + ./bin + 0775 + + + + + + ./target/jdk-httpclient-scenario.jar + ./libs + 0775 + + + \ No newline at end of file diff --git a/test/plugin/scenarios/jdk-httpclient-scenario/src/main/java/test/apache/skywalking/apm/testcase/jdk/httpclient/Application.java b/test/plugin/scenarios/jdk-httpclient-scenario/src/main/java/test/apache/skywalking/apm/testcase/jdk/httpclient/Application.java new file mode 100644 index 0000000000..5147a224ee --- /dev/null +++ b/test/plugin/scenarios/jdk-httpclient-scenario/src/main/java/test/apache/skywalking/apm/testcase/jdk/httpclient/Application.java @@ -0,0 +1,30 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package test.apache.skywalking.apm.testcase.jdk.httpclient; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class Application { + + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } + +} diff --git a/test/plugin/scenarios/jdk-httpclient-scenario/src/main/java/test/apache/skywalking/apm/testcase/jdk/httpclient/controller/CaseController.java b/test/plugin/scenarios/jdk-httpclient-scenario/src/main/java/test/apache/skywalking/apm/testcase/jdk/httpclient/controller/CaseController.java new file mode 100644 index 0000000000..25d4f4e6b9 --- /dev/null +++ b/test/plugin/scenarios/jdk-httpclient-scenario/src/main/java/test/apache/skywalking/apm/testcase/jdk/httpclient/controller/CaseController.java @@ -0,0 +1,71 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package test.apache.skywalking.apm.testcase.jdk.httpclient.controller; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.io.IOException; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.time.Duration; +import java.util.concurrent.CompletableFuture; + +@RestController +@RequestMapping("/case") +public class CaseController { + + @GetMapping("/healthCheck") + public String healthCheck() { + return "Success"; + } + + @GetMapping("/jdk-httpclient-scenario-case") + public String testCase() throws InterruptedException, IOException { + HttpClient client = HttpClient.newBuilder() + .connectTimeout(Duration.ofSeconds(10)) + .build(); + + String json = "{\n" + + " \"username\": \"Alice\",\n" + + " \"password\": 21231231231\n" + + "}"; + + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create("http://localhost:8080/jdk-httpclient-scenario/user/login")) + .timeout(Duration.ofSeconds(10)) + .header("Content-Type", "application/json") + .POST(HttpRequest.BodyPublishers.ofString(json)) + .build(); + + client.send(request, HttpResponse.BodyHandlers.ofString()); + + HttpRequest asyncRequest = HttpRequest.newBuilder() + .uri(URI.create("http://localhost:8080/jdk-httpclient-scenario/user/asyncLogin")) + .timeout(Duration.ofSeconds(10)) + .header("Content-Type", "application/json") + .POST(HttpRequest.BodyPublishers.ofString(json)) + .build(); + CompletableFuture> future = client.sendAsync(asyncRequest, HttpResponse.BodyHandlers.ofString()); + future.join(); + return "success"; + } +} diff --git a/test/plugin/scenarios/jdk-httpclient-scenario/src/main/java/test/apache/skywalking/apm/testcase/jdk/httpclient/controller/UserController.java b/test/plugin/scenarios/jdk-httpclient-scenario/src/main/java/test/apache/skywalking/apm/testcase/jdk/httpclient/controller/UserController.java new file mode 100644 index 0000000000..a9665ffcb6 --- /dev/null +++ b/test/plugin/scenarios/jdk-httpclient-scenario/src/main/java/test/apache/skywalking/apm/testcase/jdk/httpclient/controller/UserController.java @@ -0,0 +1,45 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package test.apache.skywalking.apm.testcase.jdk.httpclient.controller; + +import com.alibaba.fastjson.JSONObject; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import test.apache.skywalking.apm.testcase.jdk.httpclient.dto.UserLoginDTO; + +@RestController +@RequestMapping("/user") +public class UserController { + @PostMapping("/login") + public JSONObject login(@RequestBody UserLoginDTO userLoginDTO) { + JSONObject resultJson = new JSONObject(); + resultJson.put("code", 200); + resultJson.put("message", "success"); + return resultJson; + } + + @PostMapping("/asyncLogin") + public JSONObject asyncLogin(@RequestBody UserLoginDTO userLoginDTO) { + JSONObject resultJson = new JSONObject(); + resultJson.put("code", 200); + resultJson.put("message", "success"); + return resultJson; + } +} diff --git a/test/plugin/scenarios/jdk-httpclient-scenario/src/main/java/test/apache/skywalking/apm/testcase/jdk/httpclient/dto/UserLoginDTO.java b/test/plugin/scenarios/jdk-httpclient-scenario/src/main/java/test/apache/skywalking/apm/testcase/jdk/httpclient/dto/UserLoginDTO.java new file mode 100644 index 0000000000..1226f82d2d --- /dev/null +++ b/test/plugin/scenarios/jdk-httpclient-scenario/src/main/java/test/apache/skywalking/apm/testcase/jdk/httpclient/dto/UserLoginDTO.java @@ -0,0 +1,40 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package test.apache.skywalking.apm.testcase.jdk.httpclient.dto; + +public class UserLoginDTO { + private String username; + + private String password; + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } +} diff --git a/test/plugin/scenarios/jdk-httpclient-scenario/src/main/resources/application.yaml b/test/plugin/scenarios/jdk-httpclient-scenario/src/main/resources/application.yaml new file mode 100644 index 0000000000..2bed23f8ca --- /dev/null +++ b/test/plugin/scenarios/jdk-httpclient-scenario/src/main/resources/application.yaml @@ -0,0 +1,21 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +server: + port: 8080 + servlet: + context-path: /jdk-httpclient-scenario + diff --git a/test/plugin/scenarios/jdk-httpclient-scenario/support-version.list b/test/plugin/scenarios/jdk-httpclient-scenario/support-version.list new file mode 100644 index 0000000000..feef03cdea --- /dev/null +++ b/test/plugin/scenarios/jdk-httpclient-scenario/support-version.list @@ -0,0 +1,17 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +all \ No newline at end of file diff --git a/test/plugin/scenarios/impala-jdbc-2.6.x-scenario/support-version.list b/test/plugin/scenarios/jdk-virtual-thread-executor-scenario/bin/startup.sh similarity index 85% rename from test/plugin/scenarios/impala-jdbc-2.6.x-scenario/support-version.list rename to test/plugin/scenarios/jdk-virtual-thread-executor-scenario/bin/startup.sh index e795caf90a..ac52eb1f0c 100644 --- a/test/plugin/scenarios/impala-jdbc-2.6.x-scenario/support-version.list +++ b/test/plugin/scenarios/jdk-virtual-thread-executor-scenario/bin/startup.sh @@ -1,3 +1,5 @@ +#!/bin/bash +# # Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information @@ -14,8 +16,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -# lists your version here (Contains only the last version number of each minor version.) +home="$(cd "$(dirname $0)"; pwd)" -2.6.26.1031 -2.6.24.1029 -2.6.20.1024 \ No newline at end of file +java -jar ${agent_opts} ${home}/../libs/jdk-virtual-thread-executor-scenario.jar & \ No newline at end of file diff --git a/test/plugin/scenarios/jdk-virtual-thread-executor-scenario/config/expectedData.yaml b/test/plugin/scenarios/jdk-virtual-thread-executor-scenario/config/expectedData.yaml new file mode 100644 index 0000000000..f2ccbf3ed1 --- /dev/null +++ b/test/plugin/scenarios/jdk-virtual-thread-executor-scenario/config/expectedData.yaml @@ -0,0 +1,101 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +segmentItems: + - serviceName: jdk-virtual-thread-executor-scenario + segmentSize: ge 0 + segments: + - segmentId: not null + spans: + - operationName: GET:/case + parentSpanId: -1 + spanId: 0 + spanLayer: Http + startTime: not null + endTime: not null + componentId: 14 + isError: false + spanType: Entry + peer: '' + skipAnalysis: false + tags: + - {key: url, value: 'http://localhost:8080/virtual-thread-executor-scenario/case'} + - {key: http.method, value: GET} + - {key: http.status_code, value: '200'} + - segmentId: not null + spans: + - operationName: /apache/skywalking + parentSpanId: 0 + spanId: 1 + spanLayer: Http + startTime: nq 0 + endTime: nq 0 + componentId: 13 + isError: false + spanType: Exit + peer: github.com:443 + skipAnalysis: false + tags: + - {key: url, value: 'https://github.com/apache/skywalking'} + - {key: http.method, value: GET} + - {key: http.status_code, value: '200'} + - operationName: java.util.concurrent.ThreadPerTaskExecutor$TaskRunner.run + parentSpanId: -1 + spanId: 0 + startTime: nq 0 + endTime: nq 0 + componentId: 161 + isError: false + spanType: Local + skipAnalysis: false + tags: + - { key: thread.carrier, value: not null } + refs: + - { parentEndpoint: 'GET:/case', networkAddress: '', refType: CrossThread, + parentSpanId: 0, parentTraceSegmentId: not null, parentServiceInstance: not + null, parentService: jdk-virtual-thread-executor-scenario, traceId: not null } + + - segmentId: not null + spans: + - operationName: /apache/skywalking + parentSpanId: 0 + spanId: 1 + spanLayer: Http + startTime: nq 0 + endTime: nq 0 + componentId: 13 + isError: false + spanType: Exit + peer: github.com:443 + skipAnalysis: false + tags: + - { key: url, value: 'https://github.com/apache/skywalking' } + - { key: http.method, value: GET } + - { key: http.status_code, value: '200' } + - operationName: java.util.concurrent.ThreadPerTaskExecutor$ThreadBoundFuture.run + parentSpanId: -1 + spanId: 0 + startTime: nq 0 + endTime: nq 0 + componentId: 161 + isError: false + spanType: Local + skipAnalysis: false + tags: + - { key: thread.carrier, value: not null } + refs: + - { parentEndpoint: 'GET:/case', networkAddress: '', refType: CrossThread, + parentSpanId: 0, parentTraceSegmentId: not null, parentServiceInstance: not + null, parentService: jdk-virtual-thread-executor-scenario, traceId: not null } diff --git a/test/plugin/scenarios/jdk-virtual-thread-executor-scenario/configuration.yml b/test/plugin/scenarios/jdk-virtual-thread-executor-scenario/configuration.yml new file mode 100644 index 0000000000..f36c72f82e --- /dev/null +++ b/test/plugin/scenarios/jdk-virtual-thread-executor-scenario/configuration.yml @@ -0,0 +1,22 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +type: jvm +entryService: http://localhost:8080/virtual-thread-executor-scenario/case +healthCheck: http://localhost:8080/virtual-thread-executor-scenario/healthCheck +runningMode: with_bootstrap +withPlugins: apm-jdk-virtual-thread-executor-plugin-*.jar +startScript: ./bin/startup.sh diff --git a/test/plugin/scenarios/jdk-virtual-thread-executor-scenario/pom.xml b/test/plugin/scenarios/jdk-virtual-thread-executor-scenario/pom.xml new file mode 100644 index 0000000000..cc26ef8c91 --- /dev/null +++ b/test/plugin/scenarios/jdk-virtual-thread-executor-scenario/pom.xml @@ -0,0 +1,128 @@ + + + + 4.0.0 + + org.apache.skywalking + jdk-virtual-thread-executor-scenario + 5.0.0 + + + UTF-8 + 21 + 3.8.1 + 2.7.15 + + + skywalking-jdk-virtual-thread-executor-scenario + + + + org.springframework.boot + spring-boot-starter-web + 2.7.15 + + + + org.redisson + redisson + 3.33.0 + + + + + jdk-virtual-thread-executor-scenario + + + org.springframework.boot + spring-boot-maven-plugin + ${spring.boot.version} + + + + repackage + + + + + + maven-compiler-plugin + ${maven-compiler-plugin.version} + + ${compiler.version} + ${compiler.version} + ${project.build.sourceEncoding} + + + + org.apache.maven.plugins + maven-assembly-plugin + + + assemble + package + + single + + + + src/main/assembly/assembly.xml + + ./target/ + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + + + attach-javadocs + + jar + + + + + false + + + + + org.apache.maven.plugins + maven-jar-plugin + + + empty-javadoc-jar + package + + jar + + + javadoc + ${basedir}/javadoc + + + + + + + \ No newline at end of file diff --git a/test/plugin/scenarios/jdk-virtual-thread-executor-scenario/src/main/assembly/assembly.xml b/test/plugin/scenarios/jdk-virtual-thread-executor-scenario/src/main/assembly/assembly.xml new file mode 100644 index 0000000000..55f39086fb --- /dev/null +++ b/test/plugin/scenarios/jdk-virtual-thread-executor-scenario/src/main/assembly/assembly.xml @@ -0,0 +1,41 @@ + + + + + zip + + + + + ./bin + 0775 + + + + + + ./target/jdk-virtual-thread-executor-scenario.jar + ./libs + 0775 + + + \ No newline at end of file diff --git a/test/plugin/scenarios/jdk-virtual-thread-executor-scenario/src/main/java/test/apache/skywalking/apm/testcase/jdk/virtualThread/Application.java b/test/plugin/scenarios/jdk-virtual-thread-executor-scenario/src/main/java/test/apache/skywalking/apm/testcase/jdk/virtualThread/Application.java new file mode 100644 index 0000000000..57aef5d5b6 --- /dev/null +++ b/test/plugin/scenarios/jdk-virtual-thread-executor-scenario/src/main/java/test/apache/skywalking/apm/testcase/jdk/virtualThread/Application.java @@ -0,0 +1,73 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package test.apache.skywalking.apm.testcase.jdk.virtualThread; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.Bean; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.client.RestTemplate; + +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +@SpringBootApplication +public class Application { + + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } + + @Bean + public RestTemplate restTemplate() { + return new RestTemplate(); + } + + @RestController + static class TestController { + private final RestTemplate restTemplate; + private final ExecutorService executorService; + + public TestController(final RestTemplate restTemplate) { + this.restTemplate = restTemplate; + this.executorService = Executors.newVirtualThreadPerTaskExecutor(); + } + + @GetMapping("/healthCheck") + public String healthCheck() { + return "Success"; + } + + @GetMapping("/case") + public String testCase() throws ExecutionException, InterruptedException { + Runnable runnable = () -> restTemplate.getForEntity("https://github.com/apache/skywalking", String.class); + executorService.execute(runnable); + + Callable callable = () -> { + restTemplate.getForEntity("https://github.com/apache/skywalking", String.class); + return "success"; + }; + executorService.submit(callable).get(); + + return "success"; + } + } +} diff --git a/test/plugin/scenarios/jdk-virtual-thread-executor-scenario/src/main/resources/application.yaml b/test/plugin/scenarios/jdk-virtual-thread-executor-scenario/src/main/resources/application.yaml new file mode 100644 index 0000000000..0ddde72c7c --- /dev/null +++ b/test/plugin/scenarios/jdk-virtual-thread-executor-scenario/src/main/resources/application.yaml @@ -0,0 +1,21 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +server: + port: 8080 + servlet: + context-path: /virtual-thread-executor-scenario + diff --git a/test/plugin/scenarios/jdk-virtual-thread-executor-scenario/support-version.list b/test/plugin/scenarios/jdk-virtual-thread-executor-scenario/support-version.list new file mode 100644 index 0000000000..feef03cdea --- /dev/null +++ b/test/plugin/scenarios/jdk-virtual-thread-executor-scenario/support-version.list @@ -0,0 +1,17 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +all \ No newline at end of file diff --git a/test/plugin/scenarios/jedis-2.x-3.x-cluster-scenario/configuration.yml b/test/plugin/scenarios/jedis-2.x-3.x-cluster-scenario/configuration.yml index e37ee9af2f..79a60472da 100644 --- a/test/plugin/scenarios/jedis-2.x-3.x-cluster-scenario/configuration.yml +++ b/test/plugin/scenarios/jedis-2.x-3.x-cluster-scenario/configuration.yml @@ -23,19 +23,19 @@ environment: - SW_PLUGIN_JEDIS_TRACE_REDIS_PARAMETERS=true dependencies: redis-server1: - image: bitnami/redis-cluster:7.0 + image: bitnamilegacy/redis-cluster:7.0 hostname: redis-server1 environment: - ALLOW_EMPTY_PASSWORD=true - REDIS_NODES=redis-server1 redis-server2 redis-server3 redis-server2: - image: bitnami/redis-cluster:7.0 + image: bitnamilegacy/redis-cluster:7.0 hostname: redis-server2 environment: - ALLOW_EMPTY_PASSWORD=true - REDIS_NODES=redis-server1 redis-server2 redis-server3 redis-server3: - image: bitnami/redis-cluster:7.0 + image: bitnamilegacy/redis-cluster:7.0 hostname: redis-server3 depends_on: - redis-server1 diff --git a/test/plugin/scenarios/jedis-4.x-scenario/config/expectedData.yaml b/test/plugin/scenarios/jedis-4.x-scenario/config/expectedData.yaml index 6be161a8b8..14e3547916 100644 --- a/test/plugin/scenarios/jedis-4.x-scenario/config/expectedData.yaml +++ b/test/plugin/scenarios/jedis-4.x-scenario/config/expectedData.yaml @@ -36,6 +36,7 @@ segmentItems: - {key: cache.key, value: a} - {key: cache.cmd, value: set} - {key: cache.type, value: Redis} + - {key: actual_target, value: 'redis-server:6379'} - {key: cache.op, value: write} - operationName: Jedis/get operationId: 0 @@ -53,6 +54,7 @@ segmentItems: - {key: cache.key, value: a} - {key: cache.cmd, value: get} - {key: cache.type, value: Redis} + - {key: actual_target, value: 'redis-server:6379'} - {key: cache.op, value: read} - operationName: Jedis/del operationId: 0 @@ -70,6 +72,7 @@ segmentItems: - {key: cache.key, value: a} - {key: cache.cmd, value: del} - {key: cache.type, value: Redis} + - {key: actual_target, value: 'redis-server:6379'} - {key: cache.op, value: write} - operationName: Jedis/syncAndReturnAll operationId: 0 @@ -86,6 +89,7 @@ segmentItems: tags: - {key: cache.type, value: Redis} - {key: cache.cmd, value: BATCH_EXECUTE} + - {key: actual_target, value: 'redis-server:6379'} - operationName: Jedis/exec operationId: 0 parentSpanId: 0 @@ -101,6 +105,7 @@ segmentItems: tags: - {key: cache.type, value: Redis} - {key: cache.cmd, value: BATCH_EXECUTE} + - {key: actual_target, value: 'redis-server:6379'} - operationName: Jedis/discard operationId: 0 parentSpanId: 0 @@ -116,6 +121,7 @@ segmentItems: tags: - {key: cache.type, value: Redis} - {key: cache.cmd, value: BATCH_EXECUTE} + - {key: actual_target, value: 'redis-server:6379'} - operationName: Jedis/xadd operationId: 0 parentSpanId: 0 @@ -132,6 +138,7 @@ segmentItems: - {key: cache.key, value: abc} - {key: cache.cmd, value: xadd} - {key: cache.type, value: Redis} + - {key: actual_target, value: 'redis-server:6379'} - {key: cache.op, value: write} - operationName: Jedis/xread operationId: 0 @@ -148,6 +155,7 @@ segmentItems: tags: - {key: cache.cmd, value: xread} - {key: cache.type, value: Redis} + - {key: actual_target, value: 'redis-server:6379'} - {key: cache.op, value: read} - operationName: Jedis/xdel operationId: 0 @@ -165,6 +173,109 @@ segmentItems: - {key: cache.key, value: abc} - {key: cache.cmd, value: xdel} - {key: cache.type, value: Redis} + - {key: actual_target, value: 'redis-server:6379'} + - {key: cache.op, value: write} + - operationName: Jedis/set + parentSpanId: 0 + spanId: 10 + spanLayer: Cache + startTime: gt 0 + endTime: gt 0 + componentId: 30 + isError: false + spanType: Exit + peer: redis-server:6379,redis-server:6379 + skipAnalysis: false + tags: + - {key: cache.key, value: x} + - {key: cache.cmd, value: set} + - {key: cache.type, value: Redis} + - {key: actual_target, value: 'redis-server:6379'} + - {key: cache.op, value: write} + - operationName: Jedis/get + parentSpanId: 0 + spanId: 11 + spanLayer: Cache + startTime: gt 0 + endTime: gt 0 + componentId: 30 + isError: false + spanType: Exit + peer: redis-server:6379,redis-server:6379 + skipAnalysis: false + tags: + - {key: cache.key, value: x} + - {key: cache.cmd, value: get} + - {key: cache.type, value: Redis} + - {key: actual_target, value: 'redis-server:6379'} + - {key: cache.op, value: read} + - operationName: Jedis/del + parentSpanId: 0 + spanId: 12 + spanLayer: Cache + startTime: gt 0 + endTime: gt 0 + componentId: 30 + isError: false + spanType: Exit + peer: redis-server:6379,redis-server:6379 + skipAnalysis: false + tags: + - {key: cache.key, value: x} + - {key: cache.cmd, value: del} + - {key: cache.type, value: Redis} + - {key: actual_target, value: 'redis-server:6379'} + - {key: cache.op, value: write} + - operationName: Jedis/set + parentSpanId: 0 + spanId: 13 + spanLayer: Cache + startTime: gt 0 + endTime: gt 0 + componentId: 30 + isError: false + spanType: Exit + peer: redis-cluster-m3:6379,redis-cluster-m2:6379,redis-cluster-m1:6379 + skipAnalysis: false + tags: + - {key: cache.key, value: x} + - {key: cache.cmd, value: set} + - {key: cache.type, value: Redis} + - {key: actual_target, value: not blank } + - {key: cache.op, value: write} + - operationName: Jedis/get + parentSpanId: 0 + spanId: 14 + spanLayer: Cache + startTime: gt 0 + endTime: gt 0 + componentId: 30 + isError: false + spanType: Exit + peer: redis-cluster-m3:6379,redis-cluster-m2:6379,redis-cluster-m1:6379 + skipAnalysis: false + tags: + - {key: cache.key, value: x} + - {key: cache.cmd, value: get} + - {key: cache.type, value: Redis} + - {key: actual_target, value: not blank} + - {key: cache.op, value: read} + - operationName: Jedis/del + parentSpanId: 0 + spanId: 15 + spanLayer: Cache + startTime: gt 0 + endTime: gt 0 + componentId: 30 + isError: false + spanType: Exit + peer: redis-cluster-m3:6379,redis-cluster-m2:6379,redis-cluster-m1:6379 + skipAnalysis: false + tags: + - {key: cache.key, value: x} + - {key: cache.cmd, value: del} + - {key: cache.type, value: Redis} + - {key: actual_target, value: not blank } - {key: cache.op, value: write} - operationName: GET:/jedis-scenario/case/jedis-scenario operationId: 0 @@ -181,4 +292,4 @@ segmentItems: tags: - {key: url, value: 'http://localhost:8080/jedis-scenario/case/jedis-scenario'} - {key: http.method, value: GET} - - {key: http.status_code, value: '200'} \ No newline at end of file + - {key: http.status_code, value: '200'} diff --git a/test/plugin/scenarios/jedis-4.x-scenario/configuration.yml b/test/plugin/scenarios/jedis-4.x-scenario/configuration.yml index 79dfda5c0b..e340adec0d 100644 --- a/test/plugin/scenarios/jedis-4.x-scenario/configuration.yml +++ b/test/plugin/scenarios/jedis-4.x-scenario/configuration.yml @@ -21,8 +21,27 @@ startScript: ./bin/startup.sh environment: - REDIS_HOST=redis-server - REDIS_PORT=6379 + - REDIS_CLUSTER_M1_HOST=redis-cluster-m1 + - REDIS_CLUSTER_M1_PORT=6379 + - REDIS_CLUSTER_M2_HOST=redis-cluster-m2 + - REDIS_CLUSTER_M2_PORT=6379 + - REDIS_CLUSTER_M3_HOST=redis-cluster-m3 + - REDIS_CLUSTER_M3_PORT=6379 + dependencies: redis-server: image: redis:7.0.4 hostname: redis-server + redis-cluster-m1: + image: redis:7.2.4 + hostname: redis-cluster-m1 + command: sh -c " printf '%s\\n' 'port 6379'>> /etc/redis.conf && printf '%s\\n' 'cluster-enabled yes'>> /etc/redis.conf && printf '%s\\n' 'cluster-config-file nodes.conf'>> /etc/redis.conf && printf '%s\\n' 'nohup redis-server /etc/redis.conf>>/tmp/redis.log &' >> /tmp/start-redis.sh && printf '%s\\n' 'while true; do printf yes | redis-cli --cluster create redis-cluster-m1:6379 redis-cluster-m2:6379 redis-cluster-m3:6379 --cluster-replicas 0 && break; done ' >> /tmp/start-redis.sh && printf '%s\\n' 'tail -f /tmp/redis.log' >> /tmp/start-redis.sh && sh /tmp/start-redis.sh" + redis-cluster-m2: + image: redis:7.2.4 + hostname: redis-cluster-m2 + command: sh -c " printf '%s\\n' 'port 6379'>> /etc/redis.conf && printf '%s\\n' 'cluster-enabled yes'>> /etc/redis.conf && printf '%s\\n' 'cluster-config-file nodes.conf'>> /etc/redis.conf && printf '%s\\n' 'nohup redis-server /etc/redis.conf>>/tmp/redis.log &' >> /tmp/start-redis.sh && printf '%s\\n' 'while true; do printf yes | redis-cli --cluster create redis-cluster-m1:6379 redis-cluster-m2:6379 redis-cluster-m3:6379 --cluster-replicas 0 && break; done ' >> /tmp/start-redis.sh && printf '%s\\n' 'tail -f /tmp/redis.log' >> /tmp/start-redis.sh && sh /tmp/start-redis.sh" + redis-cluster-m3: + image: redis:7.2.4 + hostname: redis-cluster-m3 + command: sh -c " printf '%s\\n' 'port 6379'>> /etc/redis.conf && printf '%s\\n' 'cluster-enabled yes'>> /etc/redis.conf && printf '%s\\n' 'cluster-config-file nodes.conf'>> /etc/redis.conf && printf '%s\\n' 'nohup redis-server /etc/redis.conf>>/tmp/redis.log &' >> /tmp/start-redis.sh && printf '%s\\n' 'while true; do printf yes | redis-cli --cluster create redis-cluster-m1:6379 redis-cluster-m2:6379 redis-cluster-m3:6379 --cluster-replicas 0 && break; done ' >> /tmp/start-redis.sh && printf '%s\\n' 'tail -f /tmp/redis.log' >> /tmp/start-redis.sh && sh /tmp/start-redis.sh" diff --git a/test/plugin/scenarios/jedis-4.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/jedis/controller/CaseController.java b/test/plugin/scenarios/jedis-4.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/jedis/controller/CaseController.java index a7f570960a..fd7999060a 100644 --- a/test/plugin/scenarios/jedis-4.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/jedis/controller/CaseController.java +++ b/test/plugin/scenarios/jedis-4.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/jedis/controller/CaseController.java @@ -18,10 +18,13 @@ package org.apache.skywalking.apm.testcase.jedis.controller; +import java.util.Arrays; +import java.util.HashSet; import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; +import redis.clients.jedis.HostAndPort; @RestController @RequestMapping("/case") @@ -35,6 +38,21 @@ public class CaseController { @Value("${redis.port:6379}") private Integer redisPort; + @Value("${redis.cluster.m1.host:127.0.0.1}") + private String redisClusterM1Host; + @Value("${redis.cluster.m1.port:6379}") + private Integer redisClusterM1Port; + + @Value("${redis.cluster.m2.host:127.0.0.1}") + private String redisClusterM2Host; + @Value("${redis.cluster.m2.port:6379}") + private Integer redisClusterM2Port; + + @Value("${redis.cluster.m3.host:127.0.0.1}") + private String redisClusterM3Host; + @Value("${redis.cluster.m3.port:6379}") + private Integer redisClusterM3Port; + @RequestMapping("/jedis-scenario") @ResponseBody public String testcase() throws Exception { @@ -43,7 +61,6 @@ public String testcase() throws Exception { command.get("a"); command.del("a"); } - try (RedisPipelineCommandExecutor command = new RedisPipelineCommandExecutor(redisHost, redisPort)) { command.pipelineExecute(); } @@ -55,7 +72,18 @@ public String testcase() throws Exception { try (RedisStreamCommandExecutor executor = new RedisStreamCommandExecutor(redisHost, redisPort)) { executor.exec(); } - + try (ShardingExecutor shardingExecutor = new ShardingExecutor( + Arrays.asList(new HostAndPort(redisHost, redisPort), new HostAndPort(redisHost, redisPort)))) { + shardingExecutor.exec(); + } + try (ClusterExecutor clusterExecutor = new ClusterExecutor(new HashSet<>( + Arrays.asList( + new HostAndPort(redisClusterM1Host, redisClusterM1Port), + new HostAndPort(redisClusterM2Host, redisClusterM2Port), + new HostAndPort(redisClusterM3Host, redisClusterM3Port) + )))) { + clusterExecutor.exec(); + } return SUCCESS; } diff --git a/test/plugin/scenarios/jedis-4.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/jedis/controller/ClusterExecutor.java b/test/plugin/scenarios/jedis-4.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/jedis/controller/ClusterExecutor.java new file mode 100644 index 0000000000..8e9293e04e --- /dev/null +++ b/test/plugin/scenarios/jedis-4.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/jedis/controller/ClusterExecutor.java @@ -0,0 +1,42 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.testcase.jedis.controller; + +import java.util.Set; +import redis.clients.jedis.HostAndPort; +import redis.clients.jedis.JedisCluster; + +public class ClusterExecutor implements AutoCloseable { + + private final JedisCluster jedisCluster; + + public ClusterExecutor(Set jedisClusterNodes) { + this.jedisCluster = new JedisCluster(jedisClusterNodes); + } + + public void exec() { + jedisCluster.set("x", "1"); + jedisCluster.get("x"); + jedisCluster.del("x"); + } + + public void close() { + this.jedisCluster.close(); + } +} diff --git a/test/plugin/scenarios/jedis-4.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/jedis/controller/ShardingExecutor.java b/test/plugin/scenarios/jedis-4.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/jedis/controller/ShardingExecutor.java new file mode 100644 index 0000000000..482d942837 --- /dev/null +++ b/test/plugin/scenarios/jedis-4.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/jedis/controller/ShardingExecutor.java @@ -0,0 +1,42 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.testcase.jedis.controller; + +import java.util.List; +import redis.clients.jedis.HostAndPort; +import redis.clients.jedis.JedisSharding; + +public class ShardingExecutor implements AutoCloseable { + private JedisSharding jedisSharding; + + public ShardingExecutor(List hostAndPorts) { + this.jedisSharding = new JedisSharding(hostAndPorts); + } + + public void exec() { + jedisSharding.set("x", "1"); + jedisSharding.get("x"); + jedisSharding.del("x"); + } + + public void close() { + jedisSharding.close(); + } + +} diff --git a/test/plugin/scenarios/jedis-4.x-scenario/support-version.list b/test/plugin/scenarios/jedis-4.x-scenario/support-version.list index 209481d5b8..31258966c3 100644 --- a/test/plugin/scenarios/jedis-4.x-scenario/support-version.list +++ b/test/plugin/scenarios/jedis-4.x-scenario/support-version.list @@ -14,6 +14,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +4.4.6 +4.3.2 4.2.3 4.1.1 -4.0.1 \ No newline at end of file +4.0.1 diff --git a/test/plugin/scenarios/jetty-11.x-thread-pool-scenario/config/expectedData.yaml b/test/plugin/scenarios/jetty-11.x-thread-pool-scenario/config/expectedData.yaml index d911caaa10..ff04156c69 100644 --- a/test/plugin/scenarios/jetty-11.x-thread-pool-scenario/config/expectedData.yaml +++ b/test/plugin/scenarios/jetty-11.x-thread-pool-scenario/config/expectedData.yaml @@ -15,7 +15,7 @@ # limitations under the License. meterItems: - serviceName: jetty-11.x-thread-pool-scenario - meterSize: 5 + meterSize: ge 5 meters: - meterId: name: thread_pool diff --git a/test/plugin/scenarios/jetty-scenario/jettyclient-scenario/src/main/java/org/apache/skywalking/apm/testcase/jettyclient/controller/CaseController.java b/test/plugin/scenarios/jetty-scenario/jettyclient-scenario/src/main/java/org/apache/skywalking/apm/testcase/jettyclient/controller/CaseController.java index d45d8adfed..4e31db9383 100644 --- a/test/plugin/scenarios/jetty-scenario/jettyclient-scenario/src/main/java/org/apache/skywalking/apm/testcase/jettyclient/controller/CaseController.java +++ b/test/plugin/scenarios/jetty-scenario/jettyclient-scenario/src/main/java/org/apache/skywalking/apm/testcase/jettyclient/controller/CaseController.java @@ -16,7 +16,7 @@ * */ -package org.apache.skywalking.apm.testcase.jettyclient.contr; +package org.apache.skywalking.apm.testcase.jettyclient.controller; import javax.annotation.PostConstruct; import java.io.IOException; @@ -26,6 +26,8 @@ import org.apache.http.impl.client.HttpClients; import org.eclipse.jetty.client.HttpClient; import org.eclipse.jetty.client.api.Response; +import org.eclipse.jetty.client.api.Result; +import org.eclipse.jetty.client.util.BufferingResponseListener; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.PropertySource; import org.springframework.stereotype.Controller; @@ -51,13 +53,19 @@ public void init() throws Exception { @ResponseBody public String jettyClientScenario() throws Exception { client.newRequest("http://" + jettyServerHost + ":18080/jettyserver-case/case/receiveContext-0").send(); - Response.CompleteListener listener = result -> { - CloseableHttpClient httpclient = HttpClients.createDefault(); - HttpGet httpget = new HttpGet("http://" + jettyServerHost + ":18080/jettyserver-case/case/receiveContext-0"); - try { - httpclient.execute(httpget); - } catch (IOException e) { - throw new RuntimeException(e); + Response.Listener listener = new BufferingResponseListener() { + public void onComplete(Result result) { + byte[] bytes = this.getContent(); + if (bytes == null || bytes.length == 0) { + throw new RuntimeException("content cant be empty"); + } + CloseableHttpClient httpclient = HttpClients.createDefault(); + HttpGet httpget = new HttpGet("http://" + jettyServerHost + ":18080/jettyserver-case/case/receiveContext-0"); + try { + httpclient.execute(httpget); + } catch (IOException e) { + throw new RuntimeException(e); + } } }; client.newRequest("http://" + jettyServerHost + ":18080/jettyserver-case/case/receiveContext-1").send(listener); diff --git a/test/plugin/scenarios/jetty-scenario/jettyserver-scenario/src/main/java/org/apache/skywalking/apm/testcase/jettyserver/servlet/AsyncCaseServlet.java b/test/plugin/scenarios/jetty-scenario/jettyserver-scenario/src/main/java/org/apache/skywalking/apm/testcase/jettyserver/servlet/AsyncCaseServlet.java index 667fdafd16..4075fc0e34 100644 --- a/test/plugin/scenarios/jetty-scenario/jettyserver-scenario/src/main/java/org/apache/skywalking/apm/testcase/jettyserver/servlet/AsyncCaseServlet.java +++ b/test/plugin/scenarios/jetty-scenario/jettyserver-scenario/src/main/java/org/apache/skywalking/apm/testcase/jettyserver/servlet/AsyncCaseServlet.java @@ -19,6 +19,7 @@ package org.apache.skywalking.apm.testcase.jettyserver.servlet; import java.io.IOException; +import java.io.PrintWriter; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; @@ -31,6 +32,9 @@ public class AsyncCaseServlet extends HttpServlet { protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { try { Thread.sleep(2000); + resp.setContentType("text/plain;charset=UTF-8"); + PrintWriter out = resp.getWriter(); + out.print("Success"); } catch (InterruptedException e) { } } diff --git a/test/plugin/scenarios/jetty-scenario/jettyserver-scenario/src/main/java/org/apache/skywalking/apm/testcase/jettyserver/servlet/CaseServlet.java b/test/plugin/scenarios/jetty-scenario/jettyserver-scenario/src/main/java/org/apache/skywalking/apm/testcase/jettyserver/servlet/CaseServlet.java index 9dfae15f64..22b6eeac90 100644 --- a/test/plugin/scenarios/jetty-scenario/jettyserver-scenario/src/main/java/org/apache/skywalking/apm/testcase/jettyserver/servlet/CaseServlet.java +++ b/test/plugin/scenarios/jetty-scenario/jettyserver-scenario/src/main/java/org/apache/skywalking/apm/testcase/jettyserver/servlet/CaseServlet.java @@ -19,6 +19,7 @@ package org.apache.skywalking.apm.testcase.jettyserver.servlet; import java.io.IOException; +import java.io.PrintWriter; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; @@ -31,6 +32,9 @@ public class CaseServlet extends HttpServlet { protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { try { Thread.sleep(2000); + resp.setContentType("text/plain;charset=UTF-8"); + PrintWriter out = resp.getWriter(); + out.print("Success"); } catch (InterruptedException e) { } } diff --git a/test/plugin/scenarios/jetty-thread-pool-scenario/config/expectedData.yaml b/test/plugin/scenarios/jetty-thread-pool-scenario/config/expectedData.yaml index 00b0074a84..276fa97406 100644 --- a/test/plugin/scenarios/jetty-thread-pool-scenario/config/expectedData.yaml +++ b/test/plugin/scenarios/jetty-thread-pool-scenario/config/expectedData.yaml @@ -15,7 +15,7 @@ # limitations under the License. meterItems: - serviceName: jetty-thread-pool-scenario - meterSize: 5 + meterSize: ge 5 meters: - meterId: name: thread_pool diff --git a/test/plugin/scenarios/kafka-scenario/configuration.yml b/test/plugin/scenarios/kafka-scenario/configuration.yml index 5a74c2ba42..7db52f161f 100644 --- a/test/plugin/scenarios/kafka-scenario/configuration.yml +++ b/test/plugin/scenarios/kafka-scenario/configuration.yml @@ -28,7 +28,7 @@ dependencies: image: zookeeper:3.4 hostname: zookeeper-server kafka-server: - image: bitnami/kafka:2.1.1 + image: bitnamilegacy/kafka:2.4.1 hostname: kafka-server environment: - KAFKA_ZOOKEEPER_CONNECT=zookeeper-server:2181 diff --git a/test/plugin/scenarios/kafka-scenario/src/main/java/test/apache/skywalking/apm/testcase/kafka/controller/CaseController.java b/test/plugin/scenarios/kafka-scenario/src/main/java/test/apache/skywalking/apm/testcase/kafka/controller/CaseController.java index ea8bb6e3c1..ee6458bdc1 100644 --- a/test/plugin/scenarios/kafka-scenario/src/main/java/test/apache/skywalking/apm/testcase/kafka/controller/CaseController.java +++ b/test/plugin/scenarios/kafka-scenario/src/main/java/test/apache/skywalking/apm/testcase/kafka/controller/CaseController.java @@ -24,6 +24,7 @@ import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.function.Consumer; +import java.util.Collection; import java.util.regex.Pattern; import java.util.List; import java.util.ArrayList; @@ -32,10 +33,10 @@ import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.Response; +import org.apache.kafka.clients.consumer.ConsumerRebalanceListener; import org.apache.kafka.clients.consumer.ConsumerRecord; import org.apache.kafka.clients.consumer.ConsumerRecords; import org.apache.kafka.clients.consumer.KafkaConsumer; -import org.apache.kafka.clients.consumer.internals.NoOpConsumerRebalanceListener; import org.apache.kafka.clients.producer.Callback; import org.apache.kafka.clients.producer.KafkaProducer; import org.apache.kafka.clients.producer.Producer; @@ -270,7 +271,17 @@ public void run() { consumerProperties.put("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer"); consumerProperties.put("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer"); KafkaConsumer consumer = new KafkaConsumer<>(consumerProperties); - consumer.subscribe(topicPattern, new NoOpConsumerRebalanceListener()); + consumer.subscribe(topicPattern, new ConsumerRebalanceListener() { + @Override + public void onPartitionsRevoked(Collection collection) { + + } + + @Override + public void onPartitionsAssigned(Collection collection) { + + } + }); while (true) { if (pollAndInvoke(consumer)) break; } diff --git a/test/plugin/scenarios/kafka-scenario/support-version.list b/test/plugin/scenarios/kafka-scenario/support-version.list index 1ffc63cb11..f59368c253 100644 --- a/test/plugin/scenarios/kafka-scenario/support-version.list +++ b/test/plugin/scenarios/kafka-scenario/support-version.list @@ -28,3 +28,7 @@ 3.0.2 3.1.2 3.2.3 +3.6.0 +3.7.0 +3.7.1 +3.9.1 \ No newline at end of file diff --git a/test/plugin/scenarios/lettuce-6.5.x-scenario/bin/startup.sh b/test/plugin/scenarios/lettuce-6.5.x-scenario/bin/startup.sh new file mode 100644 index 0000000000..3ef5c866ff --- /dev/null +++ b/test/plugin/scenarios/lettuce-6.5.x-scenario/bin/startup.sh @@ -0,0 +1,21 @@ +#!/bin/bash +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +home="$(cd "$(dirname $0)"; pwd)" + +java -Dredis.host=${REDIS_SERVERS} -jar -Dskywalking.plugin.lettuce.trace_redis_parameters=true ${agent_opts} ${home}/../libs/lettuce-6.5.x-scenario.jar & \ No newline at end of file diff --git a/test/plugin/scenarios/lettuce-6.5.x-scenario/config/expectedData.yaml b/test/plugin/scenarios/lettuce-6.5.x-scenario/config/expectedData.yaml new file mode 100644 index 0000000000..dbeb6b6138 --- /dev/null +++ b/test/plugin/scenarios/lettuce-6.5.x-scenario/config/expectedData.yaml @@ -0,0 +1,206 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +segmentItems: + - serviceName: lettuce-6.5.x-scenario + segmentSize: nq 0 + segments: + - segmentId: not null + spans: + - operationName: HEAD:/lettuce-6.5.x-scenario/case/healthCheck + parentSpanId: -1 + spanId: 0 + spanLayer: Http + startTime: not null + endTime: not null + componentId: 1 + isError: false + spanType: Entry + peer: '' + skipAnalysis: false + tags: + - {key: url, value: 'http://localhost:8080/lettuce-6.5.x-scenario/case/healthCheck'} + - {key: http.method, value: HEAD} + - {key: http.status_code, value: '200'} + - segmentId: not null + spans: + - operationName: Lettuce/SET + parentSpanId: 0 + spanId: 1 + spanLayer: Cache + startTime: not null + endTime: not null + componentId: 57 + isError: false + spanType: Exit + peer: not null + skipAnalysis: false + tags: + - { key: cache.type, value: Redis } + - { key: cache.key, value: key0 } + - { key: cache.cmd, value: SET } + - { key: cache.op, value: write } + - operationName: RedisReactive/local + parentSpanId: -1 + spanId: 0 + isError: false + spanType: Local + refs: + - { parentEndpoint: GET:/lettuce-6.5.x-scenario/case/lettuce-case, networkAddress: '', refType: CrossThread, + parentSpanId: 5, parentTraceSegmentId: not null, parentServiceInstance: not + null, parentService: not null, traceId: not null } + - segmentId: not null + spans: + - operationName: Lettuce/SET + parentSpanId: 0 + spanId: 1 + spanLayer: Cache + startTime: not null + endTime: not null + componentId: 57 + isError: false + spanType: Exit + peer: not null + skipAnalysis: false + tags: + - { key: cache.type, value: Redis } + - { key: cache.key, value: key1 } + - { key: cache.cmd, value: SET } + - { key: cache.op, value: write } + - operationName: RedisReactive/local + parentSpanId: -1 + spanId: 0 + isError: false + spanType: Local + refs: + - { parentEndpoint: GET:/lettuce-6.5.x-scenario/case/lettuce-case, networkAddress: '', refType: CrossThread, + parentSpanId: 6, parentTraceSegmentId: not null, parentServiceInstance: not + null, parentService: not null, traceId: not null } + - segmentId: not null + spans: + - operationName: Lettuce/GET + parentSpanId: 0 + spanId: 1 + spanLayer: Cache + startTime: not null + endTime: not null + componentId: 57 + isError: false + spanType: Exit + peer: not null + skipAnalysis: false + tags: + - {key: cache.type, value: Redis} + - {key: cache.key, value: key} + - {key: cache.cmd, value: GET} + - {key: cache.op, value: read} + - operationName: Lettuce/SET + parentSpanId: 0 + spanId: 2 + spanLayer: Cache + startTime: not null + endTime: not null + componentId: 57 + isError: false + spanType: Exit + peer: not null + skipAnalysis: false + tags: + - { key: cache.type, value: Redis } + - { key: cache.key, value: key0 } + - { key: cache.cmd, value: SET } + - { key: cache.op, value: write } + - operationName: Lettuce/SET + parentSpanId: 0 + spanId: 3 + spanLayer: Cache + startTime: not null + endTime: not null + componentId: 57 + isError: false + spanType: Exit + peer: not null + skipAnalysis: false + tags: + - { key: cache.type, value: Redis } + - { key: cache.key, value: key1 } + - { key: cache.cmd, value: SET } + - { key: cache.op, value: write } + - operationName: Lettuce/Reactive/createMono + parentSpanId: 0 + spanId: 4 + spanLayer: Cache + startTime: not null + endTime: not null + componentId: 57 + isError: false + spanType: Local + skipAnalysis: false + - operationName: Lettuce/Reactive/createMono + parentSpanId: 0 + spanId: 5 + spanLayer: Cache + startTime: not null + endTime: not null + componentId: 57 + isError: false + spanType: Local + skipAnalysis: false + - operationName: Lettuce/Reactive/createMono + parentSpanId: 0 + spanId: 6 + spanLayer: Cache + startTime: not null + endTime: not null + componentId: 57 + isError: false + spanType: Local + skipAnalysis: false + - operationName: Lettuce/GET + parentSpanId: 7 + spanId: 8 + spanLayer: Cache + startTime: not null + endTime: not null + componentId: 57 + isError: false + spanType: Exit + peer: not null + skipAnalysis: false + tags: + - { key: cache.type, value: Redis } + - { key: cache.key, value: key } + - { key: cache.cmd, value: GET } + - { key: cache.op, value: read } + - operationName: RedisReactive/local + parentSpanId: 0 + spanId: 7 + isError: false + spanType: Local + - operationName: GET:/lettuce-6.5.x-scenario/case/lettuce-case + parentSpanId: -1 + spanId: 0 + spanLayer: Http + startTime: not null + endTime: not null + componentId: 1 + isError: false + spanType: Entry + peer: '' + skipAnalysis: false + tags: + - {key: url, value: 'http://localhost:8080/lettuce-6.5.x-scenario/case/lettuce-case'} + - {key: http.method, value: GET} + - {key: http.status_code, value: '200'} diff --git a/test/plugin/scenarios/oracle-scenario/configuration.yml b/test/plugin/scenarios/lettuce-6.5.x-scenario/configuration.yml similarity index 71% rename from test/plugin/scenarios/oracle-scenario/configuration.yml rename to test/plugin/scenarios/lettuce-6.5.x-scenario/configuration.yml index 0b112b9e7b..6ddf05ae02 100644 --- a/test/plugin/scenarios/oracle-scenario/configuration.yml +++ b/test/plugin/scenarios/lettuce-6.5.x-scenario/configuration.yml @@ -15,16 +15,14 @@ # limitations under the License. type: jvm -entryService: http://localhost:8080/oracle-scenario/case/oracle -healthCheck: http://localhost:8080/oracle-scenario/case/healthCheck +entryService: http://localhost:8080/lettuce-6.5.x-scenario/case/lettuce-case +healthCheck: http://localhost:8080/lettuce-6.5.x-scenario/case/healthCheck startScript: ./bin/startup.sh environment: - - oracle.address=oracle-server:1521 - - oracle.username=system - - oracle.password=oracle + - REDIS_SERVERS=redis-server:6379 +depends_on: + - redis-server dependencies: - oracle-server: - image: deepdiver/docker-oracle-xe-11g - hostname: oracle-server - expose: - - "1521" + redis-server: + image: redis:3.2.9-alpine + hostname: redis-server diff --git a/test/plugin/scenarios/lettuce-6.5.x-scenario/pom.xml b/test/plugin/scenarios/lettuce-6.5.x-scenario/pom.xml new file mode 100644 index 0000000000..248aded74a --- /dev/null +++ b/test/plugin/scenarios/lettuce-6.5.x-scenario/pom.xml @@ -0,0 +1,136 @@ + + + + 4.0.0 + + org.apache.skywalking + lettuce-6.5.x-scenario + 1.0.0 + + + UTF-8 + 1.8 + 3.8.1 + 6.5.0.RELEASE + ${test.framework.version} + 2.6.2 + 4.3.8.RELEASE + 2.1.6.RELEASE + 3.4.2 + + + lettuce-6.5.x-scenario + + + + com.squareup.okhttp3 + okhttp + ${okhttp.version} + + + io.lettuce + lettuce-core + ${test.framework.version} + + + + org.springframework.boot + spring-boot-starter + ${spring.boot.version} + + + org.apache.logging.log4j + log4j-api + ${log4j.version} + + + org.apache.logging.log4j + log4j-core + ${log4j.version} + + + org.apache.logging.log4j + log4j-slf4j-impl + ${log4j.version} + + + org.apache.logging.log4j + log4j-jcl + ${log4j.version} + + + org.springframework.boot + spring-boot-starter-tomcat + ${spring.boot.version} + + + org.springframework.boot + spring-boot-starter-web + ${spring.boot.version} + + + + + lettuce-6.5.x-scenario + + + org.springframework.boot + spring-boot-maven-plugin + ${spring.boot.version} + + + + repackage + + + + + + maven-compiler-plugin + ${maven-compiler-plugin.version} + + ${compiler.version} + ${compiler.version} + ${project.build.sourceEncoding} + + + + org.apache.maven.plugins + maven-assembly-plugin + + + assemble + package + + single + + + + src/main/assembly/assembly.xml + + ./target/ + + + + + + + \ No newline at end of file diff --git a/test/plugin/scenarios/lettuce-6.5.x-scenario/src/main/assembly/assembly.xml b/test/plugin/scenarios/lettuce-6.5.x-scenario/src/main/assembly/assembly.xml new file mode 100644 index 0000000000..a48d54c644 --- /dev/null +++ b/test/plugin/scenarios/lettuce-6.5.x-scenario/src/main/assembly/assembly.xml @@ -0,0 +1,41 @@ + + + + + zip + + + + + ./bin + 0775 + + + + + + ${project.build.directory}/lettuce-6.5.x-scenario.jar + ./libs + 0775 + + + diff --git a/test/plugin/scenarios/lettuce-6.5.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/lettuce/Application.java b/test/plugin/scenarios/lettuce-6.5.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/lettuce/Application.java new file mode 100644 index 0000000000..f96b624013 --- /dev/null +++ b/test/plugin/scenarios/lettuce-6.5.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/lettuce/Application.java @@ -0,0 +1,30 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.testcase.lettuce; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class Application { + + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } +} diff --git a/test/plugin/scenarios/lettuce-6.5.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/lettuce/controller/LettuceController.java b/test/plugin/scenarios/lettuce-6.5.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/lettuce/controller/LettuceController.java new file mode 100644 index 0000000000..d2cc201025 --- /dev/null +++ b/test/plugin/scenarios/lettuce-6.5.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/lettuce/controller/LettuceController.java @@ -0,0 +1,88 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.testcase.lettuce.controller; + +import io.lettuce.core.LettuceFutures; +import io.lettuce.core.RedisClient; +import io.lettuce.core.RedisFuture; +import io.lettuce.core.api.StatefulRedisConnection; +import io.lettuce.core.api.async.RedisAsyncCommands; +import io.lettuce.core.api.reactive.RedisReactiveCommands; +import io.lettuce.core.api.sync.RedisCommands; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.TimeUnit; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.PropertySource; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseBody; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +@Controller +@RequestMapping("/case") +@PropertySource("classpath:application.properties") +public class LettuceController { + + @Value("${redis.servers:127.0.0.1:6379}") + private String address; + + @RequestMapping("/lettuce-case") + @ResponseBody + public String lettuceCase() { + RedisClient redisClient = RedisClient.create("redis://" + address); + StatefulRedisConnection connection0 = redisClient.connect(); + RedisCommands syncCommand = connection0.sync(); + syncCommand.get("key"); + + StatefulRedisConnection connection1 = redisClient.connect(); + RedisAsyncCommands asyncCommands = connection1.async(); + asyncCommands.setAutoFlushCommands(false); + List> futures = new ArrayList<>(); + futures.add(asyncCommands.set("key0", "value0")); + futures.add(asyncCommands.set("key1", "value1")); + asyncCommands.flushCommands(); + LettuceFutures.awaitAll(5, TimeUnit.SECONDS, futures.toArray(new RedisFuture[futures.size()])); + + StatefulRedisConnection connection2 = redisClient.connect(); + RedisReactiveCommands reactiveCommands = connection2.reactive(); + + Mono result = reactiveCommands.get("key") + .then(Flux.concat( + reactiveCommands.set("key0", "value0"), + reactiveCommands.set("key1", "value1") + ).then()) + .thenReturn("Success"); + + result.block(); + + connection0.close(); + connection1.close(); + connection2.close(); + redisClient.shutdown(); + return "Success"; + } + + @RequestMapping("/healthCheck") + @ResponseBody + public String healthCheck() { + return "healthCheck"; + } +} diff --git a/test/plugin/scenarios/lettuce-6.5.x-scenario/src/main/resources/application.properties b/test/plugin/scenarios/lettuce-6.5.x-scenario/src/main/resources/application.properties new file mode 100644 index 0000000000..0b168c7fdd --- /dev/null +++ b/test/plugin/scenarios/lettuce-6.5.x-scenario/src/main/resources/application.properties @@ -0,0 +1,19 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# +server.port=8080 +server.servlet.context-path=/lettuce-6.5.x-scenario \ No newline at end of file diff --git a/test/plugin/scenarios/lettuce-6.5.x-scenario/src/main/resources/log4j2.xml b/test/plugin/scenarios/lettuce-6.5.x-scenario/src/main/resources/log4j2.xml new file mode 100644 index 0000000000..9849ed5a8a --- /dev/null +++ b/test/plugin/scenarios/lettuce-6.5.x-scenario/src/main/resources/log4j2.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/plugin/scenarios/lettuce-6.5.x-scenario/support-version.list b/test/plugin/scenarios/lettuce-6.5.x-scenario/support-version.list new file mode 100644 index 0000000000..df1aa5eb09 --- /dev/null +++ b/test/plugin/scenarios/lettuce-6.5.x-scenario/support-version.list @@ -0,0 +1,20 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +6.5.0.RELEASE +6.5.5.RELEASE +6.6.0.RELEASE +6.7.1.RELEASE \ No newline at end of file diff --git a/test/plugin/scenarios/lettuce-scenario/config/expectedData.yaml b/test/plugin/scenarios/lettuce-scenario/config/expectedData.yaml index 43db792872..884191cfe3 100644 --- a/test/plugin/scenarios/lettuce-scenario/config/expectedData.yaml +++ b/test/plugin/scenarios/lettuce-scenario/config/expectedData.yaml @@ -34,6 +34,60 @@ segmentItems: - {key: url, value: 'http://localhost:8080/lettuce-scenario/case/healthCheck'} - {key: http.method, value: HEAD} - {key: http.status_code, value: '200'} + - segmentId: not null + spans: + - operationName: Lettuce/SET + parentSpanId: 0 + spanId: 1 + spanLayer: Cache + startTime: not null + endTime: not null + componentId: 57 + isError: false + spanType: Exit + peer: not null + skipAnalysis: false + tags: + - { key: cache.type, value: Redis } + - { key: cache.key, value: key0 } + - { key: cache.cmd, value: SET } + - { key: cache.op, value: write } + - operationName: RedisReactive/local + parentSpanId: -1 + spanId: 0 + isError: false + spanType: Local + refs: + - { parentEndpoint: GET:/lettuce-scenario/case/lettuce-case, networkAddress: '', refType: CrossThread, + parentSpanId: 5, parentTraceSegmentId: not null, parentServiceInstance: not + null, parentService: not null, traceId: not null } + - segmentId: not null + spans: + - operationName: Lettuce/SET + parentSpanId: 0 + spanId: 1 + spanLayer: Cache + startTime: not null + endTime: not null + componentId: 57 + isError: false + spanType: Exit + peer: not null + skipAnalysis: false + tags: + - { key: cache.type, value: Redis } + - { key: cache.key, value: key1 } + - { key: cache.cmd, value: SET } + - { key: cache.op, value: write } + - operationName: RedisReactive/local + parentSpanId: -1 + spanId: 0 + isError: false + spanType: Local + refs: + - { parentEndpoint: GET:/lettuce-scenario/case/lettuce-case, networkAddress: '', refType: CrossThread, + parentSpanId: 6, parentTraceSegmentId: not null, parentServiceInstance: not + null, parentService: not null, traceId: not null } - segmentId: not null spans: - operationName: Lettuce/GET @@ -84,6 +138,57 @@ segmentItems: - { key: cache.key, value: key1 } - { key: cache.cmd, value: SET } - { key: cache.op, value: write } + - operationName: Lettuce/Reactive/createMono + parentSpanId: 0 + spanId: 4 + spanLayer: Cache + startTime: not null + endTime: not null + componentId: 57 + isError: false + spanType: Local + skipAnalysis: false + - operationName: Lettuce/Reactive/createMono + parentSpanId: 0 + spanId: 5 + spanLayer: Cache + startTime: not null + endTime: not null + componentId: 57 + isError: false + spanType: Local + skipAnalysis: false + - operationName: Lettuce/Reactive/createMono + parentSpanId: 0 + spanId: 6 + spanLayer: Cache + startTime: not null + endTime: not null + componentId: 57 + isError: false + spanType: Local + skipAnalysis: false + - operationName: Lettuce/GET + parentSpanId: 7 + spanId: 8 + spanLayer: Cache + startTime: not null + endTime: not null + componentId: 57 + isError: false + spanType: Exit + peer: not null + skipAnalysis: false + tags: + - { key: cache.type, value: Redis } + - { key: cache.key, value: key } + - { key: cache.cmd, value: GET } + - { key: cache.op, value: read } + - operationName: RedisReactive/local + parentSpanId: 0 + spanId: 7 + isError: false + spanType: Local - operationName: GET:/lettuce-scenario/case/lettuce-case parentSpanId: -1 spanId: 0 diff --git a/test/plugin/scenarios/lettuce-scenario/src/main/java/org/apache/skywalking/apm/testcase/lettuce/controller/LettuceController.java b/test/plugin/scenarios/lettuce-scenario/src/main/java/org/apache/skywalking/apm/testcase/lettuce/controller/LettuceController.java index c2f391b07c..d2cc201025 100644 --- a/test/plugin/scenarios/lettuce-scenario/src/main/java/org/apache/skywalking/apm/testcase/lettuce/controller/LettuceController.java +++ b/test/plugin/scenarios/lettuce-scenario/src/main/java/org/apache/skywalking/apm/testcase/lettuce/controller/LettuceController.java @@ -23,6 +23,7 @@ import io.lettuce.core.RedisFuture; import io.lettuce.core.api.StatefulRedisConnection; import io.lettuce.core.api.async.RedisAsyncCommands; +import io.lettuce.core.api.reactive.RedisReactiveCommands; import io.lettuce.core.api.sync.RedisCommands; import java.util.ArrayList; import java.util.List; @@ -32,6 +33,8 @@ import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; @Controller @RequestMapping("/case") @@ -58,8 +61,21 @@ public String lettuceCase() { asyncCommands.flushCommands(); LettuceFutures.awaitAll(5, TimeUnit.SECONDS, futures.toArray(new RedisFuture[futures.size()])); + StatefulRedisConnection connection2 = redisClient.connect(); + RedisReactiveCommands reactiveCommands = connection2.reactive(); + + Mono result = reactiveCommands.get("key") + .then(Flux.concat( + reactiveCommands.set("key0", "value0"), + reactiveCommands.set("key1", "value1") + ).then()) + .thenReturn("Success"); + + result.block(); + connection0.close(); connection1.close(); + connection2.close(); redisClient.shutdown(); return "Success"; } diff --git a/test/plugin/scenarios/lettuce-scenario/support-version.list b/test/plugin/scenarios/lettuce-scenario/support-version.list index add9033ee2..88e41e9ab3 100644 --- a/test/plugin/scenarios/lettuce-scenario/support-version.list +++ b/test/plugin/scenarios/lettuce-scenario/support-version.list @@ -17,4 +17,5 @@ 5.2.1.RELEASE 5.1.8.RELEASE 5.0.5.RELEASE -6.1.4.RELEASE \ No newline at end of file +6.1.4.RELEASE +6.4.2.RELEASE \ No newline at end of file diff --git a/test/plugin/scenarios/lettuce-webflux-5x-scenario/bin/startup.sh b/test/plugin/scenarios/lettuce-webflux-5x-scenario/bin/startup.sh new file mode 100644 index 0000000000..d719f14162 --- /dev/null +++ b/test/plugin/scenarios/lettuce-webflux-5x-scenario/bin/startup.sh @@ -0,0 +1,21 @@ +#!/bin/bash +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +home="$(cd "$(dirname $0)"; pwd)" + +java -Dredis.host=${REDIS_SERVERS} -jar -Dskywalking.plugin.lettuce.trace_redis_parameters=true ${agent_opts} ${home}/../libs/lettuce-webflux-5x-scenario.jar & \ No newline at end of file diff --git a/test/plugin/scenarios/lettuce-webflux-5x-scenario/config/expectedData.yaml b/test/plugin/scenarios/lettuce-webflux-5x-scenario/config/expectedData.yaml new file mode 100644 index 0000000000..cdc04a6242 --- /dev/null +++ b/test/plugin/scenarios/lettuce-webflux-5x-scenario/config/expectedData.yaml @@ -0,0 +1,134 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +segmentItems: + - serviceName: lettuce-webflux-5x-scenario + segmentSize: nq 0 + segments: + - segmentId: not null + spans: + - operationName: /case/healthCheck + parentSpanId: -1 + spanId: 0 + spanLayer: Http + startTime: not null + endTime: not null + componentId: 67 + isError: false + spanType: Entry + peer: '' + skipAnalysis: false + tags: + - {key: url, value: 'http://localhost:8080/case/healthCheck'} + - {key: http.method, value: HEAD} + - {key: http.status_code, value: '200'} + - segmentId: not null + spans: + - operationName: Lettuce/GET + parentSpanId: 0 + spanId: 1 + spanLayer: Cache + startTime: not null + endTime: not null + componentId: 57 + isError: false + spanType: Exit + peer: not null + skipAnalysis: false + tags: + - {key: cache.type, value: Redis} + - {key: cache.key, value: key} + - {key: cache.cmd, value: GET} + - {key: cache.op, value: read} + - operationName: RedisReactive/local + parentSpanId: -1 + spanId: 0 + isError: false + spanType: Local + refs: + - { parentEndpoint: /case/lettuce-case, networkAddress: '', refType: CrossThread, + parentSpanId: 0, parentTraceSegmentId: not null, parentServiceInstance: not + null, parentService: not null, traceId: not null } + - segmentId: not null + spans: + - operationName: Lettuce/SET + parentSpanId: 0 + spanId: 1 + spanLayer: Cache + startTime: not null + endTime: not null + componentId: 57 + isError: false + spanType: Exit + peer: not null + skipAnalysis: false + tags: + - { key: cache.type, value: Redis } + - { key: cache.key, value: key0 } + - { key: cache.cmd, value: SET } + - { key: cache.op, value: write } + - operationName: RedisReactive/local + parentSpanId: -1 + spanId: 0 + isError: false + spanType: Local + refs: + - { parentEndpoint: /case/lettuce-case, networkAddress: '', refType: CrossThread, + parentSpanId: 0, parentTraceSegmentId: not null, parentServiceInstance: not + null, parentService: not null, traceId: not null } + - segmentId: not null + spans: + - operationName: Lettuce/SET + parentSpanId: 0 + spanId: 1 + spanLayer: Cache + startTime: not null + endTime: not null + componentId: 57 + isError: false + spanType: Exit + peer: not null + skipAnalysis: false + tags: + - { key: cache.type, value: Redis } + - { key: cache.key, value: key1 } + - { key: cache.cmd, value: SET } + - { key: cache.op, value: write } + - operationName: RedisReactive/local + parentSpanId: -1 + spanId: 0 + isError: false + spanType: Local + refs: + - { parentEndpoint: /case/lettuce-case, networkAddress: '', refType: CrossThread, + parentSpanId: 0, parentTraceSegmentId: not null, parentServiceInstance: not + null, parentService: not null, traceId: not null } + - segmentId: not null + spans: + - operationName: /case/lettuce-case + parentSpanId: -1 + spanId: 0 + spanLayer: Http + startTime: not null + endTime: not null + componentId: 67 + isError: false + spanType: Entry + peer: '' + skipAnalysis: false + tags: + - {key: url, value: 'http://localhost:8080/case/lettuce-case'} + - {key: http.method, value: GET} + - {key: http.status_code, value: '200'} diff --git a/test/plugin/scenarios/lettuce-webflux-5x-scenario/configuration.yml b/test/plugin/scenarios/lettuce-webflux-5x-scenario/configuration.yml new file mode 100644 index 0000000000..d016d55400 --- /dev/null +++ b/test/plugin/scenarios/lettuce-webflux-5x-scenario/configuration.yml @@ -0,0 +1,30 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +type: jvm +entryService: http://localhost:8080/case/lettuce-case +healthCheck: http://localhost:8080/case/healthCheck +startScript: ./bin/startup.sh +withPlugins: apm-spring-webflux-5.x-*.jar +runningMode: with_optional +environment: + - REDIS_SERVERS=redis-server:6379 +depends_on: + - redis-server +dependencies: + redis-server: + image: redis:3.2.9-alpine + hostname: redis-server diff --git a/test/plugin/scenarios/lettuce-webflux-5x-scenario/pom.xml b/test/plugin/scenarios/lettuce-webflux-5x-scenario/pom.xml new file mode 100644 index 0000000000..bf393539a2 --- /dev/null +++ b/test/plugin/scenarios/lettuce-webflux-5x-scenario/pom.xml @@ -0,0 +1,98 @@ + + + + 4.0.0 + + org.apache.skywalking + lettuce-webflux-5x-scenario + 5.0.0 + + + UTF-8 + 1.8 + 3.8.1 + 5.1.8.RELEASE + ${test.framework.version} + 2.1.6.RELEASE + + + skywalking-lettuce-webflux-5x-scenario + + + + io.lettuce + lettuce-core + ${test.framework.version} + + + + org.springframework.boot + spring-boot-starter-webflux + ${spring.boot.version} + + + + + lettuce-webflux-5x-scenario + + + org.springframework.boot + spring-boot-maven-plugin + ${spring.boot.version} + + + + repackage + + + + + + maven-compiler-plugin + ${maven-compiler-plugin.version} + + ${compiler.version} + ${compiler.version} + ${project.build.sourceEncoding} + + + + org.apache.maven.plugins + maven-assembly-plugin + + + assemble + package + + single + + + + src/main/assembly/assembly.xml + + ./target/ + + + + + + + \ No newline at end of file diff --git a/test/plugin/scenarios/lettuce-webflux-5x-scenario/src/main/assembly/assembly.xml b/test/plugin/scenarios/lettuce-webflux-5x-scenario/src/main/assembly/assembly.xml new file mode 100644 index 0000000000..ffc45011aa --- /dev/null +++ b/test/plugin/scenarios/lettuce-webflux-5x-scenario/src/main/assembly/assembly.xml @@ -0,0 +1,41 @@ + + + + + zip + + + + + ./bin + 0775 + + + + + + ${project.build.directory}/lettuce-webflux-5x-scenario.jar + ./libs + 0775 + + + diff --git a/test/plugin/scenarios/lettuce-webflux-5x-scenario/src/main/java/org/apache/skywalking/apm/testcase/lettuce/Application.java b/test/plugin/scenarios/lettuce-webflux-5x-scenario/src/main/java/org/apache/skywalking/apm/testcase/lettuce/Application.java new file mode 100644 index 0000000000..dae1948397 --- /dev/null +++ b/test/plugin/scenarios/lettuce-webflux-5x-scenario/src/main/java/org/apache/skywalking/apm/testcase/lettuce/Application.java @@ -0,0 +1,34 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.testcase.lettuce; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class Application { + + public static void main(String[] args) { + try { + SpringApplication.run(Application.class, args); + } catch (Exception e) { + // Never do this + } + } +} diff --git a/test/plugin/scenarios/lettuce-webflux-5x-scenario/src/main/java/org/apache/skywalking/apm/testcase/lettuce/RedisClientConfig.java b/test/plugin/scenarios/lettuce-webflux-5x-scenario/src/main/java/org/apache/skywalking/apm/testcase/lettuce/RedisClientConfig.java new file mode 100644 index 0000000000..0898d08338 --- /dev/null +++ b/test/plugin/scenarios/lettuce-webflux-5x-scenario/src/main/java/org/apache/skywalking/apm/testcase/lettuce/RedisClientConfig.java @@ -0,0 +1,37 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.testcase.lettuce; + +import io.lettuce.core.RedisClient; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * + */ +@Configuration +public class RedisClientConfig { + + @Bean(destroyMethod = "shutdown") + public RedisClient redisClient(@Value("${redis.servers:127.0.0.1:6379}") String address) { + + return RedisClient.create("redis://" + address); + } +} diff --git a/test/plugin/scenarios/lettuce-webflux-5x-scenario/src/main/java/org/apache/skywalking/apm/testcase/lettuce/controller/LettuceReactiveController.java b/test/plugin/scenarios/lettuce-webflux-5x-scenario/src/main/java/org/apache/skywalking/apm/testcase/lettuce/controller/LettuceReactiveController.java new file mode 100644 index 0000000000..2664bd2713 --- /dev/null +++ b/test/plugin/scenarios/lettuce-webflux-5x-scenario/src/main/java/org/apache/skywalking/apm/testcase/lettuce/controller/LettuceReactiveController.java @@ -0,0 +1,68 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.testcase.lettuce.controller; + +import io.lettuce.core.RedisClient; +import io.lettuce.core.api.reactive.RedisReactiveCommands; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.PropertySource; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +import javax.annotation.Resource; + +@RestController +@RequestMapping("/case") +@PropertySource("classpath:application.properties") +public class LettuceReactiveController { + + @Value("${redis.servers:127.0.0.1:6379}") + private String address; + + @Resource + private RedisClient redisClient; + + @GetMapping("/lettuce-case") + public Mono lettuceCase() { + + return Mono.usingWhen( + Mono.fromCallable(() -> redisClient.connect()), + connection -> { + RedisReactiveCommands cmd = connection.reactive(); + return cmd.get("key") + .then(Flux.concat( + cmd.set("key0", "value0"), + cmd.set("key1", "value1") + ).then()) + .thenReturn("Success"); + }, + connection -> Mono.fromFuture(connection.closeAsync()), + connection -> Mono.fromFuture(connection.closeAsync()), + connection -> Mono.fromFuture(connection.closeAsync()) + ); + } + + @GetMapping("/healthCheck") + public Mono healthCheck() { + return Mono.just("healthCheck"); + } +} diff --git a/test/plugin/scenarios/lettuce-webflux-5x-scenario/src/main/resources/application.properties b/test/plugin/scenarios/lettuce-webflux-5x-scenario/src/main/resources/application.properties new file mode 100644 index 0000000000..d3b193de12 --- /dev/null +++ b/test/plugin/scenarios/lettuce-webflux-5x-scenario/src/main/resources/application.properties @@ -0,0 +1,18 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# +server.port=8080 diff --git a/test/plugin/scenarios/lettuce-webflux-5x-scenario/src/main/resources/log4j2.xml b/test/plugin/scenarios/lettuce-webflux-5x-scenario/src/main/resources/log4j2.xml new file mode 100644 index 0000000000..9849ed5a8a --- /dev/null +++ b/test/plugin/scenarios/lettuce-webflux-5x-scenario/src/main/resources/log4j2.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/plugin/scenarios/lettuce-webflux-5x-scenario/support-version.list b/test/plugin/scenarios/lettuce-webflux-5x-scenario/support-version.list new file mode 100644 index 0000000000..a7e848bbbf --- /dev/null +++ b/test/plugin/scenarios/lettuce-webflux-5x-scenario/support-version.list @@ -0,0 +1,19 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +5.1.8.RELEASE +5.2.1.RELEASE +6.1.4.RELEASE \ No newline at end of file diff --git a/test/plugin/scenarios/lettuce-webflux-6x-scenario/bin/startup.sh b/test/plugin/scenarios/lettuce-webflux-6x-scenario/bin/startup.sh new file mode 100644 index 0000000000..686b12f52a --- /dev/null +++ b/test/plugin/scenarios/lettuce-webflux-6x-scenario/bin/startup.sh @@ -0,0 +1,21 @@ +#!/bin/bash +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +home="$(cd "$(dirname $0)"; pwd)" + +java -Dredis.host=${REDIS_SERVERS} -jar -Dskywalking.plugin.lettuce.trace_redis_parameters=true ${agent_opts} ${home}/../libs/lettuce-webflux-6x-scenario.jar & \ No newline at end of file diff --git a/test/plugin/scenarios/lettuce-webflux-6x-scenario/config/expectedData.yaml b/test/plugin/scenarios/lettuce-webflux-6x-scenario/config/expectedData.yaml new file mode 100644 index 0000000000..e5857aa054 --- /dev/null +++ b/test/plugin/scenarios/lettuce-webflux-6x-scenario/config/expectedData.yaml @@ -0,0 +1,135 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +segmentItems: + - serviceName: lettuce-webflux-6x-scenario + segmentSize: nq 0 + segments: + - segmentId: not null + spans: + - operationName: /case/healthCheck + parentSpanId: -1 + spanId: 0 + spanLayer: Http + startTime: not null + endTime: not null + componentId: 67 + isError: false + spanType: Entry + peer: '' + skipAnalysis: false + tags: + - { key: url, value: 'http://localhost:8080/case/healthCheck' } + - { key: http.method, value: HEAD } + - { key: http.status_code, value: '200' } + - segmentId: not null + spans: + - operationName: Lettuce/GET + parentSpanId: 0 + spanId: 1 + spanLayer: Cache + startTime: not null + endTime: not null + componentId: 57 + isError: false + spanType: Exit + peer: not null + skipAnalysis: false + tags: + - { key: cache.type, value: Redis } + - { key: cache.key, value: key } + - { key: cache.cmd, value: GET } + - { key: cache.op, value: read } + - operationName: RedisReactive/local + parentSpanId: -1 + spanId: 0 + isError: false + spanType: Local + refs: + - { parentEndpoint: /case/lettuce-case, networkAddress: '', refType: CrossThread, + parentSpanId: 0, parentTraceSegmentId: not null, parentServiceInstance: not + null, parentService: not null, traceId: not null } + - segmentId: not null + spans: + - operationName: Lettuce/SET + parentSpanId: 0 + spanId: 1 + spanLayer: Cache + startTime: not null + endTime: not null + componentId: 57 + isError: false + spanType: Exit + peer: not null + skipAnalysis: false + tags: + - { key: cache.type, value: Redis } + - { key: cache.key, value: key0 } + - { key: cache.cmd, value: SET } + - { key: cache.op, value: write } + - operationName: RedisReactive/local + parentSpanId: -1 + spanId: 0 + isError: false + spanType: Local + refs: + - { parentEndpoint: /case/lettuce-case, networkAddress: '', refType: CrossThread, + parentSpanId: 0, parentTraceSegmentId: not null, parentServiceInstance: not + null, parentService: not null, traceId: not null } + - segmentId: not null + spans: + - operationName: Lettuce/SET + parentSpanId: 0 + spanId: 1 + spanLayer: Cache + startTime: not null + endTime: not null + componentId: 57 + isError: false + spanType: Exit + peer: not null + skipAnalysis: false + tags: + - { key: cache.type, value: Redis } + - { key: cache.key, value: key1 } + - { key: cache.cmd, value: SET } + - { key: cache.op, value: write } + - operationName: RedisReactive/local + parentSpanId: -1 + spanId: 0 + isError: false + spanType: Local + refs: + - { parentEndpoint: /case/lettuce-case, networkAddress: '', refType: CrossThread, + parentSpanId: 0, parentTraceSegmentId: not null, parentServiceInstance: not + null, parentService: not null, traceId: not null } + - segmentId: not null + spans: + - operationName: /case/lettuce-case + parentSpanId: -1 + spanId: 0 + spanLayer: Http + startTime: not null + endTime: not null + componentId: 67 + isError: false + spanType: Entry + peer: '' + skipAnalysis: false + tags: + - { key: url, value: 'http://localhost:8080/case/lettuce-case' } + - { key: http.method, value: GET } + - { key: http.status_code, value: '200' } + \ No newline at end of file diff --git a/test/plugin/scenarios/lettuce-webflux-6x-scenario/configuration.yml b/test/plugin/scenarios/lettuce-webflux-6x-scenario/configuration.yml new file mode 100644 index 0000000000..a3eecec7bb --- /dev/null +++ b/test/plugin/scenarios/lettuce-webflux-6x-scenario/configuration.yml @@ -0,0 +1,30 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +type: jvm +entryService: http://localhost:8080/case/lettuce-case +healthCheck: http://localhost:8080/case/healthCheck +startScript: ./bin/startup.sh +withPlugins: apm-spring-webflux-6.x-*.jar +runningMode: with_optional +environment: + - REDIS_SERVERS=redis-server:6379 +depends_on: + - redis-server +dependencies: + redis-server: + image: redis:3.2.9-alpine + hostname: redis-server diff --git a/test/plugin/scenarios/lettuce-webflux-6x-scenario/pom.xml b/test/plugin/scenarios/lettuce-webflux-6x-scenario/pom.xml new file mode 100644 index 0000000000..2e05615440 --- /dev/null +++ b/test/plugin/scenarios/lettuce-webflux-6x-scenario/pom.xml @@ -0,0 +1,117 @@ + + + + 4.0.0 + + org.apache.skywalking + lettuce-webflux-6x-scenario + 5.0.0 + + + UTF-8 + 17 + 3.8.1 + 6.4.2.RELEASE + ${test.framework.version} + 3.0.13 + + + skywalking-lettuce-webflux-6x-scenario + + + + + org.springframework.boot + spring-boot-dependencies + ${spring.boot.version} + pom + import + + + + + + + io.lettuce + lettuce-core + ${test.framework.version} + + + + org.springframework.boot + spring-boot-starter-webflux + ${spring.boot.version} + + + org.apache.logging.log4j + log4j-api + + + + + + + + lettuce-webflux-6x-scenario + + + org.springframework.boot + spring-boot-maven-plugin + ${spring.boot.version} + + + + repackage + + + + + + maven-compiler-plugin + ${maven-compiler-plugin.version} + + ${compiler.version} + ${compiler.version} + ${project.build.sourceEncoding} + + + + org.apache.maven.plugins + maven-assembly-plugin + + + assemble + package + + single + + + + src/main/assembly/assembly.xml + + ./target/ + + + + + + + \ No newline at end of file diff --git a/test/plugin/scenarios/lettuce-webflux-6x-scenario/src/main/assembly/assembly.xml b/test/plugin/scenarios/lettuce-webflux-6x-scenario/src/main/assembly/assembly.xml new file mode 100644 index 0000000000..87c9882cab --- /dev/null +++ b/test/plugin/scenarios/lettuce-webflux-6x-scenario/src/main/assembly/assembly.xml @@ -0,0 +1,41 @@ + + + + + zip + + + + + ./bin + 0775 + + + + + + ${project.build.directory}/lettuce-webflux-6x-scenario.jar + ./libs + 0775 + + + diff --git a/test/plugin/scenarios/lettuce-webflux-6x-scenario/src/main/java/org/apache/skywalking/apm/testcase/lettuce/Application.java b/test/plugin/scenarios/lettuce-webflux-6x-scenario/src/main/java/org/apache/skywalking/apm/testcase/lettuce/Application.java new file mode 100644 index 0000000000..f96b624013 --- /dev/null +++ b/test/plugin/scenarios/lettuce-webflux-6x-scenario/src/main/java/org/apache/skywalking/apm/testcase/lettuce/Application.java @@ -0,0 +1,30 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.testcase.lettuce; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class Application { + + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } +} diff --git a/test/plugin/scenarios/lettuce-webflux-6x-scenario/src/main/java/org/apache/skywalking/apm/testcase/lettuce/RedisClientConfig.java b/test/plugin/scenarios/lettuce-webflux-6x-scenario/src/main/java/org/apache/skywalking/apm/testcase/lettuce/RedisClientConfig.java new file mode 100644 index 0000000000..0898d08338 --- /dev/null +++ b/test/plugin/scenarios/lettuce-webflux-6x-scenario/src/main/java/org/apache/skywalking/apm/testcase/lettuce/RedisClientConfig.java @@ -0,0 +1,37 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.testcase.lettuce; + +import io.lettuce.core.RedisClient; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * + */ +@Configuration +public class RedisClientConfig { + + @Bean(destroyMethod = "shutdown") + public RedisClient redisClient(@Value("${redis.servers:127.0.0.1:6379}") String address) { + + return RedisClient.create("redis://" + address); + } +} diff --git a/test/plugin/scenarios/lettuce-webflux-6x-scenario/src/main/java/org/apache/skywalking/apm/testcase/lettuce/controller/LettuceReactiveController.java b/test/plugin/scenarios/lettuce-webflux-6x-scenario/src/main/java/org/apache/skywalking/apm/testcase/lettuce/controller/LettuceReactiveController.java new file mode 100644 index 0000000000..697369fcf8 --- /dev/null +++ b/test/plugin/scenarios/lettuce-webflux-6x-scenario/src/main/java/org/apache/skywalking/apm/testcase/lettuce/controller/LettuceReactiveController.java @@ -0,0 +1,63 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.testcase.lettuce.controller; + +import io.lettuce.core.RedisClient; +import io.lettuce.core.api.reactive.RedisReactiveCommands; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +@RestController +@RequestMapping("/case") +public class LettuceReactiveController { + + @Value("${redis.servers:127.0.0.1:6379}") + private String address; + + @Autowired + private RedisClient redisClient; + + @GetMapping("/lettuce-case") + public Mono lettuceCase() { + + return Mono.usingWhen( + Mono.fromCallable(() -> redisClient.connect()), + connection -> { + RedisReactiveCommands cmd = connection.reactive(); + return cmd.get("key") + .then(Flux.concat( + cmd.set("key0", "value0"), + cmd.set("key1", "value1") + ).then()) + .thenReturn("Success"); + }, + connection -> Mono.fromFuture(connection.closeAsync()) + ); + } + + @GetMapping("/healthCheck") + public Mono healthCheck() { + return Mono.just("healthCheck"); + } +} diff --git a/test/plugin/scenarios/lettuce-webflux-6x-scenario/src/main/resources/application.properties b/test/plugin/scenarios/lettuce-webflux-6x-scenario/src/main/resources/application.properties new file mode 100644 index 0000000000..afdb52cef7 --- /dev/null +++ b/test/plugin/scenarios/lettuce-webflux-6x-scenario/src/main/resources/application.properties @@ -0,0 +1,18 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# +server.port=8080 \ No newline at end of file diff --git a/test/plugin/scenarios/lettuce-webflux-6x-scenario/support-version.list b/test/plugin/scenarios/lettuce-webflux-6x-scenario/support-version.list new file mode 100644 index 0000000000..2cba7f3294 --- /dev/null +++ b/test/plugin/scenarios/lettuce-webflux-6x-scenario/support-version.list @@ -0,0 +1,20 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +6.4.2.RELEASE +6.5.5.RELEASE +6.6.0.RELEASE +6.7.1.RELEASE \ No newline at end of file diff --git a/test/plugin/scenarios/micronaut-http-scenario/pom.xml b/test/plugin/scenarios/micronaut-http-scenario/pom.xml index f9706e1da6..f04874ebeb 100644 --- a/test/plugin/scenarios/micronaut-http-scenario/pom.xml +++ b/test/plugin/scenarios/micronaut-http-scenario/pom.xml @@ -29,13 +29,7 @@ micronaut-scenario jar - 1.8 - - - 1.8 netty - 1.8 - 1.8 org.apache.skywalking.apm.testcase.micronaut.Application @@ -134,7 +128,7 @@ org.apache.maven.plugins maven-compiler-plugin - 3.10.1 + 3.14.0 diff --git a/test/plugin/scenarios/mongodb-3.x-scenario/config/expectedData.yaml b/test/plugin/scenarios/mongodb-3.x-scenario/config/expectedData.yaml index e17d7162cf..a050100ce9 100644 --- a/test/plugin/scenarios/mongodb-3.x-scenario/config/expectedData.yaml +++ b/test/plugin/scenarios/mongodb-3.x-scenario/config/expectedData.yaml @@ -31,6 +31,7 @@ segmentItems: peer: mongodb-server:27017 tags: - {key: db.type, value: MongoDB} + - {key: db.instance, value: test-database} - {key: db.bind_vars, value: not null} skipAnalysis: 'false' - operationName: MongoDB/MixedBulkWriteOperation @@ -45,6 +46,8 @@ segmentItems: peer: mongodb-server:27017 tags: - {key: db.type, value: MongoDB} + - {key: db.instance, value: test-database} + - {key: db.collection, value: testCollection} - {key: db.bind_vars, value: not null} skipAnalysis: 'false' - operationName: MongoDB/FindOperation @@ -59,9 +62,11 @@ segmentItems: peer: mongodb-server:27017 tags: - {key: db.type, value: MongoDB} + - {key: db.instance, value: test-database} + - {key: db.collection, value: testCollection} - {key: db.bind_vars, value: not null} skipAnalysis: 'false' - - operationName: MongoDB/MixedBulkWriteOperation + - operationName: MongoDB/AggregateOperation parentSpanId: 0 spanId: 4 spanLayer: Database @@ -73,9 +78,11 @@ segmentItems: peer: mongodb-server:27017 tags: - {key: db.type, value: MongoDB} + - {key: db.instance, value: test-database} + - {key: db.collection, value: testCollection} - {key: db.bind_vars, value: not null} skipAnalysis: 'false' - - operationName: MongoDB/FindOperation + - operationName: MongoDB/MixedBulkWriteOperation parentSpanId: 0 spanId: 5 spanLayer: Database @@ -87,9 +94,11 @@ segmentItems: peer: mongodb-server:27017 tags: - {key: db.type, value: MongoDB} + - {key: db.instance, value: test-database} + - {key: db.collection, value: testCollection} - {key: db.bind_vars, value: not null} skipAnalysis: 'false' - - operationName: MongoDB/MixedBulkWriteOperation + - operationName: MongoDB/FindOperation parentSpanId: 0 spanId: 6 spanLayer: Database @@ -101,9 +110,11 @@ segmentItems: peer: mongodb-server:27017 tags: - {key: db.type, value: MongoDB} + - {key: db.instance, value: test-database} + - {key: db.collection, value: testCollection} - {key: db.bind_vars, value: not null} skipAnalysis: 'false' - - operationName: MongoDB/DropDatabaseOperation + - operationName: MongoDB/MixedBulkWriteOperation parentSpanId: 0 spanId: 7 spanLayer: Database @@ -115,6 +126,23 @@ segmentItems: peer: mongodb-server:27017 tags: - {key: db.type, value: MongoDB} + - {key: db.instance, value: test-database} + - {key: db.collection, value: testCollection} + - {key: db.bind_vars, value: not null} + skipAnalysis: 'false' + - operationName: MongoDB/DropDatabaseOperation + parentSpanId: 0 + spanId: 8 + spanLayer: Database + startTime: nq 0 + endTime: nq 0 + componentId: 42 + isError: false + spanType: Exit + peer: mongodb-server:27017 + tags: + - {key: db.type, value: MongoDB} + - {key: db.instance, value: test-database} - {key: db.bind_vars, value: not null} skipAnalysis: 'false' - operationName: GET:/mongodb-case/case/mongodb diff --git a/test/plugin/scenarios/mongodb-3.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/mongodb/controller/CaseController.java b/test/plugin/scenarios/mongodb-3.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/mongodb/controller/CaseController.java index dad835c02a..d26f7d092e 100644 --- a/test/plugin/scenarios/mongodb-3.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/mongodb/controller/CaseController.java +++ b/test/plugin/scenarios/mongodb-3.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/mongodb/controller/CaseController.java @@ -19,16 +19,23 @@ package org.apache.skywalking.apm.testcase.mongodb.controller; import com.mongodb.MongoClient; +import com.mongodb.client.AggregateIterable; import com.mongodb.client.FindIterable; import com.mongodb.client.MongoCollection; import com.mongodb.client.MongoDatabase; +import com.mongodb.client.model.Aggregates; +import com.mongodb.client.model.Filters; import org.bson.BsonDocument; import org.bson.Document; +import org.bson.conversions.Bson; import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +import java.util.Arrays; +import java.util.List; + import static com.mongodb.client.model.Filters.eq; @RestController @@ -65,6 +72,13 @@ public String mongoDBCase() { FindIterable findIterable = collection.find(eq("name", "org")); findIterable.first(); + // AggregateOperation + List pipeline = Arrays.asList( + Aggregates.match(Filters.eq("name", "test")) + ); + AggregateIterable aggregateIterable = collection.aggregate(pipeline); + aggregateIterable.first(); + // MixedBulkWriteOperation collection.updateOne(eq("name", "org"), BsonDocument.parse("{ $set : { \"name\": \"testA\"} }")); diff --git a/test/plugin/scenarios/mongodb-4.x-scenario/config/expectedData.yaml b/test/plugin/scenarios/mongodb-4.x-scenario/config/expectedData.yaml index 299d8b45ea..7673d38268 100644 --- a/test/plugin/scenarios/mongodb-4.x-scenario/config/expectedData.yaml +++ b/test/plugin/scenarios/mongodb-4.x-scenario/config/expectedData.yaml @@ -47,6 +47,7 @@ segmentItems: tags: - {key: db.type, value: MongoDB} - {key: db.instance, value: test-database} + - {key: db.collection, value: testCollection} - {key: db.bind_vars, value: not null} skipAnalysis: 'false' - operationName: MongoDB/FindOperation @@ -62,9 +63,10 @@ segmentItems: tags: - {key: db.type, value: MongoDB} - {key: db.instance, value: test-database} + - {key: db.collection, value: testCollection} - {key: db.bind_vars, value: '{"name": "org"}'} skipAnalysis: 'false' - - operationName: MongoDB/MixedBulkWriteOperation + - operationName: MongoDB/AggregateOperation parentSpanId: 0 spanId: 4 spanLayer: Database @@ -77,11 +79,28 @@ segmentItems: tags: - {key: db.type, value: MongoDB} - {key: db.instance, value: test-database} + - {key: db.collection, value: testCollection} + - {key: db.bind_vars, value: '{"$match": {"name": "test"}},'} + skipAnalysis: 'false' + - operationName: MongoDB/MixedBulkWriteOperation + parentSpanId: 0 + spanId: 5 + spanLayer: Database + startTime: nq 0 + endTime: nq 0 + componentId: 42 + isError: false + spanType: Exit + peer: mongodb-server:27017 + tags: + - {key: db.type, value: MongoDB} + - {key: db.instance, value: test-database} + - {key: db.collection, value: testCollection} - {key: db.bind_vars, value: '{"name": "org"},'} skipAnalysis: 'false' - operationName: MongoDB/FindOperation parentSpanId: 0 - spanId: 5 + spanId: 6 spanLayer: Database startTime: nq 0 endTime: nq 0 @@ -92,11 +111,12 @@ segmentItems: tags: - {key: db.type, value: MongoDB} - {key: db.instance, value: test-database} + - {key: db.collection, value: testCollection} - {key: db.bind_vars, value: '{"name": "testA"}'} skipAnalysis: 'false' - operationName: MongoDB/MixedBulkWriteOperation parentSpanId: 0 - spanId: 6 + spanId: 7 spanLayer: Database startTime: nq 0 endTime: nq 0 @@ -107,11 +127,12 @@ segmentItems: tags: - {key: db.type, value: MongoDB} - {key: db.instance, value: test-database} + - {key: db.collection, value: testCollection} - {key: db.bind_vars, value: '{"id": "1"},'} skipAnalysis: 'false' - operationName: MongoDB/DropDatabaseOperation parentSpanId: 0 - spanId: 7 + spanId: 8 spanLayer: Database startTime: nq 0 endTime: nq 0 diff --git a/test/plugin/scenarios/mongodb-4.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/mongodb/controller/CaseController.java b/test/plugin/scenarios/mongodb-4.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/mongodb/controller/CaseController.java index 33dfea5768..e50909855c 100644 --- a/test/plugin/scenarios/mongodb-4.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/mongodb/controller/CaseController.java +++ b/test/plugin/scenarios/mongodb-4.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/mongodb/controller/CaseController.java @@ -18,18 +18,25 @@ package org.apache.skywalking.apm.testcase.mongodb.controller; +import com.mongodb.client.AggregateIterable; import com.mongodb.client.MongoClient; import com.mongodb.client.MongoClients; import com.mongodb.client.MongoCollection; import com.mongodb.client.MongoDatabase; import com.mongodb.client.FindIterable; +import com.mongodb.client.model.Aggregates; +import com.mongodb.client.model.Filters; import org.bson.BsonDocument; import org.bson.Document; +import org.bson.conversions.Bson; import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +import java.util.Arrays; +import java.util.List; + import static com.mongodb.client.model.Filters.eq; @RestController @@ -63,6 +70,13 @@ public String mongoDBCase() { FindIterable findIterable = collection.find(eq("name", "org")); findIterable.first(); + // AggregateOperation + List pipeline = Arrays.asList( + Aggregates.match(Filters.eq("name", "test")) + ); + AggregateIterable aggregateIterable = collection.aggregate(pipeline); + aggregateIterable.first(); + // MixedBulkWriteOperation collection.updateOne(eq("name", "org"), BsonDocument.parse("{ $set : { \"name\": \"testA\"} }")); diff --git a/test/plugin/scenarios/nats-2.14.x-2.15.x-scenario/bin/startup.sh b/test/plugin/scenarios/nats-2.14.x-2.16.5-scenario/bin/startup.sh similarity index 94% rename from test/plugin/scenarios/nats-2.14.x-2.15.x-scenario/bin/startup.sh rename to test/plugin/scenarios/nats-2.14.x-2.16.5-scenario/bin/startup.sh index d2237e1e87..ded2569231 100644 --- a/test/plugin/scenarios/nats-2.14.x-2.15.x-scenario/bin/startup.sh +++ b/test/plugin/scenarios/nats-2.14.x-2.16.5-scenario/bin/startup.sh @@ -21,4 +21,4 @@ home="$(cd "$(dirname $0)"; pwd)" java -jar ${agent_opts} -Dserver.port=8080 \ -Dskywalking.agent.service_name=scenario-8080- \ -Dnats.server=nats-server \ - ${home}/../libs/nats-2.14.x-2.15.x-scenario.jar & \ No newline at end of file + ${home}/../libs/nats-2.14.x-2.16.5-scenario.jar & diff --git a/test/plugin/scenarios/nats-2.14.x-2.15.x-scenario/config/expectedData.yaml b/test/plugin/scenarios/nats-2.14.x-2.16.5-scenario/config/expectedData.yaml similarity index 100% rename from test/plugin/scenarios/nats-2.14.x-2.15.x-scenario/config/expectedData.yaml rename to test/plugin/scenarios/nats-2.14.x-2.16.5-scenario/config/expectedData.yaml diff --git a/test/plugin/scenarios/nats-2.14.x-2.15.x-scenario/configuration.yml b/test/plugin/scenarios/nats-2.14.x-2.16.5-scenario/configuration.yml similarity index 100% rename from test/plugin/scenarios/nats-2.14.x-2.15.x-scenario/configuration.yml rename to test/plugin/scenarios/nats-2.14.x-2.16.5-scenario/configuration.yml diff --git a/test/plugin/scenarios/nats-2.14.x-2.15.x-scenario/pom.xml b/test/plugin/scenarios/nats-2.14.x-2.16.5-scenario/pom.xml similarity index 96% rename from test/plugin/scenarios/nats-2.14.x-2.15.x-scenario/pom.xml rename to test/plugin/scenarios/nats-2.14.x-2.16.5-scenario/pom.xml index 5234f29e9a..3fdcfa2857 100644 --- a/test/plugin/scenarios/nats-2.14.x-2.15.x-scenario/pom.xml +++ b/test/plugin/scenarios/nats-2.14.x-2.16.5-scenario/pom.xml @@ -21,7 +21,7 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> org.apache.skywalking.apm.testcase - nats-2.14.x-2.15.x-scenario + nats-2.14.x-2.16.5-scenario 1.0.0 jar @@ -75,7 +75,7 @@ - nats-2.14.x-2.15.x-scenario + nats-2.14.x-2.16.5-scenario org.springframework.boot @@ -112,4 +112,4 @@ - \ No newline at end of file + diff --git a/test/plugin/scenarios/nats-2.14.x-2.16.5-scenario/src/main/assembly/assembly.xml b/test/plugin/scenarios/nats-2.14.x-2.16.5-scenario/src/main/assembly/assembly.xml new file mode 100644 index 0000000000..dd2cf1d4f5 --- /dev/null +++ b/test/plugin/scenarios/nats-2.14.x-2.16.5-scenario/src/main/assembly/assembly.xml @@ -0,0 +1,41 @@ + + + + + zip + + + + + ./bin + 0775 + + + + + + ${project.build.directory}/nats-2.14.x-2.16.5-scenario.jar + ./libs + 0775 + + + diff --git a/test/plugin/scenarios/nats-2.14.x-2.15.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/nats/client/Application.java b/test/plugin/scenarios/nats-2.14.x-2.16.5-scenario/src/main/java/org/apache/skywalking/apm/testcase/nats/client/Application.java similarity index 100% rename from test/plugin/scenarios/nats-2.14.x-2.15.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/nats/client/Application.java rename to test/plugin/scenarios/nats-2.14.x-2.16.5-scenario/src/main/java/org/apache/skywalking/apm/testcase/nats/client/Application.java diff --git a/test/plugin/scenarios/nats-2.14.x-2.15.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/nats/client/controller/StartController.java b/test/plugin/scenarios/nats-2.14.x-2.16.5-scenario/src/main/java/org/apache/skywalking/apm/testcase/nats/client/controller/StartController.java similarity index 100% rename from test/plugin/scenarios/nats-2.14.x-2.15.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/nats/client/controller/StartController.java rename to test/plugin/scenarios/nats-2.14.x-2.16.5-scenario/src/main/java/org/apache/skywalking/apm/testcase/nats/client/controller/StartController.java diff --git a/test/plugin/scenarios/nats-2.14.x-2.15.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/nats/client/publisher/JetStreamPublisher.java b/test/plugin/scenarios/nats-2.14.x-2.16.5-scenario/src/main/java/org/apache/skywalking/apm/testcase/nats/client/publisher/JetStreamPublisher.java similarity index 100% rename from test/plugin/scenarios/nats-2.14.x-2.15.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/nats/client/publisher/JetStreamPublisher.java rename to test/plugin/scenarios/nats-2.14.x-2.16.5-scenario/src/main/java/org/apache/skywalking/apm/testcase/nats/client/publisher/JetStreamPublisher.java diff --git a/test/plugin/scenarios/nats-2.14.x-2.15.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/nats/client/publisher/JetStreamPublisherFetcher.java b/test/plugin/scenarios/nats-2.14.x-2.16.5-scenario/src/main/java/org/apache/skywalking/apm/testcase/nats/client/publisher/JetStreamPublisherFetcher.java similarity index 100% rename from test/plugin/scenarios/nats-2.14.x-2.15.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/nats/client/publisher/JetStreamPublisherFetcher.java rename to test/plugin/scenarios/nats-2.14.x-2.16.5-scenario/src/main/java/org/apache/skywalking/apm/testcase/nats/client/publisher/JetStreamPublisherFetcher.java diff --git a/test/plugin/scenarios/nats-2.14.x-2.15.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/nats/client/publisher/NormalPublisher.java b/test/plugin/scenarios/nats-2.14.x-2.16.5-scenario/src/main/java/org/apache/skywalking/apm/testcase/nats/client/publisher/NormalPublisher.java similarity index 100% rename from test/plugin/scenarios/nats-2.14.x-2.15.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/nats/client/publisher/NormalPublisher.java rename to test/plugin/scenarios/nats-2.14.x-2.16.5-scenario/src/main/java/org/apache/skywalking/apm/testcase/nats/client/publisher/NormalPublisher.java diff --git a/test/plugin/scenarios/nats-2.14.x-2.15.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/nats/client/publisher/Publisher.java b/test/plugin/scenarios/nats-2.14.x-2.16.5-scenario/src/main/java/org/apache/skywalking/apm/testcase/nats/client/publisher/Publisher.java similarity index 100% rename from test/plugin/scenarios/nats-2.14.x-2.15.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/nats/client/publisher/Publisher.java rename to test/plugin/scenarios/nats-2.14.x-2.16.5-scenario/src/main/java/org/apache/skywalking/apm/testcase/nats/client/publisher/Publisher.java diff --git a/test/plugin/scenarios/nats-2.14.x-2.15.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/nats/client/publisher/ReqReplyPublisher.java b/test/plugin/scenarios/nats-2.14.x-2.16.5-scenario/src/main/java/org/apache/skywalking/apm/testcase/nats/client/publisher/ReqReplyPublisher.java similarity index 100% rename from test/plugin/scenarios/nats-2.14.x-2.15.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/nats/client/publisher/ReqReplyPublisher.java rename to test/plugin/scenarios/nats-2.14.x-2.16.5-scenario/src/main/java/org/apache/skywalking/apm/testcase/nats/client/publisher/ReqReplyPublisher.java diff --git a/test/plugin/scenarios/nats-2.14.x-2.15.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/nats/client/subscriber/Consumer.java b/test/plugin/scenarios/nats-2.14.x-2.16.5-scenario/src/main/java/org/apache/skywalking/apm/testcase/nats/client/subscriber/Consumer.java similarity index 100% rename from test/plugin/scenarios/nats-2.14.x-2.15.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/nats/client/subscriber/Consumer.java rename to test/plugin/scenarios/nats-2.14.x-2.16.5-scenario/src/main/java/org/apache/skywalking/apm/testcase/nats/client/subscriber/Consumer.java diff --git a/test/plugin/scenarios/nats-2.14.x-2.15.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/nats/client/subscriber/JetStreamFetcherConsumer.java b/test/plugin/scenarios/nats-2.14.x-2.16.5-scenario/src/main/java/org/apache/skywalking/apm/testcase/nats/client/subscriber/JetStreamFetcherConsumer.java similarity index 100% rename from test/plugin/scenarios/nats-2.14.x-2.15.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/nats/client/subscriber/JetStreamFetcherConsumer.java rename to test/plugin/scenarios/nats-2.14.x-2.16.5-scenario/src/main/java/org/apache/skywalking/apm/testcase/nats/client/subscriber/JetStreamFetcherConsumer.java diff --git a/test/plugin/scenarios/nats-2.14.x-2.15.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/nats/client/subscriber/JetStreamHandlerConsumer.java b/test/plugin/scenarios/nats-2.14.x-2.16.5-scenario/src/main/java/org/apache/skywalking/apm/testcase/nats/client/subscriber/JetStreamHandlerConsumer.java similarity index 100% rename from test/plugin/scenarios/nats-2.14.x-2.15.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/nats/client/subscriber/JetStreamHandlerConsumer.java rename to test/plugin/scenarios/nats-2.14.x-2.16.5-scenario/src/main/java/org/apache/skywalking/apm/testcase/nats/client/subscriber/JetStreamHandlerConsumer.java diff --git a/test/plugin/scenarios/nats-2.14.x-2.15.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/nats/client/subscriber/NextMsgConsumer.java b/test/plugin/scenarios/nats-2.14.x-2.16.5-scenario/src/main/java/org/apache/skywalking/apm/testcase/nats/client/subscriber/NextMsgConsumer.java similarity index 100% rename from test/plugin/scenarios/nats-2.14.x-2.15.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/nats/client/subscriber/NextMsgConsumer.java rename to test/plugin/scenarios/nats-2.14.x-2.16.5-scenario/src/main/java/org/apache/skywalking/apm/testcase/nats/client/subscriber/NextMsgConsumer.java diff --git a/test/plugin/scenarios/nats-2.14.x-2.15.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/nats/client/subscriber/ReqReplyConsumer.java b/test/plugin/scenarios/nats-2.14.x-2.16.5-scenario/src/main/java/org/apache/skywalking/apm/testcase/nats/client/subscriber/ReqReplyConsumer.java similarity index 100% rename from test/plugin/scenarios/nats-2.14.x-2.15.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/nats/client/subscriber/ReqReplyConsumer.java rename to test/plugin/scenarios/nats-2.14.x-2.16.5-scenario/src/main/java/org/apache/skywalking/apm/testcase/nats/client/subscriber/ReqReplyConsumer.java diff --git a/test/plugin/scenarios/nats-2.14.x-2.15.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/nats/client/work/StopSignal.java b/test/plugin/scenarios/nats-2.14.x-2.16.5-scenario/src/main/java/org/apache/skywalking/apm/testcase/nats/client/work/StopSignal.java similarity index 100% rename from test/plugin/scenarios/nats-2.14.x-2.15.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/nats/client/work/StopSignal.java rename to test/plugin/scenarios/nats-2.14.x-2.16.5-scenario/src/main/java/org/apache/skywalking/apm/testcase/nats/client/work/StopSignal.java diff --git a/test/plugin/scenarios/nats-2.14.x-2.15.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/nats/client/work/StreamUtil.java b/test/plugin/scenarios/nats-2.14.x-2.16.5-scenario/src/main/java/org/apache/skywalking/apm/testcase/nats/client/work/StreamUtil.java similarity index 100% rename from test/plugin/scenarios/nats-2.14.x-2.15.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/nats/client/work/StreamUtil.java rename to test/plugin/scenarios/nats-2.14.x-2.16.5-scenario/src/main/java/org/apache/skywalking/apm/testcase/nats/client/work/StreamUtil.java diff --git a/test/plugin/scenarios/nats-2.14.x-2.15.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/nats/client/work/TrackedConnection.java b/test/plugin/scenarios/nats-2.14.x-2.16.5-scenario/src/main/java/org/apache/skywalking/apm/testcase/nats/client/work/TrackedConnection.java similarity index 100% rename from test/plugin/scenarios/nats-2.14.x-2.15.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/nats/client/work/TrackedConnection.java rename to test/plugin/scenarios/nats-2.14.x-2.16.5-scenario/src/main/java/org/apache/skywalking/apm/testcase/nats/client/work/TrackedConnection.java diff --git a/test/plugin/scenarios/nats-2.14.x-2.15.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/nats/client/work/WorkBuilder.java b/test/plugin/scenarios/nats-2.14.x-2.16.5-scenario/src/main/java/org/apache/skywalking/apm/testcase/nats/client/work/WorkBuilder.java similarity index 100% rename from test/plugin/scenarios/nats-2.14.x-2.15.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/nats/client/work/WorkBuilder.java rename to test/plugin/scenarios/nats-2.14.x-2.16.5-scenario/src/main/java/org/apache/skywalking/apm/testcase/nats/client/work/WorkBuilder.java diff --git a/test/plugin/scenarios/nats-2.14.x-2.16.5-scenario/support-version.list b/test/plugin/scenarios/nats-2.14.x-2.16.5-scenario/support-version.list new file mode 100644 index 0000000000..25a0bd9d45 --- /dev/null +++ b/test/plugin/scenarios/nats-2.14.x-2.16.5-scenario/support-version.list @@ -0,0 +1,19 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +2.16.5 +2.15.6 +2.14.2 diff --git a/test/plugin/scenarios/oracle-scenario/config/expectedData.yaml b/test/plugin/scenarios/oracle-scenario/config/expectedData.yaml deleted file mode 100644 index 91df4be88d..0000000000 --- a/test/plugin/scenarios/oracle-scenario/config/expectedData.yaml +++ /dev/null @@ -1,113 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -segmentItems: -- serviceName: oracle-scenario - segmentSize: ge 1 - segments: - - segmentId: not null - spans: - - operationName: Oracle/JDBC/Statement/execute - parentSpanId: 0 - spanId: 1 - spanLayer: Database - startTime: nq 0 - endTime: nq 0 - componentId: 34 - isError: false - spanType: Exit - peer: oracle-server:1521 - tags: - - {key: db.type, value: Oracle} - - {key: db.instance, value: xe} - - key: db.statement - value: "CREATE TABLE test_007(\nid VARCHAR(1) PRIMARY KEY, \nvalue VARCHAR(1)\ - \ NOT NULL)" - skipAnalysis: 'false' - - operationName: Oracle/JDBC/PreparedStatement/execute - parentSpanId: 0 - spanId: 2 - spanLayer: Database - startTime: nq 0 - endTime: nq 0 - componentId: 34 - isError: false - spanType: Exit - peer: oracle-server:1521 - tags: - - {key: db.type, value: Oracle} - - {key: db.instance, value: xe} - - {key: db.statement, value: 'INSERT INTO test_007(id, value) VALUES(?,?)'} - skipAnalysis: 'false' - - operationName: Oracle/JDBC/PreparedStatement/executeQuery - parentSpanId: 0 - spanId: 3 - spanLayer: Database - startTime: nq 0 - endTime: nq 0 - componentId: 34 - isError: false - spanType: Exit - peer: oracle-server:1521 - tags: - - {key: db.type, value: Oracle} - - {key: db.instance, value: xe} - - {key: db.statement, value: 'SELECT id, value FROM test_007 WHERE id=?'} - skipAnalysis: 'false' - - operationName: Oracle/JDBC/Statement/execute - parentSpanId: 0 - spanId: 4 - spanLayer: Database - startTime: nq 0 - endTime: nq 0 - componentId: 34 - isError: false - spanType: Exit - peer: oracle-server:1521 - tags: - - {key: db.type, value: Oracle} - - {key: db.instance, value: xe} - - {key: db.statement, value: DROP table test_007} - skipAnalysis: 'false' - - operationName: Oracle/JDBC/Connection/close - parentSpanId: 0 - spanId: 5 - spanLayer: Database - tags: - - {key: db.type, value: Oracle} - - {key: db.instance, value: xe} - - {key: db.statement, value: ''} - startTime: nq 0 - endTime: nq 0 - componentId: 34 - isError: false - spanType: Exit - peer: oracle-server:1521 - skipAnalysis: 'false' - - operationName: GET:/oracle-scenario/case/oracle - parentSpanId: -1 - spanId: 0 - spanLayer: Http - startTime: nq 0 - endTime: nq 0 - componentId: 1 - isError: false - spanType: Entry - peer: '' - tags: - - {key: url, value: 'http://localhost:8080/oracle-scenario/case/oracle'} - - {key: http.method, value: GET} - - {key: http.status_code, value: '200'} - skipAnalysis: 'false' diff --git a/test/plugin/scenarios/oracle-scenario/src/main/java/org/apache/skywalking/apm/testcase/oracle/controller/CaseController.java b/test/plugin/scenarios/oracle-scenario/src/main/java/org/apache/skywalking/apm/testcase/oracle/controller/CaseController.java deleted file mode 100644 index c3bb2d2aa9..0000000000 --- a/test/plugin/scenarios/oracle-scenario/src/main/java/org/apache/skywalking/apm/testcase/oracle/controller/CaseController.java +++ /dev/null @@ -1,139 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package org.apache.skywalking.apm.testcase.oracle.controller; - -import java.sql.Connection; -import java.sql.DriverManager; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Statement; -import javax.annotation.PostConstruct; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.ResponseBody; -import org.springframework.web.bind.annotation.RestController; - -@RestController -@RequestMapping("/case") -public class CaseController { - - private static final Logger LOGGER = LogManager.getLogger(CaseController.class); - - @Value("${oracle.address}") - private String oracleHostAndPort; - - @Value("${oracle.username}") - private String oracleUsername; - - @Value("${oracle.password}") - private String oraclePassword; - - private String connectURL; - private static final String TEST_EXIST_SQL = "SELECT * FROM dual"; - private static final String CREATE_TABLE_SQL = "CREATE TABLE test_007(\n" + "id VARCHAR(1) PRIMARY KEY, \n" + "value VARCHAR(1) NOT NULL)"; - private static final String INSERT_DATA_SQL = "INSERT INTO test_007(id, value) VALUES(?,?)"; - private static final String QUERY_DATA_SQL = "SELECT id, value FROM test_007 WHERE id=?"; - private static final String DROP_TABLE_SQL = "DROP table test_007"; - - private static final String SUCCESS = "Success"; - - @PostConstruct - public void setUp() throws ClassNotFoundException { - Class.forName("oracle.jdbc.driver.OracleDriver"); - connectURL = "jdbc:oracle:thin:@" + oracleHostAndPort + ":xe"; - } - - @RequestMapping("/oracle") - @ResponseBody - public String testcase() { - Connection connection = null; - try { - // create table by using statement - connection = DriverManager.getConnection(connectURL, oracleUsername, oraclePassword); - Statement statement = connection.createStatement(); - statement.execute(CREATE_TABLE_SQL); - statement.close(); - - // insert table by using PreparedStatement - PreparedStatement insertDataPreparedStatement = connection.prepareStatement(INSERT_DATA_SQL); - insertDataPreparedStatement.setString(1, "1"); - insertDataPreparedStatement.setString(2, "1"); - insertDataPreparedStatement.execute(); - insertDataPreparedStatement.close(); - - // query data by using PreparedStatement - PreparedStatement queryDataPreparedStatement = connection.prepareStatement(QUERY_DATA_SQL); - queryDataPreparedStatement.setString(1, "1"); - ResultSet resultSet = queryDataPreparedStatement.executeQuery(); - resultSet.next(); - LOGGER.info("Query id[{}]: value={}", "1", resultSet.getString(2)); - queryDataPreparedStatement.close(); - - // drop table by using statement - Statement dropTableStatement = connection.createStatement(); - dropTableStatement.execute(DROP_TABLE_SQL); - dropTableStatement.close(); - } catch (SQLException e) { - String message = "Failed to execute sql"; - LOGGER.error(message); - throw new RuntimeException(message); - } finally { - if (connection != null) { - try { - connection.close(); - } catch (SQLException e) { - String message = "Failed to close connection"; - LOGGER.error(message); - throw new RuntimeException(message); - } - } - } - return SUCCESS; - } - - @RequestMapping("/healthCheck") - @ResponseBody - public String healthCheck() { - Connection connection = null; - try { - connection = DriverManager.getConnection(connectURL, oracleUsername, oraclePassword); - PreparedStatement preparedStatement = connection.prepareStatement(TEST_EXIST_SQL); - preparedStatement.execute(); - preparedStatement.close(); - } catch (SQLException e) { - String message = "Failed to execute sql"; - LOGGER.error(message); - throw new RuntimeException(message); - } finally { - if (connection != null) { - try { - connection.close(); - } catch (SQLException e) { - String message = "Failed to close connection"; - LOGGER.error(message); - throw new RuntimeException(message); - } - } - } - return SUCCESS; - } -} diff --git a/test/plugin/scenarios/redisson-scenario/support-version.list b/test/plugin/scenarios/redisson-scenario/support-version.list index 78009656c1..aa0a5594df 100644 --- a/test/plugin/scenarios/redisson-scenario/support-version.list +++ b/test/plugin/scenarios/redisson-scenario/support-version.list @@ -14,6 +14,13 @@ # See the License for the specific language governing permissions and # limitations under the License. +# 3.5.0-3.12.4, 3.26.1-3.30.0 have been tested, and 3.12.5-3.26.0 are also supported but not included in the test +3.30.0 +3.29.0 +3.28.0 +3.27.2 +3.26.1 +3.12.4 3.11.5 3.10.7 3.9.1 diff --git a/test/plugin/scenarios/rocketmq-5-grpc-scenario/pom.xml b/test/plugin/scenarios/rocketmq-5-grpc-scenario/pom.xml index d27b6488b1..018ce5430e 100644 --- a/test/plugin/scenarios/rocketmq-5-grpc-scenario/pom.xml +++ b/test/plugin/scenarios/rocketmq-5-grpc-scenario/pom.xml @@ -34,7 +34,7 @@ 1.8 3.8.1 5.0.5 - 5.1.1 + 5.1.4 2.1.6.RELEASE 1.18.20 diff --git a/test/plugin/scenarios/rocketmq-5-grpc-scenario/src/main/java/test/apache/skywalking/apm/testcase/rocketmq/client/java/controller/CaseController.java b/test/plugin/scenarios/rocketmq-5-grpc-scenario/src/main/java/test/apache/skywalking/apm/testcase/rocketmq/client/java/controller/CaseController.java index 5219bc8db9..313389913e 100644 --- a/test/plugin/scenarios/rocketmq-5-grpc-scenario/src/main/java/test/apache/skywalking/apm/testcase/rocketmq/client/java/controller/CaseController.java +++ b/test/plugin/scenarios/rocketmq-5-grpc-scenario/src/main/java/test/apache/skywalking/apm/testcase/rocketmq/client/java/controller/CaseController.java @@ -48,35 +48,27 @@ public class CaseController { @Autowired private MessageService messageService; + private volatile boolean consumerStarted = false; + @RequestMapping("/rocketmq-5-grpc-scenario") @ResponseBody public String testcase() { try { messageService.sendNormalMessage(NORMAL_TOPIC, TAG_NOMARL, GROUP); - Thread t1 = new Thread(() -> messageService.pushConsumes( - Collections.singletonList(NORMAL_TOPIC), - Collections.singletonList(TAG_NOMARL), - GROUP - )); - t1.start(); - t1.join(); messageService.sendNormalMessageAsync(ASYNC_PRODUCER_TOPIC, TAG_ASYNC_PRODUCER, GROUP); messageService.sendNormalMessageAsync(ASYNC_PRODUCER_TOPIC, TAG_ASYNC_PRODUCER, GROUP); - Thread t2 = new Thread(() -> messageService.simpleConsumes(Collections.singletonList(ASYNC_PRODUCER_TOPIC), - Collections.singletonList(TAG_ASYNC_PRODUCER), GROUP, - 10, 10 - )); - t2.start(); - t2.join(); + new Thread(() -> messageService.simpleConsumes( + Collections.singletonList(ASYNC_PRODUCER_TOPIC), + Collections.singletonList(TAG_ASYNC_PRODUCER), GROUP, + 10, 10 + )).start(); messageService.sendNormalMessage(ASYNC_CONSUMER_TOPIC, TAG_ASYNC_CONSUMER, GROUP); messageService.sendNormalMessage(ASYNC_CONSUMER_TOPIC, TAG_ASYNC_CONSUMER, GROUP); - Thread t3 = new Thread(() -> messageService.simpleConsumeAsync(ASYNC_CONSUMER_TOPIC, TAG_ASYNC_CONSUMER, GROUP, 10, - 10 - )); - t3.start(); - t3.join(); + new Thread(() -> messageService.simpleConsumeAsync( + ASYNC_CONSUMER_TOPIC, TAG_ASYNC_CONSUMER, GROUP, 10, 10 + )).start(); } catch (Exception e) { log.error("testcase error", e); } @@ -86,11 +78,45 @@ public String testcase() { @RequestMapping("/healthCheck") @ResponseBody public String healthCheck() throws Exception { - System.setProperty(MixAll.ROCKETMQ_HOME_ENV, this.getClass().getResource("/").getPath()); - messageService.updateNormalTopic(NORMAL_TOPIC); - messageService.updateNormalTopic(ASYNC_PRODUCER_TOPIC); - messageService.updateNormalTopic(ASYNC_CONSUMER_TOPIC); - final Producer producer = ProducerSingleton.getInstance(endpoints, NORMAL_TOPIC); + if (!consumerStarted) { + // Set flag early to prevent re-entry from concurrent healthCheck + // requests (each curl has a 3s timeout, and initialization may + // take longer than that). + consumerStarted = true; + try { + System.setProperty(MixAll.ROCKETMQ_HOME_ENV, this.getClass().getResource("/").getPath()); + messageService.updateNormalTopic(NORMAL_TOPIC); + messageService.updateNormalTopic(ASYNC_PRODUCER_TOPIC); + messageService.updateNormalTopic(ASYNC_CONSUMER_TOPIC); + // Start push consumer early so it has time to receive messages + messageService.pushConsumes( + Collections.singletonList(NORMAL_TOPIC), + Collections.singletonList(TAG_NOMARL), + GROUP + ); + final Producer producer = ProducerSingleton.getInstance(endpoints, NORMAL_TOPIC); + // Send a probe message so the consumer has something to receive + messageService.sendNormalMessage(NORMAL_TOPIC, TAG_NOMARL, GROUP); + } catch (Exception e) { + consumerStarted = false; + throw e; + } + } + + // Wait until the consumer has actually received a probe message, + // confirming it can consume from the topic. + // Send a fresh probe on every retry so the consumer picks it up once + // rebalance finishes (messages sent before rebalance may never arrive). + if (!MessageService.CONSUMER_READY) { + try { + messageService.sendNormalMessage(NORMAL_TOPIC, TAG_NOMARL, GROUP); + System.out.printf("HealthCheck: sent probe message (consumer not ready yet).%n"); + } catch (Exception e) { + System.out.printf("HealthCheck: failed to send probe: %s%n", e.getMessage()); + } + throw new RuntimeException("Consumer has not received probe message yet"); + } + return SUCCESS; } } diff --git a/test/plugin/scenarios/rocketmq-5-grpc-scenario/src/main/java/test/apache/skywalking/apm/testcase/rocketmq/client/java/controller/MessageService.java b/test/plugin/scenarios/rocketmq-5-grpc-scenario/src/main/java/test/apache/skywalking/apm/testcase/rocketmq/client/java/controller/MessageService.java index 0c87f66981..33b9bad539 100644 --- a/test/plugin/scenarios/rocketmq-5-grpc-scenario/src/main/java/test/apache/skywalking/apm/testcase/rocketmq/client/java/controller/MessageService.java +++ b/test/plugin/scenarios/rocketmq-5-grpc-scenario/src/main/java/test/apache/skywalking/apm/testcase/rocketmq/client/java/controller/MessageService.java @@ -87,8 +87,8 @@ public void sendNormalMessageAsync(String topic, String tag, String group) throw .build(); try { CompletableFuture future = producer.sendAsync(message); - future.join(); - log.info("Send async message successfully"); + SendReceipt sendReceipt = future.join(); + log.info("Send async message successfully, messageId={}", sendReceipt.getMessageId()); } catch (Throwable t) { log.error("Failed to send message", t); } @@ -142,17 +142,34 @@ public void simpleConsumes(List topics, .build(); Duration invisibleDuration = Duration.ofSeconds(duration); - final List messages = consumer.receive(maxMessageNum, invisibleDuration); - messages.forEach(messageView -> { - log.info("Received message: {}", messageView); - }); - for (MessageView msg : messages) { - final MessageId messageId = msg.getMessageId(); - try { - consumer.ack(msg); - log.info("Message is acknowledged successfully, messageId={}", messageId); - } catch (Throwable t) { - log.error("Message is failed to be acknowledged, messageId={}", messageId, t); + int counter = 0; + int checkCounter = 0; + while (true) { + final List messages = consumer.receive(maxMessageNum, invisibleDuration); + messages.forEach(messageView -> { + log.info("Received message: {}", messageView); + }); + boolean finishFlag = false; + for (MessageView msg : messages) { + final MessageId messageId = msg.getMessageId(); + try { + consumer.ack(msg); + log.info("Message is acknowledged successfully, messageId={}", messageId); + counter++; + if (counter >= 2) { + finishFlag = true; + } + } catch (Throwable t) { + log.error("Message is failed to be acknowledged, messageId={}", messageId, t); + } + } + checkCounter++; + if (finishFlag) { + break; + } + if (checkCounter >= 3) { + log.error("Message is failed to receive after 3 attempts"); + break; } } } catch (Exception e) { @@ -186,26 +203,23 @@ public void simpleConsumeAsync(String topic, maxMessageNum, invisibleDuration ); - future0.whenCompleteAsync((messages, throwable) -> { - if (null != throwable) { - log.error("Failed to receive message from remote", throwable); - return; - } + future0.thenComposeAsync(messages -> { log.info("Received {} message(s)", messages.size()); final Map> map = messages.stream().collect(Collectors.toMap(message -> message, consumer::ackAsync)); - for (Map.Entry> entry : map.entrySet()) { + List> ackFutures = map.entrySet().stream().map(entry -> { final MessageId messageId = entry.getKey().getMessageId(); final CompletableFuture future = entry.getValue(); - future.whenCompleteAsync((v, t) -> { + return future.whenCompleteAsync((v, t) -> { if (null != t) { log.error("Message is failed to be acknowledged, messageId={}", messageId, t); return; } log.info("Message is acknowledged successfully, messageId={}", messageId); }, ackCallbackExecutor); - } - }, receiveCallbackExecutor); + }).collect(Collectors.toList()); + return CompletableFuture.allOf(ackFutures.toArray(new CompletableFuture[0])); + }, receiveCallbackExecutor).join(); } catch (Exception e) { log.error("consumer start error", e); } @@ -226,6 +240,8 @@ public void updateNormalTopic(String topic) { MQAdminStartup.main(subArgs); } + public static volatile boolean CONSUMER_READY = false; + public static class MyConsumer implements MessageListener { @Override @@ -233,6 +249,7 @@ public ConsumeResult consume(MessageView messageView) { log.info("Consume message successfully, messageId={},messageBody={}", messageView.getMessageId(), messageView.getBody().toString() ); + CONSUMER_READY = true; return ConsumeResult.SUCCESS; } } diff --git a/test/plugin/scenarios/rocketmq-5-grpc-scenario/src/main/resources/log4j2.xml b/test/plugin/scenarios/rocketmq-5-grpc-scenario/src/main/resources/log4j2.xml index 9849ed5a8a..b16354bfa3 100644 --- a/test/plugin/scenarios/rocketmq-5-grpc-scenario/src/main/resources/log4j2.xml +++ b/test/plugin/scenarios/rocketmq-5-grpc-scenario/src/main/resources/log4j2.xml @@ -23,7 +23,7 @@ - + diff --git a/test/plugin/scenarios/rocketmq-5-grpc-scenario/support-version.list b/test/plugin/scenarios/rocketmq-5-grpc-scenario/support-version.list index 4f8dd365dc..c7b3c10e09 100644 --- a/test/plugin/scenarios/rocketmq-5-grpc-scenario/support-version.list +++ b/test/plugin/scenarios/rocketmq-5-grpc-scenario/support-version.list @@ -16,5 +16,4 @@ # lists your version here (Contains only the last version number of each minor version.) -5.1.1 -5.1.4 +5.4.0 diff --git a/test/plugin/scenarios/rocketmq-scenario/src/main/java/test/apache/skywalking/apm/testcase/rocketmq/controller/CaseController.java b/test/plugin/scenarios/rocketmq-scenario/src/main/java/test/apache/skywalking/apm/testcase/rocketmq/controller/CaseController.java index ab96fd6a7c..85d32069a5 100644 --- a/test/plugin/scenarios/rocketmq-scenario/src/main/java/test/apache/skywalking/apm/testcase/rocketmq/controller/CaseController.java +++ b/test/plugin/scenarios/rocketmq-scenario/src/main/java/test/apache/skywalking/apm/testcase/rocketmq/controller/CaseController.java @@ -25,6 +25,7 @@ import org.apache.rocketmq.client.consumer.listener.MessageListenerConcurrently; import org.apache.rocketmq.client.producer.DefaultMQProducer; import org.apache.rocketmq.client.producer.SendResult; +import org.apache.rocketmq.common.consumer.ConsumeFromWhere; import org.apache.rocketmq.common.message.Message; import org.apache.rocketmq.common.message.MessageExt; import org.apache.rocketmq.remoting.common.RemotingHelper; @@ -47,17 +48,19 @@ public class CaseController { @Value("${name.server}") private String namerServer; + private volatile boolean consumerStarted = false; + private volatile boolean consumerReady = false; + private DefaultMQProducer probeProducer; + @RequestMapping("/rocketmq-scenario") @ResponseBody public String testcase() { try { - // start producer DefaultMQProducer producer = new DefaultMQProducer("please_rename_unique_group_name"); producer.setNamesrvAddr(namerServer); producer.start(); System.out.printf("Provider Started.%n"); - // send msg Message msg = new Message("TopicTest", ("Hello RocketMQ sendMsg " + new Date()).getBytes(RemotingHelper.DEFAULT_CHARSET) ); @@ -65,30 +68,6 @@ public String testcase() { msg.setKeys("KeyA"); SendResult sendResult = producer.send(msg); System.out.printf("%s send msg: %s%n", new Date(), sendResult); - - // start consumer - Thread thread = new Thread(new Runnable() { - @Override - public void run() { - try { - DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("please_rename_unique_group_name"); - consumer.setNamesrvAddr(namerServer); - consumer.subscribe("TopicTest", "*"); - consumer.registerMessageListener(new MessageListenerConcurrently() { - @Override - public ConsumeConcurrentlyStatus consumeMessage(List msgs, ConsumeConcurrentlyContext context) { - System.out.printf("%s Receive New Messages: %s %n", Thread.currentThread().getName(), new String(msgs.get(0).getBody(), StandardCharsets.UTF_8)); - return ConsumeConcurrentlyStatus.CONSUME_SUCCESS; - } - }); - consumer.start(); - System.out.printf("Consumer Started.%n"); - } catch (Exception e) { - log.error("consumer start error", e); - } - } - }); - thread.start(); } catch (Exception e) { log.error("testcase error", e); } @@ -98,11 +77,60 @@ public ConsumeConcurrentlyStatus consumeMessage(List msgs, ConsumeCo @RequestMapping("/healthCheck") @ResponseBody public String healthCheck() throws Exception { - // start producer - DefaultMQProducer producer = new DefaultMQProducer("healthCheck_please_rename_unique_group_name"); - producer.setNamesrvAddr(namerServer); - producer.start(); - System.out.printf("HealthCheck Provider Started.%n"); + if (!consumerStarted) { + // Set flag early to prevent re-entry from concurrent healthCheck + // requests (each curl has a 3s timeout, and initialization may + // take longer than that). + consumerStarted = true; + try { + // Speed up client-side rebalance from default 20s to 2s so the + // consumer discovers topic queues faster after startup. + System.setProperty("rocketmq.client.rebalance.waitInterval", "2000"); + + // Start a producer that stays alive to send probe messages on each retry. + probeProducer = new DefaultMQProducer("healthCheck_please_rename_unique_group_name"); + probeProducer.setNamesrvAddr(namerServer); + probeProducer.start(); + Message probeMsg = new Message("TopicTest", "probe".getBytes(StandardCharsets.UTF_8)); + probeProducer.send(probeMsg); + System.out.printf("HealthCheck: Topic created via probe message.%n"); + + // Start consumer after topic exists so rebalance finds queues immediately. + DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("please_rename_unique_group_name"); + consumer.setNamesrvAddr(namerServer); + consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET); + consumer.subscribe("TopicTest", "*"); + consumer.registerMessageListener(new MessageListenerConcurrently() { + @Override + public ConsumeConcurrentlyStatus consumeMessage(List msgs, ConsumeConcurrentlyContext context) { + System.out.printf("%s Receive New Messages: %s %n", Thread.currentThread().getName(), new String(msgs.get(0).getBody(), StandardCharsets.UTF_8)); + consumerReady = true; + return ConsumeConcurrentlyStatus.CONSUME_SUCCESS; + } + }); + consumer.start(); + System.out.printf("Consumer Started.%n"); + } catch (Exception e) { + consumerStarted = false; + throw e; + } + } + + // Wait until the consumer has actually received a probe message, + // confirming rebalance is complete and it can consume from the topic. + // Send a fresh probe on every retry so the consumer picks it up once + // rebalance finishes (messages sent before rebalance may never arrive). + if (!consumerReady) { + try { + Message probeMsg = new Message("TopicTest", "probe".getBytes(StandardCharsets.UTF_8)); + probeProducer.send(probeMsg); + System.out.printf("HealthCheck: sent probe message (consumer not ready yet).%n"); + } catch (Exception e) { + System.out.printf("HealthCheck: failed to send probe: %s%n", e.getMessage()); + } + throw new RuntimeException("Consumer has not received probe message yet"); + } + return SUCCESS; } diff --git a/test/plugin/scenarios/rocketmq-scenario/src/main/resources/log4j2.xml b/test/plugin/scenarios/rocketmq-scenario/src/main/resources/log4j2.xml index 9849ed5a8a..b16354bfa3 100644 --- a/test/plugin/scenarios/rocketmq-scenario/src/main/resources/log4j2.xml +++ b/test/plugin/scenarios/rocketmq-scenario/src/main/resources/log4j2.xml @@ -23,7 +23,7 @@ - + diff --git a/test/plugin/scenarios/rocketmq-scenario/support-version.list b/test/plugin/scenarios/rocketmq-scenario/support-version.list index b391ea9d2a..8dc0b9e562 100644 --- a/test/plugin/scenarios/rocketmq-scenario/support-version.list +++ b/test/plugin/scenarios/rocketmq-scenario/support-version.list @@ -16,11 +16,11 @@ # lists your version here (Contains only the last version number of each minor version.) -5.1.0 +5.4.0 4.9.4 4.8.0 4.7.1 4.6.0 4.5.2 4.4.0 -4.3.1 +4.3.1 \ No newline at end of file diff --git a/test/plugin/scenarios/sofarpc-scenario/config/expectedData.yaml b/test/plugin/scenarios/sofarpc-scenario/config/expectedData.yaml index f8d0f4f807..c6655d281b 100644 --- a/test/plugin/scenarios/sofarpc-scenario/config/expectedData.yaml +++ b/test/plugin/scenarios/sofarpc-scenario/config/expectedData.yaml @@ -34,6 +34,24 @@ segmentItems: refType: CrossProcess, parentSpanId: 1, parentTraceSegmentId: not null, parentServiceInstance: not null, parentService: sofarpc-scenario, traceId: not null} skipAnalysis: 'false' + - segmentId: not null + spans: + - operationName: Thread/com.alipay.sofa.rpc.message.bolt.BoltInvokerCallback/onResponse + parentSpanId: -1 + spanId: 0 + spanLayer: Unknown + startTime: nq 0 + endTime: nq 0 + componentId: 0 + isError: false + spanType: Local + peer: '' + skipAnalysis: false + refs: + - {parentEndpoint: 'GET:/sofarpc-scenario/case/sofarpc', networkAddress: '', + refType: CrossThread, parentSpanId: 2, parentTraceSegmentId: not null, + parentServiceInstance: not null, parentService: sofarpc-scenario, + traceId: not null} - segmentId: not null spans: - operationName: org.apache.skywalking.apm.testcase.sofarpc.interfaces.SofaRpcDemoService.hello(java.lang.String) @@ -49,6 +67,19 @@ segmentItems: tags: - {key: url, value: 'bolt://127.0.0.1:12200/org.apache.skywalking.apm.testcase.sofarpc.interfaces.SofaRpcDemoService.hello(java.lang.String)'} skipAnalysis: 'false' + - operationName: org.apache.skywalking.apm.testcase.sofarpc.interfaces.SofaRpcDemoService.callback(java.lang.String) + parentSpanId: 0 + spanId: 2 + spanLayer: RPCFramework + startTime: nq 0 + endTime: nq 0 + componentId: 43 + isError: false + spanType: Exit + peer: 127.0.0.1:12200 + tags: + - { key: url, value: 'bolt://127.0.0.1:12200/org.apache.skywalking.apm.testcase.sofarpc.interfaces.SofaRpcDemoService.callback(java.lang.String)' } + skipAnalysis: 'false' - operationName: GET:/sofarpc-scenario/case/sofarpc parentSpanId: -1 spanId: 0 @@ -64,3 +95,20 @@ segmentItems: - {key: http.method, value: GET} - {key: http.status_code, value: '200'} skipAnalysis: 'false' + - segmentId: not null + spans: + - operationName: org.apache.skywalking.apm.testcase.sofarpc.interfaces.SofaRpcDemoService.callback(java.lang.String) + parentSpanId: -1 + spanId: 0 + spanLayer: RPCFramework + startTime: nq 0 + endTime: nq 0 + componentId: 43 + isError: false + spanType: Entry + peer: '' + refs: + - { parentEndpoint: GET:/sofarpc-scenario/case/sofarpc, networkAddress: '127.0.0.1:12200', + refType: CrossProcess, parentSpanId: 2, parentTraceSegmentId: not null, parentServiceInstance: not + null, parentService: sofarpc-scenario, traceId: not null } + skipAnalysis: 'false' diff --git a/test/plugin/scenarios/sofarpc-scenario/src/main/java/org/apache/skywalking/apm/testcase/sofarpc/SofaRpcApplication.java b/test/plugin/scenarios/sofarpc-scenario/src/main/java/org/apache/skywalking/apm/testcase/sofarpc/SofaRpcApplication.java index faa8bb8639..609a0e5266 100644 --- a/test/plugin/scenarios/sofarpc-scenario/src/main/java/org/apache/skywalking/apm/testcase/sofarpc/SofaRpcApplication.java +++ b/test/plugin/scenarios/sofarpc-scenario/src/main/java/org/apache/skywalking/apm/testcase/sofarpc/SofaRpcApplication.java @@ -18,6 +18,7 @@ package org.apache.skywalking.apm.testcase.sofarpc; +import com.alipay.sofa.rpc.common.RpcConstants; import com.alipay.sofa.rpc.config.ConsumerConfig; import com.alipay.sofa.rpc.config.ProviderConfig; import com.alipay.sofa.rpc.config.ServerConfig; @@ -42,8 +43,9 @@ public static class SofaRpcConfiguration { public ProviderConfig provider() { ServerConfig config = new ServerConfig().setProtocol("bolt").setPort(12200).setDaemon(true); - ProviderConfig providerConfig = new ProviderConfig().setInterfaceId(SofaRpcDemoService.class - .getName()).setRef(new SofaRpcDemoServiceImpl()).setServer(config); + ProviderConfig providerConfig = new ProviderConfig().setInterfaceId( + SofaRpcDemoService.class + .getName()).setRef(new SofaRpcDemoServiceImpl()).setServer(config); providerConfig.export(); return providerConfig; @@ -55,5 +57,13 @@ public ConsumerConfig consumer() { .setProtocol("bolt") .setDirectUrl("bolt://127.0.0.1:12200"); } + + @Bean + public ConsumerConfig callbackConsumer() { + return new ConsumerConfig().setInterfaceId(SofaRpcDemoService.class.getName()) + .setProtocol("bolt") + .setInvokeType(RpcConstants.INVOKER_TYPE_CALLBACK) + .setDirectUrl("bolt://127.0.0.1:12200"); + } } } diff --git a/test/plugin/scenarios/sofarpc-scenario/src/main/java/org/apache/skywalking/apm/testcase/sofarpc/callback/TestCallback.java b/test/plugin/scenarios/sofarpc-scenario/src/main/java/org/apache/skywalking/apm/testcase/sofarpc/callback/TestCallback.java new file mode 100644 index 0000000000..81da5ba588 --- /dev/null +++ b/test/plugin/scenarios/sofarpc-scenario/src/main/java/org/apache/skywalking/apm/testcase/sofarpc/callback/TestCallback.java @@ -0,0 +1,47 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.testcase.sofarpc.callback; + +import com.alipay.sofa.rpc.core.exception.SofaRpcException; +import com.alipay.sofa.rpc.core.invoke.SofaResponseCallback; +import com.alipay.sofa.rpc.core.request.RequestBase; +import org.apache.skywalking.apm.testcase.sofarpc.interfaces.SofaRpcDemoService; + +public class TestCallback implements SofaResponseCallback { + + private SofaRpcDemoService service; + + public TestCallback(final SofaRpcDemoService service) { + this.service = service; + } + + @Override + public void onAppResponse(final Object o, final String s, final RequestBase requestBase) { + } + + @Override + public void onAppException(final Throwable throwable, final String s, final RequestBase requestBase) { + + } + + @Override + public void onSofaException(final SofaRpcException e, final String s, final RequestBase requestBase) { + + } +} diff --git a/test/plugin/scenarios/sofarpc-scenario/src/main/java/org/apache/skywalking/apm/testcase/sofarpc/controller/CaseController.java b/test/plugin/scenarios/sofarpc-scenario/src/main/java/org/apache/skywalking/apm/testcase/sofarpc/controller/CaseController.java index ca9320001e..fff7b5ac34 100644 --- a/test/plugin/scenarios/sofarpc-scenario/src/main/java/org/apache/skywalking/apm/testcase/sofarpc/controller/CaseController.java +++ b/test/plugin/scenarios/sofarpc-scenario/src/main/java/org/apache/skywalking/apm/testcase/sofarpc/controller/CaseController.java @@ -19,8 +19,11 @@ package org.apache.skywalking.apm.testcase.sofarpc.controller; import com.alipay.sofa.rpc.config.ConsumerConfig; +import com.alipay.sofa.rpc.context.RpcInvokeContext; +import org.apache.skywalking.apm.testcase.sofarpc.callback.TestCallback; import org.apache.skywalking.apm.testcase.sofarpc.interfaces.SofaRpcDemoService; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; @@ -32,8 +35,13 @@ public class CaseController { private static final String SUCCESS = "Success"; @Autowired + @Qualifier("consumer") private ConsumerConfig consumerConfig; + @Autowired + @Qualifier("callbackConsumer") + private ConsumerConfig callbackConsumer; + @RequestMapping("/healthCheck") @ResponseBody public String healthCheck() { @@ -45,6 +53,11 @@ public String healthCheck() { public String sofarpc() { SofaRpcDemoService service = consumerConfig.refer(); service.hello("sofarpc"); + + SofaRpcDemoService callbackService = callbackConsumer.refer(); + RpcInvokeContext invokeCtx = RpcInvokeContext.peekContext(); + invokeCtx.setResponseCallback(new TestCallback(service)); + callbackService.callback("sofarpc"); return SUCCESS; } } diff --git a/test/plugin/scenarios/sofarpc-scenario/src/main/java/org/apache/skywalking/apm/testcase/sofarpc/interfaces/SofaRpcDemoService.java b/test/plugin/scenarios/sofarpc-scenario/src/main/java/org/apache/skywalking/apm/testcase/sofarpc/interfaces/SofaRpcDemoService.java index cd541109b7..f2d0f93e9e 100644 --- a/test/plugin/scenarios/sofarpc-scenario/src/main/java/org/apache/skywalking/apm/testcase/sofarpc/interfaces/SofaRpcDemoService.java +++ b/test/plugin/scenarios/sofarpc-scenario/src/main/java/org/apache/skywalking/apm/testcase/sofarpc/interfaces/SofaRpcDemoService.java @@ -21,4 +21,6 @@ public interface SofaRpcDemoService { String hello(String name); + + String callback(String name); } diff --git a/test/plugin/scenarios/sofarpc-scenario/src/main/java/org/apache/skywalking/apm/testcase/sofarpc/service/SofaRpcDemoServiceImpl.java b/test/plugin/scenarios/sofarpc-scenario/src/main/java/org/apache/skywalking/apm/testcase/sofarpc/service/SofaRpcDemoServiceImpl.java index 9fed6ddc3d..b946932117 100644 --- a/test/plugin/scenarios/sofarpc-scenario/src/main/java/org/apache/skywalking/apm/testcase/sofarpc/service/SofaRpcDemoServiceImpl.java +++ b/test/plugin/scenarios/sofarpc-scenario/src/main/java/org/apache/skywalking/apm/testcase/sofarpc/service/SofaRpcDemoServiceImpl.java @@ -25,4 +25,9 @@ public class SofaRpcDemoServiceImpl implements SofaRpcDemoService { public String hello(String name) { return "hello, " + name; } + + @Override + public String callback(String name) { + return "hello, " + name; + } } diff --git a/test/plugin/scenarios/solon-2.x-scenario/bin/startup.sh b/test/plugin/scenarios/solon-2.x-scenario/bin/startup.sh new file mode 100644 index 0000000000..6cfc6ad702 --- /dev/null +++ b/test/plugin/scenarios/solon-2.x-scenario/bin/startup.sh @@ -0,0 +1,21 @@ +#!/bin/bash +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +home="$(cd "$(dirname $0)"; pwd)" + +java -jar ${agent_opts} "-Dskywalking.plugin.solon.http_params_length_threshold=1024" "-Dskywalking.plugin.solon.include_http_headers=content_type,host" ${home}/../libs/solon-2.x-scenario.jar & \ No newline at end of file diff --git a/test/plugin/scenarios/solon-2.x-scenario/config/expectedData.yaml b/test/plugin/scenarios/solon-2.x-scenario/config/expectedData.yaml new file mode 100644 index 0000000000..bca5273af9 --- /dev/null +++ b/test/plugin/scenarios/solon-2.x-scenario/config/expectedData.yaml @@ -0,0 +1,99 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +segmentItems: + - serviceName: solon-2.x-scenario + segmentSize: ge 2 + segments: + - segmentId: not null + spans: + - operationName: GET:/testcase/error + parentSpanId: -1 + spanId: 0 + spanLayer: Http + startTime: not null + endTime: not null + componentId: 158 + isError: true + spanType: Entry + peer: '' + skipAnalysis: false + tags: + - {key: url, value: not null} + - {key: http.method, value: GET} + - {key: http.headers, value: not null} + - {key: http.params, value: not null} + - {key: http.status_code, value: '500'} + logs: + - logEvent: + - {key: event, value: error} + - {key: error.kind, value: java.lang.RuntimeException} + - {key: message, value: this is Error} + - {key: stack, value: not null} + refs: + - {parentEndpoint: 'GET:/testcase/test', networkAddress: 'localhost:8082', + refType: CrossProcess, parentSpanId: 2, parentTraceSegmentId: not null, + parentServiceInstance: not null, parentService: solon-2.x-scenario, + traceId: not null} + - segmentId: not null + spans: + - operationName: H2/JDBC/PreparedStatement/executeQuery + parentSpanId: 0 + spanId: 1 + spanLayer: Database + startTime: not null + endTime: not null + componentId: 32 + isError: false + spanType: Exit + peer: localhost:-1 + skipAnalysis: false + tags: + - {key: db.type, value: H2} + - {key: db.instance, value: test} + - {key: db.statement, value: SELECT 1 = 1} + - operationName: /testcase/error + parentSpanId: 0 + spanId: 2 + spanLayer: Http + startTime: not null + endTime: not null + componentId: 128 + isError: true + spanType: Exit + peer: localhost:8082 + skipAnalysis: false + tags: + - {key: http.method, value: GET} + - {key: url, value: 'http://localhost:8082/testcase/error'} + - {key: http.status_code, value: '500'} + - operationName: GET:/testcase/test + parentSpanId: -1 + spanId: 0 + spanLayer: Http + startTime: not null + endTime: not null + componentId: 158 + isError: false + spanType: Entry + peer: '' + skipAnalysis: false + tags: + - {key: url, value: 'http://localhost:8082/testcase/test'} + - {key: http.method, value: GET} + - {key: http.headers, value: not null} + - {key: http.params, value: '{}'} + - {key: http.status_code, value: '200'} diff --git a/test/plugin/scenarios/solon-2.x-scenario/configuration.yml b/test/plugin/scenarios/solon-2.x-scenario/configuration.yml new file mode 100644 index 0000000000..33cd22a0dc --- /dev/null +++ b/test/plugin/scenarios/solon-2.x-scenario/configuration.yml @@ -0,0 +1,20 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +type: jvm +entryService: http://localhost:8082/testcase/test +healthCheck: http://localhost:8082/testcase/healthCheck +startScript: ./bin/startup.sh diff --git a/test/plugin/scenarios/solon-2.x-scenario/pom.xml b/test/plugin/scenarios/solon-2.x-scenario/pom.xml new file mode 100644 index 0000000000..3b850fff6b --- /dev/null +++ b/test/plugin/scenarios/solon-2.x-scenario/pom.xml @@ -0,0 +1,105 @@ + + + + 4.0.0 + + org.apache.skywalking + solon-2.x-scenario + jar + 5.0.0 + + skywalking-solon-2.x-scenario + + + UTF-8 + 2.8.3 + ${test.solon.version} + + + + + org.noear + solon-api + ${test.solon.version} + + + cn.hutool + hutool-http + 5.8.25 + + + com.h2database + h2 + 1.4.200 + + + + + solon-2.x-scenario + + + org.apache.maven.plugins + maven-compiler-plugin + 3.6.0 + + 1.8 + 1.8 + ${project.build.sourceEncoding} + + + + org.noear + solon-maven-plugin + 2.8.3 + test.apache.skywalking.apm.testcase.sc.solon.App + + + + package + + repackage + + + + + + org.apache.maven.plugins + maven-assembly-plugin + + + assemble + package + + single + + + + src/main/assembly/assembly.xml + + ./target/ + + + + + + + + diff --git a/test/plugin/scenarios/solon-2.x-scenario/src/main/assembly/assembly.xml b/test/plugin/scenarios/solon-2.x-scenario/src/main/assembly/assembly.xml new file mode 100644 index 0000000000..c408decc9d --- /dev/null +++ b/test/plugin/scenarios/solon-2.x-scenario/src/main/assembly/assembly.xml @@ -0,0 +1,41 @@ + + + + + zip + + + + + ./bin + 0775 + + + ${project.build.directory} + ./libs + + solon-2.x-scenario.jar + + 0775 + + + diff --git a/test/plugin/scenarios/solon-2.x-scenario/src/main/java/test/apache/skywalking/apm/testcase/sc/solon/App.java b/test/plugin/scenarios/solon-2.x-scenario/src/main/java/test/apache/skywalking/apm/testcase/sc/solon/App.java new file mode 100644 index 0000000000..dafe2bf504 --- /dev/null +++ b/test/plugin/scenarios/solon-2.x-scenario/src/main/java/test/apache/skywalking/apm/testcase/sc/solon/App.java @@ -0,0 +1,29 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package test.apache.skywalking.apm.testcase.sc.solon; + +import org.noear.solon.Solon; +import org.noear.solon.annotation.SolonMain; + +@SolonMain +public class App { + + public static void main(String[] args) { + Solon.start(App.class, args); + } +} diff --git a/test/plugin/scenarios/solon-2.x-scenario/src/main/java/test/apache/skywalking/apm/testcase/sc/solon/controller/Controller.java b/test/plugin/scenarios/solon-2.x-scenario/src/main/java/test/apache/skywalking/apm/testcase/sc/solon/controller/Controller.java new file mode 100644 index 0000000000..dece4c9615 --- /dev/null +++ b/test/plugin/scenarios/solon-2.x-scenario/src/main/java/test/apache/skywalking/apm/testcase/sc/solon/controller/Controller.java @@ -0,0 +1,55 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package test.apache.skywalking.apm.testcase.sc.solon.controller; + +import cn.hutool.http.HttpRequest; +import org.noear.solon.annotation.Body; +import org.noear.solon.annotation.Get; +import org.noear.solon.annotation.Inject; +import org.noear.solon.annotation.Mapping; +import org.noear.solon.annotation.Path; +import test.apache.skywalking.apm.testcase.sc.solon.service.TestService; + +import java.sql.SQLException; + +@org.noear.solon.annotation.Controller +public class Controller { + + @Inject + private TestService testService; + + @Mapping("/testcase/healthCheck") + public String healthCheck() { + return "healthCheck"; + } + + @Get + @Mapping("/testcase/{test}") + public String hello(@Body String body, @Path("test") String test) throws SQLException { + testService.executeSQL(); + String body1 = HttpRequest.get("http://localhost:8082/testcase/error").execute().body(); + return "Hello World"; + } + + @Get + @Mapping("/testcase/error") + public String error() { + throw new RuntimeException("this is Error"); + } + +} diff --git a/test/plugin/scenarios/solon-2.x-scenario/src/main/java/test/apache/skywalking/apm/testcase/sc/solon/service/TestService.java b/test/plugin/scenarios/solon-2.x-scenario/src/main/java/test/apache/skywalking/apm/testcase/sc/solon/service/TestService.java new file mode 100644 index 0000000000..ed05be7667 --- /dev/null +++ b/test/plugin/scenarios/solon-2.x-scenario/src/main/java/test/apache/skywalking/apm/testcase/sc/solon/service/TestService.java @@ -0,0 +1,43 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package test.apache.skywalking.apm.testcase.sc.solon.service; + +import org.noear.solon.annotation.Component; +import org.noear.solon.annotation.Init; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.sql.SQLException; + +@Component +public class TestService { + + private Connection connection; + + @Init + private void setUp() throws SQLException { + connection = DriverManager.getConnection("jdbc:h2:mem:test;DB_CLOSE_DELAY=-1", "sa", ""); + } + + public void executeSQL() throws SQLException { + PreparedStatement preparedStatement = connection.prepareStatement("SELECT 1 = 1"); + preparedStatement.executeQuery(); + } + +} diff --git a/test/plugin/scenarios/solon-2.x-scenario/src/main/resources/app.yml b/test/plugin/scenarios/solon-2.x-scenario/src/main/resources/app.yml new file mode 100644 index 0000000000..86f1a8ddc0 --- /dev/null +++ b/test/plugin/scenarios/solon-2.x-scenario/src/main/resources/app.yml @@ -0,0 +1,18 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +server.port: 8082 \ No newline at end of file diff --git a/test/plugin/scenarios/solon-2.x-scenario/support-version.list b/test/plugin/scenarios/solon-2.x-scenario/support-version.list new file mode 100644 index 0000000000..7e5c2fd224 --- /dev/null +++ b/test/plugin/scenarios/solon-2.x-scenario/support-version.list @@ -0,0 +1,18 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +2.7.2 +2.8.3 \ No newline at end of file diff --git a/test/plugin/scenarios/solrj-7.x-scenario/config/expectedData.yaml b/test/plugin/scenarios/solrj-7.x-scenario/config/expectedData.yaml index 3ff59ca319..93dd81f044 100644 --- a/test/plugin/scenarios/solrj-7.x-scenario/config/expectedData.yaml +++ b/test/plugin/scenarios/solrj-7.x-scenario/config/expectedData.yaml @@ -32,7 +32,7 @@ segmentItems: tags: - {key: db.type, value: Solr} - {key: http.status_code, value: '200'} - - {key: QTime, value: gt 0} + - {key: QTime, value: ge 0} skipAnalysis: 'false' - operationName: solrJ/mycore/update/COMMIT parentSpanId: 0 @@ -47,7 +47,7 @@ segmentItems: tags: - {key: db.type, value: Solr} - {key: http.status_code, value: '200'} - - {key: QTime, value: gt 0} + - {key: QTime, value: ge 0} skipAnalysis: 'false' - operationName: solrJ/mycore/update/OPTIMIZE parentSpanId: 0 @@ -62,7 +62,7 @@ segmentItems: tags: - {key: db.type, value: Solr} - {key: http.status_code, value: '200'} - - {key: QTime, value: gt 0} + - {key: QTime, value: ge 0} skipAnalysis: 'false' - operationName: solrJ/mycore/select parentSpanId: 0 @@ -111,7 +111,7 @@ segmentItems: tags: - {key: db.type, value: Solr} - {key: http.status_code, value: '200'} - - {key: QTime, value: gt 0} + - {key: QTime, value: ge 0} skipAnalysis: 'false' - operationName: solrJ/mycore/update/DELETE_BY_QUERY parentSpanId: 0 @@ -126,7 +126,7 @@ segmentItems: tags: - {key: db.type, value: Solr} - {key: http.status_code, value: '200'} - - {key: QTime, value: gt 0} + - {key: QTime, value: ge 0} skipAnalysis: 'false' - operationName: GET:/solrj-scenario/case/solrj parentSpanId: -1 diff --git a/test/plugin/scenarios/spring-4.1.x-scenario/pom.xml b/test/plugin/scenarios/spring-4.1.x-scenario/pom.xml index 012ab866eb..b6a8d68bb0 100644 --- a/test/plugin/scenarios/spring-4.1.x-scenario/pom.xml +++ b/test/plugin/scenarios/spring-4.1.x-scenario/pom.xml @@ -33,6 +33,7 @@ 3.8.1 4.1.0.RELEASE spring + 9.4.0 skywalking-spring-4.1.x-scenario @@ -86,6 +87,26 @@ log4j-core 2.8.1 + + org.springframework + spring-aop + ${test.framework.version} + + + org.springframework + spring-aspects + ${test.framework.version} + + + org.springframework.retry + spring-retry + 1.2.5.RELEASE + + + org.apache.skywalking + apm-toolkit-trace + ${apm-toolkit-trace.version} + diff --git a/test/plugin/scenarios/spring-4.1.x-scenario/src/main/java/test/apache/skywalking/apm/testcase/spring3/config/RetryConfig.java b/test/plugin/scenarios/spring-4.1.x-scenario/src/main/java/test/apache/skywalking/apm/testcase/spring3/config/RetryConfig.java new file mode 100644 index 0000000000..4d7d40d153 --- /dev/null +++ b/test/plugin/scenarios/spring-4.1.x-scenario/src/main/java/test/apache/skywalking/apm/testcase/spring3/config/RetryConfig.java @@ -0,0 +1,27 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package test.apache.skywalking.apm.testcase.spring3.config; + +import org.springframework.context.annotation.Configuration; +import org.springframework.retry.annotation.EnableRetry; + +@Configuration +@EnableRetry +public class RetryConfig { +} diff --git a/test/plugin/scenarios/spring-4.1.x-scenario/src/main/java/test/apache/skywalking/apm/testcase/spring3/service/TestServiceBean.java b/test/plugin/scenarios/spring-4.1.x-scenario/src/main/java/test/apache/skywalking/apm/testcase/spring3/service/TestServiceBean.java index c940d5425c..b99b6b38c3 100644 --- a/test/plugin/scenarios/spring-4.1.x-scenario/src/main/java/test/apache/skywalking/apm/testcase/spring3/service/TestServiceBean.java +++ b/test/plugin/scenarios/spring-4.1.x-scenario/src/main/java/test/apache/skywalking/apm/testcase/spring3/service/TestServiceBean.java @@ -18,7 +18,10 @@ package test.apache.skywalking.apm.testcase.spring3.service; +import org.apache.skywalking.apm.toolkit.trace.Trace; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.retry.annotation.Backoff; +import org.springframework.retry.annotation.Retryable; import org.springframework.stereotype.Service; import test.apache.skywalking.apm.testcase.spring3.component.TestComponentBean; import test.apache.skywalking.apm.testcase.spring3.dao.TestRepositoryBean; @@ -35,4 +38,10 @@ public void doSomeBusiness(String name) { componentBean.componentMethod(name); repositoryBean.doSomeStuff(name); } + + // Test the class is enhanced by both SkyWalking and Spring AOP + @Retryable(value = Exception.class, backoff = @Backoff(delay = 1000, multiplier = 2)) + @Trace + private void doRetry() { + } } diff --git a/test/plugin/scenarios/spring-ai-1.x-scenario/bin/startup.sh b/test/plugin/scenarios/spring-ai-1.x-scenario/bin/startup.sh new file mode 100644 index 0000000000..8cb423ece2 --- /dev/null +++ b/test/plugin/scenarios/spring-ai-1.x-scenario/bin/startup.sh @@ -0,0 +1,21 @@ +#!/bin/bash +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +home="$(cd "$(dirname $0)"; pwd)" + +java -Dskywalking.plugin.springai.collect_input_messages=true -Dskywalking.plugin.springai.collect_output_messages=true -Dskywalking.plugin.springai.collect_tool_input=true -Dskywalking.plugin.springai.collect_tool_output=true -jar ${agent_opts} ${home}/../libs/spring-ai-1.x-scenario.jar & \ No newline at end of file diff --git a/test/plugin/scenarios/spring-ai-1.x-scenario/config/expectedData.yaml b/test/plugin/scenarios/spring-ai-1.x-scenario/config/expectedData.yaml new file mode 100644 index 0000000000..5f40e79a48 --- /dev/null +++ b/test/plugin/scenarios/spring-ai-1.x-scenario/config/expectedData.yaml @@ -0,0 +1,156 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +segmentItems: + - serviceName: spring-ai-1.x-scenario + segmentSize: gt 0 + segments: + - segmentId: not null + spans: + - operationName: Spring-ai/tool/execute/get_weather + parentSpanId: 0 + spanId: 1 + spanLayer: Unknown + startTime: not null + endTime: not null + componentId: 178 + isError: false + spanType: Local + peer: '' + skipAnalysis: false + tags: + - { key: gen_ai.tool.name, value: get_weather } + - { key: gen_ai.operation.name, value: execute_tool } + - { key: gen_ai.tool.call.arguments, value: '{"arg0":"new york"}' } + - { key: gen_ai.tool.call.result, value: '"Sunny, 10°C"' } + + - operationName: Spring-ai/tool/call + parentSpanId: -1 + spanId: 0 + spanLayer: Unknown + startTime: not null + endTime: not null + componentId: 178 + isError: false + spanType: Local + peer: '' + skipAnalysis: false + refs: + - { parentEndpoint: 'GET:/spring-ai-1.x-scenario/case/spring-ai-1.x-scenario-case', + networkAddress: '', refType: CrossThread, parentSpanId: 4, parentTraceSegmentId: not null, + parentServiceInstance: not null, parentService: spring-ai-1.x-scenario, + traceId: not null } + + - segmentId: not null + spans: + - operationName: Spring-ai/tool/execute/get_weather + parentSpanId: 2 + spanId: 3 + spanLayer: Unknown + spanType: Local + startTime: not null + endTime: not null + tags: + - { key: gen_ai.tool.name, value: get_weather } + - { key: gen_ai.operation.name, value: execute_tool } + - { key: gen_ai.tool.call.arguments, value: '{"arg0":"new york"}' } + - { key: gen_ai.tool.call.result, value: '"Sunny, 10°C"' } + + - operationName: Spring-ai/tool/call + parentSpanId: 1 + spanId: 2 + spanLayer: Unknown + startTime: not null + endTime: not null + spanType: Local + + - operationName: Spring-ai/openai/call + parentSpanId: 0 + spanId: 1 + spanLayer: GenAI + startTime: not null + endTime: not null + componentId: 173 + spanType: Exit + peer: http://localhost:8080/spring-ai-1.x-scenario/llm/v1/chat/completions + tags: + - { key: gen_ai.operation.name, value: chat } + - { key: gen_ai.provider.name, value: openai } + - { key: gen_ai.request.model, value: gpt-4.1-2025-04-14 } + - { key: gen_ai.request.temperature, value: '0.7' } + - { key: gen_ai.request.top_p, value: '0.9' } + - { key: gen_ai.response.id, value: 'chatcmpl-CyJXJt7gxwDgz' } + - { key: gen_ai.response.model, value: gpt-4.1-2025-04-14 } + - { key: gen_ai.usage.input_tokens, value: '52' } + - { key: gen_ai.usage.output_tokens, value: '17' } + - { key: gen_ai.client.token.usage, value: '69' } + - { key: gen_ai.response.finish_reasons, value: STOP } + - { key: gen_ai.input.messages, value: not null } + - { key: gen_ai.output.messages, value: not null } + + - operationName: Spring-ai/openai/stream + parentSpanId: 0 + spanId: 4 + spanLayer: GenAI + startTime: not null + endTime: not null + componentId: 173 + spanType: Exit + peer: http://localhost:8080/spring-ai-1.x-scenario/llm/v1/chat/completions + tags: + - { key: gen_ai.operation.name, value: chat } + - { key: gen_ai.provider.name, value: openai } + - { key: gen_ai.request.model, value: gpt-4.1-2025-04-14 } + - { key: gen_ai.request.temperature, value: '0.7' } + - { key: gen_ai.request.top_k, value: null } + - { key: gen_ai.request.top_p, value: '0.9' } + - { key: gen_ai.server.time_to_first_token, value: not null } + - { key: gen_ai.response.id, value: 'chatcmpl-fc1b64d3' } + - { key: gen_ai.response.model, value: gpt-4.1-2025-04-14 } + - { key: gen_ai.response.finish_reasons, value: STOP } + - { key: gen_ai.usage.input_tokens, value: '104' } + - { key: gen_ai.usage.output_tokens, value: '34' } + - { key: gen_ai.client.token.usage, value: '138' } + - { key: gen_ai.input.messages, value: not null } + - { key: gen_ai.output.messages, value: not null } + + - operationName: /spring-ai-1.x-scenario/llm/v1/chat/completions + parentSpanId: 0 + spanId: 5 + spanLayer: Http + startTime: not null + endTime: not null + componentId: 99 + isError: false + spanType: Exit + peer: localhost:8080 + skipAnalysis: false + tags: + - { key: url, value: 'http://localhost:8080/spring-ai-1.x-scenario/llm/v1/chat/completions' } + - { key: http.method, value: POST } + - { key: http.status_code, value: '200' } + + - operationName: GET:/spring-ai-1.x-scenario/case/spring-ai-1.x-scenario-case + parentSpanId: -1 + spanId: 0 + spanLayer: Http + startTime: not null + endTime: not null + spanType: Entry + componentId: 1 + tags: + - { key: url, value: http://localhost:8080/spring-ai-1.x-scenario/case/spring-ai-1.x-scenario-case } + - { key: http.method, value: GET } + - { key: http.status_code, value: '200' } \ No newline at end of file diff --git a/test/plugin/scenarios/spring-ai-1.x-scenario/configuration.yml b/test/plugin/scenarios/spring-ai-1.x-scenario/configuration.yml new file mode 100644 index 0000000000..073d9271d5 --- /dev/null +++ b/test/plugin/scenarios/spring-ai-1.x-scenario/configuration.yml @@ -0,0 +1,21 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +type: jvm +entryService: http://localhost:8080/spring-ai-1.x-scenario/case/spring-ai-1.x-scenario-case +healthCheck: http://localhost:8080/spring-ai-1.x-scenario/case/healthCheck +startScript: ./bin/startup.sh + diff --git a/test/plugin/scenarios/spring-ai-1.x-scenario/pom.xml b/test/plugin/scenarios/spring-ai-1.x-scenario/pom.xml new file mode 100644 index 0000000000..b9d39f538a --- /dev/null +++ b/test/plugin/scenarios/spring-ai-1.x-scenario/pom.xml @@ -0,0 +1,156 @@ + + + + 4.0.0 + + org.apache.skywalking + spring-ai-1.x-scenario + 5.0.0 + + + UTF-8 + 17 + 3.8.1 + + + spring-ai-1.x-scenario + + + + org.springframework.boot + spring-boot-starter-web + 3.5.7 + + + + com.alibaba + fastjson + 1.2.83 + + + + org.springframework.ai + spring-ai-client-chat + + + + org.springframework.ai + spring-ai-starter-model-openai + + + + org.projectlombok + lombok + 1.18.42 + + + + + + + + org.springframework.ai + spring-ai-bom + 1.1.0 + pom + import + + + + + + spring-ai-1.x-scenario + + + org.springframework.boot + spring-boot-maven-plugin + 3.5.7 + + + + repackage + + + + + + maven-compiler-plugin + ${maven-compiler-plugin.version} + + ${compiler.version} + ${compiler.version} + ${project.build.sourceEncoding} + + + + org.apache.maven.plugins + maven-assembly-plugin + + + assemble + package + + single + + + + src/main/assembly/assembly.xml + + ./target/ + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + + + attach-javadocs + + jar + + + + + false + + + + + org.apache.maven.plugins + maven-jar-plugin + + + empty-javadoc-jar + package + + jar + + + javadoc + ${basedir}/javadoc + + + + + + + \ No newline at end of file diff --git a/test/plugin/scenarios/spring-ai-1.x-scenario/src/main/assembly/assembly.xml b/test/plugin/scenarios/spring-ai-1.x-scenario/src/main/assembly/assembly.xml new file mode 100644 index 0000000000..deed40fcfe --- /dev/null +++ b/test/plugin/scenarios/spring-ai-1.x-scenario/src/main/assembly/assembly.xml @@ -0,0 +1,41 @@ + + + + + zip + + + + + ./bin + 0775 + + + + + + ./target/spring-ai-1.x-scenario.jar + ./libs + 0775 + + + \ No newline at end of file diff --git a/test/plugin/scenarios/spring-ai-1.x-scenario/src/main/java/test/apache/skywalking/apm/testcase/jdk/httpclient/Application.java b/test/plugin/scenarios/spring-ai-1.x-scenario/src/main/java/test/apache/skywalking/apm/testcase/jdk/httpclient/Application.java new file mode 100644 index 0000000000..5147a224ee --- /dev/null +++ b/test/plugin/scenarios/spring-ai-1.x-scenario/src/main/java/test/apache/skywalking/apm/testcase/jdk/httpclient/Application.java @@ -0,0 +1,30 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package test.apache.skywalking.apm.testcase.jdk.httpclient; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class Application { + + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } + +} diff --git a/test/plugin/scenarios/spring-ai-1.x-scenario/src/main/java/test/apache/skywalking/apm/testcase/jdk/httpclient/config/ChatClientConfig.java b/test/plugin/scenarios/spring-ai-1.x-scenario/src/main/java/test/apache/skywalking/apm/testcase/jdk/httpclient/config/ChatClientConfig.java new file mode 100644 index 0000000000..79fde137ab --- /dev/null +++ b/test/plugin/scenarios/spring-ai-1.x-scenario/src/main/java/test/apache/skywalking/apm/testcase/jdk/httpclient/config/ChatClientConfig.java @@ -0,0 +1,32 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package test.apache.skywalking.apm.testcase.jdk.httpclient.config; + +import org.springframework.ai.chat.client.ChatClient; +import org.springframework.ai.openai.OpenAiChatModel; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class ChatClientConfig { + + @Bean + public ChatClient openAIChatClient(OpenAiChatModel model) { + return ChatClient.create(model); + } +} diff --git a/test/plugin/scenarios/spring-ai-1.x-scenario/src/main/java/test/apache/skywalking/apm/testcase/jdk/httpclient/controller/CaseController.java b/test/plugin/scenarios/spring-ai-1.x-scenario/src/main/java/test/apache/skywalking/apm/testcase/jdk/httpclient/controller/CaseController.java new file mode 100644 index 0000000000..d6512b726e --- /dev/null +++ b/test/plugin/scenarios/spring-ai-1.x-scenario/src/main/java/test/apache/skywalking/apm/testcase/jdk/httpclient/controller/CaseController.java @@ -0,0 +1,68 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package test.apache.skywalking.apm.testcase.jdk.httpclient.controller; + +import lombok.RequiredArgsConstructor; +import org.springframework.ai.chat.client.ChatClient; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import test.apache.skywalking.apm.testcase.jdk.httpclient.tool.WeatherTool; + +@RestController +@RequestMapping("/case") +@RequiredArgsConstructor +public class CaseController { + + private final WeatherTool weatherTool; + private final ChatClient chatClient; + + @GetMapping("/healthCheck") + public String healthCheck() { + return "Success"; + } + + @GetMapping("/spring-ai-1.x-scenario-case") + public String testCase() throws Exception { + + String systemPrompt = """ + You are a professional technical assistant. + Strictly use the provided context to answer questions. + If the information is not in the context, say: "I'm sorry, I don't have that information in my knowledge base." + Do not use outside knowledge. Be concise. + """; + + chatClient + .prompt("What's the weather in New York?") + .system(systemPrompt) + .tools(weatherTool) + .call() + .content(); + + chatClient + .prompt("What's the weather in New York?") + .system(systemPrompt) + .tools(weatherTool) + .stream() + .content() + .doOnNext(System.out::println) + .blockLast(); + + return "success"; + } +} diff --git a/test/plugin/scenarios/spring-ai-1.x-scenario/src/main/java/test/apache/skywalking/apm/testcase/jdk/httpclient/controller/LLMMockController.java b/test/plugin/scenarios/spring-ai-1.x-scenario/src/main/java/test/apache/skywalking/apm/testcase/jdk/httpclient/controller/LLMMockController.java new file mode 100644 index 0000000000..5221245280 --- /dev/null +++ b/test/plugin/scenarios/spring-ai-1.x-scenario/src/main/java/test/apache/skywalking/apm/testcase/jdk/httpclient/controller/LLMMockController.java @@ -0,0 +1,230 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package test.apache.skywalking.apm.testcase.jdk.httpclient.controller; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.io.IOException; +import java.io.PrintWriter; +import java.time.Instant; + +@RestController +@RequestMapping("/llm") +public class LLMMockController { + @RequestMapping("/v1/chat/completions") + public Object completions(@RequestBody JSONObject request, HttpServletResponse response) throws IOException { + Boolean isStream = request.getBoolean("stream"); + if (isStream == null) isStream = false; + + JSONArray messages = request.getJSONArray("messages"); + JSONObject lastMessage = messages.getJSONObject(messages.size() - 1); + String lastRole = lastMessage.getString("role"); + + if (isStream) { + response.setContentType("text/event-stream"); + response.setCharacterEncoding("UTF-8"); + response.setHeader("Cache-Control", "no-cache"); + response.setHeader("Connection", "keep-alive"); + + PrintWriter writer = response.getWriter(); + String id = "chatcmpl-fc1b64d3"; + long created = Instant.now().getEpochSecond(); + String model = "gpt-4.1-2025-04-14"; + + try { + if ("tool".equals(lastRole)) { + String fullContent = "The weather in New York is currently sunny with a temperature of 10°C."; + writeStreamChunk(writer, id, created, model, "{\"role\":\"assistant\"}", "null"); + + int len = fullContent.length(); + String[] parts = {fullContent.substring(0, len / 3), fullContent.substring(len / 3, len * 2 / 3), fullContent.substring(len * 2 / 3)}; + + for (String part : parts) { + Thread.sleep(50); + writeStreamChunk(writer, id, created, model, "{\"content\":\"" + escapeJson(part) + "\"}", "null"); + } + + writeStreamChunk(writer, id, created, model, "{}", "\"stop\""); + } else { + writeStreamChunk(writer, id, created, model, "{\"role\":\"assistant\"}", "null"); + + String toolCallDelta = """ + { + "tool_calls": [ + { + "index": 0, + "id": "call_iV4bvFIZujbb", + "type": "function", + "function": { + "name": "get_weather", + "arguments": "" + } + } + ] + } + """; + writeStreamChunk(writer, id, created, model, toolCallDelta, "null"); + + String args = "{\\\"arg0\\\":\\\"new york\\\"}"; + String argsDelta = """ + { + "tool_calls": [ + { + "index": 0, + "function": { + "arguments": "%s" + } + } + ] + } + """.formatted(args); + Thread.sleep(50); + writeStreamChunk(writer, id, created, model, argsDelta, "null"); + + writeStreamChunk(writer, id, created, model, "{}", "\"tool_calls\""); + } + + writer.write("data: [DONE]\n\n"); + writer.flush(); + + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + return null; + } + + String toolCallResponse = """ + { + "choices": [ + { + "finish_reason": "tool_calls", + "index": 0, + "message": { + "role": "assistant", + "content": null, + "tool_calls": [ + { + "function": { + "arguments": "{\\"arg0\\":\\"new york\\"}", + "name": "get_weather" + }, + "id": "call_iV4bvFIZujbb", + "type": "function" + } + ] + } + } + ], + "created": 1768490813, + "id": "chatcmpl-CyJXJt7gxwDgz", + "usage": { + "completion_tokens": 17, + "completion_tokens_details": { + "accepted_prediction_tokens": 0, + "audio_tokens": 0, + "reasoning_tokens": 0, + "rejected_prediction_tokens": 0 + }, + "prompt_tokens": 52, + "prompt_tokens_details": { + "audio_tokens": 0, + "cached_tokens": 0 + }, + "total_tokens": 69 + }, + "model": "gpt-4.1-2025-04-14", + "object": "chat.completion" + } + """; + + String finalResponse = """ + { + "choices": [ + { + "finish_reason": "stop", + "index": 0, + "message": { + "content": "The weather in New York is currently sunny with a temperature of 10°C.", + "role": "assistant" + } + } + ], + "created": 1768491057, + "id":"chatcmpl-CyJXJt7gxwDgz", + "model": "gpt-4.1-2025-04-14", + "object": "chat.completion" + } + """; + + if ("tool".equals(lastRole)) { + return JSON.parseObject(finalResponse); + } + + return JSON.parseObject(toolCallResponse); + } + + private void writeStreamChunk(PrintWriter writer, String id, long created, String model, String delta, String finishReason) { + String json = """ + { + "choices": [ + { + "delta": %s, + "finish_reason": %s, + "index": 0, + "logprobs": null + } + ], + "object": "chat.completion.chunk", + "usage": { + "completion_tokens": 17, + "completion_tokens_details": { + "accepted_prediction_tokens": 0, + "audio_tokens": 0, + "reasoning_tokens": 0, + "rejected_prediction_tokens": 0 + }, + "prompt_tokens": 52, + "prompt_tokens_details": { + "audio_tokens": 0, + "cached_tokens": 0 + }, + "total_tokens": 69 + }, + "created": %d, + "system_fingerprint": null, + "model": "%s", + "id": "%s" + } + """.formatted(delta, finishReason, created, model, id); + + String cleanJson = json.replace("\n", "").replace("\r", ""); + writer.write("data: " + cleanJson + "\n\n"); + writer.flush(); + } + + private String escapeJson(String input) { + if (input == null) return ""; + return input.replace("\"", "\\\"").replace("\n", "\\n").replace("\r", "\\r"); + } +} diff --git a/test/plugin/scenarios/spring-ai-1.x-scenario/src/main/java/test/apache/skywalking/apm/testcase/jdk/httpclient/tool/WeatherTool.java b/test/plugin/scenarios/spring-ai-1.x-scenario/src/main/java/test/apache/skywalking/apm/testcase/jdk/httpclient/tool/WeatherTool.java new file mode 100644 index 0000000000..1606b67436 --- /dev/null +++ b/test/plugin/scenarios/spring-ai-1.x-scenario/src/main/java/test/apache/skywalking/apm/testcase/jdk/httpclient/tool/WeatherTool.java @@ -0,0 +1,33 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package test.apache.skywalking.apm.testcase.jdk.httpclient.tool; + +import org.springframework.ai.tool.annotation.Tool; +import org.springframework.stereotype.Component; + +@Component +public class WeatherTool { + @Tool(name = "get_weather", description = "Get weather by city name") + public String getWeather(String city) { + return switch (city.toLowerCase()) { + case "new york" -> "Sunny, 10°C"; + case "london" -> "Cloudy, 12°C"; + default -> "Unknown city"; + }; + } +} diff --git a/test/plugin/scenarios/spring-ai-1.x-scenario/src/main/resources/application.yaml b/test/plugin/scenarios/spring-ai-1.x-scenario/src/main/resources/application.yaml new file mode 100644 index 0000000000..c4f5c58851 --- /dev/null +++ b/test/plugin/scenarios/spring-ai-1.x-scenario/src/main/resources/application.yaml @@ -0,0 +1,35 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +server: + port: 8080 + servlet: + context-path: /spring-ai-1.x-scenario + +spring: + ai: + openai: + api-key: xxxxxxxxxxxxxxxxxxxxxxxxxxx + base-url: http://localhost:8080/spring-ai-1.x-scenario/llm + chat: + options: + model: gpt-4.1-2025-04-14 + temperature: 0.7 + max-tokens: 1000 + top-p: 0.9 + + + diff --git a/test/plugin/scenarios/spring-ai-1.x-scenario/support-version.list b/test/plugin/scenarios/spring-ai-1.x-scenario/support-version.list new file mode 100644 index 0000000000..9d9826957c --- /dev/null +++ b/test/plugin/scenarios/spring-ai-1.x-scenario/support-version.list @@ -0,0 +1,18 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +1.0.1 +1.1.1 \ No newline at end of file diff --git a/test/plugin/scenarios/spring-kafka-1.3.x-scenario/configuration.yml b/test/plugin/scenarios/spring-kafka-1.3.x-scenario/configuration.yml index f608847078..8d387e2d45 100644 --- a/test/plugin/scenarios/spring-kafka-1.3.x-scenario/configuration.yml +++ b/test/plugin/scenarios/spring-kafka-1.3.x-scenario/configuration.yml @@ -28,7 +28,7 @@ dependencies: image: zookeeper:3.4 hostname: zookeeper-server kafka-server: - image: bitnami/kafka:2.1.1 + image: bitnamilegacy/kafka:2.4.1 hostname: kafka-server environment: - KAFKA_ZOOKEEPER_CONNECT=zookeeper-server:2181 diff --git a/test/plugin/scenarios/spring-kafka-2.2.x-scenario/configuration.yml b/test/plugin/scenarios/spring-kafka-2.2.x-scenario/configuration.yml index c8b94d2bee..164b29135a 100644 --- a/test/plugin/scenarios/spring-kafka-2.2.x-scenario/configuration.yml +++ b/test/plugin/scenarios/spring-kafka-2.2.x-scenario/configuration.yml @@ -28,7 +28,7 @@ dependencies: image: zookeeper:3.4 hostname: zookeeper-server kafka-server: - image: bitnami/kafka:2.1.1 + image: bitnamilegacy/kafka:2.4.1 hostname: kafka-server environment: - KAFKA_ZOOKEEPER_CONNECT=zookeeper-server:2181 diff --git a/test/plugin/scenarios/spring-kafka-2.3.x-scenario/configuration.yml b/test/plugin/scenarios/spring-kafka-2.3.x-scenario/configuration.yml index 3d40a52355..295f967541 100644 --- a/test/plugin/scenarios/spring-kafka-2.3.x-scenario/configuration.yml +++ b/test/plugin/scenarios/spring-kafka-2.3.x-scenario/configuration.yml @@ -28,7 +28,7 @@ dependencies: image: zookeeper:3.4 hostname: zookeeper-server kafka-server: - image: bitnami/kafka:2.1.1 + image: bitnamilegacy/kafka:2.4.1 hostname: kafka-server environment: - KAFKA_ZOOKEEPER_CONNECT=zookeeper-server:2181 diff --git a/test/plugin/scenarios/spring-rabbitmq-scenario/bin/startup.sh b/test/plugin/scenarios/spring-rabbitmq-scenario/bin/startup.sh new file mode 100644 index 0000000000..f98cd90a75 --- /dev/null +++ b/test/plugin/scenarios/spring-rabbitmq-scenario/bin/startup.sh @@ -0,0 +1,20 @@ +#!/bin/bash +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +home="$(cd "$(dirname $0)"; pwd)" + +java -Dspring.rabbitmq.host=${RABBITMQ_HOST:-rabbitmq-server} -jar ${agent_opts} ${home}/../libs/spring-rabbitmq-scenario.jar & diff --git a/test/plugin/scenarios/spring-rabbitmq-scenario/config/expectedData.yaml b/test/plugin/scenarios/spring-rabbitmq-scenario/config/expectedData.yaml new file mode 100644 index 0000000000..a9c4a431a2 --- /dev/null +++ b/test/plugin/scenarios/spring-rabbitmq-scenario/config/expectedData.yaml @@ -0,0 +1,165 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +segmentItems: +- serviceName: spring-rabbitmq-scenario + segmentSize: ge 4 + segments: + - segmentId: not null + spans: + - operationName: HEAD:/spring-rabbitmq-scenario/case/healthcheck + parentSpanId: -1 + spanId: 0 + spanLayer: Http + startTime: nq 0 + endTime: nq 0 + componentId: 1 + isError: false + spanType: Entry + peer: '' + tags: + - {key: url, value: 'http://localhost:8080/spring-rabbitmq-scenario/case/healthcheck'} + - {key: http.method, value: HEAD} + - {key: http.status_code, value: '200'} + skipAnalysis: 'false' + - segmentId: not null + spans: + - operationName: RabbitMQ/Topic/Queue/test/Producer + parentSpanId: 0 + spanId: 1 + spanLayer: MQ + startTime: nq 0 + endTime: nq 0 + componentId: 52 + isError: false + spanType: Exit + peer: not blank + tags: + - {key: mq.broker, value: not blank} + - {key: mq.queue, value: test} + - {key: mq.topic, value: ''} + skipAnalysis: 'false' + - operationName: RabbitMQ/Topic/Queue/test-batch/Producer + parentSpanId: 0 + spanId: 2 + spanLayer: MQ + startTime: nq 0 + endTime: nq 0 + componentId: 52 + isError: false + spanType: Exit + peer: not blank + tags: + - {key: mq.broker, value: not blank} + - {key: mq.queue, value: test-batch} + - {key: mq.topic, value: ''} + skipAnalysis: 'false' + - operationName: RabbitMQ/Topic/Queue/test-batch/Producer + parentSpanId: 0 + spanId: 3 + spanLayer: MQ + startTime: nq 0 + endTime: nq 0 + componentId: 52 + isError: false + spanType: Exit + peer: not blank + tags: + - {key: mq.broker, value: not blank} + - {key: mq.queue, value: test-batch} + - {key: mq.topic, value: ''} + skipAnalysis: 'false' + - operationName: RabbitMQ/Topic/Queue/test-batch/Producer + parentSpanId: 0 + spanId: 4 + spanLayer: MQ + startTime: nq 0 + endTime: nq 0 + componentId: 52 + isError: false + spanType: Exit + peer: not blank + tags: + - {key: mq.broker, value: not blank} + - {key: mq.queue, value: test-batch} + - {key: mq.topic, value: ''} + skipAnalysis: 'false' + - operationName: GET:/spring-rabbitmq-scenario/case/rabbitmq + parentSpanId: -1 + spanId: 0 + spanLayer: Http + startTime: nq 0 + endTime: nq 0 + componentId: 1 + isError: false + spanType: Entry + peer: '' + tags: + - {key: url, value: 'http://localhost:8080/spring-rabbitmq-scenario/case/rabbitmq'} + - {key: http.method, value: GET} + - {key: http.status_code, value: '200'} + skipAnalysis: 'false' + - segmentId: not null + spans: + - operationName: RabbitMQ/Topic/Queue/test-batch/Consumer + parentSpanId: -1 + spanId: 0 + spanLayer: MQ + startTime: nq 0 + endTime: nq 0 + componentId: 53 + isError: false + spanType: Entry + peer: not blank + tags: + - {key: transmission.latency, value: ge 0} + - {key: mq.broker, value: not blank} + - {key: mq.topic, value: ''} + - {key: mq.queue, value: test-batch} + - {key: transmission.latency, value: ge 0} + - {key: transmission.latency, value: ge 0} + refs: + - {parentEndpoint: GET:/spring-rabbitmq-scenario/case/rabbitmq, networkAddress: not null, + refType: CrossProcess, parentSpanId: 2, parentTraceSegmentId: not null, parentServiceInstance: not + null, parentService: spring-rabbitmq-scenario, traceId: not null} + - {parentEndpoint: GET:/spring-rabbitmq-scenario/case/rabbitmq, networkAddress: not null, + refType: CrossProcess, parentSpanId: 3, parentTraceSegmentId: not null, parentServiceInstance: not + null, parentService: spring-rabbitmq-scenario, traceId: not null} + - {parentEndpoint: GET:/spring-rabbitmq-scenario/case/rabbitmq, networkAddress: not null, + refType: CrossProcess, parentSpanId: 4, parentTraceSegmentId: not null, parentServiceInstance: not + null, parentService: spring-rabbitmq-scenario, traceId: not null} + skipAnalysis: 'false' + - segmentId: not null + spans: + - operationName: RabbitMQ/Topic/Queue/test/Consumer + parentSpanId: -1 + spanId: 0 + spanLayer: MQ + startTime: nq 0 + endTime: nq 0 + componentId: 53 + isError: false + spanType: Entry + peer: not blank + tags: + - {key: transmission.latency, value: ge 0} + - {key: mq.broker, value: not blank} + - {key: mq.topic, value: ''} + - {key: mq.queue, value: test} + refs: + - {parentEndpoint: GET:/spring-rabbitmq-scenario/case/rabbitmq, networkAddress: not null, + refType: CrossProcess, parentSpanId: 1, parentTraceSegmentId: not null, parentServiceInstance: not + null, parentService: spring-rabbitmq-scenario, traceId: not null} + skipAnalysis: 'false' diff --git a/test/plugin/scenarios/spring-rabbitmq-scenario/configuration.yml b/test/plugin/scenarios/spring-rabbitmq-scenario/configuration.yml new file mode 100644 index 0000000000..31cb305bd1 --- /dev/null +++ b/test/plugin/scenarios/spring-rabbitmq-scenario/configuration.yml @@ -0,0 +1,33 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +type: jvm +entryService: http://localhost:8080/spring-rabbitmq-scenario/case/rabbitmq +healthCheck: http://localhost:8080/spring-rabbitmq-scenario/case/healthcheck +startScript: ./bin/startup.sh +environment: + - RABBITMQ_HOST=rabbitmq-server +depends_on: + - rabbitmq-server +dependencies: + rabbitmq-server: + image: rabbitmq:3.8.18 + hostname: rabbitmq-server + expose: + - 5672 + - 15672 + environment: + - RABBITMQ_DEFAULT_PASS=admin + - RABBITMQ_DEFAULT_USER=admin + - RABBITMQ_DEFAULT_VHOST=/ diff --git a/test/plugin/scenarios/spring-rabbitmq-scenario/pom.xml b/test/plugin/scenarios/spring-rabbitmq-scenario/pom.xml new file mode 100644 index 0000000000..0bf98a4869 --- /dev/null +++ b/test/plugin/scenarios/spring-rabbitmq-scenario/pom.xml @@ -0,0 +1,117 @@ + + + + 4.0.0 + + org.apache.skywalking + spring-rabbitmq-scenario + 1.0-SNAPSHOT + + + UTF-8 + 17 + 3.8.1 + 3.0.0 + ${test.framework.version} + 2.19.0 + + + + + org.springframework.boot + spring-boot-starter-amqp + ${test.framework.version} + + + + org.springframework.boot + spring-boot-starter + ${test.framework.version} + + + spring-boot-starter-logging + org.springframework.boot + + + + + org.springframework.boot + spring-boot-starter-web + ${test.framework.version} + + + org.apache.logging.log4j + log4j-api + ${log4j.version} + + + org.apache.logging.log4j + log4j-core + ${log4j.version} + + + + + spring-rabbitmq-scenario + + + org.springframework.boot + spring-boot-maven-plugin + ${test.framework.version} + + + + repackage + + + + + + maven-compiler-plugin + ${maven-compiler-plugin.version} + + ${compiler.version} + ${compiler.version} + ${project.build.sourceEncoding} + + + + org.apache.maven.plugins + maven-assembly-plugin + + + assemble + package + + single + + + + src/main/assembly/assembly.xml + + + + + + + + + \ No newline at end of file diff --git a/test/plugin/scenarios/spring-rabbitmq-scenario/src/main/assembly/assembly.xml b/test/plugin/scenarios/spring-rabbitmq-scenario/src/main/assembly/assembly.xml new file mode 100644 index 0000000000..0e51e5c531 --- /dev/null +++ b/test/plugin/scenarios/spring-rabbitmq-scenario/src/main/assembly/assembly.xml @@ -0,0 +1,41 @@ + + + + + zip + + + + + ./bin + 0775 + + + + + + ${project.build.directory}/spring-rabbitmq-scenario.jar + ./libs + 0775 + + + diff --git a/test/plugin/scenarios/spring-rabbitmq-scenario/src/main/java/org/apache/skywalking/apm/testcase/spring/rabbitmq/Application.java b/test/plugin/scenarios/spring-rabbitmq-scenario/src/main/java/org/apache/skywalking/apm/testcase/spring/rabbitmq/Application.java new file mode 100644 index 0000000000..9001ed22a9 --- /dev/null +++ b/test/plugin/scenarios/spring-rabbitmq-scenario/src/main/java/org/apache/skywalking/apm/testcase/spring/rabbitmq/Application.java @@ -0,0 +1,30 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.testcase.spring.rabbitmq; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class Application { + + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } +} diff --git a/test/plugin/scenarios/spring-rabbitmq-scenario/src/main/java/org/apache/skywalking/apm/testcase/spring/rabbitmq/config/RabbitMqConfig.java b/test/plugin/scenarios/spring-rabbitmq-scenario/src/main/java/org/apache/skywalking/apm/testcase/spring/rabbitmq/config/RabbitMqConfig.java new file mode 100644 index 0000000000..a0de5ee428 --- /dev/null +++ b/test/plugin/scenarios/spring-rabbitmq-scenario/src/main/java/org/apache/skywalking/apm/testcase/spring/rabbitmq/config/RabbitMqConfig.java @@ -0,0 +1,54 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.testcase.spring.rabbitmq.config; + +import org.springframework.amqp.core.Queue; +import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory; +import org.springframework.amqp.rabbit.connection.ConnectionFactory; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class RabbitMqConfig { + + @Bean + public Queue testQueue() { + return new Queue("test", false); + } + + @Bean + public Queue testBatchQueue() { + return new Queue("test-batch", false); + } + + @Bean + public SimpleRabbitListenerContainerFactory batchRabbitListenerContainerFactory( + ConnectionFactory connectionFactory) { + SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory(); + factory.setConnectionFactory(connectionFactory); + // Enable batch listening mode + factory.setBatchListener(true); + factory.setConsumerBatchEnabled(true); + // Set batch size + factory.setBatchSize(3); + factory.setPrefetchCount(3); + factory.setReceiveTimeout(3000L); + return factory; + } +} diff --git a/test/plugin/scenarios/spring-rabbitmq-scenario/src/main/java/org/apache/skywalking/apm/testcase/spring/rabbitmq/controller/CaseController.java b/test/plugin/scenarios/spring-rabbitmq-scenario/src/main/java/org/apache/skywalking/apm/testcase/spring/rabbitmq/controller/CaseController.java new file mode 100644 index 0000000000..e143c51bf9 --- /dev/null +++ b/test/plugin/scenarios/spring-rabbitmq-scenario/src/main/java/org/apache/skywalking/apm/testcase/spring/rabbitmq/controller/CaseController.java @@ -0,0 +1,85 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.testcase.spring.rabbitmq.controller; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.springframework.amqp.core.Message; +import org.springframework.amqp.core.MessageBuilder; +import org.springframework.amqp.rabbit.annotation.RabbitListener; +import org.springframework.amqp.rabbit.core.RabbitTemplate; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/case") +public class CaseController { + + private static final Logger LOGGER = LogManager.getLogger(CaseController.class); + + private static final String QUEUE_NAME = "test"; + private static final String BATCH_QUEUE_NAME = "test-batch"; + + private static final String MESSAGE = "rabbitmq-testcase"; + + @Autowired + private RabbitTemplate rabbitTemplate; + + @RequestMapping("/rabbitmq") + @ResponseBody + public String send() { + LOGGER.info("Message being published -------------->" + MESSAGE); + rabbitTemplate.convertAndSend(QUEUE_NAME, MESSAGE); + LOGGER.info("Message has been published-------------->" + MESSAGE); + + // Also send batch messages to test batch consumption + LOGGER.info("Sending batch messages --------------"); + List messages = new ArrayList<>(); + for (int i = 0; i < 3; i++) { + Message message = MessageBuilder.withBody(("batch-message-" + i).getBytes()).build(); + messages.add(message); + } + for (Message message : messages) { + rabbitTemplate.send(BATCH_QUEUE_NAME, message); + } + LOGGER.info("Batch messages have been published--------------"); + + return "Success"; + } + + @RabbitListener(queues = QUEUE_NAME) + public void consumer(String message) { + LOGGER.info("Message Consumer received-------------->" + message); + } + + @RabbitListener(queues = BATCH_QUEUE_NAME, containerFactory = "batchRabbitListenerContainerFactory") + public void batchConsumer(List messages) { + LOGGER.info("Batch Consumer received " + messages.size() + " messages: " + messages); + } + + @RequestMapping("/healthcheck") + public String healthCheck() { + return "Success"; + } +} diff --git a/test/plugin/scenarios/spring-rabbitmq-scenario/src/main/resources/application.yml b/test/plugin/scenarios/spring-rabbitmq-scenario/src/main/resources/application.yml new file mode 100644 index 0000000000..5b93e6c91a --- /dev/null +++ b/test/plugin/scenarios/spring-rabbitmq-scenario/src/main/resources/application.yml @@ -0,0 +1,29 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# +server: + port: 8080 + servlet: + context-path: /spring-rabbitmq-scenario + +spring: + rabbitmq: + host: ${RABBITMQ_HOST:127.0.0.1} + port: 5672 + username: admin + password: admin + virtual-host: / diff --git a/test/plugin/scenarios/spring-rabbitmq-scenario/src/main/resources/log4j2.xml b/test/plugin/scenarios/spring-rabbitmq-scenario/src/main/resources/log4j2.xml new file mode 100644 index 0000000000..565fda00e0 --- /dev/null +++ b/test/plugin/scenarios/spring-rabbitmq-scenario/src/main/resources/log4j2.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + diff --git a/test/plugin/scenarios/spring-rabbitmq-scenario/support-version.list b/test/plugin/scenarios/spring-rabbitmq-scenario/support-version.list new file mode 100644 index 0000000000..8c67d6f4a8 --- /dev/null +++ b/test/plugin/scenarios/spring-rabbitmq-scenario/support-version.list @@ -0,0 +1,18 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +3.0.0 +4.0.0 \ No newline at end of file diff --git a/test/plugin/scenarios/spring-scheduled-3.x-5.x-scenario/config/expectedData.yaml b/test/plugin/scenarios/spring-scheduled-3.x-5.x-scenario/config/expectedData.yaml new file mode 100644 index 0000000000..285bb6bd3e --- /dev/null +++ b/test/plugin/scenarios/spring-scheduled-3.x-5.x-scenario/config/expectedData.yaml @@ -0,0 +1,68 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +segmentItems: +- serviceName: spring-scheduled-3.x-5.x-scenario + segmentSize: ge 2 + segments: + - segmentId: not null + spans: + - operationName: GET:/spring-scheduled-3.x-5.x-scenario/case/call + parentSpanId: -1 + spanId: 0 + spanLayer: Http + startTime: nq 0 + endTime: nq 0 + componentId: 1 + isError: false + spanType: Entry + peer: '' + skipAnalysis: false + tags: + - {key: url, value: 'http://localhost:8080/spring-scheduled-3.x-5.x-scenario/case/call'} + - {key: http.method, value: GET} + - {key: http.status_code, value: '200'} + refs: + - {parentEndpoint: SpringScheduled/org.apache.skywalking.apm.testcase.spring.scheduled.job.SchedulingJob.work, networkAddress: 'localhost:8080', refType: CrossProcess, parentSpanId: 1, parentTraceSegmentId: not null, parentServiceInstance: not null, parentService: not null, traceId: not null} + - segmentId: not null + spans: + - operationName: /spring-scheduled-3.x-5.x-scenario/case/call + parentSpanId: 0 + spanId: 1 + spanLayer: Http + startTime: not null + endTime: not null + componentId: 12 + isError: false + spanType: Exit + peer: localhost:8080 + skipAnalysis: false + tags: + - {key: http.method, value: GET} + - {key: url, value: 'http://localhost:8080/spring-scheduled-3.x-5.x-scenario/case/call'} + - {key: http.status_code, value: '200'} + - operationName: SpringScheduled/org.apache.skywalking.apm.testcase.spring.scheduled.job.SchedulingJob.work + parentSpanId: -1 + spanId: 0 + spanLayer: Unknown + startTime: not null + endTime: not null + componentId: 96 + isError: false + spanType: Local + peer: '' + skipAnalysis: false + tags: + - {key: x-le, value: '{"logic-span":true}'} diff --git a/test/plugin/scenarios/spring-scheduled-3.x-5.x-scenario/configuration.yml b/test/plugin/scenarios/spring-scheduled-3.x-5.x-scenario/configuration.yml new file mode 100644 index 0000000000..61cc0a3fb3 --- /dev/null +++ b/test/plugin/scenarios/spring-scheduled-3.x-5.x-scenario/configuration.yml @@ -0,0 +1,19 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +type: tomcat +entryService: http://localhost:8080/spring-scheduled-3.x-5.x-scenario/case/healthCheck +healthCheck: http://localhost:8080/spring-scheduled-3.x-5.x-scenario/case/healthCheck \ No newline at end of file diff --git a/test/plugin/scenarios/spring-scheduled-scenario/pom.xml b/test/plugin/scenarios/spring-scheduled-3.x-5.x-scenario/pom.xml similarity index 94% rename from test/plugin/scenarios/spring-scheduled-scenario/pom.xml rename to test/plugin/scenarios/spring-scheduled-3.x-5.x-scenario/pom.xml index 10f388f6ac..8dd8e29cbe 100644 --- a/test/plugin/scenarios/spring-scheduled-scenario/pom.xml +++ b/test/plugin/scenarios/spring-scheduled-3.x-5.x-scenario/pom.xml @@ -21,13 +21,13 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> org.apache.skywalking.apm.testcase - spring-scheduled-scenario + spring-scheduled-3.x-5.x-scenario 1.0.0 war 4.0.0 - skywalking-spring-scheduled-scenario + skywalking-spring-scheduled-3.x-5.x-scenario UTF-8 @@ -92,7 +92,7 @@ - spring-scheduled-scenario + spring-scheduled-3.x-5.x-scenario maven-compiler-plugin @@ -109,7 +109,7 @@ 2.1 8080 - /spring-scheduled-scenario + /spring-scheduled-3.x-5.x-scenario UTF-8 tomcat7 diff --git a/test/plugin/scenarios/spring-scheduled-scenario/src/main/java/org/apache/skywalking/apm/testcase/spring/scheduled/controller/CaseController.java b/test/plugin/scenarios/spring-scheduled-3.x-5.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/spring/scheduled/controller/CaseController.java similarity index 100% rename from test/plugin/scenarios/spring-scheduled-scenario/src/main/java/org/apache/skywalking/apm/testcase/spring/scheduled/controller/CaseController.java rename to test/plugin/scenarios/spring-scheduled-3.x-5.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/spring/scheduled/controller/CaseController.java diff --git a/test/plugin/scenarios/spring-scheduled-3.x-5.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/spring/scheduled/job/SchedulingJob.java b/test/plugin/scenarios/spring-scheduled-3.x-5.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/spring/scheduled/job/SchedulingJob.java new file mode 100644 index 0000000000..c943af8a56 --- /dev/null +++ b/test/plugin/scenarios/spring-scheduled-3.x-5.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/spring/scheduled/job/SchedulingJob.java @@ -0,0 +1,48 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.testcase.spring.scheduled.job; + +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.scheduling.annotation.Scheduled; + +import java.io.IOException; + +@Configuration +@EnableScheduling +public class SchedulingJob { + + private static final Logger LOGGER = LogManager.getLogger(SchedulingJob.class); + + private static final OkHttpClient CLIENT = new OkHttpClient.Builder().build(); + + @Scheduled(fixedDelay = 5000) + public void work() throws IOException { + LOGGER.info("work job running!"); + + Request request = new Request.Builder().url("http://localhost:8080/spring-scheduled-3.x-5.x-scenario/case/call").build(); + Response response = CLIENT.newCall(request).execute(); + response.body().close(); + } +} diff --git a/test/plugin/scenarios/spring-scheduled-scenario/src/main/resources/log4j2.xml b/test/plugin/scenarios/spring-scheduled-3.x-5.x-scenario/src/main/resources/log4j2.xml similarity index 100% rename from test/plugin/scenarios/spring-scheduled-scenario/src/main/resources/log4j2.xml rename to test/plugin/scenarios/spring-scheduled-3.x-5.x-scenario/src/main/resources/log4j2.xml diff --git a/test/plugin/scenarios/spring-scheduled-scenario/src/main/webapp/WEB-INF/spring-mvc-servlet.xml b/test/plugin/scenarios/spring-scheduled-3.x-5.x-scenario/src/main/webapp/WEB-INF/spring-mvc-servlet.xml similarity index 100% rename from test/plugin/scenarios/spring-scheduled-scenario/src/main/webapp/WEB-INF/spring-mvc-servlet.xml rename to test/plugin/scenarios/spring-scheduled-3.x-5.x-scenario/src/main/webapp/WEB-INF/spring-mvc-servlet.xml diff --git a/test/plugin/scenarios/spring-scheduled-3.x-5.x-scenario/src/main/webapp/WEB-INF/web.xml b/test/plugin/scenarios/spring-scheduled-3.x-5.x-scenario/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 0000000000..041c8b4f29 --- /dev/null +++ b/test/plugin/scenarios/spring-scheduled-3.x-5.x-scenario/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,34 @@ + + + skywalking-spring-scheduled-3.x-5.x-scenario + + + spring-mvc + org.springframework.web.servlet.DispatcherServlet + 1 + + + spring-mvc + / + + diff --git a/test/plugin/scenarios/spring-scheduled-scenario/support-version.list b/test/plugin/scenarios/spring-scheduled-3.x-5.x-scenario/support-version.list similarity index 97% rename from test/plugin/scenarios/spring-scheduled-scenario/support-version.list rename to test/plugin/scenarios/spring-scheduled-3.x-5.x-scenario/support-version.list index 78ff603b37..9685fe8ec3 100644 --- a/test/plugin/scenarios/spring-scheduled-scenario/support-version.list +++ b/test/plugin/scenarios/spring-scheduled-3.x-5.x-scenario/support-version.list @@ -22,4 +22,5 @@ 4.3.28.RELEASE 5.0.18.RELEASE 5.1.17.RELEASE -5.2.8.RELEASE +5.2.25.RELEASE +5.3.34 \ No newline at end of file diff --git a/test/plugin/scenarios/spring-scheduled-scenario/config/expectedData.yaml b/test/plugin/scenarios/spring-scheduled-6.x-scenario/config/expectedData.yaml similarity index 91% rename from test/plugin/scenarios/spring-scheduled-scenario/config/expectedData.yaml rename to test/plugin/scenarios/spring-scheduled-6.x-scenario/config/expectedData.yaml index 1c5f45888c..f0a297781f 100644 --- a/test/plugin/scenarios/spring-scheduled-scenario/config/expectedData.yaml +++ b/test/plugin/scenarios/spring-scheduled-6.x-scenario/config/expectedData.yaml @@ -14,12 +14,12 @@ # See the License for the specific language governing permissions and # limitations under the License. segmentItems: -- serviceName: spring-scheduled-scenario +- serviceName: spring-scheduled-6.x-scenario segmentSize: ge 2 segments: - segmentId: not null spans: - - operationName: GET:/spring-scheduled-scenario/case/call + - operationName: GET:/spring-scheduled-6.x-scenario/case/call parentSpanId: -1 spanId: 0 spanLayer: Http @@ -31,14 +31,14 @@ segmentItems: peer: '' skipAnalysis: false tags: - - {key: url, value: 'http://localhost:8080/spring-scheduled-scenario/case/call'} + - {key: url, value: 'http://localhost:8080/spring-scheduled-6.x-scenario/case/call'} - {key: http.method, value: GET} - {key: http.status_code, value: '200'} refs: - {parentEndpoint: SpringScheduled/org.apache.skywalking.apm.testcase.spring.scheduled.job.SchedulingJob.work, networkAddress: 'localhost:8080', refType: CrossProcess, parentSpanId: 1, parentTraceSegmentId: not null, parentServiceInstance: not null, parentService: not null, traceId: not null} - segmentId: not null spans: - - operationName: /spring-scheduled-scenario/case/call + - operationName: /spring-scheduled-6.x-scenario/case/call parentSpanId: 0 spanId: 1 spanLayer: Http @@ -51,7 +51,7 @@ segmentItems: skipAnalysis: false tags: - {key: http.method, value: GET} - - {key: url, value: 'http://localhost:8080/spring-scheduled-scenario/case/call'} + - {key: url, value: 'http://localhost:8080/spring-scheduled-6.x-scenario/case/call'} - {key: http.status_code, value: '200'} - operationName: SpringScheduled/org.apache.skywalking.apm.testcase.spring.scheduled.job.SchedulingJob.work parentSpanId: -1 diff --git a/test/plugin/scenarios/spring-scheduled-scenario/configuration.yml b/test/plugin/scenarios/spring-scheduled-6.x-scenario/configuration.yml similarity index 82% rename from test/plugin/scenarios/spring-scheduled-scenario/configuration.yml rename to test/plugin/scenarios/spring-scheduled-6.x-scenario/configuration.yml index 93a92b6b00..9fec8d6ef0 100644 --- a/test/plugin/scenarios/spring-scheduled-scenario/configuration.yml +++ b/test/plugin/scenarios/spring-scheduled-6.x-scenario/configuration.yml @@ -15,5 +15,5 @@ # limitations under the License. type: tomcat -entryService: http://localhost:8080/spring-scheduled-scenario/case/healthCheck -healthCheck: http://localhost:8080/spring-scheduled-scenario/case/healthCheck \ No newline at end of file +entryService: http://localhost:8080/spring-scheduled-6.x-scenario/case/healthCheck +healthCheck: http://localhost:8080/spring-scheduled-6.x-scenario/case/healthCheck \ No newline at end of file diff --git a/test/plugin/scenarios/spring-scheduled-6.x-scenario/pom.xml b/test/plugin/scenarios/spring-scheduled-6.x-scenario/pom.xml new file mode 100644 index 0000000000..273d32a00b --- /dev/null +++ b/test/plugin/scenarios/spring-scheduled-6.x-scenario/pom.xml @@ -0,0 +1,112 @@ + + + + + org.apache.skywalking.apm.testcase + spring-scheduled-6.x-scenario + 1.0.0 + war + + 4.0.0 + + skywalking-spring-scheduled-6.x-scenario + + + UTF-8 + 17 + 3.8.1 + 1.18.20 + 6.1.6 + spring-scheduled + + + + + jakarta.servlet + jakarta.servlet-api + 6.0.0 + provided + + + org.apache.logging.log4j + log4j-api + 2.8.1 + + + org.apache.logging.log4j + log4j-core + 2.8.1 + + + org.projectlombok + lombok + ${lombok.version} + provided + + + + org.springframework + spring-context + ${test.framework.version} + + + org.springframework + spring-web + ${test.framework.version} + + + org.springframework + spring-webmvc + ${test.framework.version} + + + cglib + cglib + 2.2 + + + + com.squareup.okhttp3 + okhttp + 3.0.0 + + + + + spring-scheduled-6.x-scenario + + + maven-compiler-plugin + ${maven-compiler-plugin.version} + + ${compiler.version} + ${compiler.version} + ${project.build.sourceEncoding} + + + + org.apache.maven.plugins + maven-war-plugin + 3.3.1 + + + + diff --git a/test/plugin/scenarios/spring-scheduled-6.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/spring/scheduled/controller/CaseController.java b/test/plugin/scenarios/spring-scheduled-6.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/spring/scheduled/controller/CaseController.java new file mode 100644 index 0000000000..41dd892c59 --- /dev/null +++ b/test/plugin/scenarios/spring-scheduled-6.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/spring/scheduled/controller/CaseController.java @@ -0,0 +1,46 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.testcase.spring.scheduled.controller; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseBody; + +@Controller +@RequestMapping("/case") +public class CaseController { + + private static final Logger LOGGER = LogManager.getLogger(CaseController.class); + + private static final String SUCCESS = "Success"; + + @RequestMapping("/healthCheck") + @ResponseBody + public String healthCheck() { + return SUCCESS; + } + + @RequestMapping("/call") + @ResponseBody + public String call() { + return SUCCESS; + } +} diff --git a/test/plugin/scenarios/spring-scheduled-scenario/src/main/java/org/apache/skywalking/apm/testcase/spring/scheduled/job/SchedulingJob.java b/test/plugin/scenarios/spring-scheduled-6.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/spring/scheduled/job/SchedulingJob.java similarity index 96% rename from test/plugin/scenarios/spring-scheduled-scenario/src/main/java/org/apache/skywalking/apm/testcase/spring/scheduled/job/SchedulingJob.java rename to test/plugin/scenarios/spring-scheduled-6.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/spring/scheduled/job/SchedulingJob.java index dc36b167e6..fd2e65ec45 100644 --- a/test/plugin/scenarios/spring-scheduled-scenario/src/main/java/org/apache/skywalking/apm/testcase/spring/scheduled/job/SchedulingJob.java +++ b/test/plugin/scenarios/spring-scheduled-6.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/spring/scheduled/job/SchedulingJob.java @@ -41,7 +41,7 @@ public class SchedulingJob { public void work() throws IOException { LOGGER.info("work job running!"); - Request request = new Request.Builder().url("http://localhost:8080/spring-scheduled-scenario/case/call").build(); + Request request = new Request.Builder().url("http://localhost:8080/spring-scheduled-6.x-scenario/case/call").build(); Response response = CLIENT.newCall(request).execute(); response.body().close(); } diff --git a/test/plugin/scenarios/spring-scheduled-6.x-scenario/src/main/resources/log4j2.xml b/test/plugin/scenarios/spring-scheduled-6.x-scenario/src/main/resources/log4j2.xml new file mode 100644 index 0000000000..b16354bfa3 --- /dev/null +++ b/test/plugin/scenarios/spring-scheduled-6.x-scenario/src/main/resources/log4j2.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/plugin/scenarios/spring-scheduled-6.x-scenario/src/main/webapp/WEB-INF/spring-mvc-servlet.xml b/test/plugin/scenarios/spring-scheduled-6.x-scenario/src/main/webapp/WEB-INF/spring-mvc-servlet.xml new file mode 100644 index 0000000000..78ce64329d --- /dev/null +++ b/test/plugin/scenarios/spring-scheduled-6.x-scenario/src/main/webapp/WEB-INF/spring-mvc-servlet.xml @@ -0,0 +1,38 @@ + + + + + + + + + + \ No newline at end of file diff --git a/test/plugin/scenarios/spring-scheduled-scenario/src/main/webapp/WEB-INF/web.xml b/test/plugin/scenarios/spring-scheduled-6.x-scenario/src/main/webapp/WEB-INF/web.xml similarity index 95% rename from test/plugin/scenarios/spring-scheduled-scenario/src/main/webapp/WEB-INF/web.xml rename to test/plugin/scenarios/spring-scheduled-6.x-scenario/src/main/webapp/WEB-INF/web.xml index 97967a024e..2bbdb36e96 100644 --- a/test/plugin/scenarios/spring-scheduled-scenario/src/main/webapp/WEB-INF/web.xml +++ b/test/plugin/scenarios/spring-scheduled-6.x-scenario/src/main/webapp/WEB-INF/web.xml @@ -20,7 +20,7 @@ xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" version="3.1"> - skywalking-spring-scheduled-scenario + skywalking-spring-scheduled-6.x-scenario spring-mvc diff --git a/test/plugin/scenarios/spring-scheduled-6.x-scenario/support-version.list b/test/plugin/scenarios/spring-scheduled-6.x-scenario/support-version.list new file mode 100644 index 0000000000..44322d6089 --- /dev/null +++ b/test/plugin/scenarios/spring-scheduled-6.x-scenario/support-version.list @@ -0,0 +1,18 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +6.0.19 +6.1.6 \ No newline at end of file diff --git a/test/plugin/scenarios/tomcat-thread-pool-scenario/config/expectedData.yaml b/test/plugin/scenarios/tomcat-thread-pool-scenario/config/expectedData.yaml index 6a4cd68333..c4cc8f9ddd 100644 --- a/test/plugin/scenarios/tomcat-thread-pool-scenario/config/expectedData.yaml +++ b/test/plugin/scenarios/tomcat-thread-pool-scenario/config/expectedData.yaml @@ -15,7 +15,7 @@ # limitations under the License. meterItems: - serviceName: tomcat-thread-pool-scenario - meterSize: 5 + meterSize: ge 5 meters: - meterId: name: thread_pool diff --git a/test/plugin/scenarios/undertow-worker-thread-pool-scenario/config/expectedData.yaml b/test/plugin/scenarios/undertow-worker-thread-pool-scenario/config/expectedData.yaml index 32d7928752..ba30823ec1 100644 --- a/test/plugin/scenarios/undertow-worker-thread-pool-scenario/config/expectedData.yaml +++ b/test/plugin/scenarios/undertow-worker-thread-pool-scenario/config/expectedData.yaml @@ -15,36 +15,36 @@ # limitations under the License. meterItems: - serviceName: undertow-worker-thread-pool-scenario - meterSize: 5 + meterSize: ge 5 meters: - meterId: name: thread_pool tags: - {name: metric_type, value: core_pool_size} - {name: pool_name, value: undertow_worker_pool} - singleValue: ge 1 + singleValue: ge -1 - meterId: name: thread_pool tags: - {name: metric_type, value: max_pool_size} - {name: pool_name, value: undertow_worker_pool} - singleValue: ge 1 + singleValue: ge -1 - meterId: name: thread_pool tags: - {name: metric_type, value: pool_size} - {name: pool_name, value: undertow_worker_pool} - singleValue: ge 0 + singleValue: ge -1 - meterId: name: thread_pool tags: - {name: metric_type, value: active_size} - {name: pool_name, value: undertow_worker_pool} - singleValue: ge 0 + singleValue: ge -1 - meterId: name: thread_pool tags: - {name: metric_type, value: queue_size} - {name: pool_name, value: undertow_worker_pool} - singleValue: ge 0 + singleValue: ge -1 diff --git a/test/plugin/scenarios/undertow-worker-thread-pool-scenario/pom.xml b/test/plugin/scenarios/undertow-worker-thread-pool-scenario/pom.xml index 7babe86a4e..ff3f3f582e 100644 --- a/test/plugin/scenarios/undertow-worker-thread-pool-scenario/pom.xml +++ b/test/plugin/scenarios/undertow-worker-thread-pool-scenario/pom.xml @@ -41,7 +41,7 @@ org.springframework.boot spring-boot-dependencies - ${spring-boot-version} + ${test.framework.version} pom import