From 17cc913034ee93e6b6540ac694186aacb12c41e0 Mon Sep 17 00:00:00 2001 From: Owais Lone Date: Sat, 26 Sep 2020 00:27:33 +0530 Subject: [PATCH 1/4] wip --- .../opentelemetry/instrumentation/tornado/__init__.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/instrumentation/opentelemetry-instrumentation-tornado/src/opentelemetry/instrumentation/tornado/__init__.py b/instrumentation/opentelemetry-instrumentation-tornado/src/opentelemetry/instrumentation/tornado/__init__.py index 6379d841a03..a66f355359a 100644 --- a/instrumentation/opentelemetry-instrumentation-tornado/src/opentelemetry/instrumentation/tornado/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-tornado/src/opentelemetry/instrumentation/tornado/__init__.py @@ -71,7 +71,16 @@ def get_excluded_urls(): return ExcludeList(urls) +def get_traced_request_attrs(): + attrs = configuration.Configuration().TORNADO_TRACED_REQUEST_ATTRS or "" + if attrs: + attrs = [attr.strip() for attr in attrs.split(",")] + else: + attrs = [] + return attrs + _excluded_urls = get_excluded_urls() +_traced_attrs = get_traced_request_attrs() class TornadoInstrumentor(BaseInstrumentor): From 7ef87f92f6c8bf2db2e4ab35f3cfc430b09fb9d8 Mon Sep 17 00:00:00 2001 From: Owais Lone Date: Tue, 29 Sep 2020 19:09:43 +0530 Subject: [PATCH 2/4] Added ability to extract span attributes from Tonado request objects. OTEL_PYTHON_TONADO_TRACED_REQUEST_ATTRS env var can be set to a command separated list of attributes names that will be extracted from Tornado's request object and set as attributes on spans. --- .../instrumentation/django/middleware.py | 8 ++++---- .../instrumentation/falcon/__init__.py | 12 +++++++----- .../README.rst | 12 ++++++++++++ .../instrumentation/tornado/__init__.py | 5 ++++- .../tests/test_instrumentation.py | 15 +++++++++++++++ .../src/opentelemetry/instrumentation/utils.py | 15 +++++++++++++++ 6 files changed, 57 insertions(+), 10 deletions(-) diff --git a/instrumentation/opentelemetry-instrumentation-django/src/opentelemetry/instrumentation/django/middleware.py b/instrumentation/opentelemetry-instrumentation-django/src/opentelemetry/instrumentation/django/middleware.py index 07e3eb710b8..11991413eb8 100644 --- a/instrumentation/opentelemetry-instrumentation-django/src/opentelemetry/instrumentation/django/middleware.py +++ b/instrumentation/opentelemetry-instrumentation-django/src/opentelemetry/instrumentation/django/middleware.py @@ -17,6 +17,7 @@ from opentelemetry.configuration import Configuration from opentelemetry.context import attach, detach from opentelemetry.instrumentation.django.version import __version__ +from opentelemetry.instrumentation.utils import extract_attributes_from_object from opentelemetry.instrumentation.wsgi import ( add_response_attributes, collect_request_attributes, @@ -111,10 +112,9 @@ def process_request(self, request): if span.is_recording(): attributes = collect_request_attributes(environ) - for attr in self._traced_request_attrs: - value = getattr(request, attr, None) - if value is not None: - attributes[attr] = str(value) + attributes = extract_attributes_from_object( + request, self._traced_request_attrs, attributes + ) for key, value in attributes.items(): span.set_attribute(key, value) diff --git a/instrumentation/opentelemetry-instrumentation-falcon/src/opentelemetry/instrumentation/falcon/__init__.py b/instrumentation/opentelemetry-instrumentation-falcon/src/opentelemetry/instrumentation/falcon/__init__.py index 660fc23063c..7bc2f796815 100644 --- a/instrumentation/opentelemetry-instrumentation-falcon/src/opentelemetry/instrumentation/falcon/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-falcon/src/opentelemetry/instrumentation/falcon/__init__.py @@ -53,7 +53,10 @@ def on_get(self, req, resp): from opentelemetry.configuration import Configuration from opentelemetry.instrumentation.falcon.version import __version__ from opentelemetry.instrumentation.instrumentor import BaseInstrumentor -from opentelemetry.instrumentation.utils import http_status_to_canonical_code +from opentelemetry.instrumentation.utils import ( + extract_attributes_from_object, + http_status_to_canonical_code, +) from opentelemetry.trace.status import Status from opentelemetry.util import ExcludeList, time_ns @@ -162,10 +165,9 @@ def process_request(self, req, resp): if not span: return - for attr in self._traced_request_attrs: - value = getattr(req, attr, None) - if value is not None: - span.set_attribute(attr, str(value)) + attributes = extract_attributes_from_object(req, self._traced_request_attrs) + for key, value in attributes.items(): + span.set_attribute(key, str(value)) def process_resource(self, req, resp, resource, params): span = req.env.get(_ENVIRON_SPAN_KEY) diff --git a/instrumentation/opentelemetry-instrumentation-tornado/README.rst b/instrumentation/opentelemetry-instrumentation-tornado/README.rst index d84fbd0412c..65e8b982421 100644 --- a/instrumentation/opentelemetry-instrumentation-tornado/README.rst +++ b/instrumentation/opentelemetry-instrumentation-tornado/README.rst @@ -31,6 +31,18 @@ A comma separated list of paths that should not be automatically traced. For exa Then any requests made to ``/healthz`` and ``/ping`` will not be automatically traced. +Request attributes +******************** +To extract certain attributes from Tornado's request object and use them as span attributes, set the environment variable ``OTEL_PYTHON_TORNADO_TRACED_REQUEST_ATTRS`` to a comma +delimited list of request attribute names. + +For example, + +:: + + export OTEL_PYTHON_TONADO_TRACED_REQUEST_ATTRS='uri,query' + +will extract path_info and content_type attributes from every traced request and add them as span attritbues. References ---------- diff --git a/instrumentation/opentelemetry-instrumentation-tornado/src/opentelemetry/instrumentation/tornado/__init__.py b/instrumentation/opentelemetry-instrumentation-tornado/src/opentelemetry/instrumentation/tornado/__init__.py index a66f355359a..5357be6d0fc 100644 --- a/instrumentation/opentelemetry-instrumentation-tornado/src/opentelemetry/instrumentation/tornado/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-tornado/src/opentelemetry/instrumentation/tornado/__init__.py @@ -50,6 +50,7 @@ def get(self): from opentelemetry.instrumentation.instrumentor import BaseInstrumentor from opentelemetry.instrumentation.tornado.version import __version__ from opentelemetry.instrumentation.utils import ( + extract_attributes_from_object, http_status_to_canonical_code, unwrap, ) @@ -79,6 +80,7 @@ def get_traced_request_attrs(): attrs = [] return attrs + _excluded_urls = get_excluded_urls() _traced_attrs = get_traced_request_attrs() @@ -205,7 +207,7 @@ def _get_attributes_from_request(request): if request.remote_ip: attrs["net.peer.ip"] = request.remote_ip - return attrs + return extract_attributes_from_object(request, _traced_attrs, attrs) def _get_operation_name(handler, request): @@ -220,6 +222,7 @@ def _start_span(tracer, handler, start_time) -> _TraceContext: _get_header_from_request_headers, handler.request.headers, ) ) + span = tracer.start_span( _get_operation_name(handler, handler.request), kind=trace.SpanKind.SERVER, diff --git a/instrumentation/opentelemetry-instrumentation-tornado/tests/test_instrumentation.py b/instrumentation/opentelemetry-instrumentation-tornado/tests/test_instrumentation.py index d900b5d360e..64b055a2bcd 100644 --- a/instrumentation/opentelemetry-instrumentation-tornado/tests/test_instrumentation.py +++ b/instrumentation/opentelemetry-instrumentation-tornado/tests/test_instrumentation.py @@ -354,6 +354,21 @@ def test_excluded(path): test_excluded("/healthz") test_excluded("/ping") + @patch( + "opentelemetry.instrumentation.tornado._traced_attrs", + ["uri", "full_url", "query"], + ) + def test_traced_attrs(self): + self.fetch("/ping?q=abc&b=123") + spans = self.sorted_spans(self.memory_exporter.get_finished_spans()) + self.assertEqual(len(spans), 2) + server = spans[0] + self.assertEqual(server.kind, SpanKind.SERVER) + self.assert_span_has_attributes( + server, {"uri": "/ping?q=abc&b=123", "query": "q=abc&b=123"} + ) + self.memory_exporter.clear() + class TestTornadoUninstrument(TornadoTest): def test_uninstrument(self): diff --git a/opentelemetry-instrumentation/src/opentelemetry/instrumentation/utils.py b/opentelemetry-instrumentation/src/opentelemetry/instrumentation/utils.py index 8553b1bc63a..6220854ad59 100644 --- a/opentelemetry-instrumentation/src/opentelemetry/instrumentation/utils.py +++ b/opentelemetry-instrumentation/src/opentelemetry/instrumentation/utils.py @@ -12,11 +12,26 @@ # See the License for the specific language governing permissions and # limitations under the License. +from typing import Dict, Sequence + from wrapt import ObjectProxy from opentelemetry.trace.status import StatusCanonicalCode +def extract_attributes_from_object( + obj: any, attributes: Sequence[str], existing: Dict[str, str] = None +) -> Dict[str, str]: + extracted = {} + if existing: + extracted.update(existing) + for attr in attributes: + value = getattr(obj, attr, None) + if value is not None: + extracted[attr] = str(value) + return extracted + + def http_status_to_canonical_code( status: int, allow_redirect: bool = True ) -> StatusCanonicalCode: From 1f4fd30c6c1f5044bc924a98b0c1c466e53f7666 Mon Sep 17 00:00:00 2001 From: Owais Lone Date: Tue, 29 Sep 2020 23:16:35 +0530 Subject: [PATCH 3/4] Update instrumentation/opentelemetry-instrumentation-tornado/README.rst Co-authored-by: (Eliseo) Nathaniel Ruiz Nowell --- .../opentelemetry-instrumentation-tornado/README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/instrumentation/opentelemetry-instrumentation-tornado/README.rst b/instrumentation/opentelemetry-instrumentation-tornado/README.rst index 65e8b982421..5d428435c28 100644 --- a/instrumentation/opentelemetry-instrumentation-tornado/README.rst +++ b/instrumentation/opentelemetry-instrumentation-tornado/README.rst @@ -42,7 +42,7 @@ For example, export OTEL_PYTHON_TONADO_TRACED_REQUEST_ATTRS='uri,query' -will extract path_info and content_type attributes from every traced request and add them as span attritbues. +will extract path_info and content_type attributes from every traced request and add them as span attributes. References ---------- From 5c0bc91d85a8372eb7e8a26945144b9411c80c00 Mon Sep 17 00:00:00 2001 From: Owais Lone Date: Tue, 29 Sep 2020 23:17:34 +0530 Subject: [PATCH 4/4] Update instrumentation/opentelemetry-instrumentation-tornado/tests/test_instrumentation.py Co-authored-by: (Eliseo) Nathaniel Ruiz Nowell --- .../src/opentelemetry/instrumentation/falcon/__init__.py | 6 ++++-- .../opentelemetry-instrumentation-tornado/CHANGELOG.md | 2 ++ .../opentelemetry-instrumentation-tornado/README.rst | 2 +- .../tests/test_instrumentation.py | 6 +++--- 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/instrumentation/opentelemetry-instrumentation-falcon/src/opentelemetry/instrumentation/falcon/__init__.py b/instrumentation/opentelemetry-instrumentation-falcon/src/opentelemetry/instrumentation/falcon/__init__.py index 7bc2f796815..bfcd45a8b58 100644 --- a/instrumentation/opentelemetry-instrumentation-falcon/src/opentelemetry/instrumentation/falcon/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-falcon/src/opentelemetry/instrumentation/falcon/__init__.py @@ -165,9 +165,11 @@ def process_request(self, req, resp): if not span: return - attributes = extract_attributes_from_object(req, self._traced_request_attrs) + attributes = extract_attributes_from_object( + req, self._traced_request_attrs + ) for key, value in attributes.items(): - span.set_attribute(key, str(value)) + span.set_attribute(key, value) def process_resource(self, req, resp, resource, params): span = req.env.get(_ENVIRON_SPAN_KEY) diff --git a/instrumentation/opentelemetry-instrumentation-tornado/CHANGELOG.md b/instrumentation/opentelemetry-instrumentation-tornado/CHANGELOG.md index 7cc628718de..e82446d66d7 100644 --- a/instrumentation/opentelemetry-instrumentation-tornado/CHANGELOG.md +++ b/instrumentation/opentelemetry-instrumentation-tornado/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +- Added support for `OTEL_PYTHON_TORNADO_TRACED_REQUEST_ATTRS` ([#1178](https://github.com/open-telemetry/opentelemetry-python/pull/1178)) + ## Version 0.13b0 Released 2020-09-17 diff --git a/instrumentation/opentelemetry-instrumentation-tornado/README.rst b/instrumentation/opentelemetry-instrumentation-tornado/README.rst index 5d428435c28..088c7f0e85d 100644 --- a/instrumentation/opentelemetry-instrumentation-tornado/README.rst +++ b/instrumentation/opentelemetry-instrumentation-tornado/README.rst @@ -40,7 +40,7 @@ For example, :: - export OTEL_PYTHON_TONADO_TRACED_REQUEST_ATTRS='uri,query' + export OTEL_PYTHON_TORNADO_TRACED_REQUEST_ATTRS='uri,query' will extract path_info and content_type attributes from every traced request and add them as span attributes. diff --git a/instrumentation/opentelemetry-instrumentation-tornado/tests/test_instrumentation.py b/instrumentation/opentelemetry-instrumentation-tornado/tests/test_instrumentation.py index 64b055a2bcd..eb2852f1123 100644 --- a/instrumentation/opentelemetry-instrumentation-tornado/tests/test_instrumentation.py +++ b/instrumentation/opentelemetry-instrumentation-tornado/tests/test_instrumentation.py @@ -362,10 +362,10 @@ def test_traced_attrs(self): self.fetch("/ping?q=abc&b=123") spans = self.sorted_spans(self.memory_exporter.get_finished_spans()) self.assertEqual(len(spans), 2) - server = spans[0] - self.assertEqual(server.kind, SpanKind.SERVER) + server_span = spans[0] + self.assertEqual(server_span.kind, SpanKind.SERVER) self.assert_span_has_attributes( - server, {"uri": "/ping?q=abc&b=123", "query": "q=abc&b=123"} + server_span, {"uri": "/ping?q=abc&b=123", "query": "q=abc&b=123"} ) self.memory_exporter.clear()