-
Notifications
You must be signed in to change notification settings - Fork 834
PyMySQL Integration #504
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
PyMySQL Integration #504
Changes from all commits
de2fda9
789eaed
69af52d
7d25e82
0037579
71c4754
225bb4c
0512fb5
6e2f49f
4464472
a7b82c3
5d16a3a
f6acff9
c072146
6e6be34
0b0132c
a2bf714
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| OpenTelemetry PyMySQL Integration | ||
| ================================= | ||
|
|
||
| .. automodule:: opentelemetry.ext.pymysql | ||
| :members: | ||
| :undoc-members: | ||
| :show-inheritance: |
| Original file line number | Diff line number | Diff line change | ||||||
|---|---|---|---|---|---|---|---|---|
|
|
@@ -178,6 +178,9 @@ def get_connection_attributes(self, connection): | |||||||
| self.name = self.database_component | ||||||||
| self.database = self.connection_props.get("database", "") | ||||||||
| if self.database: | ||||||||
| # PyMySQL encodes names with utf-8 | ||||||||
| if hasattr(self.database, "decode"): | ||||||||
| self.database = self.database.decode(errors="ignore") | ||||||||
|
Comment on lines
+182
to
+183
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||
| self.name += "." + self.database | ||||||||
| user = self.connection_props.get("user") | ||||||||
| if user is not None: | ||||||||
|
|
||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,108 @@ | ||
| # 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 os | ||
| import time | ||
| import unittest | ||
|
|
||
| import pymysql as pymy | ||
|
|
||
| from opentelemetry import trace as trace_api | ||
| from opentelemetry.ext.pymysql import trace_integration | ||
| from opentelemetry.sdk.trace import Tracer, TracerProvider | ||
| from opentelemetry.sdk.trace.export import SimpleExportSpanProcessor | ||
| from opentelemetry.sdk.trace.export.in_memory_span_exporter import ( | ||
| InMemorySpanExporter, | ||
| ) | ||
|
|
||
| MYSQL_USER = os.getenv("MYSQL_USER ", "testuser") | ||
| MYSQL_PASSWORD = os.getenv("MYSQL_PASSWORD ", "testpassword") | ||
| MYSQL_HOST = os.getenv("MYSQL_HOST ", "localhost") | ||
| MYSQL_PORT = int(os.getenv("MYSQL_PORT ", "3306")) | ||
| MYSQL_DB_NAME = os.getenv("MYSQL_DB_NAME ", "opentelemetry-tests") | ||
|
|
||
|
|
||
| class TestFunctionalPyMysql(unittest.TestCase): | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also here, using TestBase could help a lot. |
||
| @classmethod | ||
| def setUpClass(cls): | ||
| cls._connection = None | ||
| cls._cursor = None | ||
| cls._tracer_provider = TracerProvider() | ||
| cls._tracer = Tracer(cls._tracer_provider, None) | ||
| cls._span_exporter = InMemorySpanExporter() | ||
| cls._span_processor = SimpleExportSpanProcessor(cls._span_exporter) | ||
| cls._tracer_provider.add_span_processor(cls._span_processor) | ||
| trace_integration(cls._tracer_provider) | ||
| cls._connection = pymy.connect( | ||
| user=MYSQL_USER, | ||
| password=MYSQL_PASSWORD, | ||
| host=MYSQL_HOST, | ||
| port=MYSQL_PORT, | ||
| database=MYSQL_DB_NAME, | ||
| ) | ||
| cls._cursor = cls._connection.cursor() | ||
|
|
||
| @classmethod | ||
| def tearDownClass(cls): | ||
| if cls._connection: | ||
| cls._connection.close() | ||
|
|
||
| def setUp(self): | ||
| self._span_exporter.clear() | ||
|
|
||
| def validate_spans(self): | ||
| spans = self._span_exporter.get_finished_spans() | ||
| self.assertEqual(len(spans), 2) | ||
| for span in spans: | ||
| if span.name == "rootSpan": | ||
| root_span = span | ||
| else: | ||
| db_span = span | ||
| self.assertIsInstance(span.start_time, int) | ||
| self.assertIsInstance(span.end_time, int) | ||
| self.assertIsNotNone(root_span) | ||
| self.assertIsNotNone(db_span) | ||
| self.assertEqual(root_span.name, "rootSpan") | ||
| self.assertEqual(db_span.name, "mysql.opentelemetry-tests") | ||
| self.assertIsNotNone(db_span.parent) | ||
| self.assertEqual(db_span.parent.name, root_span.name) | ||
| self.assertIs(db_span.kind, trace_api.SpanKind.CLIENT) | ||
| self.assertEqual(db_span.attributes["db.instance"], MYSQL_DB_NAME) | ||
| self.assertEqual(db_span.attributes["net.peer.name"], MYSQL_HOST) | ||
| self.assertEqual(db_span.attributes["net.peer.port"], MYSQL_PORT) | ||
|
|
||
| def test_execute(self): | ||
| """Should create a child span for execute | ||
| """ | ||
| with self._tracer.start_as_current_span("rootSpan"): | ||
| self._cursor.execute("CREATE TABLE IF NOT EXISTS test (id INT)") | ||
| self.validate_spans() | ||
|
|
||
| def test_executemany(self): | ||
| """Should create a child span for executemany | ||
| """ | ||
| with self._tracer.start_as_current_span("rootSpan"): | ||
| data = ["1", "2", "3"] | ||
| stmt = "INSERT INTO test (id) VALUES (%s)" | ||
| self._cursor.executemany(stmt, data) | ||
| self.validate_spans() | ||
|
|
||
| def test_callproc(self): | ||
| """Should create a child span for callproc | ||
| """ | ||
| with self._tracer.start_as_current_span("rootSpan"), self.assertRaises( | ||
| Exception | ||
| ): | ||
|
Comment on lines
+104
to
+106
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think two separated |
||
| self._cursor.callproc("test", ()) | ||
| self.validate_spans() | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| # Changelog | ||
|
|
||
| ## Unreleased |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| OpenTelemetry PyMySQL integration | ||
| ================================= | ||
|
|
||
| |pypi| | ||
|
|
||
| .. |pypi| image:: https://badge.fury.io/py/opentelemetry-ext-pymysql.svg | ||
| :target: https://pypi.org/project/opentelemetry-ext-pymysql/ | ||
|
|
||
| Integration with PyMySQL that supports the PyMySQL library and is | ||
| specified to trace_integration using 'PyMySQL'. | ||
|
|
||
|
|
||
| Installation | ||
| ------------ | ||
|
|
||
| :: | ||
|
|
||
| pip install opentelemetry-ext-pymysql | ||
|
|
||
|
|
||
| References | ||
| ---------- | ||
| * `OpenTelemetry PyMySQL Integration <https://opentelemetry-python.readthedocs.io/en/latest/ext/pymysql/pymysql.html>`_ | ||
| * `OpenTelemetry Project <https://opentelemetry.io/>`_ |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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. | ||
| # | ||
| [metadata] | ||
| name = opentelemetry-ext-pymysql | ||
| description = OpenTelemetry PyMySQL 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/tree/master/ext/opentelemetry-ext-pymysql | ||
| platforms = any | ||
| license = Apache-2.0 | ||
| classifiers = | ||
| Development Status :: 4 - Beta | ||
| Intended Audience :: Developers | ||
| License :: OSI Approved :: Apache Software License | ||
| Programming Language :: Python | ||
| Programming Language :: Python :: 3 | ||
| Programming Language :: Python :: 3.4 | ||
| Programming Language :: Python :: 3.5 | ||
| Programming Language :: Python :: 3.6 | ||
| Programming Language :: Python :: 3.7 | ||
mauriciovasquezbernal marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| Programming Language :: Python :: 3.8 | ||
|
|
||
| [options] | ||
| python_requires = >=3.4 | ||
| package_dir= | ||
| =src | ||
| packages=find_namespace: | ||
| install_requires = | ||
| opentelemetry-api == 0.7.dev0 | ||
| opentelemetry-ext-dbapi == 0.7.dev0 | ||
| PyMySQL ~= 0.9.3 | ||
|
|
||
| [options.packages.find] | ||
| where = src | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| # 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 os | ||
|
|
||
| import setuptools | ||
|
|
||
| BASE_DIR = os.path.dirname(__file__) | ||
| VERSION_FILENAME = os.path.join( | ||
| BASE_DIR, "src", "opentelemetry", "ext", "pymysql", "version.py" | ||
| ) | ||
| PACKAGE_INFO = {} | ||
| with open(VERSION_FILENAME) as f: | ||
| exec(f.read(), PACKAGE_INFO) | ||
|
|
||
| setuptools.setup(version=PACKAGE_INFO["__version__"]) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,68 @@ | ||
| # 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. | ||
|
|
||
| """ | ||
| The integration with PyMySQL supports the `PyMySQL`_ library and is specified | ||
| to ``trace_integration`` using ``'PyMySQL'``. | ||
|
|
||
| .. _PyMySQL: https://pypi.org/project/PyMySQL/ | ||
|
|
||
| Usage | ||
| ----- | ||
|
|
||
| .. code:: python | ||
|
|
||
| import pymysql | ||
| from opentelemetry import trace | ||
| from opentelemetry.ext.pymysql import trace_integration | ||
| from opentelemetry.sdk.trace import TracerProvider | ||
|
|
||
| trace.set_tracer_provider(TracerProvider()) | ||
| trace_integration() | ||
| cnx = pymysql.connect(database="MySQL_Database") | ||
| cursor = cnx.cursor() | ||
| cursor.execute("INSERT INTO test (testField) VALUES (123)" | ||
| cnx.commit() | ||
| cursor.close() | ||
| cnx.close() | ||
|
|
||
| API | ||
| --- | ||
| """ | ||
|
|
||
| import typing | ||
|
|
||
| import pymysql | ||
|
|
||
| from opentelemetry.ext.dbapi import wrap_connect | ||
| from opentelemetry.ext.pymysql.version import __version__ | ||
| from opentelemetry.trace import TracerProvider, get_tracer | ||
|
|
||
|
|
||
| def trace_integration(tracer_provider: typing.Optional[TracerProvider] = None): | ||
| """Integrate with the PyMySQL library. | ||
| https://github.com/PyMySQL/PyMySQL/ | ||
| """ | ||
|
|
||
| tracer = get_tracer(__name__, __version__, tracer_provider) | ||
|
|
||
| connection_attributes = { | ||
| "database": "db", | ||
| "port": "port", | ||
| "host": "host", | ||
| "user": "user", | ||
| } | ||
| wrap_connect( | ||
| tracer, pymysql, "connect", "mysql", "sql", connection_attributes | ||
| ) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| # Copyright The OpenTelemetry Authors | ||
| # | ||
| # Licensed under the Apache License, Version 2.0 (the "License"); | ||
| # you may not use this file except in compliance with the License. | ||
| # You may obtain a copy of the License at | ||
| # | ||
| # http://www.apache.org/licenses/LICENSE-2.0 | ||
| # | ||
| # Unless required by applicable law or agreed to in writing, software | ||
| # distributed under the License is distributed on an "AS IS" BASIS, | ||
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| # See the License for the specific language governing permissions and | ||
| # limitations under the License. | ||
|
|
||
| __version__ = "0.7.dev0" |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,38 @@ | ||
| # Copyright The OpenTelemetry Authors | ||
| # | ||
| # Licensed under the Apache License, Version 2.0 (the "License"); | ||
| # you may not use this file except in compliance with the License. | ||
| # You may obtain a copy of the License at | ||
| # | ||
| # http://www.apache.org/licenses/LICENSE-2.0 | ||
| # | ||
| # Unless required by applicable law or agreed to in writing, software | ||
| # distributed under the License is distributed on an "AS IS" BASIS, | ||
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| # See the License for the specific language governing permissions and | ||
| # limitations under the License. | ||
|
|
||
| from unittest import mock | ||
|
|
||
| import pymysql | ||
|
|
||
| import opentelemetry.ext.pymysql | ||
| from opentelemetry.ext.pymysql import trace_integration | ||
| from opentelemetry.test.test_base import TestBase | ||
|
|
||
|
|
||
| class TestPyMysqlIntegration(TestBase): | ||
| def test_trace_integration(self): | ||
| with mock.patch("pymysql.connect"): | ||
| trace_integration() | ||
| cnx = pymysql.connect(database="test") | ||
| cursor = cnx.cursor() | ||
| query = "SELECT * FROM test" | ||
| cursor.execute(query) | ||
|
|
||
| spans_list = self.memory_exporter.get_finished_spans() | ||
| self.assertEqual(len(spans_list), 1) | ||
| span = spans_list[0] | ||
|
|
||
| # Check version and name in span's instrumentation info | ||
| self.check_span_instrumentation_info(span, opentelemetry.ext.pymysql) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.