-
Notifications
You must be signed in to change notification settings - Fork 716
Adding pymongo integration #232
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
Changes from all commits
b837c9f
a12fe5d
276ecb4
2b90351
eccef1a
a187fec
a43c980
8dfb44e
77d3649
f3af20f
cf3a605
0670790
dbe5c6a
fbeb356
4db5db6
5783c16
bf6ec3b
17dcda7
40f2abd
3595748
77425b7
3f602d9
a858564
ee8adf2
2b23370
2af10a1
f2c344e
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,27 @@ | ||
OpenTelemetry pymongo integration | ||
================================= | ||
|
||
The integration with MongoDB supports the `pymongo`_ library and is specified | ||
to ``trace_integration`` using ``'pymongo'``. | ||
|
||
.. _pymongo: https://pypi.org/project/pymongo | ||
|
||
Usage | ||
----- | ||
|
||
.. code:: python | ||
|
||
from pymongo import MongoClient | ||
from opentelemetry.trace import tracer | ||
from opentelemetry.trace.ext.pymongo import trace_integration | ||
|
||
trace_integration(tracer()) | ||
client = MongoClient() | ||
db = client["MongoDB_Database"] | ||
collection = db["MongoDB_Collection"] | ||
collection.find_one() | ||
|
||
References | ||
---------- | ||
|
||
* `OpenTelemetry Project <https://opentelemetry.io/>`_ |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
# 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-pymongo | ||
description = OpenTelemetry pymongo 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-pymongo | ||
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 | ||
pymongo ~= 3.1 | ||
|
||
[options.packages.find] | ||
where = src |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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", "pymongo", "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,109 @@ | ||
# 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-pymongo package allows tracing commands made by the | ||
pymongo library. | ||
""" | ||
|
||
from pymongo import monitoring | ||
|
||
from opentelemetry.trace import SpanKind | ||
from opentelemetry.trace.status import Status, StatusCanonicalCode | ||
|
||
DATABASE_TYPE = "mongodb" | ||
COMMAND_ATTRIBUTES = ["filter", "sort", "skip", "limit", "pipeline"] | ||
|
||
|
||
def trace_integration(tracer=None): | ||
"""Integrate with pymongo to trace it using event listener. | ||
https://api.mongodb.com/python/current/api/pymongo/monitoring.html | ||
""" | ||
|
||
monitoring.register(CommandTracer(tracer)) | ||
|
||
|
||
class CommandTracer(monitoring.CommandListener): | ||
def __init__(self, tracer): | ||
if tracer is None: | ||
raise ValueError("The tracer is not provided.") | ||
self._tracer = tracer | ||
hectorhdzg marked this conversation as resolved.
Show resolved
Hide resolved
|
||
self._span_dict = {} | ||
|
||
def started(self, event: monitoring.CommandStartedEvent): | ||
command = event.command.get(event.command_name, "") | ||
name = DATABASE_TYPE + "." + event.command_name | ||
statement = event.command_name | ||
if command: | ||
name += "." + command | ||
statement += " " + command | ||
|
||
try: | ||
span = self._tracer.start_span(name, kind=SpanKind.CLIENT) | ||
span.set_attribute("component", DATABASE_TYPE) | ||
span.set_attribute("db.type", DATABASE_TYPE) | ||
span.set_attribute("db.instance", event.database_name) | ||
span.set_attribute("db.statement", statement) | ||
if event.connection_id is not None: | ||
span.set_attribute("peer.hostname", event.connection_id[0]) | ||
span.set_attribute("peer.port", event.connection_id[1]) | ||
|
||
# pymongo specific, not specified by spec | ||
span.set_attribute("db.mongo.operation_id", event.operation_id) | ||
span.set_attribute("db.mongo.request_id", event.request_id) | ||
|
||
for attr in COMMAND_ATTRIBUTES: | ||
_attr = event.command.get(attr) | ||
if _attr is not None: | ||
span.set_attribute("db.mongo." + attr, str(_attr)) | ||
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. On using 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. string casting is more important for other fields, dictionaries and lists are also part of COMMAND_ATTRIBUTES, I can add specific checks for limit and skip(both int) if the type is important here 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 at this point, it would be simpler to take |
||
|
||
# Add Span to dictionary | ||
self._span_dict[_get_span_dict_key(event)] = span | ||
except Exception as ex: # noqa pylint: disable=broad-except | ||
if span is not None: | ||
span.set_status(Status(StatusCanonicalCode.INTERNAL, str(ex))) | ||
span.end() | ||
self._remove_span(event) | ||
|
||
def succeeded(self, event: monitoring.CommandSucceededEvent): | ||
span = self._get_span(event) | ||
if span is not None: | ||
span.set_attribute( | ||
"db.mongo.duration_micros", event.duration_micros | ||
) | ||
span.set_status(Status(StatusCanonicalCode.OK, event.reply)) | ||
span.end() | ||
self._remove_span(event) | ||
|
||
def failed(self, event: monitoring.CommandFailedEvent): | ||
span = self._get_span(event) | ||
if span is not None: | ||
span.set_attribute( | ||
"db.mongo.duration_micros", event.duration_micros | ||
) | ||
span.set_status(Status(StatusCanonicalCode.UNKNOWN, event.failure)) | ||
span.end() | ||
self._remove_span(event) | ||
|
||
def _get_span(self, event): | ||
return self._span_dict.get(_get_span_dict_key(event)) | ||
|
||
def _remove_span(self, event): | ||
self._span_dict.pop(_get_span_dict_key(event)) | ||
|
||
|
||
def _get_span_dict_key(event): | ||
if event.connection_id is not None: | ||
return (event.request_id, event.connection_id) | ||
return event.request_id |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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" |
Uh oh!
There was an error while loading. Please reload this page.