Merge branch 'master' into req

This commit is contained in:
Leighton Chen 2020-11-23 11:04:45 -05:00 committed by GitHub
commit 848cad8d12
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
92 changed files with 804 additions and 568 deletions

View File

@ -19,6 +19,11 @@ Please describe the tests that you ran to verify your changes. Provide instructi
- [ ] Test A - [ ] Test A
# Does This PR Require a Core Repo Change?
- [ ] Yes. - Link to PR:
- [ ] No.
# Checklist: # Checklist:
See [contributing.md](https://github.com/open-telemetry/opentelemetry-python-contrib/blob/master/CONTRIBUTING.md) for styleguide, changelog guidelines, and more. See [contributing.md](https://github.com/open-telemetry/opentelemetry-python-contrib/blob/master/CONTRIBUTING.md) for styleguide, changelog guidelines, and more.

View File

@ -6,7 +6,7 @@ on:
- 'release/*' - 'release/*'
pull_request: pull_request:
env: env:
CORE_REPO_SHA: 47483865854c7adae7455f8441dab7f814f4ce2a CORE_REPO_SHA: 3b813eb9921e709538dd1b07fa7a5f93600fbec1
jobs: jobs:
build: build:

1
.gitignore vendored
View File

@ -16,6 +16,7 @@ var
sdist sdist
develop-eggs develop-eggs
.installed.cfg .installed.cfg
pyvenv.cfg
lib lib
lib64 lib64
__pycache__ __pycache__

View File

@ -46,7 +46,7 @@ You can run:
- `tox -e lint` to run lint checks on all code - `tox -e lint` to run lint checks on all code
See See
[`tox.ini`](https://github.com/open-telemetry/opentelemetry-python/blob/master/tox.ini) [`tox.ini`](https://github.com/open-telemetry/opentelemetry-python-contrib/blob/master/tox.ini)
for more detail on available tox commands. for more detail on available tox commands.
## Pull Requests ## Pull Requests

View File

@ -19,5 +19,5 @@ Installation
References References
---------- ----------
* `OpenTelemetry <REPLACE ME>/ Tracing <https://opentelemetry-python.readthedocs.io/en/latest/instrumentation/<REPLACE ME>/<REPLACE ME>.html>`_ * `OpenTelemetry <REPLACE ME>/ Tracing <https://opentelemetry-python-contrib.readthedocs.io/en/latest/instrumentation/<REPLACE ME>/<REPLACE ME>.html>`_
* `OpenTelemetry Project <https://opentelemetry.io/>`_ * `OpenTelemetry Project <https://opentelemetry.io/>`_

View File

@ -19,7 +19,7 @@ long_description = file: README.rst
long_description_content_type = text/x-rst long_description_content_type = text/x-rst
author = OpenTelemetry Authors author = OpenTelemetry Authors
author_email = cncf-opentelemetry-contributors@lists.cncf.io author_email = cncf-opentelemetry-contributors@lists.cncf.io
url = https://github.com/open-telemetry/opentelemetry-python/exporter/opentelemetry-exporter-datadog url = https://github.com/open-telemetry/opentelemetry-python-contrib/exporter/opentelemetry-exporter-datadog
platforms = any platforms = any
license = Apache-2.0 license = Apache-2.0
classifiers = classifiers =

View File

@ -19,7 +19,7 @@ long_description = file: README.rst
long_description_content_type = text/x-rst long_description_content_type = text/x-rst
author = OpenTelemetry Authors author = OpenTelemetry Authors
author_email = cncf-opentelemetry-contributors@lists.cncf.io author_email = cncf-opentelemetry-contributors@lists.cncf.io
url = https://github.com/open-telemetry/opentelemetry-python/instrumentation/opentelemetry-instrumentation-aiohttp-client url = https://github.com/open-telemetry/opentelemetry-python-contrib/instrumentation/opentelemetry-instrumentation-aiohttp-client
platforms = any platforms = any
license = Apache-2.0 license = Apache-2.0
classifiers = classifiers =

View File

@ -17,5 +17,5 @@ Installation
References References
---------- ----------
* `OpenTelemetry aiopg Instrumentation <https://opentelemetry-python.readthedocs.io/en/latest/instrumentation/aiopg/aiopg.html>`_ * `OpenTelemetry aiopg Instrumentation <https://opentelemetry-python-contrib.readthedocs.io/en/latest/instrumentation/aiopg/aiopg.html>`_
* `OpenTelemetry Project <https://opentelemetry.io/>`_ * `OpenTelemetry Project <https://opentelemetry.io/>`_

View File

@ -19,7 +19,7 @@ long_description = file: README.rst
long_description_content_type = text/x-rst long_description_content_type = text/x-rst
author = OpenTelemetry Authors author = OpenTelemetry Authors
author_email = cncf-opentelemetry-contributors@lists.cncf.io author_email = cncf-opentelemetry-contributors@lists.cncf.io
url = https://github.com/open-telemetry/opentelemetry-python/tree/master/instrumentation/opentelemetry-instrumentation-aiopg url = https://github.com/open-telemetry/opentelemetry-python-contrib/tree/master/instrumentation/opentelemetry-instrumentation-aiopg
platforms = any platforms = any
license = Apache-2.0 license = Apache-2.0
classifiers = classifiers =

View File

@ -101,9 +101,16 @@ class AsyncTracedCursor(TracedCursor):
*args: typing.Tuple[typing.Any, typing.Any], *args: typing.Tuple[typing.Any, typing.Any],
**kwargs: typing.Dict[typing.Any, typing.Any] **kwargs: typing.Dict[typing.Any, typing.Any]
): ):
name = ""
if len(args) > 0 and args[0]:
name = args[0]
elif self._db_api_integration.database:
name = self._db_api_integration.database
else:
name = self._db_api_integration.name
with self._db_api_integration.get_tracer().start_as_current_span( with self._db_api_integration.get_tracer().start_as_current_span(
self._db_api_integration.name, kind=SpanKind.CLIENT name, kind=SpanKind.CLIENT
) as span: ) as span:
self._populate_span(span, *args) self._populate_span(span, *args)
try: try:

View File

@ -215,12 +215,12 @@ class TestAiopgIntegration(TestBase):
spans_list = self.memory_exporter.get_finished_spans() spans_list = self.memory_exporter.get_finished_spans()
self.assertEqual(len(spans_list), 1) self.assertEqual(len(spans_list), 1)
span = spans_list[0] span = spans_list[0]
self.assertEqual(span.name, "testcomponent.testdatabase") self.assertEqual(span.name, "Test query")
self.assertIs(span.kind, trace_api.SpanKind.CLIENT) self.assertIs(span.kind, trace_api.SpanKind.CLIENT)
self.assertEqual(span.attributes["component"], "testcomponent") self.assertEqual(span.attributes["component"], "testcomponent")
self.assertEqual(span.attributes["db.type"], "testtype") self.assertEqual(span.attributes["db.system"], "testcomponent")
self.assertEqual(span.attributes["db.instance"], "testdatabase") self.assertEqual(span.attributes["db.name"], "testdatabase")
self.assertEqual(span.attributes["db.statement"], "Test query") self.assertEqual(span.attributes["db.statement"], "Test query")
self.assertEqual( self.assertEqual(
span.attributes["db.statement.parameters"], span.attributes["db.statement.parameters"],
@ -230,7 +230,7 @@ class TestAiopgIntegration(TestBase):
self.assertEqual(span.attributes["net.peer.name"], "testhost") self.assertEqual(span.attributes["net.peer.name"], "testhost")
self.assertEqual(span.attributes["net.peer.port"], 123) self.assertEqual(span.attributes["net.peer.port"], 123)
self.assertIs( self.assertIs(
span.status.status_code, trace_api.status.StatusCode.UNSET, span.status.status_code, trace_api.status.StatusCode.UNSET
) )
def test_span_not_recording(self): def test_span_not_recording(self):
@ -281,7 +281,7 @@ class TestAiopgIntegration(TestBase):
span = spans_list[0] span = spans_list[0]
self.assertEqual(span.attributes["db.statement"], "Test query") self.assertEqual(span.attributes["db.statement"], "Test query")
self.assertIs( self.assertIs(
span.status.status_code, trace_api.status.StatusCode.ERROR, span.status.status_code, trace_api.status.StatusCode.ERROR
) )
self.assertEqual(span.status.description, "Test Exception") self.assertEqual(span.status.description, "Test Exception")

View File

@ -19,7 +19,7 @@ long_description = file: README.rst
long_description_content_type = text/x-rst long_description_content_type = text/x-rst
author = OpenTelemetry Authors author = OpenTelemetry Authors
author_email = cncf-opentelemetry-contributors@lists.cncf.io author_email = cncf-opentelemetry-contributors@lists.cncf.io
url = https://github.com/open-telemetry/opentelemetry-python/instrumentation/opentelemetry-instrumentation-asgi url = https://github.com/open-telemetry/opentelemetry-python-contrib/instrumentation/opentelemetry-instrumentation-asgi
platforms = any platforms = any
license = Apache-2.0 license = Apache-2.0
classifiers = classifiers =

View File

@ -19,5 +19,5 @@ Installation
References References
---------- ----------
* `OpenTelemetry asyncpg Instrumentation <https://opentelemetry-python.readthedocs.io/en/latest/instrumentation/asyncpg/asyncpg.html>`_ * `OpenTelemetry asyncpg Instrumentation <https://opentelemetry-python-contrib.readthedocs.io/en/latest/instrumentation/asyncpg/asyncpg.html>`_
* `OpenTelemetry Project <https://opentelemetry.io/>`_ * `OpenTelemetry Project <https://opentelemetry.io/>`_

View File

@ -19,7 +19,7 @@ long_description = file: README.rst
long_description_content_type = text/x-rst long_description_content_type = text/x-rst
author = OpenTelemetry Authors author = OpenTelemetry Authors
author_email = cncf-opentelemetry-contributors@lists.cncf.io author_email = cncf-opentelemetry-contributors@lists.cncf.io
url = https://github.com/open-telemetry/opentelemetry-python/instrumentation/opentelemetry-instrumentation-asyncpg url = https://github.com/open-telemetry/opentelemetry-python-contrib/instrumentation/opentelemetry-instrumentation-asyncpg
platforms = any platforms = any
license = Apache-2.0 license = Apache-2.0
classifiers = classifiers =

View File

@ -19,5 +19,5 @@ Installation
References References
---------- ----------
* `OpenTelemetry Boto Tracing <https://opentelemetry-python.readthedocs.io/en/latest/instrumentation/boto/boto.html>`_ * `OpenTelemetry Boto Tracing <https://opentelemetry-python-contrib.readthedocs.io/en/latest/instrumentation/boto/boto.html>`_
* `OpenTelemetry Project <https://opentelemetry.io/>`_ * `OpenTelemetry Project <https://opentelemetry.io/>`_

View File

@ -19,7 +19,7 @@ long_description = file: README.rst
long_description_content_type = text/x-rst long_description_content_type = text/x-rst
author = OpenTelemetry Authors author = OpenTelemetry Authors
author_email = cncf-opentelemetry-contributors@lists.cncf.io author_email = cncf-opentelemetry-contributors@lists.cncf.io
url = https://github.com/open-telemetry/opentelemetry-python/tree/master/instrumentation/opentelemetry-instrumentation-boto url = https://github.com/open-telemetry/opentelemetry-python-contrib/tree/master/instrumentation/opentelemetry-instrumentation-boto
platforms = any platforms = any
license = Apache-2.0 license = Apache-2.0
classifiers = classifiers =

View File

@ -48,13 +48,15 @@ from boto.connection import AWSAuthConnection, AWSQueryConnection
from wrapt import wrap_function_wrapper from wrapt import wrap_function_wrapper
from opentelemetry.instrumentation.boto.version import __version__ from opentelemetry.instrumentation.boto.version import __version__
from opentelemetry.instrumentation.botocore import add_span_arg_tags, unwrap
from opentelemetry.instrumentation.instrumentor import BaseInstrumentor from opentelemetry.instrumentation.instrumentor import BaseInstrumentor
from opentelemetry.instrumentation.utils import unwrap
from opentelemetry.sdk.trace import Resource from opentelemetry.sdk.trace import Resource
from opentelemetry.trace import SpanKind, get_tracer from opentelemetry.trace import SpanKind, get_tracer
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
SERVICE_PARAMS_BLOCK_LIST = {"s3": ["params.Body"]}
def _get_instance_region_name(instance): def _get_instance_region_name(instance):
region = getattr(instance, "region", None) region = getattr(instance, "region", None)
@ -201,3 +203,50 @@ class BotoInstrumentor(BaseInstrumentor):
args, args,
kwargs, kwargs,
) )
def flatten_dict(dict_, sep=".", prefix=""):
"""
Returns a normalized dict of depth 1 with keys in order of embedding
"""
# NOTE: This should probably be in `opentelemetry.instrumentation.utils`.
# adapted from https://stackoverflow.com/a/19647596
return (
{
prefix + sep + k if prefix else k: v
for kk, vv in dict_.items()
for k, v in flatten_dict(vv, sep, kk).items()
}
if isinstance(dict_, dict)
else {prefix: dict_}
)
def add_span_arg_tags(span, aws_service, args, args_names, args_traced):
def truncate_arg_value(value, max_len=1024):
"""Truncate values which are bytes and greater than `max_len`.
Useful for parameters like "Body" in `put_object` operations.
"""
if isinstance(value, bytes) and len(value) > max_len:
return b"..."
return value
if not span.is_recording():
return
# Do not trace `Key Management Service` or `Secure Token Service` API calls
# over concerns of security leaks.
if aws_service not in {"kms", "sts"}:
tags = dict(
(name, value)
for (name, value) in zip(args_names, args)
if name in args_traced
)
tags = flatten_dict(tags)
for param_key, value in tags.items():
if param_key in SERVICE_PARAMS_BLOCK_LIST.get(aws_service, {}):
continue
span.set_attribute(param_key, truncate_arg_value(value))

View File

@ -5,6 +5,8 @@
([#181](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/181)) ([#181](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/181))
- Make botocore instrumentation check if instrumentation has been suppressed - Make botocore instrumentation check if instrumentation has been suppressed
([#182](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/182)) ([#182](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/182))
- Botocore SpanKind as CLIENT and modify existing traced attributes
([#150])(https://github.com/open-telemetry/opentelemetry-python-contrib/pull/150)
## Version 0.13b0 ## Version 0.13b0

View File

@ -19,5 +19,5 @@ Installation
References References
---------- ----------
* `OpenTelemetry Botocore Tracing <https://opentelemetry-python.readthedocs.io/en/latest/instrumentation/botocore/botocore.html>`_ * `OpenTelemetry Botocore Tracing <https://opentelemetry-python-contrib.readthedocs.io/en/latest/instrumentation/botocore/botocore.html>`_
* `OpenTelemetry Project <https://opentelemetry.io/>`_ * `OpenTelemetry Project <https://opentelemetry.io/>`_

View File

@ -19,7 +19,7 @@ long_description = file: README.rst
long_description_content_type = text/x-rst long_description_content_type = text/x-rst
author = OpenTelemetry Authors author = OpenTelemetry Authors
author_email = cncf-opentelemetry-contributors@lists.cncf.io author_email = cncf-opentelemetry-contributors@lists.cncf.io
url = https://github.com/open-telemetry/opentelemetry-python/tree/master/instrumentation/opentelemetry-instrumentation-botocore url = https://github.com/open-telemetry/opentelemetry-python-contrib/tree/master/instrumentation/opentelemetry-instrumentation-botocore
platforms = any platforms = any
license = Apache-2.0 license = Apache-2.0
classifiers = classifiers =

View File

@ -49,12 +49,14 @@ API
import logging import logging
from botocore.client import BaseClient from botocore.client import BaseClient
from botocore.exceptions import ClientError, ParamValidationError
from wrapt import ObjectProxy, wrap_function_wrapper from wrapt import ObjectProxy, wrap_function_wrapper
from opentelemetry import context as context_api from opentelemetry import context as context_api
from opentelemetry import propagators from opentelemetry import propagators
from opentelemetry.instrumentation.botocore.version import __version__ from opentelemetry.instrumentation.botocore.version import __version__
from opentelemetry.instrumentation.instrumentor import BaseInstrumentor from opentelemetry.instrumentation.instrumentor import BaseInstrumentor
from opentelemetry.instrumentation.utils import unwrap
from opentelemetry.sdk.trace import Resource from opentelemetry.sdk.trace import Resource
from opentelemetry.trace import SpanKind, get_tracer from opentelemetry.trace import SpanKind, get_tracer
@ -70,15 +72,13 @@ def _patched_endpoint_prepare_request(wrapped, instance, args, kwargs):
class BotocoreInstrumentor(BaseInstrumentor): class BotocoreInstrumentor(BaseInstrumentor):
"""A instrumentor for Botocore """An instrumentor for Botocore.
See `BaseInstrumentor` See `BaseInstrumentor`
""" """
def _instrument(self, **kwargs): def _instrument(self, **kwargs):
# FIXME should the tracer provider be accessed via Configuration
# instead?
# pylint: disable=attribute-defined-outside-init # pylint: disable=attribute-defined-outside-init
self._tracer = get_tracer( self._tracer = get_tracer(
__name__, __version__, kwargs.get("tracer_provider") __name__, __version__, kwargs.get("tracer_provider")
@ -99,137 +99,66 @@ class BotocoreInstrumentor(BaseInstrumentor):
def _uninstrument(self, **kwargs): def _uninstrument(self, **kwargs):
unwrap(BaseClient, "_make_api_call") unwrap(BaseClient, "_make_api_call")
# pylint: disable=too-many-branches
def _patched_api_call(self, original_func, instance, args, kwargs): def _patched_api_call(self, original_func, instance, args, kwargs):
if context_api.get_value("suppress_instrumentation"): if context_api.get_value("suppress_instrumentation"):
return original_func(*args, **kwargs) return original_func(*args, **kwargs)
endpoint_name = deep_getattr(instance, "_endpoint._endpoint_prefix") # pylint: disable=protected-access
service_name = instance._service_model.service_name
operation_name, api_params = args
error = None
result = None
with self._tracer.start_as_current_span( with self._tracer.start_as_current_span(
"{}.command".format(endpoint_name), kind=SpanKind.CONSUMER, "{}".format(service_name), kind=SpanKind.CLIENT,
) as span: ) as span:
operation = None
if args and span.is_recording():
operation = args[0]
span.resource = Resource(
attributes={
"endpoint": endpoint_name,
"operation": operation.lower(),
}
)
else:
span.resource = Resource(
attributes={"endpoint": endpoint_name}
)
add_span_arg_tags(
span,
endpoint_name,
args,
("action", "params", "path", "verb"),
{"params", "path", "verb"},
)
if span.is_recording(): if span.is_recording():
region_name = deep_getattr(instance, "meta.region_name") span.set_attribute("aws.operation", operation_name)
span.set_attribute("aws.region", instance.meta.region_name)
meta = { span.set_attribute("aws.service", service_name)
"aws.agent": "botocore", if "QueueUrl" in api_params:
"aws.operation": operation, span.set_attribute("aws.queue_url", api_params["QueueUrl"])
"aws.region": region_name, if "TableName" in api_params:
} span.set_attribute(
for key, value in meta.items(): "aws.table_name", api_params["TableName"]
span.set_attribute(key, value) )
try:
result = original_func(*args, **kwargs) result = original_func(*args, **kwargs)
except ClientError as ex:
error = ex
if error:
result = error.response
if span.is_recording(): if span.is_recording():
if "ResponseMetadata" in result:
metadata = result["ResponseMetadata"]
req_id = None
if "RequestId" in metadata:
req_id = metadata["RequestId"]
elif "HTTPHeaders" in metadata:
headers = metadata["HTTPHeaders"]
if "x-amzn-RequestId" in headers:
req_id = headers["x-amzn-RequestId"]
elif "x-amz-request-id" in headers:
req_id = headers["x-amz-request-id"]
elif "x-amz-id-2" in headers:
req_id = headers["x-amz-id-2"]
if req_id:
span.set_attribute( span.set_attribute(
"http.status_code", "aws.request_id", req_id,
result["ResponseMetadata"]["HTTPStatusCode"],
) )
if "HTTPStatusCode" in metadata:
span.set_attribute( span.set_attribute(
"retry_attempts", "http.status_code", metadata["HTTPStatusCode"],
result["ResponseMetadata"]["RetryAttempts"],
) )
if error:
raise error
return result return result
def unwrap(obj, attr):
function = getattr(obj, attr, None)
if (
function
and isinstance(function, ObjectProxy)
and hasattr(function, "__wrapped__")
):
setattr(obj, attr, function.__wrapped__)
def add_span_arg_tags(span, endpoint_name, args, args_names, args_traced):
def truncate_arg_value(value, max_len=1024):
"""Truncate values which are bytes and greater than `max_len`.
Useful for parameters like "Body" in `put_object` operations.
"""
if isinstance(value, bytes) and len(value) > max_len:
return b"..."
return value
def flatten_dict(dict_, sep=".", prefix=""):
"""
Returns a normalized dict of depth 1 with keys in order of embedding
"""
# adapted from https://stackoverflow.com/a/19647596
return (
{
prefix + sep + k if prefix else k: v
for kk, vv in dict_.items()
for k, v in flatten_dict(vv, sep, kk).items()
}
if isinstance(dict_, dict)
else {prefix: dict_}
)
if not span.is_recording():
return
if endpoint_name not in {"kms", "sts"}:
tags = dict(
(name, value)
for (name, value) in zip(args_names, args)
if name in args_traced
)
tags = flatten_dict(tags)
for key, value in {
k: truncate_arg_value(v)
for k, v in tags.items()
if k not in {"s3": ["params.Body"]}.get(endpoint_name, [])
}.items():
span.set_attribute(key, value)
def deep_getattr(obj, attr_string, default=None):
"""
Returns the attribute of ``obj`` at the dotted path given by
``attr_string``, if no such attribute is reachable, returns ``default``.
>>> deep_getattr(cass, "cluster")
<cassandra.cluster.Cluster object at 0xa20c350
>>> deep_getattr(cass, "cluster.metadata.partitioner")
u"org.apache.cassandra.dht.Murmur3Partitioner"
>>> deep_getattr(cass, "i.dont.exist", default="default")
"default"
"""
attrs = attr_string.split(".")
for attr in attrs:
try:
obj = getattr(obj, attr)
except AttributeError:
return default
return obj

View File

@ -17,29 +17,25 @@ from unittest.mock import Mock, patch
import botocore.session import botocore.session
from botocore.exceptions import ParamValidationError from botocore.exceptions import ParamValidationError
from moto import ( # pylint: disable=import-error from moto import ( # pylint: disable=import-error
mock_dynamodb2,
mock_ec2, mock_ec2,
mock_kinesis, mock_kinesis,
mock_kms, mock_kms,
mock_lambda, mock_lambda,
mock_s3, mock_s3,
mock_sqs, mock_sqs,
mock_sts,
mock_xray, mock_xray,
) )
from opentelemetry import propagators from opentelemetry import propagators
from opentelemetry import trace as trace_api
from opentelemetry.context import attach, detach, set_value from opentelemetry.context import attach, detach, set_value
from opentelemetry.instrumentation.botocore import BotocoreInstrumentor from opentelemetry.instrumentation.botocore import BotocoreInstrumentor
from opentelemetry.sdk.resources import Resource
from opentelemetry.test.mock_textmap import MockTextMapPropagator from opentelemetry.test.mock_textmap import MockTextMapPropagator
from opentelemetry.test.test_base import TestBase from opentelemetry.test.test_base import TestBase
def assert_span_http_status_code(span, code):
"""Assert on the span"s "http.status_code" tag"""
tag = span.attributes["http.status_code"]
assert tag == code, "%r != %r" % (tag, code)
class TestBotocoreInstrumentor(TestBase): class TestBotocoreInstrumentor(TestBase):
"""Botocore integration testsuite""" """Botocore integration testsuite"""
@ -66,20 +62,17 @@ class TestBotocoreInstrumentor(TestBase):
assert spans assert spans
span = spans[0] span = spans[0]
self.assertEqual(len(spans), 1) self.assertEqual(len(spans), 1)
self.assertEqual(span.attributes["aws.agent"], "botocore")
self.assertEqual(span.attributes["aws.region"], "us-west-2")
self.assertEqual(span.attributes["aws.operation"], "DescribeInstances")
assert_span_http_status_code(span, 200)
self.assertEqual( self.assertEqual(
span.resource, span.attributes,
Resource( {
attributes={ "aws.operation": "DescribeInstances",
"endpoint": "ec2", "aws.region": "us-west-2",
"operation": "describeinstances", "aws.request_id": "fdcdcab1-ae5c-489e-9c33-4637c5dda355",
} "aws.service": "ec2",
), "http.status_code": 200,
},
) )
self.assertEqual(span.name, "ec2.command") self.assertEqual(span.name, "ec2")
@mock_ec2 @mock_ec2
def test_not_recording(self): def test_not_recording(self):
@ -117,13 +110,14 @@ class TestBotocoreInstrumentor(TestBase):
assert spans assert spans
span = spans[0] span = spans[0]
self.assertEqual(len(spans), 2) self.assertEqual(len(spans), 2)
self.assertEqual(span.attributes["aws.operation"], "ListBuckets")
assert_span_http_status_code(span, 200)
self.assertEqual( self.assertEqual(
span.resource, span.attributes,
Resource( {
attributes={"endpoint": "s3", "operation": "listbuckets"} "aws.operation": "ListBuckets",
), "aws.region": "us-west-2",
"aws.service": "s3",
"http.status_code": 200,
},
) )
# testing for span error # testing for span error
@ -134,10 +128,15 @@ class TestBotocoreInstrumentor(TestBase):
assert spans assert spans
span = spans[2] span = spans[2]
self.assertEqual( self.assertEqual(
span.resource, span.attributes,
Resource( {
attributes={"endpoint": "s3", "operation": "listobjects"} "aws.operation": "ListObjects",
), "aws.region": "us-west-2",
"aws.service": "s3",
},
)
self.assertIs(
span.status.status_code, trace_api.status.StatusCode.ERROR,
) )
# Comment test for issue 1088 # Comment test for issue 1088
@ -148,29 +147,42 @@ class TestBotocoreInstrumentor(TestBase):
location = {"LocationConstraint": "us-west-2"} location = {"LocationConstraint": "us-west-2"}
s3.create_bucket(Bucket="mybucket", CreateBucketConfiguration=location) s3.create_bucket(Bucket="mybucket", CreateBucketConfiguration=location)
s3.put_object(**params) s3.put_object(**params)
s3.get_object(Bucket="mybucket", Key="foo")
spans = self.memory_exporter.get_finished_spans() spans = self.memory_exporter.get_finished_spans()
assert spans assert spans
span = spans[0] self.assertEqual(len(spans), 3)
self.assertEqual(len(spans), 2) create_bucket_attributes = spans[0].attributes
self.assertEqual(span.attributes["aws.operation"], "CreateBucket")
assert_span_http_status_code(span, 200)
self.assertEqual( self.assertEqual(
span.resource, create_bucket_attributes,
Resource( {
attributes={"endpoint": "s3", "operation": "createbucket"} "aws.operation": "CreateBucket",
), "aws.region": "us-west-2",
"aws.service": "s3",
"http.status_code": 200,
},
) )
self.assertEqual(spans[1].attributes["aws.operation"], "PutObject") put_object_attributes = spans[1].attributes
self.assertEqual( self.assertEqual(
spans[1].resource, put_object_attributes,
Resource(attributes={"endpoint": "s3", "operation": "putobject"}), {
) "aws.operation": "PutObject",
self.assertEqual(spans[1].attributes["params.Key"], str(params["Key"])) "aws.region": "us-west-2",
self.assertEqual( "aws.service": "s3",
spans[1].attributes["params.Bucket"], str(params["Bucket"]) "http.status_code": 200,
},
) )
self.assertTrue("params.Body" not in spans[1].attributes.keys()) self.assertTrue("params.Body" not in spans[1].attributes.keys())
get_object_attributes = spans[2].attributes
self.assertEqual(
get_object_attributes,
{
"aws.operation": "GetObject",
"aws.region": "us-west-2",
"aws.service": "s3",
"http.status_code": 200,
},
)
@mock_sqs @mock_sqs
def test_sqs_client(self): def test_sqs_client(self):
@ -182,14 +194,62 @@ class TestBotocoreInstrumentor(TestBase):
assert spans assert spans
span = spans[0] span = spans[0]
self.assertEqual(len(spans), 1) self.assertEqual(len(spans), 1)
self.assertEqual(span.attributes["aws.region"], "us-east-1") actual = span.attributes
self.assertEqual(span.attributes["aws.operation"], "ListQueues") self.assertRegex(actual["aws.request_id"], r"[A-Z0-9]{52}")
assert_span_http_status_code(span, 200) del actual["aws.request_id"]
self.assertEqual( self.assertEqual(
span.resource, actual,
Resource( {
attributes={"endpoint": "sqs", "operation": "listqueues"} "aws.operation": "ListQueues",
), "aws.region": "us-east-1",
"aws.service": "sqs",
"http.status_code": 200,
},
)
@mock_sqs
def test_sqs_send_message(self):
sqs = self.session.create_client("sqs", region_name="us-east-1")
test_queue_name = "test_queue_name"
response = sqs.create_queue(QueueName=test_queue_name)
sqs.send_message(
QueueUrl=response["QueueUrl"], MessageBody="Test SQS MESSAGE!"
)
spans = self.memory_exporter.get_finished_spans()
assert spans
self.assertEqual(len(spans), 2)
create_queue_attributes = spans[0].attributes
self.assertRegex(
create_queue_attributes["aws.request_id"], r"[A-Z0-9]{52}"
)
del create_queue_attributes["aws.request_id"]
self.assertEqual(
create_queue_attributes,
{
"aws.operation": "CreateQueue",
"aws.region": "us-east-1",
"aws.service": "sqs",
"http.status_code": 200,
},
)
send_msg_attributes = spans[1].attributes
self.assertRegex(
send_msg_attributes["aws.request_id"], r"[A-Z0-9]{52}"
)
del send_msg_attributes["aws.request_id"]
self.assertEqual(
send_msg_attributes,
{
"aws.operation": "SendMessage",
"aws.queue_url": response["QueueUrl"],
"aws.region": "us-east-1",
"aws.service": "sqs",
"http.status_code": 200,
},
) )
@mock_kinesis @mock_kinesis
@ -204,14 +264,14 @@ class TestBotocoreInstrumentor(TestBase):
assert spans assert spans
span = spans[0] span = spans[0]
self.assertEqual(len(spans), 1) self.assertEqual(len(spans), 1)
self.assertEqual(span.attributes["aws.region"], "us-east-1")
self.assertEqual(span.attributes["aws.operation"], "ListStreams")
assert_span_http_status_code(span, 200)
self.assertEqual( self.assertEqual(
span.resource, span.attributes,
Resource( {
attributes={"endpoint": "kinesis", "operation": "liststreams"} "aws.operation": "ListStreams",
), "aws.region": "us-east-1",
"aws.service": "kinesis",
"http.status_code": 200,
},
) )
@mock_kinesis @mock_kinesis
@ -249,14 +309,14 @@ class TestBotocoreInstrumentor(TestBase):
assert spans assert spans
span = spans[0] span = spans[0]
self.assertEqual(len(spans), 1) self.assertEqual(len(spans), 1)
self.assertEqual(span.attributes["aws.region"], "us-east-1")
self.assertEqual(span.attributes["aws.operation"], "ListFunctions")
assert_span_http_status_code(span, 200)
self.assertEqual( self.assertEqual(
span.resource, span.attributes,
Resource( {
attributes={"endpoint": "lambda", "operation": "listfunctions"} "aws.operation": "ListFunctions",
), "aws.region": "us-east-1",
"aws.service": "lambda",
"http.status_code": 200,
},
) )
@mock_kms @mock_kms
@ -269,12 +329,38 @@ class TestBotocoreInstrumentor(TestBase):
assert spans assert spans
span = spans[0] span = spans[0]
self.assertEqual(len(spans), 1) self.assertEqual(len(spans), 1)
self.assertEqual(span.attributes["aws.region"], "us-east-1")
self.assertEqual(span.attributes["aws.operation"], "ListKeys")
assert_span_http_status_code(span, 200)
self.assertEqual( self.assertEqual(
span.resource, span.attributes,
Resource(attributes={"endpoint": "kms", "operation": "listkeys"}), {
"aws.operation": "ListKeys",
"aws.region": "us-east-1",
"aws.service": "kms",
"http.status_code": 200,
},
)
# checking for protection on kms against security leak
self.assertTrue("params" not in span.attributes.keys())
@mock_sts
def test_sts_client(self):
sts = self.session.create_client("sts", region_name="us-east-1")
sts.get_caller_identity()
spans = self.memory_exporter.get_finished_spans()
assert spans
span = spans[0]
self.assertEqual(len(spans), 1)
self.assertEqual(
span.attributes,
{
"aws.operation": "GetCallerIdentity",
"aws.region": "us-east-1",
"aws.request_id": "c6104cbe-af31-11e0-8154-cbc7ccf896c7",
"aws.service": "sts",
"http.status_code": 200,
},
) )
# checking for protection on sts against security leak # checking for protection on sts against security leak
@ -299,25 +385,19 @@ class TestBotocoreInstrumentor(TestBase):
ec2.describe_instances() ec2.describe_instances()
spans = self.memory_exporter.get_finished_spans() spans = self.memory_exporter.get_finished_spans()
assert spans
span = spans[0]
self.assertEqual(len(spans), 1) self.assertEqual(len(spans), 1)
self.assertEqual(span.attributes["aws.agent"], "botocore") span = spans[0]
self.assertEqual(span.attributes["aws.region"], "us-west-2") describe_instances_attributes = spans[0].attributes
self.assertEqual( self.assertEqual(
span.attributes["aws.operation"], "DescribeInstances" describe_instances_attributes,
{
"aws.operation": "DescribeInstances",
"aws.region": "us-west-2",
"aws.request_id": "fdcdcab1-ae5c-489e-9c33-4637c5dda355",
"aws.service": "ec2",
"http.status_code": 200,
},
) )
assert_span_http_status_code(span, 200)
self.assertEqual(
span.resource,
Resource(
attributes={
"endpoint": "ec2",
"operation": "describeinstances",
}
),
)
self.assertEqual(span.name, "ec2.command")
self.assertIn(MockTextMapPropagator.TRACE_ID_KEY, headers) self.assertIn(MockTextMapPropagator.TRACE_ID_KEY, headers)
self.assertEqual( self.assertEqual(
@ -345,3 +425,74 @@ class TestBotocoreInstrumentor(TestBase):
spans = self.memory_exporter.get_finished_spans() spans = self.memory_exporter.get_finished_spans()
self.assertEqual(0, len(spans)) self.assertEqual(0, len(spans))
@mock_dynamodb2
def test_dynamodb_client(self):
ddb = self.session.create_client("dynamodb", region_name="us-west-2")
test_table_name = "test_table_name"
ddb.create_table(
AttributeDefinitions=[
{"AttributeName": "id", "AttributeType": "S"},
],
KeySchema=[{"AttributeName": "id", "KeyType": "HASH"}],
ProvisionedThroughput={
"ReadCapacityUnits": 5,
"WriteCapacityUnits": 5,
},
TableName=test_table_name,
)
ddb.put_item(TableName=test_table_name, Item={"id": {"S": "test_key"}})
ddb.get_item(TableName=test_table_name, Key={"id": {"S": "test_key"}})
spans = self.memory_exporter.get_finished_spans()
assert spans
self.assertEqual(len(spans), 3)
create_table_attributes = spans[0].attributes
self.assertRegex(
create_table_attributes["aws.request_id"], r"[A-Z0-9]{52}"
)
del create_table_attributes["aws.request_id"]
self.assertEqual(
create_table_attributes,
{
"aws.operation": "CreateTable",
"aws.region": "us-west-2",
"aws.service": "dynamodb",
"aws.table_name": "test_table_name",
"http.status_code": 200,
},
)
put_item_attributes = spans[1].attributes
self.assertRegex(
put_item_attributes["aws.request_id"], r"[A-Z0-9]{52}"
)
del put_item_attributes["aws.request_id"]
self.assertEqual(
put_item_attributes,
{
"aws.operation": "PutItem",
"aws.region": "us-west-2",
"aws.service": "dynamodb",
"aws.table_name": "test_table_name",
"http.status_code": 200,
},
)
get_item_attributes = spans[2].attributes
self.assertRegex(
get_item_attributes["aws.request_id"], r"[A-Z0-9]{52}"
)
del get_item_attributes["aws.request_id"]
self.assertEqual(
get_item_attributes,
{
"aws.operation": "GetItem",
"aws.region": "us-west-2",
"aws.service": "dynamodb",
"aws.table_name": "test_table_name",
"http.status_code": 200,
},
)

View File

@ -63,6 +63,6 @@ accomplish this as shown in the example above.
References References
---------- ----------
* `OpenTelemetry Celery Instrumentation <https://opentelemetry-python.readthedocs.io/en/latest/instrumentation/celery/celery.html>`_ * `OpenTelemetry Celery Instrumentation <https://opentelemetry-python-contrib.readthedocs.io/en/latest/instrumentation/celery/celery.html>`_
* `OpenTelemetry Project <https://opentelemetry.io/>`_ * `OpenTelemetry Project <https://opentelemetry.io/>`_

View File

@ -19,7 +19,7 @@ long_description = file: README.rst
long_description_content_type = text/x-rst long_description_content_type = text/x-rst
author = OpenTelemetry Authors author = OpenTelemetry Authors
author_email = cncf-opentelemetry-contributors@lists.cncf.io author_email = cncf-opentelemetry-contributors@lists.cncf.io
url = https://github.com/open-telemetry/opentelemetry-python/tree/master/instrumentation/opentelemetry-instrumentation-celery url = https://github.com/open-telemetry/opentelemetry-python-contrib/tree/master/instrumentation/opentelemetry-instrumentation-celery
platforms = any platforms = any
license = Apache-2.0 license = Apache-2.0
classifiers = classifiers =

View File

@ -2,7 +2,10 @@
## Unreleased ## Unreleased
Stop capturing query parameters by default - Update dbapi and its dependant instrumentations to follow semantic conventions
([#195](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/195))
- Stop capturing query parameters by default
([#156](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/156)) ([#156](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/156))
## Version 0.13b0 ## Version 0.13b0

View File

@ -17,5 +17,5 @@ Installation
References References
---------- ----------
* `OpenTelemetry Database API Instrumentation <https://opentelemetry-python.readthedocs.io/en/latest/instrumentation/dbapi/dbapi.html>`_ * `OpenTelemetry Database API Instrumentation <https://opentelemetry-python-contrib.readthedocs.io/en/latest/instrumentation/dbapi/dbapi.html>`_
* `OpenTelemetry Project <https://opentelemetry.io/>`_ * `OpenTelemetry Project <https://opentelemetry.io/>`_

View File

@ -19,7 +19,7 @@ long_description = file: README.rst
long_description_content_type = text/x-rst long_description_content_type = text/x-rst
author = OpenTelemetry Authors author = OpenTelemetry Authors
author_email = cncf-opentelemetry-contributors@lists.cncf.io author_email = cncf-opentelemetry-contributors@lists.cncf.io
url = https://github.com/open-telemetry/opentelemetry-python/tree/master/instrumentation/opentelemetry-instrumentation-dbapi url = https://github.com/open-telemetry/opentelemetry-python-contrib/tree/master/instrumentation/opentelemetry-instrumentation-dbapi
platforms = any platforms = any
license = Apache-2.0 license = Apache-2.0
classifiers = classifiers =

View File

@ -143,7 +143,7 @@ def wrap_connect(
def unwrap_connect( def unwrap_connect(
connect_module: typing.Callable[..., typing.Any], connect_method_name: str, connect_module: typing.Callable[..., typing.Any], connect_method_name: str
): ):
"""Disable integration with DB API library. """Disable integration with DB API library.
https://www.python.org/dev/peps/pep-0249/ https://www.python.org/dev/peps/pep-0249/
@ -251,8 +251,7 @@ class DatabaseApiIntegration:
args: typing.Tuple[typing.Any, typing.Any], args: typing.Tuple[typing.Any, typing.Any],
kwargs: typing.Dict[typing.Any, typing.Any], kwargs: typing.Dict[typing.Any, typing.Any],
): ):
"""Add object proxy to connection object. """Add object proxy to connection object."""
"""
connection = connect_method(*args, **kwargs) connection = connect_method(*args, **kwargs)
self.get_connection_attributes(connection) self.get_connection_attributes(connection)
return get_traced_connection_proxy(connection, self) return get_traced_connection_proxy(connection, self)
@ -278,6 +277,9 @@ class DatabaseApiIntegration:
self.database = self.database.decode(errors="ignore") self.database = self.database.decode(errors="ignore")
self.name += "." + self.database self.name += "." + self.database
user = self.connection_props.get("user") user = self.connection_props.get("user")
# PyMySQL encodes this data
if user and isinstance(user, bytes):
user = user.decode()
if user is not None: if user is not None:
self.span_attributes["db.user"] = str(user) self.span_attributes["db.user"] = str(user)
host = self.connection_props.get("host") host = self.connection_props.get("host")
@ -325,8 +327,10 @@ class TracedCursor:
span.set_attribute( span.set_attribute(
"component", self._db_api_integration.database_component "component", self._db_api_integration.database_component
) )
span.set_attribute("db.type", self._db_api_integration.database_type) span.set_attribute(
span.set_attribute("db.instance", self._db_api_integration.database) "db.system", self._db_api_integration.database_component
)
span.set_attribute("db.name", self._db_api_integration.database)
span.set_attribute("db.statement", statement) span.set_attribute("db.statement", statement)
for ( for (
@ -344,9 +348,16 @@ class TracedCursor:
*args: typing.Tuple[typing.Any, typing.Any], *args: typing.Tuple[typing.Any, typing.Any],
**kwargs: typing.Dict[typing.Any, typing.Any] **kwargs: typing.Dict[typing.Any, typing.Any]
): ):
name = ""
if args:
name = args[0]
elif self._db_api_integration.database:
name = self._db_api_integration.database
else:
name = self._db_api_integration.name
with self._db_api_integration.get_tracer().start_as_current_span( with self._db_api_integration.get_tracer().start_as_current_span(
self._db_api_integration.name, kind=SpanKind.CLIENT name, kind=SpanKind.CLIENT
) as span: ) as span:
self._populate_span(span, *args) self._populate_span(span, *args)
try: try:

View File

@ -50,19 +50,19 @@ class TestDBApiIntegration(TestBase):
spans_list = self.memory_exporter.get_finished_spans() spans_list = self.memory_exporter.get_finished_spans()
self.assertEqual(len(spans_list), 1) self.assertEqual(len(spans_list), 1)
span = spans_list[0] span = spans_list[0]
self.assertEqual(span.name, "testcomponent.testdatabase") self.assertEqual(span.name, "Test query")
self.assertIs(span.kind, trace_api.SpanKind.CLIENT) self.assertIs(span.kind, trace_api.SpanKind.CLIENT)
self.assertEqual(span.attributes["component"], "testcomponent") self.assertEqual(span.attributes["component"], "testcomponent")
self.assertEqual(span.attributes["db.type"], "testtype") self.assertEqual(span.attributes["db.system"], "testcomponent")
self.assertEqual(span.attributes["db.instance"], "testdatabase") self.assertEqual(span.attributes["db.name"], "testdatabase")
self.assertEqual(span.attributes["db.statement"], "Test query") self.assertEqual(span.attributes["db.statement"], "Test query")
self.assertFalse("db.statement.parameters" in span.attributes) self.assertFalse("db.statement.parameters" in span.attributes)
self.assertEqual(span.attributes["db.user"], "testuser") self.assertEqual(span.attributes["db.user"], "testuser")
self.assertEqual(span.attributes["net.peer.name"], "testhost") self.assertEqual(span.attributes["net.peer.name"], "testhost")
self.assertEqual(span.attributes["net.peer.port"], 123) self.assertEqual(span.attributes["net.peer.port"], 123)
self.assertIs( self.assertIs(
span.status.status_code, trace_api.status.StatusCode.UNSET, span.status.status_code, trace_api.status.StatusCode.UNSET
) )
def test_span_succeeded_with_capture_of_statement_parameters(self): def test_span_succeeded_with_capture_of_statement_parameters(self):
@ -93,12 +93,12 @@ class TestDBApiIntegration(TestBase):
spans_list = self.memory_exporter.get_finished_spans() spans_list = self.memory_exporter.get_finished_spans()
self.assertEqual(len(spans_list), 1) self.assertEqual(len(spans_list), 1)
span = spans_list[0] span = spans_list[0]
self.assertEqual(span.name, "testcomponent.testdatabase") self.assertEqual(span.name, "Test query")
self.assertIs(span.kind, trace_api.SpanKind.CLIENT) self.assertIs(span.kind, trace_api.SpanKind.CLIENT)
self.assertEqual(span.attributes["component"], "testcomponent") self.assertEqual(span.attributes["component"], "testcomponent")
self.assertEqual(span.attributes["db.type"], "testtype") self.assertEqual(span.attributes["db.system"], "testcomponent")
self.assertEqual(span.attributes["db.instance"], "testdatabase") self.assertEqual(span.attributes["db.name"], "testdatabase")
self.assertEqual(span.attributes["db.statement"], "Test query") self.assertEqual(span.attributes["db.statement"], "Test query")
self.assertEqual( self.assertEqual(
span.attributes["db.statement.parameters"], span.attributes["db.statement.parameters"],
@ -108,7 +108,7 @@ class TestDBApiIntegration(TestBase):
self.assertEqual(span.attributes["net.peer.name"], "testhost") self.assertEqual(span.attributes["net.peer.name"], "testhost")
self.assertEqual(span.attributes["net.peer.port"], 123) self.assertEqual(span.attributes["net.peer.port"], 123)
self.assertIs( self.assertIs(
span.status.status_code, trace_api.status.StatusCode.UNSET, span.status.status_code, trace_api.status.StatusCode.UNSET
) )
def test_span_not_recording(self): def test_span_not_recording(self):
@ -159,7 +159,7 @@ class TestDBApiIntegration(TestBase):
span = spans_list[0] span = spans_list[0]
self.assertEqual(span.attributes["db.statement"], "Test query") self.assertEqual(span.attributes["db.statement"], "Test query")
self.assertIs( self.assertIs(
span.status.status_code, trace_api.status.StatusCode.ERROR, span.status.status_code, trace_api.status.StatusCode.ERROR
) )
self.assertEqual(span.status.description, "Test Exception") self.assertEqual(span.status.description, "Test Exception")

View File

@ -49,5 +49,5 @@ References
---------- ----------
* `Django <https://www.djangoproject.com/>`_ * `Django <https://www.djangoproject.com/>`_
* `OpenTelemetry Instrumentation for Django <https://opentelemetry-python.readthedocs.io/en/latest/instrumentation/django/django.html>`_ * `OpenTelemetry Instrumentation for Django <https://opentelemetry-python-contrib.readthedocs.io/en/latest/instrumentation/django/django.html>`_
* `OpenTelemetry Project <https://opentelemetry.io/>`_ * `OpenTelemetry Project <https://opentelemetry.io/>`_

View File

@ -19,7 +19,7 @@ long_description = file: README.rst
long_description_content_type = text/x-rst long_description_content_type = text/x-rst
author = OpenTelemetry Authors author = OpenTelemetry Authors
author_email = cncf-opentelemetry-contributors@lists.cncf.io author_email = cncf-opentelemetry-contributors@lists.cncf.io
url = https://github.com/open-telemetry/opentelemetry-python/tree/master/instrumentation/opentelemetry-instrumentation-django url = https://github.com/open-telemetry/opentelemetry-python-contrib/tree/master/instrumentation/opentelemetry-instrumentation-django
platforms = any platforms = any
license = Apache-2.0 license = Apache-2.0
classifiers = classifiers =

View File

@ -19,5 +19,5 @@ Installation
References References
---------- ----------
* `OpenTelemetry elasticsearch Integration <https://opentelemetry-python.readthedocs.io/en/latest/instrumentation/elasticsearch/elasticsearch.html>`_ * `OpenTelemetry elasticsearch Integration <https://opentelemetry-python-contrib.readthedocs.io/en/latest/instrumentation/elasticsearch/elasticsearch.html>`_
* `OpenTelemetry Project <https://opentelemetry.io/>`_ * `OpenTelemetry Project <https://opentelemetry.io/>`_

View File

@ -19,7 +19,7 @@ long_description = file: README.rst
long_description_content_type = text/x-rst long_description_content_type = text/x-rst
author = OpenTelemetry Authors author = OpenTelemetry Authors
author_email = cncf-opentelemetry-contributors@lists.cncf.io author_email = cncf-opentelemetry-contributors@lists.cncf.io
url = https://github.com/open-telemetry/opentelemetry-python/tree/master/instrumentation/opentelemetry-instrumentation-elasticsearch url = https://github.com/open-telemetry/opentelemetry-python-contrib/tree/master/instrumentation/opentelemetry-instrumentation-elasticsearch
platforms = any platforms = any
license = Apache-2.0 license = Apache-2.0
classifiers = classifiers =

View File

@ -49,5 +49,5 @@ Falcon Request object reference: https://falcon.readthedocs.io/en/stable/api/req
References References
---------- ----------
* `OpenTelemetry Falcon Instrumentation <https://opentelemetry-python.readthedocs.io/en/latest/instrumentation/falcon/falcon.html>`_ * `OpenTelemetry Falcon Instrumentation <https://opentelemetry-python-contrib.readthedocs.io/en/latest/instrumentation/falcon/falcon.html>`_
* `OpenTelemetry Project <https://opentelemetry.io/>`_ * `OpenTelemetry Project <https://opentelemetry.io/>`_

View File

@ -19,7 +19,7 @@ long_description = file: README.rst
long_description_content_type = text/x-rst long_description_content_type = text/x-rst
author = OpenTelemetry Authors author = OpenTelemetry Authors
author_email = cncf-opentelemetry-contributors@lists.cncf.io author_email = cncf-opentelemetry-contributors@lists.cncf.io
url = https://github.com/open-telemetry/opentelemetry-python/tree/master/instrumentation/opentelemetry-instrumentation-falcon url = https://github.com/open-telemetry/opentelemetry-python-contrib/tree/master/instrumentation/opentelemetry-instrumentation-falcon
platforms = any platforms = any
license = Apache-2.0 license = Apache-2.0
classifiers = classifiers =

View File

@ -19,7 +19,7 @@ long_description = file: README.rst
long_description_content_type = text/x-rst long_description_content_type = text/x-rst
author = OpenTelemetry Authors author = OpenTelemetry Authors
author_email = cncf-opentelemetry-contributors@lists.cncf.io author_email = cncf-opentelemetry-contributors@lists.cncf.io
url = https://github.com/open-telemetry/opentelemetry-python/tree/master/instrumentation/opentelemetry-instrumentation-fastapi url = https://github.com/open-telemetry/opentelemetry-python-contrib/tree/master/instrumentation/opentelemetry-instrumentation-fastapi
platforms = any platforms = any
license = Apache-2.0 license = Apache-2.0
classifiers = classifiers =

View File

@ -2,6 +2,9 @@
## Unreleased ## Unreleased
- Add span name callback
([#152](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/152))
## Version 0.15b0 ## Version 0.15b0
Released 2020-11-02 Released 2020-11-02

View File

@ -34,5 +34,5 @@ will exclude requests such as ``https://site/client/123/info`` and ``https://sit
References References
---------- ----------
* `OpenTelemetry Flask Instrumentation <https://opentelemetry-python.readthedocs.io/en/latest/instrumentation/flask/flask.html>`_ * `OpenTelemetry Flask Instrumentation <https://opentelemetry-python-contrib.readthedocs.io/en/stable/instrumentation/flask/flask.html>`_
* `OpenTelemetry Project <https://opentelemetry.io/>`_ * `OpenTelemetry Project <https://opentelemetry.io/>`_

View File

@ -19,7 +19,7 @@ long_description = file: README.rst
long_description_content_type = text/x-rst long_description_content_type = text/x-rst
author = OpenTelemetry Authors author = OpenTelemetry Authors
author_email = cncf-opentelemetry-contributors@lists.cncf.io author_email = cncf-opentelemetry-contributors@lists.cncf.io
url = https://github.com/open-telemetry/opentelemetry-python/tree/master/instrumentation/opentelemetry-instrumentation-flask url = https://github.com/open-telemetry/opentelemetry-python-contrib/tree/master/instrumentation/opentelemetry-instrumentation-flask
platforms = any platforms = any
license = Apache-2.0 license = Apache-2.0
classifiers = classifiers =

View File

@ -20,7 +20,7 @@ This library builds on the OpenTelemetry WSGI middleware to track web requests
in Flask applications. In addition to opentelemetry-instrumentation-wsgi, it supports in Flask applications. In addition to opentelemetry-instrumentation-wsgi, it supports
flask-specific features such as: flask-specific features such as:
* The Flask endpoint name is used as the Span name. * The Flask url rule pattern is used as the Span name.
* The ``http.route`` Span attribute is set so that one can see which URL rule * The ``http.route`` Span attribute is set so that one can see which URL rule
matched a request. matched a request.
@ -75,6 +75,15 @@ def get_excluded_urls():
_excluded_urls = get_excluded_urls() _excluded_urls = get_excluded_urls()
def get_default_span_name():
span_name = ""
try:
span_name = flask.request.url_rule.rule
except AttributeError:
span_name = otel_wsgi.get_default_span_name(flask.request.environ)
return span_name
def _rewrapped_app(wsgi_app): def _rewrapped_app(wsgi_app):
def _wrapped_app(environ, start_response): def _wrapped_app(environ, start_response):
# We want to measure the time for route matching, etc. # We want to measure the time for route matching, etc.
@ -105,18 +114,13 @@ def _rewrapped_app(wsgi_app):
return _wrapped_app return _wrapped_app
def _wrapped_before_request(name_callback):
def _before_request(): def _before_request():
if _excluded_urls.url_disabled(flask.request.url): if _excluded_urls.url_disabled(flask.request.url):
return return
environ = flask.request.environ environ = flask.request.environ
span_name = None span_name = name_callback()
try:
span_name = flask.request.url_rule.rule
except AttributeError:
pass
if span_name is None:
span_name = otel_wsgi.get_default_span_name(environ)
token = context.attach( token = context.attach(
propagators.extract(otel_wsgi.carrier_getter, environ) propagators.extract(otel_wsgi.carrier_getter, environ)
) )
@ -143,6 +147,8 @@ def _before_request():
environ[_ENVIRON_SPAN_KEY] = span environ[_ENVIRON_SPAN_KEY] = span
environ[_ENVIRON_TOKEN] = token environ[_ENVIRON_TOKEN] = token
return _before_request
def _teardown_request(exc): def _teardown_request(exc):
if _excluded_urls.url_disabled(flask.request.url): if _excluded_urls.url_disabled(flask.request.url):
@ -167,12 +173,19 @@ def _teardown_request(exc):
class _InstrumentedFlask(flask.Flask): class _InstrumentedFlask(flask.Flask):
name_callback = get_default_span_name
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
self._original_wsgi_ = self.wsgi_app self._original_wsgi_ = self.wsgi_app
self.wsgi_app = _rewrapped_app(self.wsgi_app) self.wsgi_app = _rewrapped_app(self.wsgi_app)
_before_request = _wrapped_before_request(
_InstrumentedFlask.name_callback
)
self._before_request = _before_request
self.before_request(_before_request) self.before_request(_before_request)
self.teardown_request(_teardown_request) self.teardown_request(_teardown_request)
@ -186,9 +199,14 @@ class FlaskInstrumentor(BaseInstrumentor):
def _instrument(self, **kwargs): def _instrument(self, **kwargs):
self._original_flask = flask.Flask self._original_flask = flask.Flask
name_callback = kwargs.get("name_callback")
if callable(name_callback):
_InstrumentedFlask.name_callback = name_callback
flask.Flask = _InstrumentedFlask flask.Flask = _InstrumentedFlask
def instrument_app(self, app): # pylint: disable=no-self-use def instrument_app(
self, app, name_callback=get_default_span_name
): # pylint: disable=no-self-use
if not hasattr(app, "_is_instrumented"): if not hasattr(app, "_is_instrumented"):
app._is_instrumented = False app._is_instrumented = False
@ -196,6 +214,8 @@ class FlaskInstrumentor(BaseInstrumentor):
app._original_wsgi_app = app.wsgi_app app._original_wsgi_app = app.wsgi_app
app.wsgi_app = _rewrapped_app(app.wsgi_app) app.wsgi_app = _rewrapped_app(app.wsgi_app)
_before_request = _wrapped_before_request(name_callback)
app._before_request = _before_request
app.before_request(_before_request) app.before_request(_before_request)
app.teardown_request(_teardown_request) app.teardown_request(_teardown_request)
app._is_instrumented = True app._is_instrumented = True
@ -215,7 +235,7 @@ class FlaskInstrumentor(BaseInstrumentor):
app.wsgi_app = app._original_wsgi_app app.wsgi_app = app._original_wsgi_app
# FIXME add support for other Flask blueprints that are not None # FIXME add support for other Flask blueprints that are not None
app.before_request_funcs[None].remove(_before_request) app.before_request_funcs[None].remove(app._before_request)
app.teardown_request_funcs[None].remove(_teardown_request) app.teardown_request_funcs[None].remove(_teardown_request)
del app._original_wsgi_app del app._original_wsgi_app

View File

@ -178,3 +178,63 @@ class TestProgrammatic(InstrumentationTest, TestBase, WsgiTestBase):
self.client.get("/excluded_noarg2") self.client.get("/excluded_noarg2")
span_list = self.memory_exporter.get_finished_spans() span_list = self.memory_exporter.get_finished_spans()
self.assertEqual(len(span_list), 1) self.assertEqual(len(span_list), 1)
class TestProgrammaticCustomSpanName(
InstrumentationTest, TestBase, WsgiTestBase
):
def setUp(self):
super().setUp()
def custom_span_name():
return "flask-custom-span-name"
self.app = Flask(__name__)
FlaskInstrumentor().instrument_app(
self.app, name_callback=custom_span_name
)
self._common_initialization()
def tearDown(self):
super().tearDown()
with self.disable_logging():
FlaskInstrumentor().uninstrument_app(self.app)
def test_custom_span_name(self):
self.client.get("/hello/123")
span_list = self.memory_exporter.get_finished_spans()
self.assertEqual(len(span_list), 1)
self.assertEqual(span_list[0].name, "flask-custom-span-name")
class TestProgrammaticCustomSpanNameCallbackWithoutApp(
InstrumentationTest, TestBase, WsgiTestBase
):
def setUp(self):
super().setUp()
def custom_span_name():
return "instrument-without-app"
FlaskInstrumentor().instrument(name_callback=custom_span_name)
# pylint: disable=import-outside-toplevel,reimported,redefined-outer-name
from flask import Flask
self.app = Flask(__name__)
self._common_initialization()
def tearDown(self):
super().tearDown()
with self.disable_logging():
FlaskInstrumentor().uninstrument()
def test_custom_span_name(self):
self.client.get("/hello/123")
span_list = self.memory_exporter.get_finished_spans()
self.assertEqual(len(span_list), 1)
self.assertEqual(span_list[0].name, "instrument-without-app")

View File

@ -19,7 +19,7 @@ long_description = file: README.rst
long_description_content_type = text/x-rst long_description_content_type = text/x-rst
author = OpenTelemetry Authors author = OpenTelemetry Authors
author_email = cncf-opentelemetry-contributors@lists.cncf.io author_email = cncf-opentelemetry-contributors@lists.cncf.io
url = https://github.com/open-telemetry/opentelemetry-python/tree/master/instrumentation/opentelemetry-instrumentation-grpc url = https://github.com/open-telemetry/opentelemetry-python-contrib/tree/master/instrumentation/opentelemetry-instrumentation-grpc
platforms = any platforms = any
license = Apache-2.0 license = Apache-2.0
classifiers = classifiers =

View File

@ -87,13 +87,17 @@ class OpenTelemetryClientInterceptor(
def __init__(self, tracer, exporter, interval): def __init__(self, tracer, exporter, interval):
self._tracer = tracer self._tracer = tracer
self._meter = None self._accumulator = None
if exporter and interval: if exporter and interval:
self._meter = metrics.get_meter(__name__) self._accumulator = metrics.get_meter(__name__)
self.controller = PushController( self.controller = PushController(
meter=self._meter, exporter=exporter, interval=interval accumulator=self._accumulator,
exporter=exporter,
interval=interval,
)
self._metrics_recorder = TimedMetricRecorder(
self._accumulator, "client",
) )
self._metrics_recorder = TimedMetricRecorder(self._meter, "client")
def _start_span(self, method): def _start_span(self, method):
return self._tracer.start_as_current_span( return self._tracer.start_as_current_span(

View File

@ -17,5 +17,5 @@ Installation
References References
---------- ----------
* `OpenTelemetry jinja2 integration <https://opentelemetry-python.readthedocs.io/en/latest/instrumentation/jinja2/jinja2.html>`_ * `OpenTelemetry jinja2 integration <https://opentelemetry-python-contrib.readthedocs.io/en/latest/instrumentation/jinja2/jinja2.html>`_
* `OpenTelemetry Project <https://opentelemetry.io/>`_ * `OpenTelemetry Project <https://opentelemetry.io/>`_

View File

@ -19,7 +19,7 @@ long_description = file: README.rst
long_description_content_type = text/x-rst long_description_content_type = text/x-rst
author = OpenTelemetry Authors author = OpenTelemetry Authors
author_email = cncf-opentelemetry-contributors@lists.cncf.io author_email = cncf-opentelemetry-contributors@lists.cncf.io
url = https://github.com/open-telemetry/opentelemetry-python/tree/master/instrumentation/opentelemetry-instrumentation-jinja2 url = https://github.com/open-telemetry/opentelemetry-python-contrib/tree/master/instrumentation/opentelemetry-instrumentation-jinja2
platforms = any platforms = any
license = Apache-2.0 license = Apache-2.0
classifiers = classifiers =

View File

@ -20,6 +20,6 @@ Installation
References References
---------- ----------
* `OpenTelemetry MySQL Instrumentation <https://opentelemetry-python.readthedocs.io/en/latest/instrumentation/mysql/mysql.html>`_ * `OpenTelemetry MySQL Instrumentation <https://opentelemetry-python-contrib.readthedocs.io/en/latest/instrumentation/mysql/mysql.html>`_
* `OpenTelemetry Project <https://opentelemetry.io/>`_ * `OpenTelemetry Project <https://opentelemetry.io/>`_

View File

@ -19,7 +19,7 @@ long_description = file: README.rst
long_description_content_type = text/x-rst long_description_content_type = text/x-rst
author = OpenTelemetry Authors author = OpenTelemetry Authors
author_email = cncf-opentelemetry-contributors@lists.cncf.io author_email = cncf-opentelemetry-contributors@lists.cncf.io
url = https://github.com/open-telemetry/opentelemetry-python/tree/master/instrumentation/opentelemetry-instrumentation-mysql url = https://github.com/open-telemetry/opentelemetry-python-contrib/tree/master/instrumentation/opentelemetry-instrumentation-mysql
platforms = any platforms = any
license = Apache-2.0 license = Apache-2.0
classifiers = classifiers =

View File

@ -16,5 +16,5 @@ Installation
References References
---------- ----------
* `OpenTelemetry Psycopg Instrumentation <https://opentelemetry-python.readthedocs.io/en/latest/instrumentation/psycopg2/psycopg2.html>`_ * `OpenTelemetry Psycopg Instrumentation <https://opentelemetry-python-contrib.readthedocs.io/en/latest/instrumentation/psycopg2/psycopg2.html>`_
* `OpenTelemetry Project <https://opentelemetry.io/>`_ * `OpenTelemetry Project <https://opentelemetry.io/>`_

View File

@ -19,7 +19,7 @@ long_description = file: README.rst
long_description_content_type = text/x-rst long_description_content_type = text/x-rst
author = OpenTelemetry Authors author = OpenTelemetry Authors
author_email = cncf-opentelemetry-contributors@lists.cncf.io author_email = cncf-opentelemetry-contributors@lists.cncf.io
url = https://github.com/open-telemetry/opentelemetry-python/tree/master/instrumentation/opentelemetry-instrumentation-psycopg2 url = https://github.com/open-telemetry/opentelemetry-python-contrib/tree/master/instrumentation/opentelemetry-instrumentation-psycopg2
platforms = any platforms = any
license = Apache-2.0 license = Apache-2.0
classifiers = classifiers =

View File

@ -16,5 +16,5 @@ Installation
References References
---------- ----------
* `OpenTelemetry Pymemcache Instrumentation <https://opentelemetry-python.readthedocs.io/en/latest/instrumentation/pymemcache/pymemcache.html>`_ * `OpenTelemetry Pymemcache Instrumentation <https://opentelemetry-python-contrib.readthedocs.io/en/latest/instrumentation/pymemcache/pymemcache.html>`_
* `OpenTelemetry Project <https://opentelemetry.io/>`_ * `OpenTelemetry Project <https://opentelemetry.io/>`_

View File

@ -19,7 +19,7 @@ long_description = file: README.rst
long_description_content_type = text/x-rst long_description_content_type = text/x-rst
author = OpenTelemetry Authors author = OpenTelemetry Authors
author_email = cncf-opentelemetry-contributors@lists.cncf.io author_email = cncf-opentelemetry-contributors@lists.cncf.io
url = https://github.com/open-telemetry/opentelemetry-python/instrumentation/opentelemetry-instrumentation-pymemcache url = https://github.com/open-telemetry/opentelemetry-python-contrib/instrumentation/opentelemetry-instrumentation-pymemcache
platforms = any platforms = any
license = Apache-2.0 license = Apache-2.0
classifiers = classifiers =

View File

@ -2,6 +2,9 @@
## Unreleased ## Unreleased
- Update pymongo instrumentation to follow semantic conventions
([#203](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/203))
## Version 0.14b0 ## Version 0.14b0
Released 2020-10-13 Released 2020-10-13

View File

@ -16,6 +16,6 @@ Installation
References References
---------- ----------
* `OpenTelemetry pymongo Instrumentation <https://opentelemetry-python.readthedocs.io/en/latest/instrumentation/pymongo/pymongo.html>`_ * `OpenTelemetry pymongo Instrumentation <https://opentelemetry-python-contrib.readthedocs.io/en/latest/instrumentation/pymongo/pymongo.html>`_
* `OpenTelemetry Project <https://opentelemetry.io/>`_ * `OpenTelemetry Project <https://opentelemetry.io/>`_

View File

@ -19,7 +19,7 @@ long_description = file: README.rst
long_description_content_type = text/x-rst long_description_content_type = text/x-rst
author = OpenTelemetry Authors author = OpenTelemetry Authors
author_email = cncf-opentelemetry-contributors@lists.cncf.io author_email = cncf-opentelemetry-contributors@lists.cncf.io
url = https://github.com/open-telemetry/opentelemetry-python/tree/master/instrumentation/opentelemetry-instrumentation-pymongo url = https://github.com/open-telemetry/opentelemetry-python-contrib/tree/master/instrumentation/opentelemetry-instrumentation-pymongo
platforms = any platforms = any
license = Apache-2.0 license = Apache-2.0
classifiers = classifiers =

View File

@ -46,7 +46,6 @@ from opentelemetry.trace import SpanKind, get_tracer
from opentelemetry.trace.status import Status, StatusCode from opentelemetry.trace.status import Status, StatusCode
DATABASE_TYPE = "mongodb" DATABASE_TYPE = "mongodb"
COMMAND_ATTRIBUTES = ["filter", "sort", "skip", "limit", "pipeline"]
class CommandTracer(monitoring.CommandListener): class CommandTracer(monitoring.CommandListener):
@ -60,7 +59,7 @@ class CommandTracer(monitoring.CommandListener):
if not self.is_enabled: if not self.is_enabled:
return return
command = event.command.get(event.command_name, "") command = event.command.get(event.command_name, "")
name = DATABASE_TYPE + "." + event.command_name name = event.command_name
statement = event.command_name statement = event.command_name
if command: if command:
name += "." + str(command) name += "." + str(command)
@ -69,23 +68,13 @@ class CommandTracer(monitoring.CommandListener):
try: try:
span = self._tracer.start_span(name, kind=SpanKind.CLIENT) span = self._tracer.start_span(name, kind=SpanKind.CLIENT)
if span.is_recording(): if span.is_recording():
span.set_attribute("component", DATABASE_TYPE) span.set_attribute("db.system", DATABASE_TYPE)
span.set_attribute("db.type", DATABASE_TYPE) span.set_attribute("db.name", event.database_name)
span.set_attribute("db.instance", event.database_name)
span.set_attribute("db.statement", statement) span.set_attribute("db.statement", statement)
if event.connection_id is not None: if event.connection_id is not None:
span.set_attribute("net.peer.name", event.connection_id[0]) span.set_attribute("net.peer.name", event.connection_id[0])
span.set_attribute("net.peer.port", event.connection_id[1]) span.set_attribute("net.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 # Add Span to dictionary
self._span_dict[_get_span_dict_key(event)] = span self._span_dict[_get_span_dict_key(event)] = span
except Exception as ex: # noqa pylint: disable=broad-except except Exception as ex: # noqa pylint: disable=broad-except
@ -101,10 +90,6 @@ class CommandTracer(monitoring.CommandListener):
span = self._pop_span(event) span = self._pop_span(event)
if span is None: if span is None:
return return
if span.is_recording():
span.set_attribute(
"db.mongo.duration_micros", event.duration_micros
)
span.end() span.end()
def failed(self, event: monitoring.CommandFailedEvent): def failed(self, event: monitoring.CommandFailedEvent):
@ -115,9 +100,6 @@ class CommandTracer(monitoring.CommandListener):
if span is None: if span is None:
return return
if span.is_recording(): if span.is_recording():
span.set_attribute(
"db.mongo.duration_micros", event.duration_micros
)
span.set_status(Status(StatusCode.ERROR, event.failure)) span.set_status(Status(StatusCode.ERROR, event.failure))
span.end() span.end()

View File

@ -39,10 +39,6 @@ class TestPymongo(TestBase):
def test_started(self): def test_started(self):
command_attrs = { command_attrs = {
"filter": "filter",
"sort": "sort",
"limit": "limit",
"pipeline": "pipeline",
"command_name": "find", "command_name": "find",
} }
command_tracer = CommandTracer(self.tracer) command_tracer = CommandTracer(self.tracer)
@ -55,24 +51,12 @@ class TestPymongo(TestBase):
# pylint: disable=protected-access # pylint: disable=protected-access
span = command_tracer._pop_span(mock_event) span = command_tracer._pop_span(mock_event)
self.assertIs(span.kind, trace_api.SpanKind.CLIENT) self.assertIs(span.kind, trace_api.SpanKind.CLIENT)
self.assertEqual(span.name, "mongodb.command_name.find") self.assertEqual(span.name, "command_name.find")
self.assertEqual(span.attributes["component"], "mongodb") self.assertEqual(span.attributes["db.system"], "mongodb")
self.assertEqual(span.attributes["db.type"], "mongodb") self.assertEqual(span.attributes["db.name"], "database_name")
self.assertEqual(span.attributes["db.instance"], "database_name")
self.assertEqual(span.attributes["db.statement"], "command_name find") self.assertEqual(span.attributes["db.statement"], "command_name find")
self.assertEqual(span.attributes["net.peer.name"], "test.com") self.assertEqual(span.attributes["net.peer.name"], "test.com")
self.assertEqual(span.attributes["net.peer.port"], "1234") self.assertEqual(span.attributes["net.peer.port"], "1234")
self.assertEqual(
span.attributes["db.mongo.operation_id"], "operation_id"
)
self.assertEqual(
span.attributes["db.mongo.request_id"], "test_request_id"
)
self.assertEqual(span.attributes["db.mongo.filter"], "filter")
self.assertEqual(span.attributes["db.mongo.sort"], "sort")
self.assertEqual(span.attributes["db.mongo.limit"], "limit")
self.assertEqual(span.attributes["db.mongo.pipeline"], "pipeline")
def test_succeeded(self): def test_succeeded(self):
mock_event = MockEvent({}) mock_event = MockEvent({})
@ -82,9 +66,6 @@ class TestPymongo(TestBase):
spans_list = self.memory_exporter.get_finished_spans() spans_list = self.memory_exporter.get_finished_spans()
self.assertEqual(len(spans_list), 1) self.assertEqual(len(spans_list), 1)
span = spans_list[0] span = spans_list[0]
self.assertEqual(
span.attributes["db.mongo.duration_micros"], "duration_micros"
)
self.assertIs( self.assertIs(
span.status.status_code, trace_api.status.StatusCode.UNSET span.status.status_code, trace_api.status.StatusCode.UNSET
) )
@ -116,9 +97,6 @@ class TestPymongo(TestBase):
self.assertEqual(len(spans_list), 1) self.assertEqual(len(spans_list), 1)
span = spans_list[0] span = spans_list[0]
self.assertEqual(
span.attributes["db.mongo.duration_micros"], "duration_micros"
)
self.assertIs( self.assertIs(
span.status.status_code, trace_api.status.StatusCode.ERROR, span.status.status_code, trace_api.status.StatusCode.ERROR,
) )
@ -139,13 +117,9 @@ class TestPymongo(TestBase):
first_span = spans_list[0] first_span = spans_list[0]
second_span = spans_list[1] second_span = spans_list[1]
self.assertEqual(first_span.attributes["db.mongo.request_id"], "first")
self.assertIs( self.assertIs(
first_span.status.status_code, trace_api.status.StatusCode.UNSET, first_span.status.status_code, trace_api.status.StatusCode.UNSET,
) )
self.assertEqual(
second_span.attributes["db.mongo.request_id"], "second"
)
self.assertIs( self.assertIs(
second_span.status.status_code, trace_api.status.StatusCode.ERROR, second_span.status.status_code, trace_api.status.StatusCode.ERROR,
) )
@ -165,7 +139,7 @@ class TestPymongo(TestBase):
self.assertEqual(len(spans_list), 1) self.assertEqual(len(spans_list), 1)
span = spans_list[0] span = spans_list[0]
self.assertEqual(span.name, "mongodb.command_name.123") self.assertEqual(span.name, "command_name.123")
class MockCommand: class MockCommand:

View File

@ -16,5 +16,5 @@ Installation
References References
---------- ----------
* `OpenTelemetry PyMySQL Instrumentation <https://opentelemetry-python.readthedocs.io/en/latest/instrumentation/pymysql/pymysql.html>`_ * `OpenTelemetry PyMySQL Instrumentation <https://opentelemetry-python-contrib.readthedocs.io/en/latest/instrumentation/pymysql/pymysql.html>`_
* `OpenTelemetry Project <https://opentelemetry.io/>`_ * `OpenTelemetry Project <https://opentelemetry.io/>`_

View File

@ -19,7 +19,7 @@ long_description = file: README.rst
long_description_content_type = text/x-rst long_description_content_type = text/x-rst
author = OpenTelemetry Authors author = OpenTelemetry Authors
author_email = cncf-opentelemetry-contributors@lists.cncf.io author_email = cncf-opentelemetry-contributors@lists.cncf.io
url = https://github.com/open-telemetry/opentelemetry-python/tree/master/instrumentation/opentelemetry-instrumentation-pymysql url = https://github.com/open-telemetry/opentelemetry-python-contrib/tree/master/instrumentation/opentelemetry-instrumentation-pymysql
platforms = any platforms = any
license = Apache-2.0 license = Apache-2.0
classifiers = classifiers =

View File

@ -27,6 +27,6 @@ will exclude requests such as ``https://site/client/123/info`` and ``https://sit
References References
---------- ----------
* `OpenTelemetry Pyramid Instrumentation <https://opentelemetry-python.readthedocs.io/en/latest/instrumentation/pyramid/pyramid.html>`_ * `OpenTelemetry Pyramid Instrumentation <https://opentelemetry-python-contrib.readthedocs.io/en/latest/instrumentation/pyramid/pyramid.html>`_
* `OpenTelemetry Project <https://opentelemetry.io/>`_ * `OpenTelemetry Project <https://opentelemetry.io/>`_

View File

@ -19,7 +19,7 @@ long_description = file: README.rst
long_description_content_type = text/x-rst long_description_content_type = text/x-rst
author = OpenTelemetry Authors author = OpenTelemetry Authors
author_email = cncf-opentelemetry-contributors@lists.cncf.io author_email = cncf-opentelemetry-contributors@lists.cncf.io
url = https://github.com/open-telemetry/opentelemetry-python/tree/master/instrumentation/opentelemetry-instrumentation-pyramid url = https://github.com/open-telemetry/opentelemetry-python-contrib/tree/master/instrumentation/opentelemetry-instrumentation-pyramid
platforms = any platforms = any
license = Apache-2.0 license = Apache-2.0
classifiers = classifiers =

View File

@ -2,6 +2,9 @@
## Unreleased ## Unreleased
- Update redis instrumentation to follow semantic conventions
([#184](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/184))
## Version 0.13b0 ## Version 0.13b0
Released 2020-09-17 Released 2020-09-17

View File

@ -19,5 +19,5 @@ Installation
References References
---------- ----------
* `OpenTelemetry Redis Instrumentation <https://opentelemetry-python.readthedocs.io/en/latest/instrumentation/opentelemetry-instrumentation-redis/opentelemetry-instrumentation-redis.html>`_ * `OpenTelemetry Redis Instrumentation <https://opentelemetry-python-contrib.readthedocs.io/en/latest/instrumentation/opentelemetry-instrumentation-redis/opentelemetry-instrumentation-redis.html>`_
* `OpenTelemetry Project <https://opentelemetry.io/>`_ * `OpenTelemetry Project <https://opentelemetry.io/>`_

View File

@ -19,7 +19,7 @@ long_description = file: README.rst
long_description_content_type = text/x-rst long_description_content_type = text/x-rst
author = OpenTelemetry Authors author = OpenTelemetry Authors
author_email = cncf-opentelemetry-contributors@lists.cncf.io author_email = cncf-opentelemetry-contributors@lists.cncf.io
url = https://github.com/open-telemetry/opentelemetry-python/tree/master/instrumentation/opentelemetry-instrumentation-redis url = https://github.com/open-telemetry/opentelemetry-python-contrib/tree/master/instrumentation/opentelemetry-instrumentation-redis
platforms = any platforms = any
license = Apache-2.0 license = Apache-2.0
classifiers = classifiers =

View File

@ -56,7 +56,6 @@ from opentelemetry.instrumentation.utils import unwrap
_DEFAULT_SERVICE = "redis" _DEFAULT_SERVICE = "redis"
_RAWCMD = "db.statement" _RAWCMD = "db.statement"
_CMD = "redis.command"
def _set_connection_attributes(span, conn): def _set_connection_attributes(span, conn):
@ -71,8 +70,13 @@ def _set_connection_attributes(span, conn):
def _traced_execute_command(func, instance, args, kwargs): def _traced_execute_command(func, instance, args, kwargs):
tracer = getattr(redis, "_opentelemetry_tracer") tracer = getattr(redis, "_opentelemetry_tracer")
query = _format_command_args(args) query = _format_command_args(args)
name = ""
if len(args) > 0 and args[0]:
name = args[0]
else:
name = instance.connection_pool.connection_kwargs.get("db", 0)
with tracer.start_as_current_span( with tracer.start_as_current_span(
_CMD, kind=trace.SpanKind.CLIENT name, kind=trace.SpanKind.CLIENT
) as span: ) as span:
if span.is_recording(): if span.is_recording():
span.set_attribute("service", tracer.instrumentation_info.name) span.set_attribute("service", tracer.instrumentation_info.name)
@ -88,8 +92,10 @@ def _traced_execute_pipeline(func, instance, args, kwargs):
cmds = [_format_command_args(c) for c, _ in instance.command_stack] cmds = [_format_command_args(c) for c, _ in instance.command_stack]
resource = "\n".join(cmds) resource = "\n".join(cmds)
span_name = " ".join([args[0] for args, _ in instance.command_stack])
with tracer.start_as_current_span( with tracer.start_as_current_span(
_CMD, kind=trace.SpanKind.CLIENT span_name, kind=trace.SpanKind.CLIENT
) as span: ) as span:
if span.is_recording(): if span.is_recording():
span.set_attribute("service", tracer.instrumentation_info.name) span.set_attribute("service", tracer.instrumentation_info.name)

View File

@ -20,15 +20,16 @@ Some utils used by the redis integration
def _extract_conn_attributes(conn_kwargs): def _extract_conn_attributes(conn_kwargs):
""" Transform redis conn info into dict """ """ Transform redis conn info into dict """
attributes = { attributes = {
"db.type": "redis", "db.system": "redis",
"db.instance": conn_kwargs.get("db", 0), "db.name": conn_kwargs.get("db", 0),
} }
try: try:
attributes["db.url"] = "redis://{}:{}".format( attributes["net.peer.name"] = conn_kwargs["host"]
conn_kwargs["host"], conn_kwargs["port"] attributes["net.peer.ip"] = conn_kwargs["port"]
) attributes["net.transport"] = "IP.TCP"
except KeyError: except KeyError:
pass # don't include url attribute attributes["net.peer.name"] = conn_kwargs["path"]
attributes["net.transport"] = "Unix"
return attributes return attributes

View File

@ -31,7 +31,7 @@ class TestRedis(TestBase):
spans = self.memory_exporter.get_finished_spans() spans = self.memory_exporter.get_finished_spans()
self.assertEqual(len(spans), 1) self.assertEqual(len(spans), 1)
span = spans[0] span = spans[0]
self.assertEqual(span.name, "redis.command") self.assertEqual(span.name, "GET")
self.assertEqual(span.kind, SpanKind.CLIENT) self.assertEqual(span.kind, SpanKind.CLIENT)
def test_not_recording(self): def test_not_recording(self):

View File

@ -19,5 +19,5 @@ Installation
References References
---------- ----------
* `OpenTelemetry requests Instrumentation <https://opentelemetry-python.readthedocs.io/en/latest/instrumentation/requests/requests.html>`_ * `OpenTelemetry requests Instrumentation <https://opentelemetry-python-contrib.readthedocs.io/en/latest/instrumentation/requests/requests.html>`_
* `OpenTelemetry Project <https://opentelemetry.io/>`_ * `OpenTelemetry Project <https://opentelemetry.io/>`_

View File

@ -19,7 +19,7 @@ long_description = file: README.rst
long_description_content_type = text/x-rst long_description_content_type = text/x-rst
author = OpenTelemetry Authors author = OpenTelemetry Authors
author_email = cncf-opentelemetry-contributors@lists.cncf.io author_email = cncf-opentelemetry-contributors@lists.cncf.io
url = https://github.com/open-telemetry/opentelemetry-python/tree/master/instrumentation/opentelemetry-instrumentation-requests url = https://github.com/open-telemetry/opentelemetry-python-contrib/tree/master/instrumentation/opentelemetry-instrumentation-requests
platforms = any platforms = any
license = Apache-2.0 license = Apache-2.0
classifiers = classifiers =

View File

@ -19,5 +19,5 @@ Installation
References References
---------- ----------
* `OpenTelemetry sklearn Instrumentation <https://opentelemetry-python.readthedocs.io/en/latest/instrumentation/sklearn/sklearn.html>`_ * `OpenTelemetry sklearn Instrumentation <https://opentelemetry-python-contrib.readthedocs.io/en/latest/instrumentation/sklearn/sklearn.html>`_
* `OpenTelemetry Project <https://opentelemetry.io/>`_ * `OpenTelemetry Project <https://opentelemetry.io/>`_

View File

@ -20,5 +20,5 @@ References
---------- ----------
* `SQLAlchemy Project <https://www.sqlalchemy.org/>`_ * `SQLAlchemy Project <https://www.sqlalchemy.org/>`_
* `OpenTelemetry SQLAlchemy Tracing <https://opentelemetry-python.readthedocs.io/en/latest/instrumentation/sqlalchemy/sqlalchemy.html>`_ * `OpenTelemetry SQLAlchemy Tracing <https://opentelemetry-python-contrib.readthedocs.io/en/latest/instrumentation/sqlalchemy/sqlalchemy.html>`_
* `OpenTelemetry Project <https://opentelemetry.io/>`_ * `OpenTelemetry Project <https://opentelemetry.io/>`_

View File

@ -19,7 +19,7 @@ long_description = file: README.rst
long_description_content_type = text/x-rst long_description_content_type = text/x-rst
author = OpenTelemetry Authors author = OpenTelemetry Authors
author_email = cncf-opentelemetry-contributors@lists.cncf.io author_email = cncf-opentelemetry-contributors@lists.cncf.io
url = https://github.com/open-telemetry/opentelemetry-python/tree/master/instrumentation/opentelemetry-instrumentation-sqlalchemy url = https://github.com/open-telemetry/opentelemetry-python-contrib/tree/master/instrumentation/opentelemetry-instrumentation-sqlalchemy
platforms = any platforms = any
license = Apache-2.0 license = Apache-2.0
classifiers = classifiers =

View File

@ -16,6 +16,6 @@ Installation
References References
---------- ----------
* `OpenTelemetry SQLite3 Instrumentation <https://opentelemetry-python.readthedocs.io/en/latest/instrumentation/sqlite3/sqlite3.html>`_ * `OpenTelemetry SQLite3 Instrumentation <https://opentelemetry-python-contrib.readthedocs.io/en/latest/instrumentation/sqlite3/sqlite3.html>`_
* `OpenTelemetry Project <https://opentelemetry.io/>`_ * `OpenTelemetry Project <https://opentelemetry.io/>`_

View File

@ -19,7 +19,7 @@ long_description = file: README.rst
long_description_content_type = text/x-rst long_description_content_type = text/x-rst
author = OpenTelemetry Authors author = OpenTelemetry Authors
author_email = cncf-opentelemetry-contributors@lists.cncf.io author_email = cncf-opentelemetry-contributors@lists.cncf.io
url = https://github.com/open-telemetry/opentelemetry-python/tree/master/instrumentation/opentelemetry-instrumentation-sqlite3 url = https://github.com/open-telemetry/opentelemetry-python-contrib/tree/master/instrumentation/opentelemetry-instrumentation-sqlite3
platforms = any platforms = any
license = Apache-2.0 license = Apache-2.0
classifiers = classifiers =

View File

@ -51,7 +51,7 @@ class SQLite3Instrumentor(BaseInstrumentor):
# No useful attributes of sqlite3 connection object # No useful attributes of sqlite3 connection object
_CONNECTION_ATTRIBUTES = {} _CONNECTION_ATTRIBUTES = {}
_DATABASE_COMPONENT = "sqlite3" _DATABASE_COMPONENT = "sqlite"
_DATABASE_TYPE = "sql" _DATABASE_TYPE = "sql"
def _instrument(self, **kwargs): def _instrument(self, **kwargs):

View File

@ -37,7 +37,7 @@ class TestSQLite3(TestBase):
if cls._connection: if cls._connection:
cls._connection.close() cls._connection.close()
def validate_spans(self): def validate_spans(self, span_name):
spans = self.memory_exporter.get_finished_spans() spans = self.memory_exporter.get_finished_spans()
self.assertEqual(len(spans), 2) self.assertEqual(len(spans), 2)
for span in spans: for span in spans:
@ -50,34 +50,30 @@ class TestSQLite3(TestBase):
self.assertIsNotNone(root_span) self.assertIsNotNone(root_span)
self.assertIsNotNone(child_span) self.assertIsNotNone(child_span)
self.assertEqual(root_span.name, "rootSpan") self.assertEqual(root_span.name, "rootSpan")
self.assertEqual(child_span.name, "sqlite3") self.assertEqual(child_span.name, span_name)
self.assertIsNotNone(child_span.parent) self.assertIsNotNone(child_span.parent)
self.assertIs(child_span.parent, root_span.get_span_context()) self.assertIs(child_span.parent, root_span.get_span_context())
self.assertIs(child_span.kind, trace_api.SpanKind.CLIENT) self.assertIs(child_span.kind, trace_api.SpanKind.CLIENT)
def test_execute(self): def test_execute(self):
"""Should create a child span for execute method """Should create a child span for execute method"""
""" stmt = "CREATE TABLE IF NOT EXISTS test (id integer)"
with self._tracer.start_as_current_span("rootSpan"): with self._tracer.start_as_current_span("rootSpan"):
self._cursor.execute( self._cursor.execute(stmt)
"CREATE TABLE IF NOT EXISTS test (id integer)" self.validate_spans(stmt)
)
self.validate_spans()
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 (?)"
with self._tracer.start_as_current_span("rootSpan"): with self._tracer.start_as_current_span("rootSpan"):
data = [("1",), ("2",), ("3",)] data = [("1",), ("2",), ("3",)]
stmt = "INSERT INTO test (id) VALUES (?)"
self._cursor.executemany(stmt, data) self._cursor.executemany(stmt, data)
self.validate_spans() self.validate_spans(stmt)
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(
Exception Exception
): ):
self._cursor.callproc("test", ()) self._cursor.callproc("test", ())
self.validate_spans() self.validate_spans("test")

View File

@ -19,7 +19,7 @@ long_description = file: README.rst
long_description_content_type = text/x-rst long_description_content_type = text/x-rst
author = OpenTelemetry Authors author = OpenTelemetry Authors
author_email = cncf-opentelemetry-contributors@lists.cncf.io author_email = cncf-opentelemetry-contributors@lists.cncf.io
url = https://github.com/open-telemetry/opentelemetry-python/tree/master/instrumentation/opentelemetry-instrumentation-starlette url = https://github.com/open-telemetry/opentelemetry-python-contrib/tree/master/instrumentation/opentelemetry-instrumentation-starlette
platforms = any platforms = any
license = Apache-2.0 license = Apache-2.0
classifiers = classifiers =

View File

@ -19,6 +19,6 @@ Installation
References References
---------- ----------
* `OpenTelemetry System Metrics Instrumentation <https://opentelemetry-python.readthedocs.io/en/latest/instrumentation/system_metrics/system_metrics.html>`_ * `OpenTelemetry System Metrics Instrumentation <https://opentelemetry-python-contrib.readthedocs.io/en/latest/instrumentation/system_metrics/system_metrics.html>`_
* `OpenTelemetry Project <https://opentelemetry.io/>`_ * `OpenTelemetry Project <https://opentelemetry.io/>`_

View File

@ -19,7 +19,7 @@ long_description = file: README.rst
long_description_content_type = text/x-rst long_description_content_type = text/x-rst
author = OpenTelemetry Authors author = OpenTelemetry Authors
author_email = cncf-opentelemetry-contributors@lists.cncf.io author_email = cncf-opentelemetry-contributors@lists.cncf.io
url = https://github.com/open-telemetry/opentelemetry-python/tree/master/instrumentation/opentelemetry-instrumentation-system-metrics url = https://github.com/open-telemetry/opentelemetry-python-contrib/tree/master/instrumentation/opentelemetry-instrumentation-system-metrics
platforms = any platforms = any
license = Apache-2.0 license = Apache-2.0
classifiers = classifiers =

View File

@ -92,9 +92,9 @@ class SystemMetrics:
config: typing.Optional[typing.Dict[str, typing.List[str]]] = None, config: typing.Optional[typing.Dict[str, typing.List[str]]] = None,
): ):
self._labels = {} if labels is None else labels self._labels = {} if labels is None else labels
self.meter = metrics.get_meter(__name__) self.accumulator = metrics.get_meter(__name__)
self.controller = PushController( self.controller = PushController(
meter=self.meter, exporter=exporter, interval=interval accumulator=self.accumulator, exporter=exporter, interval=interval
) )
self._python_implementation = python_implementation().lower() self._python_implementation = python_implementation().lower()
if config is None: if config is None:
@ -155,7 +155,7 @@ class SystemMetrics:
self._runtime_cpu_time_labels = self._labels.copy() self._runtime_cpu_time_labels = self._labels.copy()
self._runtime_gc_count_labels = self._labels.copy() self._runtime_gc_count_labels = self._labels.copy()
self.meter.register_sumobserver( self.accumulator.register_sumobserver(
callback=self._get_system_cpu_time, callback=self._get_system_cpu_time,
name="system.cpu.time", name="system.cpu.time",
description="System CPU time", description="System CPU time",
@ -163,7 +163,7 @@ class SystemMetrics:
value_type=float, value_type=float,
) )
self.meter.register_valueobserver( self.accumulator.register_valueobserver(
callback=self._get_system_cpu_utilization, callback=self._get_system_cpu_utilization,
name="system.cpu.utilization", name="system.cpu.utilization",
description="System CPU utilization", description="System CPU utilization",
@ -171,7 +171,7 @@ class SystemMetrics:
value_type=float, value_type=float,
) )
self.meter.register_valueobserver( self.accumulator.register_valueobserver(
callback=self._get_system_memory_usage, callback=self._get_system_memory_usage,
name="system.memory.usage", name="system.memory.usage",
description="System memory usage", description="System memory usage",
@ -179,7 +179,7 @@ class SystemMetrics:
value_type=int, value_type=int,
) )
self.meter.register_valueobserver( self.accumulator.register_valueobserver(
callback=self._get_system_memory_utilization, callback=self._get_system_memory_utilization,
name="system.memory.utilization", name="system.memory.utilization",
description="System memory utilization", description="System memory utilization",
@ -187,7 +187,7 @@ class SystemMetrics:
value_type=float, value_type=float,
) )
self.meter.register_valueobserver( self.accumulator.register_valueobserver(
callback=self._get_system_swap_usage, callback=self._get_system_swap_usage,
name="system.swap.usage", name="system.swap.usage",
description="System swap usage", description="System swap usage",
@ -195,7 +195,7 @@ class SystemMetrics:
value_type=int, value_type=int,
) )
self.meter.register_valueobserver( self.accumulator.register_valueobserver(
callback=self._get_system_swap_utilization, callback=self._get_system_swap_utilization,
name="system.swap.utilization", name="system.swap.utilization",
description="System swap utilization", description="System swap utilization",
@ -203,7 +203,7 @@ class SystemMetrics:
value_type=float, value_type=float,
) )
# self.meter.register_sumobserver( # self.accumulator.register_sumobserver(
# callback=self._get_system_swap_page_faults, # callback=self._get_system_swap_page_faults,
# name="system.swap.page_faults", # name="system.swap.page_faults",
# description="System swap page faults", # description="System swap page faults",
@ -211,7 +211,7 @@ class SystemMetrics:
# value_type=int, # value_type=int,
# ) # )
# self.meter.register_sumobserver( # self.accumulator.register_sumobserver(
# callback=self._get_system_swap_page_operations, # callback=self._get_system_swap_page_operations,
# name="system.swap.page_operations", # name="system.swap.page_operations",
# description="System swap page operations", # description="System swap page operations",
@ -219,7 +219,7 @@ class SystemMetrics:
# value_type=int, # value_type=int,
# ) # )
self.meter.register_sumobserver( self.accumulator.register_sumobserver(
callback=self._get_system_disk_io, callback=self._get_system_disk_io,
name="system.disk.io", name="system.disk.io",
description="System disk IO", description="System disk IO",
@ -227,7 +227,7 @@ class SystemMetrics:
value_type=int, value_type=int,
) )
self.meter.register_sumobserver( self.accumulator.register_sumobserver(
callback=self._get_system_disk_operations, callback=self._get_system_disk_operations,
name="system.disk.operations", name="system.disk.operations",
description="System disk operations", description="System disk operations",
@ -235,7 +235,7 @@ class SystemMetrics:
value_type=int, value_type=int,
) )
self.meter.register_sumobserver( self.accumulator.register_sumobserver(
callback=self._get_system_disk_time, callback=self._get_system_disk_time,
name="system.disk.time", name="system.disk.time",
description="System disk time", description="System disk time",
@ -243,7 +243,7 @@ class SystemMetrics:
value_type=float, value_type=float,
) )
self.meter.register_sumobserver( self.accumulator.register_sumobserver(
callback=self._get_system_disk_merged, callback=self._get_system_disk_merged,
name="system.disk.merged", name="system.disk.merged",
description="System disk merged", description="System disk merged",
@ -251,7 +251,7 @@ class SystemMetrics:
value_type=int, value_type=int,
) )
# self.meter.register_valueobserver( # self.accumulator.register_valueobserver(
# callback=self._get_system_filesystem_usage, # callback=self._get_system_filesystem_usage,
# name="system.filesystem.usage", # name="system.filesystem.usage",
# description="System filesystem usage", # description="System filesystem usage",
@ -259,7 +259,7 @@ class SystemMetrics:
# value_type=int, # value_type=int,
# ) # )
# self.meter.register_valueobserver( # self.accumulator.register_valueobserver(
# callback=self._get_system_filesystem_utilization, # callback=self._get_system_filesystem_utilization,
# name="system.filesystem.utilization", # name="system.filesystem.utilization",
# description="System filesystem utilization", # description="System filesystem utilization",
@ -267,7 +267,7 @@ class SystemMetrics:
# value_type=float, # value_type=float,
# ) # )
self.meter.register_sumobserver( self.accumulator.register_sumobserver(
callback=self._get_system_network_dropped_packets, callback=self._get_system_network_dropped_packets,
name="system.network.dropped_packets", name="system.network.dropped_packets",
description="System network dropped_packets", description="System network dropped_packets",
@ -275,7 +275,7 @@ class SystemMetrics:
value_type=int, value_type=int,
) )
self.meter.register_sumobserver( self.accumulator.register_sumobserver(
callback=self._get_system_network_packets, callback=self._get_system_network_packets,
name="system.network.packets", name="system.network.packets",
description="System network packets", description="System network packets",
@ -283,7 +283,7 @@ class SystemMetrics:
value_type=int, value_type=int,
) )
self.meter.register_sumobserver( self.accumulator.register_sumobserver(
callback=self._get_system_network_errors, callback=self._get_system_network_errors,
name="system.network.errors", name="system.network.errors",
description="System network errors", description="System network errors",
@ -291,7 +291,7 @@ class SystemMetrics:
value_type=int, value_type=int,
) )
self.meter.register_sumobserver( self.accumulator.register_sumobserver(
callback=self._get_system_network_io, callback=self._get_system_network_io,
name="system.network.io", name="system.network.io",
description="System network io", description="System network io",
@ -299,7 +299,7 @@ class SystemMetrics:
value_type=int, value_type=int,
) )
self.meter.register_updownsumobserver( self.accumulator.register_updownsumobserver(
callback=self._get_system_network_connections, callback=self._get_system_network_connections,
name="system.network.connections", name="system.network.connections",
description="System network connections", description="System network connections",
@ -307,7 +307,7 @@ class SystemMetrics:
value_type=int, value_type=int,
) )
self.meter.register_sumobserver( self.accumulator.register_sumobserver(
callback=self._get_runtime_memory, callback=self._get_runtime_memory,
name="runtime.{}.memory".format(self._python_implementation), name="runtime.{}.memory".format(self._python_implementation),
description="Runtime {} memory".format( description="Runtime {} memory".format(
@ -317,7 +317,7 @@ class SystemMetrics:
value_type=int, value_type=int,
) )
self.meter.register_sumobserver( self.accumulator.register_sumobserver(
callback=self._get_runtime_cpu_time, callback=self._get_runtime_cpu_time,
name="runtime.{}.cpu_time".format(self._python_implementation), name="runtime.{}.cpu_time".format(self._python_implementation),
description="Runtime {} CPU time".format( description="Runtime {} CPU time".format(
@ -327,7 +327,7 @@ class SystemMetrics:
value_type=float, value_type=float,
) )
self.meter.register_sumobserver( self.accumulator.register_sumobserver(
callback=self._get_runtime_gc_count, callback=self._get_runtime_gc_count,
name="runtime.{}.gc_count".format(self._python_implementation), name="runtime.{}.gc_count".format(self._python_implementation),
description="Runtime {} GC count".format( description="Runtime {} GC count".format(

View File

@ -47,5 +47,5 @@ will extract path_info and content_type attributes from every traced request and
References References
---------- ----------
* `OpenTelemetry Tornado Instrumentation <https://opentelemetry-python.readthedocs.io/en/latest/instrumentation/tornado/tornado.html>`_ * `OpenTelemetry Tornado Instrumentation <https://opentelemetry-python-contrib.readthedocs.io/en/latest/instrumentation/tornado/tornado.html>`_
* `OpenTelemetry Project <https://opentelemetry.io/>`_ * `OpenTelemetry Project <https://opentelemetry.io/>`_

View File

@ -19,7 +19,7 @@ long_description = file: README.rst
long_description_content_type = text/x-rst long_description_content_type = text/x-rst
author = OpenTelemetry Authors author = OpenTelemetry Authors
author_email = cncf-opentelemetry-contributors@lists.cncf.io author_email = cncf-opentelemetry-contributors@lists.cncf.io
url = https://github.com/open-telemetry/opentelemetry-python/tree/master/instrumentation/opentelemetry-instrumentation-tornado url = https://github.com/open-telemetry/opentelemetry-python-contrib/tree/master/instrumentation/opentelemetry-instrumentation-tornado
platforms = any platforms = any
license = Apache-2.0 license = Apache-2.0
classifiers = classifiers =

View File

@ -21,6 +21,6 @@ Installation
References References
---------- ----------
* `OpenTelemetry WSGI Middleware <https://opentelemetry-python.readthedocs.io/en/latest/instrumentation/wsgi/wsgi.html>`_ * `OpenTelemetry WSGI Middleware <https://opentelemetry-python-contrib.readthedocs.io/en/latest/instrumentation/wsgi/wsgi.html>`_
* `OpenTelemetry Project <https://opentelemetry.io/>`_ * `OpenTelemetry Project <https://opentelemetry.io/>`_
* `WSGI <https://www.python.org/dev/peps/pep-3333>`_ * `WSGI <https://www.python.org/dev/peps/pep-3333>`_

View File

@ -19,7 +19,7 @@ long_description = file: README.rst
long_description_content_type = text/x-rst long_description_content_type = text/x-rst
author = OpenTelemetry Authors author = OpenTelemetry Authors
author_email = cncf-opentelemetry-contributors@lists.cncf.io author_email = cncf-opentelemetry-contributors@lists.cncf.io
url = https://github.com/open-telemetry/opentelemetry-python/tree/master/instrumentation/opentelemetry-instrumentation-wsgi url = https://github.com/open-telemetry/opentelemetry-python-contrib/tree/master/instrumentation/opentelemetry-instrumentation-wsgi
platforms = any platforms = any
license = Apache-2.0 license = Apache-2.0
classifiers = classifiers =

View File

@ -2,5 +2,9 @@
## Unreleased ## Unreleased
- Fix typo for installing OTel SDK in docs
([#200](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/200))
- Import missing components for docs
([#198](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/198))
- Provide components needed to Configure OTel SDK for Tracing with AWS X-Ray - Provide components needed to Configure OTel SDK for Tracing with AWS X-Ray
([#130](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/130)) ([#130](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/130))

View File

@ -24,9 +24,19 @@ Usage (AWS X-Ray IDs Generator)
Configure the OTel SDK TracerProvider with the provided custom IDs Generator to Configure the OTel SDK TracerProvider with the provided custom IDs Generator to
make spans compatible with the AWS X-Ray backend tracing service. make spans compatible with the AWS X-Ray backend tracing service.
Install the OpenTelemetry SDK package.
::
pip install opentelemetry-sdk
Next, use the provided `AwsXRayIdsGenerator` to initialize the `TracerProvider`.
.. code-block:: python .. code-block:: python
import opentelemetry.trace as trace
from opentelemetry.sdk.extension.aws.trace import AwsXRayIdsGenerator from opentelemetry.sdk.extension.aws.trace import AwsXRayIdsGenerator
from opentelemetry.sdk.trace import TracerProvider
trace.set_tracer_provider( trace.set_tracer_provider(
TracerProvider(ids_generator=AwsXRayIdsGenerator()) TracerProvider(ids_generator=AwsXRayIdsGenerator())
@ -36,14 +46,25 @@ make spans compatible with the AWS X-Ray backend tracing service.
Usage (AWS X-Ray Propagator) Usage (AWS X-Ray Propagator)
---------------------------- ----------------------------
Set this environment variable to have the OTel SDK use the provided AWS X-Ray Use the provided AWS X-Ray Propagator to inject the necessary context into
Propagator: traces sent to external systems.
This can be done by either setting this environment variable:
:: ::
export OTEL_PROPAGATORS = aws_xray export OTEL_PROPAGATORS = aws_xray
Or by setting this propagator in your instrumented application:
.. code-block:: python
from opentelemetry import propagators
from opentelemetry.sdk.extension.aws.trace.propagation.aws_xray_format import AwsXRayFormat
propagators.set_global_textmap(AwsXRayFormat())
References References
---------- ----------

View File

@ -31,9 +31,9 @@ MYSQL_USER = os.getenv("MYSQL_USER ", "testuser")
MYSQL_PASSWORD = os.getenv("MYSQL_PASSWORD", "testpassword") MYSQL_PASSWORD = os.getenv("MYSQL_PASSWORD", "testpassword")
POSTGRES_DB_NAME = os.getenv("POSTGRESQL_DB_NAME", "opentelemetry-tests") POSTGRES_DB_NAME = os.getenv("POSTGRESQL_DB_NAME", "opentelemetry-tests")
POSTGRES_HOST = os.getenv("POSTGRESQL_HOST", "localhost") POSTGRES_HOST = os.getenv("POSTGRESQL_HOST", "localhost")
POSTGRES_PASSWORD = os.getenv("POSTGRESQL_HOST", "testpassword") POSTGRES_PASSWORD = os.getenv("POSTGRESQL_PASSWORD", "testpassword")
POSTGRES_PORT = int(os.getenv("POSTGRESQL_PORT", "5432")) POSTGRES_PORT = int(os.getenv("POSTGRESQL_PORT", "5432"))
POSTGRES_USER = os.getenv("POSTGRESQL_HOST", "testuser") POSTGRES_USER = os.getenv("POSTGRESQL_USER", "testuser")
REDIS_HOST = os.getenv("REDIS_HOST", "localhost") REDIS_HOST = os.getenv("REDIS_HOST", "localhost")
REDIS_PORT = int(os.getenv("REDIS_PORT ", "6379")) REDIS_PORT = int(os.getenv("REDIS_PORT ", "6379"))
RETRY_COUNT = 8 RETRY_COUNT = 8

View File

@ -53,7 +53,7 @@ class TestFunctionalMysql(TestBase):
) )
self._cursor = self._connection.cursor() self._cursor = self._connection.cursor()
def validate_spans(self): def validate_spans(self, span_name):
spans = self.memory_exporter.get_finished_spans() spans = self.memory_exporter.get_finished_spans()
self.assertEqual(len(spans), 2) self.assertEqual(len(spans), 2)
for span in spans: for span in spans:
@ -66,42 +66,47 @@ class TestFunctionalMysql(TestBase):
self.assertIsNotNone(root_span) self.assertIsNotNone(root_span)
self.assertIsNotNone(db_span) self.assertIsNotNone(db_span)
self.assertEqual(root_span.name, "rootSpan") self.assertEqual(root_span.name, "rootSpan")
self.assertEqual(db_span.name, "mysql.opentelemetry-tests") self.assertEqual(db_span.name, span_name)
self.assertIsNotNone(db_span.parent) self.assertIsNotNone(db_span.parent)
self.assertIs(db_span.parent, root_span.get_span_context()) self.assertIs(db_span.parent, root_span.get_span_context())
self.assertIs(db_span.kind, trace_api.SpanKind.CLIENT) self.assertIs(db_span.kind, trace_api.SpanKind.CLIENT)
self.assertEqual(db_span.attributes["db.instance"], MYSQL_DB_NAME) self.assertEqual(db_span.attributes["db.system"], "mysql")
self.assertEqual(db_span.attributes["db.name"], MYSQL_DB_NAME)
self.assertEqual(db_span.attributes["db.user"], MYSQL_USER)
self.assertEqual(db_span.attributes["net.peer.name"], MYSQL_HOST) self.assertEqual(db_span.attributes["net.peer.name"], MYSQL_HOST)
self.assertEqual(db_span.attributes["net.peer.port"], MYSQL_PORT) self.assertEqual(db_span.attributes["net.peer.port"], MYSQL_PORT)
def test_execute(self): def test_execute(self):
"""Should create a child span for execute""" """Should create a child span for execute"""
stmt = "CREATE TABLE IF NOT EXISTS test (id INT)"
with self._tracer.start_as_current_span("rootSpan"): with self._tracer.start_as_current_span("rootSpan"):
self._cursor.execute("CREATE TABLE IF NOT EXISTS test (id INT)") self._cursor.execute(stmt)
self.validate_spans() self.validate_spans(stmt)
def test_execute_with_connection_context_manager(self): def test_execute_with_connection_context_manager(self):
"""Should create a child span for execute with connection context""" """Should create a child span for execute with connection context"""
stmt = "CREATE TABLE IF NOT EXISTS test (id INT)"
with self._tracer.start_as_current_span("rootSpan"): with self._tracer.start_as_current_span("rootSpan"):
with self._connection as conn: with self._connection as conn:
cursor = conn.cursor() cursor = conn.cursor()
cursor.execute("CREATE TABLE IF NOT EXISTS test (id INT)") cursor.execute(stmt)
self.validate_spans() self.validate_spans(stmt)
def test_execute_with_cursor_context_manager(self): def test_execute_with_cursor_context_manager(self):
"""Should create a child span for execute with cursor context""" """Should create a child span for execute with cursor context"""
stmt = "CREATE TABLE IF NOT EXISTS test (id INT)"
with self._tracer.start_as_current_span("rootSpan"): with self._tracer.start_as_current_span("rootSpan"):
with self._connection.cursor() as cursor: with self._connection.cursor() as cursor:
cursor.execute("CREATE TABLE IF NOT EXISTS test (id INT)") cursor.execute(stmt)
self.validate_spans() self.validate_spans(stmt)
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 (%s)"
with self._tracer.start_as_current_span("rootSpan"): with self._tracer.start_as_current_span("rootSpan"):
data = (("1",), ("2",), ("3",)) data = (("1",), ("2",), ("3",))
stmt = "INSERT INTO test (id) VALUES (%s)"
self._cursor.executemany(stmt, data) self._cursor.executemany(stmt, data)
self.validate_spans() self.validate_spans(stmt)
def test_callproc(self): def test_callproc(self):
"""Should create a child span for callproc""" """Should create a child span for callproc"""
@ -109,4 +114,4 @@ class TestFunctionalMysql(TestBase):
Exception Exception
): ):
self._cursor.callproc("test", ()) self._cursor.callproc("test", ())
self.validate_spans() self.validate_spans("test")

View File

@ -25,8 +25,8 @@ from opentelemetry.test.test_base import TestBase
POSTGRES_HOST = os.getenv("POSTGRESQL_HOST", "localhost") POSTGRES_HOST = os.getenv("POSTGRESQL_HOST", "localhost")
POSTGRES_PORT = int(os.getenv("POSTGRESQL_PORT", "5432")) POSTGRES_PORT = int(os.getenv("POSTGRESQL_PORT", "5432"))
POSTGRES_DB_NAME = os.getenv("POSTGRESQL_DB_NAME", "opentelemetry-tests") POSTGRES_DB_NAME = os.getenv("POSTGRESQL_DB_NAME", "opentelemetry-tests")
POSTGRES_PASSWORD = os.getenv("POSTGRESQL_HOST ", "testpassword") POSTGRES_PASSWORD = os.getenv("POSTGRESQL_PASSWORD", "testpassword")
POSTGRES_USER = os.getenv("POSTGRESQL_HOST ", "testuser") POSTGRES_USER = os.getenv("POSTGRESQL_USER", "testuser")
def async_call(coro): def async_call(coro):
@ -61,7 +61,7 @@ class TestFunctionalAiopgConnect(TestBase):
cls._connection.close() cls._connection.close()
AiopgInstrumentor().uninstrument() AiopgInstrumentor().uninstrument()
def validate_spans(self): def validate_spans(self, span_name):
spans = self.memory_exporter.get_finished_spans() spans = self.memory_exporter.get_finished_spans()
self.assertEqual(len(spans), 2) self.assertEqual(len(spans), 2)
for span in spans: for span in spans:
@ -74,34 +74,31 @@ class TestFunctionalAiopgConnect(TestBase):
self.assertIsNotNone(root_span) self.assertIsNotNone(root_span)
self.assertIsNotNone(child_span) self.assertIsNotNone(child_span)
self.assertEqual(root_span.name, "rootSpan") self.assertEqual(root_span.name, "rootSpan")
self.assertEqual(child_span.name, "postgresql.opentelemetry-tests") self.assertEqual(child_span.name, span_name)
self.assertIsNotNone(child_span.parent) self.assertIsNotNone(child_span.parent)
self.assertIs(child_span.parent, root_span.get_span_context()) self.assertIs(child_span.parent, root_span.get_span_context())
self.assertIs(child_span.kind, trace_api.SpanKind.CLIENT) self.assertIs(child_span.kind, trace_api.SpanKind.CLIENT)
self.assertEqual( self.assertEqual(child_span.attributes["db.system"], "postgresql")
child_span.attributes["db.instance"], POSTGRES_DB_NAME self.assertEqual(child_span.attributes["db.name"], POSTGRES_DB_NAME)
) self.assertEqual(child_span.attributes["db.user"], POSTGRES_USER)
self.assertEqual(child_span.attributes["net.peer.name"], POSTGRES_HOST) self.assertEqual(child_span.attributes["net.peer.name"], POSTGRES_HOST)
self.assertEqual(child_span.attributes["net.peer.port"], POSTGRES_PORT) self.assertEqual(child_span.attributes["net.peer.port"], POSTGRES_PORT)
def test_execute(self): def test_execute(self):
"""Should create a child span for execute method""" """Should create a child span for execute method"""
stmt = "CREATE TABLE IF NOT EXISTS test (id integer)"
with self._tracer.start_as_current_span("rootSpan"): with self._tracer.start_as_current_span("rootSpan"):
async_call( async_call(self._cursor.execute(stmt))
self._cursor.execute( self.validate_spans(stmt)
"CREATE TABLE IF NOT EXISTS test (id integer)"
)
)
self.validate_spans()
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 (%s)"
with pytest.raises(psycopg2.ProgrammingError): with pytest.raises(psycopg2.ProgrammingError):
with self._tracer.start_as_current_span("rootSpan"): with self._tracer.start_as_current_span("rootSpan"):
data = (("1",), ("2",), ("3",)) data = (("1",), ("2",), ("3",))
stmt = "INSERT INTO test (id) VALUES (%s)"
async_call(self._cursor.executemany(stmt, data)) async_call(self._cursor.executemany(stmt, data))
self.validate_spans() self.validate_spans(stmt)
def test_callproc(self): def test_callproc(self):
"""Should create a child span for callproc""" """Should create a child span for callproc"""
@ -109,7 +106,7 @@ class TestFunctionalAiopgConnect(TestBase):
Exception Exception
): ):
async_call(self._cursor.callproc("test", ())) async_call(self._cursor.callproc("test", ()))
self.validate_spans() self.validate_spans("test")
class TestFunctionalAiopgCreatePool(TestBase): class TestFunctionalAiopgCreatePool(TestBase):
@ -142,7 +139,7 @@ class TestFunctionalAiopgCreatePool(TestBase):
cls._pool.close() cls._pool.close()
AiopgInstrumentor().uninstrument() AiopgInstrumentor().uninstrument()
def validate_spans(self): def validate_spans(self, span_name):
spans = self.memory_exporter.get_finished_spans() spans = self.memory_exporter.get_finished_spans()
self.assertEqual(len(spans), 2) self.assertEqual(len(spans), 2)
for span in spans: for span in spans:
@ -155,34 +152,31 @@ class TestFunctionalAiopgCreatePool(TestBase):
self.assertIsNotNone(root_span) self.assertIsNotNone(root_span)
self.assertIsNotNone(child_span) self.assertIsNotNone(child_span)
self.assertEqual(root_span.name, "rootSpan") self.assertEqual(root_span.name, "rootSpan")
self.assertEqual(child_span.name, "postgresql.opentelemetry-tests") self.assertEqual(child_span.name, span_name)
self.assertIsNotNone(child_span.parent) self.assertIsNotNone(child_span.parent)
self.assertIs(child_span.parent, root_span.get_span_context()) self.assertIs(child_span.parent, root_span.get_span_context())
self.assertIs(child_span.kind, trace_api.SpanKind.CLIENT) self.assertIs(child_span.kind, trace_api.SpanKind.CLIENT)
self.assertEqual( self.assertEqual(child_span.attributes["db.system"], "postgresql")
child_span.attributes["db.instance"], POSTGRES_DB_NAME self.assertEqual(child_span.attributes["db.name"], POSTGRES_DB_NAME)
) self.assertEqual(child_span.attributes["db.user"], POSTGRES_USER)
self.assertEqual(child_span.attributes["net.peer.name"], POSTGRES_HOST) self.assertEqual(child_span.attributes["net.peer.name"], POSTGRES_HOST)
self.assertEqual(child_span.attributes["net.peer.port"], POSTGRES_PORT) self.assertEqual(child_span.attributes["net.peer.port"], POSTGRES_PORT)
def test_execute(self): def test_execute(self):
"""Should create a child span for execute method""" """Should create a child span for execute method"""
stmt = "CREATE TABLE IF NOT EXISTS test (id integer)"
with self._tracer.start_as_current_span("rootSpan"): with self._tracer.start_as_current_span("rootSpan"):
async_call( async_call(self._cursor.execute(stmt))
self._cursor.execute( self.validate_spans(stmt)
"CREATE TABLE IF NOT EXISTS test (id integer)"
)
)
self.validate_spans()
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 (%s)"
with pytest.raises(psycopg2.ProgrammingError): with pytest.raises(psycopg2.ProgrammingError):
with self._tracer.start_as_current_span("rootSpan"): with self._tracer.start_as_current_span("rootSpan"):
data = (("1",), ("2",), ("3",)) data = (("1",), ("2",), ("3",))
stmt = "INSERT INTO test (id) VALUES (%s)"
async_call(self._cursor.executemany(stmt, data)) async_call(self._cursor.executemany(stmt, data))
self.validate_spans() self.validate_spans(stmt)
def test_callproc(self): def test_callproc(self):
"""Should create a child span for callproc""" """Should create a child span for callproc"""
@ -190,4 +184,4 @@ class TestFunctionalAiopgCreatePool(TestBase):
Exception Exception
): ):
async_call(self._cursor.callproc("test", ())) async_call(self._cursor.callproc("test", ()))
self.validate_spans() self.validate_spans("test")

View File

@ -23,8 +23,8 @@ from opentelemetry.test.test_base import TestBase
POSTGRES_HOST = os.getenv("POSTGRESQL_HOST", "localhost") POSTGRES_HOST = os.getenv("POSTGRESQL_HOST", "localhost")
POSTGRES_PORT = int(os.getenv("POSTGRESQL_PORT", "5432")) POSTGRES_PORT = int(os.getenv("POSTGRESQL_PORT", "5432"))
POSTGRES_DB_NAME = os.getenv("POSTGRESQL_DB_NAME", "opentelemetry-tests") POSTGRES_DB_NAME = os.getenv("POSTGRESQL_DB_NAME", "opentelemetry-tests")
POSTGRES_PASSWORD = os.getenv("POSTGRESQL_HOST ", "testpassword") POSTGRES_PASSWORD = os.getenv("POSTGRESQL_PASSWORD", "testpassword")
POSTGRES_USER = os.getenv("POSTGRESQL_HOST ", "testuser") POSTGRES_USER = os.getenv("POSTGRESQL_USER", "testuser")
class TestFunctionalPsycopg(TestBase): class TestFunctionalPsycopg(TestBase):
@ -53,7 +53,7 @@ class TestFunctionalPsycopg(TestBase):
cls._connection.close() cls._connection.close()
Psycopg2Instrumentor().uninstrument() Psycopg2Instrumentor().uninstrument()
def validate_spans(self): def validate_spans(self, span_name):
spans = self.memory_exporter.get_finished_spans() spans = self.memory_exporter.get_finished_spans()
self.assertEqual(len(spans), 2) self.assertEqual(len(spans), 2)
for span in spans: for span in spans:
@ -66,47 +66,48 @@ class TestFunctionalPsycopg(TestBase):
self.assertIsNotNone(root_span) self.assertIsNotNone(root_span)
self.assertIsNotNone(child_span) self.assertIsNotNone(child_span)
self.assertEqual(root_span.name, "rootSpan") self.assertEqual(root_span.name, "rootSpan")
self.assertEqual(child_span.name, "postgresql.opentelemetry-tests") self.assertEqual(child_span.name, span_name)
self.assertIsNotNone(child_span.parent) self.assertIsNotNone(child_span.parent)
self.assertIs(child_span.parent, root_span.get_span_context()) self.assertIs(child_span.parent, root_span.get_span_context())
self.assertIs(child_span.kind, trace_api.SpanKind.CLIENT) self.assertIs(child_span.kind, trace_api.SpanKind.CLIENT)
self.assertEqual( self.assertEqual(child_span.attributes["db.system"], "postgresql")
child_span.attributes["db.instance"], POSTGRES_DB_NAME self.assertEqual(child_span.attributes["db.name"], POSTGRES_DB_NAME)
) self.assertEqual(child_span.attributes["db.user"], POSTGRES_USER)
self.assertEqual(child_span.attributes["net.peer.name"], POSTGRES_HOST) self.assertEqual(child_span.attributes["net.peer.name"], POSTGRES_HOST)
self.assertEqual(child_span.attributes["net.peer.port"], POSTGRES_PORT) self.assertEqual(child_span.attributes["net.peer.port"], POSTGRES_PORT)
def test_execute(self): def test_execute(self):
"""Should create a child span for execute method""" """Should create a child span for execute method"""
stmt = "CREATE TABLE IF NOT EXISTS test (id integer)"
with self._tracer.start_as_current_span("rootSpan"): with self._tracer.start_as_current_span("rootSpan"):
self._cursor.execute( self._cursor.execute(stmt)
"CREATE TABLE IF NOT EXISTS test (id integer)" self.validate_spans(stmt)
)
self.validate_spans()
def test_execute_with_connection_context_manager(self): def test_execute_with_connection_context_manager(self):
"""Should create a child span for execute with connection context""" """Should create a child span for execute with connection context"""
stmt = "CREATE TABLE IF NOT EXISTS test (id INT)"
with self._tracer.start_as_current_span("rootSpan"): with self._tracer.start_as_current_span("rootSpan"):
with self._connection as conn: with self._connection as conn:
cursor = conn.cursor() cursor = conn.cursor()
cursor.execute("CREATE TABLE IF NOT EXISTS test (id INT)") cursor.execute(stmt)
self.validate_spans() self.validate_spans(stmt)
def test_execute_with_cursor_context_manager(self): def test_execute_with_cursor_context_manager(self):
"""Should create a child span for execute with cursor context""" """Should create a child span for execute with cursor context"""
stmt = "CREATE TABLE IF NOT EXISTS test (id INT)"
with self._tracer.start_as_current_span("rootSpan"): with self._tracer.start_as_current_span("rootSpan"):
with self._connection.cursor() as cursor: with self._connection.cursor() as cursor:
cursor.execute("CREATE TABLE IF NOT EXISTS test (id INT)") cursor.execute(stmt)
self.validate_spans() self.validate_spans(stmt)
self.assertTrue(cursor.closed) self.assertTrue(cursor.closed)
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 (%s)"
with self._tracer.start_as_current_span("rootSpan"): with self._tracer.start_as_current_span("rootSpan"):
data = (("1",), ("2",), ("3",)) data = (("1",), ("2",), ("3",))
stmt = "INSERT INTO test (id) VALUES (%s)"
self._cursor.executemany(stmt, data) self._cursor.executemany(stmt, data)
self.validate_spans() self.validate_spans(stmt)
def test_callproc(self): def test_callproc(self):
"""Should create a child span for callproc""" """Should create a child span for callproc"""
@ -114,4 +115,4 @@ class TestFunctionalPsycopg(TestBase):
Exception Exception
): ):
self._cursor.callproc("test", ()) self._cursor.callproc("test", ())
self.validate_spans() self.validate_spans("test")

View File

@ -53,9 +53,7 @@ class TestFunctionalPymongo(TestBase):
self.assertIsNotNone(pymongo_span.parent) self.assertIsNotNone(pymongo_span.parent)
self.assertIs(pymongo_span.parent, root_span.get_span_context()) self.assertIs(pymongo_span.parent, root_span.get_span_context())
self.assertIs(pymongo_span.kind, trace_api.SpanKind.CLIENT) self.assertIs(pymongo_span.kind, trace_api.SpanKind.CLIENT)
self.assertEqual( self.assertEqual(pymongo_span.attributes["db.name"], MONGODB_DB_NAME)
pymongo_span.attributes["db.instance"], MONGODB_DB_NAME
)
self.assertEqual( self.assertEqual(
pymongo_span.attributes["net.peer.name"], MONGODB_HOST pymongo_span.attributes["net.peer.name"], MONGODB_HOST
) )

View File

@ -50,7 +50,7 @@ class TestFunctionalPyMysql(TestBase):
cls._connection.close() cls._connection.close()
PyMySQLInstrumentor().uninstrument() PyMySQLInstrumentor().uninstrument()
def validate_spans(self): def validate_spans(self, span_name):
spans = self.memory_exporter.get_finished_spans() spans = self.memory_exporter.get_finished_spans()
self.assertEqual(len(spans), 2) self.assertEqual(len(spans), 2)
for span in spans: for span in spans:
@ -63,34 +63,38 @@ class TestFunctionalPyMysql(TestBase):
self.assertIsNotNone(root_span) self.assertIsNotNone(root_span)
self.assertIsNotNone(db_span) self.assertIsNotNone(db_span)
self.assertEqual(root_span.name, "rootSpan") self.assertEqual(root_span.name, "rootSpan")
self.assertEqual(db_span.name, "mysql.opentelemetry-tests") self.assertEqual(db_span.name, span_name)
self.assertIsNotNone(db_span.parent) self.assertIsNotNone(db_span.parent)
self.assertIs(db_span.parent, root_span.get_span_context()) self.assertIs(db_span.parent, root_span.get_span_context())
self.assertIs(db_span.kind, trace_api.SpanKind.CLIENT) self.assertIs(db_span.kind, trace_api.SpanKind.CLIENT)
self.assertEqual(db_span.attributes["db.instance"], MYSQL_DB_NAME) self.assertEqual(db_span.attributes["db.system"], "mysql")
self.assertEqual(db_span.attributes["db.name"], MYSQL_DB_NAME)
self.assertEqual(db_span.attributes["db.user"], MYSQL_USER)
self.assertEqual(db_span.attributes["net.peer.name"], MYSQL_HOST) self.assertEqual(db_span.attributes["net.peer.name"], MYSQL_HOST)
self.assertEqual(db_span.attributes["net.peer.port"], MYSQL_PORT) self.assertEqual(db_span.attributes["net.peer.port"], MYSQL_PORT)
def test_execute(self): def test_execute(self):
"""Should create a child span for execute""" """Should create a child span for execute"""
stmt = "CREATE TABLE IF NOT EXISTS test (id INT)"
with self._tracer.start_as_current_span("rootSpan"): with self._tracer.start_as_current_span("rootSpan"):
self._cursor.execute("CREATE TABLE IF NOT EXISTS test (id INT)") self._cursor.execute(stmt)
self.validate_spans() self.validate_spans(stmt)
def test_execute_with_cursor_context_manager(self): def test_execute_with_cursor_context_manager(self):
"""Should create a child span for execute with cursor context""" """Should create a child span for execute with cursor context"""
stmt = "CREATE TABLE IF NOT EXISTS test (id INT)"
with self._tracer.start_as_current_span("rootSpan"): with self._tracer.start_as_current_span("rootSpan"):
with self._connection.cursor() as cursor: with self._connection.cursor() as cursor:
cursor.execute("CREATE TABLE IF NOT EXISTS test (id INT)") cursor.execute(stmt)
self.validate_spans() self.validate_spans(stmt)
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 (%s)"
with self._tracer.start_as_current_span("rootSpan"): with self._tracer.start_as_current_span("rootSpan"):
data = (("1",), ("2",), ("3",)) data = (("1",), ("2",), ("3",))
stmt = "INSERT INTO test (id) VALUES (%s)"
self._cursor.executemany(stmt, data) self._cursor.executemany(stmt, data)
self.validate_spans() self.validate_spans(stmt)
def test_callproc(self): def test_callproc(self):
"""Should create a child span for callproc""" """Should create a child span for callproc"""
@ -98,4 +102,4 @@ class TestFunctionalPyMysql(TestBase):
Exception Exception
): ):
self._cursor.callproc("test", ()) self._cursor.callproc("test", ())
self.validate_spans() self.validate_spans("test")

View File

@ -33,14 +33,13 @@ class TestRedisInstrument(TestBase):
super().tearDown() super().tearDown()
RedisInstrumentor().uninstrument() RedisInstrumentor().uninstrument()
def _check_span(self, span): def _check_span(self, span, name):
self.assertEqual(span.attributes["service"], self.test_service) self.assertEqual(span.attributes["service"], self.test_service)
self.assertEqual(span.name, "redis.command") self.assertEqual(span.name, name)
self.assertIs(span.status.status_code, trace.status.StatusCode.UNSET) self.assertIs(span.status.status_code, trace.status.StatusCode.UNSET)
self.assertEqual(span.attributes.get("db.instance"), 0) self.assertEqual(span.attributes.get("db.name"), 0)
self.assertEqual( self.assertEqual(span.attributes["net.peer.name"], "localhost")
span.attributes.get("db.url"), "redis://localhost:6379" self.assertEqual(span.attributes["net.peer.ip"], 6379)
)
def test_long_command(self): def test_long_command(self):
self.redis_client.mget(*range(1000)) self.redis_client.mget(*range(1000))
@ -48,7 +47,7 @@ class TestRedisInstrument(TestBase):
spans = self.memory_exporter.get_finished_spans() spans = self.memory_exporter.get_finished_spans()
self.assertEqual(len(spans), 1) self.assertEqual(len(spans), 1)
span = spans[0] span = spans[0]
self._check_span(span) self._check_span(span, "MGET")
self.assertTrue( self.assertTrue(
span.attributes.get("db.statement").startswith("MGET 0 1 2 3") span.attributes.get("db.statement").startswith("MGET 0 1 2 3")
) )
@ -59,7 +58,7 @@ class TestRedisInstrument(TestBase):
spans = self.memory_exporter.get_finished_spans() spans = self.memory_exporter.get_finished_spans()
self.assertEqual(len(spans), 1) self.assertEqual(len(spans), 1)
span = spans[0] span = spans[0]
self._check_span(span) self._check_span(span, "GET")
self.assertEqual(span.attributes.get("db.statement"), "GET cheese") self.assertEqual(span.attributes.get("db.statement"), "GET cheese")
self.assertEqual(span.attributes.get("redis.args_length"), 2) self.assertEqual(span.attributes.get("redis.args_length"), 2)
@ -73,7 +72,7 @@ class TestRedisInstrument(TestBase):
spans = self.memory_exporter.get_finished_spans() spans = self.memory_exporter.get_finished_spans()
self.assertEqual(len(spans), 1) self.assertEqual(len(spans), 1)
span = spans[0] span = spans[0]
self._check_span(span) self._check_span(span, "SET RPUSH HGETALL")
self.assertEqual( self.assertEqual(
span.attributes.get("db.statement"), span.attributes.get("db.statement"),
"SET blah 32\nRPUSH foo éé\nHGETALL xxx", "SET blah 32\nRPUSH foo éé\nHGETALL xxx",
@ -91,7 +90,7 @@ class TestRedisInstrument(TestBase):
# single span for the whole pipeline # single span for the whole pipeline
self.assertEqual(len(spans), 2) self.assertEqual(len(spans), 2)
span = spans[0] span = spans[0]
self._check_span(span) self._check_span(span, "SET")
self.assertEqual(span.attributes.get("db.statement"), "SET b 2") self.assertEqual(span.attributes.get("db.statement"), "SET b 2")
def test_parent(self): def test_parent(self):
@ -115,4 +114,4 @@ class TestRedisInstrument(TestBase):
self.assertEqual( self.assertEqual(
child_span.attributes.get("service"), self.test_service child_span.attributes.get("service"), self.test_service
) )
self.assertEqual(child_span.name, "redis.command") self.assertEqual(child_span.name, "GET")