You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@skywalking.apache.org by ke...@apache.org on 2021/10/20 07:11:30 UTC

[skywalking-python] branch master updated: Cleanup and Python 3.10 support (#167)

This is an automated email from the ASF dual-hosted git repository.

kezhenxu94 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/skywalking-python.git


The following commit(s) were added to refs/heads/master by this push:
     new dc927d8  Cleanup and Python 3.10 support (#167)
dc927d8 is described below

commit dc927d83d5e144401164e3991b75d26adf04b9c7
Author: Superskyyy <Su...@outlook.com>
AuthorDate: Wed Oct 20 03:11:22 2021 -0400

    Cleanup and Python 3.10 support (#167)
    
    * Cleanup py310 (#14)
    
    * [skip ci] refine changelog
    
    Signed-off-by: Superskyyy <su...@outlook.com>
    
    * re-enable CI and fix a vocab
    
    Signed-off-by: Superskyyy <su...@outlook.com>
    
    * [skip ci] enhance doc generator
    
    Signed-off-by: Superskyyy <su...@outlook.com>
    
    * Remove legacy license checker from CI
    
    Signed-off-by: Superskyyy <su...@outlook.com>
    
    * fix
    
    Signed-off-by: Superskyyy <su...@outlook.com>
---
 .dlc.json                                          | 15 ++++
 docker/Makefile => .flake8                         | 39 ++-------
 .github/workflows/{build.yaml => CI.yaml}          | 43 ++++++----
 .../workflows/dead-link-checker.yaml               | 54 +++++--------
 .licenserc.yaml                                    |  1 +
 CHANGELOG.md                                       | 11 +++
 Makefile                                           |  8 +-
 docker/Makefile                                    |  3 +-
 docs/en/contribution/PluginTest.md                 |  6 +-
 docs/en/setup/Container.md                         |  2 +-
 docs/en/setup/Plugins.md                           | 59 ++++++++------
 docs/en/setup/advanced/LogReporter.md              |  4 +-
 protocol                                           |  2 +-
 requirements.txt                                   | 93 +++++++---------------
 setup.py                                           |  1 +
 skywalking/bootstrap/loader/sitecustomize.py       |  6 +-
 skywalking/config.py                               | 14 ++--
 skywalking/decorators.py                           |  4 +-
 skywalking/plugins/__init__.py                     | 27 ++-----
 skywalking/plugins/sw_aiohttp.py                   | 11 ++-
 skywalking/plugins/sw_celery.py                    | 11 +++
 skywalking/plugins/sw_django.py                    | 10 ++-
 skywalking/plugins/sw_elasticsearch.py             |  8 ++
 skywalking/plugins/sw_falcon.py                    |  9 +++
 skywalking/plugins/sw_flask.py                     |  8 ++
 skywalking/plugins/sw_http_server.py               | 12 +++
 skywalking/plugins/sw_kafka.py                     | 13 ++-
 .../plugins/{sw_psycopg2.py => sw_psycopg.py}      | 29 ++++---
 skywalking/plugins/sw_psycopg2.py                  |  9 +++
 skywalking/plugins/sw_pymongo.py                   | 21 ++---
 skywalking/plugins/sw_pymysql.py                   |  8 ++
 skywalking/plugins/sw_pyramid.py                   |  8 ++
 skywalking/plugins/sw_rabbitmq.py                  |  8 ++
 skywalking/plugins/sw_redis.py                     | 10 ++-
 skywalking/plugins/sw_requests.py                  |  8 ++
 skywalking/plugins/sw_sanic.py                     | 30 ++++---
 skywalking/plugins/sw_tornado.py                   | 13 ++-
 skywalking/plugins/sw_urllib3.py                   |  8 ++
 skywalking/plugins/sw_urllib_request.py            |  8 ++
 skywalking/trace/carrier.py                        |  2 +-
 .../pytest.ini => skywalking/utils/comparator.py   | 11 ++-
 .../pytest.ini => skywalking/utils/exception.py    |  6 +-
 tests/orchestrator.py                              | 61 ++++++++++++++
 tests/plugin/base.py                               |  8 +-
 tests/plugin/conftest.py                           |  1 +
 tests/plugin/docker/docker-compose.base.yml        |  2 +-
 tests/plugin/pytest.ini                            |  3 +-
 tests/plugin/sw_aiohttp/expected.data.yml          |  3 -
 tests/plugin/sw_aiohttp/test_aiohttp.py            |  6 +-
 tests/plugin/sw_django/expected.data.yml           |  3 -
 tests/plugin/sw_django/test_django.py              |  9 +--
 tests/plugin/sw_elasticsearch/expected.data.yml    |  4 -
 .../plugin/sw_elasticsearch/test_elasticsearch.py  |  6 +-
 tests/plugin/sw_falcon/expected.data.yml           |  3 -
 tests/plugin/sw_falcon/test_falcon.py              |  8 +-
 tests/plugin/sw_flask/expected.data.yml            |  4 -
 tests/plugin/sw_flask/test_flask.py                |  7 +-
 tests/plugin/sw_http/expected.data.yml             |  2 -
 tests/plugin/sw_http/test_http.py                  |  1 -
 tests/plugin/sw_http_wsgi/expected.data.yml        |  1 -
 tests/plugin/sw_http_wsgi/test_http_wsgi.py        |  6 +-
 tests/plugin/sw_kafka/expected.data.yml            |  1 -
 tests/plugin/sw_kafka/test_kafka.py                |  6 +-
 .../plugin/{pytest.ini => sw_psycopg/__init__.py}  |  4 -
 .../{sw_sanic => sw_psycopg}/docker-compose.yml    | 28 ++++++-
 .../{sw_psycopg2 => sw_psycopg}/expected.data.yml  |  4 -
 .../services/__init__.py}                          |  4 -
 .../services/consumer.py}                          | 19 +++--
 .../services/provider.py}                          | 23 +++---
 .../test_psycopg.py}                               |  7 +-
 tests/plugin/sw_psycopg2/expected.data.yml         |  2 -
 tests/plugin/sw_psycopg2/test_psycopg2.py          |  6 +-
 tests/plugin/sw_pymongo/expected.data.yml          |  6 --
 tests/plugin/sw_pymongo/test_pymongo.py            |  6 +-
 tests/plugin/sw_pymysql/expected.data.yml          |  2 -
 tests/plugin/sw_pymysql/test_pymysql.py            |  6 +-
 tests/plugin/sw_pyramid/expected.data.yml          |  1 -
 .../sw_pyramid/{test_plugin.py => test_pyramid.py} |  7 +-
 tests/plugin/sw_rabbitmq/expected.data.yml         |  1 -
 tests/plugin/sw_rabbitmq/services/producer.py      |  8 +-
 tests/plugin/sw_rabbitmq/test_rabbitmq.py          |  6 +-
 tests/plugin/sw_redis/expected.data.yml            |  3 -
 tests/plugin/sw_redis/test_redis.py                |  6 +-
 tests/plugin/sw_requests/expected.data.yml         |  1 -
 tests/plugin/sw_requests/test_request.py           | 10 +--
 tests/plugin/sw_sanic/docker-compose.yml           |  4 +-
 tests/plugin/sw_sanic/expected.data.yml            |  1 -
 tests/plugin/sw_sanic/services/consumer.py         |  7 --
 tests/plugin/sw_sanic/services/provider.py         | 10 +--
 tests/plugin/sw_sanic/test_sanic.py                | 10 +--
 tests/plugin/sw_tornado/expected.data.yml          |  1 -
 tests/plugin/sw_tornado/test_tornado.py            |  7 +-
 tests/plugin/sw_urllib3/expected.data.yml          |  1 -
 tests/plugin/sw_urllib3/test_urllib3.py            |  7 +-
 tests/test_version_check.py                        | 16 ++--
 tools/doc/plugin_doc_gen.py                        | 91 +++++++++++++++++++++
 96 files changed, 676 insertions(+), 437 deletions(-)

diff --git a/.dlc.json b/.dlc.json
new file mode 100644
index 0000000..3d8e27b
--- /dev/null
+++ b/.dlc.json
@@ -0,0 +1,15 @@
+{
+  "ignorePatterns": [
+    {
+      "pattern": "^http://localhost"
+    }
+  ],
+  "timeout": "10s",
+  "retryOn429": true,
+  "retryCount": 10,
+  "fallbackRetryDelay": "1000s",
+  "aliveStatusCodes": [
+    200,
+    401
+  ]
+}
\ No newline at end of file
diff --git a/docker/Makefile b/.flake8
similarity index 50%
copy from docker/Makefile
copy to .flake8
index 7967b93..c0044e9 100644
--- a/docker/Makefile
+++ b/.flake8
@@ -14,37 +14,8 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-D := docker
-
-P := grpc http kafka
-
-TARGETS := py3.6 py3.7 py3.8 py3.9
-
-py3.6: BASE_IMAGE = python:3.6
-py3.7: BASE_IMAGE = python:3.7
-py3.8: BASE_IMAGE = python:3.8
-py3.9: BASE_IMAGE = python:3.9
-
-PUSH_TARGETS := $(TARGETS:%=push-%)
-
-word-dash = $(word $2,$(subst -, ,$1))
-
-build: $(TARGETS)
-push: $(PUSH_TARGETS)
-
-$(TARGETS):
-	for p in $(P); do \
-		$(D) build $(SW_BUILD_ARGS) \
-				--build-arg BASE_IMAGE=$(BASE_IMAGE) \
-				--build-arg SW_PYTHON_AGENT_PROTOCOL=$$p \
-				--build-arg SW_PYTHON_AGENT_VERSION=${AGENT_VERSION} \
-				-t apache/skywalking-python:${AGENT_VERSION}-$$p-$@ \
-				. ; \
-	done
-
-
-$(PUSH_TARGETS):
-	$(eval version := $(call word-dash,$@,2))
-	for p in $(P); do \
-		$(D) push apache/skywalking-python:${AGENT_VERSION}-$$p-${version}; \
-	done
+[flake8]
+ignore = E501, E126, W503  # careful with E303, which may cause too many blank lines in pycharm formatted code
+max-line-length = 120
+max-complexity = 15
+exclude = venv*,*egg_info,protocol
\ No newline at end of file
diff --git a/.github/workflows/build.yaml b/.github/workflows/CI.yaml
similarity index 67%
rename from .github/workflows/build.yaml
rename to .github/workflows/CI.yaml
index 9a18021..648464a 100644
--- a/.github/workflows/build.yaml
+++ b/.github/workflows/CI.yaml
@@ -15,20 +15,42 @@
 # limitations under the License.
 #
 
-name: Build
+name: CI
 
 on:
   push:
     branches:
       - master
   pull_request:
+#  schedule: TODO: unpin minor lib versions and check weekly
+#    - 0 0 0 ? * FRI *
+concurrency:
+  group: ci-it-${{ github.event.pull_request.number || github.ref }}
+  cancel-in-progress: true
 
 jobs:
-  Build:
+  license-and-lint:
+    name: License and Lint
     runs-on: ubuntu-latest
+    steps:
+      - name: Checkout source codes
+        uses: actions/checkout@v2
+        with:
+          submodules: true
+      - name: Check License
+        uses: apache/skywalking-eyes@49d536241d6fe8f92400702b08710514dc298cd4
+        env:
+          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+      - name: Lint codes
+        run: make lint
+  plugin-and-unit-tests:
+    name: Plugin and Unit Tests
+    needs: license-and-lint
+    runs-on: ubuntu-latest
+    timeout-minutes: 60
     strategy:
       matrix:
-        python-version: [3.6, 3.7, 3.8, 3.9]
+        python-version: ["3.6", "3.7", "3.8", "3.9", "3.10"]
       fail-fast: false
     env:
       SW_PYTHON_VERSION: ${{ matrix.python-version }}
@@ -37,27 +59,18 @@ jobs:
         uses: actions/checkout@v2
         with:
           submodules: true
-      - name: Check License
-        uses: apache/skywalking-eyes@63d89639812f1a94bd45d9329d0f936ec4769a37
-        env:
-          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
       - name: Set up Python ${{ matrix.python-version }}
         uses: actions/setup-python@v2
         with:
           python-version: ${{ matrix.python-version }}
       - name: Set up dependencies
         run: make setup install
-      - name: Lint codes
-        run: make lint
-      - name: Check license header
-        run: make license
       - name: Run unit tests
         run: make test
-
   CheckStatus:
     runs-on: ubuntu-latest
-    timeout-minutes: 90
-    needs: [Build]
+    timeout-minutes: 60
+    needs: [plugin-and-unit-tests]
     steps:
       - name: Nothing
-        run: echo "Just to make the GitHub merge button green"
+        run: echo "Just to make the GitHub merge button green"
\ No newline at end of file
diff --git a/docker/Makefile b/.github/workflows/dead-link-checker.yaml
similarity index 50%
copy from docker/Makefile
copy to .github/workflows/dead-link-checker.yaml
index 7967b93..1934585 100644
--- a/docker/Makefile
+++ b/.github/workflows/dead-link-checker.yaml
@@ -14,37 +14,23 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-D := docker
-
-P := grpc http kafka
-
-TARGETS := py3.6 py3.7 py3.8 py3.9
-
-py3.6: BASE_IMAGE = python:3.6
-py3.7: BASE_IMAGE = python:3.7
-py3.8: BASE_IMAGE = python:3.8
-py3.9: BASE_IMAGE = python:3.9
-
-PUSH_TARGETS := $(TARGETS:%=push-%)
-
-word-dash = $(word $2,$(subst -, ,$1))
-
-build: $(TARGETS)
-push: $(PUSH_TARGETS)
-
-$(TARGETS):
-	for p in $(P); do \
-		$(D) build $(SW_BUILD_ARGS) \
-				--build-arg BASE_IMAGE=$(BASE_IMAGE) \
-				--build-arg SW_PYTHON_AGENT_PROTOCOL=$$p \
-				--build-arg SW_PYTHON_AGENT_VERSION=${AGENT_VERSION} \
-				-t apache/skywalking-python:${AGENT_VERSION}-$$p-$@ \
-				. ; \
-	done
-
-
-$(PUSH_TARGETS):
-	$(eval version := $(call word-dash,$@,2))
-	for p in $(P); do \
-		$(D) push apache/skywalking-python:${AGENT_VERSION}-$$p-${version}; \
-	done
+name: Dead Link Checker
+
+on:
+  pull_request:
+
+concurrency:
+  group: dlc-${{ github.event.pull_request.number || github.ref }}
+  cancel-in-progress: true
+
+jobs:
+  CheckDeadLinks:
+    runs-on: ubuntu-latest
+    timeout-minutes: 30
+    steps:
+      - uses: actions/checkout@v2
+      - run: sudo npm install -g markdown-link-check
+      - run: |
+          for file in $(find . -name "*.md"); do
+            markdown-link-check -c .dlc.json -q "$file"
+          done
diff --git a/.licenserc.yaml b/.licenserc.yaml
index 75f8a95..ff1069f 100644
--- a/.licenserc.yaml
+++ b/.licenserc.yaml
@@ -29,5 +29,6 @@ header:
     - 'NOTICE'
     - '.github/PULL_REQUEST_TEMPLATE'
     - '.gitignore'
+    - '**/*.json'
 
   comment: on-failure
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 73940ec..88b265d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,16 @@
 ## Change Logs
 
+### 1.0.0 
+- Feature:
+
+- Plugins:
+  - Add Psycopg(3.x) support (In-progress)
+
+- Fixes:
+
+- Others:
+  - Add support for Python 3.10
+
 ### 0.7.0
 
 - Feature:
diff --git a/Makefile b/Makefile
index 6b8cfa3..1743436 100644
--- a/Makefile
+++ b/Makefile
@@ -31,8 +31,12 @@ gen:
 
 lint: clean
 	flake8 --version || python3 -m pip install flake8
-	flake8 . --count --select=E9,F63,F7,F82 --show-source
-	flake8 . --count --max-complexity=15 --max-line-length=120
+	flake8 . --count --show-source
+
+dev-check:
+	flake8 --version || python3 -m pip install flake8
+	flake8 . --count --show-source
+	python3 tools/check-license-header.py skywalking tests tools
 
 license: clean
 	python3 tools/check-license-header.py skywalking tests tools
diff --git a/docker/Makefile b/docker/Makefile
index 7967b93..eeba161 100644
--- a/docker/Makefile
+++ b/docker/Makefile
@@ -18,12 +18,13 @@ D := docker
 
 P := grpc http kafka
 
-TARGETS := py3.6 py3.7 py3.8 py3.9
+TARGETS := py3.6 py3.7 py3.8 py3.9 py3.10
 
 py3.6: BASE_IMAGE = python:3.6
 py3.7: BASE_IMAGE = python:3.7
 py3.8: BASE_IMAGE = python:3.8
 py3.9: BASE_IMAGE = python:3.9
+py3.9: BASE_IMAGE = python:3.10
 
 PUSH_TARGETS := $(TARGETS:%=push-%)
 
diff --git a/docs/en/contribution/PluginTest.md b/docs/en/contribution/PluginTest.md
index 47c45f1..540c9a2 100644
--- a/docs/en/contribution/PluginTest.md
+++ b/docs/en/contribution/PluginTest.md
@@ -1,6 +1,10 @@
 # Plugin Test
 
 Plugin tests are required and should pass before a new plugin is able to merge into the master branch.
+Specify a support matrix in each plugin in the `skywalking/plugins` folder, along with their website links,
+the matrix and links will be used for plugin support table documentation generation for this doc [Plugins.md](../setup/Plugins.md).
+
+Use `tools/plugin_doc_gen.py` to generate a table and paste into `Plugins.md` after all test passes.
 
 ## Mock Collector
 
@@ -31,7 +35,7 @@ If we want to test the plugin for the built-in library `http`, we will:
 take this [provider service](https://github.com/apache/skywalking-python/blob/master/tests/plugin/sw_http/services/provider.py) as example.
 2. Compose a `docker-compose.yml` file, orchestrating the service built in step 1 and the mock collector, 
 take this [docker-compose.yml](https://github.com/apache/skywalking-python/blob/master/tests/plugin/sw_http/docker-compose.yml) as an example.
-3. Write test codes to trigger the endpoint int step 1, and send the expected data file to the mock collector to verify, 
+3. Write test codes to trigger the endpoint in step 1, and send the expected data file to the mock collector to verify, 
 take this [test](https://github.com/apache/skywalking-python/blob/master/tests/plugin/sw_http/test_http.py) as example.
 
 ## Notes
diff --git a/docs/en/setup/Container.md b/docs/en/setup/Container.md
index b776db9..a904321 100644
--- a/docs/en/setup/Container.md
+++ b/docs/en/setup/Container.md
@@ -4,7 +4,7 @@
 source**
 
 This image hosts the SkyWalking Python agent package on top of official Python base images providing support from 
-Python 3.6 - 3.9.
+Python 3.6 - 3.10.
 
 ## How to use this image
 
diff --git a/docs/en/setup/Plugins.md b/docs/en/setup/Plugins.md
index 9ca850f..63bf5d2 100644
--- a/docs/en/setup/Plugins.md
+++ b/docs/en/setup/Plugins.md
@@ -1,27 +1,40 @@
 # Supported Libraries
+This document is **automatically** generated from the SkyWalking Python testing matrix.
 
-Library | Versions | Plugin Name
-| :--- | :--- | :--- |
-| [http.server](https://docs.python.org/3/library/http.server.html) | Python 3.6 ~ 3.9 | `sw_http_server` |
-| [urllib.request](https://docs.python.org/3/library/urllib.request.html) | Python 3.6 ~ 3.9 | `sw_urllib_request` |
-| [requests](https://requests.readthedocs.io/en/master/) | >= 2.9.0 < 2.15.0, >= 2.17.0 <= 2.24.0 | `sw_requests` |
-| [Flask](https://flask.palletsprojects.com/en/1.1.x/) | >=1.0.4 <= 1.1.2 | `sw_flask` |
-| [PyMySQL](https://pymysql.readthedocs.io/en/latest/) | 0.10.0 | `sw_pymysql` |
-| [Django](https://www.djangoproject.com/) | >=2.0 <= 3.1 | `sw_django` |
-| [redis-py](https://github.com/andymccurdy/redis-py/) | 3.5.3 | `sw_redis` |
-| [kafka-python](https://kafka-python.readthedocs.io/en/master/) | 2.0.1 | `sw_kafka` |
-| [tornado](https://www.tornadoweb.org/en/stable/) | >=5.0 | `sw_tornado` |
-| [pika](https://pika.readthedocs.io/en/stable/) | 1.1.0 | `sw_rabbitmq` |
-| [pymongo](https://pymongo.readthedocs.io/en/stable/) | 3.11.0 | `sw_pymongo` |
-| [elasticsearch](https://github.com/elastic/elasticsearch-py) | 7.9.0 | `sw_elasticsearch` |
-| [urllib3](https://urllib3.readthedocs.io/en/latest/) | >= 1.25.9 <= 1.25.10 | `sw_urllib3` |
-| [sanic](https://sanic.readthedocs.io/en/latest/) | >= 20.3.0 <= 20.9.1 | `sw_sanic` |
-| [aiohttp](https://sanic.readthedocs.io/en/latest/) | >= 3.7.3 | `sw_aiohttp` |
-| [pyramid](https://trypyramid.com) | >= 1.9 | `sw_pyramid` |
-| [psycopg2](https://www.psycopg.org/) | >= 2.8.6 | `sw_psycopg2` |
-| [celery](https://docs.celeryproject.org/) | >= 4.2.1 | `sw_celery` |
-| [falcon](https://falcon.readthedocs.io/en/stable/) | >= 1.4.1 | `sw_falcon` |
+The column of versions only indicates the set of library versions tested in a best-effort manner.
+
+If you find newer major versions that are missing from the following table, and it's not documented as a limitation, 
+please PR to update the test matrix in the plugin.
 
-* Note: The celery server running with "celery -A ..." should be run with the HTTP protocol as it uses multiprocessing by default which is not compatible with the gRPC protocol implementation in SkyWalking currently. Celery clients can use whatever protocol they want.
+Versions marked as NOT SUPPORTED may be due to
+an incompatible version with Python in the original library
+or a limitation of SkyWalking auto-instrumentation (welcome to contribute!)
 
-The column `Versions` only indicates that the versions are tested, if you found the newer versions are also supported, welcome to add the newer version into the table.
+### Plugin Support Table
+Library | Python Version - Lib Version | Plugin Name
+| :--- | :--- | :--- |
+| [aiohttp](https://docs.aiohttp.org) | Python >=3.10 - NOT SUPPORTED YET; Python >=3.6 - ['3.7.4'];  | `sw_aiohttp` |
+| [celery](https://docs.celeryproject.org) | Python >=3.6 - ['5.1'];  | `sw_celery` |
+| [django](https://www.djangoproject.com/) | Python >=3.6 - ['3.2'];  | `sw_django` |
+| [elasticsearch](https://github.com/elastic/elasticsearch-py) | Python >=3.6 - ['7.13', '7.14', '7.15'];  | `sw_elasticsearch` |
+| [hug](https://falcon.readthedocs.io/en/stable/) | Python >=3.10 - ['2.5', '2.6']; Python >=3.6 - ['2.4.1', '2.5', '2.6'];  | `sw_falcon` |
+| [flask](https://flask.palletsprojects.com) | Python >=3.6 - ['1.1', '2.0'];  | `sw_flask` |
+| [http_server](https://docs.python.org/3/library/http.server.html) | Python >=3.6 - ['*'];  | `sw_http_server` |
+| [werkzeug](https://werkzeug.palletsprojects.com/) | Python >=3.6 - ['1.0.1', '2.0'];  | `sw_http_server` |
+| [kafka-python](https://kafka-python.readthedocs.io) | Python >=3.6 - ['2.0'];  | `sw_kafka` |
+| [psycopg[binary]](https://www.psycopg.org/) | Python >=3.6 - ['3.0'];  | `sw_psycopg` |
+| [psycopg2-binary](https://www.psycopg.org/) | Python >=3.10 - NOT SUPPORTED YET; Python >=3.6 - ['2.9'];  | `sw_psycopg2` |
+| [pymongo](https://pymongo.readthedocs.io) | Python >=3.6 - ['3.11'];  | `sw_pymongo` |
+| [pymysql](https://pymysql.readthedocs.io/en/latest/) | Python >=3.6 - ['1.0'];  | `sw_pymysql` |
+| [pyramid](https://trypyramid.com) | Python >=3.6 - ['1.10', '2.0'];  | `sw_pyramid` |
+| [pika](https://pika.readthedocs.io) | Python >=3.6 - ['1.2'];  | `sw_rabbitmq` |
+| [redis](https://github.com/andymccurdy/redis-py/) | Python >=3.6 - ['3.5'];  | `sw_redis` |
+| [requests](https://requests.readthedocs.io/en/master/) | Python >=3.6 - ['2.26', '2.25'];  | `sw_requests` |
+| [sanic](https://sanic.readthedocs.io/en/latest) | Python >=3.10 - NOT SUPPORTED YET; Python >=3.7 - ['20.12']; Python >=3.6 - ['20.12'];  | `sw_sanic` |
+| [tornado](https://www.tornadoweb.org) | Python >=3.6 - ['6.0', '6.1'];  | `sw_tornado` |
+| [urllib3](https://urllib3.readthedocs.io/en/latest/) | Python >=3.6 - ['1.26', '1.25'];  | `sw_urllib3` |
+| [urllib_request](https://docs.python.org/3/library/urllib.request.html) | Python >=3.6 - ['*'];  | `sw_urllib_request` |
+### Notes
+- The celery server running with "celery -A ..." should be run with the HTTP protocol
+as it uses multiprocessing by default which is not compatible with the gRPC protocol implementation 
+in SkyWalking currently. Celery clients can use whatever protocol they want.
diff --git a/docs/en/setup/advanced/LogReporter.md b/docs/en/setup/advanced/LogReporter.md
index f223e7c..d08972b 100644
--- a/docs/en/setup/advanced/LogReporter.md
+++ b/docs/en/setup/advanced/LogReporter.md
@@ -1,6 +1,6 @@
 # Python Agent Log Reporter
 
-This functionality reports logs collected from the Python logging module(in theory, also logging libraries depending on the core logging module).
+This functionality reports logs collected from the Python logging module (in theory, also logging libraries depending on the core logging module).
 
 To utilize this feature, you will need to add some new configurations to the agent initialization step.
 
@@ -39,7 +39,7 @@ Note that it also works with your custom logger levels, simply specify its strin
 ### Ignore log filters
 The following config is disabled by default. When enabled, the log reporter will collect logs disregarding your custom log filters.
 
-For example, if you attach the filter below to the logger - the default behavior of log reporting aligns with the filter
+For example, if you attach the filter below to the logger - the default behavior of log reporting aligns with the filter 
 (not reporting any logs with a message starting with `SW test`)
 ```python
 class AppFilter(logging.Filter):
diff --git a/protocol b/protocol
index 7da226c..e6742be 160000
--- a/protocol
+++ b/protocol
@@ -1 +1 @@
-Subproject commit 7da226cfced7fa4eb91c6528e8c30827288531a0
+Subproject commit e6742be211302cf7eb93db83bdf1da2a8e600d17
diff --git a/requirements.txt b/requirements.txt
old mode 100755
new mode 100644
index e97bddc..90e5185
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,65 +1,30 @@
-asgiref==3.2.10
-aiofiles==0.6.0
-aiohttp==3.7.3
-attrs==19.3.0
-blindspin==2.0.1
-celery==4.4.7
-certifi==2020.6.20
-chardet==3.0.4
-click==7.1.2
-colorama==0.4.3
-crayons==0.3.1
-deprecation==2.1.0
-Django==3.1
-docker==4.2.2
-elasticsearch==7.8.0
-Flask==1.1.2
-gevent==20.6.2
-greenlet==0.4.16
-grpcio==1.31.0
-grpcio-tools==1.31.0
-httpcore==0.11.1
-httptools==0.1.1
-httpx==0.15.4
-idna==2.10
-importlib-metadata==1.7.0
-iniconfig==1.0.1
-itsdangerous==1.1.0
-Jinja2==2.11.2
-kafka-python==2.0.1
-MarkupSafe==1.1.1
-more-itertools==8.4.0
-multidict==5.0.0
-packaging==20.4
-parameterized==0.7.4
-pika==1.1.0
-pluggy==0.13.1
-protobuf==3.12.4
-psycopg2-binary==2.8.6
-py==1.9.0
-pymongo==3.11.0
-PyMySQL==0.10.0
-pyparsing==2.4.7
-pyramid==1.10.5
-pytest==6.0.1
-pytz==2020.1
-PyYAML==5.3.1
+pytest==6.2.5
+grpcio_tools==1.41.0
+wrapt==1.13.2
+requests==2.26.0
+urllib3==1.26.7
+testcontainers==3.4.2
+packaging==21.0
+aiohttp==3.7.4
+celery==5.1.2
+contextvars==2.4
+Django==3.2.8
+elasticsearch==7.15.1
+Flask==2.0.2
+kafka-python==2.0
+gevent==21.8.0
+hug==2.6.1
+multidict==5.2.0
+pika==1.2.0
+protobuf==3.18.1
+#psycopg2-binary==2.9.1
+psycopg[binary]==3.0.1
+pymongo==3.12.0
+pymysql==1.0.2
+pyramid==2.0
+PyYAML==6.0
 redis==3.5.3
-requests==2.24.0
-rfc3986==1.4.0
-sanic==20.9.1
-six==1.15.0
-sniffio==1.2.0
-sqlparse==0.3.1
-testcontainers==3.0.3
-toml==0.10.1
-tornado==6.0.4
-hug==2.4.1
-urllib3==1.25.10
-websockets==8.1
-websocket-client==0.57.0
-Werkzeug==1.0.1
-wrapt==1.12.1
-zipp==3.1.0
-zope.event==4.4
-zope.interface==5.1.0
+sanic==21.9.1
+tornado==6.1
+Werkzeug==2.0.2
+yarl==1.7.0
diff --git a/setup.py b/setup.py
index fb1001a..22544ac 100644
--- a/setup.py
+++ b/setup.py
@@ -67,6 +67,7 @@ setup(
         "Programming Language :: Python :: 3.7",
         "Programming Language :: Python :: 3.8",
         "Programming Language :: Python :: 3.9",
+        "Programming Language :: Python :: 3.10",
 
         "Topic :: Software Development",
     ],
diff --git a/skywalking/bootstrap/loader/sitecustomize.py b/skywalking/bootstrap/loader/sitecustomize.py
index 0a8bda9..1bd8aeb 100644
--- a/skywalking/bootstrap/loader/sitecustomize.py
+++ b/skywalking/bootstrap/loader/sitecustomize.py
@@ -131,7 +131,11 @@ if not (version_match and prefix_match):
     os._exit(1)  # do not go further
 
 else:
-    from skywalking import agent
+    from skywalking import agent, config
+
+    # also override debug for skywalking agent itself
+    if os.environ.get('SW_PYTHON_CLI_DEBUG_ENABLED') == 'True':  # set from the original CLI runner
+        config.logging_level = 'DEBUG'
 
     # Currently supports configs read from os.environ
 
diff --git a/skywalking/config.py b/skywalking/config.py
index ae3491b..59865cc 100644
--- a/skywalking/config.py
+++ b/skywalking/config.py
@@ -68,7 +68,7 @@ celery_parameters_length = int(os.getenv('SW_CELERY_PARAMETERS_LENGTH') or '512'
 # profile configs
 get_profile_task_interval = int(os.getenv('SW_PROFILE_TASK_QUERY_INTERVAL') or '20')  # type: int
 profile_active = False if os.getenv('SW_AGENT_PROFILE_ACTIVE') and \
-                         os.getenv('SW_AGENT_PROFILE_ACTIVE') == 'False' else True  # type: bool
+                          os.getenv('SW_AGENT_PROFILE_ACTIVE') == 'False' else True  # type: bool
 profile_max_parallel = int(os.getenv("SW_AGENT_PROFILE_MAX_PARALLEL") or '5')  # type: int
 profile_duration = int(os.getenv('SW_AGENT_PROFILE_DURATION') or '10')  # type: int
 profile_dump_max_stack_depth = int(os.getenv('SW_AGENT_PROFILE_DUMP_MAX_STACK_DEPTH') or '500')  # type: int
@@ -79,11 +79,11 @@ log_reporter_active = True if os.getenv('SW_AGENT_LOG_REPORTER_ACTIVE') and \
 log_reporter_max_buffer_size = int(os.getenv('SW_AGENT_LOG_REPORTER_BUFFER_SIZE') or '10000')  # type: int
 log_reporter_level = os.getenv('SW_AGENT_LOG_REPORTER_LEVEL') or 'WARNING'  # type: str
 log_reporter_ignore_filter = True if os.getenv('SW_AGENT_LOG_REPORTER_IGNORE_FILTER') and \
-                         os.getenv('SW_AGENT_LOG_REPORTER_IGNORE_FILTER') == 'True' else False  # type: bool
+                                     os.getenv('SW_AGENT_LOG_REPORTER_IGNORE_FILTER') == 'True' else False  # type: bool
 log_reporter_formatted = False if os.getenv('SW_AGENT_LOG_REPORTER_FORMATTED') and \
-                         os.getenv('SW_AGENT_LOG_REPORTER_FORMATTED') == 'False' else True  # type: bool
+                                  os.getenv('SW_AGENT_LOG_REPORTER_FORMATTED') == 'False' else True  # type: bool
 log_reporter_layout = os.getenv('SW_AGENT_LOG_REPORTER_LAYOUT') or \
-                        '%(asctime)s [%(threadName)s] %(levelname)s %(name)s - %(message)s'  # type: str
+                      '%(asctime)s [%(threadName)s] %(levelname)s %(name)s - %(message)s'  # type: str
 cause_exception_depth = int(os.getenv('SW_AGENT_CAUSE_EXCEPTION_DEPTH') or '5')  # type: int
 
 options = {key for key in globals() if key not in options}  # THIS MUST FOLLOW DIRECTLY AFTER LIST OF CONFIG OPTIONS!
@@ -104,10 +104,10 @@ def finalize():
     suffix = r'^.+(?:' + '|'.join(reesc.sub(r'\\\1', s.strip()) for s in ignore_suffix.split(',')) + ')$'
     method = r'^' + '|'.join(s.strip() for s in http_ignore_method.split(',')) + '$'
     path = '^(?:' + \
-           '|'.join(                          # replaces ","
+           '|'.join(  # replaces ","
                '(?:(?:[^/]+/)*[^/]+)?'.join(  # replaces "**"
-                   '[^/]*'.join(              # replaces "*"
-                       '[^/]'.join(           # replaces "?"
+                   '[^/]*'.join(  # replaces "*"
+                       '[^/]'.join(  # replaces "?"
                            reesc.sub(r'\\\1', s) for s in p2.split('?')
                        ) for p2 in p1.split('*')
                    ) for p1 in p0.strip().split('**')
diff --git a/skywalking/decorators.py b/skywalking/decorators.py
index a6cdbc2..29d8e43 100644
--- a/skywalking/decorators.py
+++ b/skywalking/decorators.py
@@ -15,9 +15,9 @@
 # limitations under the License.
 #
 
+import inspect
 from functools import wraps
 from typing import List
-import inspect
 
 from skywalking import Layer, Component
 from skywalking.trace.context import get_context
@@ -75,7 +75,7 @@ def runnable(
 
         @wraps(func)
         def wrapper(*args, **kwargs):
-            _op = op or "Thread/"+func.__name__
+            _op = op or "Thread/" + func.__name__
             context = get_context()
             with context.new_local_span(op=_op) as span:
                 context.continued(snapshot)
diff --git a/skywalking/plugins/__init__.py b/skywalking/plugins/__init__.py
index 7aae8f5..c62a958 100644
--- a/skywalking/plugins/__init__.py
+++ b/skywalking/plugins/__init__.py
@@ -16,18 +16,18 @@
 #
 import inspect
 import logging
-from skywalking.loggings import logger
 import pkgutil
 import re
 import traceback
 
 import pkg_resources
-
 from packaging import version
 
-from skywalking import config
-
 import skywalking
+from skywalking import config
+from skywalking.loggings import logger
+from skywalking.utils.comparator import operators
+from skywalking.utils.exception import VersionRuleException
 
 
 def install():
@@ -43,6 +43,7 @@ def install():
         logger.debug('installing plugin %s', modname)
         plugin = importer.find_module(modname).load_module(modname)
 
+        # todo: refactor the version checker, currently it doesn't really work as intended
         supported = pkg_version_check(plugin)
         if not supported:
             logger.debug('check version for plugin %s\'s corresponding package failed, thus '
@@ -56,26 +57,12 @@ def install():
         # noinspection PyBroadException
         try:
             plugin.install()
+            logger.debug('Successfully installed plugin %s', modname)
         except Exception:
             logger.warning('failed to install plugin %s', modname)
             traceback.print_exc() if logger.isEnabledFor(logging.DEBUG) else None
 
 
-_operators = {
-    '<': lambda cv, ev: cv < ev,
-    '<=': lambda cv, ev: cv < ev or cv == ev,
-    '==': lambda cv, ev: cv == ev,
-    '>=': lambda cv, ev: cv > ev or cv == ev,
-    '>': lambda cv, ev: cv > ev,
-    '!=': lambda cv, ev: cv != ev
-}
-
-
-class VersionRuleException(Exception):
-    def __init__(self, message):
-        self.message = message
-
-
 def pkg_version_check(plugin):
     supported = True
 
@@ -118,7 +105,7 @@ def check(rule_unit, current_version):
     expect_pkg_version = rule_unit[idx:]
 
     expect_version = version.parse(expect_pkg_version)
-    f = _operators.get(symbol) or None
+    f = operators.get(symbol) or None
     if not f:
         raise VersionRuleException("version rule {} error. only allow >,>=,==,<=,<,!= symbols".format(rule_unit))
 
diff --git a/skywalking/plugins/sw_aiohttp.py b/skywalking/plugins/sw_aiohttp.py
index d44909f..aa110c6 100644
--- a/skywalking/plugins/sw_aiohttp.py
+++ b/skywalking/plugins/sw_aiohttp.py
@@ -21,6 +21,15 @@ from skywalking.trace.context import get_context, NoopContext
 from skywalking.trace.span import NoopSpan
 from skywalking.trace.tags import TagHttpMethod, TagHttpURL, TagHttpStatusCode
 
+link_vector = ["https://docs.aiohttp.org"]
+support_matrix = {
+    "aiohttp": {
+        ">=3.10": [],  # waiting for 3.8 release
+        ">=3.6": ["3.7.4"]
+    }
+}
+note = """"""
+
 
 def install():
     from aiohttp import ClientSession
@@ -79,7 +88,7 @@ def install():
         with span:
             span.layer = Layer.Http
             span.component = Component.AioHttp
-            span.peer = '%s:%d' % request._transport_peername if isinstance(request._transport_peername, (list, tuple))\
+            span.peer = '%s:%d' % request._transport_peername if isinstance(request._transport_peername, (list, tuple)) \
                 else request._transport_peername
 
             span.tag(TagHttpMethod(method))  # pyre-ignore
diff --git a/skywalking/plugins/sw_celery.py b/skywalking/plugins/sw_celery.py
index 8a9c52d..6778a92 100644
--- a/skywalking/plugins/sw_celery.py
+++ b/skywalking/plugins/sw_celery.py
@@ -20,6 +20,17 @@ from skywalking.trace.carrier import Carrier
 from skywalking.trace.context import get_context
 from skywalking.trace.tags import TagMqBroker, TagCeleryParameters
 
+link_vector = ["https://docs.celeryproject.org"]
+# TODO: Celery is missing plugin test
+support_matrix = {
+    "celery": {
+        ">=3.6": ["5.1"]
+    }
+}
+note = """The celery server running with "celery -A ..." should be run with the HTTP protocol
+as it uses multiprocessing by default which is not compatible with the gRPC protocol implementation
+in SkyWalking currently. Celery clients can use whatever protocol they want."""
+
 
 def install():
     from urllib.parse import urlparse
diff --git a/skywalking/plugins/sw_django.py b/skywalking/plugins/sw_django.py
index 98b5c66..bb3c282 100644
--- a/skywalking/plugins/sw_django.py
+++ b/skywalking/plugins/sw_django.py
@@ -21,10 +21,14 @@ from skywalking.trace.context import get_context, NoopContext
 from skywalking.trace.span import NoopSpan
 from skywalking.trace.tags import TagHttpMethod, TagHttpURL, TagHttpStatusCode, TagHttpParams
 
-version_rule = {
-    "name": "django",
-    "rules": [">=2.0"]
+link_vector = ["https://www.djangoproject.com/"]
+support_matrix = {
+    "django": {
+        ">=3.6": ["3.2"],
+        # ">=3.8": ["4.0a1"]  # expected Dec 2021
+    }
 }
+note = """"""
 
 
 def install():
diff --git a/skywalking/plugins/sw_elasticsearch.py b/skywalking/plugins/sw_elasticsearch.py
index e46ac15..2711d22 100644
--- a/skywalking/plugins/sw_elasticsearch.py
+++ b/skywalking/plugins/sw_elasticsearch.py
@@ -19,6 +19,14 @@ from skywalking import Layer, Component, config
 from skywalking.trace.context import get_context
 from skywalking.trace.tags import TagDbType, TagDbStatement
 
+link_vector = ["https://github.com/elastic/elasticsearch-py"]
+support_matrix = {
+    "elasticsearch": {
+        ">=3.6": ["7.13", "7.14", "7.15"],
+    }
+}
+note = """"""
+
 
 def install():
     from elasticsearch import Transport
diff --git a/skywalking/plugins/sw_falcon.py b/skywalking/plugins/sw_falcon.py
index 86bd493..8c1b5e9 100644
--- a/skywalking/plugins/sw_falcon.py
+++ b/skywalking/plugins/sw_falcon.py
@@ -21,6 +21,15 @@ from skywalking.trace.context import get_context, NoopContext
 from skywalking.trace.span import NoopSpan
 from skywalking.trace.tags import TagHttpMethod, TagHttpURL, TagHttpParams, TagHttpStatusCode, TagHttpStatusMsg
 
+link_vector = ["https://falcon.readthedocs.io/en/stable/"]
+support_matrix = {
+    "hug": {
+        ">=3.10": ["2.5", "2.6"],  # api deprecated for 3.10
+        ">=3.6": ["2.4.1", "2.5", "2.6"],  # support begins 2.4.1
+    }
+}
+note = """"""
+
 
 def install():
     from falcon import API, request, RequestOptions
diff --git a/skywalking/plugins/sw_flask.py b/skywalking/plugins/sw_flask.py
index 2bab80c..fe55ac2 100644
--- a/skywalking/plugins/sw_flask.py
+++ b/skywalking/plugins/sw_flask.py
@@ -21,6 +21,14 @@ from skywalking.trace.context import get_context, NoopContext
 from skywalking.trace.span import NoopSpan
 from skywalking.trace.tags import TagHttpMethod, TagHttpURL, TagHttpStatusCode, TagHttpParams
 
+link_vector = ["https://flask.palletsprojects.com"]
+support_matrix = {
+    "flask": {
+        ">=3.6": ["1.1", "2.0"]  # 1.1 to be removed in near future
+    }
+}
+note = """"""
+
 
 def install():
     from flask import Flask
diff --git a/skywalking/plugins/sw_http_server.py b/skywalking/plugins/sw_http_server.py
index ae5634b..899ba95 100644
--- a/skywalking/plugins/sw_http_server.py
+++ b/skywalking/plugins/sw_http_server.py
@@ -23,6 +23,18 @@ from skywalking.trace.context import get_context, NoopContext
 from skywalking.trace.span import NoopSpan
 from skywalking.trace.tags import TagHttpMethod, TagHttpURL, TagHttpStatusCode
 
+link_vector = ["https://docs.python.org/3/library/http.server.html",
+               "https://werkzeug.palletsprojects.com/"]
+support_matrix = {
+    "http_server": {
+        ">=3.6": ["*"]
+    },
+    "werkzeug": {
+        ">=3.6": ["1.0.1", "2.0"]
+    }
+}
+note = """"""
+
 
 def install():
     from http.server import BaseHTTPRequestHandler
diff --git a/skywalking/plugins/sw_kafka.py b/skywalking/plugins/sw_kafka.py
index 1d101bd..f76daaf 100644
--- a/skywalking/plugins/sw_kafka.py
+++ b/skywalking/plugins/sw_kafka.py
@@ -21,6 +21,15 @@ from skywalking.trace.carrier import Carrier
 from skywalking.trace.context import get_context
 from skywalking.trace.tags import TagMqBroker, TagMqTopic
 
+link_vector = ["https://kafka-python.readthedocs.io"]
+
+support_matrix = {
+    "kafka-python": {
+        ">=3.6": ["2.0"]
+    }
+}
+note = """"""
+
 
 def install():
     from kafka import KafkaProducer
@@ -38,8 +47,8 @@ def _sw__poll_once_func(__poll_once):
         if res:
             brokers = ";".join(this.config["bootstrap_servers"])
             context = get_context()
-            topics = ";".join(this._subscription.subscription or
-                              [t.topic for t in this._subscription._user_assignment])
+            topics = ";".join(this._subscription.subscription
+                              or [t.topic for t in this._subscription._user_assignment])
 
             with context.new_entry_span(
                     op="Kafka/" + topics + "/Consumer/" + (this.config["group_id"] or "")) as span:
diff --git a/skywalking/plugins/sw_psycopg2.py b/skywalking/plugins/sw_psycopg.py
similarity index 86%
copy from skywalking/plugins/sw_psycopg2.py
copy to skywalking/plugins/sw_psycopg.py
index b8725b5..9af2ab5 100644
--- a/skywalking/plugins/sw_psycopg2.py
+++ b/skywalking/plugins/sw_psycopg.py
@@ -19,10 +19,18 @@ from skywalking import Layer, Component, config
 from skywalking.trace.context import get_context
 from skywalking.trace.tags import TagDbType, TagDbInstance, TagDbStatement, TagDbSqlParameters
 
+link_vector = ["https://www.psycopg.org/"]
+support_matrix = {
+    "psycopg[binary]": {
+        ">=3.6": ["3.0"]  # psycopg is psycopg3
+    }
+}
+note = """"""
+
 
 def install():
     import wrapt  # psycopg2 is read-only C extension objects so they need to be proxied
-    import psycopg2
+    import psycopg
 
     class ProxyCursor(wrapt.ObjectProxy):
         def __init__(self, cur):
@@ -34,8 +42,9 @@ def install():
             return ProxyCursor(wrapt.ObjectProxy.__enter__(self))
 
         def execute(self, query, vars=None):
-            dsn = self.connection.get_dsn_parameters()
-            peer = dsn['host'] + ':' + dsn['port']
+            dsn = self.connection.info.get_parameters()
+            port = self.connection.info.port
+            peer = dsn['host'] + ':' + str(port)
 
             with get_context().new_exit_span(op="PostgreSLQ/Psycopg/execute", peer=peer,
                                              component=Component.Psycopg) as span:
@@ -56,8 +65,9 @@ def install():
                 return self._self_cur.execute(query, vars)
 
         def executemany(self, query, vars_list):
-            dsn = self.connection.get_dsn_parameters()
-            peer = dsn['host'] + ':' + dsn['port']
+            dsn = self.connection.info.get_parameters()
+            port = self.connection.info.port
+            peer = dsn['host'] + ':' + str(port)
 
             with get_context().new_exit_span(op="PostgreSLQ/Psycopg/executemany", peer=peer,
                                              component=Component.Psycopg) as span:
@@ -88,8 +98,9 @@ def install():
                 return self._self_cur.executemany(query, vars_list)
 
         def callproc(self, procname, parameters=None):
-            dsn = self.connection.get_dsn_parameters()
-            peer = dsn['host'] + ':' + dsn['port']
+            dsn = self.connection.info.get_parameters()
+            port = self.connection.info.port
+            peer = dsn['host'] + ':' + str(port)
 
             with get_context().new_exit_span(op="PostgreSLQ/Psycopg/callproc", peer=peer,
                                              component=Component.Psycopg) as span:
@@ -117,5 +128,5 @@ def install():
     def connect(*args, **kwargs):
         return ProxyConnection(_connect(*args, **kwargs))
 
-    _connect = psycopg2.connect
-    psycopg2.connect = connect
+    _connect = psycopg.connect
+    psycopg.connect = connect
diff --git a/skywalking/plugins/sw_psycopg2.py b/skywalking/plugins/sw_psycopg2.py
index b8725b5..65cd780 100644
--- a/skywalking/plugins/sw_psycopg2.py
+++ b/skywalking/plugins/sw_psycopg2.py
@@ -19,6 +19,15 @@ from skywalking import Layer, Component, config
 from skywalking.trace.context import get_context
 from skywalking.trace.tags import TagDbType, TagDbInstance, TagDbStatement, TagDbSqlParameters
 
+link_vector = ["https://www.psycopg.org/"]
+support_matrix = {
+    "psycopg2-binary": {
+        ">=3.10": [],
+        ">=3.6": ["2.9"]  # transition to psycopg(3), not working for python 3.10
+    }
+}
+note = """"""
+
 
 def install():
     import wrapt  # psycopg2 is read-only C extension objects so they need to be proxied
diff --git a/skywalking/plugins/sw_pymongo.py b/skywalking/plugins/sw_pymongo.py
index 01abea7..b4779a7 100644
--- a/skywalking/plugins/sw_pymongo.py
+++ b/skywalking/plugins/sw_pymongo.py
@@ -19,10 +19,13 @@ from skywalking import Layer, Component, config
 from skywalking.trace.context import get_context
 from skywalking.trace.tags import TagDbType, TagDbInstance, TagDbStatement
 
-version_rule = {
-    "name": "pymongo",
-    "rules": [">=3.7.0"]
+link_vector = ["https://pymongo.readthedocs.io"]
+support_matrix = {
+    "pymongo": {
+        ">=3.6": ["3.11"]  # TODO: "3.12" incompatible with all python versions, need investigation
+    }
 }
+note = """"""
 
 
 def install():
@@ -31,10 +34,10 @@ def install():
     from pymongo.pool import SocketInfo
 
     bulk_op_map = {
-            0: "insert",
-            1: "update",
-            2: "delete"
-        }
+        0: "insert",
+        1: "update",
+        2: "delete"
+    }
     # handle insert_many and bulk write
     inject_bulk_write(_Bulk, bulk_op_map)
 
@@ -106,7 +109,7 @@ def inject_bulk_write(_Bulk, bulk_op_map):
         context = get_context()
 
         sw_op = "MixedBulkWriteOperation"
-        with context.new_exit_span(op="MongoDB/"+sw_op, peer=peer, component=Component.MongoDB) as span:
+        with context.new_exit_span(op="MongoDB/" + sw_op, peer=peer, component=Component.MongoDB) as span:
             span.layer = Layer.Database
 
             bulk_result = _execute(this, *args, **kwargs)
@@ -140,7 +143,7 @@ def inject_cursor(Cursor):
         context = get_context()
         op = "FindOperation"
 
-        with context.new_exit_span(op="MongoDB/"+op, peer=peer, component=Component.MongoDB) as span:
+        with context.new_exit_span(op="MongoDB/" + op, peer=peer, component=Component.MongoDB) as span:
             span.layer = Layer.Database
 
             # __send_message return nothing
diff --git a/skywalking/plugins/sw_pymysql.py b/skywalking/plugins/sw_pymysql.py
index ef84ee8..dc9266a 100644
--- a/skywalking/plugins/sw_pymysql.py
+++ b/skywalking/plugins/sw_pymysql.py
@@ -19,6 +19,14 @@ from skywalking import Layer, Component, config
 from skywalking.trace.context import get_context
 from skywalking.trace.tags import TagDbType, TagDbInstance, TagDbStatement, TagDbSqlParameters
 
+link_vector = ["https://pymysql.readthedocs.io/en/latest/"]
+support_matrix = {
+    "pymysql": {
+        ">=3.6": ["1.0"]
+    }
+}
+note = """"""
+
 
 def install():
     from pymysql.cursors import Cursor
diff --git a/skywalking/plugins/sw_pyramid.py b/skywalking/plugins/sw_pyramid.py
index d56f7dd..3d312ee 100644
--- a/skywalking/plugins/sw_pyramid.py
+++ b/skywalking/plugins/sw_pyramid.py
@@ -21,6 +21,14 @@ from skywalking.trace.context import get_context, NoopContext
 from skywalking.trace.span import NoopSpan
 from skywalking.trace.tags import TagHttpMethod, TagHttpURL, TagHttpStatusCode
 
+link_vector = ["https://trypyramid.com"]
+support_matrix = {
+    "pyramid": {
+        ">=3.6": ["1.10", "2.0"]
+    }
+}
+note = """"""
+
 
 def install():
     from pyramid.router import Router
diff --git a/skywalking/plugins/sw_rabbitmq.py b/skywalking/plugins/sw_rabbitmq.py
index 87d0877..bfcd3d3 100644
--- a/skywalking/plugins/sw_rabbitmq.py
+++ b/skywalking/plugins/sw_rabbitmq.py
@@ -20,6 +20,14 @@ from skywalking.trace.carrier import Carrier
 from skywalking.trace.context import get_context
 from skywalking.trace.tags import TagMqBroker, TagMqTopic, TagMqQueue
 
+link_vector = ["https://pika.readthedocs.io"]
+support_matrix = {
+    "pika": {
+        ">=3.6": ["1.2"]
+    }
+}
+note = """"""
+
 
 def install():
     from pika.channel import Channel
diff --git a/skywalking/plugins/sw_redis.py b/skywalking/plugins/sw_redis.py
index b02cce3..d885578 100644
--- a/skywalking/plugins/sw_redis.py
+++ b/skywalking/plugins/sw_redis.py
@@ -19,6 +19,14 @@ from skywalking import Layer, Component
 from skywalking.trace.context import get_context
 from skywalking.trace.tags import TagDbType, TagDbInstance, TagDbStatement
 
+link_vector = ["https://github.com/andymccurdy/redis-py/"]
+support_matrix = {
+    "redis": {
+        ">=3.6": ["3.5"]  # "4.0" next, incompatible to current instrumentation
+    }
+}
+note = """"""
+
 
 def install():
     from redis.connection import Connection
@@ -29,7 +37,7 @@ def install():
         peer = "%s:%s" % (this.host, this.port)
         op = args[0]
         context = get_context()
-        with context.new_exit_span(op="Redis/"+op or "/", peer=peer, component=Component.Redis) as span:
+        with context.new_exit_span(op="Redis/" + op or "/", peer=peer, component=Component.Redis) as span:
             span.layer = Layer.Cache
 
             res = _send_command(this, *args, **kwargs)
diff --git a/skywalking/plugins/sw_requests.py b/skywalking/plugins/sw_requests.py
index d44c837..4b2f94b 100644
--- a/skywalking/plugins/sw_requests.py
+++ b/skywalking/plugins/sw_requests.py
@@ -20,6 +20,14 @@ from skywalking.trace.context import get_context, NoopContext
 from skywalking.trace.span import NoopSpan
 from skywalking.trace.tags import TagHttpMethod, TagHttpURL, TagHttpStatusCode
 
+link_vector = ["https://requests.readthedocs.io/en/master/"]
+support_matrix = {
+    "requests": {
+        ">=3.6": ["2.26", "2.25"]
+    }
+}
+note = """"""
+
 
 def install():
     from requests import Session
diff --git a/skywalking/plugins/sw_sanic.py b/skywalking/plugins/sw_sanic.py
index 7b64670..dc7421e 100644
--- a/skywalking/plugins/sw_sanic.py
+++ b/skywalking/plugins/sw_sanic.py
@@ -24,20 +24,31 @@ from skywalking.trace.tags import TagHttpMethod, TagHttpURL, TagHttpStatusCode,
 
 logger = logging.getLogger(__name__)
 
-version_rule = {
-    "name": "sanic",
-    "rules": [">=20.3.0", "<21.0.0"]
+# version_rule = {
+#     "name": "sanic",
+#     "rules": [">=20.3.0 <21.0.0"]
+# }
+
+link_vector = ["https://sanic.readthedocs.io/en/latest"]
+support_matrix = {
+    "sanic": {
+        ">=3.10": [],  # not supporting any version yet
+        ">=3.7": ["20.12"],  # 21.9 Future LTS - Not supported by SW yet
+        ">=3.6": ["20.12"]  # 20.12 last LTS for python 3.6
+    }  # TODO: add instrumentation for 21.9 (method signature change) remove - write_callback, stream_callback
 }
+note = """"""
 
 
 def install():
     from sanic import Sanic, handlers, response
+    # TODO: format_http1_response is removed from response in later versions.
 
     _format_http1_response = response.format_http1_response
     _handle_request = Sanic.handle_request
-    _handlers_ErrorHandler_reponse = handlers.ErrorHandler.response
+    _handlers_ErrorHandler_response = handlers.ErrorHandler.response
 
-    def _sw_format_http1_reponse(status: int, headers, body=b""):
+    def _sw_format_http1_response(status: int, headers, body=b""):
         if status is not None:
             entry_span = get_context().active_span()
             if entry_span is not None and type(entry_span) is not NoopSpan:
@@ -47,17 +58,17 @@ def install():
 
         return _format_http1_response(status, headers, body)
 
-    def _sw_handlers_ErrorHandler_reponse(self: handlers.ErrorHandler, req, e):
+    def _sw_handlers_ErrorHandler_response(self: handlers.ErrorHandler, req, e):
         if e is not None:
             entry_span = get_context().active_span()
             if entry_span is not None and type(entry_span) is not NoopSpan:
                 entry_span.raised()
 
-        return _handlers_ErrorHandler_reponse(self, req, e)
+        return _handlers_ErrorHandler_response(self, req, e)
 
-    response.format_http1_response = _sw_format_http1_reponse
+    response.format_http1_response = _sw_format_http1_response
     Sanic.handle_request = _gen_sw_handle_request(_handle_request)
-    handlers.ErrorHandler.response = _sw_handlers_ErrorHandler_reponse
+    handlers.ErrorHandler.response = _sw_handlers_ErrorHandler_response
 
 
 def _gen_sw_handle_request(_handle_request):
@@ -91,4 +102,5 @@ def _gen_sw_handle_request(_handle_request):
                 result = await resp
 
         return result
+
     return _sw_handle_request
diff --git a/skywalking/plugins/sw_tornado.py b/skywalking/plugins/sw_tornado.py
index 0abdc35..4803e94 100644
--- a/skywalking/plugins/sw_tornado.py
+++ b/skywalking/plugins/sw_tornado.py
@@ -23,10 +23,17 @@ from skywalking.trace.context import get_context, NoopContext
 from skywalking.trace.span import NoopSpan
 from skywalking.trace.tags import TagHttpMethod, TagHttpURL, TagHttpStatusCode
 
-version_rule = {
-    "name": "tornado",
-    "rules": [">=5.0"]
+# version_rule = {
+#     "name": "tornado",
+#     "rules": [">=5.0"]
+# }
+link_vector = ["https://www.tornadoweb.org"]
+support_matrix = {
+    "tornado": {
+        ">=3.6": ["6.0", "6.1"]
+    }
 }
+note = """"""
 
 
 def install():
diff --git a/skywalking/plugins/sw_urllib3.py b/skywalking/plugins/sw_urllib3.py
index b7a94c4..7bfb20b 100644
--- a/skywalking/plugins/sw_urllib3.py
+++ b/skywalking/plugins/sw_urllib3.py
@@ -20,6 +20,14 @@ from skywalking.trace.context import get_context, NoopContext
 from skywalking.trace.span import NoopSpan
 from skywalking.trace.tags import TagHttpMethod, TagHttpURL, TagHttpStatusCode
 
+link_vector = ["https://urllib3.readthedocs.io/en/latest/"]
+support_matrix = {
+    "urllib3": {
+        ">=3.6": ["1.26", "1.25"]
+    }
+}
+note = """"""
+
 
 def install():
     from urllib3.request import RequestMethods
diff --git a/skywalking/plugins/sw_urllib_request.py b/skywalking/plugins/sw_urllib_request.py
index 5a084b6..ff9b81c 100644
--- a/skywalking/plugins/sw_urllib_request.py
+++ b/skywalking/plugins/sw_urllib_request.py
@@ -22,6 +22,14 @@ from skywalking.trace.context import get_context, NoopContext
 from skywalking.trace.span import NoopSpan
 from skywalking.trace.tags import TagHttpMethod, TagHttpURL, TagHttpStatusCode
 
+link_vector = ["https://docs.python.org/3/library/urllib.request.html"]
+support_matrix = {
+    "urllib_request": {
+        ">=3.6": ["*"]
+    }
+}
+note = """"""
+
 
 def install():
     import socket
diff --git a/skywalking/trace/carrier.py b/skywalking/trace/carrier.py
index fff0a24..b52f27d 100644
--- a/skywalking/trace/carrier.py
+++ b/skywalking/trace/carrier.py
@@ -23,7 +23,7 @@ from skywalking.utils.lang import b64encode, b64decode
 
 class CarrierItem(object):
     def __init__(self, key: str = '', val: str = ''):
-        self.key = config.agent_namespace + "-" + key if config.agent_namespace else key   # type: str
+        self.key = config.agent_namespace + "-" + key if config.agent_namespace else key  # type: str
         self.val = val  # type: str
 
     @property
diff --git a/tests/plugin/pytest.ini b/skywalking/utils/comparator.py
similarity index 76%
copy from tests/plugin/pytest.ini
copy to skywalking/utils/comparator.py
index 82a1b14..af5cf68 100644
--- a/tests/plugin/pytest.ini
+++ b/skywalking/utils/comparator.py
@@ -15,6 +15,11 @@
 # limitations under the License.
 #
 
-[pytest]
-usefixtures = docker_compose
-addopts = -x
+operators = {
+    '<': lambda cv, ev: cv < ev,
+    '<=': lambda cv, ev: cv < ev or cv == ev,
+    '==': lambda cv, ev: cv == ev,
+    '>=': lambda cv, ev: cv > ev or cv == ev,
+    '>': lambda cv, ev: cv > ev,
+    '!=': lambda cv, ev: cv != ev
+}
diff --git a/tests/plugin/pytest.ini b/skywalking/utils/exception.py
similarity index 88%
copy from tests/plugin/pytest.ini
copy to skywalking/utils/exception.py
index 82a1b14..5eb5d0d 100644
--- a/tests/plugin/pytest.ini
+++ b/skywalking/utils/exception.py
@@ -15,6 +15,6 @@
 # limitations under the License.
 #
 
-[pytest]
-usefixtures = docker_compose
-addopts = -x
+class VersionRuleException(Exception):
+    def __init__(self, message):
+        self.message = message
diff --git a/tests/orchestrator.py b/tests/orchestrator.py
new file mode 100644
index 0000000..39848bb
--- /dev/null
+++ b/tests/orchestrator.py
@@ -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.
+#
+
+"""
+A management utility to handle testing matrix for different Pythons and Library versions
+"""
+import sys
+
+from skywalking.utils.comparator import operators
+from skywalking.utils.exception import VersionRuleException
+
+
+def compare_version(rule_unit):
+    idx = 2 if rule_unit[1] == '=' else 1
+    symbol = rule_unit[0:idx]
+    expect_python_version = tuple(map(int, rule_unit[idx:].split('.')))
+    test_python_version = sys.version_info[:2]  # type: tuple
+    f = operators.get(symbol) or None
+    if not f:
+        raise VersionRuleException("version rule {} error. only allow >,>=,==,<=,<,!= symbols".format(rule_unit))
+
+    return f(test_python_version, expect_python_version)
+
+
+def get_test_vector(lib_name: str, support_matrix: dict):
+    """
+    If gets empty or ! will get skipped
+    Args:
+        support_matrix: a test matrix including python version specification and lib version
+        lib_name: the name of the tested lib, used for requirements.txt generation
+
+    Returns:
+
+    """
+    test_matrix = support_matrix[lib_name]
+    for py_version in test_matrix:
+        if compare_version(py_version):
+            # proceed if current python version is valid
+            version_row = test_matrix[py_version]
+            return [f"{lib_name}=={idx}" for idx in version_row]
+    return []  # non-match, CI will skip the test case for this version
+
+
+if __name__ == '__main__':
+    import pytest
+
+    pytest.main(['-v', '../tests/'])
diff --git a/tests/plugin/base.py b/tests/plugin/base.py
index 8682389..5817b02 100644
--- a/tests/plugin/base.py
+++ b/tests/plugin/base.py
@@ -18,7 +18,6 @@
 import inspect
 import os
 import sys
-import time
 from abc import ABC
 from difflib import Differ
 from os.path import dirname
@@ -40,15 +39,15 @@ class TestPluginBase(ABC):
         if expected_file_name is None:
             expected_file_name = os.path.join(dirname(inspect.getfile(self.__class__)), 'expected.data.yml')
 
-        time.sleep(10)
+        # time.sleep(10)
 
         with open(expected_file_name) as expected_data_file:
             expected_data = os.linesep.join(expected_data_file.readlines())
 
-            response = requests.post(url='http://0.0.0.0:12800/dataValidate', data=expected_data)
+            response = requests.post(url='http://localhost:12800/dataValidate', data=expected_data)
 
             if response.status_code != 200:
-                res = requests.get('http://0.0.0.0:12800/receiveData')
+                res = requests.get('http://localhost:12800/receiveData')
 
                 actual_data = yaml.dump(yaml.load(res.content, Loader=Loader))
 
@@ -61,7 +60,6 @@ class TestPluginBase(ABC):
                 print('diff list: ')
 
                 sys.stdout.writelines(diff_list)
-
             assert response.status_code == 200
 
             return response
diff --git a/tests/plugin/conftest.py b/tests/plugin/conftest.py
index 66c74c7..da1300a 100644
--- a/tests/plugin/conftest.py
+++ b/tests/plugin/conftest.py
@@ -64,6 +64,7 @@ def docker_compose(request, prepare, version):
             time.sleep(10)
             exception = e
     if exception:
+        time.sleep(100)
         compose.stop()
         raise Exception("""Wait time exceeded {0} sec. Exception {1}""".format(100, exception))
 
diff --git a/tests/plugin/docker/docker-compose.base.yml b/tests/plugin/docker/docker-compose.base.yml
index ce44b0b..44f39a9 100644
--- a/tests/plugin/docker/docker-compose.base.yml
+++ b/tests/plugin/docker/docker-compose.base.yml
@@ -19,7 +19,7 @@ version: '2.1'
 
 services:
   collector:
-    image: ghcr.io/apache/skywalking-agent-test-tool/mock-collector:2ed17bb73eb993cc7d4effa7a5a36dd91a9ec8cd
+    image: ghcr.io/apache/skywalking-agent-test-tool/mock-collector:5acb890f225ca37ee60675ce3e330545e23e3cbc
     ports:
       - 19876:19876
       - 12800:12800
diff --git a/tests/plugin/pytest.ini b/tests/plugin/pytest.ini
index 82a1b14..2cdcd56 100644
--- a/tests/plugin/pytest.ini
+++ b/tests/plugin/pytest.ini
@@ -17,4 +17,5 @@
 
 [pytest]
 usefixtures = docker_compose
-addopts = -x
+#addopts = -x
+empty_parameter_set_mark = skip
diff --git a/tests/plugin/sw_aiohttp/expected.data.yml b/tests/plugin/sw_aiohttp/expected.data.yml
index b90dec9..60e2e76 100644
--- a/tests/plugin/sw_aiohttp/expected.data.yml
+++ b/tests/plugin/sw_aiohttp/expected.data.yml
@@ -22,7 +22,6 @@ segmentItems:
       - segmentId: not null
         spans:
           - operationName: /skywalking
-            operationId: 0
             parentSpanId: -1
             spanId: 0
             spanLayer: Http
@@ -54,7 +53,6 @@ segmentItems:
       - segmentId: not null
         spans:
           - operationName: /skywalking
-            operationId: 0
             parentSpanId: 0
             spanId: 1
             spanLayer: Http
@@ -72,7 +70,6 @@ segmentItems:
             peer: provider:9091
             skipAnalysis: false
           - operationName: /skywalking
-            operationId: 0
             parentSpanId: -1
             spanId: 0
             spanLayer: Http
diff --git a/tests/plugin/sw_aiohttp/test_aiohttp.py b/tests/plugin/sw_aiohttp/test_aiohttp.py
index b0c9c49..1e72a89 100644
--- a/tests/plugin/sw_aiohttp/test_aiohttp.py
+++ b/tests/plugin/sw_aiohttp/test_aiohttp.py
@@ -19,6 +19,8 @@ from typing import Callable
 import pytest
 import requests
 
+from skywalking.plugins.sw_aiohttp import support_matrix
+from tests.orchestrator import get_test_vector
 from tests.plugin.base import TestPluginBase
 
 
@@ -29,8 +31,6 @@ def prepare():
 
 
 class TestPlugin(TestPluginBase):
-    @pytest.mark.parametrize('version', [
-        'aiohttp==3.7.3',
-    ])
+    @pytest.mark.parametrize('version', get_test_vector(lib_name='aiohttp', support_matrix=support_matrix))
     def test_plugin(self, docker_compose, version):
         self.validate()
diff --git a/tests/plugin/sw_django/expected.data.yml b/tests/plugin/sw_django/expected.data.yml
index 2c29a1b..9222ce0 100644
--- a/tests/plugin/sw_django/expected.data.yml
+++ b/tests/plugin/sw_django/expected.data.yml
@@ -22,7 +22,6 @@ segmentItems:
       - segmentId: not null
         spans:
           - operationName: /users
-            operationId: 0
             parentSpanId: -1
             spanId: 0
             spanLayer: Http
@@ -54,7 +53,6 @@ segmentItems:
       - segmentId: not null
         spans:
           - operationName: /users
-            operationId: 0
             parentSpanId: 0
             spanId: 1
             spanLayer: Http
@@ -72,7 +70,6 @@ segmentItems:
               - key: http.status.code
                 value: '200'
           - operationName: /users
-            operationId: 0
             parentSpanId: -1
             spanId: 0
             spanLayer: Http
diff --git a/tests/plugin/sw_django/test_django.py b/tests/plugin/sw_django/test_django.py
index b8a6515..0ef92a6 100644
--- a/tests/plugin/sw_django/test_django.py
+++ b/tests/plugin/sw_django/test_django.py
@@ -19,6 +19,8 @@ from typing import Callable
 import pytest
 import requests
 
+from skywalking.plugins.sw_django import support_matrix
+from tests.orchestrator import get_test_vector
 from tests.plugin.base import TestPluginBase
 
 
@@ -29,11 +31,6 @@ def prepare():
 
 
 class TestPlugin(TestPluginBase):
-    @pytest.mark.parametrize('version', [
-        'django==2.0',
-        'django==2.2',
-        'django==3.0',
-        'django==3.1',
-    ])
+    @pytest.mark.parametrize('version', get_test_vector(lib_name='django', support_matrix=support_matrix))
     def test_plugin(self, docker_compose, version):
         self.validate()
diff --git a/tests/plugin/sw_elasticsearch/expected.data.yml b/tests/plugin/sw_elasticsearch/expected.data.yml
index 60536c3..0e1bfd8 100644
--- a/tests/plugin/sw_elasticsearch/expected.data.yml
+++ b/tests/plugin/sw_elasticsearch/expected.data.yml
@@ -22,7 +22,6 @@ segmentItems:
       - segmentId: not null
         spans:
           - operationName: Elasticsearch/PUT/test
-            operationId: 0
             parentSpanId: 0
             spanId: 1
             spanLayer: Database
@@ -39,7 +38,6 @@ segmentItems:
               - key: db.statement
                 value: ''
           - operationName: Elasticsearch/PUT/test/test/1
-            operationId: 0
             parentSpanId: 0
             spanId: 2
             spanLayer: Database
@@ -56,7 +54,6 @@ segmentItems:
               - key: db.statement
                 value: '{''song'': ''Despacito'', ''artist'': ''Luis Fonsi''}'
           - operationName: Elasticsearch/GET/test/_doc/1
-            operationId: 0
             parentSpanId: 0
             spanId: 3
             spanLayer: Database
@@ -73,7 +70,6 @@ segmentItems:
               - key: db.statement
                 value: ''
           - operationName: /users
-            operationId: 0
             parentSpanId: -1
             spanId: 0
             spanLayer: Http
diff --git a/tests/plugin/sw_elasticsearch/test_elasticsearch.py b/tests/plugin/sw_elasticsearch/test_elasticsearch.py
index 85fe3f3..738f626 100644
--- a/tests/plugin/sw_elasticsearch/test_elasticsearch.py
+++ b/tests/plugin/sw_elasticsearch/test_elasticsearch.py
@@ -19,6 +19,8 @@ from typing import Callable
 import pytest
 import requests
 
+from skywalking.plugins.sw_elasticsearch import support_matrix
+from tests.orchestrator import get_test_vector
 from tests.plugin.base import TestPluginBase
 
 
@@ -29,8 +31,6 @@ def prepare():
 
 
 class TestPlugin(TestPluginBase):
-    @pytest.mark.parametrize('version', [
-        'elasticsearch==7.9.0',
-    ])
+    @pytest.mark.parametrize('version', get_test_vector(lib_name='elasticsearch', support_matrix=support_matrix))
     def test_plugin(self, docker_compose, version):
         self.validate()
diff --git a/tests/plugin/sw_falcon/expected.data.yml b/tests/plugin/sw_falcon/expected.data.yml
index b95f487..23f7440 100644
--- a/tests/plugin/sw_falcon/expected.data.yml
+++ b/tests/plugin/sw_falcon/expected.data.yml
@@ -22,7 +22,6 @@ segmentItems:
     - componentId: 7012
       endTime: gt 0
       isError: false
-      operationId: 0
       operationName: /users
       parentSpanId: -1
       peer: not null
@@ -48,7 +47,6 @@ segmentItems:
     - componentId: 7002
       endTime: gt 0
       isError: false
-      operationId: 0
       operationName: /users
       parentSpanId: 0
       peer: provider:9091
@@ -67,7 +65,6 @@ segmentItems:
     - componentId: 7012
       endTime: gt 0
       isError: false
-      operationId: 0
       operationName: /users
       parentSpanId: -1
       peer: not null
diff --git a/tests/plugin/sw_falcon/test_falcon.py b/tests/plugin/sw_falcon/test_falcon.py
index bb6dde8..17aa61f 100644
--- a/tests/plugin/sw_falcon/test_falcon.py
+++ b/tests/plugin/sw_falcon/test_falcon.py
@@ -20,6 +20,8 @@ from typing import Callable
 import pytest
 import requests
 
+from skywalking.plugins.sw_falcon import support_matrix
+from tests.orchestrator import get_test_vector
 from tests.plugin.base import TestPluginBase
 
 
@@ -30,10 +32,6 @@ def prepare():
 
 
 class TestPlugin(TestPluginBase):
-    @pytest.mark.parametrize('version', [
-        'hug==2.4.1',
-        'hug==2.5.0',
-        'hug==2.6.0',
-    ])
+    @pytest.mark.parametrize('version', get_test_vector(lib_name='hug', support_matrix=support_matrix))
     def test_plugin(self, docker_compose, version):
         self.validate()
diff --git a/tests/plugin/sw_flask/expected.data.yml b/tests/plugin/sw_flask/expected.data.yml
index 6629f69..6d4dcf9 100644
--- a/tests/plugin/sw_flask/expected.data.yml
+++ b/tests/plugin/sw_flask/expected.data.yml
@@ -22,7 +22,6 @@ segmentItems:
       - segmentId: not null
         spans:
           - operationName: /users
-            operationId: 0
             parentSpanId: -1
             spanId: 0
             spanLayer: Http
@@ -51,7 +50,6 @@ segmentItems:
       - segmentId: not null
         spans:
           - operationName: /users
-            operationId: 0
             parentSpanId: -1
             spanId: 0
             spanLayer: Http
@@ -83,7 +81,6 @@ segmentItems:
       - segmentId: not null
         spans:
           - operationName: /users
-            operationId: 0
             parentSpanId: 0
             spanId: 1
             spanLayer: Http
@@ -125,7 +122,6 @@ segmentItems:
       - segmentId: not null
         spans:
           - operationName: /users
-            operationId: 0
             parentSpanId: 0
             spanId: 1
             spanLayer: Http
diff --git a/tests/plugin/sw_flask/test_flask.py b/tests/plugin/sw_flask/test_flask.py
index 1f7c3b8..f252438 100644
--- a/tests/plugin/sw_flask/test_flask.py
+++ b/tests/plugin/sw_flask/test_flask.py
@@ -19,6 +19,8 @@ from typing import Callable
 import pytest
 import requests
 
+from skywalking.plugins.sw_flask import support_matrix
+from tests.orchestrator import get_test_vector
 from tests.plugin.base import TestPluginBase
 
 
@@ -29,10 +31,7 @@ def prepare():
 
 
 class TestPlugin(TestPluginBase):
-    @pytest.mark.parametrize('version', [
-        'flask==1.1.2',
-        'flask==1.0.4',
-    ])
+    @pytest.mark.parametrize('version', get_test_vector(lib_name='flask', support_matrix=support_matrix))
     def test_plugin(self, docker_compose, version):
         self.validate()
 
diff --git a/tests/plugin/sw_http/expected.data.yml b/tests/plugin/sw_http/expected.data.yml
index 66c2663..16adce8 100644
--- a/tests/plugin/sw_http/expected.data.yml
+++ b/tests/plugin/sw_http/expected.data.yml
@@ -22,7 +22,6 @@ segmentItems:
       - segmentId: not null
         spans:
           - operationName: /users
-            operationId: 0
             parentSpanId: -1
             spanId: 0
             spanLayer: Http
@@ -54,7 +53,6 @@ segmentItems:
       - segmentId: not null
         spans:
           - operationName: /users
-            operationId: 0
             parentSpanId: 0
             spanId: 1
             spanLayer: Http
diff --git a/tests/plugin/sw_http/test_http.py b/tests/plugin/sw_http/test_http.py
index d9317ba..cda9c6a 100644
--- a/tests/plugin/sw_http/test_http.py
+++ b/tests/plugin/sw_http/test_http.py
@@ -18,7 +18,6 @@ from typing import Callable
 
 import pytest
 import requests
-
 from tests.plugin.base import TestPluginBase
 
 
diff --git a/tests/plugin/sw_http_wsgi/expected.data.yml b/tests/plugin/sw_http_wsgi/expected.data.yml
index 66c2663..9ccfec5 100644
--- a/tests/plugin/sw_http_wsgi/expected.data.yml
+++ b/tests/plugin/sw_http_wsgi/expected.data.yml
@@ -54,7 +54,6 @@ segmentItems:
       - segmentId: not null
         spans:
           - operationName: /users
-            operationId: 0
             parentSpanId: 0
             spanId: 1
             spanLayer: Http
diff --git a/tests/plugin/sw_http_wsgi/test_http_wsgi.py b/tests/plugin/sw_http_wsgi/test_http_wsgi.py
index 15e0f69..4a2a1f1 100644
--- a/tests/plugin/sw_http_wsgi/test_http_wsgi.py
+++ b/tests/plugin/sw_http_wsgi/test_http_wsgi.py
@@ -19,6 +19,8 @@ from typing import Callable
 import pytest
 import requests
 
+from skywalking.plugins.sw_http_server import support_matrix
+from tests.orchestrator import get_test_vector
 from tests.plugin.base import TestPluginBase
 
 
@@ -29,8 +31,6 @@ def prepare():
 
 
 class TestPlugin(TestPluginBase):
-    @pytest.mark.parametrize('version', [
-        'werkzeug==1.0.1',
-    ])
+    @pytest.mark.parametrize('version', get_test_vector(lib_name='werkzeug', support_matrix=support_matrix))
     def test_plugin(self, docker_compose, version):
         self.validate()
diff --git a/tests/plugin/sw_kafka/expected.data.yml b/tests/plugin/sw_kafka/expected.data.yml
index e2efcf1..837c390 100644
--- a/tests/plugin/sw_kafka/expected.data.yml
+++ b/tests/plugin/sw_kafka/expected.data.yml
@@ -22,7 +22,6 @@ segmentItems:
       - segmentId: not null
         spans:
           - operationName: Kafka/skywalking/Producer
-            operationId: 0
             parentSpanId: 0
             spanId: 1
             spanLayer: MQ
diff --git a/tests/plugin/sw_kafka/test_kafka.py b/tests/plugin/sw_kafka/test_kafka.py
index 6f981a2..a383c5a 100644
--- a/tests/plugin/sw_kafka/test_kafka.py
+++ b/tests/plugin/sw_kafka/test_kafka.py
@@ -19,6 +19,8 @@ from typing import Callable
 import pytest
 import requests
 
+from skywalking.plugins.sw_kafka import support_matrix
+from tests.orchestrator import get_test_vector
 from tests.plugin.base import TestPluginBase
 
 
@@ -29,8 +31,6 @@ def prepare():
 
 
 class TestPlugin(TestPluginBase):
-    @pytest.mark.parametrize('version', [
-        'kafka-python==2.0.1',
-    ])
+    @pytest.mark.parametrize('version', get_test_vector(lib_name='kafka-python', support_matrix=support_matrix))
     def test_plugin(self, docker_compose, version):
         self.validate()
diff --git a/tests/plugin/pytest.ini b/tests/plugin/sw_psycopg/__init__.py
similarity index 93%
copy from tests/plugin/pytest.ini
copy to tests/plugin/sw_psycopg/__init__.py
index 82a1b14..b1312a0 100644
--- a/tests/plugin/pytest.ini
+++ b/tests/plugin/sw_psycopg/__init__.py
@@ -14,7 +14,3 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 #
-
-[pytest]
-usefixtures = docker_compose
-addopts = -x
diff --git a/tests/plugin/sw_sanic/docker-compose.yml b/tests/plugin/sw_psycopg/docker-compose.yml
similarity index 70%
copy from tests/plugin/sw_sanic/docker-compose.yml
copy to tests/plugin/sw_psycopg/docker-compose.yml
index 773f937..3dcb485 100644
--- a/tests/plugin/sw_sanic/docker-compose.yml
+++ b/tests/plugin/sw_psycopg/docker-compose.yml
@@ -23,6 +23,24 @@ services:
       service: collector
       file: ../docker/docker-compose.base.yml
 
+  postgres:
+    image: postgres:11
+    hostname: postgres
+    ports:
+      - "5432:5444"
+    environment:
+      - POSTGRES_USER=root
+      - POSTGRES_PASSWORD=root
+      - POSTGRES_DB=admin
+      - POSTGRES_PORT=5444
+    healthcheck:
+      test: ["CMD", "bash", "-c", "cat < /dev/null > /dev/tcp/127.0.0.1/5432"]
+      interval: 5s
+      timeout: 60s
+      retries: 120
+    networks:
+      - beyond
+
   provider:
     extends:
       service: agent
@@ -31,10 +49,12 @@ services:
       - 9091:9091
     volumes:
       - .:/app
-    command: ['bash', '-c', 'pip install sanic && pip install -r /app/requirements.txt && python3 /app/services/provider.py']
+    command: ['bash', '-c', 'pip install flask && pip install -r /app/requirements.txt && sw-python run python3 /app/services/provider.py']
     depends_on:
       collector:
         condition: service_healthy
+      postgres:
+        condition: service_healthy
     healthcheck:
       test: ["CMD", "bash", "-c", "cat < /dev/null > /dev/tcp/127.0.0.1/9091"]
       interval: 5s
@@ -43,6 +63,8 @@ services:
     environment:
       SW_AGENT_NAME: provider
       SW_AGENT_LOGGING_LEVEL: DEBUG
+      SW_SQL_PARAMETERS_LENGTH: 512
+
   consumer:
     extends:
       service: agent
@@ -51,7 +73,7 @@ services:
       - 9090:9090
     volumes:
       - .:/app
-    command: ['bash', '-c', 'pip install sanic && pip install -r /app/requirements.txt && python3 /app/services/consumer.py']
+    command: ['bash', '-c', 'pip install flask && sw-python run python3 /app/services/consumer.py']
     depends_on:
       collector:
         condition: service_healthy
@@ -60,6 +82,6 @@ services:
     environment:
       SW_AGENT_NAME: consumer
       SW_AGENT_LOGGING_LEVEL: DEBUG
-      SW_SANIC_COLLECT_HTTP_PARAMS: 'True'
+
 networks:
   beyond:
diff --git a/tests/plugin/sw_psycopg2/expected.data.yml b/tests/plugin/sw_psycopg/expected.data.yml
similarity index 96%
copy from tests/plugin/sw_psycopg2/expected.data.yml
copy to tests/plugin/sw_psycopg/expected.data.yml
index c8119dc..2544a3b 100644
--- a/tests/plugin/sw_psycopg2/expected.data.yml
+++ b/tests/plugin/sw_psycopg/expected.data.yml
@@ -22,7 +22,6 @@ segmentItems:
       - segmentId: not null
         spans:
           - operationName: PostgreSLQ/Psycopg/execute
-            operationId: 0
             parentSpanId: 0
             spanId: 1
             spanLayer: Database
@@ -43,7 +42,6 @@ segmentItems:
             peer: postgres:5432
             skipAnalysis: false
           - operationName: /users
-            operationId: 0
             parentSpanId: -1
             spanId: 0
             spanLayer: Http
@@ -75,7 +73,6 @@ segmentItems:
       - segmentId: not null
         spans:
           - operationName: /users
-            operationId: 0
             parentSpanId: 0
             spanId: 1
             spanLayer: Http
@@ -94,7 +91,6 @@ segmentItems:
             peer: provider:9091
             skipAnalysis: false
           - operationName: /users
-            operationId: 0
             parentSpanId: -1
             spanId: 0
             spanLayer: Http
diff --git a/tests/plugin/pytest.ini b/tests/plugin/sw_psycopg/services/__init__.py
similarity index 93%
copy from tests/plugin/pytest.ini
copy to tests/plugin/sw_psycopg/services/__init__.py
index 82a1b14..b1312a0 100644
--- a/tests/plugin/pytest.ini
+++ b/tests/plugin/sw_psycopg/services/__init__.py
@@ -14,7 +14,3 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 #
-
-[pytest]
-usefixtures = docker_compose
-addopts = -x
diff --git a/tests/plugin/sw_http/test_http.py b/tests/plugin/sw_psycopg/services/consumer.py
similarity index 71%
copy from tests/plugin/sw_http/test_http.py
copy to tests/plugin/sw_psycopg/services/consumer.py
index d9317ba..ff935a4 100644
--- a/tests/plugin/sw_http/test_http.py
+++ b/tests/plugin/sw_psycopg/services/consumer.py
@@ -14,20 +14,19 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 #
-from typing import Callable
 
-import pytest
 import requests
 
-from tests.plugin.base import TestPluginBase
 
+if __name__ == '__main__':
+    from flask import Flask, jsonify
 
-@pytest.fixture
-def prepare():
-    # type: () -> Callable
-    return lambda *_: requests.post('http://0.0.0.0:9090')
+    app = Flask(__name__)
 
+    @app.route("/users", methods=["POST", "GET"])
+    def application():
+        res = requests.post("http://provider:9091/users")
+        return jsonify(res.json())
 
-class TestPlugin(TestPluginBase):
-    def test_plugin(self, docker_compose, version):
-        self.validate()
+    PORT = 9090
+    app.run(host='0.0.0.0', port=PORT, debug=True)
diff --git a/tests/plugin/sw_rabbitmq/services/producer.py b/tests/plugin/sw_psycopg/services/provider.py
similarity index 65%
copy from tests/plugin/sw_rabbitmq/services/producer.py
copy to tests/plugin/sw_psycopg/services/provider.py
index 4656346..c9d5ada 100644
--- a/tests/plugin/sw_rabbitmq/services/producer.py
+++ b/tests/plugin/sw_psycopg/services/provider.py
@@ -15,27 +15,26 @@
 # limitations under the License.
 #
 
+import time
+
 
 if __name__ == '__main__':
     from flask import Flask, jsonify
+    import psycopg
+
     app = Flask(__name__)
-    import pika
-    parameters = (pika.URLParameters("amqp://admin:admin@rabbitmq-server:5672/%2F"))
 
     @app.route("/users", methods=["POST", "GET"])
     def application():
-        connection = pika.BlockingConnection(parameters)
-        channel = connection.channel()
-        channel.queue_declare("test")
-        channel.exchange_declare("test")
-        channel.queue_bind(exchange='test', queue="test", routing_key='test')
-        channel.basic_publish(exchange='test', routing_key='test',  properties=pika.BasicProperties(
-            headers={'key': 'value'}
-        ),
-                              body=b'Test message.')
+        time.sleep(0.5)
+        connection = psycopg.connect(host='postgres', user='root', password='root', dbname='admin', port=5432)
+        with connection.cursor() as cursor:
+            sql = "select * from user where user = %s"
+            cursor.execute(sql, ("root",))
+
         connection.close()
 
         return jsonify({"song": "Despacito", "artist": "Luis Fonsi"})
 
-    PORT = 9090
+    PORT = 9091
     app.run(host='0.0.0.0', port=PORT, debug=True)
diff --git a/tests/plugin/sw_elasticsearch/test_elasticsearch.py b/tests/plugin/sw_psycopg/test_psycopg.py
similarity index 83%
copy from tests/plugin/sw_elasticsearch/test_elasticsearch.py
copy to tests/plugin/sw_psycopg/test_psycopg.py
index 85fe3f3..9b293c1 100644
--- a/tests/plugin/sw_elasticsearch/test_elasticsearch.py
+++ b/tests/plugin/sw_psycopg/test_psycopg.py
@@ -18,7 +18,8 @@ from typing import Callable
 
 import pytest
 import requests
-
+from skywalking.plugins.sw_psycopg import support_matrix
+from tests.orchestrator import get_test_vector
 from tests.plugin.base import TestPluginBase
 
 
@@ -29,8 +30,6 @@ def prepare():
 
 
 class TestPlugin(TestPluginBase):
-    @pytest.mark.parametrize('version', [
-        'elasticsearch==7.9.0',
-    ])
+    @pytest.mark.parametrize('version', get_test_vector(lib_name='psycopg[binary]', support_matrix=support_matrix))
     def test_plugin(self, docker_compose, version):
         self.validate()
diff --git a/tests/plugin/sw_psycopg2/expected.data.yml b/tests/plugin/sw_psycopg2/expected.data.yml
index c8119dc..c5a91a1 100644
--- a/tests/plugin/sw_psycopg2/expected.data.yml
+++ b/tests/plugin/sw_psycopg2/expected.data.yml
@@ -22,7 +22,6 @@ segmentItems:
       - segmentId: not null
         spans:
           - operationName: PostgreSLQ/Psycopg/execute
-            operationId: 0
             parentSpanId: 0
             spanId: 1
             spanLayer: Database
@@ -75,7 +74,6 @@ segmentItems:
       - segmentId: not null
         spans:
           - operationName: /users
-            operationId: 0
             parentSpanId: 0
             spanId: 1
             spanLayer: Http
diff --git a/tests/plugin/sw_psycopg2/test_psycopg2.py b/tests/plugin/sw_psycopg2/test_psycopg2.py
index a21cb72..d6f954d 100644
--- a/tests/plugin/sw_psycopg2/test_psycopg2.py
+++ b/tests/plugin/sw_psycopg2/test_psycopg2.py
@@ -19,6 +19,8 @@ from typing import Callable
 import pytest
 import requests
 
+from skywalking.plugins.sw_psycopg2 import support_matrix
+from tests.orchestrator import get_test_vector
 from tests.plugin.base import TestPluginBase
 
 
@@ -29,8 +31,6 @@ def prepare():
 
 
 class TestPlugin(TestPluginBase):
-    @pytest.mark.parametrize('version', [
-        'psycopg2-binary==2.8.6',
-    ])
+    @pytest.mark.parametrize('version', get_test_vector(lib_name='psycopg2-binary', support_matrix=support_matrix))
     def test_plugin(self, docker_compose, version):
         self.validate()
diff --git a/tests/plugin/sw_pymongo/expected.data.yml b/tests/plugin/sw_pymongo/expected.data.yml
index d445249..6afd548 100644
--- a/tests/plugin/sw_pymongo/expected.data.yml
+++ b/tests/plugin/sw_pymongo/expected.data.yml
@@ -22,7 +22,6 @@ segmentItems:
       - segmentId: not null
         spans:
           - operationName: MongoDB/MixedBulkWriteOperation
-            operationId: 0
             parentSpanId: 0
             spanId: 1
             spanLayer: Database
@@ -69,7 +68,6 @@ segmentItems:
       - segmentId: not null
         spans:
           - operationName: MongoDB/FindOperation
-            operationId: 0
             parentSpanId: 0
             spanId: 1
             spanLayer: Database
@@ -116,7 +114,6 @@ segmentItems:
       - segmentId: not null
         spans:
           - operationName: MongoDB/DeleteOperation
-            operationId: 0
             parentSpanId: 0
             spanId: 1
             spanLayer: Database
@@ -166,7 +163,6 @@ segmentItems:
       - segmentId: not null
         spans:
           - operationName: /insert_many
-            operationId: 0
             parentSpanId: 0
             spanId: 1
             spanLayer: Http
@@ -184,7 +180,6 @@ segmentItems:
               - key: http.status.code
                 value: '200'
           - operationName: /find_one
-            operationId: 0
             parentSpanId: 0
             spanId: 2
             spanLayer: Http
@@ -202,7 +197,6 @@ segmentItems:
               - key: http.status.code
                 value: '200'
           - operationName: /delete_one
-            operationId: 0
             parentSpanId: 0
             spanId: 3
             spanLayer: Http
diff --git a/tests/plugin/sw_pymongo/test_pymongo.py b/tests/plugin/sw_pymongo/test_pymongo.py
index 61f382e..3b2b81a 100644
--- a/tests/plugin/sw_pymongo/test_pymongo.py
+++ b/tests/plugin/sw_pymongo/test_pymongo.py
@@ -19,6 +19,8 @@ from typing import Callable
 import pytest
 import requests
 
+from skywalking.plugins.sw_pymongo import support_matrix
+from tests.orchestrator import get_test_vector
 from tests.plugin.base import TestPluginBase
 
 
@@ -29,8 +31,6 @@ def prepare():
 
 
 class TestPlugin(TestPluginBase):
-    @pytest.mark.parametrize('version', [
-        'pymongo==3.11.0',
-    ])
+    @pytest.mark.parametrize('version', get_test_vector(lib_name='pymongo', support_matrix=support_matrix))
     def test_plugin(self, docker_compose, version):
         self.validate()
diff --git a/tests/plugin/sw_pymysql/expected.data.yml b/tests/plugin/sw_pymysql/expected.data.yml
index a13b5a4..901f8eb 100644
--- a/tests/plugin/sw_pymysql/expected.data.yml
+++ b/tests/plugin/sw_pymysql/expected.data.yml
@@ -22,7 +22,6 @@ segmentItems:
       - segmentId: not null
         spans:
           - operationName: Mysql/PyMsql/execute
-            operationId: 0
             parentSpanId: 0
             spanId: 1
             spanLayer: Database
@@ -74,7 +73,6 @@ segmentItems:
       - segmentId: not null
         spans:
           - operationName: /users
-            operationId: 0
             parentSpanId: 0
             spanId: 1
             spanLayer: Http
diff --git a/tests/plugin/sw_pymysql/test_pymysql.py b/tests/plugin/sw_pymysql/test_pymysql.py
index c4af0a1..12ca166 100644
--- a/tests/plugin/sw_pymysql/test_pymysql.py
+++ b/tests/plugin/sw_pymysql/test_pymysql.py
@@ -19,6 +19,8 @@ from typing import Callable
 import pytest
 import requests
 
+from skywalking.plugins.sw_pymysql import support_matrix
+from tests.orchestrator import get_test_vector
 from tests.plugin.base import TestPluginBase
 
 
@@ -29,8 +31,6 @@ def prepare():
 
 
 class TestPlugin(TestPluginBase):
-    @pytest.mark.parametrize('version', [
-        'PyMySQL==0.10.0',
-    ])
+    @pytest.mark.parametrize('version', get_test_vector(lib_name='pymysql', support_matrix=support_matrix))
     def test_plugin(self, docker_compose, version):
         self.validate()
diff --git a/tests/plugin/sw_pyramid/expected.data.yml b/tests/plugin/sw_pyramid/expected.data.yml
index e946688..b68b0ed 100644
--- a/tests/plugin/sw_pyramid/expected.data.yml
+++ b/tests/plugin/sw_pyramid/expected.data.yml
@@ -54,7 +54,6 @@ segmentItems:
       - segmentId: not null
         spans:
           - operationName: /pyramid
-            operationId: 0
             parentSpanId: 0
             spanId: 1
             spanLayer: Http
diff --git a/tests/plugin/sw_pyramid/test_plugin.py b/tests/plugin/sw_pyramid/test_pyramid.py
similarity index 84%
rename from tests/plugin/sw_pyramid/test_plugin.py
rename to tests/plugin/sw_pyramid/test_pyramid.py
index 6aa822f..95b1d9f 100644
--- a/tests/plugin/sw_pyramid/test_plugin.py
+++ b/tests/plugin/sw_pyramid/test_pyramid.py
@@ -19,6 +19,8 @@ from typing import Callable
 import pytest
 import requests
 
+from skywalking.plugins.sw_pyramid import support_matrix
+from tests.orchestrator import get_test_vector
 from tests.plugin.base import TestPluginBase
 
 
@@ -29,9 +31,6 @@ def prepare():
 
 
 class TestPlugin(TestPluginBase):
-    @pytest.mark.parametrize('version', [
-        'pyramid==1.10',
-        'pyramid==1.9',
-    ])
+    @pytest.mark.parametrize('version', get_test_vector(lib_name='pyramid', support_matrix=support_matrix))
     def test_plugin(self, docker_compose, version):
         self.validate()
diff --git a/tests/plugin/sw_rabbitmq/expected.data.yml b/tests/plugin/sw_rabbitmq/expected.data.yml
index 926026b..e5e9a79 100644
--- a/tests/plugin/sw_rabbitmq/expected.data.yml
+++ b/tests/plugin/sw_rabbitmq/expected.data.yml
@@ -22,7 +22,6 @@ segmentItems:
       - segmentId: not null
         spans:
           - operationName: RabbitMQ/Topic/test/Queue/test/Producer
-            operationId: 0
             parentSpanId: 0
             spanId: 1
             spanLayer: MQ
diff --git a/tests/plugin/sw_rabbitmq/services/producer.py b/tests/plugin/sw_rabbitmq/services/producer.py
index 4656346..8c90c31 100644
--- a/tests/plugin/sw_rabbitmq/services/producer.py
+++ b/tests/plugin/sw_rabbitmq/services/producer.py
@@ -29,10 +29,10 @@ if __name__ == '__main__':
         channel.queue_declare("test")
         channel.exchange_declare("test")
         channel.queue_bind(exchange='test', queue="test", routing_key='test')
-        channel.basic_publish(exchange='test', routing_key='test',  properties=pika.BasicProperties(
-            headers={'key': 'value'}
-        ),
-                              body=b'Test message.')
+        channel.basic_publish(
+            exchange='test', routing_key='test',
+            properties=pika.BasicProperties(headers={'key': 'value'}),
+            body=b'Test message.')
         connection.close()
 
         return jsonify({"song": "Despacito", "artist": "Luis Fonsi"})
diff --git a/tests/plugin/sw_rabbitmq/test_rabbitmq.py b/tests/plugin/sw_rabbitmq/test_rabbitmq.py
index 24086c9..e9ab381 100644
--- a/tests/plugin/sw_rabbitmq/test_rabbitmq.py
+++ b/tests/plugin/sw_rabbitmq/test_rabbitmq.py
@@ -19,6 +19,8 @@ from typing import Callable
 import pytest
 import requests
 
+from skywalking.plugins.sw_rabbitmq import support_matrix
+from tests.orchestrator import get_test_vector
 from tests.plugin.base import TestPluginBase
 
 
@@ -29,8 +31,6 @@ def prepare():
 
 
 class TestPlugin(TestPluginBase):
-    @pytest.mark.parametrize('version', [
-        'pika==1.1.0',
-    ])
+    @pytest.mark.parametrize('version', get_test_vector(lib_name='pika', support_matrix=support_matrix))
     def test_plugin(self, docker_compose, version):
         self.validate()
diff --git a/tests/plugin/sw_redis/expected.data.yml b/tests/plugin/sw_redis/expected.data.yml
index f4dbcb3..cbf57ad 100644
--- a/tests/plugin/sw_redis/expected.data.yml
+++ b/tests/plugin/sw_redis/expected.data.yml
@@ -22,7 +22,6 @@ segmentItems:
       - segmentId: not null
         spans:
           - operationName: Redis/SET
-            operationId: 0
             parentSpanId: 0
             spanId: 1
             spanLayer: Cache
@@ -40,7 +39,6 @@ segmentItems:
             peer: redis:6379
             skipAnalysis: false
           - operationName: Redis/GET
-            operationId: 0
             parentSpanId: 0
             spanId: 2
             spanLayer: Cache
@@ -90,7 +88,6 @@ segmentItems:
       - segmentId: not null
         spans:
           - operationName: /users
-            operationId: 0
             parentSpanId: 0
             spanId: 1
             spanLayer: Http
diff --git a/tests/plugin/sw_redis/test_redis.py b/tests/plugin/sw_redis/test_redis.py
index 3d240b5..a3ca1b9 100644
--- a/tests/plugin/sw_redis/test_redis.py
+++ b/tests/plugin/sw_redis/test_redis.py
@@ -19,6 +19,8 @@ from typing import Callable
 import pytest
 import requests
 
+from skywalking.plugins.sw_redis import support_matrix
+from tests.orchestrator import get_test_vector
 from tests.plugin.base import TestPluginBase
 
 
@@ -29,8 +31,6 @@ def prepare():
 
 
 class TestPlugin(TestPluginBase):
-    @pytest.mark.parametrize('version', [
-        'redis==3.5.3',
-    ])
+    @pytest.mark.parametrize('version', get_test_vector(lib_name='redis', support_matrix=support_matrix))
     def test_plugin(self, docker_compose, version):
         self.validate()
diff --git a/tests/plugin/sw_requests/expected.data.yml b/tests/plugin/sw_requests/expected.data.yml
index c0fde99..5cee8ed 100644
--- a/tests/plugin/sw_requests/expected.data.yml
+++ b/tests/plugin/sw_requests/expected.data.yml
@@ -54,7 +54,6 @@ segmentItems:
       - segmentId: not null
         spans:
           - operationName: /users
-            operationId: 0
             parentSpanId: 0
             spanId: 1
             spanLayer: Http
diff --git a/tests/plugin/sw_requests/test_request.py b/tests/plugin/sw_requests/test_request.py
index 001f1f3..8108326 100644
--- a/tests/plugin/sw_requests/test_request.py
+++ b/tests/plugin/sw_requests/test_request.py
@@ -19,6 +19,8 @@ from typing import Callable
 import pytest
 import requests
 
+from skywalking.plugins.sw_requests import support_matrix
+from tests.orchestrator import get_test_vector
 from tests.plugin.base import TestPluginBase
 
 
@@ -29,12 +31,6 @@ def prepare():
 
 
 class TestPlugin(TestPluginBase):
-    @pytest.mark.parametrize('version', [
-        'requests==2.24.0',
-        'requests==2.20.0',
-        'requests==2.19.0',
-        'requests==2.13.0',
-        'requests==2.9.0',
-    ])
+    @pytest.mark.parametrize('version', get_test_vector(lib_name='requests', support_matrix=support_matrix))
     def test_plugin(self, docker_compose, version):
         self.validate()
diff --git a/tests/plugin/sw_sanic/docker-compose.yml b/tests/plugin/sw_sanic/docker-compose.yml
index 773f937..0c13afd 100644
--- a/tests/plugin/sw_sanic/docker-compose.yml
+++ b/tests/plugin/sw_sanic/docker-compose.yml
@@ -31,7 +31,7 @@ services:
       - 9091:9091
     volumes:
       - .:/app
-    command: ['bash', '-c', 'pip install sanic && pip install -r /app/requirements.txt && python3 /app/services/provider.py']
+    command: ['bash', '-c', 'pip install sanic && pip install -r /app/requirements.txt && sw-python run python3 /app/services/provider.py']
     depends_on:
       collector:
         condition: service_healthy
@@ -51,7 +51,7 @@ services:
       - 9090:9090
     volumes:
       - .:/app
-    command: ['bash', '-c', 'pip install sanic && pip install -r /app/requirements.txt && python3 /app/services/consumer.py']
+    command: ['bash', '-c', 'pip install sanic && pip install -r /app/requirements.txt && sw-python run python3 /app/services/consumer.py']
     depends_on:
       collector:
         condition: service_healthy
diff --git a/tests/plugin/sw_sanic/expected.data.yml b/tests/plugin/sw_sanic/expected.data.yml
index c5f66c1..3c4ca4c 100644
--- a/tests/plugin/sw_sanic/expected.data.yml
+++ b/tests/plugin/sw_sanic/expected.data.yml
@@ -54,7 +54,6 @@ segmentItems:
       - segmentId: not null
         spans:
           - operationName: /users
-            operationId: 0
             parentSpanId: 0
             spanId: 1
             spanLayer: Http
diff --git a/tests/plugin/sw_sanic/services/consumer.py b/tests/plugin/sw_sanic/services/consumer.py
index 49a7301..2cdb6a8 100644
--- a/tests/plugin/sw_sanic/services/consumer.py
+++ b/tests/plugin/sw_sanic/services/consumer.py
@@ -17,14 +17,7 @@
 
 import requests
 
-from skywalking import agent, config
-
 if __name__ == '__main__':
-    config.service_name = 'consumer'
-    config.logging_level = 'DEBUG'
-    config.sanic_collect_http_params = True
-    agent.start()
-
     from sanic import Sanic, response
 
     app = Sanic(__name__)
diff --git a/tests/plugin/sw_sanic/services/provider.py b/tests/plugin/sw_sanic/services/provider.py
index 18b24a9..c22baba 100644
--- a/tests/plugin/sw_sanic/services/provider.py
+++ b/tests/plugin/sw_sanic/services/provider.py
@@ -17,13 +17,7 @@
 
 import time
 
-from skywalking import agent, config
-
 if __name__ == '__main__':
-    config.service_name = 'provider'
-    config.logging_level = 'DEBUG'
-    agent.start()
-
     from sanic import Sanic, response
 
     app = Sanic(__name__)
@@ -31,7 +25,9 @@ if __name__ == '__main__':
     @app.route("/users", methods=["GET"])
     async def application(req):
         time.sleep(0.5)
-        return response.json({"song": "Despacito", "artist": "Luis Fonsi"})
+        return response.json(
+            {"song": "Despacito", "artist": "Luis Fonsi"}
+        )
 
     PORT = 9091
     app.run(host='0.0.0.0', port=PORT, debug=True)
diff --git a/tests/plugin/sw_sanic/test_sanic.py b/tests/plugin/sw_sanic/test_sanic.py
index a14cdee..3413f4a 100644
--- a/tests/plugin/sw_sanic/test_sanic.py
+++ b/tests/plugin/sw_sanic/test_sanic.py
@@ -19,6 +19,8 @@ from typing import Callable
 import pytest
 import requests
 
+from skywalking.plugins.sw_sanic import support_matrix
+from tests.orchestrator import get_test_vector
 from tests.plugin.base import TestPluginBase
 
 
@@ -29,12 +31,6 @@ def prepare():
 
 
 class TestPlugin(TestPluginBase):
-    @pytest.mark.parametrize('version', [
-        # 'sanic==20.3.0',
-        'sanic==20.6.0',
-        'sanic==20.9.0',
-        'sanic==20.9.1',
-        'sanic==20.12.3'
-    ])
+    @pytest.mark.parametrize('version', get_test_vector(lib_name='sanic', support_matrix=support_matrix))
     def test_plugin(self, docker_compose, version):
         self.validate()
diff --git a/tests/plugin/sw_tornado/expected.data.yml b/tests/plugin/sw_tornado/expected.data.yml
index f5d980d..c2bb860 100644
--- a/tests/plugin/sw_tornado/expected.data.yml
+++ b/tests/plugin/sw_tornado/expected.data.yml
@@ -54,7 +54,6 @@ segmentItems:
       - segmentId: not null
         spans:
           - operationName: /users
-            operationId: 0
             parentSpanId: 0
             spanId: 1
             spanLayer: Http
diff --git a/tests/plugin/sw_tornado/test_tornado.py b/tests/plugin/sw_tornado/test_tornado.py
index b7828eb..15693af 100644
--- a/tests/plugin/sw_tornado/test_tornado.py
+++ b/tests/plugin/sw_tornado/test_tornado.py
@@ -19,6 +19,8 @@ from typing import Callable
 import pytest
 import requests
 
+from skywalking.plugins.sw_tornado import support_matrix
+from tests.orchestrator import get_test_vector
 from tests.plugin.base import TestPluginBase
 
 
@@ -29,9 +31,6 @@ def prepare():
 
 
 class TestPlugin(TestPluginBase):
-    @pytest.mark.parametrize('version', [
-        'tornado==6.0.4',
-        'tornado==5.1.1',
-    ])
+    @pytest.mark.parametrize('version', get_test_vector(lib_name='tornado', support_matrix=support_matrix))
     def test_plugin(self, docker_compose, version):
         self.validate()
diff --git a/tests/plugin/sw_urllib3/expected.data.yml b/tests/plugin/sw_urllib3/expected.data.yml
index 161b020..d67762e 100644
--- a/tests/plugin/sw_urllib3/expected.data.yml
+++ b/tests/plugin/sw_urllib3/expected.data.yml
@@ -55,7 +55,6 @@ segmentItems:
       - segmentId: not null
         spans:
           - operationName: /users
-            operationId: 0
             parentSpanId: 0
             spanId: 1
             spanLayer: Http
diff --git a/tests/plugin/sw_urllib3/test_urllib3.py b/tests/plugin/sw_urllib3/test_urllib3.py
index e56a82b..9316d1e 100644
--- a/tests/plugin/sw_urllib3/test_urllib3.py
+++ b/tests/plugin/sw_urllib3/test_urllib3.py
@@ -19,6 +19,8 @@ from typing import Callable
 import pytest
 import requests
 
+from skywalking.plugins.sw_urllib3 import support_matrix
+from tests.orchestrator import get_test_vector
 from tests.plugin.base import TestPluginBase
 
 
@@ -29,9 +31,6 @@ def prepare():
 
 
 class TestPlugin(TestPluginBase):
-    @pytest.mark.parametrize('version', [
-        'urllib3==1.25.10',
-        'urllib3==1.25.9'
-    ])
+    @pytest.mark.parametrize('version', get_test_vector(lib_name='urllib3', support_matrix=support_matrix))
     def test_plugin(self, docker_compose, version):
         self.validate()
diff --git a/tests/test_version_check.py b/tests/test_version_check.py
index 371b14d..81032ff 100644
--- a/tests/test_version_check.py
+++ b/tests/test_version_check.py
@@ -18,14 +18,14 @@
 import unittest
 
 from packaging import version
-
-from skywalking.plugins import _operators, check
+from skywalking.plugins import check
+from skywalking.utils.comparator import operators
 
 
 class TestVersionCheck(unittest.TestCase):
     def test_operators(self):
         # <
-        f = _operators.get("<")
+        f = operators.get("<")
         v1 = version.parse("1.0.0")
         v2 = version.parse("1.0.1")
         self.assertTrue(f(v1, v2))
@@ -35,7 +35,7 @@ class TestVersionCheck(unittest.TestCase):
         self.assertFalse(f(v1, v2))
 
         # <=
-        f = _operators.get("<=")
+        f = operators.get("<=")
         v1 = version.parse("1.0")
         v2 = version.parse("1.0")
         self.assertTrue(v1, v2)
@@ -45,7 +45,7 @@ class TestVersionCheck(unittest.TestCase):
         self.assertFalse(f(v2, v1))
 
         # =
-        f = _operators.get("==")
+        f = operators.get("==")
         v1 = version.parse("1.0.0")
         v2 = version.parse("1.0.0")
         self.assertTrue(f(v1, v2))
@@ -54,7 +54,7 @@ class TestVersionCheck(unittest.TestCase):
         self.assertFalse(f(v1, v2))
 
         # >=
-        f = _operators.get(">=")
+        f = operators.get(">=")
         v1 = version.parse("1.0.0")
         v2 = version.parse("1.0.0")
         self.assertTrue(f(v1, v2))
@@ -64,7 +64,7 @@ class TestVersionCheck(unittest.TestCase):
         self.assertTrue(f(v2, v1))
 
         # >
-        f = _operators.get(">")
+        f = operators.get(">")
         v1 = version.parse("1.0.0")
         v2 = version.parse("1.0.1")
         self.assertFalse(f(v1, v2))
@@ -74,7 +74,7 @@ class TestVersionCheck(unittest.TestCase):
         self.assertFalse(f(v1, v2))
 
         # !=
-        f = _operators.get("!=")
+        f = operators.get("!=")
         v1 = version.parse("1.0.0")
         v2 = version.parse("1.0.1")
         self.assertTrue(f(v1, v2))
diff --git a/tools/doc/plugin_doc_gen.py b/tools/doc/plugin_doc_gen.py
new file mode 100644
index 0000000..62f14a8
--- /dev/null
+++ b/tools/doc/plugin_doc_gen.py
@@ -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.
+#
+
+"""
+A tool to generate test matrix report for SkyWalking Python Plugins
+"""
+import pkgutil
+
+from skywalking.plugins import __path__ as plugins_path
+
+doc_head = """# Supported Libraries
+This document is **automatically** generated from the SkyWalking Python testing matrix.
+
+The column of versions only indicates the set of library versions tested in a best-effort manner.
+
+If you find newer major versions that are missing from the following table, and it's not documented as a limitation,
+please PR to update the test matrix in the plugin.
+
+Versions marked as NOT SUPPORTED may be due to
+an incompatible version with Python in the original library
+or a limitation of SkyWalking auto-instrumentation (welcome to contribute!)
+
+"""
+table_head = """### Plugin Support Table
+Library | Python Version - Lib Version | Plugin Name
+| :--- | :--- | :--- |
+"""
+
+
+def generate_plugin_doc():
+    """
+    Generates a test matrix table to the current dir
+
+    Returns: None
+
+    """
+    table_entries = []
+    note_entries = []
+    for importer, modname, ispkg in pkgutil.iter_modules(plugins_path):
+        plugin = importer.find_module(modname).load_module(modname)
+
+        try:
+            plugin_support_matrix = plugin.support_matrix  # type: dict
+            plugin_support_links = plugin.link_vector  # type: list
+            libs_tested = list(plugin_support_matrix.keys())
+            links_tested = plugin_support_links  # type: list
+            if plugin.note:
+                note_entries.append(plugin.note)
+        except AttributeError:
+            raise AttributeError(f"Missing attribute in {modname}, please follow the correct plugin style.")
+
+        for lib, link in zip(libs_tested, links_tested):  # NOTE: maybe a two lib support like http.server + werkzeug
+            lib_entry = str(lib)
+            lib_link = link
+            version_vector = plugin_support_matrix[lib_entry]  # type: dict
+            pretty_vector = ""
+            for python_version in version_vector:  # e.g. {'>=3.10': ['2.5', '2.6'], '>=3.6': ['2.4.1', '2.5', '2.6']}
+                lib_versions = version_vector[python_version]
+                pretty_vector += f"Python {python_version} " \
+                                 f"- {str(lib_versions) if lib_versions else 'NOT SUPPORTED YET'}; "
+            table_entry = f"| [{lib_entry}]({lib_link}) | {pretty_vector} | `{modname}` |"
+            table_entries.append(table_entry)
+
+    with open("../../docs/en/setup/Plugins.md", "w") as plugin_doc:
+        plugin_doc.write(doc_head)
+
+        plugin_doc.write(table_head)
+        for table_entry in table_entries:
+            plugin_doc.write(f"{table_entry}\n")
+
+        plugin_doc.write("### Notes\n")
+        for note_entry in note_entries:
+            plugin_doc.write(f"- {note_entry}\n")
+
+
+if __name__ == "__main__":
+    generate_plugin_doc()