(instrumentation-sqlite3): trace connections made with dbapi2.connect (#873)

* Change wrap_connect method

* Wrap both connect defs

* Add tests

* Add CHANGELOG entry
This commit is contained in:
Srikanth Chekuri
2022-01-26 05:49:25 +05:30
committed by GitHub
parent ec5b73c626
commit 571c3780e6
3 changed files with 36 additions and 11 deletions

View File

@ -34,6 +34,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- `opentelemetry-instrumentation-django` Django: fix issue preventing detection of MIDDLEWARE_CLASSES - `opentelemetry-instrumentation-django` Django: fix issue preventing detection of MIDDLEWARE_CLASSES
- `opentelemetry-instrumentation-sqlite3` Instrumentation now works with `dbapi2.connect`
## [1.8.0-0.27b0](https://github.com/open-telemetry/opentelemetry-python/releases/tag/v1.8.0-0.27b0) - 2021-12-17 ## [1.8.0-0.27b0](https://github.com/open-telemetry/opentelemetry-python/releases/tag/v1.8.0-0.27b0) - 2021-12-17
### Added ### Added

View File

@ -40,6 +40,7 @@ API
""" """
import sqlite3 import sqlite3
from sqlite3 import dbapi2
from typing import Collection from typing import Collection
from opentelemetry.instrumentation import dbapi from opentelemetry.instrumentation import dbapi
@ -54,6 +55,8 @@ _DATABASE_SYSTEM = "sqlite"
class SQLite3Instrumentor(BaseInstrumentor): class SQLite3Instrumentor(BaseInstrumentor):
_TO_WRAP = [sqlite3, dbapi2]
def instrumentation_dependencies(self) -> Collection[str]: def instrumentation_dependencies(self) -> Collection[str]:
return _instruments return _instruments
@ -63,19 +66,21 @@ class SQLite3Instrumentor(BaseInstrumentor):
""" """
tracer_provider = kwargs.get("tracer_provider") tracer_provider = kwargs.get("tracer_provider")
dbapi.wrap_connect( for module in self._TO_WRAP:
__name__, dbapi.wrap_connect(
sqlite3, __name__,
"connect", module,
_DATABASE_SYSTEM, "connect",
_CONNECTION_ATTRIBUTES, _DATABASE_SYSTEM,
version=__version__, _CONNECTION_ATTRIBUTES,
tracer_provider=tracer_provider, version=__version__,
) tracer_provider=tracer_provider,
)
def _uninstrument(self, **kwargs): def _uninstrument(self, **kwargs):
""" "Disable SQLite3 instrumentation""" """ "Disable SQLite3 instrumentation"""
dbapi.unwrap_connect(sqlite3, "connect") for module in self._TO_WRAP:
dbapi.unwrap_connect(module, "connect")
@staticmethod @staticmethod
def instrument_connection(connection, tracer_provider=None): def instrument_connection(connection, tracer_provider=None):

View File

@ -13,6 +13,7 @@
# limitations under the License. # limitations under the License.
import sqlite3 import sqlite3
from sqlite3 import dbapi2
from opentelemetry import trace as trace_api from opentelemetry import trace as trace_api
from opentelemetry.instrumentation.sqlite3 import SQLite3Instrumentor from opentelemetry.instrumentation.sqlite3 import SQLite3Instrumentor
@ -25,10 +26,14 @@ class TestSQLite3(TestBase):
super().setUpClass() super().setUpClass()
cls._connection = None cls._connection = None
cls._cursor = None cls._cursor = None
cls._connection2 = None
cls._cursor2 = None
cls._tracer = cls.tracer_provider.get_tracer(__name__) cls._tracer = cls.tracer_provider.get_tracer(__name__)
SQLite3Instrumentor().instrument(tracer_provider=cls.tracer_provider) SQLite3Instrumentor().instrument(tracer_provider=cls.tracer_provider)
cls._connection = sqlite3.connect(":memory:") cls._connection = sqlite3.connect(":memory:")
cls._cursor = cls._connection.cursor() cls._cursor = cls._connection.cursor()
cls._connection2 = dbapi2.connect(":memory:")
cls._cursor2 = cls._connection2.cursor()
@classmethod @classmethod
def tearDownClass(cls): def tearDownClass(cls):
@ -36,9 +41,14 @@ class TestSQLite3(TestBase):
cls._cursor.close() cls._cursor.close()
if cls._connection: if cls._connection:
cls._connection.close() cls._connection.close()
if cls._cursor2:
cls._cursor2.close()
if cls._connection2:
cls._connection2.close()
def validate_spans(self, span_name): def validate_spans(self, span_name):
spans = self.memory_exporter.get_finished_spans() spans = self.memory_exporter.get_finished_spans()
self.memory_exporter.clear()
self.assertEqual(len(spans), 2) self.assertEqual(len(spans), 2)
for span in spans: for span in spans:
if span.name == "rootSpan": if span.name == "rootSpan":
@ -62,14 +72,22 @@ class TestSQLite3(TestBase):
self._cursor.execute(stmt) self._cursor.execute(stmt)
self.validate_spans("CREATE") self.validate_spans("CREATE")
with self._tracer.start_as_current_span("rootSpan"):
self._cursor2.execute(stmt)
self.validate_spans("CREATE")
def test_executemany(self): def test_executemany(self):
"""Should create a child span for executemany""" """Should create a child span for executemany"""
stmt = "INSERT INTO test (id) VALUES (?)" stmt = "INSERT INTO test (id) VALUES (?)"
data = [("1",), ("2",), ("3",)]
with self._tracer.start_as_current_span("rootSpan"): with self._tracer.start_as_current_span("rootSpan"):
data = [("1",), ("2",), ("3",)]
self._cursor.executemany(stmt, data) self._cursor.executemany(stmt, data)
self.validate_spans("INSERT") self.validate_spans("INSERT")
with self._tracer.start_as_current_span("rootSpan"):
self._cursor2.executemany(stmt, data)
self.validate_spans("INSERT")
def test_callproc(self): def test_callproc(self):
"""Should create a child span for callproc""" """Should create a child span for callproc"""
with self._tracer.start_as_current_span("rootSpan"), self.assertRaises( with self._tracer.start_as_current_span("rootSpan"), self.assertRaises(