diff --git a/CHANGELOG.md b/CHANGELOG.md index 2923ebc791..ea44000be5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +- `opentelemetry-instrumentation-aiohttp-server` Add instrumentor and auto instrumentation support for aiohttp-server + ([#1800](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1800)) ### Added - `opentelemetry-instrumentation-system-metrics` Add support for collecting process metrics @@ -19,7 +21,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `opentelemetry-resource-detector-azure` Using new Cloud Resource ID attribute. ([#1976](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1976)) - ## Version 1.20.0/0.41b0 (2023-09-01) ### Fixed diff --git a/instrumentation/README.md b/instrumentation/README.md index 12b8ada105..cc5ea02def 100644 --- a/instrumentation/README.md +++ b/instrumentation/README.md @@ -3,6 +3,7 @@ | --------------- | ------------------ | --------------- | | [opentelemetry-instrumentation-aio-pika](./opentelemetry-instrumentation-aio-pika) | aio_pika >= 7.2.0, < 10.0.0 | No | [opentelemetry-instrumentation-aiohttp-client](./opentelemetry-instrumentation-aiohttp-client) | aiohttp ~= 3.0 | No +| [opentelemetry-instrumentation-aiohttp-server](./opentelemetry-instrumentation-aiohttp-server) | aiohttp ~= 3.0 | No | [opentelemetry-instrumentation-aiopg](./opentelemetry-instrumentation-aiopg) | aiopg >= 0.13.0, < 2.0.0 | No | [opentelemetry-instrumentation-asgi](./opentelemetry-instrumentation-asgi) | asgiref ~= 3.0 | No | [opentelemetry-instrumentation-asyncpg](./opentelemetry-instrumentation-asyncpg) | asyncpg >= 0.12.0 | No diff --git a/instrumentation/opentelemetry-instrumentation-aiohttp-server/README.rst b/instrumentation/opentelemetry-instrumentation-aiohttp-server/README.rst new file mode 100644 index 0000000000..b8606ad687 --- /dev/null +++ b/instrumentation/opentelemetry-instrumentation-aiohttp-server/README.rst @@ -0,0 +1,24 @@ +OpenTelemetry aiohttp server Integration +======================================== + +|pypi| + +.. |pypi| image:: https://badge.fury.io/py/opentelemetry-instrumentation-aiohttp-client.svg + :target: https://pypi.org/project/opentelemetry-instrumentation-aiohttp-client/ + +This library allows tracing HTTP requests made by the +`aiohttp server `_ library. + +Installation +------------ + +:: + + pip install opentelemetry-instrumentation-aiohttp-server + +References +---------- + +* `OpenTelemetry Project `_ +* `aiohttp client Tracing `_ +* `OpenTelemetry Python Examples `_ diff --git a/instrumentation/opentelemetry-instrumentation-aiohttp-server/pyproject.toml b/instrumentation/opentelemetry-instrumentation-aiohttp-server/pyproject.toml new file mode 100644 index 0000000000..77bfa08b11 --- /dev/null +++ b/instrumentation/opentelemetry-instrumentation-aiohttp-server/pyproject.toml @@ -0,0 +1,61 @@ +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" + +[project] +name = "opentelemetry-instrumentation-aiohttp-server" +dynamic = ["version"] +description = "Aiohttp server instrumentation for OpenTelemetry" +readme = "README.rst" +license = "Apache-2.0" +requires-python = ">=3.7" +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.7", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11" +] +dependencies = [ + "opentelemetry-api ~= 1.12", + "opentelemetry-instrumentation == 0.42b0.dev", + "opentelemetry-semantic-conventions == 0.42b0.dev", + "opentelemetry-util-http == 0.42b0.dev", + "wrapt >= 1.0.0, < 2.0.0", +] + +[project.optional-dependencies] +instruments = [ + "aiohttp ~= 3.0", +] +test = [ + "opentelemetry-instrumentation-aiohttp-server[instruments]", + "pytest-asyncio", + "pytest-aiohttp", +] + +[project.entry-points.opentelemetry_instrumentor] +aiohttp-server = "opentelemetry.instrumentation.aiohttp_server:AioHttpServerInstrumentor" + +[project.urls] +Homepage = "https://github.com/open-telemetry/opentelemetry-python-contrib/tree/main/instrumentation/opentelemetry-instrumentation-aiohttp-server" + +[tool.hatch.version] +path = "src/opentelemetry/instrumentation/aiohttp_server/version.py" + +[tool.hatch.build.targets.sdist] +include = [ + "/src", + "/tests", +] + +[tool.hatch.build.targets.wheel] +packages = ["src/opentelemetry"] diff --git a/instrumentation/opentelemetry-instrumentation-aiohttp-server/src/opentelemetry/instrumentation/aiohttp_server/__init__.py b/instrumentation/opentelemetry-instrumentation-aiohttp-server/src/opentelemetry/instrumentation/aiohttp_server/__init__.py new file mode 100644 index 0000000000..3fd8e62e78 --- /dev/null +++ b/instrumentation/opentelemetry-instrumentation-aiohttp-server/src/opentelemetry/instrumentation/aiohttp_server/__init__.py @@ -0,0 +1,267 @@ +# Copyright 2020, 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. + +import urllib +from aiohttp import web +from multidict import CIMultiDictProxy +from timeit import default_timer +from typing import Tuple, Dict, List, Union + +from opentelemetry import context, trace, metrics +from opentelemetry.context import _SUPPRESS_HTTP_INSTRUMENTATION_KEY +from opentelemetry.instrumentation.aiohttp_server.package import _instruments +from opentelemetry.instrumentation.aiohttp_server.version import __version__ +from opentelemetry.instrumentation.instrumentor import BaseInstrumentor +from opentelemetry.instrumentation.utils import http_status_to_status_code +from opentelemetry.propagators.textmap import Getter +from opentelemetry.propagate import extract +from opentelemetry.semconv.trace import SpanAttributes +from opentelemetry.semconv.metrics import MetricInstruments +from opentelemetry.trace.status import Status, StatusCode +from opentelemetry.util.http import get_excluded_urls +from opentelemetry.util.http import remove_url_credentials + +_duration_attrs = [ + SpanAttributes.HTTP_METHOD, + SpanAttributes.HTTP_HOST, + SpanAttributes.HTTP_SCHEME, + SpanAttributes.HTTP_STATUS_CODE, + SpanAttributes.HTTP_FLAVOR, + SpanAttributes.HTTP_SERVER_NAME, + SpanAttributes.NET_HOST_NAME, + SpanAttributes.NET_HOST_PORT, + SpanAttributes.HTTP_ROUTE, +] + +_active_requests_count_attrs = [ + SpanAttributes.HTTP_METHOD, + SpanAttributes.HTTP_HOST, + SpanAttributes.HTTP_SCHEME, + SpanAttributes.HTTP_FLAVOR, + SpanAttributes.HTTP_SERVER_NAME, +] + +tracer = trace.get_tracer(__name__) +meter = metrics.get_meter(__name__, __version__) +_excluded_urls = get_excluded_urls("AIOHTTP_SERVER") + + +def _parse_duration_attrs(req_attrs): + duration_attrs = {} + for attr_key in _duration_attrs: + if req_attrs.get(attr_key) is not None: + duration_attrs[attr_key] = req_attrs[attr_key] + return duration_attrs + + +def _parse_active_request_count_attrs(req_attrs): + active_requests_count_attrs = {} + for attr_key in _active_requests_count_attrs: + if req_attrs.get(attr_key) is not None: + active_requests_count_attrs[attr_key] = req_attrs[attr_key] + return active_requests_count_attrs + + +def get_default_span_details(request: web.Request) -> Tuple[str, dict]: + """Default implementation for get_default_span_details + Args: + request: the request object itself. + Returns: + a tuple of the span name, and any attributes to attach to the span. + """ + span_name = request.path.strip() or f"HTTP {request.method}" + return span_name, {} + + +def _get_view_func(request: web.Request) -> str: + """Returns the name of the request handler. + Args: + request: the request object itself. + Returns: + a string containing the name of the handler function + """ + try: + return request.match_info.handler.__name__ + except AttributeError: + return "unknown" + + +def collect_request_attributes(request: web.Request) -> Dict: + """Collects HTTP request attributes from the ASGI scope and returns a + dictionary to be used as span creation attributes.""" + + server_host, port, http_url = ( + request.url.host, + request.url.port, + str(request.url), + ) + query_string = request.query_string + if query_string and http_url: + if isinstance(query_string, bytes): + query_string = query_string.decode("utf8") + http_url += "?" + urllib.parse.unquote(query_string) + + result = { + SpanAttributes.HTTP_SCHEME: request.scheme, + SpanAttributes.HTTP_HOST: server_host, + SpanAttributes.NET_HOST_PORT: port, + SpanAttributes.HTTP_ROUTE: _get_view_func(request), + SpanAttributes.HTTP_FLAVOR: f"{request.version.major}.{request.version.minor}", + SpanAttributes.HTTP_TARGET: request.path, + SpanAttributes.HTTP_URL: remove_url_credentials(http_url), + } + + http_method = request.method + if http_method: + result[SpanAttributes.HTTP_METHOD] = http_method + + http_host_value_list = ( + [request.host] if type(request.host) != list else request.host + ) + if http_host_value_list: + result[SpanAttributes.HTTP_SERVER_NAME] = ",".join( + http_host_value_list + ) + http_user_agent = request.headers.get("user-agent") + if http_user_agent: + result[SpanAttributes.HTTP_USER_AGENT] = http_user_agent + + # remove None values + result = {k: v for k, v in result.items() if v is not None} + + return result + + +def set_status_code(span, status_code: int) -> None: + """Adds HTTP response attributes to span using the status_code argument.""" + + try: + status_code = int(status_code) + except ValueError: + span.set_status( + Status( + StatusCode.ERROR, + "Non-integer HTTP status: " + repr(status_code), + ) + ) + else: + span.set_attribute(SpanAttributes.HTTP_STATUS_CODE, status_code) + span.set_status( + Status(http_status_to_status_code(status_code, server_span=True)) + ) + + +class AiohttpGetter(Getter): + """Extract current trace from headers""" + + def get(self, carrier, key: str) -> Union[List, None]: + """Getter implementation to retrieve an HTTP header value from the ASGI + scope. + + Args: + carrier: ASGI scope object + key: header name in scope + Returns: + A list of all header values matching the key, or None if the key + does not match any header. + """ + headers: CIMultiDictProxy = carrier.headers + if not headers: + return None + return headers.getall(key, None) + + def keys(self, carrier: Dict) -> List: + return list(carrier.keys()) + + +getter = AiohttpGetter() + + +@web.middleware +async def middleware(request, handler): + """Middleware for aiohttp implementing tracing logic""" + if ( + context.get_value("suppress_instrumentation") + or context.get_value(_SUPPRESS_HTTP_INSTRUMENTATION_KEY) + or _excluded_urls.url_disabled(request.url.path) + ): + return await handler(request) + + span_name, additional_attributes = get_default_span_details(request) + + req_attrs = collect_request_attributes(request) + duration_attrs = _parse_duration_attrs(req_attrs) + active_requests_count_attrs = _parse_active_request_count_attrs(req_attrs) + + duration_histogram = meter.create_histogram( + name=MetricInstruments.HTTP_SERVER_DURATION, + unit="ms", + description="measures the duration of the inbound HTTP request", + ) + + active_requests_counter = meter.create_up_down_counter( + name=MetricInstruments.HTTP_SERVER_ACTIVE_REQUESTS, + unit="requests", + description="measures the number of concurrent HTTP requests those are currently in flight", + ) + + with tracer.start_as_current_span( + span_name, + context=extract(request, getter=getter), + kind=trace.SpanKind.SERVER, + ) as span: + attributes = collect_request_attributes(request) + attributes.update(additional_attributes) + span.set_attributes(attributes) + start = default_timer() + active_requests_counter.add(1, active_requests_count_attrs) + try: + resp = await handler(request) + set_status_code(span, resp.status) + except web.HTTPException as ex: + set_status_code(span, ex.status_code) + raise + finally: + duration = max((default_timer() - start) * 1000, 0) + duration_histogram.record(duration, duration_attrs) + active_requests_counter.add(-1, active_requests_count_attrs) + return resp + + +class _InstrumentedApplication(web.Application): + """Insert tracing middleware""" + + def __init__(self, *args, **kwargs): + middlewares = kwargs.pop("middlewares", []) + middlewares.insert(0, middleware) + kwargs["middlewares"] = middlewares + super().__init__(*args, **kwargs) + + +class AioHttpServerInstrumentor(BaseInstrumentor): + # pylint: disable=protected-access,attribute-defined-outside-init + """An instrumentor for aiohttp.web.Application + + See `BaseInstrumentor` + """ + + def _instrument(self, **kwargs): + self._original_app = web.Application + setattr(web, "Application", _InstrumentedApplication) + + def _uninstrument(self, **kwargs): + setattr(web, "Application", self._original_app) + + def instrumentation_dependencies(self): + return _instruments diff --git a/instrumentation/opentelemetry-instrumentation-aiohttp-server/src/opentelemetry/instrumentation/aiohttp_server/package.py b/instrumentation/opentelemetry-instrumentation-aiohttp-server/src/opentelemetry/instrumentation/aiohttp_server/package.py new file mode 100644 index 0000000000..557f1a54a9 --- /dev/null +++ b/instrumentation/opentelemetry-instrumentation-aiohttp-server/src/opentelemetry/instrumentation/aiohttp_server/package.py @@ -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 = ("aiohttp ~= 3.0",) diff --git a/instrumentation/opentelemetry-instrumentation-aiohttp-server/src/opentelemetry/instrumentation/aiohttp_server/version.py b/instrumentation/opentelemetry-instrumentation-aiohttp-server/src/opentelemetry/instrumentation/aiohttp_server/version.py new file mode 100644 index 0000000000..c2996671d6 --- /dev/null +++ b/instrumentation/opentelemetry-instrumentation-aiohttp-server/src/opentelemetry/instrumentation/aiohttp_server/version.py @@ -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.42b0.dev" diff --git a/instrumentation/opentelemetry-instrumentation-aiohttp-server/tests/__init__.py b/instrumentation/opentelemetry-instrumentation-aiohttp-server/tests/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/instrumentation/opentelemetry-instrumentation-aiohttp-server/tests/test_aiohttp_server_integration.py b/instrumentation/opentelemetry-instrumentation-aiohttp-server/tests/test_aiohttp_server_integration.py new file mode 100644 index 0000000000..973aea0d1c --- /dev/null +++ b/instrumentation/opentelemetry-instrumentation-aiohttp-server/tests/test_aiohttp_server_integration.py @@ -0,0 +1,105 @@ +# Copyright 2020, 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. + +import pytest +import pytest_asyncio +import aiohttp +from http import HTTPStatus +from .utils import HTTPMethod + +from opentelemetry import trace as trace_api +from opentelemetry.test.test_base import TestBase +from opentelemetry.instrumentation.aiohttp_server import AioHttpServerInstrumentor +from opentelemetry.semconv.trace import SpanAttributes +from opentelemetry.util._importlib_metadata import entry_points + +from opentelemetry.test.globals_test import ( + reset_trace_globals, +) + + +@pytest.fixture(scope="session") +def tracer(): + test_base = TestBase() + + tracer_provider, memory_exporter = test_base.create_tracer_provider() + + reset_trace_globals() + trace_api.set_tracer_provider(tracer_provider) + + yield tracer_provider, memory_exporter + + reset_trace_globals() + + +async def default_handler(request, status=200): + return aiohttp.web.Response(status=status) + + +@pytest_asyncio.fixture +async def server_fixture(tracer, aiohttp_server): + _, memory_exporter = tracer + + AioHttpServerInstrumentor().instrument() + + app = aiohttp.web.Application() + app.add_routes( + [aiohttp.web.get("/test-path", default_handler)]) + + server = await aiohttp_server(app) + yield server, app + + memory_exporter.clear() + + AioHttpServerInstrumentor().uninstrument() + + +def test_checking_instrumentor_pkg_installed(): + + (instrumentor_entrypoint,) = entry_points(group="opentelemetry_instrumentor", name="aiohttp-server") + instrumentor = instrumentor_entrypoint.load()() + assert (isinstance(instrumentor, AioHttpServerInstrumentor)) + + +@pytest.mark.asyncio +@pytest.mark.parametrize("url, expected_method, expected_status_code", [ + ("/test-path", HTTPMethod.GET, HTTPStatus.OK), + ("/not-found", HTTPMethod.GET, HTTPStatus.NOT_FOUND) +]) +async def test_status_code_instrumentation( + tracer, + server_fixture, + aiohttp_client, + url, + expected_method, + expected_status_code +): + _, memory_exporter = tracer + server, app = server_fixture + + assert len(memory_exporter.get_finished_spans()) == 0 + + client = await aiohttp_client(server) + await client.get(url) + + assert len(memory_exporter.get_finished_spans()) == 1 + + [span] = memory_exporter.get_finished_spans() + + assert expected_method.value == span.attributes[SpanAttributes.HTTP_METHOD] + assert expected_status_code == span.attributes[SpanAttributes.HTTP_STATUS_CODE] + + assert f"http://{server.host}:{server.port}{url}" == span.attributes[ + SpanAttributes.HTTP_URL + ] diff --git a/instrumentation/opentelemetry-instrumentation-aiohttp-server/tests/utils.py b/instrumentation/opentelemetry-instrumentation-aiohttp-server/tests/utils.py new file mode 100644 index 0000000000..8fedcb32e3 --- /dev/null +++ b/instrumentation/opentelemetry-instrumentation-aiohttp-server/tests/utils.py @@ -0,0 +1,32 @@ +# Copyright 2020, 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 enum import Enum + + +class HTTPMethod(Enum): + """HTTP methods and descriptions""" + + def __repr__(self): + return f"{self.value}" + + CONNECT = 'CONNECT' + DELETE = 'DELETE' + GET = 'GET' + HEAD = 'HEAD' + OPTIONS = 'OPTIONS' + PATCH = 'PATCH' + POST = 'POST' + PUT = 'PUT' + TRACE = 'TRACE' diff --git a/opentelemetry-contrib-instrumentations/pyproject.toml b/opentelemetry-contrib-instrumentations/pyproject.toml index feb229e384..008e5fb281 100644 --- a/opentelemetry-contrib-instrumentations/pyproject.toml +++ b/opentelemetry-contrib-instrumentations/pyproject.toml @@ -31,6 +31,7 @@ classifiers = [ dependencies = [ "opentelemetry-instrumentation-aio-pika==0.42b0.dev", "opentelemetry-instrumentation-aiohttp-client==0.42b0.dev", + "opentelemetry-instrumentation-aiohttp-server==0.42b0.dev", "opentelemetry-instrumentation-aiopg==0.42b0.dev", "opentelemetry-instrumentation-asgi==0.42b0.dev", "opentelemetry-instrumentation-asyncpg==0.42b0.dev", diff --git a/opentelemetry-instrumentation/src/opentelemetry/instrumentation/bootstrap_gen.py b/opentelemetry-instrumentation/src/opentelemetry/instrumentation/bootstrap_gen.py index 49b044a2a3..eb9eac8762 100644 --- a/opentelemetry-instrumentation/src/opentelemetry/instrumentation/bootstrap_gen.py +++ b/opentelemetry-instrumentation/src/opentelemetry/instrumentation/bootstrap_gen.py @@ -24,6 +24,10 @@ "library": "aiohttp ~= 3.0", "instrumentation": "opentelemetry-instrumentation-aiohttp-client==0.42b0.dev", }, + "aiohttp": { + "library": "aiohttp ~= 3.0", + "instrumentation": "opentelemetry-instrumentation-aiohttp-server==0.42b0.dev", + }, "aiopg": { "library": "aiopg >= 0.13.0, < 2.0.0", "instrumentation": "opentelemetry-instrumentation-aiopg==0.42b0.dev", diff --git a/tox.ini b/tox.ini index aea0b49d4a..2eaef92404 100644 --- a/tox.ini +++ b/tox.ini @@ -29,6 +29,10 @@ envlist = py3{7,8,9,10,11}-test-instrumentation-aiohttp-client pypy3-test-instrumentation-aiohttp-client + ; opentelemetry-instrumentation-aiohttp-server + py3{6,7,8,9,10,11}-test-instrumentation-aiohttp-server + pypy3-test-instrumentation-aiohttp-server + ; opentelemetry-instrumentation-aiopg py3{7,8,9,10,11}-test-instrumentation-aiopg ; instrumentation-aiopg intentionally excluded from pypy3 @@ -305,6 +309,7 @@ changedir = test-opentelemetry-instrumentation: opentelemetry-instrumentation/tests test-instrumentation-aio-pika: instrumentation/opentelemetry-instrumentation-aio-pika/tests test-instrumentation-aiohttp-client: instrumentation/opentelemetry-instrumentation-aiohttp-client/tests + test-instrumentation-aiohttp-server: instrumentation/opentelemetry-instrumentation-aiohttp-server/tests test-instrumentation-aiopg: instrumentation/opentelemetry-instrumentation-aiopg/tests test-instrumentation-asgi: instrumentation/opentelemetry-instrumentation-asgi/tests test-instrumentation-asyncpg: instrumentation/opentelemetry-instrumentation-asyncpg/tests @@ -450,6 +455,8 @@ commands_pre = aiohttp-client: pip install {toxinidir}/instrumentation/opentelemetry-instrumentation-aiohttp-client[test] + aiohttp-server: pip install {toxinidir}/instrumentation/opentelemetry-instrumentation-aiohttp-server[test] + aiopg: pip install {toxinidir}/instrumentation/opentelemetry-instrumentation-dbapi pip install {toxinidir}/instrumentation/opentelemetry-instrumentation-aiopg[test] richconsole: pip install flaky {toxinidir}/exporter/opentelemetry-exporter-richconsole[test] @@ -555,6 +562,7 @@ commands_pre = python -m pip install -e {toxinidir}/instrumentation/opentelemetry-instrumentation-pymemcache[test] python -m pip install -e {toxinidir}/instrumentation/opentelemetry-instrumentation-psycopg2[test] python -m pip install -e {toxinidir}/instrumentation/opentelemetry-instrumentation-aiohttp-client[test] + python -m pip install -e {toxinidir}/instrumentation/opentelemetry-instrumentation-aiohttp-server[test] python -m pip install -e {toxinidir}/instrumentation/opentelemetry-instrumentation-aiopg[test] python -m pip install -e {toxinidir}/instrumentation/opentelemetry-instrumentation-sqlite3[test] python -m pip install -e {toxinidir}/instrumentation/opentelemetry-instrumentation-pyramid[test]