Skip to content

Commit

Permalink
Adding pymongo integration (#232)
Browse files Browse the repository at this point in the history
  • Loading branch information
hectorhdzg authored and c24t committed Nov 7, 2019
1 parent e95a115 commit 9fd4ccd
Show file tree
Hide file tree
Showing 8 changed files with 417 additions and 2 deletions.
27 changes: 27 additions & 0 deletions ext/opentelemetry-ext-pymongo/README.rst
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/>`_
46 changes: 46 additions & 0 deletions ext/opentelemetry-ext-pymongo/setup.cfg
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
26 changes: 26 additions & 0 deletions ext/opentelemetry-ext-pymongo/setup.py
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
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))

# 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"
Empty file.
Loading

0 comments on commit 9fd4ccd

Please sign in to comment.