Add asyncclick instrumentation (#3319)
* Add asyncclick instrumentation * Add instrumentation for asyncclick based CLI apps * Add tox * Add Changelog * Update workflows This implementation is based on the original click instrumentation work by: - Emídio Neto <9735060+emdneto@users.noreply.github.com> - Anuraag (Rag) Agrawal <anuraaga@gmail.com> - Riccardo Magliocchetti <riccardo.magliocchetti@gmail.com> * Update CHANGELOG.md Co-authored-by: Emídio Neto <9735060+emdneto@users.noreply.github.com> * Fix async refs in code-block example --------- Co-authored-by: Emídio Neto <9735060+emdneto@users.noreply.github.com>
This commit is contained in:
parent
50ab047143
commit
0ea9998c4c
|
|
@ -11,6 +11,9 @@ components:
|
|||
- oxeye-nikolay
|
||||
- nikosokolik
|
||||
|
||||
instrumentation/opentelemetry-instrumentation-asyncclick:
|
||||
- jomcgi
|
||||
|
||||
instrumentation/opentelemetry-instrumentation-kafka-python:
|
||||
- nozik
|
||||
|
||||
|
|
|
|||
|
|
@ -525,6 +525,28 @@ jobs:
|
|||
- name: Run tests
|
||||
run: tox -e py38-test-instrumentation-boto -- -ra
|
||||
|
||||
py38-test-instrumentation-asyncclick:
|
||||
name: instrumentation-asyncclick
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout contrib repo @ SHA - ${{ env.CONTRIB_REPO_SHA }}
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: open-telemetry/opentelemetry-python-contrib
|
||||
ref: ${{ env.CONTRIB_REPO_SHA }}
|
||||
|
||||
- name: Set up Python 3.8
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.8"
|
||||
architecture: "x64"
|
||||
|
||||
- name: Install tox
|
||||
run: pip install tox-uv
|
||||
|
||||
- name: Run tests
|
||||
run: tox -e py38-test-instrumentation-asyncclick -- -ra
|
||||
|
||||
py38-test-instrumentation-click:
|
||||
name: instrumentation-click
|
||||
runs-on: ubuntu-latest
|
||||
|
|
|
|||
|
|
@ -322,6 +322,24 @@ jobs:
|
|||
- name: Run tests
|
||||
run: tox -e lint-instrumentation-boto
|
||||
|
||||
lint-instrumentation-asyncclick:
|
||||
name: instrumentation-asyncclick
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repo @ SHA - ${{ github.sha }}
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Python 3.13
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.13"
|
||||
|
||||
- name: Install tox
|
||||
run: pip install tox-uv
|
||||
|
||||
- name: Run tests
|
||||
run: tox -e lint-instrumentation-asyncclick
|
||||
|
||||
lint-instrumentation-click:
|
||||
name: instrumentation-click
|
||||
runs-on: ubuntu-latest
|
||||
|
|
|
|||
|
|
@ -2842,6 +2842,132 @@ jobs:
|
|||
- name: Run tests
|
||||
run: tox -e py311-test-instrumentation-boto -- -ra
|
||||
|
||||
py38-test-instrumentation-asyncclick_ubuntu-latest:
|
||||
name: instrumentation-asyncclick 3.8 Ubuntu
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repo @ SHA - ${{ github.sha }}
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Python 3.8
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.8"
|
||||
|
||||
- name: Install tox
|
||||
run: pip install tox-uv
|
||||
|
||||
- name: Run tests
|
||||
run: tox -e py38-test-instrumentation-asyncclick -- -ra
|
||||
|
||||
py39-test-instrumentation-asyncclick_ubuntu-latest:
|
||||
name: instrumentation-asyncclick 3.9 Ubuntu
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repo @ SHA - ${{ github.sha }}
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Python 3.9
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.9"
|
||||
|
||||
- name: Install tox
|
||||
run: pip install tox-uv
|
||||
|
||||
- name: Run tests
|
||||
run: tox -e py39-test-instrumentation-asyncclick -- -ra
|
||||
|
||||
py310-test-instrumentation-asyncclick_ubuntu-latest:
|
||||
name: instrumentation-asyncclick 3.10 Ubuntu
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repo @ SHA - ${{ github.sha }}
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Python 3.10
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.10"
|
||||
|
||||
- name: Install tox
|
||||
run: pip install tox-uv
|
||||
|
||||
- name: Run tests
|
||||
run: tox -e py310-test-instrumentation-asyncclick -- -ra
|
||||
|
||||
py311-test-instrumentation-asyncclick_ubuntu-latest:
|
||||
name: instrumentation-asyncclick 3.11 Ubuntu
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repo @ SHA - ${{ github.sha }}
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Python 3.11
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.11"
|
||||
|
||||
- name: Install tox
|
||||
run: pip install tox-uv
|
||||
|
||||
- name: Run tests
|
||||
run: tox -e py311-test-instrumentation-asyncclick -- -ra
|
||||
|
||||
py312-test-instrumentation-asyncclick_ubuntu-latest:
|
||||
name: instrumentation-asyncclick 3.12 Ubuntu
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repo @ SHA - ${{ github.sha }}
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Python 3.12
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.12"
|
||||
|
||||
- name: Install tox
|
||||
run: pip install tox-uv
|
||||
|
||||
- name: Run tests
|
||||
run: tox -e py312-test-instrumentation-asyncclick -- -ra
|
||||
|
||||
py313-test-instrumentation-asyncclick_ubuntu-latest:
|
||||
name: instrumentation-asyncclick 3.13 Ubuntu
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repo @ SHA - ${{ github.sha }}
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Python 3.13
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.13"
|
||||
|
||||
- name: Install tox
|
||||
run: pip install tox-uv
|
||||
|
||||
- name: Run tests
|
||||
run: tox -e py313-test-instrumentation-asyncclick -- -ra
|
||||
|
||||
pypy3-test-instrumentation-asyncclick_ubuntu-latest:
|
||||
name: instrumentation-asyncclick pypy-3.8 Ubuntu
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repo @ SHA - ${{ github.sha }}
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Python pypy-3.8
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "pypy-3.8"
|
||||
|
||||
- name: Install tox
|
||||
run: pip install tox-uv
|
||||
|
||||
- name: Run tests
|
||||
run: tox -e pypy3-test-instrumentation-asyncclick -- -ra
|
||||
|
||||
py38-test-instrumentation-click_ubuntu-latest:
|
||||
name: instrumentation-click 3.8 Ubuntu
|
||||
runs-on: ubuntu-latest
|
||||
|
|
@ -4389,129 +4515,3 @@ jobs:
|
|||
|
||||
- name: Run tests
|
||||
run: tox -e py312-test-instrumentation-urllib -- -ra
|
||||
|
||||
py313-test-instrumentation-urllib_ubuntu-latest:
|
||||
name: instrumentation-urllib 3.13 Ubuntu
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repo @ SHA - ${{ github.sha }}
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Python 3.13
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.13"
|
||||
|
||||
- name: Install tox
|
||||
run: pip install tox-uv
|
||||
|
||||
- name: Run tests
|
||||
run: tox -e py313-test-instrumentation-urllib -- -ra
|
||||
|
||||
pypy3-test-instrumentation-urllib_ubuntu-latest:
|
||||
name: instrumentation-urllib pypy-3.8 Ubuntu
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repo @ SHA - ${{ github.sha }}
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Python pypy-3.8
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "pypy-3.8"
|
||||
|
||||
- name: Install tox
|
||||
run: pip install tox-uv
|
||||
|
||||
- name: Run tests
|
||||
run: tox -e pypy3-test-instrumentation-urllib -- -ra
|
||||
|
||||
py38-test-instrumentation-urllib3-0_ubuntu-latest:
|
||||
name: instrumentation-urllib3-0 3.8 Ubuntu
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repo @ SHA - ${{ github.sha }}
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Python 3.8
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.8"
|
||||
|
||||
- name: Install tox
|
||||
run: pip install tox-uv
|
||||
|
||||
- name: Run tests
|
||||
run: tox -e py38-test-instrumentation-urllib3-0 -- -ra
|
||||
|
||||
py38-test-instrumentation-urllib3-1_ubuntu-latest:
|
||||
name: instrumentation-urllib3-1 3.8 Ubuntu
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repo @ SHA - ${{ github.sha }}
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Python 3.8
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.8"
|
||||
|
||||
- name: Install tox
|
||||
run: pip install tox-uv
|
||||
|
||||
- name: Run tests
|
||||
run: tox -e py38-test-instrumentation-urllib3-1 -- -ra
|
||||
|
||||
py39-test-instrumentation-urllib3-0_ubuntu-latest:
|
||||
name: instrumentation-urllib3-0 3.9 Ubuntu
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repo @ SHA - ${{ github.sha }}
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Python 3.9
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.9"
|
||||
|
||||
- name: Install tox
|
||||
run: pip install tox-uv
|
||||
|
||||
- name: Run tests
|
||||
run: tox -e py39-test-instrumentation-urllib3-0 -- -ra
|
||||
|
||||
py39-test-instrumentation-urllib3-1_ubuntu-latest:
|
||||
name: instrumentation-urllib3-1 3.9 Ubuntu
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repo @ SHA - ${{ github.sha }}
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Python 3.9
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.9"
|
||||
|
||||
- name: Install tox
|
||||
run: pip install tox-uv
|
||||
|
||||
- name: Run tests
|
||||
run: tox -e py39-test-instrumentation-urllib3-1 -- -ra
|
||||
|
||||
py310-test-instrumentation-urllib3-0_ubuntu-latest:
|
||||
name: instrumentation-urllib3-0 3.10 Ubuntu
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repo @ SHA - ${{ github.sha }}
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Python 3.10
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.10"
|
||||
|
||||
- name: Install tox
|
||||
run: pip install tox-uv
|
||||
|
||||
- name: Run tests
|
||||
run: tox -e py310-test-instrumentation-urllib3-0 -- -ra
|
||||
|
|
|
|||
|
|
@ -16,6 +16,132 @@ env:
|
|||
|
||||
jobs:
|
||||
|
||||
py313-test-instrumentation-urllib_ubuntu-latest:
|
||||
name: instrumentation-urllib 3.13 Ubuntu
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repo @ SHA - ${{ github.sha }}
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Python 3.13
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.13"
|
||||
|
||||
- name: Install tox
|
||||
run: pip install tox-uv
|
||||
|
||||
- name: Run tests
|
||||
run: tox -e py313-test-instrumentation-urllib -- -ra
|
||||
|
||||
pypy3-test-instrumentation-urllib_ubuntu-latest:
|
||||
name: instrumentation-urllib pypy-3.8 Ubuntu
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repo @ SHA - ${{ github.sha }}
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Python pypy-3.8
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "pypy-3.8"
|
||||
|
||||
- name: Install tox
|
||||
run: pip install tox-uv
|
||||
|
||||
- name: Run tests
|
||||
run: tox -e pypy3-test-instrumentation-urllib -- -ra
|
||||
|
||||
py38-test-instrumentation-urllib3-0_ubuntu-latest:
|
||||
name: instrumentation-urllib3-0 3.8 Ubuntu
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repo @ SHA - ${{ github.sha }}
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Python 3.8
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.8"
|
||||
|
||||
- name: Install tox
|
||||
run: pip install tox-uv
|
||||
|
||||
- name: Run tests
|
||||
run: tox -e py38-test-instrumentation-urllib3-0 -- -ra
|
||||
|
||||
py38-test-instrumentation-urllib3-1_ubuntu-latest:
|
||||
name: instrumentation-urllib3-1 3.8 Ubuntu
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repo @ SHA - ${{ github.sha }}
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Python 3.8
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.8"
|
||||
|
||||
- name: Install tox
|
||||
run: pip install tox-uv
|
||||
|
||||
- name: Run tests
|
||||
run: tox -e py38-test-instrumentation-urllib3-1 -- -ra
|
||||
|
||||
py39-test-instrumentation-urllib3-0_ubuntu-latest:
|
||||
name: instrumentation-urllib3-0 3.9 Ubuntu
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repo @ SHA - ${{ github.sha }}
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Python 3.9
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.9"
|
||||
|
||||
- name: Install tox
|
||||
run: pip install tox-uv
|
||||
|
||||
- name: Run tests
|
||||
run: tox -e py39-test-instrumentation-urllib3-0 -- -ra
|
||||
|
||||
py39-test-instrumentation-urllib3-1_ubuntu-latest:
|
||||
name: instrumentation-urllib3-1 3.9 Ubuntu
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repo @ SHA - ${{ github.sha }}
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Python 3.9
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.9"
|
||||
|
||||
- name: Install tox
|
||||
run: pip install tox-uv
|
||||
|
||||
- name: Run tests
|
||||
run: tox -e py39-test-instrumentation-urllib3-1 -- -ra
|
||||
|
||||
py310-test-instrumentation-urllib3-0_ubuntu-latest:
|
||||
name: instrumentation-urllib3-0 3.10 Ubuntu
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repo @ SHA - ${{ github.sha }}
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Python 3.10
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.10"
|
||||
|
||||
- name: Install tox
|
||||
run: pip install tox-uv
|
||||
|
||||
- name: Run tests
|
||||
run: tox -e py310-test-instrumentation-urllib3-0 -- -ra
|
||||
|
||||
py310-test-instrumentation-urllib3-1_ubuntu-latest:
|
||||
name: instrumentation-urllib3-1 3.10 Ubuntu
|
||||
runs-on: ubuntu-latest
|
||||
|
|
@ -4389,129 +4515,3 @@ jobs:
|
|||
|
||||
- name: Run tests
|
||||
run: tox -e py312-test-instrumentation-threading -- -ra
|
||||
|
||||
py313-test-instrumentation-threading_ubuntu-latest:
|
||||
name: instrumentation-threading 3.13 Ubuntu
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repo @ SHA - ${{ github.sha }}
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Python 3.13
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.13"
|
||||
|
||||
- name: Install tox
|
||||
run: pip install tox-uv
|
||||
|
||||
- name: Run tests
|
||||
run: tox -e py313-test-instrumentation-threading -- -ra
|
||||
|
||||
pypy3-test-instrumentation-threading_ubuntu-latest:
|
||||
name: instrumentation-threading pypy-3.8 Ubuntu
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repo @ SHA - ${{ github.sha }}
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Python pypy-3.8
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "pypy-3.8"
|
||||
|
||||
- name: Install tox
|
||||
run: pip install tox-uv
|
||||
|
||||
- name: Run tests
|
||||
run: tox -e pypy3-test-instrumentation-threading -- -ra
|
||||
|
||||
py38-test-instrumentation-tornado_ubuntu-latest:
|
||||
name: instrumentation-tornado 3.8 Ubuntu
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repo @ SHA - ${{ github.sha }}
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Python 3.8
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.8"
|
||||
|
||||
- name: Install tox
|
||||
run: pip install tox-uv
|
||||
|
||||
- name: Run tests
|
||||
run: tox -e py38-test-instrumentation-tornado -- -ra
|
||||
|
||||
py39-test-instrumentation-tornado_ubuntu-latest:
|
||||
name: instrumentation-tornado 3.9 Ubuntu
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repo @ SHA - ${{ github.sha }}
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Python 3.9
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.9"
|
||||
|
||||
- name: Install tox
|
||||
run: pip install tox-uv
|
||||
|
||||
- name: Run tests
|
||||
run: tox -e py39-test-instrumentation-tornado -- -ra
|
||||
|
||||
py310-test-instrumentation-tornado_ubuntu-latest:
|
||||
name: instrumentation-tornado 3.10 Ubuntu
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repo @ SHA - ${{ github.sha }}
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Python 3.10
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.10"
|
||||
|
||||
- name: Install tox
|
||||
run: pip install tox-uv
|
||||
|
||||
- name: Run tests
|
||||
run: tox -e py310-test-instrumentation-tornado -- -ra
|
||||
|
||||
py311-test-instrumentation-tornado_ubuntu-latest:
|
||||
name: instrumentation-tornado 3.11 Ubuntu
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repo @ SHA - ${{ github.sha }}
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Python 3.11
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.11"
|
||||
|
||||
- name: Install tox
|
||||
run: pip install tox-uv
|
||||
|
||||
- name: Run tests
|
||||
run: tox -e py311-test-instrumentation-tornado -- -ra
|
||||
|
||||
py312-test-instrumentation-tornado_ubuntu-latest:
|
||||
name: instrumentation-tornado 3.12 Ubuntu
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repo @ SHA - ${{ github.sha }}
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Python 3.12
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.12"
|
||||
|
||||
- name: Install tox
|
||||
run: pip install tox-uv
|
||||
|
||||
- name: Run tests
|
||||
run: tox -e py312-test-instrumentation-tornado -- -ra
|
||||
|
|
|
|||
|
|
@ -16,6 +16,132 @@ env:
|
|||
|
||||
jobs:
|
||||
|
||||
py313-test-instrumentation-threading_ubuntu-latest:
|
||||
name: instrumentation-threading 3.13 Ubuntu
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repo @ SHA - ${{ github.sha }}
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Python 3.13
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.13"
|
||||
|
||||
- name: Install tox
|
||||
run: pip install tox-uv
|
||||
|
||||
- name: Run tests
|
||||
run: tox -e py313-test-instrumentation-threading -- -ra
|
||||
|
||||
pypy3-test-instrumentation-threading_ubuntu-latest:
|
||||
name: instrumentation-threading pypy-3.8 Ubuntu
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repo @ SHA - ${{ github.sha }}
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Python pypy-3.8
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "pypy-3.8"
|
||||
|
||||
- name: Install tox
|
||||
run: pip install tox-uv
|
||||
|
||||
- name: Run tests
|
||||
run: tox -e pypy3-test-instrumentation-threading -- -ra
|
||||
|
||||
py38-test-instrumentation-tornado_ubuntu-latest:
|
||||
name: instrumentation-tornado 3.8 Ubuntu
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repo @ SHA - ${{ github.sha }}
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Python 3.8
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.8"
|
||||
|
||||
- name: Install tox
|
||||
run: pip install tox-uv
|
||||
|
||||
- name: Run tests
|
||||
run: tox -e py38-test-instrumentation-tornado -- -ra
|
||||
|
||||
py39-test-instrumentation-tornado_ubuntu-latest:
|
||||
name: instrumentation-tornado 3.9 Ubuntu
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repo @ SHA - ${{ github.sha }}
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Python 3.9
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.9"
|
||||
|
||||
- name: Install tox
|
||||
run: pip install tox-uv
|
||||
|
||||
- name: Run tests
|
||||
run: tox -e py39-test-instrumentation-tornado -- -ra
|
||||
|
||||
py310-test-instrumentation-tornado_ubuntu-latest:
|
||||
name: instrumentation-tornado 3.10 Ubuntu
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repo @ SHA - ${{ github.sha }}
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Python 3.10
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.10"
|
||||
|
||||
- name: Install tox
|
||||
run: pip install tox-uv
|
||||
|
||||
- name: Run tests
|
||||
run: tox -e py310-test-instrumentation-tornado -- -ra
|
||||
|
||||
py311-test-instrumentation-tornado_ubuntu-latest:
|
||||
name: instrumentation-tornado 3.11 Ubuntu
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repo @ SHA - ${{ github.sha }}
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Python 3.11
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.11"
|
||||
|
||||
- name: Install tox
|
||||
run: pip install tox-uv
|
||||
|
||||
- name: Run tests
|
||||
run: tox -e py311-test-instrumentation-tornado -- -ra
|
||||
|
||||
py312-test-instrumentation-tornado_ubuntu-latest:
|
||||
name: instrumentation-tornado 3.12 Ubuntu
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repo @ SHA - ${{ github.sha }}
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Python 3.12
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.12"
|
||||
|
||||
- name: Install tox
|
||||
run: pip install tox-uv
|
||||
|
||||
- name: Run tests
|
||||
run: tox -e py312-test-instrumentation-tornado -- -ra
|
||||
|
||||
py313-test-instrumentation-tornado_ubuntu-latest:
|
||||
name: instrumentation-tornado 3.13 Ubuntu
|
||||
runs-on: ubuntu-latest
|
||||
|
|
|
|||
|
|
@ -11,6 +11,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
|
||||
## Unreleased
|
||||
|
||||
- `opentelemetry-instrumentation-asyncclick`: new instrumentation to trace asyncclick commands
|
||||
([#3319](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3319))
|
||||
|
||||
### Fixed
|
||||
|
||||
- `opentelemetry-instrumentation` Fix client address is set to server address in new semconv
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ aiohttp~=3.0
|
|||
aiokafka~=0.11.0
|
||||
aiopg>=0.13.0,<1.3.0
|
||||
asyncpg>=0.12.0
|
||||
asyncclick~=8.0
|
||||
boto~=2.0
|
||||
botocore~=1.0
|
||||
boto3~=1.0
|
||||
|
|
|
|||
|
|
@ -0,0 +1,7 @@
|
|||
.. include:: ../../../instrumentation/opentelemetry-instrumentation-asyncclick/README.rst
|
||||
:end-before: References
|
||||
|
||||
.. automodule:: opentelemetry.instrumentation.asyncclick
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
|
@ -7,6 +7,7 @@
|
|||
| [opentelemetry-instrumentation-aiokafka](./opentelemetry-instrumentation-aiokafka) | aiokafka >= 0.8, < 1.0 | No | development
|
||||
| [opentelemetry-instrumentation-aiopg](./opentelemetry-instrumentation-aiopg) | aiopg >= 0.13.0, < 2.0.0 | No | development
|
||||
| [opentelemetry-instrumentation-asgi](./opentelemetry-instrumentation-asgi) | asgiref ~= 3.0 | Yes | migration
|
||||
| [opentelemetry-instrumentation-asyncclick](./opentelemetry-instrumentation-asyncclick) | asyncclick ~= 8.0 | No | development
|
||||
| [opentelemetry-instrumentation-asyncio](./opentelemetry-instrumentation-asyncio) | asyncio | No | development
|
||||
| [opentelemetry-instrumentation-asyncpg](./opentelemetry-instrumentation-asyncpg) | asyncpg >= 0.12.0 | No | development
|
||||
| [opentelemetry-instrumentation-aws-lambda](./opentelemetry-instrumentation-aws-lambda) | aws_lambda | No | development
|
||||
|
|
|
|||
|
|
@ -0,0 +1,24 @@
|
|||
OpenTelemetry asyncclick Instrumentation
|
||||
========================================
|
||||
|
||||
|pypi|
|
||||
|
||||
.. |pypi| image:: https://badge.fury.io/py/opentelemetry-instrumentation-asyncclick.svg
|
||||
:target: https://pypi.org/project/opentelemetry-instrumentation-asyncclick/
|
||||
|
||||
This library allows tracing requests made by the asyncclick fork of the click library.
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
|
||||
::
|
||||
|
||||
pip install opentelemetry-instrumentation-asyncclick
|
||||
|
||||
|
||||
References
|
||||
----------
|
||||
|
||||
* `OpenTelemetry asyncclick/ Tracing <https://opentelemetry-python-contrib.readthedocs.io/en/latest/instrumentation/asyncclick/asyncclick.html>`_
|
||||
* `OpenTelemetry Project <https://opentelemetry.io/>`_
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
[build-system]
|
||||
requires = ["hatchling"]
|
||||
build-backend = "hatchling.build"
|
||||
|
||||
[project]
|
||||
name = "opentelemetry-instrumentation-asyncclick"
|
||||
dynamic = ["version"]
|
||||
description = "Async Click instrumentation for OpenTelemetry"
|
||||
readme = "README.rst"
|
||||
license = "Apache-2.0"
|
||||
requires-python = ">=3.8"
|
||||
authors = [
|
||||
{ name = "OpenTelemetry Authors", email = "cncf-opentelemetry-contributors@lists.cncf.io" },
|
||||
]
|
||||
classifiers = [
|
||||
"Development Status :: 4 - Beta",
|
||||
"Intended Audience :: Developers",
|
||||
"License :: OSI Approved :: Apache Software License",
|
||||
"Programming Language :: Python",
|
||||
"Programming Language :: Python :: 3",
|
||||
"Programming Language :: Python :: 3.8",
|
||||
"Programming Language :: Python :: 3.9",
|
||||
"Programming Language :: Python :: 3.10",
|
||||
"Programming Language :: Python :: 3.11",
|
||||
"Programming Language :: Python :: 3.12",
|
||||
"Programming Language :: Python :: 3.13",
|
||||
]
|
||||
dependencies = [
|
||||
"opentelemetry-api ~= 1.12",
|
||||
"opentelemetry-semantic-conventions == 0.53b0.dev",
|
||||
"wrapt ~= 1.0",
|
||||
"typing_extensions ~= 4.12",
|
||||
]
|
||||
|
||||
[project.optional-dependencies]
|
||||
instruments = [
|
||||
"asyncclick ~= 8.0",
|
||||
]
|
||||
|
||||
[project.entry-points.opentelemetry_instrumentor]
|
||||
asyncclick = "opentelemetry.instrumentation.asyncclick:AsyncClickInstrumentor"
|
||||
|
||||
[project.urls]
|
||||
Homepage = "https://github.com/open-telemetry/opentelemetry-python-contrib/instrumentation/opentelemetry-instrumentation-asyncclick"
|
||||
Repository = "https://github.com/open-telemetry/opentelemetry-python-contrib"
|
||||
|
||||
[tool.hatch.version]
|
||||
path = "src/opentelemetry/instrumentation/asyncclick/version.py"
|
||||
|
||||
[tool.hatch.build.targets.sdist]
|
||||
include = [
|
||||
"/src",
|
||||
"/tests",
|
||||
]
|
||||
|
||||
[tool.hatch.build.targets.wheel]
|
||||
packages = ["src/opentelemetry"]
|
||||
|
|
@ -0,0 +1,163 @@
|
|||
# Copyright The OpenTelemetry Authors
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""
|
||||
Instrument `asyncclick`_ CLI applications. The instrumentor will avoid instrumenting
|
||||
well-known servers (e.g. *flask run* and *uvicorn*) to avoid unexpected effects
|
||||
like every request having the same Trace ID.
|
||||
|
||||
|
||||
|
||||
.. _asyncclick: https://pypi.org/project/asyncclick/
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import asyncio
|
||||
import asyncclick
|
||||
from opentelemetry.instrumentation.asyncclick import AsyncClickInstrumentor
|
||||
|
||||
AsyncClickInstrumentor().instrument()
|
||||
|
||||
@asyncclick.command()
|
||||
async def hello():
|
||||
asyncclick.echo(f'Hello world!')
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(hello())
|
||||
|
||||
API
|
||||
---
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import sys
|
||||
from functools import partial
|
||||
from logging import getLogger
|
||||
from typing import (
|
||||
TYPE_CHECKING,
|
||||
Any,
|
||||
Awaitable,
|
||||
Callable,
|
||||
Collection,
|
||||
TypeVar,
|
||||
)
|
||||
|
||||
import asyncclick
|
||||
from typing_extensions import ParamSpec, Unpack
|
||||
from wrapt import (
|
||||
wrap_function_wrapper, # type: ignore[reportUnknownVariableType]
|
||||
)
|
||||
|
||||
from opentelemetry import trace
|
||||
from opentelemetry.instrumentation.asyncclick.package import _instruments
|
||||
from opentelemetry.instrumentation.asyncclick.version import __version__
|
||||
from opentelemetry.instrumentation.instrumentor import BaseInstrumentor
|
||||
from opentelemetry.instrumentation.utils import (
|
||||
unwrap,
|
||||
)
|
||||
from opentelemetry.semconv._incubating.attributes.process_attributes import (
|
||||
PROCESS_COMMAND_ARGS,
|
||||
PROCESS_EXECUTABLE_NAME,
|
||||
PROCESS_EXIT_CODE,
|
||||
PROCESS_PID,
|
||||
)
|
||||
from opentelemetry.semconv.attributes.error_attributes import ERROR_TYPE
|
||||
from opentelemetry.trace.status import StatusCode
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from typing import TypedDict
|
||||
|
||||
class InstrumentKwargs(TypedDict, total=False):
|
||||
tracer_provider: trace.TracerProvider
|
||||
|
||||
class UninstrumentKwargs(TypedDict, total=False):
|
||||
pass
|
||||
|
||||
|
||||
_logger = getLogger(__name__)
|
||||
|
||||
|
||||
T = TypeVar("T")
|
||||
P = ParamSpec("P")
|
||||
|
||||
|
||||
async def _command_invoke_wrapper(
|
||||
wrapped: Callable[P, Awaitable[T]],
|
||||
instance: asyncclick.core.Command,
|
||||
args: tuple[Any, ...],
|
||||
kwargs: dict[str, Any],
|
||||
tracer: trace.Tracer,
|
||||
) -> T:
|
||||
# Subclasses of Command include groups and CLI runners, but
|
||||
# we only want to instrument the actual commands which are
|
||||
# instances of Command itself.
|
||||
if instance.__class__ != asyncclick.Command:
|
||||
return await wrapped(*args, **kwargs)
|
||||
|
||||
ctx = args[0]
|
||||
|
||||
span_name = ctx.info_name
|
||||
span_attributes = {
|
||||
PROCESS_COMMAND_ARGS: sys.argv,
|
||||
PROCESS_EXECUTABLE_NAME: sys.argv[0],
|
||||
PROCESS_EXIT_CODE: 0,
|
||||
PROCESS_PID: os.getpid(),
|
||||
}
|
||||
|
||||
with tracer.start_as_current_span(
|
||||
name=span_name,
|
||||
kind=trace.SpanKind.INTERNAL,
|
||||
attributes=span_attributes,
|
||||
) as span:
|
||||
try:
|
||||
result = await wrapped(*args, **kwargs)
|
||||
return result
|
||||
except Exception as exc:
|
||||
span.set_status(StatusCode.ERROR, str(exc))
|
||||
if span.is_recording():
|
||||
span.set_attribute(ERROR_TYPE, type(exc).__qualname__)
|
||||
span.set_attribute(
|
||||
PROCESS_EXIT_CODE, getattr(exc, "exit_code", 1)
|
||||
)
|
||||
raise
|
||||
|
||||
|
||||
# pylint: disable=no-self-use
|
||||
class AsyncClickInstrumentor(BaseInstrumentor):
|
||||
"""An instrumentor for asyncclick"""
|
||||
|
||||
def instrumentation_dependencies(self) -> Collection[str]:
|
||||
return _instruments
|
||||
|
||||
def _instrument(self, **kwargs: Unpack[InstrumentKwargs]) -> None:
|
||||
tracer_provider = kwargs.get("tracer_provider")
|
||||
tracer = trace.get_tracer(
|
||||
__name__,
|
||||
__version__,
|
||||
tracer_provider,
|
||||
)
|
||||
|
||||
wrap_function_wrapper(
|
||||
asyncclick.core.Command,
|
||||
"invoke",
|
||||
partial(_command_invoke_wrapper, tracer=tracer),
|
||||
)
|
||||
|
||||
def _uninstrument(self, **kwargs: Unpack["UninstrumentKwargs"]) -> None:
|
||||
unwrap(asyncclick.core.Command, "invoke")
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
# Copyright The OpenTelemetry Authors
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
|
||||
_instruments = ("asyncclick ~= 8.0",)
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
# Copyright The OpenTelemetry Authors
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
__version__ = "0.53b0.dev"
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
|
||||
asyncclick~=8.0
|
||||
Flask~=3.0
|
||||
pytest==7.4.4
|
||||
anyio~=4.5
|
||||
-e opentelemetry-instrumentation
|
||||
-e instrumentation/opentelemetry-instrumentation-asyncclick
|
||||
-e instrumentation/opentelemetry-instrumentation-flask
|
||||
-e instrumentation/opentelemetry-instrumentation-wsgi
|
||||
-e util/opentelemetry-util-http
|
||||
|
|
@ -0,0 +1,371 @@
|
|||
# Copyright The OpenTelemetry Authors
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
import os
|
||||
from typing import Any
|
||||
from unittest import IsolatedAsyncioTestCase, mock
|
||||
|
||||
import asyncclick
|
||||
import asyncclick.testing
|
||||
from asyncclick.testing import CliRunner
|
||||
|
||||
from opentelemetry.instrumentation.asyncclick import AsyncClickInstrumentor
|
||||
from opentelemetry.test.test_base import TestBase
|
||||
from opentelemetry.trace import SpanKind
|
||||
from opentelemetry.trace.status import StatusCode
|
||||
|
||||
|
||||
def run_asyncclick_command_test(
|
||||
command: asyncclick.core.Command, args: tuple[Any, ...] = (), **kwargs: Any
|
||||
) -> asyncclick.testing.Result:
|
||||
"""
|
||||
Run an asyncclick command and return the result.
|
||||
|
||||
Args:
|
||||
command: The AsyncClick command to run
|
||||
args: Command-line arguments
|
||||
**kwargs: Additional arguments for CliRunner.invoke
|
||||
|
||||
Returns:
|
||||
The result of invoking the command
|
||||
"""
|
||||
|
||||
async def _run() -> asyncclick.testing.Result:
|
||||
runner = CliRunner()
|
||||
return await runner.invoke(command, args, **kwargs)
|
||||
|
||||
return asyncio.run(_run())
|
||||
|
||||
|
||||
class ClickTestCase(TestBase, IsolatedAsyncioTestCase):
|
||||
# pylint: disable=unbalanced-tuple-unpacking
|
||||
def setUp(self): # pylint: disable=invalid-name
|
||||
super().setUp()
|
||||
|
||||
AsyncClickInstrumentor().instrument()
|
||||
|
||||
def tearDown(self): # pylint: disable=invalid-name
|
||||
super().tearDown()
|
||||
AsyncClickInstrumentor().uninstrument()
|
||||
|
||||
@mock.patch("sys.argv", ["command.py"])
|
||||
def test_cli_command_wrapping(self):
|
||||
@asyncclick.command()
|
||||
async def command() -> None:
|
||||
pass
|
||||
|
||||
result = run_asyncclick_command_test(command)
|
||||
self.assertEqual(result.exit_code, 0)
|
||||
|
||||
(span,) = self.memory_exporter.get_finished_spans()
|
||||
self.assertEqual(span.status.status_code, StatusCode.UNSET)
|
||||
self.assertEqual(span.kind, SpanKind.INTERNAL)
|
||||
self.assertEqual(span.name, "command")
|
||||
self.assertEqual(
|
||||
dict(span.attributes),
|
||||
{
|
||||
"process.executable.name": "command.py",
|
||||
"process.command_args": ("command.py",),
|
||||
"process.exit.code": 0,
|
||||
"process.pid": os.getpid(),
|
||||
},
|
||||
)
|
||||
|
||||
@mock.patch("sys.argv", ["command.py"])
|
||||
def test_cli_command_wrapping_with_name(self):
|
||||
@asyncclick.command("mycommand")
|
||||
async def renamedcommand() -> None:
|
||||
pass
|
||||
|
||||
result = run_asyncclick_command_test(renamedcommand)
|
||||
self.assertEqual(result.exit_code, 0)
|
||||
|
||||
(span,) = self.memory_exporter.get_finished_spans()
|
||||
self.assertEqual(span.status.status_code, StatusCode.UNSET)
|
||||
self.assertEqual(span.kind, SpanKind.INTERNAL)
|
||||
self.assertEqual(span.name, "mycommand")
|
||||
self.assertEqual(
|
||||
dict(span.attributes),
|
||||
{
|
||||
"process.executable.name": "command.py",
|
||||
"process.command_args": ("command.py",),
|
||||
"process.exit.code": 0,
|
||||
"process.pid": os.getpid(),
|
||||
},
|
||||
)
|
||||
|
||||
@mock.patch("sys.argv", ["command.py", "--opt", "argument"])
|
||||
def test_cli_command_wrapping_with_options(self):
|
||||
@asyncclick.command()
|
||||
@asyncclick.argument("argument")
|
||||
@asyncclick.option("--opt/--no-opt", default=False)
|
||||
async def command(argument: str, opt: str) -> None:
|
||||
pass
|
||||
|
||||
argv = ["command.py", "--opt", "argument"]
|
||||
result = run_asyncclick_command_test(command, argv[1:])
|
||||
self.assertEqual(result.exit_code, 0)
|
||||
|
||||
(span,) = self.memory_exporter.get_finished_spans()
|
||||
self.assertEqual(span.status.status_code, StatusCode.UNSET)
|
||||
self.assertEqual(span.kind, SpanKind.INTERNAL)
|
||||
self.assertEqual(span.name, "command")
|
||||
self.assertEqual(
|
||||
dict(span.attributes),
|
||||
{
|
||||
"process.executable.name": "command.py",
|
||||
"process.command_args": tuple(argv),
|
||||
"process.exit.code": 0,
|
||||
"process.pid": os.getpid(),
|
||||
},
|
||||
)
|
||||
|
||||
@mock.patch("sys.argv", ["command-raises.py"])
|
||||
def test_cli_command_raises_error(self):
|
||||
@asyncclick.command()
|
||||
async def command_raises() -> None:
|
||||
raise ValueError()
|
||||
|
||||
result = run_asyncclick_command_test(command_raises)
|
||||
self.assertEqual(result.exit_code, 1)
|
||||
|
||||
(span,) = self.memory_exporter.get_finished_spans()
|
||||
self.assertEqual(span.status.status_code, StatusCode.ERROR)
|
||||
self.assertEqual(span.kind, SpanKind.INTERNAL)
|
||||
self.assertEqual(span.name, "command-raises")
|
||||
self.assertEqual(
|
||||
dict(span.attributes),
|
||||
{
|
||||
"process.executable.name": "command-raises.py",
|
||||
"process.command_args": ("command-raises.py",),
|
||||
"process.exit.code": 1,
|
||||
"process.pid": os.getpid(),
|
||||
"error.type": "ValueError",
|
||||
},
|
||||
)
|
||||
|
||||
def test_uninstrument(self):
|
||||
AsyncClickInstrumentor().uninstrument()
|
||||
|
||||
@asyncclick.command()
|
||||
async def notracecommand() -> None:
|
||||
pass
|
||||
|
||||
result = run_asyncclick_command_test(notracecommand)
|
||||
self.assertEqual(result.exit_code, 0)
|
||||
|
||||
self.assertFalse(self.memory_exporter.get_finished_spans())
|
||||
AsyncClickInstrumentor().instrument()
|
||||
|
||||
@mock.patch("sys.argv", ["command.py", "sub1", "sub2"])
|
||||
def test_nested_command_groups(self):
|
||||
"""Test instrumentation of nested command groups."""
|
||||
|
||||
@asyncclick.group()
|
||||
async def cli() -> None:
|
||||
pass
|
||||
|
||||
@cli.group()
|
||||
async def sub1() -> None:
|
||||
pass
|
||||
|
||||
@sub1.command()
|
||||
async def sub2() -> None:
|
||||
pass
|
||||
|
||||
result = run_asyncclick_command_test(cli, ["sub1", "sub2"])
|
||||
self.assertEqual(result.exit_code, 0)
|
||||
|
||||
spans = self.memory_exporter.get_finished_spans()
|
||||
self.assertEqual(
|
||||
len(spans), 1
|
||||
) # Only the leaf command should be instrumented
|
||||
span = spans[0]
|
||||
self.assertEqual(span.name, "sub2")
|
||||
self.assertEqual(
|
||||
dict(span.attributes),
|
||||
{
|
||||
"process.executable.name": "command.py",
|
||||
"process.command_args": ("command.py", "sub1", "sub2"),
|
||||
"process.exit.code": 0,
|
||||
"process.pid": os.getpid(),
|
||||
},
|
||||
)
|
||||
|
||||
@mock.patch("sys.argv", ["command.py"])
|
||||
def test_command_with_callback(self):
|
||||
"""Test instrumentation of commands with callbacks."""
|
||||
callback_called = False
|
||||
|
||||
def callback_func(
|
||||
ctx: asyncclick.Context, param: asyncclick.Parameter, value: bool
|
||||
) -> bool:
|
||||
nonlocal callback_called
|
||||
callback_called = True
|
||||
return value
|
||||
|
||||
@asyncclick.command()
|
||||
@asyncclick.option(
|
||||
"--option", callback=callback_func, default="default"
|
||||
)
|
||||
async def command(option: str) -> None:
|
||||
pass
|
||||
|
||||
result = run_asyncclick_command_test(command)
|
||||
self.assertEqual(result.exit_code, 0)
|
||||
self.assertTrue(callback_called)
|
||||
|
||||
(span,) = self.memory_exporter.get_finished_spans()
|
||||
self.assertEqual(span.name, "command")
|
||||
self.assertEqual(span.status.status_code, StatusCode.UNSET)
|
||||
|
||||
@mock.patch("sys.argv", ["command.py"])
|
||||
def test_command_with_result_callback(self):
|
||||
"""Test instrumentation with result callbacks."""
|
||||
callback_called = False
|
||||
|
||||
@asyncclick.group(chain=True)
|
||||
async def cli() -> None:
|
||||
pass
|
||||
|
||||
@cli.result_callback()
|
||||
async def process_result(result: asyncclick.testing.Result) -> None:
|
||||
nonlocal callback_called
|
||||
callback_called = True
|
||||
|
||||
@cli.command()
|
||||
async def command() -> None:
|
||||
pass
|
||||
|
||||
result = run_asyncclick_command_test(cli, ["command"])
|
||||
self.assertEqual(result.exit_code, 0)
|
||||
self.assertTrue(callback_called)
|
||||
|
||||
spans = self.memory_exporter.get_finished_spans()
|
||||
self.assertEqual(len(spans), 1)
|
||||
span = spans[0]
|
||||
self.assertEqual(span.name, "command")
|
||||
|
||||
@mock.patch("sys.argv", ["command.py"])
|
||||
def test_command_chaining(self):
|
||||
"""Test instrumentation with command chaining."""
|
||||
|
||||
@asyncclick.group(chain=True)
|
||||
async def cli() -> None:
|
||||
pass
|
||||
|
||||
@cli.command()
|
||||
async def cmd1() -> None:
|
||||
return "result1"
|
||||
|
||||
@cli.command()
|
||||
async def cmd2() -> None:
|
||||
return "result2"
|
||||
|
||||
result = run_asyncclick_command_test(cli, ["cmd1", "cmd2"])
|
||||
self.assertEqual(result.exit_code, 0)
|
||||
|
||||
spans = self.memory_exporter.get_finished_spans()
|
||||
self.assertEqual(len(spans), 2)
|
||||
# Spans should be ordered by execution
|
||||
self.assertEqual(spans[0].name, "cmd1")
|
||||
self.assertEqual(spans[1].name, "cmd2")
|
||||
|
||||
@mock.patch("sys.argv", ["command.py"])
|
||||
def test_custom_exit_codes(self) -> None:
|
||||
"""Test instrumentation with custom exit codes."""
|
||||
|
||||
@asyncclick.command()
|
||||
async def command() -> None:
|
||||
raise asyncclick.exceptions.Exit(code=42)
|
||||
|
||||
result = run_asyncclick_command_test(command)
|
||||
self.assertEqual(result.exit_code, 42)
|
||||
|
||||
(span,) = self.memory_exporter.get_finished_spans()
|
||||
self.assertEqual(span.name, "command")
|
||||
self.assertEqual(span.status.status_code, StatusCode.ERROR)
|
||||
self.assertEqual(span.attributes["process.exit.code"], 42)
|
||||
self.assertEqual(span.attributes["error.type"], "Exit")
|
||||
|
||||
@mock.patch("sys.argv", ["command.py"])
|
||||
def test_context_object_passing(self):
|
||||
"""Test that instrumentation preserves context object passing."""
|
||||
|
||||
@asyncclick.group()
|
||||
@asyncclick.option("--debug/--no-debug", default=False)
|
||||
@asyncclick.pass_context
|
||||
async def cli(ctx: asyncclick.Context, debug: bool) -> None:
|
||||
ctx.ensure_object(dict)
|
||||
ctx.obj["DEBUG"] = debug
|
||||
|
||||
@cli.command()
|
||||
@asyncclick.pass_context
|
||||
async def command(ctx: asyncclick.Context) -> None:
|
||||
assert isinstance(ctx.obj, dict)
|
||||
assert "DEBUG" in ctx.obj
|
||||
|
||||
result = run_asyncclick_command_test(cli, ["--debug", "command"])
|
||||
self.assertEqual(result.exit_code, 0)
|
||||
|
||||
(span,) = self.memory_exporter.get_finished_spans()
|
||||
self.assertEqual(span.name, "command")
|
||||
|
||||
def test_multiple_instrumentation(self):
|
||||
"""Test that instrumenting multiple times only applies once."""
|
||||
# Already instrumented in setUp, instrument again
|
||||
AsyncClickInstrumentor().instrument()
|
||||
|
||||
@asyncclick.command()
|
||||
async def command() -> None:
|
||||
pass
|
||||
|
||||
result = run_asyncclick_command_test(command)
|
||||
self.assertEqual(result.exit_code, 0)
|
||||
|
||||
spans = self.memory_exporter.get_finished_spans()
|
||||
self.assertEqual(len(spans), 1)
|
||||
|
||||
def test_concurrency(self):
|
||||
"""Test instrumentation with concurrent command execution."""
|
||||
|
||||
@asyncclick.command()
|
||||
async def command1() -> None:
|
||||
pass
|
||||
|
||||
@asyncclick.command()
|
||||
async def command2() -> None:
|
||||
pass
|
||||
|
||||
async def run_both() -> (
|
||||
tuple[asyncclick.testing.Result, asyncclick.testing.Result]
|
||||
):
|
||||
runner = CliRunner()
|
||||
task1 = asyncio.create_task(runner.invoke(command1))
|
||||
task2 = asyncio.create_task(runner.invoke(command2))
|
||||
results = await asyncio.gather(task1, task2)
|
||||
return results
|
||||
|
||||
results = asyncio.run(run_both())
|
||||
self.assertEqual(results[0].exit_code, 0)
|
||||
self.assertEqual(results[1].exit_code, 0)
|
||||
|
||||
spans = self.memory_exporter.get_finished_spans()
|
||||
self.assertEqual(len(spans), 2)
|
||||
span_names = [span.name for span in spans]
|
||||
self.assertIn("command1", span_names)
|
||||
self.assertIn("command2", span_names)
|
||||
|
|
@ -36,6 +36,7 @@ dependencies = [
|
|||
"opentelemetry-instrumentation-aiokafka==0.53b0.dev",
|
||||
"opentelemetry-instrumentation-aiopg==0.53b0.dev",
|
||||
"opentelemetry-instrumentation-asgi==0.53b0.dev",
|
||||
"opentelemetry-instrumentation-asyncclick==0.53b0.dev",
|
||||
"opentelemetry-instrumentation-asyncio==0.53b0.dev",
|
||||
"opentelemetry-instrumentation-asyncpg==0.53b0.dev",
|
||||
"opentelemetry-instrumentation-aws-lambda==0.53b0.dev",
|
||||
|
|
|
|||
|
|
@ -48,6 +48,10 @@ libraries = [
|
|||
"library": "asgiref ~= 3.0",
|
||||
"instrumentation": "opentelemetry-instrumentation-asgi==0.53b0.dev",
|
||||
},
|
||||
{
|
||||
"library": "asyncclick ~= 8.0",
|
||||
"instrumentation": "opentelemetry-instrumentation-asyncclick==0.53b0.dev",
|
||||
},
|
||||
{
|
||||
"library": "asyncpg >= 0.12.0",
|
||||
"instrumentation": "opentelemetry-instrumentation-asyncpg==0.53b0.dev",
|
||||
|
|
|
|||
|
|
@ -196,12 +196,14 @@ pythonVersion = "3.8"
|
|||
reportPrivateUsage = false # Ignore private attributes added by instrumentation packages.
|
||||
# Add progressively instrumentation packages here.
|
||||
include = [
|
||||
"instrumentation/opentelemetry-instrumentation-asyncclick",
|
||||
"instrumentation/opentelemetry-instrumentation-threading",
|
||||
"instrumentation-genai/opentelemetry-instrumentation-vertexai",
|
||||
]
|
||||
# We should also add type hints to the test suite - It helps on finding bugs.
|
||||
# We are excluding for now because it's easier, and more important to add to the instrumentation packages.
|
||||
exclude = [
|
||||
"instrumentation/opentelemetry-instrumentation-asyncclick/tests/**/*.py",
|
||||
"instrumentation/opentelemetry-instrumentation-threading/tests/**",
|
||||
"instrumentation-genai/opentelemetry-instrumentation-vertexai/tests/**/*.py",
|
||||
"instrumentation-genai/opentelemetry-instrumentation-vertexai/examples/**/*.py",
|
||||
|
|
|
|||
13
tox.ini
13
tox.ini
|
|
@ -109,6 +109,11 @@ envlist =
|
|||
; pypy3-test-instrumentation-boto
|
||||
lint-instrumentation-boto
|
||||
|
||||
; opentelemetry-instrumentation-asyncclick
|
||||
py3{8,9,10,11,12,13}-test-instrumentation-asyncclick
|
||||
pypy3-test-instrumentation-asyncclick
|
||||
lint-instrumentation-asyncclick
|
||||
|
||||
; opentelemetry-instrumentation-click
|
||||
py3{8,9,10,11,12,13}-test-instrumentation-click
|
||||
pypy3-test-instrumentation-click
|
||||
|
|
@ -466,6 +471,9 @@ deps =
|
|||
pypy3-test-instrumentation-celery: -r {toxinidir}/instrumentation/opentelemetry-instrumentation-celery/test-requirements-1.txt
|
||||
lint-instrumentation-celery: -r {toxinidir}/instrumentation/opentelemetry-instrumentation-celery/test-requirements-1.txt
|
||||
|
||||
asyncclick: {[testenv]test_deps}
|
||||
asyncclick: -r {toxinidir}/instrumentation/opentelemetry-instrumentation-asyncclick/test-requirements.txt
|
||||
|
||||
click: {[testenv]test_deps}
|
||||
click: -r {toxinidir}/instrumentation/opentelemetry-instrumentation-click/test-requirements.txt
|
||||
|
||||
|
|
@ -741,6 +749,9 @@ commands =
|
|||
test-instrumentation-asgi: pytest {toxinidir}/instrumentation/opentelemetry-instrumentation-asgi/tests {posargs}
|
||||
lint-instrumentation-asgi: sh -c "cd instrumentation && pylint --rcfile ../.pylintrc opentelemetry-instrumentation-asgi"
|
||||
|
||||
test-instrumentation-asyncclick: pytest {toxinidir}/instrumentation/opentelemetry-instrumentation-asyncclick/tests {posargs}
|
||||
lint-instrumentation-asyncclick: sh -c "cd instrumentation && pylint --rcfile ../.pylintrc opentelemetry-instrumentation-asyncclick"
|
||||
|
||||
test-instrumentation-asyncpg: pytest {toxinidir}/instrumentation/opentelemetry-instrumentation-asyncpg/tests {posargs}
|
||||
lint-instrumentation-asyncpg: sh -c "cd instrumentation && pylint --rcfile ../.pylintrc opentelemetry-instrumentation-asyncpg"
|
||||
|
||||
|
|
@ -1036,5 +1047,7 @@ deps =
|
|||
{toxinidir}/util/opentelemetry-util-http
|
||||
{toxinidir}/instrumentation-genai/opentelemetry-instrumentation-vertexai[instruments]
|
||||
{toxinidir}/instrumentation-genai/opentelemetry-instrumentation-google-genai[instruments]
|
||||
{toxinidir}/instrumentation/opentelemetry-instrumentation-asyncclick[instruments]
|
||||
|
||||
commands =
|
||||
pyright
|
||||
|
|
|
|||
63
uv.lock
63
uv.lock
|
|
@ -20,6 +20,7 @@ members = [
|
|||
"opentelemetry-instrumentation-aiokafka",
|
||||
"opentelemetry-instrumentation-aiopg",
|
||||
"opentelemetry-instrumentation-asgi",
|
||||
"opentelemetry-instrumentation-asyncclick",
|
||||
"opentelemetry-instrumentation-asyncio",
|
||||
"opentelemetry-instrumentation-asyncpg",
|
||||
"opentelemetry-instrumentation-aws-lambda",
|
||||
|
|
@ -590,6 +591,42 @@ wheels = [
|
|||
{ url = "https://files.pythonhosted.org/packages/a7/fa/e01228c2938de91d47b307831c62ab9e4001e747789d0b05baf779a6488c/async_timeout-4.0.3-py3-none-any.whl", hash = "sha256:7405140ff1230c310e51dc27b3145b9092d659ce68ff733fb0cefe3ee42be028", size = 5721 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "asyncclick"
|
||||
version = "8.1.7.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
resolution-markers = [
|
||||
"python_full_version < '3.9'",
|
||||
]
|
||||
dependencies = [
|
||||
{ name = "anyio", version = "4.5.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.9'" },
|
||||
{ name = "colorama", marker = "python_full_version < '3.9' and sys_platform == 'win32'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/5e/bf/59d836c3433d7aa07f76c2b95c4eb763195ea8a5d7f9ad3311ed30c2af61/asyncclick-8.1.7.2.tar.gz", hash = "sha256:219ea0f29ccdc1bb4ff43bcab7ce0769ac6d48a04f997b43ec6bee99a222daa0", size = 349073 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/1e/6e/9acdbb25733e1de411663b59abe521bec738e72fe4e85843f6ff8b212832/asyncclick-8.1.7.2-py3-none-any.whl", hash = "sha256:1ab940b04b22cb89b5b400725132b069d01b0c3472a9702c7a2c9d5d007ded02", size = 99191 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "asyncclick"
|
||||
version = "8.1.8"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
resolution-markers = [
|
||||
"python_full_version >= '3.13'",
|
||||
"python_full_version >= '3.11' and python_full_version < '3.13'",
|
||||
"python_full_version == '3.10.*'",
|
||||
"python_full_version == '3.9.*'",
|
||||
]
|
||||
dependencies = [
|
||||
{ name = "anyio", version = "4.8.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.9'" },
|
||||
{ name = "colorama", marker = "python_full_version >= '3.9' and sys_platform == 'win32'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/cb/b5/e1e5fdf1c1bb7e6e614987c120a98d9324bf8edfaa5f5cd16a6235c9d91b/asyncclick-8.1.8.tar.gz", hash = "sha256:0f0eb0f280e04919d67cf71b9fcdfb4db2d9ff7203669c40284485c149578e4c", size = 232900 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/14/cc/a436f0fc2d04e57a0697e0f87a03b9eaed03ad043d2d5f887f8eebcec95f/asyncclick-8.1.8-py3-none-any.whl", hash = "sha256:eb1ccb44bc767f8f0695d592c7806fdf5bd575605b4ee246ffd5fadbcfdbd7c6", size = 99093 },
|
||||
{ url = "https://files.pythonhosted.org/packages/92/c4/ae9e9d25522c6dc96ff167903880a0fe94d7bd31ed999198ee5017d977ed/asyncclick-8.1.8.0-py3-none-any.whl", hash = "sha256:be146a2d8075d4fe372ff4e877f23c8b5af269d16705c1948123b9415f6fd678", size = 99115 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "asyncpg"
|
||||
version = "0.30.0"
|
||||
|
|
@ -2861,6 +2898,32 @@ requires-dist = [
|
|||
]
|
||||
provides-extras = ["instruments"]
|
||||
|
||||
[[package]]
|
||||
name = "opentelemetry-instrumentation-asyncclick"
|
||||
source = { editable = "instrumentation/opentelemetry-instrumentation-asyncclick" }
|
||||
dependencies = [
|
||||
{ name = "opentelemetry-api" },
|
||||
{ name = "opentelemetry-semantic-conventions" },
|
||||
{ name = "typing-extensions" },
|
||||
{ name = "wrapt" },
|
||||
]
|
||||
|
||||
[package.optional-dependencies]
|
||||
instruments = [
|
||||
{ name = "asyncclick", version = "8.1.7.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.9'" },
|
||||
{ name = "asyncclick", version = "8.1.8", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.9'" },
|
||||
]
|
||||
|
||||
[package.metadata]
|
||||
requires-dist = [
|
||||
{ name = "asyncclick", marker = "extra == 'instruments'", specifier = "~=8.0" },
|
||||
{ name = "opentelemetry-api", git = "https://github.com/open-telemetry/opentelemetry-python?subdirectory=opentelemetry-api&branch=main" },
|
||||
{ name = "opentelemetry-semantic-conventions", git = "https://github.com/open-telemetry/opentelemetry-python?subdirectory=opentelemetry-semantic-conventions&branch=main" },
|
||||
{ name = "typing-extensions", specifier = "~=4.12" },
|
||||
{ name = "wrapt", specifier = "~=1.0" },
|
||||
]
|
||||
provides-extras = ["instruments"]
|
||||
|
||||
[[package]]
|
||||
name = "opentelemetry-instrumentation-asyncio"
|
||||
source = { editable = "instrumentation/opentelemetry-instrumentation-asyncio" }
|
||||
|
|
|
|||
Loading…
Reference in New Issue