diff --git a/CHANGELOG.md b/CHANGELOG.md index 39c6da1b03..fcf353507b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Add optional distro and configurator selection for auto-instrumentation ([#1823](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1823)) +- Instrument all httpx versions >= 0.18. ([#1748](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1748)) ## Version 1.18.0/0.39b0 (2023-05-10) @@ -41,12 +42,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed +- Fix redis db.statements to be sanitized by default + ([#1778](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1778)) - Fix elasticsearch db.statement attribute to be sanitized by default ([#1758](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1758)) - Fix `AttributeError` when AWS Lambda handler receives a list event ([#1738](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1738)) - Fix `None does not implement middleware` error when there are no middlewares registered ([#1766](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1766)) +- Fix Flask instrumentation to only close the span if it was created by the same request context. + ([#1692](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1692)) ## Version 1.17.0/0.38b0 (2023-03-22) diff --git a/dev-requirements.txt b/dev-requirements.txt index a8efb950dd..8973fb9476 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -14,7 +14,7 @@ bleach==4.1.0 # transient dependency for readme-renderer grpcio-tools==1.29.0 mypy-protobuf>=1.23 protobuf~=3.13 -markupsafe==2.0.1 +markupsafe>=2.0.1 codespell==2.1.0 requests==2.28.1 ruamel.yaml==0.17.21 diff --git a/instrumentation/opentelemetry-instrumentation-aiohttp-client/pyproject.toml b/instrumentation/opentelemetry-instrumentation-aiohttp-client/pyproject.toml index acdc1a2a4d..ea23325caa 100644 --- a/instrumentation/opentelemetry-instrumentation-aiohttp-client/pyproject.toml +++ b/instrumentation/opentelemetry-instrumentation-aiohttp-client/pyproject.toml @@ -38,6 +38,7 @@ instruments = [ ] test = [ "opentelemetry-instrumentation-aiohttp-client[instruments]", + "http-server-mock" ] [project.entry-points.opentelemetry_instrumentor] diff --git a/instrumentation/opentelemetry-instrumentation-aiohttp-client/tests/test_aiohttp_client_integration.py b/instrumentation/opentelemetry-instrumentation-aiohttp-client/tests/test_aiohttp_client_integration.py index d9f76f0239..7c3d7b634d 100644 --- a/instrumentation/opentelemetry-instrumentation-aiohttp-client/tests/test_aiohttp_client_integration.py +++ b/instrumentation/opentelemetry-instrumentation-aiohttp-client/tests/test_aiohttp_client_integration.py @@ -23,6 +23,7 @@ import aiohttp import aiohttp.test_utils import yarl +from http_server_mock import HttpServerMock from pkg_resources import iter_entry_points from opentelemetry import context @@ -313,18 +314,26 @@ async def request_handler(request): def test_credential_removal(self): trace_configs = [aiohttp_client.create_trace_config()] - url = "http://username:password@httpbin.org/status/200" - with self.subTest(url=url): + app = HttpServerMock("test_credential_removal") - async def do_request(url): - async with aiohttp.ClientSession( - trace_configs=trace_configs, - ) as session: - async with session.get(url): - pass + @app.route("/status/200") + def index(): + return "hello" - loop = asyncio.get_event_loop() - loop.run_until_complete(do_request(url)) + url = "http://username:password@localhost:5000/status/200" + + with app.run("localhost", 5000): + with self.subTest(url=url): + + async def do_request(url): + async with aiohttp.ClientSession( + trace_configs=trace_configs, + ) as session: + async with session.get(url): + pass + + loop = asyncio.get_event_loop() + loop.run_until_complete(do_request(url)) self.assert_spans( [ @@ -333,7 +342,9 @@ async def do_request(url): (StatusCode.UNSET, None), { SpanAttributes.HTTP_METHOD: "GET", - SpanAttributes.HTTP_URL: "http://httpbin.org/status/200", + SpanAttributes.HTTP_URL: ( + "http://localhost:5000/status/200" + ), SpanAttributes.HTTP_STATUS_CODE: int(HTTPStatus.OK), }, ) diff --git a/instrumentation/opentelemetry-instrumentation-asgi/tests/test_asgi_middleware.py b/instrumentation/opentelemetry-instrumentation-asgi/tests/test_asgi_middleware.py index bfa5720f99..7cbae79c6d 100644 --- a/instrumentation/opentelemetry-instrumentation-asgi/tests/test_asgi_middleware.py +++ b/instrumentation/opentelemetry-instrumentation-asgi/tests/test_asgi_middleware.py @@ -705,11 +705,11 @@ def test_response_attributes_invalid_status_code(self): self.assertEqual(self.span.set_status.call_count, 1) def test_credential_removal(self): - self.scope["server"] = ("username:password@httpbin.org", 80) + self.scope["server"] = ("username:password@mock", 80) self.scope["path"] = "/status/200" attrs = otel_asgi.collect_request_attributes(self.scope) self.assertEqual( - attrs[SpanAttributes.HTTP_URL], "http://httpbin.org/status/200" + attrs[SpanAttributes.HTTP_URL], "http://mock/status/200" ) def test_collect_target_attribute_missing(self): diff --git a/instrumentation/opentelemetry-instrumentation-flask/pyproject.toml b/instrumentation/opentelemetry-instrumentation-flask/pyproject.toml index 2e6b9d9646..885ca8965a 100644 --- a/instrumentation/opentelemetry-instrumentation-flask/pyproject.toml +++ b/instrumentation/opentelemetry-instrumentation-flask/pyproject.toml @@ -30,6 +30,7 @@ dependencies = [ "opentelemetry-instrumentation-wsgi == 0.40b0.dev", "opentelemetry-semantic-conventions == 0.40b0.dev", "opentelemetry-util-http == 0.40b0.dev", + "packaging >= 21.0", ] [project.optional-dependencies] @@ -38,7 +39,7 @@ instruments = [ ] test = [ "opentelemetry-instrumentation-flask[instruments]", - "markupsafe==2.0.1", + "markupsafe==2.1.2", "opentelemetry-test-utils == 0.40b0.dev", ] diff --git a/instrumentation/opentelemetry-instrumentation-flask/src/opentelemetry/instrumentation/flask/__init__.py b/instrumentation/opentelemetry-instrumentation-flask/src/opentelemetry/instrumentation/flask/__init__.py index fd3c40aab3..73c2f4fe2d 100644 --- a/instrumentation/opentelemetry-instrumentation-flask/src/opentelemetry/instrumentation/flask/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-flask/src/opentelemetry/instrumentation/flask/__init__.py @@ -238,13 +238,14 @@ def response_hook(span: Span, status: str, response_headers: List): API --- """ +import weakref from logging import getLogger -from threading import get_ident from time import time_ns from timeit import default_timer from typing import Collection import flask +from packaging import version as package_version import opentelemetry.instrumentation.wsgi as otel_wsgi from opentelemetry import context, trace @@ -265,11 +266,21 @@ def response_hook(span: Span, status: str, response_headers: List): _ENVIRON_STARTTIME_KEY = "opentelemetry-flask.starttime_key" _ENVIRON_SPAN_KEY = "opentelemetry-flask.span_key" _ENVIRON_ACTIVATION_KEY = "opentelemetry-flask.activation_key" -_ENVIRON_THREAD_ID_KEY = "opentelemetry-flask.thread_id_key" +_ENVIRON_REQCTX_REF_KEY = "opentelemetry-flask.reqctx_ref_key" _ENVIRON_TOKEN = "opentelemetry-flask.token" _excluded_urls_from_env = get_excluded_urls("FLASK") +if package_version.parse(flask.__version__) >= package_version.parse("2.2.0"): + + def _request_ctx_ref() -> weakref.ReferenceType: + return weakref.ref(flask.globals.request_ctx._get_current_object()) + +else: + + def _request_ctx_ref() -> weakref.ReferenceType: + return weakref.ref(flask._request_ctx_stack.top) + def get_default_span_name(): try: @@ -399,7 +410,7 @@ def _before_request(): activation = trace.use_span(span, end_on_exit=True) activation.__enter__() # pylint: disable=E1101 flask_request_environ[_ENVIRON_ACTIVATION_KEY] = activation - flask_request_environ[_ENVIRON_THREAD_ID_KEY] = get_ident() + flask_request_environ[_ENVIRON_REQCTX_REF_KEY] = _request_ctx_ref() flask_request_environ[_ENVIRON_SPAN_KEY] = span flask_request_environ[_ENVIRON_TOKEN] = token @@ -439,17 +450,22 @@ def _teardown_request(exc): return activation = flask.request.environ.get(_ENVIRON_ACTIVATION_KEY) - thread_id = flask.request.environ.get(_ENVIRON_THREAD_ID_KEY) - if not activation or thread_id != get_ident(): + + original_reqctx_ref = flask.request.environ.get( + _ENVIRON_REQCTX_REF_KEY + ) + current_reqctx_ref = _request_ctx_ref() + if not activation or original_reqctx_ref != current_reqctx_ref: # This request didn't start a span, maybe because it was created in # a way that doesn't run `before_request`, like when it is created # with `app.test_request_context`. # - # Similarly, check the thread_id against the current thread to ensure - # tear down only happens on the original thread. This situation can - # arise if the original thread handling the request spawn children - # threads and then uses something like copy_current_request_context - # to copy the request context. + # Similarly, check that the request_ctx that created the span + # matches the current request_ctx, and only tear down if they match. + # This situation can arise if the original request_ctx handling + # the request calls functions that push new request_ctx's, + # like any decorated with `flask.copy_current_request_context`. + return if exc is None: activation.__exit__(None, None, None) diff --git a/instrumentation/opentelemetry-instrumentation-flask/tests/base_test.py b/instrumentation/opentelemetry-instrumentation-flask/tests/base_test.py index a9cc4e55f7..6117521bb9 100644 --- a/instrumentation/opentelemetry-instrumentation-flask/tests/base_test.py +++ b/instrumentation/opentelemetry-instrumentation-flask/tests/base_test.py @@ -19,7 +19,7 @@ from werkzeug.test import Client from werkzeug.wrappers import Response -from opentelemetry import context +from opentelemetry import context, trace class InstrumentationTest: @@ -37,6 +37,21 @@ def _sqlcommenter_endpoint(): ) return sqlcommenter_flask_values + @staticmethod + def _copy_context_endpoint(): + @flask.copy_current_request_context + def _extract_header(): + return flask.request.headers["x-req"] + + # Despite `_extract_header` copying the request context, + # calling it shouldn't detach the parent Flask span's contextvar + request_header = _extract_header() + + return { + "span_name": trace.get_current_span().name, + "request_header": request_header, + } + @staticmethod def _multithreaded_endpoint(count): def do_random_stuff(): @@ -84,6 +99,7 @@ def excluded2_endpoint(): self.app.route("/hello/")(self._hello_endpoint) self.app.route("/sqlcommenter")(self._sqlcommenter_endpoint) self.app.route("/multithreaded")(self._multithreaded_endpoint) + self.app.route("/copy_context")(self._copy_context_endpoint) self.app.route("/excluded/")(self._hello_endpoint) self.app.route("/excluded")(excluded_endpoint) self.app.route("/excluded2")(excluded2_endpoint) diff --git a/instrumentation/opentelemetry-instrumentation-flask/tests/test_copy_context.py b/instrumentation/opentelemetry-instrumentation-flask/tests/test_copy_context.py new file mode 100644 index 0000000000..96268de5e7 --- /dev/null +++ b/instrumentation/opentelemetry-instrumentation-flask/tests/test_copy_context.py @@ -0,0 +1,48 @@ +# 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. +import flask +from werkzeug.test import Client +from werkzeug.wrappers import Response + +from opentelemetry.instrumentation.flask import FlaskInstrumentor +from opentelemetry.test.wsgitestutil import WsgiTestBase + +from .base_test import InstrumentationTest + + +class TestCopyContext(InstrumentationTest, WsgiTestBase): + def setUp(self): + super().setUp() + FlaskInstrumentor().instrument() + self.app = flask.Flask(__name__) + self._common_initialization() + + def tearDown(self): + super().tearDown() + with self.disable_logging(): + FlaskInstrumentor().uninstrument() + + def test_copycontext(self): + """Test that instrumentation tear down does not blow up + when the request calls functions where the context has been + copied via `flask.copy_current_request_context` + """ + self.app = flask.Flask(__name__) + self.app.route("/copy_context")(self._copy_context_endpoint) + client = Client(self.app, Response) + resp = client.get("/copy_context", headers={"x-req": "a-header"}) + + self.assertEqual(200, resp.status_code) + self.assertEqual("/copy_context", resp.json["span_name"]) + self.assertEqual("a-header", resp.json["request_header"]) diff --git a/instrumentation/opentelemetry-instrumentation-httpx/README.rst b/instrumentation/opentelemetry-instrumentation-httpx/README.rst index ffa86cb4bc..1e03eb128e 100644 --- a/instrumentation/opentelemetry-instrumentation-httpx/README.rst +++ b/instrumentation/opentelemetry-instrumentation-httpx/README.rst @@ -30,7 +30,7 @@ When using the instrumentor, all clients will automatically trace requests. import httpx from opentelemetry.instrumentation.httpx import HTTPXClientInstrumentor - url = "https://httpbin.org/get" + url = "https://some.url/get" HTTPXClientInstrumentor().instrument() with httpx.Client() as client: @@ -51,7 +51,7 @@ use the `instrument_client` method. import httpx from opentelemetry.instrumentation.httpx import HTTPXClientInstrumentor - url = "https://httpbin.org/get" + url = "https://some.url/get" with httpx.Client(transport=telemetry_transport) as client: HTTPXClientInstrumentor.instrument_client(client) @@ -96,7 +96,7 @@ If you don't want to use the instrumentor class, you can use the transport class SyncOpenTelemetryTransport, ) - url = "https://httpbin.org/get" + url = "https://some.url/get" transport = httpx.HTTPTransport() telemetry_transport = SyncOpenTelemetryTransport(transport) diff --git a/instrumentation/opentelemetry-instrumentation-httpx/pyproject.toml b/instrumentation/opentelemetry-instrumentation-httpx/pyproject.toml index 229fca1611..d079de20b3 100644 --- a/instrumentation/opentelemetry-instrumentation-httpx/pyproject.toml +++ b/instrumentation/opentelemetry-instrumentation-httpx/pyproject.toml @@ -32,7 +32,7 @@ dependencies = [ [project.optional-dependencies] instruments = [ - "httpx >= 0.18.0, <= 0.23.0", + "httpx >= 0.18.0", ] test = [ "opentelemetry-instrumentation-httpx[instruments]", diff --git a/instrumentation/opentelemetry-instrumentation-httpx/src/opentelemetry/instrumentation/httpx/__init__.py b/instrumentation/opentelemetry-instrumentation-httpx/src/opentelemetry/instrumentation/httpx/__init__.py index b603cbcdd6..736e6c3d32 100644 --- a/instrumentation/opentelemetry-instrumentation-httpx/src/opentelemetry/instrumentation/httpx/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-httpx/src/opentelemetry/instrumentation/httpx/__init__.py @@ -25,7 +25,7 @@ import httpx from opentelemetry.instrumentation.httpx import HTTPXClientInstrumentor - url = "https://httpbin.org/get" + url = "https://some.url/get" HTTPXClientInstrumentor().instrument() with httpx.Client() as client: @@ -46,7 +46,7 @@ import httpx from opentelemetry.instrumentation.httpx import HTTPXClientInstrumentor - url = "https://httpbin.org/get" + url = "https://some.url/get" with httpx.Client(transport=telemetry_transport) as client: HTTPXClientInstrumentor.instrument_client(client) @@ -91,7 +91,7 @@ SyncOpenTelemetryTransport, ) - url = "https://httpbin.org/get" + url = "https://some.url/get" transport = httpx.HTTPTransport() telemetry_transport = SyncOpenTelemetryTransport(transport) diff --git a/instrumentation/opentelemetry-instrumentation-httpx/tests/test_httpx_integration.py b/instrumentation/opentelemetry-instrumentation-httpx/tests/test_httpx_integration.py index 3cac4c45a7..86c9a56ab2 100644 --- a/instrumentation/opentelemetry-instrumentation-httpx/tests/test_httpx_integration.py +++ b/instrumentation/opentelemetry-instrumentation-httpx/tests/test_httpx_integration.py @@ -59,7 +59,7 @@ def _async_call(coro: typing.Coroutine) -> asyncio.Task: def _response_hook(span, request: "RequestInfo", response: "ResponseInfo"): span.set_attribute( HTTP_RESPONSE_BODY, - response[2].read(), + b"".join(response[2]), ) @@ -68,7 +68,7 @@ async def _async_response_hook( ): span.set_attribute( HTTP_RESPONSE_BODY, - await response[2].aread(), + b"".join([part async for part in response[2]]), ) @@ -97,7 +97,7 @@ class BaseTestCases: class BaseTest(TestBase, metaclass=abc.ABCMeta): # pylint: disable=no-member - URL = "http://httpbin.org/status/200" + URL = "http://mock/status/200" response_hook = staticmethod(_response_hook) request_hook = staticmethod(_request_hook) no_update_request_hook = staticmethod(_no_update_request_hook) @@ -165,7 +165,7 @@ def test_basic_multiple(self): self.assert_span(num_spans=2) def test_not_foundbasic(self): - url_404 = "http://httpbin.org/status/404" + url_404 = "http://mock/status/404" with respx.mock: respx.get(url_404).mock(httpx.Response(404)) diff --git a/instrumentation/opentelemetry-instrumentation-pyramid/tests/pyramid_base_test.py b/instrumentation/opentelemetry-instrumentation-pyramid/tests/pyramid_base_test.py index f5dd9fd7d7..c6b9faa196 100644 --- a/instrumentation/opentelemetry-instrumentation-pyramid/tests/pyramid_base_test.py +++ b/instrumentation/opentelemetry-instrumentation-pyramid/tests/pyramid_base_test.py @@ -15,7 +15,11 @@ import pyramid.httpexceptions as exc from pyramid.response import Response from werkzeug.test import Client -from werkzeug.wrappers import BaseResponse + +# opentelemetry-instrumentation-pyramid uses werkzeug==0.16.1 which has +# werkzeug.wrappers.BaseResponse. This is not the case for newer versions of +# werkzeug like the one lint uses. +from werkzeug.wrappers import BaseResponse # pylint: disable=no-name-in-module class InstrumentationTest: diff --git a/instrumentation/opentelemetry-instrumentation-redis/src/opentelemetry/instrumentation/redis/__init__.py b/instrumentation/opentelemetry-instrumentation-redis/src/opentelemetry/instrumentation/redis/__init__.py index c1068bda27..188840c7b8 100644 --- a/instrumentation/opentelemetry-instrumentation-redis/src/opentelemetry/instrumentation/redis/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-redis/src/opentelemetry/instrumentation/redis/__init__.py @@ -64,8 +64,6 @@ async def redis_get(): response_hook (Callable) - a function with extra user-defined logic to be performed after performing the request this function signature is: def response_hook(span: Span, instance: redis.connection.Connection, response) -> None -sanitize_query (Boolean) - default False, enable the Redis query sanitization - for example: .. code: python @@ -88,27 +86,11 @@ def response_hook(span, instance, response): client = redis.StrictRedis(host="localhost", port=6379) client.get("my-key") -Configuration -------------- - -Query sanitization -****************** -To enable query sanitization with an environment variable, set -``OTEL_PYTHON_INSTRUMENTATION_SANITIZE_REDIS`` to "true". - -For example, - -:: - - export OTEL_PYTHON_INSTRUMENTATION_SANITIZE_REDIS="true" - -will result in traced queries like "SET ? ?". API --- """ import typing -from os import environ from typing import Any, Collection import redis @@ -116,9 +98,6 @@ def response_hook(span, instance, response): from opentelemetry import trace from opentelemetry.instrumentation.instrumentor import BaseInstrumentor -from opentelemetry.instrumentation.redis.environment_variables import ( - OTEL_PYTHON_INSTRUMENTATION_SANITIZE_REDIS, -) from opentelemetry.instrumentation.redis.package import _instruments from opentelemetry.instrumentation.redis.util import ( _extract_conn_attributes, @@ -161,10 +140,9 @@ def _instrument( tracer, request_hook: _RequestHookT = None, response_hook: _ResponseHookT = None, - sanitize_query: bool = False, ): def _traced_execute_command(func, instance, args, kwargs): - query = _format_command_args(args, sanitize_query) + query = _format_command_args(args) if len(args) > 0 and args[0]: name = args[0] @@ -194,7 +172,7 @@ def _traced_execute_pipeline(func, instance, args, kwargs): cmds = [ _format_command_args( - c.args if hasattr(c, "args") else c[0], sanitize_query + c.args if hasattr(c, "args") else c[0], ) for c in command_stack ] @@ -307,15 +285,6 @@ def _instrument(self, **kwargs): tracer, request_hook=kwargs.get("request_hook"), response_hook=kwargs.get("response_hook"), - sanitize_query=kwargs.get( - "sanitize_query", - environ.get( - OTEL_PYTHON_INSTRUMENTATION_SANITIZE_REDIS, "false" - ) - .lower() - .strip() - == "true", - ), ) def _uninstrument(self, **kwargs): diff --git a/instrumentation/opentelemetry-instrumentation-redis/src/opentelemetry/instrumentation/redis/environment_variables.py b/instrumentation/opentelemetry-instrumentation-redis/src/opentelemetry/instrumentation/redis/environment_variables.py deleted file mode 100644 index 750b97445e..0000000000 --- a/instrumentation/opentelemetry-instrumentation-redis/src/opentelemetry/instrumentation/redis/environment_variables.py +++ /dev/null @@ -1,17 +0,0 @@ -# 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. - -OTEL_PYTHON_INSTRUMENTATION_SANITIZE_REDIS = ( - "OTEL_PYTHON_INSTRUMENTATION_SANITIZE_REDIS" -) diff --git a/instrumentation/opentelemetry-instrumentation-redis/src/opentelemetry/instrumentation/redis/util.py b/instrumentation/opentelemetry-instrumentation-redis/src/opentelemetry/instrumentation/redis/util.py index 1eadaba718..b24f9b2655 100644 --- a/instrumentation/opentelemetry-instrumentation-redis/src/opentelemetry/instrumentation/redis/util.py +++ b/instrumentation/opentelemetry-instrumentation-redis/src/opentelemetry/instrumentation/redis/util.py @@ -48,41 +48,23 @@ def _extract_conn_attributes(conn_kwargs): return attributes -def _format_command_args(args, sanitize_query): +def _format_command_args(args): """Format and sanitize command arguments, and trim them as needed""" cmd_max_len = 1000 value_too_long_mark = "..." - if sanitize_query: - # Sanitized query format: "COMMAND ? ?" - args_length = len(args) - if args_length > 0: - out = [str(args[0])] + ["?"] * (args_length - 1) - out_str = " ".join(out) - if len(out_str) > cmd_max_len: - out_str = ( - out_str[: cmd_max_len - len(value_too_long_mark)] - + value_too_long_mark - ) - else: - out_str = "" - return out_str + # Sanitized query format: "COMMAND ? ?" + args_length = len(args) + if args_length > 0: + out = [str(args[0])] + ["?"] * (args_length - 1) + out_str = " ".join(out) - value_max_len = 100 - length = 0 - out = [] - for arg in args: - cmd = str(arg) + if len(out_str) > cmd_max_len: + out_str = ( + out_str[: cmd_max_len - len(value_too_long_mark)] + + value_too_long_mark + ) + else: + out_str = "" - if len(cmd) > value_max_len: - cmd = cmd[:value_max_len] + value_too_long_mark - - if length + len(cmd) > cmd_max_len: - prefix = cmd[: cmd_max_len - length] - out.append(f"{prefix}{value_too_long_mark}") - break - - out.append(cmd) - length += len(cmd) - - return " ".join(out) + return out_str diff --git a/instrumentation/opentelemetry-instrumentation-redis/tests/test_redis.py b/instrumentation/opentelemetry-instrumentation-redis/tests/test_redis.py index 56a0df6a0a..cc6e7de75a 100644 --- a/instrumentation/opentelemetry-instrumentation-redis/tests/test_redis.py +++ b/instrumentation/opentelemetry-instrumentation-redis/tests/test_redis.py @@ -168,22 +168,11 @@ def test_query_sanitizer_enabled(self): span = spans[0] self.assertEqual(span.attributes.get("db.statement"), "SET ? ?") - def test_query_sanitizer_enabled_env(self): + def test_query_sanitizer(self): redis_client = redis.Redis() connection = redis.connection.Connection() redis_client.connection = connection - RedisInstrumentor().uninstrument() - - env_patch = mock.patch.dict( - "os.environ", - {"OTEL_PYTHON_INSTRUMENTATION_SANITIZE_REDIS": "true"}, - ) - env_patch.start() - RedisInstrumentor().instrument( - tracer_provider=self.tracer_provider, - ) - with mock.patch.object(redis_client, "connection"): redis_client.set("key", "value") @@ -192,21 +181,6 @@ def test_query_sanitizer_enabled_env(self): span = spans[0] self.assertEqual(span.attributes.get("db.statement"), "SET ? ?") - env_patch.stop() - - def test_query_sanitizer_disabled(self): - redis_client = redis.Redis() - connection = redis.connection.Connection() - redis_client.connection = connection - - with mock.patch.object(redis_client, "connection"): - redis_client.set("key", "value") - - spans = self.memory_exporter.get_finished_spans() - self.assertEqual(len(spans), 1) - - span = spans[0] - self.assertEqual(span.attributes.get("db.statement"), "SET key value") def test_no_op_tracer_provider(self): RedisInstrumentor().uninstrument() diff --git a/instrumentation/opentelemetry-instrumentation-requests/tests/test_requests_integration.py b/instrumentation/opentelemetry-instrumentation-requests/tests/test_requests_integration.py index 8d6ee7c04d..6e6a68cb8f 100644 --- a/instrumentation/opentelemetry-instrumentation-requests/tests/test_requests_integration.py +++ b/instrumentation/opentelemetry-instrumentation-requests/tests/test_requests_integration.py @@ -63,7 +63,7 @@ class RequestsIntegrationTestBase(abc.ABC): # pylint: disable=no-member # pylint: disable=too-many-public-methods - URL = "http://httpbin.org/status/200" + URL = "http://mock/status/200" # pylint: disable=invalid-name def setUp(self): @@ -152,7 +152,7 @@ def response_hook(span, request_obj, response): self.assertEqual(span.attributes["response_hook_attr"], "value") def test_excluded_urls_explicit(self): - url_404 = "http://httpbin.org/status/404" + url_404 = "http://mock/status/404" httpretty.register_uri( httpretty.GET, url_404, @@ -194,7 +194,7 @@ def name_callback(method, url): self.assertEqual(span.name, "HTTP GET") def test_not_foundbasic(self): - url_404 = "http://httpbin.org/status/404" + url_404 = "http://mock/status/404" httpretty.register_uri( httpretty.GET, url_404, @@ -460,7 +460,7 @@ def perform_request(url: str, session: requests.Session = None): return session.get(url) def test_credential_removal(self): - new_url = "http://username:password@httpbin.org/status/200" + new_url = "http://username:password@mock/status/200" self.perform_request(new_url) span = self.assert_span() diff --git a/instrumentation/opentelemetry-instrumentation-tornado/pyproject.toml b/instrumentation/opentelemetry-instrumentation-tornado/pyproject.toml index a16554af74..c0553eb6c0 100644 --- a/instrumentation/opentelemetry-instrumentation-tornado/pyproject.toml +++ b/instrumentation/opentelemetry-instrumentation-tornado/pyproject.toml @@ -37,6 +37,7 @@ instruments = [ test = [ "opentelemetry-instrumentation-tornado[instruments]", "opentelemetry-test-utils == 0.40b0.dev", + "http-server-mock" ] [project.entry-points.opentelemetry_instrumentor] diff --git a/instrumentation/opentelemetry-instrumentation-tornado/tests/test_instrumentation.py b/instrumentation/opentelemetry-instrumentation-tornado/tests/test_instrumentation.py index 9fb3608572..c875a331ef 100644 --- a/instrumentation/opentelemetry-instrumentation-tornado/tests/test_instrumentation.py +++ b/instrumentation/opentelemetry-instrumentation-tornado/tests/test_instrumentation.py @@ -15,6 +15,7 @@ from unittest.mock import Mock, patch +from http_server_mock import HttpServerMock from tornado.testing import AsyncHTTPTestCase from opentelemetry import trace @@ -494,32 +495,35 @@ def test_response_headers(self): self.memory_exporter.clear() set_global_response_propagator(orig) - # todo(srikanthccv): fix this test - # this test is making request to real httpbin.org/status/200 which - # is not a good idea as it can fail due to availability of the - # service. - # def test_credential_removal(self): - # response = self.fetch( - # "http://username:password@httpbin.org/status/200" - # ) - # self.assertEqual(response.code, 200) - - # spans = self.sorted_spans(self.memory_exporter.get_finished_spans()) - # self.assertEqual(len(spans), 1) - # client = spans[0] - - # self.assertEqual(client.name, "GET") - # self.assertEqual(client.kind, SpanKind.CLIENT) - # self.assertSpanHasAttributes( - # client, - # { - # SpanAttributes.HTTP_URL: "http://httpbin.org/status/200", - # SpanAttributes.HTTP_METHOD: "GET", - # SpanAttributes.HTTP_STATUS_CODE: 200, - # }, - # ) - - # self.memory_exporter.clear() + def test_credential_removal(self): + app = HttpServerMock("test_credential_removal") + + @app.route("/status/200") + def index(): + return "hello" + + with app.run("localhost", 5000): + response = self.fetch( + "http://username:password@localhost:5000/status/200" + ) + self.assertEqual(response.code, 200) + + spans = self.sorted_spans(self.memory_exporter.get_finished_spans()) + self.assertEqual(len(spans), 1) + client = spans[0] + + self.assertEqual(client.name, "GET") + self.assertEqual(client.kind, SpanKind.CLIENT) + self.assertSpanHasAttributes( + client, + { + SpanAttributes.HTTP_URL: "http://localhost:5000/status/200", + SpanAttributes.HTTP_METHOD: "GET", + SpanAttributes.HTTP_STATUS_CODE: 200, + }, + ) + + self.memory_exporter.clear() class TestTornadoInstrumentationWithXHeaders(TornadoTest): diff --git a/instrumentation/opentelemetry-instrumentation-urllib/tests/test_metrics_instrumentation.py b/instrumentation/opentelemetry-instrumentation-urllib/tests/test_metrics_instrumentation.py index c9417fc67b..f56aa4f97d 100644 --- a/instrumentation/opentelemetry-instrumentation-urllib/tests/test_metrics_instrumentation.py +++ b/instrumentation/opentelemetry-instrumentation-urllib/tests/test_metrics_instrumentation.py @@ -27,8 +27,8 @@ class TestUrllibMetricsInstrumentation(TestBase): - URL = "http://httpbin.org/status/200" - URL_POST = "http://httpbin.org/post" + URL = "http://mock/status/200" + URL_POST = "http://mock/post" def setUp(self): super().setUp() diff --git a/instrumentation/opentelemetry-instrumentation-urllib/tests/test_urllib_integration.py b/instrumentation/opentelemetry-instrumentation-urllib/tests/test_urllib_integration.py index 9937d42176..35e9e1f1c7 100644 --- a/instrumentation/opentelemetry-instrumentation-urllib/tests/test_urllib_integration.py +++ b/instrumentation/opentelemetry-instrumentation-urllib/tests/test_urllib_integration.py @@ -46,9 +46,9 @@ class RequestsIntegrationTestBase(abc.ABC): # pylint: disable=no-member - URL = "http://httpbin.org/status/200" - URL_TIMEOUT = "http://httpbin.org/timeout/0" - URL_EXCEPTION = "http://httpbin.org/exception/0" + URL = "http://mock/status/200" + URL_TIMEOUT = "http://mock/timeout/0" + URL_EXCEPTION = "http://mock/exception/0" # pylint: disable=invalid-name def setUp(self): @@ -83,7 +83,7 @@ def setUp(self): ) httpretty.register_uri( httpretty.GET, - "http://httpbin.org/status/500", + "http://mock/status/500", status=500, ) @@ -142,7 +142,7 @@ def test_basic(self): ) def test_excluded_urls_explicit(self): - url_201 = "http://httpbin.org/status/201" + url_201 = "http://mock/status/201" httpretty.register_uri( httpretty.GET, url_201, @@ -172,7 +172,7 @@ def test_excluded_urls_from_env(self): self.assert_span(num_spans=1) def test_not_foundbasic(self): - url_404 = "http://httpbin.org/status/404/" + url_404 = "http://mock/status/404/" httpretty.register_uri( httpretty.GET, url_404, @@ -336,14 +336,14 @@ def test_custom_tracer_provider(self): def test_requests_exception_with_response(self, *_, **__): with self.assertRaises(HTTPError): - self.perform_request("http://httpbin.org/status/500") + self.perform_request("http://mock/status/500") span = self.assert_span() self.assertEqual( dict(span.attributes), { SpanAttributes.HTTP_METHOD: "GET", - SpanAttributes.HTTP_URL: "http://httpbin.org/status/500", + SpanAttributes.HTTP_URL: "http://mock/status/500", SpanAttributes.HTTP_STATUS_CODE: 500, }, ) @@ -365,7 +365,7 @@ def test_requests_timeout_exception(self, *_, **__): self.assertEqual(span.status.status_code, StatusCode.ERROR) def test_credential_removal(self): - url = "http://username:password@httpbin.org/status/200" + url = "http://username:password@mock/status/200" with self.assertRaises(Exception): self.perform_request(url) diff --git a/instrumentation/opentelemetry-instrumentation-urllib3/tests/test_urllib3_integration.py b/instrumentation/opentelemetry-instrumentation-urllib3/tests/test_urllib3_integration.py index ae59d57c51..315cec1112 100644 --- a/instrumentation/opentelemetry-instrumentation-urllib3/tests/test_urllib3_integration.py +++ b/instrumentation/opentelemetry-instrumentation-urllib3/tests/test_urllib3_integration.py @@ -35,8 +35,8 @@ class TestURLLib3Instrumentor(TestBase): - HTTP_URL = "http://httpbin.org/status/200" - HTTPS_URL = "https://httpbin.org/status/200" + HTTP_URL = "http://mock/status/200" + HTTPS_URL = "https://mock/status/200" def setUp(self): super().setUp() @@ -123,7 +123,7 @@ def test_basic_http_success(self): self.assert_success_span(response, self.HTTP_URL) def test_basic_http_success_using_connection_pool(self): - pool = urllib3.HTTPConnectionPool("httpbin.org") + pool = urllib3.HTTPConnectionPool("mock") response = pool.request("GET", "/status/200") self.assert_success_span(response, self.HTTP_URL) @@ -133,13 +133,13 @@ def test_basic_https_success(self): self.assert_success_span(response, self.HTTPS_URL) def test_basic_https_success_using_connection_pool(self): - pool = urllib3.HTTPSConnectionPool("httpbin.org") + pool = urllib3.HTTPSConnectionPool("mock") response = pool.request("GET", "/status/200") self.assert_success_span(response, self.HTTPS_URL) def test_basic_not_found(self): - url_404 = "http://httpbin.org/status/404" + url_404 = "http://mock/status/404" httpretty.register_uri(httpretty.GET, url_404, status=404) response = self.perform_request(url_404) @@ -152,30 +152,30 @@ def test_basic_not_found(self): self.assertIs(trace.status.StatusCode.ERROR, span.status.status_code) def test_basic_http_non_default_port(self): - url = "http://httpbin.org:666/status/200" + url = "http://mock:666/status/200" httpretty.register_uri(httpretty.GET, url, body="Hello!") response = self.perform_request(url) self.assert_success_span(response, url) def test_basic_http_absolute_url(self): - url = "http://httpbin.org:666/status/200" + url = "http://mock:666/status/200" httpretty.register_uri(httpretty.GET, url, body="Hello!") - pool = urllib3.HTTPConnectionPool("httpbin.org", port=666) + pool = urllib3.HTTPConnectionPool("mock", port=666) response = pool.request("GET", url) self.assert_success_span(response, url) def test_url_open_explicit_arg_parameters(self): - url = "http://httpbin.org:666/status/200" + url = "http://mock:666/status/200" httpretty.register_uri(httpretty.GET, url, body="Hello!") - pool = urllib3.HTTPConnectionPool("httpbin.org", port=666) + pool = urllib3.HTTPConnectionPool("mock", port=666) response = pool.urlopen(method="GET", url="/status/200") self.assert_success_span(response, url) def test_excluded_urls_explicit(self): - url_201 = "http://httpbin.org/status/201" + url_201 = "http://mock/status/201" httpretty.register_uri( httpretty.GET, url_201, @@ -301,7 +301,7 @@ def url_filter(url): self.assert_success_span(response, self.HTTP_URL) def test_credential_removal(self): - url = "http://username:password@httpbin.org/status/200" + url = "http://username:password@mock/status/200" response = self.perform_request(url) self.assert_success_span(response, self.HTTP_URL) @@ -339,7 +339,7 @@ def request_hook(span, request, headers, body): headers = {"header1": "value1", "header2": "value2"} body = "param1=1¶m2=2" - pool = urllib3.HTTPConnectionPool("httpbin.org") + pool = urllib3.HTTPConnectionPool("mock") response = pool.request( "POST", "/status/200", body=body, headers=headers ) @@ -366,7 +366,7 @@ def request_hook(span, request, headers, body): body = "param1=1¶m2=2" - pool = urllib3.HTTPConnectionPool("httpbin.org") + pool = urllib3.HTTPConnectionPool("mock") response = pool.urlopen("POST", "/status/200", body) self.assertEqual(b"Hello!", response.data) diff --git a/instrumentation/opentelemetry-instrumentation-urllib3/tests/test_urllib3_metrics.py b/instrumentation/opentelemetry-instrumentation-urllib3/tests/test_urllib3_metrics.py index ca691ebd47..6bf61a9fd8 100644 --- a/instrumentation/opentelemetry-instrumentation-urllib3/tests/test_urllib3_metrics.py +++ b/instrumentation/opentelemetry-instrumentation-urllib3/tests/test_urllib3_metrics.py @@ -26,7 +26,7 @@ class TestURLLib3InstrumentorMetric(HttpTestBase, TestBase): - HTTP_URL = "http://httpbin.org/status/200" + HTTP_URL = "http://mock/status/200" def setUp(self): super().setUp() @@ -68,11 +68,11 @@ def test_basic_metrics(self): min_data_point=client_duration_estimated, attributes={ "http.flavor": "1.1", - "http.host": "httpbin.org", + "http.host": "mock", "http.method": "GET", "http.scheme": "http", "http.status_code": 200, - "net.peer.name": "httpbin.org", + "net.peer.name": "mock", "net.peer.port": 80, }, ) @@ -91,11 +91,11 @@ def test_basic_metrics(self): min_data_point=0, attributes={ "http.flavor": "1.1", - "http.host": "httpbin.org", + "http.host": "mock", "http.method": "GET", "http.scheme": "http", "http.status_code": 200, - "net.peer.name": "httpbin.org", + "net.peer.name": "mock", "net.peer.port": 80, }, ) @@ -116,11 +116,11 @@ def test_basic_metrics(self): min_data_point=expected_size, attributes={ "http.flavor": "1.1", - "http.host": "httpbin.org", + "http.host": "mock", "http.method": "GET", "http.scheme": "http", "http.status_code": 200, - "net.peer.name": "httpbin.org", + "net.peer.name": "mock", "net.peer.port": 80, }, ) @@ -144,11 +144,11 @@ def test_str_request_body_size_metrics(self): min_data_point=6, attributes={ "http.flavor": "1.1", - "http.host": "httpbin.org", + "http.host": "mock", "http.method": "POST", "http.scheme": "http", "http.status_code": 200, - "net.peer.name": "httpbin.org", + "net.peer.name": "mock", "net.peer.port": 80, }, ) @@ -172,11 +172,11 @@ def test_bytes_request_body_size_metrics(self): min_data_point=6, attributes={ "http.flavor": "1.1", - "http.host": "httpbin.org", + "http.host": "mock", "http.method": "POST", "http.scheme": "http", "http.status_code": 200, - "net.peer.name": "httpbin.org", + "net.peer.name": "mock", "net.peer.port": 80, }, ) @@ -201,11 +201,11 @@ def test_fields_request_body_size_metrics(self): min_data_point=expected_value, attributes={ "http.flavor": "1.1", - "http.host": "httpbin.org", + "http.host": "mock", "http.method": "POST", "http.scheme": "http", "http.status_code": 200, - "net.peer.name": "httpbin.org", + "net.peer.name": "mock", "net.peer.port": 80, }, ) @@ -229,11 +229,11 @@ def test_bytesio_request_body_size_metrics(self): min_data_point=6, attributes={ "http.flavor": "1.1", - "http.host": "httpbin.org", + "http.host": "mock", "http.method": "POST", "http.scheme": "http", "http.status_code": 200, - "net.peer.name": "httpbin.org", + "net.peer.name": "mock", "net.peer.port": 80, }, ) diff --git a/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py b/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py index ffe2982052..926124caf3 100644 --- a/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py +++ b/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py @@ -437,10 +437,10 @@ def test_response_attributes(self): self.span.set_attribute.assert_has_calls(expected, any_order=True) def test_credential_removal(self): - self.environ["HTTP_HOST"] = "username:password@httpbin.com" + self.environ["HTTP_HOST"] = "username:password@mock" self.environ["PATH_INFO"] = "/status/200" expected = { - SpanAttributes.HTTP_URL: "http://httpbin.com/status/200", + SpanAttributes.HTTP_URL: "http://mock/status/200", SpanAttributes.NET_HOST_PORT: 80, } self.assertGreaterEqual( diff --git a/opentelemetry-instrumentation/src/opentelemetry/instrumentation/bootstrap_gen.py b/opentelemetry-instrumentation/src/opentelemetry/instrumentation/bootstrap_gen.py index 8200196ca8..f705324289 100644 --- a/opentelemetry-instrumentation/src/opentelemetry/instrumentation/bootstrap_gen.py +++ b/opentelemetry-instrumentation/src/opentelemetry/instrumentation/bootstrap_gen.py @@ -81,7 +81,7 @@ "instrumentation": "opentelemetry-instrumentation-grpc==0.40b0.dev", }, "httpx": { - "library": "httpx >= 0.18.0, <= 0.23.0", + "library": "httpx >= 0.18.0", "instrumentation": "opentelemetry-instrumentation-httpx==0.40b0.dev", }, "jinja2": { diff --git a/tests/opentelemetry-docker-tests/tests/celery/test_celery_functional.py b/tests/opentelemetry-docker-tests/tests/celery/test_celery_functional.py index 1a86154ffc..f284272c5d 100644 --- a/tests/opentelemetry-docker-tests/tests/celery/test_celery_functional.py +++ b/tests/opentelemetry-docker-tests/tests/celery/test_celery_functional.py @@ -303,8 +303,8 @@ def fn_exception(): span = spans[0] - assert span.status.is_ok is True - assert span.status.status_code == StatusCode.UNSET + assert span.status.is_ok is False + assert span.status.status_code == StatusCode.ERROR assert span.name == "run/test_celery_functional.fn_exception" assert span.attributes.get("celery.action") == "run" assert span.attributes.get("celery.state") == "FAILURE" @@ -443,8 +443,8 @@ def run(self): span = spans[0] - assert span.status.is_ok is True - assert span.status.status_code == StatusCode.UNSET + assert span.status.is_ok is False + assert span.status.status_code == StatusCode.ERROR assert span.name == "run/test_celery_functional.BaseTask" assert span.attributes.get("celery.action") == "run" assert span.attributes.get("celery.state") == "FAILURE" diff --git a/tests/opentelemetry-docker-tests/tests/redis/test_redis_functional.py b/tests/opentelemetry-docker-tests/tests/redis/test_redis_functional.py index 675a37fa9f..dc9cf8b1dc 100644 --- a/tests/opentelemetry-docker-tests/tests/redis/test_redis_functional.py +++ b/tests/opentelemetry-docker-tests/tests/redis/test_redis_functional.py @@ -47,9 +47,7 @@ def _check_span(self, span, name): def test_long_command_sanitized(self): RedisInstrumentor().uninstrument() - RedisInstrumentor().instrument( - tracer_provider=self.tracer_provider, sanitize_query=True - ) + RedisInstrumentor().instrument(tracer_provider=self.tracer_provider) self.redis_client.mget(*range(2000)) @@ -75,7 +73,7 @@ def test_long_command(self): self._check_span(span, "MGET") self.assertTrue( span.attributes.get(SpanAttributes.DB_STATEMENT).startswith( - "MGET 0 1 2 3" + "MGET ? ? ? ?" ) ) self.assertTrue( @@ -84,9 +82,7 @@ def test_long_command(self): def test_basics_sanitized(self): RedisInstrumentor().uninstrument() - RedisInstrumentor().instrument( - tracer_provider=self.tracer_provider, sanitize_query=True - ) + RedisInstrumentor().instrument(tracer_provider=self.tracer_provider) self.assertIsNone(self.redis_client.get("cheese")) spans = self.memory_exporter.get_finished_spans() @@ -105,15 +101,13 @@ def test_basics(self): span = spans[0] self._check_span(span, "GET") self.assertEqual( - span.attributes.get(SpanAttributes.DB_STATEMENT), "GET cheese" + span.attributes.get(SpanAttributes.DB_STATEMENT), "GET ?" ) self.assertEqual(span.attributes.get("db.redis.args_length"), 2) def test_pipeline_traced_sanitized(self): RedisInstrumentor().uninstrument() - RedisInstrumentor().instrument( - tracer_provider=self.tracer_provider, sanitize_query=True - ) + RedisInstrumentor().instrument(tracer_provider=self.tracer_provider) with self.redis_client.pipeline(transaction=False) as pipeline: pipeline.set("blah", 32) @@ -144,15 +138,13 @@ def test_pipeline_traced(self): self._check_span(span, "SET RPUSH HGETALL") self.assertEqual( span.attributes.get(SpanAttributes.DB_STATEMENT), - "SET blah 32\nRPUSH foo éé\nHGETALL xxx", + "SET ? ?\nRPUSH ? ?\nHGETALL ?", ) self.assertEqual(span.attributes.get("db.redis.pipeline_length"), 3) def test_pipeline_immediate_sanitized(self): RedisInstrumentor().uninstrument() - RedisInstrumentor().instrument( - tracer_provider=self.tracer_provider, sanitize_query=True - ) + RedisInstrumentor().instrument(tracer_provider=self.tracer_provider) with self.redis_client.pipeline() as pipeline: pipeline.set("a", 1) @@ -182,7 +174,7 @@ def test_pipeline_immediate(self): span = spans[0] self._check_span(span, "SET") self.assertEqual( - span.attributes.get(SpanAttributes.DB_STATEMENT), "SET b 2" + span.attributes.get(SpanAttributes.DB_STATEMENT), "SET ? ?" ) def test_parent(self): @@ -230,7 +222,7 @@ def test_basics(self): span = spans[0] self._check_span(span, "GET") self.assertEqual( - span.attributes.get(SpanAttributes.DB_STATEMENT), "GET cheese" + span.attributes.get(SpanAttributes.DB_STATEMENT), "GET ?" ) self.assertEqual(span.attributes.get("db.redis.args_length"), 2) @@ -247,7 +239,7 @@ def test_pipeline_traced(self): self._check_span(span, "SET RPUSH HGETALL") self.assertEqual( span.attributes.get(SpanAttributes.DB_STATEMENT), - "SET blah 32\nRPUSH foo éé\nHGETALL xxx", + "SET ? ?\nRPUSH ? ?\nHGETALL ?", ) self.assertEqual(span.attributes.get("db.redis.pipeline_length"), 3) @@ -308,7 +300,7 @@ def test_long_command(self): self._check_span(span, "MGET") self.assertTrue( span.attributes.get(SpanAttributes.DB_STATEMENT).startswith( - "MGET 0 1 2 3" + "MGET ? ? ? ?" ) ) self.assertTrue( @@ -322,7 +314,7 @@ def test_basics(self): span = spans[0] self._check_span(span, "GET") self.assertEqual( - span.attributes.get(SpanAttributes.DB_STATEMENT), "GET cheese" + span.attributes.get(SpanAttributes.DB_STATEMENT), "GET ?" ) self.assertEqual(span.attributes.get("db.redis.args_length"), 2) @@ -344,7 +336,7 @@ async def pipeline_simple(): self._check_span(span, "SET RPUSH HGETALL") self.assertEqual( span.attributes.get(SpanAttributes.DB_STATEMENT), - "SET blah 32\nRPUSH foo éé\nHGETALL xxx", + "SET ? ?\nRPUSH ? ?\nHGETALL ?", ) self.assertEqual(span.attributes.get("db.redis.pipeline_length"), 3) @@ -364,7 +356,7 @@ async def pipeline_immediate(): span = spans[0] self._check_span(span, "SET") self.assertEqual( - span.attributes.get(SpanAttributes.DB_STATEMENT), "SET b 2" + span.attributes.get(SpanAttributes.DB_STATEMENT), "SET ? ?" ) def test_parent(self): @@ -412,7 +404,7 @@ def test_basics(self): span = spans[0] self._check_span(span, "GET") self.assertEqual( - span.attributes.get(SpanAttributes.DB_STATEMENT), "GET cheese" + span.attributes.get(SpanAttributes.DB_STATEMENT), "GET ?" ) self.assertEqual(span.attributes.get("db.redis.args_length"), 2) @@ -434,7 +426,7 @@ async def pipeline_simple(): self._check_span(span, "SET RPUSH HGETALL") self.assertEqual( span.attributes.get(SpanAttributes.DB_STATEMENT), - "SET blah 32\nRPUSH foo éé\nHGETALL xxx", + "SET ? ?\nRPUSH ? ?\nHGETALL ?", ) self.assertEqual(span.attributes.get("db.redis.pipeline_length"), 3) @@ -488,5 +480,5 @@ def test_get(self): span = spans[0] self._check_span(span, "GET") self.assertEqual( - span.attributes.get(SpanAttributes.DB_STATEMENT), "GET foo" + span.attributes.get(SpanAttributes.DB_STATEMENT), "GET ?" ) diff --git a/tox.ini b/tox.ini index be821e9adf..2313eab1d2 100644 --- a/tox.ini +++ b/tox.ini @@ -84,8 +84,8 @@ envlist = pypy3-test-instrumentation-fastapi ; opentelemetry-instrumentation-flask - py3{7,8,9,10,11}-test-instrumentation-flask - pypy3-test-instrumentation-flask + py3{7,8,9,10,11}-test-instrumentation-flask{213,220} + pypy3-test-instrumentation-flask{213,220} ; opentelemetry-instrumentation-urllib py3{7,8,9,10,11}-test-instrumentation-urllib @@ -258,6 +258,8 @@ deps = falcon1: falcon ==1.4.1 falcon2: falcon >=2.0.0,<3.0.0 falcon3: falcon >=3.0.0,<4.0.0 + flask213: Flask ==2.1.3 + flask220: Flask >=2.2.0 grpc: pytest-asyncio sqlalchemy11: sqlalchemy>=1.1,<1.2 sqlalchemy14: aiosqlite @@ -275,7 +277,7 @@ deps = httpx18: httpx>=0.18.0,<0.19.0 httpx18: respx~=0.17.0 httpx21: httpx>=0.19.0 - httpx21: respx~=0.19.0 + httpx21: respx~=0.20.1 ; FIXME: add coverage testing ; FIXME: add mypy testing @@ -304,7 +306,7 @@ changedir = test-instrumentation-elasticsearch{2,5,6}: instrumentation/opentelemetry-instrumentation-elasticsearch/tests test-instrumentation-falcon{1,2,3}: instrumentation/opentelemetry-instrumentation-falcon/tests test-instrumentation-fastapi: instrumentation/opentelemetry-instrumentation-fastapi/tests - test-instrumentation-flask: instrumentation/opentelemetry-instrumentation-flask/tests + test-instrumentation-flask{213,220}: instrumentation/opentelemetry-instrumentation-flask/tests test-instrumentation-urllib: instrumentation/opentelemetry-instrumentation-urllib/tests test-instrumentation-urllib3: instrumentation/opentelemetry-instrumentation-urllib3/tests test-instrumentation-grpc: instrumentation/opentelemetry-instrumentation-grpc/tests @@ -365,8 +367,8 @@ commands_pre = grpc: pip install {toxinidir}/instrumentation/opentelemetry-instrumentation-grpc[test] - falcon{1,2,3},flask,django{1,2,3,4},pyramid,tornado,starlette,fastapi,aiohttp,asgi,requests,urllib,urllib3,wsgi: pip install {toxinidir}/util/opentelemetry-util-http[test] - wsgi,falcon{1,2,3},flask,django{1,2,3,4},pyramid: pip install {toxinidir}/instrumentation/opentelemetry-instrumentation-wsgi[test] + falcon{1,2,3},flask{213,220},django{1,2,3,4},pyramid,tornado,starlette,fastapi,aiohttp,asgi,requests,urllib,urllib3,wsgi: pip install {toxinidir}/util/opentelemetry-util-http[test] + wsgi,falcon{1,2,3},flask{213,220},django{1,2,3,4},pyramid: pip install {toxinidir}/instrumentation/opentelemetry-instrumentation-wsgi[test] asgi,django{3,4},starlette,fastapi: pip install {toxinidir}/instrumentation/opentelemetry-instrumentation-asgi[test] asyncpg: pip install {toxinidir}/instrumentation/opentelemetry-instrumentation-asyncpg[test] @@ -380,7 +382,7 @@ commands_pre = falcon{1,2,3}: pip install {toxinidir}/instrumentation/opentelemetry-instrumentation-falcon[test] - flask: pip install {toxinidir}/instrumentation/opentelemetry-instrumentation-flask[test] + flask{213,220}: pip install {toxinidir}/instrumentation/opentelemetry-instrumentation-flask[test] urllib: pip install {toxinidir}/instrumentation/opentelemetry-instrumentation-urllib[test]