Skip to content

Commit 0e2df03

Browse files
Add pymssql instrumentation
1 parent f70af95 commit 0e2df03

File tree

12 files changed

+519
-0
lines changed

12 files changed

+519
-0
lines changed

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
136136

137137
- `opentelemetry-instrumentation-urllib3` Add urllib3 instrumentation
138138
([#299](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/299))
139+
- `opentelemetry-instrumentation-pymssql` Add pymssql instrumentation
140+
([#394](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/394))
139141

140142
- `opentelemetry-instrumentation-flask` Added `request_hook` and `response_hook` callbacks.
141143
([#416](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/416))
+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
OpenTelemetry pymssql Instrumentation
2+
=====================================
3+
4+
.. automodule:: opentelemetry.instrumentation.pymssql
5+
:members:
6+
:undoc-members:
7+
:show-inheritance:
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
OpenTelemetry pymssql Instrumentation
2+
=====================================
3+
4+
|pypi|
5+
6+
.. |pypi| image:: https://badge.fury.io/py/opentelemetry-instrumentation-pymssql.svg
7+
:target: https://pypi.org/project/opentelemetry-instrumentation-pymssql/
8+
9+
Installation
10+
------------
11+
12+
::
13+
14+
pip install opentelemetry-instrumentation-pymssql
15+
16+
17+
References
18+
----------
19+
* `OpenTelemetry pymssql Instrumentation <https://opentelemetry-python-contrib.readthedocs.io/en/latest/instrumentation/pymssql/pymssql.html>`_
20+
* `OpenTelemetry Project <https://opentelemetry.io/>`_
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
# Copyright The OpenTelemetry Authors
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
#
15+
[metadata]
16+
name = opentelemetry-instrumentation-pymssql
17+
description = OpenTelemetry pymssql instrumentation
18+
long_description = file: README.rst
19+
long_description_content_type = text/x-rst
20+
author = OpenTelemetry Authors
21+
author_email = [email protected]
22+
url = https://github.com/open-telemetry/opentelemetry-python-contrib/tree/main/instrumentation/opentelemetry-instrumentation-pymssql
23+
platforms = any
24+
license = Apache-2.0
25+
classifiers =
26+
Development Status :: 4 - Beta
27+
Intended Audience :: Developers
28+
License :: OSI Approved :: Apache Software License
29+
Programming Language :: Python
30+
Programming Language :: Python :: 3
31+
Programming Language :: Python :: 3.6
32+
Programming Language :: Python :: 3.7
33+
Programming Language :: Python :: 3.8
34+
35+
[options]
36+
python_requires = >=3.6
37+
package_dir=
38+
=src
39+
packages=find_namespace:
40+
install_requires =
41+
opentelemetry-api ~= 1.3
42+
opentelemetry-instrumentation-dbapi == 0.23b0
43+
opentelemetry-instrumentation == 0.23b0
44+
45+
[options.extras_require]
46+
test =
47+
opentelemetry-test == 0.23b0
48+
49+
[options.packages.find]
50+
where = src
51+
52+
[options.entry_points]
53+
opentelemetry_instrumentor =
54+
pymssql = opentelemetry.instrumentation.pymssql:PyMSSQLInstrumentor
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
# Copyright The OpenTelemetry Authors
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
16+
# DO NOT EDIT. THIS FILE WAS AUTOGENERATED FROM templates/instrumentation_setup.py.txt.
17+
# RUN `python scripts/generate_setup.py` TO REGENERATE.
18+
19+
20+
import distutils.cmd
21+
import json
22+
import os
23+
from configparser import ConfigParser
24+
25+
import setuptools
26+
27+
config = ConfigParser()
28+
config.read("setup.cfg")
29+
30+
# We provide extras_require parameter to setuptools.setup later which
31+
# overwrites the extra_require section from setup.cfg. To support extra_require
32+
# secion in setup.cfg, we load it here and merge it with the extra_require param.
33+
extras_require = {}
34+
if "options.extras_require" in config:
35+
for key, value in config["options.extras_require"].items():
36+
extras_require[key] = [v for v in value.split("\n") if v.strip()]
37+
38+
BASE_DIR = os.path.dirname(__file__)
39+
PACKAGE_INFO = {}
40+
41+
VERSION_FILENAME = os.path.join(
42+
BASE_DIR,
43+
"src",
44+
"opentelemetry",
45+
"instrumentation",
46+
"pymssql",
47+
"version.py",
48+
)
49+
with open(VERSION_FILENAME) as f:
50+
exec(f.read(), PACKAGE_INFO)
51+
52+
PACKAGE_FILENAME = os.path.join(
53+
BASE_DIR,
54+
"src",
55+
"opentelemetry",
56+
"instrumentation",
57+
"pymssql",
58+
"package.py",
59+
)
60+
with open(PACKAGE_FILENAME) as f:
61+
exec(f.read(), PACKAGE_INFO)
62+
63+
# Mark any instruments/runtime dependencies as test dependencies as well.
64+
extras_require["instruments"] = PACKAGE_INFO["_instruments"]
65+
test_deps = extras_require.get("test", [])
66+
for dep in extras_require["instruments"]:
67+
test_deps.append(dep)
68+
69+
extras_require["test"] = test_deps
70+
71+
72+
class JSONMetadataCommand(distutils.cmd.Command):
73+
74+
description = (
75+
"print out package metadata as JSON. This is used by OpenTelemetry dev scripts to ",
76+
"auto-generate code in other places",
77+
)
78+
user_options = []
79+
80+
def initialize_options(self):
81+
pass
82+
83+
def finalize_options(self):
84+
pass
85+
86+
def run(self):
87+
metadata = {
88+
"name": config["metadata"]["name"],
89+
"version": PACKAGE_INFO["__version__"],
90+
"instruments": PACKAGE_INFO["_instruments"],
91+
}
92+
print(json.dumps(metadata))
93+
94+
95+
setuptools.setup(
96+
cmdclass={"meta": JSONMetadataCommand},
97+
version=PACKAGE_INFO["__version__"],
98+
extras_require=extras_require,
99+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# EditorConfig is awesome: https://EditorConfig.org
2+
3+
# top-most EditorConfig file
4+
root = true
5+
6+
[*]
7+
indent_style = space
8+
indent_size = 4
9+
end_of_line = lf
10+
charset = utf-8
11+
trim_trailing_whitespace = false
12+
insert_final_newline = false
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
# Copyright The OpenTelemetry Authors
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
"""
16+
The integration with pymssql supports the `pymssql`_ library and can be enabled
17+
by using ``PyMSSQLInstrumentor``.
18+
19+
.. _pymssql: https://pypi.org/project/pymssql/
20+
21+
Usage
22+
-----
23+
24+
.. code:: python
25+
26+
import pymssql
27+
from opentelemetry.instrumentation.pymssql import PyMSSQLInstrumentor
28+
29+
PyMSSQLInstrumentor().instrument()
30+
31+
cnx = pymssql.connect(database="MSSQL_Database")
32+
cursor = cnx.cursor()
33+
cursor.execute("INSERT INTO test (testField) VALUES (123)"
34+
cnx.commit()
35+
cursor.close()
36+
cnx.close()
37+
38+
API
39+
---
40+
"""
41+
import typing
42+
43+
import pymssql
44+
45+
from opentelemetry.instrumentation import dbapi
46+
from opentelemetry.instrumentation.instrumentor import BaseInstrumentor
47+
from opentelemetry.instrumentation.pymssql.package import _instruments
48+
from opentelemetry.instrumentation.pymssql.version import __version__
49+
50+
51+
class PyMSSQLConnectMethodArgsTuple(typing.NamedTuple):
52+
server: str = None
53+
user: str = None
54+
password: str = None
55+
database: str = None
56+
timeout: int = None
57+
login_timeout: int = None
58+
charset: str = None
59+
as_dict: bool = None
60+
host: str = None
61+
appname: str = None
62+
port: str = None
63+
conn_properties: str = None
64+
autocommit: bool = None
65+
tds_version: str = None
66+
67+
68+
class PyMSSQLDatabaseApiIntegration(dbapi.DatabaseApiIntegration):
69+
def wrapped_connection(
70+
self,
71+
connect_method: typing.Callable[..., typing.Any],
72+
args: typing.Tuple[typing.Any, typing.Any],
73+
kwargs: typing.Dict[typing.Any, typing.Any],
74+
):
75+
"""Add object proxy to connection object."""
76+
connection = connect_method(*args, **kwargs)
77+
connect_method_args = PyMSSQLConnectMethodArgsTuple(*args)
78+
79+
self.name = self.database_system
80+
self.database = kwargs.get("database") or connect_method_args.database
81+
82+
user = kwargs.get("user") or connect_method_args.user
83+
if user is not None:
84+
self.span_attributes["db.user"] = user
85+
86+
port = kwargs.get("port") or connect_method_args.port
87+
host = kwargs.get("server") or connect_method_args.server
88+
if host is None:
89+
host = kwargs.get("host") or connect_method_args.host
90+
if host is not None:
91+
# The host string can include the port, separated by either a coma or
92+
# a column
93+
for sep in (":", ","):
94+
if sep in host:
95+
tokens = host.rsplit(sep)
96+
host = tokens[0]
97+
if len(tokens) > 1:
98+
port = tokens[1]
99+
if host is not None:
100+
self.span_attributes["net.peer.name"] = host
101+
if port is not None:
102+
self.span_attributes["net.peer.port"] = port
103+
104+
charset = kwargs.get("charset") or connect_method_args.charset
105+
if charset is not None:
106+
self.span_attributes["db.charset"] = charset
107+
108+
tds_version = (
109+
kwargs.get("tds_version") or connect_method_args.tds_version
110+
)
111+
if tds_version is not None:
112+
self.span_attributes["db.protocol.tds.version"] = tds_version
113+
114+
return dbapi.get_traced_connection_proxy(connection, self)
115+
116+
117+
class PyMSSQLInstrumentor(BaseInstrumentor):
118+
_DATABASE_SYSTEM = "mssql"
119+
120+
def instrumentation_dependencies(self) -> typing.Collection[str]:
121+
return _instruments
122+
123+
def _instrument(self, **kwargs):
124+
"""Integrate with the pymssql library.
125+
https://github.com/pymssql/pymssql/
126+
"""
127+
tracer_provider = kwargs.get("tracer_provider")
128+
129+
dbapi.wrap_connect(
130+
__name__,
131+
pymssql,
132+
"connect",
133+
self._DATABASE_SYSTEM,
134+
version=__version__,
135+
tracer_provider=tracer_provider,
136+
db_api_integration_factory=PyMSSQLDatabaseApiIntegration,
137+
)
138+
139+
def _uninstrument(self, **kwargs):
140+
""""Disable pymssql instrumentation"""
141+
dbapi.unwrap_connect(pymssql, "connect")
142+
143+
# pylint:disable=no-self-use
144+
def instrument_connection(self, connection):
145+
"""Enable instrumentation in a pymssql connection.
146+
147+
Args:
148+
connection: The connection to instrument.
149+
150+
Returns:
151+
An instrumented connection.
152+
"""
153+
154+
return dbapi.instrument_connection(
155+
__name__,
156+
connection,
157+
self._DATABASE_SYSTEM,
158+
self._CONNECTION_ATTRIBUTES,
159+
version=__version__,
160+
)
161+
162+
def uninstrument_connection(self, connection):
163+
"""Disable instrumentation in a pymssql connection.
164+
165+
Args:
166+
connection: The connection to uninstrument.
167+
168+
Returns:
169+
An uninstrumented connection.
170+
"""
171+
return dbapi.uninstrument_connection(connection)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# Copyright The OpenTelemetry Authors
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
16+
_instruments = ("pymssql ~= 2.1.5",)

0 commit comments

Comments
 (0)