From 70dc15d465ea7e54ecd18572c080bf9b36cc8d2f Mon Sep 17 00:00:00 2001 From: Hector Hernandez Guzman Date: Tue, 19 Nov 2019 15:15:16 -0800 Subject: [PATCH 1/7] Adding psycopg2 integration --- ext/opentelemetry-ext-postgresql/README.rst | 25 +++++ ext/opentelemetry-ext-postgresql/setup.cfg | 47 ++++++++++ ext/opentelemetry-ext-postgresql/setup.py | 26 +++++ .../opentelemetry/ext/postgresql/__init__.py | 94 +++++++++++++++++++ .../opentelemetry/ext/postgresql/version.py | 15 +++ .../tests/__init__.py | 0 .../tests/test_postgresql_integration.py | 31 ++++++ tox.ini | 9 +- 8 files changed, 245 insertions(+), 2 deletions(-) create mode 100644 ext/opentelemetry-ext-postgresql/README.rst create mode 100644 ext/opentelemetry-ext-postgresql/setup.cfg create mode 100644 ext/opentelemetry-ext-postgresql/setup.py create mode 100644 ext/opentelemetry-ext-postgresql/src/opentelemetry/ext/postgresql/__init__.py create mode 100644 ext/opentelemetry-ext-postgresql/src/opentelemetry/ext/postgresql/version.py create mode 100644 ext/opentelemetry-ext-postgresql/tests/__init__.py create mode 100644 ext/opentelemetry-ext-postgresql/tests/test_postgresql_integration.py diff --git a/ext/opentelemetry-ext-postgresql/README.rst b/ext/opentelemetry-ext-postgresql/README.rst new file mode 100644 index 00000000000..17c2fac8c2f --- /dev/null +++ b/ext/opentelemetry-ext-postgresql/README.rst @@ -0,0 +1,25 @@ +OpenTelemetry PostgreSQL integration +================================= + +The integration with PostgreSQL supports the `Psycopg`_ library and is specified +to ``trace_integration`` using ``'PostgreSQL'``. + +.. Psycopg: http://initd.org/psycopg/ + +Usage +----- + +.. code:: python + import psycopg2 + from opentelemetry.trace import tracer + from opentelemetry.trace.ext.postgresql import trace_integration + trace_integration(tracer()) + cnx = psycopg2.connect(database='Database') + cursor = cnx.cursor() + cursor.execute("INSERT INTO test (testField) VALUES (123)" + cursor.close() + cnx.close() + +References +---------- +* `OpenTelemetry Project `_ \ No newline at end of file diff --git a/ext/opentelemetry-ext-postgresql/setup.cfg b/ext/opentelemetry-ext-postgresql/setup.cfg new file mode 100644 index 00000000000..29fe33c3d2b --- /dev/null +++ b/ext/opentelemetry-ext-postgresql/setup.cfg @@ -0,0 +1,47 @@ +# Copyright 2019, 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. +# +[metadata] +name = opentelemetry-ext-postgresql +description = OpenTelemetry PostgreSQL integration +long_description = file: README.rst +long_description_content_type = text/x-rst +author = OpenTelemetry Authors +author_email = cncf-opentelemetry-contributors@lists.cncf.io +url = https://github.com/open-telemetry/opentelemetry-python/ext/opentelemetry-ext-postgresql +platforms = any +license = Apache-2.0 +classifiers = + Development Status :: 3 - Alpha + Intended Audience :: Developers + License :: OSI Approved :: Apache Software License + Programming Language :: Python + Programming Language :: Python :: 3 + Programming Language :: Python :: 3.4 + Programming Language :: Python :: 3.5 + Programming Language :: Python :: 3.6 + Programming Language :: Python :: 3.7 + +[options] +python_requires = >=3.4 +package_dir= + =src +packages=find_namespace: +install_requires = + opentelemetry-api >= 0.3.dev0 + psycopg2-binary >= 2.7.3.1 + wrapt >= 1.0.0, < 2.0.0 + +[options.packages.find] +where = src \ No newline at end of file diff --git a/ext/opentelemetry-ext-postgresql/setup.py b/ext/opentelemetry-ext-postgresql/setup.py new file mode 100644 index 00000000000..1066ac233a8 --- /dev/null +++ b/ext/opentelemetry-ext-postgresql/setup.py @@ -0,0 +1,26 @@ +# Copyright 2019, 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 os + +import setuptools + +BASE_DIR = os.path.dirname(__file__) +VERSION_FILENAME = os.path.join( + BASE_DIR, "src", "opentelemetry", "ext", "postgresql", "version.py" +) +PACKAGE_INFO = {} +with open(VERSION_FILENAME) as f: + exec(f.read(), PACKAGE_INFO) + +setuptools.setup(version=PACKAGE_INFO["__version__"]) diff --git a/ext/opentelemetry-ext-postgresql/src/opentelemetry/ext/postgresql/__init__.py b/ext/opentelemetry-ext-postgresql/src/opentelemetry/ext/postgresql/__init__.py new file mode 100644 index 00000000000..7c5e24ea5f1 --- /dev/null +++ b/ext/opentelemetry-ext-postgresql/src/opentelemetry/ext/postgresql/__init__.py @@ -0,0 +1,94 @@ +# Copyright 2019, 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. + +""" +The opentelemetry-ext-postgresql package allows tracing PostgreSQL queries made by the +Psycopg2 library. +""" + +import typing + +import psycopg2 +from psycopg2.extensions import cursor as pgcursor +import wrapt + +from opentelemetry.context import Context +from opentelemetry.trace import SpanKind, Tracer +from opentelemetry.trace.status import Status, StatusCanonicalCode + +QUERY_WRAP_METHODS = ["execute", "executemany", "callproc"] +DATABASE_COMPONENT = "postgresql" +DATABASE_TYPE = "sql" + + +def trace_integration(tracer): + """Integrate with PostgreSQL Psycopg library. + Psycopg: http://initd.org/psycopg/ + """ + + if tracer is None: + raise ValueError("The tracer is not provided.") + + # pylint: disable=unused-argument + def wrap(wrapped, instance, args, kwargs): + """Patch Psycopg connect method. + """ + connection = wrapped(*args, **kwargs) + connection.cursor_factory = TraceCursor + return connection + + wrapt.wrap_function_wrapper(psycopg2, "connect", wrap) + + class TraceCursor(pgcursor): + def __init__(self, *args, **kwargs): + for func in QUERY_WRAP_METHODS: + wrapt.wrap_function_wrapper(self, func, self.add_span) + super(TraceCursor, self).__init__(*args, **kwargs) + + def add_span( + self, + wrapped: typing.Callable[..., any], + instance: typing.Any, + args: typing.Tuple[any, any], + kwargs: typing.Dict[any, any], + ): + + name = DATABASE_COMPONENT + database = self.connection.info.dbname + if database: + name += "." + database + + query = args[0] if args else "" + # Query with parameters + if len(args) > 1: + query += " params=" + str(args[1]) + + with tracer.start_current_span(name, kind=SpanKind.CLIENT) as span: + span.set_attribute("component", DATABASE_COMPONENT) + span.set_attribute("db.type", DATABASE_TYPE) + span.set_attribute("db.instance", database) + span.set_attribute("db.statement", query) + span.set_attribute("db.user", self.connection.info.user) + span.set_attribute("peer.hostname", self.connection.info.host) + port = self.connection.info.port + if port is not None: + span.set_attribute("peer.port", port) + try: + result = wrapped(*args, **kwargs) + span.set_status(Status(StatusCanonicalCode.OK)) + return result + except Exception as ex: # pylint: disable=broad-except + span.set_status( + Status(StatusCanonicalCode.UNKNOWN, str(ex)) + ) diff --git a/ext/opentelemetry-ext-postgresql/src/opentelemetry/ext/postgresql/version.py b/ext/opentelemetry-ext-postgresql/src/opentelemetry/ext/postgresql/version.py new file mode 100644 index 00000000000..93ef792d051 --- /dev/null +++ b/ext/opentelemetry-ext-postgresql/src/opentelemetry/ext/postgresql/version.py @@ -0,0 +1,15 @@ +# Copyright 2019, 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.3.dev0" diff --git a/ext/opentelemetry-ext-postgresql/tests/__init__.py b/ext/opentelemetry-ext-postgresql/tests/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/ext/opentelemetry-ext-postgresql/tests/test_postgresql_integration.py b/ext/opentelemetry-ext-postgresql/tests/test_postgresql_integration.py new file mode 100644 index 00000000000..b0569293f05 --- /dev/null +++ b/ext/opentelemetry-ext-postgresql/tests/test_postgresql_integration.py @@ -0,0 +1,31 @@ +# Copyright 2019, 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 unittest +from unittest import mock + +from opentelemetry import trace as trace_api + + +class TestPostgresqlIntegration(unittest.TestCase): + def setUp(self): + self.tracer = trace_api.tracer() + self.start_current_span_patcher = mock.patch.object( + self.tracer, "start_as_current_span", autospec=True, spec_set=True + ) + + self.start_as_current_span = self.start_current_span_patcher.start() + + def tearDown(self): + self.start_current_span_patcher.stop() diff --git a/tox.ini b/tox.ini index 8f0d02d1ccb..597285329d3 100644 --- a/tox.ini +++ b/tox.ini @@ -2,8 +2,8 @@ skipsdist = True skip_missing_interpreters = True envlist = - py3{4,5,6,7,8}-test-{api,sdk,example-app,example-basic-tracer,example-http,ext-wsgi,ext-http-requests,ext-jaeger,ext-pymongo,opentracing-shim} - pypy3-test-{api,sdk,example-app,example-basic-tracer,example-http,ext-wsgi,ext-http-requests,ext-jaeger,ext-pymongo,opentracing-shim} + py3{4,5,6,7,8}-test-{api,sdk,example-app,example-basic-tracer,example-http,ext-wsgi,ext-http-requests,ext-jaeger,ext-postgresql,ext-pymongo,opentracing-shim} + pypy3-test-{api,sdk,example-app,example-basic-tracer,example-http,ext-wsgi,ext-http-requests,ext-jaeger,ext-postgresql,ext-pymongo,opentracing-shim} py3{4,5,6,7,8}-coverage ; Coverage is temporarily disabled for pypy3 due to the pytest bug. @@ -36,6 +36,7 @@ changedir = test-ext-http-requests: ext/opentelemetry-ext-http-requests/tests test-ext-jaeger: ext/opentelemetry-ext-jaeger/tests test-ext-pymongo: ext/opentelemetry-ext-pymongo/tests + test-ext-postgresql: ext/opentelemetry-ext-postgresql/tests test-ext-wsgi: ext/opentelemetry-ext-wsgi/tests test-example-app: examples/opentelemetry-example-app/tests test-example-basic-tracer: examples/basic_tracer/tests @@ -62,6 +63,7 @@ commands_pre = ext: pip install {toxinidir}/opentelemetry-api wsgi: pip install {toxinidir}/ext/opentelemetry-ext-wsgi pymongo: pip install {toxinidir}/ext/opentelemetry-ext-pymongo + postgresql: pip install {toxinidir}/ext/opentelemetry-ext-postgresql http-requests: pip install {toxinidir}/ext/opentelemetry-ext-http-requests jaeger: pip install {toxinidir}/opentelemetry-sdk jaeger: pip install {toxinidir}/ext/opentelemetry-ext-jaeger @@ -110,6 +112,7 @@ commands_pre = pip install -e {toxinidir}/ext/opentelemetry-ext-http-requests pip install -e {toxinidir}/ext/opentelemetry-ext-jaeger pip install -e {toxinidir}/ext/opentelemetry-ext-pymongo + pip install -e {toxinidir}/ext/opentelemetry-ext-postgresql pip install -e {toxinidir}/ext/opentelemetry-ext-wsgi pip install -e {toxinidir}/examples/opentelemetry-example-app pip install -e {toxinidir}/ext/opentelemetry-ext-opentracing-shim @@ -133,6 +136,8 @@ commands = ext/opentelemetry-ext-opentracing-shim/tests/ \ ext/opentelemetry-ext-pymongo/src/opentelemetry \ ext/opentelemetry-ext-pymongo/tests/ \ + ext/opentelemetry-ext-postgresql/src/opentelemetry \ + ext/opentelemetry-ext-postgresql/tests/ \ ext/opentelemetry-ext-wsgi/tests/ \ examples/opentelemetry-example-app/src/opentelemetry_example_app/ \ examples/opentelemetry-example-app/tests/ \ From 75ad5d6cace91739283749c9114885ce2a91290f Mon Sep 17 00:00:00 2001 From: Hector Hernandez Guzman Date: Fri, 3 Jan 2020 14:04:18 -0800 Subject: [PATCH 2/7] Renaming ext --- .../opentelemetry/ext/postgresql/__init__.py | 94 ------------------- .../README.rst | 2 +- .../setup.cfg | 10 +- .../setup.py | 4 +- .../opentelemetry/ext/psycopg2/__init__.py | 68 ++++++++++++++ .../opentelemetry/ext/psycopg2}/version.py | 4 +- .../tests/__init__.py | 0 .../tests/test_psycopg2_integration.py} | 2 +- 8 files changed, 79 insertions(+), 105 deletions(-) delete mode 100644 ext/opentelemetry-ext-postgresql/src/opentelemetry/ext/postgresql/__init__.py rename ext/{opentelemetry-ext-postgresql => opentelemetry-ext-psycopg2}/README.rst (94%) rename ext/{opentelemetry-ext-postgresql => opentelemetry-ext-psycopg2}/setup.cfg (88%) rename ext/{opentelemetry-ext-postgresql => opentelemetry-ext-psycopg2}/setup.py (87%) create mode 100644 ext/opentelemetry-ext-psycopg2/src/opentelemetry/ext/psycopg2/__init__.py rename ext/{opentelemetry-ext-postgresql/src/opentelemetry/ext/postgresql => opentelemetry-ext-psycopg2/src/opentelemetry/ext/psycopg2}/version.py (89%) rename ext/{opentelemetry-ext-postgresql => opentelemetry-ext-psycopg2}/tests/__init__.py (100%) rename ext/{opentelemetry-ext-postgresql/tests/test_postgresql_integration.py => opentelemetry-ext-psycopg2/tests/test_psycopg2_integration.py} (96%) diff --git a/ext/opentelemetry-ext-postgresql/src/opentelemetry/ext/postgresql/__init__.py b/ext/opentelemetry-ext-postgresql/src/opentelemetry/ext/postgresql/__init__.py deleted file mode 100644 index 7c5e24ea5f1..00000000000 --- a/ext/opentelemetry-ext-postgresql/src/opentelemetry/ext/postgresql/__init__.py +++ /dev/null @@ -1,94 +0,0 @@ -# Copyright 2019, 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. - -""" -The opentelemetry-ext-postgresql package allows tracing PostgreSQL queries made by the -Psycopg2 library. -""" - -import typing - -import psycopg2 -from psycopg2.extensions import cursor as pgcursor -import wrapt - -from opentelemetry.context import Context -from opentelemetry.trace import SpanKind, Tracer -from opentelemetry.trace.status import Status, StatusCanonicalCode - -QUERY_WRAP_METHODS = ["execute", "executemany", "callproc"] -DATABASE_COMPONENT = "postgresql" -DATABASE_TYPE = "sql" - - -def trace_integration(tracer): - """Integrate with PostgreSQL Psycopg library. - Psycopg: http://initd.org/psycopg/ - """ - - if tracer is None: - raise ValueError("The tracer is not provided.") - - # pylint: disable=unused-argument - def wrap(wrapped, instance, args, kwargs): - """Patch Psycopg connect method. - """ - connection = wrapped(*args, **kwargs) - connection.cursor_factory = TraceCursor - return connection - - wrapt.wrap_function_wrapper(psycopg2, "connect", wrap) - - class TraceCursor(pgcursor): - def __init__(self, *args, **kwargs): - for func in QUERY_WRAP_METHODS: - wrapt.wrap_function_wrapper(self, func, self.add_span) - super(TraceCursor, self).__init__(*args, **kwargs) - - def add_span( - self, - wrapped: typing.Callable[..., any], - instance: typing.Any, - args: typing.Tuple[any, any], - kwargs: typing.Dict[any, any], - ): - - name = DATABASE_COMPONENT - database = self.connection.info.dbname - if database: - name += "." + database - - query = args[0] if args else "" - # Query with parameters - if len(args) > 1: - query += " params=" + str(args[1]) - - with tracer.start_current_span(name, kind=SpanKind.CLIENT) as span: - span.set_attribute("component", DATABASE_COMPONENT) - span.set_attribute("db.type", DATABASE_TYPE) - span.set_attribute("db.instance", database) - span.set_attribute("db.statement", query) - span.set_attribute("db.user", self.connection.info.user) - span.set_attribute("peer.hostname", self.connection.info.host) - port = self.connection.info.port - if port is not None: - span.set_attribute("peer.port", port) - try: - result = wrapped(*args, **kwargs) - span.set_status(Status(StatusCanonicalCode.OK)) - return result - except Exception as ex: # pylint: disable=broad-except - span.set_status( - Status(StatusCanonicalCode.UNKNOWN, str(ex)) - ) diff --git a/ext/opentelemetry-ext-postgresql/README.rst b/ext/opentelemetry-ext-psycopg2/README.rst similarity index 94% rename from ext/opentelemetry-ext-postgresql/README.rst rename to ext/opentelemetry-ext-psycopg2/README.rst index 17c2fac8c2f..2b5fc67bfbe 100644 --- a/ext/opentelemetry-ext-postgresql/README.rst +++ b/ext/opentelemetry-ext-psycopg2/README.rst @@ -1,4 +1,4 @@ -OpenTelemetry PostgreSQL integration +OpenTelemetry Psycopg integration ================================= The integration with PostgreSQL supports the `Psycopg`_ library and is specified diff --git a/ext/opentelemetry-ext-postgresql/setup.cfg b/ext/opentelemetry-ext-psycopg2/setup.cfg similarity index 88% rename from ext/opentelemetry-ext-postgresql/setup.cfg rename to ext/opentelemetry-ext-psycopg2/setup.cfg index 29fe33c3d2b..f26c5918ebf 100644 --- a/ext/opentelemetry-ext-postgresql/setup.cfg +++ b/ext/opentelemetry-ext-psycopg2/setup.cfg @@ -1,4 +1,4 @@ -# Copyright 2019, OpenTelemetry Authors +# 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. @@ -13,13 +13,13 @@ # limitations under the License. # [metadata] -name = opentelemetry-ext-postgresql -description = OpenTelemetry PostgreSQL integration +name = opentelemetry-ext-psycopg2 +description = OpenTelemetry psycopg2 integration long_description = file: README.rst long_description_content_type = text/x-rst author = OpenTelemetry Authors author_email = cncf-opentelemetry-contributors@lists.cncf.io -url = https://github.com/open-telemetry/opentelemetry-python/ext/opentelemetry-ext-postgresql +url = https://github.com/open-telemetry/opentelemetry-python/ext/opentelemetry-ext-psycopg2 platforms = any license = Apache-2.0 classifiers = @@ -39,7 +39,7 @@ package_dir= =src packages=find_namespace: install_requires = - opentelemetry-api >= 0.3.dev0 + opentelemetry-api >= 0.4.dev0 psycopg2-binary >= 2.7.3.1 wrapt >= 1.0.0, < 2.0.0 diff --git a/ext/opentelemetry-ext-postgresql/setup.py b/ext/opentelemetry-ext-psycopg2/setup.py similarity index 87% rename from ext/opentelemetry-ext-postgresql/setup.py rename to ext/opentelemetry-ext-psycopg2/setup.py index 1066ac233a8..a84391e6dd4 100644 --- a/ext/opentelemetry-ext-postgresql/setup.py +++ b/ext/opentelemetry-ext-psycopg2/setup.py @@ -1,4 +1,4 @@ -# Copyright 2019, OpenTelemetry Authors +# 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. @@ -17,7 +17,7 @@ BASE_DIR = os.path.dirname(__file__) VERSION_FILENAME = os.path.join( - BASE_DIR, "src", "opentelemetry", "ext", "postgresql", "version.py" + BASE_DIR, "src", "opentelemetry", "ext", "psycopg2", "version.py" ) PACKAGE_INFO = {} with open(VERSION_FILENAME) as f: diff --git a/ext/opentelemetry-ext-psycopg2/src/opentelemetry/ext/psycopg2/__init__.py b/ext/opentelemetry-ext-psycopg2/src/opentelemetry/ext/psycopg2/__init__.py new file mode 100644 index 00000000000..ec57d5a660e --- /dev/null +++ b/ext/opentelemetry-ext-psycopg2/src/opentelemetry/ext/psycopg2/__init__.py @@ -0,0 +1,68 @@ +# 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. + +""" +The opentelemetry-ext-psycopg2 package allows tracing PostgreSQL queries made by the +Psycopg2 library. +""" + +import logging +import typing + +import psycopg2 +from psycopg2.extensions import cursor as pgcursor +import wrapt + +from opentelemetry.ext.dbapi import DatabaseApiIntegration, TracedConnection, TracedCursor +from opentelemetry.trace import Tracer + +logger = logging.getLogger(__name__) + +DATABASE_COMPONENT = "postgresql" +DATABASE_TYPE = "sql" + + +def trace_integration(tracer): + """Integrate with PostgreSQL Psycopg library. + Psycopg: http://initd.org/psycopg/ + """ + + connection_attributes = { + "database": "info.dbname", + "port": "info.port", + "host": "info.host", + "user": "info.user", + } + + # pylint: disable=unused-argument + def wrap_connect( + wrapped: typing.Callable[..., any], + instance: typing.Any, + args: typing.Tuple[any, any], + kwargs: typing.Dict[any, any], + ): + db_integration = DatabaseApiIntegration( + tracer, + DATABASE_COMPONENT, + database_type=DATABASE_TYPE, + connection_attributes=connection_attributes + ) + return db_integration.wrapped_connection(wrapped, args, kwargs) + + try: + wrapt.wrap_function_wrapper( + psycopg2, "connect", wrap_connect + ) + except Exception as ex: # pylint: disable=broad-except + logger.warning("Failed to integrate with pyscopg2. %s", str(ex)) \ No newline at end of file diff --git a/ext/opentelemetry-ext-postgresql/src/opentelemetry/ext/postgresql/version.py b/ext/opentelemetry-ext-psycopg2/src/opentelemetry/ext/psycopg2/version.py similarity index 89% rename from ext/opentelemetry-ext-postgresql/src/opentelemetry/ext/postgresql/version.py rename to ext/opentelemetry-ext-psycopg2/src/opentelemetry/ext/psycopg2/version.py index 93ef792d051..6b39cd19b59 100644 --- a/ext/opentelemetry-ext-postgresql/src/opentelemetry/ext/postgresql/version.py +++ b/ext/opentelemetry-ext-psycopg2/src/opentelemetry/ext/psycopg2/version.py @@ -1,4 +1,4 @@ -# Copyright 2019, OpenTelemetry Authors +# 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. @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -__version__ = "0.3.dev0" +__version__ = "0.4.dev0" diff --git a/ext/opentelemetry-ext-postgresql/tests/__init__.py b/ext/opentelemetry-ext-psycopg2/tests/__init__.py similarity index 100% rename from ext/opentelemetry-ext-postgresql/tests/__init__.py rename to ext/opentelemetry-ext-psycopg2/tests/__init__.py diff --git a/ext/opentelemetry-ext-postgresql/tests/test_postgresql_integration.py b/ext/opentelemetry-ext-psycopg2/tests/test_psycopg2_integration.py similarity index 96% rename from ext/opentelemetry-ext-postgresql/tests/test_postgresql_integration.py rename to ext/opentelemetry-ext-psycopg2/tests/test_psycopg2_integration.py index b0569293f05..5ab1b7a8b95 100644 --- a/ext/opentelemetry-ext-postgresql/tests/test_postgresql_integration.py +++ b/ext/opentelemetry-ext-psycopg2/tests/test_psycopg2_integration.py @@ -1,4 +1,4 @@ -# Copyright 2019, OpenTelemetry Authors +# 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. From a733f00424249a2c216acc29e0b0b99345ce950c Mon Sep 17 00:00:00 2001 From: Hector Hernandez Guzman Date: Mon, 6 Jan 2020 10:07:02 -0800 Subject: [PATCH 3/7] WIP --- .../tests/test_dbapi_integration.py | 181 ++++++++++++++++++ .../opentelemetry/ext/psycopg2/__init__.py | 44 ++++- .../tests/test_psycopg2_integration.py | 27 ++- 3 files changed, 235 insertions(+), 17 deletions(-) create mode 100644 ext/opentelemetry-ext-dbapi/tests/test_dbapi_integration.py diff --git a/ext/opentelemetry-ext-dbapi/tests/test_dbapi_integration.py b/ext/opentelemetry-ext-dbapi/tests/test_dbapi_integration.py new file mode 100644 index 00000000000..f5d1299e838 --- /dev/null +++ b/ext/opentelemetry-ext-dbapi/tests/test_dbapi_integration.py @@ -0,0 +1,181 @@ +# Copyright 2019, 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 unittest +from unittest import mock + +from opentelemetry import trace as trace_api +from opentelemetry.ext.dbapi import DatabaseApiIntegration + + +class TestDBApiIntegration(unittest.TestCase): + def setUp(self): + self.tracer = trace_api.Tracer() + self.span = MockSpan() + self.start_current_span_patcher = mock.patch.object( + self.tracer, + "start_as_current_span", + autospec=True, + spec_set=True, + return_value=self.span, + ) + + self.start_as_current_span = self.start_current_span_patcher.start() + + def tearDown(self): + self.start_current_span_patcher.stop() + + def test_span_succeeded(self): + connection_props = { + "database": "testdatabase", + "server_host": "testhost", + "server_port": 123, + "user": "testuser", + } + connection_attributes = { + "database": "database", + "port": "server_port", + "host": "server_host", + "user": "user", + } + db_integration = DatabaseApiIntegration( + self.tracer, "testcomponent", "testtype", connection_attributes + ) + mock_connection = db_integration.wrapped_connection( + mock_connect, {}, connection_props + ) + cursor = mock_connection.cursor() + cursor.execute("Test query", ("param1Value", False)) + self.assertTrue(self.start_as_current_span.called) + self.assertEqual( + self.start_as_current_span.call_args[0][0], + "testcomponent.testdatabase", + ) + self.assertIs( + self.start_as_current_span.call_args[1]["kind"], + trace_api.SpanKind.CLIENT, + ) + self.assertEqual(self.span.attributes["component"], "testcomponent") + self.assertEqual(self.span.attributes["db.type"], "testtype") + self.assertEqual(self.span.attributes["db.instance"], "testdatabase") + self.assertEqual(self.span.attributes["db.statement"], "Test query") + self.assertEqual( + self.span.attributes["db.statement.parameters"], + "('param1Value', False)", + ) + self.assertEqual(self.span.attributes["db.user"], "testuser") + self.assertEqual(self.span.attributes["net.peer.name"], "testhost") + self.assertEqual(self.span.attributes["net.peer.port"], 123) + self.assertIs( + self.span.status.canonical_code, + trace_api.status.StatusCanonicalCode.OK, + ) + + def test_span_failed(self): + db_integration = DatabaseApiIntegration(self.tracer, "testcomponent") + mock_connection = db_integration.wrapped_connection( + mock_connect, {}, {} + ) + cursor = mock_connection.cursor() + try: + cursor.execute("Test query", throw_exception=True) + except Exception: # pylint: disable=broad-except + self.assertEqual( + self.span.attributes["db.statement"], "Test query" + ) + self.assertIs( + self.span.status.canonical_code, + trace_api.status.StatusCanonicalCode.UNKNOWN, + ) + self.assertEqual(self.span.status.description, "Test Exception") + + def test_executemany(self): + db_integration = DatabaseApiIntegration(self.tracer, "testcomponent") + mock_connection = db_integration.wrapped_connection( + mock_connect, {}, {} + ) + cursor = mock_connection.cursor() + cursor.executemany("Test query") + self.assertTrue(self.start_as_current_span.called) + self.assertEqual(self.span.attributes["db.statement"], "Test query") + + def test_callproc(self): + db_integration = DatabaseApiIntegration(self.tracer, "testcomponent") + mock_connection = db_integration.wrapped_connection( + mock_connect, {}, {} + ) + cursor = mock_connection.cursor() + cursor.callproc("Test stored procedure") + self.assertTrue(self.start_as_current_span.called) + self.assertEqual( + self.span.attributes["db.statement"], "Test stored procedure" + ) + + +# pylint: disable=unused-argument +def mock_connect(*args, **kwargs): + database = kwargs.get("database") + server_host = kwargs.get("server_host") + server_port = kwargs.get("server_port") + user = kwargs.get("user") + return MockConnection(database, server_port, server_host, user) + + +class MockConnection: + def __init__(self, database, server_port, server_host, user): + self.database = database + self.server_port = server_port + self.server_host = server_host + self.user = user + + # pylint: disable=no-self-use + def cursor(self): + return MockCursor() + + +class MockCursor: + # pylint: disable=unused-argument, no-self-use + def execute(self, query, params=None, throw_exception=False): + if throw_exception: + raise Exception("Test Exception") + + # pylint: disable=unused-argument, no-self-use + def executemany(self, query, params=None, throw_exception=False): + if throw_exception: + raise Exception("Test Exception") + + # pylint: disable=unused-argument, no-self-use + def callproc(self, query, params=None, throw_exception=False): + if throw_exception: + raise Exception("Test Exception") + + +class MockSpan: + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + return False + + def __init__(self): + self.status = None + self.name = "" + self.kind = trace_api.SpanKind.INTERNAL + self.attributes = {} + + def set_attribute(self, key, value): + self.attributes[key] = value + + def set_status(self, status): + self.status = status diff --git a/ext/opentelemetry-ext-psycopg2/src/opentelemetry/ext/psycopg2/__init__.py b/ext/opentelemetry-ext-psycopg2/src/opentelemetry/ext/psycopg2/__init__.py index ec57d5a660e..0d3fdde41c9 100644 --- a/ext/opentelemetry-ext-psycopg2/src/opentelemetry/ext/psycopg2/__init__.py +++ b/ext/opentelemetry-ext-psycopg2/src/opentelemetry/ext/psycopg2/__init__.py @@ -47,22 +47,48 @@ def trace_integration(tracer): # pylint: disable=unused-argument def wrap_connect( - wrapped: typing.Callable[..., any], + connect_func: typing.Callable[..., any], instance: typing.Any, args: typing.Tuple[any, any], kwargs: typing.Dict[any, any], ): - db_integration = DatabaseApiIntegration( - tracer, - DATABASE_COMPONENT, - database_type=DATABASE_TYPE, - connection_attributes=connection_attributes - ) - return db_integration.wrapped_connection(wrapped, args, kwargs) + connection = connect_func(*args, **kwargs) + connection.cursor_factory = PsycopgTraceCursor + return connection try: wrapt.wrap_function_wrapper( psycopg2, "connect", wrap_connect ) except Exception as ex: # pylint: disable=broad-except - logger.warning("Failed to integrate with pyscopg2. %s", str(ex)) \ No newline at end of file + logger.warning("Failed to integrate with pyscopg2. %s", str(ex)) + + + class PsycopgTraceCursor(pgcursor): + def __init__(self, *args, **kwargs): + db_integration = DatabaseApiIntegration( + tracer, + DATABASE_COMPONENT, + database_type=DATABASE_TYPE, + connection_attributes=connection_attributes + ) + self._tracedConnection = TracedConnection(db_integration) + self._tracedConnection.get_connection_attributes() + self._tracedCursor = TracedCursor(db_integration) + + super(PsycopgTraceCursor, self).__init__(*args, **kwargs) + + def execute(self, query, vars=None): + return self._tracedCursor.traced_execution( + super(PsycopgTraceCursor, self).execute, query, vars + ) + + def executemany(self, query, vars): + return self._tracedCursor.traced_execution( + super(PsycopgTraceCursor, self).executemany, query, vars + ) + + def callproc(self, procname, vars=None): + return self._tracedCursor.traced_execution( + super(PsycopgTraceCursor, self).callproc, procname, vars + ) diff --git a/ext/opentelemetry-ext-psycopg2/tests/test_psycopg2_integration.py b/ext/opentelemetry-ext-psycopg2/tests/test_psycopg2_integration.py index 5ab1b7a8b95..989d67feb0a 100644 --- a/ext/opentelemetry-ext-psycopg2/tests/test_psycopg2_integration.py +++ b/ext/opentelemetry-ext-psycopg2/tests/test_psycopg2_integration.py @@ -15,17 +15,28 @@ import unittest from unittest import mock +import psycopg2 + from opentelemetry import trace as trace_api +from opentelemetry.ext.psycopg2 import trace_integration class TestPostgresqlIntegration(unittest.TestCase): - def setUp(self): - self.tracer = trace_api.tracer() - self.start_current_span_patcher = mock.patch.object( - self.tracer, "start_as_current_span", autospec=True, spec_set=True + def test_trace_integration(self): + tracer = trace_api.Tracer() + span = mock.create_autospec(trace_api.Span, spec_set=True) + start_current_span_patcher = mock.patch.object( + tracer, + "start_as_current_span", + autospec=True, + spec_set=True, + return_value=span, ) + start_as_current_span = start_current_span_patcher.start() + trace_integration(tracer) - self.start_as_current_span = self.start_current_span_patcher.start() - - def tearDown(self): - self.start_current_span_patcher.stop() + conn = psycopg2.connect("dbname=testdb user=postgres password=passwordPostgres") + cur = conn.cursor() + cur.execute("SELECT * FROM testtable;") + cur.fetchone() + From 1b6999a859ef6507bd0944b178c617e043241ce6 Mon Sep 17 00:00:00 2001 From: Hector Hernandez Guzman Date: Mon, 6 Jan 2020 15:16:07 -0800 Subject: [PATCH 4/7] Using dbapi ext --- .../src/opentelemetry/ext/dbapi/__init__.py | 119 +++++++++--------- .../opentelemetry/ext/psycopg2/__init__.py | 46 +++---- .../tests/test_psycopg2_integration.py | 20 +-- tox.ini | 13 +- 4 files changed, 97 insertions(+), 101 deletions(-) diff --git a/ext/opentelemetry-ext-dbapi/src/opentelemetry/ext/dbapi/__init__.py b/ext/opentelemetry-ext-dbapi/src/opentelemetry/ext/dbapi/__init__.py index 7ba1de1795a..88e9d3a0b12 100644 --- a/ext/opentelemetry-ext-dbapi/src/opentelemetry/ext/dbapi/__init__.py +++ b/ext/opentelemetry-ext-dbapi/src/opentelemetry/ext/dbapi/__init__.py @@ -18,6 +18,7 @@ https://www.python.org/dev/peps/pep-0249/ """ +import functools import logging import typing @@ -72,7 +73,6 @@ def wrap_connect( class DatabaseApiIntegration: - # pylint: disable=unused-argument def __init__( self, tracer: Tracer, @@ -80,8 +80,6 @@ def __init__( database_type: str = "sql", connection_attributes=None, ): - if tracer is None: - raise ValueError("The tracer is not provided.") self.connection_attributes = connection_attributes if self.connection_attributes is None: self.connection_attributes = { @@ -107,18 +105,40 @@ def wrapped_connection( """Add object proxy to connection object. """ connection = connect_method(*args, **kwargs) + self.get_connection_attributes(connection) + traced_connection = TracedConnectionProxy(connection, self) + return traced_connection + def get_connection_attributes(self, connection): + # Populate span fields using connection for key, value in self.connection_attributes.items(): - attribute = getattr(connection, value, None) + # Allow attributes nested in connection object + attribute = functools.reduce( + lambda attribute, attribute_value: getattr( + attribute, attribute_value, None + ), + value.split("."), + connection, + ) if attribute: self.connection_props[key] = attribute - traced_connection = TracedConnection(connection, self) - return traced_connection + self.name = self.database_component + self.database = self.connection_props.get("database", "") + if self.database: + self.name += "." + self.database + user = self.connection_props.get("user") + if user is not None: + self.span_attributes["db.user"] = user + host = self.connection_props.get("host") + if host is not None: + self.span_attributes["net.peer.name"] = host + port = self.connection_props.get("port") + if port is not None: + self.span_attributes["net.peer.port"] = port # pylint: disable=abstract-method -class TracedConnection(wrapt.ObjectProxy): - +class TracedConnectionProxy(wrapt.ObjectProxy): # pylint: disable=unused-argument def __init__( self, @@ -130,62 +150,17 @@ def __init__( wrapt.ObjectProxy.__init__(self, connection) self._db_api_integration = db_api_integration - self._db_api_integration.name = ( - self._db_api_integration.database_component - ) - self._db_api_integration.database = self._db_api_integration.connection_props.get( - "database", "" - ) - if self._db_api_integration.database: - self._db_api_integration.name += ( - "." + self._db_api_integration.database - ) - user = self._db_api_integration.connection_props.get("user") - if user is not None: - self._db_api_integration.span_attributes["db.user"] = user - host = self._db_api_integration.connection_props.get("host") - if host is not None: - self._db_api_integration.span_attributes["net.peer.name"] = host - port = self._db_api_integration.connection_props.get("port") - if port is not None: - self._db_api_integration.span_attributes["net.peer.port"] = port - def cursor(self, *args, **kwargs): - return TracedCursor( + return TracedCursorProxy( self.__wrapped__.cursor(*args, **kwargs), self._db_api_integration ) -# pylint: disable=abstract-method -class TracedCursor(wrapt.ObjectProxy): - - # pylint: disable=unused-argument - def __init__( - self, - cursor, - db_api_integration: DatabaseApiIntegration, - *args, - **kwargs - ): - wrapt.ObjectProxy.__init__(self, cursor) +class TracedCursor: + def __init__(self, db_api_integration: DatabaseApiIntegration): self._db_api_integration = db_api_integration - def execute(self, *args, **kwargs): - return self._traced_execution( - self.__wrapped__.execute, *args, **kwargs - ) - - def executemany(self, *args, **kwargs): - return self._traced_execution( - self.__wrapped__.executemany, *args, **kwargs - ) - - def callproc(self, *args, **kwargs): - return self._traced_execution( - self.__wrapped__.callproc, *args, **kwargs - ) - - def _traced_execution( + def traced_execution( self, query_method: typing.Callable[..., any], *args: typing.Tuple[any, any], @@ -223,3 +198,33 @@ def _traced_execution( except Exception as ex: # pylint: disable=broad-except span.set_status(Status(StatusCanonicalCode.UNKNOWN, str(ex))) raise ex + + +# pylint: disable=abstract-method +class TracedCursorProxy(wrapt.ObjectProxy): + + # pylint: disable=unused-argument + def __init__( + self, + cursor, + db_api_integration: DatabaseApiIntegration, + *args, + **kwargs + ): + wrapt.ObjectProxy.__init__(self, cursor) + self._traced_cursor = TracedCursor(db_api_integration) + + def execute(self, *args, **kwargs): + return self._traced_cursor.traced_execution( + self.__wrapped__.execute, *args, **kwargs + ) + + def executemany(self, *args, **kwargs): + return self._traced_cursor.traced_execution( + self.__wrapped__.executemany, *args, **kwargs + ) + + def callproc(self, *args, **kwargs): + return self._traced_cursor.traced_execution( + self.__wrapped__.callproc, *args, **kwargs + ) diff --git a/ext/opentelemetry-ext-psycopg2/src/opentelemetry/ext/psycopg2/__init__.py b/ext/opentelemetry-ext-psycopg2/src/opentelemetry/ext/psycopg2/__init__.py index 0d3fdde41c9..41816884892 100644 --- a/ext/opentelemetry-ext-psycopg2/src/opentelemetry/ext/psycopg2/__init__.py +++ b/ext/opentelemetry-ext-psycopg2/src/opentelemetry/ext/psycopg2/__init__.py @@ -21,10 +21,10 @@ import typing import psycopg2 -from psycopg2.extensions import cursor as pgcursor import wrapt +from psycopg2.sql import Composable -from opentelemetry.ext.dbapi import DatabaseApiIntegration, TracedConnection, TracedCursor +from opentelemetry.ext.dbapi import DatabaseApiIntegration, TracedCursor from opentelemetry.trace import Tracer logger = logging.getLogger(__name__) @@ -44,8 +44,14 @@ def trace_integration(tracer): "host": "info.host", "user": "info.user", } - - # pylint: disable=unused-argument + db_integration = DatabaseApiIntegration( + tracer, + DATABASE_COMPONENT, + database_type=DATABASE_TYPE, + connection_attributes=connection_attributes, + ) + + # pylint: disable=unused-argument def wrap_connect( connect_func: typing.Callable[..., any], instance: typing.Any, @@ -53,42 +59,38 @@ def wrap_connect( kwargs: typing.Dict[any, any], ): connection = connect_func(*args, **kwargs) + db_integration.get_connection_attributes(connection) connection.cursor_factory = PsycopgTraceCursor return connection try: - wrapt.wrap_function_wrapper( - psycopg2, "connect", wrap_connect - ) + wrapt.wrap_function_wrapper(psycopg2, "connect", wrap_connect) except Exception as ex: # pylint: disable=broad-except logger.warning("Failed to integrate with pyscopg2. %s", str(ex)) - - class PsycopgTraceCursor(pgcursor): + class PsycopgTraceCursor(psycopg2.extensions.cursor): def __init__(self, *args, **kwargs): - db_integration = DatabaseApiIntegration( - tracer, - DATABASE_COMPONENT, - database_type=DATABASE_TYPE, - connection_attributes=connection_attributes - ) - self._tracedConnection = TracedConnection(db_integration) - self._tracedConnection.get_connection_attributes() - self._tracedCursor = TracedCursor(db_integration) - + self._traced_cursor = TracedCursor(db_integration) super(PsycopgTraceCursor, self).__init__(*args, **kwargs) + # pylint: disable=redefined-builtin def execute(self, query, vars=None): - return self._tracedCursor.traced_execution( + if isinstance(query, Composable): + query = query.as_string(self) + return self._traced_cursor.traced_execution( super(PsycopgTraceCursor, self).execute, query, vars ) + # pylint: disable=redefined-builtin def executemany(self, query, vars): - return self._tracedCursor.traced_execution( + if isinstance(query, Composable): + query = query.as_string(self) + return self._traced_cursor.traced_execution( super(PsycopgTraceCursor, self).executemany, query, vars ) + # pylint: disable=redefined-builtin def callproc(self, procname, vars=None): - return self._tracedCursor.traced_execution( + return self._traced_cursor.traced_execution( super(PsycopgTraceCursor, self).callproc, procname, vars ) diff --git a/ext/opentelemetry-ext-psycopg2/tests/test_psycopg2_integration.py b/ext/opentelemetry-ext-psycopg2/tests/test_psycopg2_integration.py index 989d67feb0a..c444dadaa75 100644 --- a/ext/opentelemetry-ext-psycopg2/tests/test_psycopg2_integration.py +++ b/ext/opentelemetry-ext-psycopg2/tests/test_psycopg2_integration.py @@ -24,19 +24,7 @@ class TestPostgresqlIntegration(unittest.TestCase): def test_trace_integration(self): tracer = trace_api.Tracer() - span = mock.create_autospec(trace_api.Span, spec_set=True) - start_current_span_patcher = mock.patch.object( - tracer, - "start_as_current_span", - autospec=True, - spec_set=True, - return_value=span, - ) - start_as_current_span = start_current_span_patcher.start() - trace_integration(tracer) - - conn = psycopg2.connect("dbname=testdb user=postgres password=passwordPostgres") - cur = conn.cursor() - cur.execute("SELECT * FROM testtable;") - cur.fetchone() - + with mock.patch("psycopg2.connect"): + trace_integration(tracer) + cnx = psycopg2.connect(database="test") + self.assertIsNotNone(cnx.cursor_factory) diff --git a/tox.ini b/tox.ini index b4c78520896..acac4e8e147 100644 --- a/tox.ini +++ b/tox.ini @@ -2,10 +2,10 @@ skipsdist = True skip_missing_interpreters = True envlist = - py3{4,5,6,7,8}-test-{api,sdk,example-app,ext-wsgi,ext-flask,ext-http-requests,ext-jaeger,ext-dbapi,ext-mysql,ext-psycopg,ext-pymongo,ext-zipkin,opentracing-shim} - pypy3-test-{api,sdk,example-app,ext-wsgi,ext-flask,ext-http-requests,ext-jaeger,ext-dbapi,ext-mysql,ext-psycopg,ext-pymongo,ext-zipkin,opentracing-shim} - py3{4,5,6,7,8}-test-{api,sdk,example-app,example-basic-tracer,example-http,ext-wsgi,ext-flask,ext-http-requests,ext-jaeger,ext-dbapi,ext-mysql,ext-psycopg,ext-pymongo,ext-zipkin,opentracing-shim} - pypy3-test-{api,sdk,example-app,example-basic-tracer,example-http,ext-wsgi,ext-flask,ext-http-requests,ext-jaeger,ext-dbapi,ext-mysql,ext-psycopg,ext-pymongo,ext-zipkin,opentracing-shim} + py3{4,5,6,7,8}-test-{api,sdk,example-app,ext-wsgi,ext-flask,ext-http-requests,ext-jaeger,ext-dbapi,ext-mysql,ext-psycopg2,ext-pymongo,ext-zipkin,opentracing-shim} + pypy3-test-{api,sdk,example-app,ext-wsgi,ext-flask,ext-http-requests,ext-jaeger,ext-dbapi,ext-mysql,ext-psycopg2,ext-pymongo,ext-zipkin,opentracing-shim} + py3{4,5,6,7,8}-test-{api,sdk,example-app,example-basic-tracer,example-http,ext-wsgi,ext-flask,ext-http-requests,ext-jaeger,ext-dbapi,ext-mysql,ext-psycopg2,ext-pymongo,ext-zipkin,opentracing-shim} + pypy3-test-{api,sdk,example-app,example-basic-tracer,example-http,ext-wsgi,ext-flask,ext-http-requests,ext-jaeger,ext-dbapi,ext-mysql,ext-psycopg2,ext-pymongo,ext-zipkin,opentracing-shim} py3{4,5,6,7,8}-coverage ; Coverage is temporarily disabled for pypy3 due to the pytest bug. @@ -39,7 +39,7 @@ changedir = test-ext-dbapi: ext/opentelemetry-ext-dbapi/tests test-ext-mysql: ext/opentelemetry-ext-mysql/tests test-ext-pymongo: ext/opentelemetry-ext-pymongo/tests - test-ext-postgresql: ext/opentelemetry-ext-postgresql/tests + test-ext-psycopg2: ext/opentelemetry-ext-psycopg2/tests test-ext-wsgi: ext/opentelemetry-ext-wsgi/tests test-ext-zipkin: ext/opentelemetry-ext-zipkin/tests test-ext-flask: ext/opentelemetry-ext-flask/tests @@ -74,7 +74,8 @@ commands_pre = mysql: pip install {toxinidir}/ext/opentelemetry-ext-dbapi mysql: pip install {toxinidir}/ext/opentelemetry-ext-mysql pymongo: pip install {toxinidir}/ext/opentelemetry-ext-pymongo - postgresql: pip install {toxinidir}/ext/opentelemetry-ext-postgresql + psycopg2: pip install {toxinidir}/ext/opentelemetry-ext-dbapi + psycopg2: pip install {toxinidir}/ext/opentelemetry-ext-psycopg2 http-requests: pip install {toxinidir}/ext/opentelemetry-ext-http-requests jaeger: pip install {toxinidir}/opentelemetry-sdk jaeger: pip install {toxinidir}/ext/opentelemetry-ext-jaeger From 0fda7e0bb2ffcab7a5431cb695b5d1806dad670e Mon Sep 17 00:00:00 2001 From: Hector Hernandez Guzman Date: Mon, 6 Jan 2020 16:16:47 -0800 Subject: [PATCH 5/7] Remove pyscopg2 from pypy because is not supported --- tox.ini | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tox.ini b/tox.ini index acac4e8e147..d2784467c58 100644 --- a/tox.ini +++ b/tox.ini @@ -3,9 +3,9 @@ skipsdist = True skip_missing_interpreters = True envlist = py3{4,5,6,7,8}-test-{api,sdk,example-app,ext-wsgi,ext-flask,ext-http-requests,ext-jaeger,ext-dbapi,ext-mysql,ext-psycopg2,ext-pymongo,ext-zipkin,opentracing-shim} - pypy3-test-{api,sdk,example-app,ext-wsgi,ext-flask,ext-http-requests,ext-jaeger,ext-dbapi,ext-mysql,ext-psycopg2,ext-pymongo,ext-zipkin,opentracing-shim} + pypy3-test-{api,sdk,example-app,ext-wsgi,ext-flask,ext-http-requests,ext-jaeger,ext-dbapi,ext-mysql,ext-pymongo,ext-zipkin,opentracing-shim} py3{4,5,6,7,8}-test-{api,sdk,example-app,example-basic-tracer,example-http,ext-wsgi,ext-flask,ext-http-requests,ext-jaeger,ext-dbapi,ext-mysql,ext-psycopg2,ext-pymongo,ext-zipkin,opentracing-shim} - pypy3-test-{api,sdk,example-app,example-basic-tracer,example-http,ext-wsgi,ext-flask,ext-http-requests,ext-jaeger,ext-dbapi,ext-mysql,ext-psycopg2,ext-pymongo,ext-zipkin,opentracing-shim} + pypy3-test-{api,sdk,example-app,example-basic-tracer,example-http,ext-wsgi,ext-flask,ext-http-requests,ext-jaeger,ext-dbapi,ext-mysql,ext-pymongo,ext-zipkin,opentracing-shim} py3{4,5,6,7,8}-coverage ; Coverage is temporarily disabled for pypy3 due to the pytest bug. From 0ee06827d813379c58d63ba78bcd89d98ae0fcfc Mon Sep 17 00:00:00 2001 From: Hector Hernandez Guzman Date: Tue, 7 Jan 2020 14:44:03 -0800 Subject: [PATCH 6/7] Addressing comments --- ext/opentelemetry-ext-dbapi/README.rst | 8 +++++--- ext/opentelemetry-ext-http-requests/README.rst | 8 ++++++-- ext/opentelemetry-ext-mysql/README.rst | 7 +++++-- ext/opentelemetry-ext-psycopg2/README.rst | 12 ++++++++---- ext/opentelemetry-ext-pymongo/README.rst | 7 +++++-- 5 files changed, 29 insertions(+), 13 deletions(-) diff --git a/ext/opentelemetry-ext-dbapi/README.rst b/ext/opentelemetry-ext-dbapi/README.rst index 6dc31216039..3fbf1243ea5 100644 --- a/ext/opentelemetry-ext-dbapi/README.rst +++ b/ext/opentelemetry-ext-dbapi/README.rst @@ -11,12 +11,14 @@ Usage .. code:: python import mysql.connector - from opentelemetry.trace import tracer + from opentelemetry import trace + from opentelemetry.sdk.trace import TracerSource from opentelemetry.ext.dbapi import trace_integration - + trace.set_preferred_tracer_source_implementation(lambda T: TracerSource()) + tracer = trace.tracer_source().get_tracer(__name__) # Ex: mysql.connector - trace_integration(tracer(), mysql.connector, "connect", "mysql") + trace_integration(tracer, mysql.connector, "connect", "mysql") References diff --git a/ext/opentelemetry-ext-http-requests/README.rst b/ext/opentelemetry-ext-http-requests/README.rst index 33759f5cece..685ae7406fa 100644 --- a/ext/opentelemetry-ext-http-requests/README.rst +++ b/ext/opentelemetry-ext-http-requests/README.rst @@ -22,9 +22,13 @@ Usage import requests import opentelemetry.ext.http_requests - from opentelemetry.trace import tracer + from opentelemetry import trace + from opentelemetry.sdk.trace import TracerSource - opentelemetry.ext.http_requests.enable(tracer()) + trace.set_preferred_tracer_source_implementation(lambda T: TracerSource()) + tracer = trace.tracer_source().get_tracer(__name__) + + opentelemetry.ext.http_requests.enable(tracer) response = requests.get(url='https://www.example.org/') Limitations diff --git a/ext/opentelemetry-ext-mysql/README.rst b/ext/opentelemetry-ext-mysql/README.rst index e819a637694..5f7a08fdfcf 100644 --- a/ext/opentelemetry-ext-mysql/README.rst +++ b/ext/opentelemetry-ext-mysql/README.rst @@ -12,10 +12,13 @@ Usage .. code:: python import mysql.connector - from opentelemetry.trace import tracer + from opentelemetry import trace + from opentelemetry.sdk.trace import TracerSource from opentelemetry.ext.mysql import trace_integration - trace_integration(tracer()) + trace.set_preferred_tracer_source_implementation(lambda T: TracerSource()) + tracer = trace.tracer_source().get_tracer(__name__) + trace_integration(tracer) cnx = mysql.connector.connect(database='MySQL_Database') cursor = cnx.cursor() cursor.execute("INSERT INTO test (testField) VALUES (123)" diff --git a/ext/opentelemetry-ext-psycopg2/README.rst b/ext/opentelemetry-ext-psycopg2/README.rst index 2b5fc67bfbe..d7599492ac4 100644 --- a/ext/opentelemetry-ext-psycopg2/README.rst +++ b/ext/opentelemetry-ext-psycopg2/README.rst @@ -11,12 +11,16 @@ Usage .. code:: python import psycopg2 - from opentelemetry.trace import tracer - from opentelemetry.trace.ext.postgresql import trace_integration - trace_integration(tracer()) + from opentelemetry import trace + from opentelemetry.sdk.trace import TracerSource + from opentelemetry.trace.ext.psycopg2 import trace_integration + + trace.set_preferred_tracer_source_implementation(lambda T: TracerSource()) + tracer = trace.tracer_source().get_tracer(__name__) + trace_integration(tracer) cnx = psycopg2.connect(database='Database') cursor = cnx.cursor() - cursor.execute("INSERT INTO test (testField) VALUES (123)" + cursor.execute("INSERT INTO test (testField) VALUES (123)") cursor.close() cnx.close() diff --git a/ext/opentelemetry-ext-pymongo/README.rst b/ext/opentelemetry-ext-pymongo/README.rst index 1e8011f4c22..71496083eb1 100644 --- a/ext/opentelemetry-ext-pymongo/README.rst +++ b/ext/opentelemetry-ext-pymongo/README.rst @@ -12,10 +12,13 @@ Usage .. code:: python from pymongo import MongoClient - from opentelemetry.trace import tracer + from opentelemetry import trace + from opentelemetry.sdk.trace import TracerSource from opentelemetry.trace.ext.pymongo import trace_integration - trace_integration(tracer()) + trace.set_preferred_tracer_source_implementation(lambda T: TracerSource()) + tracer = trace.tracer_source().get_tracer(__name__) + trace_integration(tracer) client = MongoClient() db = client["MongoDB_Database"] collection = db["MongoDB_Collection"] From 2dc06715f81625f050e13464796c5b52d6eaab32 Mon Sep 17 00:00:00 2001 From: Hector Hernandez Guzman Date: Tue, 21 Jan 2020 10:22:10 -0800 Subject: [PATCH 7/7] Use trace_api.DefaultTracer() --- .../tests/test_psycopg2_integration.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/opentelemetry-ext-psycopg2/tests/test_psycopg2_integration.py b/ext/opentelemetry-ext-psycopg2/tests/test_psycopg2_integration.py index c444dadaa75..56ab3a8aae7 100644 --- a/ext/opentelemetry-ext-psycopg2/tests/test_psycopg2_integration.py +++ b/ext/opentelemetry-ext-psycopg2/tests/test_psycopg2_integration.py @@ -23,7 +23,7 @@ class TestPostgresqlIntegration(unittest.TestCase): def test_trace_integration(self): - tracer = trace_api.Tracer() + tracer = trace_api.DefaultTracer() with mock.patch("psycopg2.connect"): trace_integration(tracer) cnx = psycopg2.connect(database="test")