Skip to content

Commit e07782f

Browse files
committed
added support for pyodbc instrumentation
closes #237 closes #238
1 parent 2b2a80d commit e07782f

File tree

8 files changed

+104
-1
lines changed

8 files changed

+104
-1
lines changed
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
from elasticapm.instrumentation.packages.dbapi2 import (ConnectionProxy,
2+
CursorProxy,
3+
DbApi2Instrumentation,
4+
extract_signature)
5+
6+
7+
class PyODBCCursorProxy(CursorProxy):
8+
provider_name = 'pyodbc'
9+
10+
def extract_signature(self, sql):
11+
return extract_signature(sql)
12+
13+
14+
class PyODBCConnectionProxy(ConnectionProxy):
15+
cursor_proxy = PyODBCCursorProxy
16+
17+
18+
class PyODBCInstrumentation(DbApi2Instrumentation):
19+
name = 'pyodbc'
20+
21+
instrument_list = [
22+
("pyodbc", "connect"),
23+
]
24+
25+
def call(self, module, method, wrapped, instance, args, kwargs):
26+
return PyODBCConnectionProxy(wrapped(*args, **kwargs))

elasticapm/instrumentation/register.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
'elasticapm.instrumentation.packages.elasticsearch.ElasticsearchInstrumentation',
2121
'elasticapm.instrumentation.packages.cassandra.CassandraInstrumentation',
2222
'elasticapm.instrumentation.packages.pymssql.PyMSSQLInstrumentation',
23+
'elasticapm.instrumentation.packages.pyodbc.PyODBCInstrumentation',
2324

2425
'elasticapm.instrumentation.packages.django.template.DjangoTemplateInstrumentation',
2526
'elasticapm.instrumentation.packages.django.template.DjangoTemplateSourceInstrumentation',

tests/.jenkins_exclude.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,3 +98,8 @@ exclude:
9898
# pymssql
9999
- PYTHON_VERSION: pypy-2 # currently fails with error on pypy2
100100
FRAMEWORK: pymssql-newest
101+
# pyodbc
102+
- PYTHON_VERSION: pypy-2
103+
FRAMEWORK: pyodbc-newest
104+
- PYTHON_VERSION: pypy-3
105+
FRAMEWORK: pyodbc-newest

tests/.jenkins_framework.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ FRAMEWORK:
1717
- redis-newest
1818
- psycopg2-newest
1919
- pymssql-newest
20+
- pyodbc-newest
2021
- memcached-newest
2122
- pylibmc-newest
2223
- elasticsearch-2

tests/Dockerfile

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,14 @@
11
ARG PYTHON_IMAGE
22
FROM ${PYTHON_IMAGE}
33

4-
RUN apt-get update && apt-get -y --no-install-recommends install ca-certificates curl netcat libmemcached-dev freetds-dev &&\
4+
RUN apt-get update && apt-get -y --no-install-recommends install \
5+
ca-certificates \
6+
curl \
7+
netcat \
8+
odbc-postgresql \
9+
unixodbc-dev \
10+
freetds-dev \
11+
libmemcached-dev &&\
512
rm -rf /var/lib/apt/lists/*
613

714
# connection to ha.pool.sks-keyservers.net fails sometimes, so let's retry with couple different servers
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import os
2+
3+
import pytest
4+
5+
pyodbc = pytest.importorskip("pyodbc")
6+
7+
pytestmark = pytest.mark.pyodbc
8+
9+
10+
@pytest.yield_fixture(scope='function')
11+
def pyodbc_postgres_connection(request):
12+
conn_str = (
13+
"DRIVER={PostgreSQL Unicode};"
14+
"DATABASE=%s;"
15+
"UID=%s;"
16+
"SERVER=%s;"
17+
"PORT=%s;"
18+
) % (
19+
os.environ.get('POSTGRES_DB', 'elasticapm_test'),
20+
os.environ.get('POSTGRES_USER', 'postgres'),
21+
os.environ.get('POSTGRES_HOST', None),
22+
os.environ.get('POSTGRES_PORT', None),
23+
)
24+
conn = pyodbc.connect(conn_str)
25+
cursor = conn.cursor()
26+
cursor.execute(
27+
"CREATE TABLE test(id int, name VARCHAR(5) NOT NULL);"
28+
"INSERT INTO test VALUES (1, 'one'), (2, 'two'), (3, 'three');"
29+
)
30+
31+
yield conn
32+
33+
# cleanup
34+
cursor.execute('ROLLBACK')
35+
36+
37+
@pytest.mark.integrationtest
38+
def test_pyodbc_select(instrument, pyodbc_postgres_connection, elasticapm_client):
39+
cursor = pyodbc_postgres_connection.cursor()
40+
query = "SELECT * FROM test WHERE name LIKE 't%'"
41+
42+
try:
43+
elasticapm_client.begin_transaction("web.django")
44+
cursor.execute(query)
45+
cursor.fetchall()
46+
elasticapm_client.end_transaction(None, "test-transaction")
47+
finally:
48+
transactions = elasticapm_client.instrumentation_store.get_all()
49+
spans = transactions[0]['spans']
50+
span = spans[0]
51+
assert span['name'] == 'SELECT FROM test'
52+
assert 'db' in span['context']
53+
assert span['context']['db']['type'] == 'sql'
54+
assert span['context']['db']['statement'] == query
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
pyodbc
2+
-r requirements-base.txt

tests/scripts/envs/pyodbc.sh

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export PYTEST_MARKER="-m pyodbc"
2+
export DOCKER_DEPS="postgres"
3+
export POSTGRES_HOST="postgres"
4+
export POSTGRES_USER="postgres"
5+
export POSTGRES_DB="elasticapm_test"
6+
export POSTGRES_HOST="postgres"
7+
export POSTGRES_PORT="5432"

0 commit comments

Comments
 (0)