From e7f91831785ffb1030d326576db8ed214668c7f7 Mon Sep 17 00:00:00 2001 From: Leighton Date: Tue, 15 Sep 2020 12:57:31 -0400 Subject: [PATCH 01/21] duration --- docs/examples/basic_meter/standard.py | 35 ++++++++ .../CHANGELOG.md | 2 + .../instrumentation/requests/__init__.py | 89 ++++++++++++------- opentelemetry-instrumentation/CHANGELOG.md | 2 + opentelemetry-instrumentation/setup.cfg | 1 + 5 files changed, 95 insertions(+), 34 deletions(-) create mode 100644 docs/examples/basic_meter/standard.py diff --git a/docs/examples/basic_meter/standard.py b/docs/examples/basic_meter/standard.py new file mode 100644 index 0000000000..2da068eda6 --- /dev/null +++ b/docs/examples/basic_meter/standard.py @@ -0,0 +1,35 @@ +# Copyright The OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +""" +This module shows how you can enable collection and exporting of standard +metrics related to instrumentations. +""" +import requests + +from opentelemetry import metrics +from opentelemetry.instrumentation.requests import RequestsInstrumentor +from opentelemetry.sdk.metrics import MeterProvider +from opentelemetry.sdk.metrics.export import ConsoleMetricsExporter + +# Sets the global MeterProvider instance +metrics.set_meter_provider(MeterProvider()) + +# Exporter to export metrics to the console +exporter = ConsoleMetricsExporter() + +RequestsInstrumentor().instrument(metrics_exporter=exporter, metrics_interval=5) +response = requests.get("http://example.com") + +input("...\n") diff --git a/instrumentation/opentelemetry-instrumentation-requests/CHANGELOG.md b/instrumentation/opentelemetry-instrumentation-requests/CHANGELOG.md index d42fd8f829..7c16ca2d60 100644 --- a/instrumentation/opentelemetry-instrumentation-requests/CHANGELOG.md +++ b/instrumentation/opentelemetry-instrumentation-requests/CHANGELOG.md @@ -6,6 +6,8 @@ ([#1040](https://github.com/open-telemetry/opentelemetry-python/pull/1040)) - Drop support for Python 3.4 ([#1099](https://github.com/open-telemetry/opentelemetry-python/pull/1099)) +- Add support for standard metrics - duration + ([#1099](https://github.com/open-telemetry/opentelemetry-python/pull/1099)) ## Version 0.12b0 diff --git a/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/__init__.py b/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/__init__.py index fef67c5d0d..a0f4661845 100644 --- a/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/__init__.py @@ -25,7 +25,7 @@ import opentelemetry.instrumentation.requests # You can optionally pass a custom TracerProvider to - RequestInstrumentor.instrument() + # RequestInstrumentor.instrument() opentelemetry.instrumentation.requests.RequestsInstrumentor().instrument() response = requests.get(url="https://www.example.org/") @@ -43,6 +43,7 @@ from opentelemetry import context, propagators from opentelemetry.instrumentation.instrumentor import BaseInstrumentor +from opentelemetry.instrumentation.metric import HTTPMetricRecorder, MetricMixin from opentelemetry.instrumentation.requests.version import __version__ from opentelemetry.instrumentation.utils import http_status_to_canonical_code from opentelemetry.trace import SpanKind, get_tracer @@ -118,43 +119,55 @@ def _instrumented_requests_call( exception = None + recorder = RequestsInstrumentor().metric_recorder + + labels = {} + labels["http.method"] = method + labels["http.url"] = url + with get_tracer( __name__, __version__, tracer_provider ).start_as_current_span(span_name, kind=SpanKind.CLIENT) as span: - if span.is_recording(): - span.set_attribute("component", "http") - span.set_attribute("http.method", method.upper()) - span.set_attribute("http.url", url) + with recorder.record_duration(labels): + if span.is_recording(): + span.set_attribute("component", "http") + span.set_attribute("http.method", method) + span.set_attribute("http.url", url) - headers = get_or_create_headers() - propagators.inject(type(headers).__setitem__, headers) - - token = context.attach( - context.set_value(_SUPPRESS_REQUESTS_INSTRUMENTATION_KEY, True) - ) - try: - result = call_wrapped() # *** PROCEED - except Exception as exc: # pylint: disable=W0703 - exception = exc - result = getattr(exc, "response", None) - finally: - context.detach(token) - - if exception is not None and span.is_recording(): - span.set_status( - Status(_exception_to_canonical_code(exception)) - ) - span.record_exception(exception) + headers = get_or_create_headers() + propagators.inject(type(headers).__setitem__, headers) - if result is not None and span.is_recording(): - span.set_attribute("http.status_code", result.status_code) - span.set_attribute("http.status_text", result.reason) - span.set_status( - Status(http_status_to_canonical_code(result.status_code)) + token = context.attach( + context.set_value(_SUPPRESS_REQUESTS_INSTRUMENTATION_KEY, True) ) - - if span_callback is not None: - span_callback(span, result) + try: + result = call_wrapped() # *** PROCEED + except Exception as exc: # pylint: disable=W0703 + exception = exc + result = getattr(exc, "response", None) + finally: + context.detach(token) + + if exception is not None and span.is_recording(): + span.set_status( + Status(_exception_to_canonical_code(exception)) + ) + span.record_exception(exception) + + if result is not None: + if span.is_recording(): + span.set_attribute("http.status_code", result.status_code) + span.set_attribute("http.status_text", result.reason) + span.set_status( + Status(http_status_to_canonical_code(result.status_code)) + ) + labels["http.status_code"] = result.status_code + labels["http.status_text"] = result.reason + if result.raw and result.raw.version: + labels["http.flavor"] = result.raw.version + + if span_callback is not None: + span_callback(span, result) if exception is not None: raise exception.with_traceback(exception.__traceback__) @@ -202,11 +215,10 @@ def _exception_to_canonical_code(exc: Exception) -> StatusCanonicalCode: return StatusCanonicalCode.UNKNOWN -class RequestsInstrumentor(BaseInstrumentor): +class RequestsInstrumentor(BaseInstrumentor, MetricMixin): """An instrumentor for requests See `BaseInstrumentor` """ - def _instrument(self, **kwargs): """Instruments requests module @@ -214,11 +226,20 @@ def _instrument(self, **kwargs): **kwargs: Optional arguments ``tracer_provider``: a TracerProvider, defaults to global ``span_callback``: An optional callback invoked before returning the http response. Invoked with Span and requests.Response + ``metrics_exporter``: the metric exporter to send requests related metrics to. + ``metrics_interval``: the export interval for the metric exporter """ _instrument( tracer_provider=kwargs.get("tracer_provider"), span_callback=kwargs.get("span_callback"), ) + self.init_metrics( + __name__, + __version__, + kwargs.get("metrics_exporter"), + kwargs.get("metrics_interval") + ) + self.metric_recorder = HTTPMetricRecorder(self.meter, SpanKind.CLIENT) def _uninstrument(self, **kwargs): _uninstrument() diff --git a/opentelemetry-instrumentation/CHANGELOG.md b/opentelemetry-instrumentation/CHANGELOG.md index 91dc011f09..febd2a90e9 100644 --- a/opentelemetry-instrumentation/CHANGELOG.md +++ b/opentelemetry-instrumentation/CHANGELOG.md @@ -4,6 +4,8 @@ - Drop support for Python 3.4 ([#1099](https://github.com/open-telemetry/opentelemetry-python/pull/1099)) +- Add support for standard metrics - duration + ([#1099](https://github.com/open-telemetry/opentelemetry-python/pull/1099)) ## 0.9b0 diff --git a/opentelemetry-instrumentation/setup.cfg b/opentelemetry-instrumentation/setup.cfg index fd131a42cb..c24f946d2a 100644 --- a/opentelemetry-instrumentation/setup.cfg +++ b/opentelemetry-instrumentation/setup.cfg @@ -42,6 +42,7 @@ zip_safe = False include_package_data = True install_requires = opentelemetry-api == 0.13dev0 + opentelemetry-sdk == 0.13dev0 wrapt >= 1.0.0, < 2.0.0 [options.packages.find] From d7f771efdd0d0fa668597b0be0130bd3f901d401 Mon Sep 17 00:00:00 2001 From: Leighton Date: Tue, 15 Sep 2020 16:37:23 -0400 Subject: [PATCH 02/21] tests --- .../tests/test_requests_integration.py | 44 +++++++++ .../tests/test_metric.py | 95 +++++++++++++++++++ 2 files changed, 139 insertions(+) create mode 100644 opentelemetry-instrumentation/tests/test_metric.py diff --git a/instrumentation/opentelemetry-instrumentation-requests/tests/test_requests_integration.py b/instrumentation/opentelemetry-instrumentation-requests/tests/test_requests_integration.py index c3457b7392..775c48d2d4 100644 --- a/instrumentation/opentelemetry-instrumentation-requests/tests/test_requests_integration.py +++ b/instrumentation/opentelemetry-instrumentation-requests/tests/test_requests_integration.py @@ -88,6 +88,23 @@ def test_basic(self): span, opentelemetry.instrumentation.requests ) + self.assertIsNotNone(RequestsInstrumentor().meter) + self.assertEqual(len(RequestsInstrumentor().meter.metrics), 1) + recorder = RequestsInstrumentor().meter.metrics.pop() + key = tuple({ + 'http.flavor': 11, + 'http.method': 'GET', + 'http.status_code': 200, + 'http.status_text':'OK', + 'http.url': 'http://httpbin.org/status/200' + }.items()) + # pylint: disable=protected-access + bound = recorder.bound_instruments.get(key) + for view_data in bound.view_datas: + self.assertEqual(view_data.labels, key) + self.assertEqual(view_data.aggregator.current.count, 1) + self.assertGreater(view_data.aggregator.current.sum, 0) + def test_not_foundbasic(self): url_404 = "http://httpbin.org/status/404" httpretty.register_uri( @@ -246,6 +263,19 @@ def test_requests_exception_without_response(self, *_, **__): span.status.canonical_code, StatusCanonicalCode.UNKNOWN ) + self.assertIsNotNone(RequestsInstrumentor().meter) + self.assertEqual(len(RequestsInstrumentor().meter.metrics), 1) + recorder = RequestsInstrumentor().meter.metrics.pop() + key = tuple({ + 'http.method': 'GET', + 'http.url': 'http://httpbin.org/status/200' + }.items()) + # pylint: disable=protected-access + bound = recorder.bound_instruments.get(key) + for view_data in bound.view_datas: + self.assertEqual(view_data.labels, key) + self.assertEqual(view_data.aggregator.current.count, 1) + mocked_response = requests.Response() mocked_response.status_code = 500 mocked_response.reason = "Internal Server Error" @@ -272,6 +302,20 @@ def test_requests_exception_with_response(self, *_, **__): self.assertEqual( span.status.canonical_code, StatusCanonicalCode.INTERNAL ) + self.assertIsNotNone(RequestsInstrumentor().meter) + self.assertEqual(len(RequestsInstrumentor().meter.metrics), 1) + recorder = RequestsInstrumentor().meter.metrics.pop() + key = tuple({ + 'http.method': 'GET', + 'http.status_code': 500, + 'http.status_text':'Internal Server Error', + 'http.url': 'http://httpbin.org/status/200' + }.items()) + # pylint: disable=protected-access + bound = recorder.bound_instruments.get(key) + for view_data in bound.view_datas: + self.assertEqual(view_data.labels, key) + self.assertEqual(view_data.aggregator.current.count, 1) @mock.patch("requests.adapters.HTTPAdapter.send", side_effect=Exception) def test_requests_basic_exception(self, *_, **__): diff --git a/opentelemetry-instrumentation/tests/test_metric.py b/opentelemetry-instrumentation/tests/test_metric.py new file mode 100644 index 0000000000..d76d80a73f --- /dev/null +++ b/opentelemetry-instrumentation/tests/test_metric.py @@ -0,0 +1,95 @@ +# Copyright The OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# type: ignore + +import time + +from unittest import mock, TestCase + +from opentelemetry import metrics as metrics_api +from opentelemetry.trace import SpanKind +from opentelemetry.instrumentation.metric import MetricMixin, HTTPMetricRecorder +from opentelemetry.metrics import set_meter_provider +from opentelemetry.sdk import metrics +from opentelemetry.sdk.metrics.export import ConsoleMetricsExporter + + +class TestMetricMixin(TestCase): + + @classmethod + def setUpClass(cls): + metrics_api._METER_PROVIDER = None + set_meter_provider(metrics.MeterProvider()) + + @classmethod + def tearDownClass(cls): + metrics_api._METER_PROVIDER = None + + def test_init(self): + mixin = MetricMixin() + mixin.init_metrics("test", 1.0) + meter = mixin.meter + self.assertTrue(isinstance(meter, metrics.Meter)) + self.assertEqual(meter.instrumentation_info.name, "test") + self.assertEqual(meter.instrumentation_info.version, 1.0) + + def test_init_exporter(self): + mixin = MetricMixin() + exporter = ConsoleMetricsExporter() + mixin.init_metrics("test", 1.0, exporter, 100.0) + meter = mixin.meter + self.assertTrue(isinstance(meter, metrics.Meter)) + self.assertEqual(meter.instrumentation_info.name, "test") + self.assertEqual(meter.instrumentation_info.version, 1.0) + # pylint: disable=protected-access + self.assertEqual(mixin._controller.exporter, exporter) + self.assertEqual(mixin._controller.meter, meter) + mixin._controller.shutdown() + + +class TestHTTPMetricRecorder(TestCase): + + @classmethod + def setUpClass(cls): + metrics_api._METER_PROVIDER = None + set_meter_provider(metrics.MeterProvider()) + + @classmethod + def tearDownClass(cls): + metrics_api._METER_PROVIDER = None + + def test_ctor(self): + meter = metrics_api.get_meter(__name__) + recorder = HTTPMetricRecorder(meter, SpanKind.CLIENT) + self.assertEqual(recorder.kind, SpanKind.CLIENT) + # pylint: disable=protected-access + self.assertTrue(isinstance(recorder._duration, metrics.ValueRecorder)) + self.assertEqual(recorder._duration.name, "http.client.duration") + self.assertEqual(recorder._duration.description, + "measures the duration of the outbound HTTP request") + + def test_record_duration(self): + meter = metrics_api.get_meter(__name__) + recorder = HTTPMetricRecorder(meter, SpanKind.CLIENT) + labels = {"test":"asd"} + with recorder.record_duration(labels): + labels["test2"] = "asd2" + time.sleep(1) + key = tuple({"test":"asd","test2":"asd2"}.items()) + # pylint: disable=protected-access + bound = recorder._duration.bound_instruments.get(key) + for view_data in bound.view_datas: + self.assertEqual(view_data.labels, key) + self.assertEqual(view_data.aggregator.current.count, 1) + self.assertGreater(view_data.aggregator.current.sum, 0) From c17732bd043529591723612dc5da39d7173937d8 Mon Sep 17 00:00:00 2001 From: Leighton Date: Tue, 15 Sep 2020 17:06:33 -0400 Subject: [PATCH 03/21] changelog --- .../CHANGELOG.md | 2 +- opentelemetry-instrumentation/CHANGELOG.md | 2 +- .../opentelemetry/instrumentation/metric.py | 74 +++++++++++++++++++ 3 files changed, 76 insertions(+), 2 deletions(-) create mode 100644 opentelemetry-instrumentation/src/opentelemetry/instrumentation/metric.py diff --git a/instrumentation/opentelemetry-instrumentation-requests/CHANGELOG.md b/instrumentation/opentelemetry-instrumentation-requests/CHANGELOG.md index 7c16ca2d60..a4eb1a0b35 100644 --- a/instrumentation/opentelemetry-instrumentation-requests/CHANGELOG.md +++ b/instrumentation/opentelemetry-instrumentation-requests/CHANGELOG.md @@ -7,7 +7,7 @@ - Drop support for Python 3.4 ([#1099](https://github.com/open-telemetry/opentelemetry-python/pull/1099)) - Add support for standard metrics - duration - ([#1099](https://github.com/open-telemetry/opentelemetry-python/pull/1099)) + ([#1116](https://github.com/open-telemetry/opentelemetry-python/pull/1116)) ## Version 0.12b0 diff --git a/opentelemetry-instrumentation/CHANGELOG.md b/opentelemetry-instrumentation/CHANGELOG.md index febd2a90e9..a95d3d35a0 100644 --- a/opentelemetry-instrumentation/CHANGELOG.md +++ b/opentelemetry-instrumentation/CHANGELOG.md @@ -5,7 +5,7 @@ - Drop support for Python 3.4 ([#1099](https://github.com/open-telemetry/opentelemetry-python/pull/1099)) - Add support for standard metrics - duration - ([#1099](https://github.com/open-telemetry/opentelemetry-python/pull/1099)) + ([#1116](https://github.com/open-telemetry/opentelemetry-python/pull/1116)) ## 0.9b0 diff --git a/opentelemetry-instrumentation/src/opentelemetry/instrumentation/metric.py b/opentelemetry-instrumentation/src/opentelemetry/instrumentation/metric.py new file mode 100644 index 0000000000..8029ac2c5f --- /dev/null +++ b/opentelemetry-instrumentation/src/opentelemetry/instrumentation/metric.py @@ -0,0 +1,74 @@ +# Copyright The OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# type: ignore + +""" +OpenTelemetry Instrumentation Metric mixin +""" +from contextlib import contextmanager +from time import time + +from opentelemetry import metrics +from opentelemetry.trace import SpanKind +from opentelemetry.sdk.metrics import PushController, ValueRecorder + + +class MetricMixin: + """Used to record metrics related to instrumentations.""" + + def init_metrics(self, name, version, exporter=None, interval=None): + self._meter = metrics.get_meter(name, version) + if exporter and interval: + self._controller = PushController( + meter=self._meter, exporter=exporter, interval=interval + ) + + @property + def meter(self): + return self._meter + + +class MetricRecorder: + """Base class for metric recorders of different types.""" + + def __init__(self, meter): + self._meter = meter + + +class HTTPMetricRecorder(MetricRecorder): + """Metric recorder for http instrumentations. Tracks duration.""" + + def __init__(self, meter, kind: SpanKind): + super().__init__(meter) + self.kind = kind + if self._meter: + self._duration = self._meter.create_metric( + name="{}.{}.duration".format("http", self.kind.name.lower()), + description="measures the duration of the {} HTTP request".format( + "inbound" if self.kind is SpanKind.SERVER else "outbound" + ), + unit="ms", + value_type=float, + metric_type=ValueRecorder, + ) + + @contextmanager + def record_duration(self, labels): + start_time = time() + try: + yield start_time + finally: + if self._meter: + elapsed_time = (time() - start_time) * 1000 + self._duration.record(elapsed_time, labels) From b85c02a750b5b522f5ec56785fec5d754e1b5c4a Mon Sep 17 00:00:00 2001 From: Leighton Date: Tue, 15 Sep 2020 17:41:04 -0400 Subject: [PATCH 04/21] fix build --- docs/instrumentation/instrumentation.rst | 1 + .../tests/test_metric.py | 17 ++++++++++------- tox.ini | 2 +- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/docs/instrumentation/instrumentation.rst b/docs/instrumentation/instrumentation.rst index 9c01b6b6f4..16b82a2093 100644 --- a/docs/instrumentation/instrumentation.rst +++ b/docs/instrumentation/instrumentation.rst @@ -13,3 +13,4 @@ Submodules :maxdepth: 1 instrumentor + metric diff --git a/opentelemetry-instrumentation/tests/test_metric.py b/opentelemetry-instrumentation/tests/test_metric.py index d76d80a73f..357b9b4c9f 100644 --- a/opentelemetry-instrumentation/tests/test_metric.py +++ b/opentelemetry-instrumentation/tests/test_metric.py @@ -19,14 +19,16 @@ from opentelemetry import metrics as metrics_api from opentelemetry.trace import SpanKind -from opentelemetry.instrumentation.metric import MetricMixin, HTTPMetricRecorder +from opentelemetry.instrumentation.metric import ( + MetricMixin, + HTTPMetricRecorder, +) from opentelemetry.metrics import set_meter_provider from opentelemetry.sdk import metrics from opentelemetry.sdk.metrics.export import ConsoleMetricsExporter class TestMetricMixin(TestCase): - @classmethod def setUpClass(cls): metrics_api._METER_PROVIDER = None @@ -59,7 +61,6 @@ def test_init_exporter(self): class TestHTTPMetricRecorder(TestCase): - @classmethod def setUpClass(cls): metrics_api._METER_PROVIDER = None @@ -76,17 +77,19 @@ def test_ctor(self): # pylint: disable=protected-access self.assertTrue(isinstance(recorder._duration, metrics.ValueRecorder)) self.assertEqual(recorder._duration.name, "http.client.duration") - self.assertEqual(recorder._duration.description, - "measures the duration of the outbound HTTP request") + self.assertEqual( + recorder._duration.description, + "measures the duration of the outbound HTTP request", + ) def test_record_duration(self): meter = metrics_api.get_meter(__name__) recorder = HTTPMetricRecorder(meter, SpanKind.CLIENT) - labels = {"test":"asd"} + labels = {"test": "asd"} with recorder.record_duration(labels): labels["test2"] = "asd2" time.sleep(1) - key = tuple({"test":"asd","test2":"asd2"}.items()) + key = tuple({"test": "asd", "test2": "asd2"}.items()) # pylint: disable=protected-access bound = recorder._duration.bound_instruments.get(key) for view_data in bound.view_datas: diff --git a/tox.ini b/tox.ini index 0575629074..2eead5f571 100644 --- a/tox.ini +++ b/tox.ini @@ -381,8 +381,8 @@ deps = commands_pre = pip install -e {toxinidir}/opentelemetry-api \ - -e {toxinidir}/opentelemetry-instrumentation \ -e {toxinidir}/opentelemetry-sdk \ + -e {toxinidir}/opentelemetry-instrumentation \ -e {toxinidir}/instrumentation/opentelemetry-instrumentation-requests \ -e {toxinidir}/instrumentation/opentelemetry-instrumentation-wsgi \ -e {toxinidir}/instrumentation/opentelemetry-instrumentation-flask From 250422e441f71b1e960bd48fd60604aa8a9ef970 Mon Sep 17 00:00:00 2001 From: Leighton Date: Tue, 15 Sep 2020 17:58:31 -0400 Subject: [PATCH 05/21] fix tests --- docs/instrumentation/metric.rst | 7 ++ .../instrumentation/requests/__init__.py | 24 ++++-- .../tests/test_requests_integration.py | 85 +++++++++++-------- 3 files changed, 75 insertions(+), 41 deletions(-) create mode 100644 docs/instrumentation/metric.rst diff --git a/docs/instrumentation/metric.rst b/docs/instrumentation/metric.rst new file mode 100644 index 0000000000..6a69eeeca5 --- /dev/null +++ b/docs/instrumentation/metric.rst @@ -0,0 +1,7 @@ +opentelemetry.instrumentation.metric package +============================================ + +.. automodule:: opentelemetry.instrumentation.metric + :members: + :undoc-members: + :show-inheritance: diff --git a/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/__init__.py b/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/__init__.py index a0f4661845..7a188c373b 100644 --- a/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/__init__.py @@ -43,7 +43,10 @@ from opentelemetry import context, propagators from opentelemetry.instrumentation.instrumentor import BaseInstrumentor -from opentelemetry.instrumentation.metric import HTTPMetricRecorder, MetricMixin +from opentelemetry.instrumentation.metric import ( + HTTPMetricRecorder, + MetricMixin, +) from opentelemetry.instrumentation.requests.version import __version__ from opentelemetry.instrumentation.utils import http_status_to_canonical_code from opentelemetry.trace import SpanKind, get_tracer @@ -138,7 +141,9 @@ def _instrumented_requests_call( propagators.inject(type(headers).__setitem__, headers) token = context.attach( - context.set_value(_SUPPRESS_REQUESTS_INSTRUMENTATION_KEY, True) + context.set_value( + _SUPPRESS_REQUESTS_INSTRUMENTATION_KEY, True + ) ) try: result = call_wrapped() # *** PROCEED @@ -156,10 +161,16 @@ def _instrumented_requests_call( if result is not None: if span.is_recording(): - span.set_attribute("http.status_code", result.status_code) + span.set_attribute( + "http.status_code", result.status_code + ) span.set_attribute("http.status_text", result.reason) span.set_status( - Status(http_status_to_canonical_code(result.status_code)) + Status( + http_status_to_canonical_code( + result.status_code + ) + ) ) labels["http.status_code"] = result.status_code labels["http.status_text"] = result.reason @@ -219,6 +230,7 @@ class RequestsInstrumentor(BaseInstrumentor, MetricMixin): """An instrumentor for requests See `BaseInstrumentor` """ + def _instrument(self, **kwargs): """Instruments requests module @@ -236,8 +248,8 @@ def _instrument(self, **kwargs): self.init_metrics( __name__, __version__, - kwargs.get("metrics_exporter"), - kwargs.get("metrics_interval") + kwargs.get("metrics_exporter"), + kwargs.get("metrics_interval"), ) self.metric_recorder = HTTPMetricRecorder(self.meter, SpanKind.CLIENT) diff --git a/instrumentation/opentelemetry-instrumentation-requests/tests/test_requests_integration.py b/instrumentation/opentelemetry-instrumentation-requests/tests/test_requests_integration.py index 775c48d2d4..e1f589d25c 100644 --- a/instrumentation/opentelemetry-instrumentation-requests/tests/test_requests_integration.py +++ b/instrumentation/opentelemetry-instrumentation-requests/tests/test_requests_integration.py @@ -91,24 +91,30 @@ def test_basic(self): self.assertIsNotNone(RequestsInstrumentor().meter) self.assertEqual(len(RequestsInstrumentor().meter.metrics), 1) recorder = RequestsInstrumentor().meter.metrics.pop() - key = tuple({ - 'http.flavor': 11, - 'http.method': 'GET', - 'http.status_code': 200, - 'http.status_text':'OK', - 'http.url': 'http://httpbin.org/status/200' - }.items()) - # pylint: disable=protected-access - bound = recorder.bound_instruments.get(key) - for view_data in bound.view_datas: - self.assertEqual(view_data.labels, key) - self.assertEqual(view_data.aggregator.current.count, 1) - self.assertGreater(view_data.aggregator.current.sum, 0) + match_key = tuple( + { + "http.flavor": 11, + "http.method": "GET", + "http.status_code": 200, + "http.status_text": "OK", + "http.url": "http://httpbin.org/status/200", + }.items() + ) + for key in recorder.bound_instruments.keys(): + self.assertEqual(key, match_key) + # pylint: disable=protected-access + bound = recorder.bound_instruments.get(key) + for view_data in bound.view_datas: + self.assertEqual(view_data.labels, key) + self.assertEqual(view_data.aggregator.current.count, 1) + self.assertGreater(view_data.aggregator.current.sum, 0) def test_not_foundbasic(self): url_404 = "http://httpbin.org/status/404" httpretty.register_uri( - httpretty.GET, url_404, status=404, + httpretty.GET, + url_404, + status=404, ) result = self.perform_request(url_404) self.assertEqual(result.status_code, 404) @@ -214,7 +220,8 @@ def span_callback(span, result: requests.Response): ) RequestsInstrumentor().instrument( - tracer_provider=self.tracer_provider, span_callback=span_callback, + tracer_provider=self.tracer_provider, + span_callback=span_callback, ) result = self.perform_request(self.URL) @@ -266,15 +273,19 @@ def test_requests_exception_without_response(self, *_, **__): self.assertIsNotNone(RequestsInstrumentor().meter) self.assertEqual(len(RequestsInstrumentor().meter.metrics), 1) recorder = RequestsInstrumentor().meter.metrics.pop() - key = tuple({ - 'http.method': 'GET', - 'http.url': 'http://httpbin.org/status/200' - }.items()) - # pylint: disable=protected-access - bound = recorder.bound_instruments.get(key) - for view_data in bound.view_datas: - self.assertEqual(view_data.labels, key) - self.assertEqual(view_data.aggregator.current.count, 1) + match_key = tuple( + { + "http.method": "GET", + "http.url": "http://httpbin.org/status/200", + }.items() + ) + for key in recorder.bound_instruments.keys(): + self.assertEqual(key, match_key) + # pylint: disable=protected-access + bound = recorder.bound_instruments.get(key) + for view_data in bound.view_datas: + self.assertEqual(view_data.labels, key) + self.assertEqual(view_data.aggregator.current.count, 1) mocked_response = requests.Response() mocked_response.status_code = 500 @@ -305,17 +316,21 @@ def test_requests_exception_with_response(self, *_, **__): self.assertIsNotNone(RequestsInstrumentor().meter) self.assertEqual(len(RequestsInstrumentor().meter.metrics), 1) recorder = RequestsInstrumentor().meter.metrics.pop() - key = tuple({ - 'http.method': 'GET', - 'http.status_code': 500, - 'http.status_text':'Internal Server Error', - 'http.url': 'http://httpbin.org/status/200' - }.items()) - # pylint: disable=protected-access - bound = recorder.bound_instruments.get(key) - for view_data in bound.view_datas: - self.assertEqual(view_data.labels, key) - self.assertEqual(view_data.aggregator.current.count, 1) + match_key = tuple( + { + "http.method": "GET", + "http.status_code": 500, + "http.status_text": "Internal Server Error", + "http.url": "http://httpbin.org/status/200", + }.items() + ) + for key in recorder.bound_instruments.keys(): + self.assertEqual(key, match_key) + # pylint: disable=protected-access + bound = recorder.bound_instruments.get(key) + for view_data in bound.view_datas: + self.assertEqual(view_data.labels, key) + self.assertEqual(view_data.aggregator.current.count, 1) @mock.patch("requests.adapters.HTTPAdapter.send", side_effect=Exception) def test_requests_basic_exception(self, *_, **__): From d36979c6439884eb876288a5e6c484ac1f340b31 Mon Sep 17 00:00:00 2001 From: Leighton Date: Tue, 15 Sep 2020 18:08:38 -0400 Subject: [PATCH 06/21] lint --- docs/examples/basic_meter/standard.py | 4 +++- .../tests/test_requests_integration.py | 7 ++----- .../src/opentelemetry/instrumentation/metric.py | 2 +- .../tests/test_metric.py | 16 +++++++++------- 4 files changed, 15 insertions(+), 14 deletions(-) diff --git a/docs/examples/basic_meter/standard.py b/docs/examples/basic_meter/standard.py index 2da068eda6..8fc8b55d21 100644 --- a/docs/examples/basic_meter/standard.py +++ b/docs/examples/basic_meter/standard.py @@ -29,7 +29,9 @@ # Exporter to export metrics to the console exporter = ConsoleMetricsExporter() -RequestsInstrumentor().instrument(metrics_exporter=exporter, metrics_interval=5) +RequestsInstrumentor().instrument( + metrics_exporter=exporter, metrics_interval=5 +) response = requests.get("http://example.com") input("...\n") diff --git a/instrumentation/opentelemetry-instrumentation-requests/tests/test_requests_integration.py b/instrumentation/opentelemetry-instrumentation-requests/tests/test_requests_integration.py index e1f589d25c..4dbad506dc 100644 --- a/instrumentation/opentelemetry-instrumentation-requests/tests/test_requests_integration.py +++ b/instrumentation/opentelemetry-instrumentation-requests/tests/test_requests_integration.py @@ -112,9 +112,7 @@ def test_basic(self): def test_not_foundbasic(self): url_404 = "http://httpbin.org/status/404" httpretty.register_uri( - httpretty.GET, - url_404, - status=404, +h httpretty.GET, url_404, status=404, ) result = self.perform_request(url_404) self.assertEqual(result.status_code, 404) @@ -220,8 +218,7 @@ def span_callback(span, result: requests.Response): ) RequestsInstrumentor().instrument( - tracer_provider=self.tracer_provider, - span_callback=span_callback, + tracer_provider=self.tracer_provider, span_callback=span_callback, ) result = self.perform_request(self.URL) diff --git a/opentelemetry-instrumentation/src/opentelemetry/instrumentation/metric.py b/opentelemetry-instrumentation/src/opentelemetry/instrumentation/metric.py index 8029ac2c5f..a057df362e 100644 --- a/opentelemetry-instrumentation/src/opentelemetry/instrumentation/metric.py +++ b/opentelemetry-instrumentation/src/opentelemetry/instrumentation/metric.py @@ -41,7 +41,7 @@ def meter(self): class MetricRecorder: """Base class for metric recorders of different types.""" - + def __init__(self, meter): self._meter = meter diff --git a/opentelemetry-instrumentation/tests/test_metric.py b/opentelemetry-instrumentation/tests/test_metric.py index 357b9b4c9f..53763b9c8b 100644 --- a/opentelemetry-instrumentation/tests/test_metric.py +++ b/opentelemetry-instrumentation/tests/test_metric.py @@ -89,10 +89,12 @@ def test_record_duration(self): with recorder.record_duration(labels): labels["test2"] = "asd2" time.sleep(1) - key = tuple({"test": "asd", "test2": "asd2"}.items()) - # pylint: disable=protected-access - bound = recorder._duration.bound_instruments.get(key) - for view_data in bound.view_datas: - self.assertEqual(view_data.labels, key) - self.assertEqual(view_data.aggregator.current.count, 1) - self.assertGreater(view_data.aggregator.current.sum, 0) + match_key = tuple({"test": "asd", "test2": "asd2"}.items()) + for key in recorder._duration.bound_instruments.keys(): + self.assertEqual(key, match_key) + # pylint: disable=protected-access + bound = recorder._duration.bound_instruments.get(key) + for view_data in bound.view_datas: + self.assertEqual(view_data.labels, key) + self.assertEqual(view_data.aggregator.current.count, 1) + self.assertGreater(view_data.aggregator.current.sum, 0) From daef87525d3edcbbd638c42de15dc7b021c17cb7 Mon Sep 17 00:00:00 2001 From: Leighton Date: Tue, 15 Sep 2020 18:18:31 -0400 Subject: [PATCH 07/21] fix build --- .../tests/test_requests_integration.py | 2 +- opentelemetry-instrumentation/tests/test_metric.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/instrumentation/opentelemetry-instrumentation-requests/tests/test_requests_integration.py b/instrumentation/opentelemetry-instrumentation-requests/tests/test_requests_integration.py index 4dbad506dc..e6b9fa9314 100644 --- a/instrumentation/opentelemetry-instrumentation-requests/tests/test_requests_integration.py +++ b/instrumentation/opentelemetry-instrumentation-requests/tests/test_requests_integration.py @@ -112,7 +112,7 @@ def test_basic(self): def test_not_foundbasic(self): url_404 = "http://httpbin.org/status/404" httpretty.register_uri( -h httpretty.GET, url_404, status=404, + httpretty.GET, url_404, status=404, ) result = self.perform_request(url_404) self.assertEqual(result.status_code, 404) diff --git a/opentelemetry-instrumentation/tests/test_metric.py b/opentelemetry-instrumentation/tests/test_metric.py index 53763b9c8b..d5abfa3f48 100644 --- a/opentelemetry-instrumentation/tests/test_metric.py +++ b/opentelemetry-instrumentation/tests/test_metric.py @@ -26,6 +26,7 @@ from opentelemetry.metrics import set_meter_provider from opentelemetry.sdk import metrics from opentelemetry.sdk.metrics.export import ConsoleMetricsExporter +from opentelemetry.sdk.util import get_dict_as_key class TestMetricMixin(TestCase): @@ -89,7 +90,7 @@ def test_record_duration(self): with recorder.record_duration(labels): labels["test2"] = "asd2" time.sleep(1) - match_key = tuple({"test": "asd", "test2": "asd2"}.items()) + match_key = get_dict_as_key({"test": "asd", "test2": "asd2"}) for key in recorder._duration.bound_instruments.keys(): self.assertEqual(key, match_key) # pylint: disable=protected-access From 94beddee0a0b8bd29b44031054b870b9823214e2 Mon Sep 17 00:00:00 2001 From: Leighton Date: Tue, 15 Sep 2020 18:23:16 -0400 Subject: [PATCH 08/21] lint --- .../src/opentelemetry/instrumentation/metric.py | 2 +- opentelemetry-instrumentation/tests/test_metric.py | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/opentelemetry-instrumentation/src/opentelemetry/instrumentation/metric.py b/opentelemetry-instrumentation/src/opentelemetry/instrumentation/metric.py index a057df362e..d5e2b6149b 100644 --- a/opentelemetry-instrumentation/src/opentelemetry/instrumentation/metric.py +++ b/opentelemetry-instrumentation/src/opentelemetry/instrumentation/metric.py @@ -20,8 +20,8 @@ from time import time from opentelemetry import metrics -from opentelemetry.trace import SpanKind from opentelemetry.sdk.metrics import PushController, ValueRecorder +from opentelemetry.trace import SpanKind class MetricMixin: diff --git a/opentelemetry-instrumentation/tests/test_metric.py b/opentelemetry-instrumentation/tests/test_metric.py index d5abfa3f48..789d0f815b 100644 --- a/opentelemetry-instrumentation/tests/test_metric.py +++ b/opentelemetry-instrumentation/tests/test_metric.py @@ -14,14 +14,13 @@ # type: ignore import time - -from unittest import mock, TestCase +from unittest import TestCase, mock from opentelemetry import metrics as metrics_api from opentelemetry.trace import SpanKind from opentelemetry.instrumentation.metric import ( - MetricMixin, HTTPMetricRecorder, + MetricMixin, ) from opentelemetry.metrics import set_meter_provider from opentelemetry.sdk import metrics From adaeee398debce7db20e2886cb6191469e2ad5d2 Mon Sep 17 00:00:00 2001 From: Leighton Date: Tue, 15 Sep 2020 18:28:02 -0400 Subject: [PATCH 09/21] lint --- opentelemetry-instrumentation/tests/test_metric.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opentelemetry-instrumentation/tests/test_metric.py b/opentelemetry-instrumentation/tests/test_metric.py index 789d0f815b..826a63cda0 100644 --- a/opentelemetry-instrumentation/tests/test_metric.py +++ b/opentelemetry-instrumentation/tests/test_metric.py @@ -17,7 +17,6 @@ from unittest import TestCase, mock from opentelemetry import metrics as metrics_api -from opentelemetry.trace import SpanKind from opentelemetry.instrumentation.metric import ( HTTPMetricRecorder, MetricMixin, @@ -26,6 +25,7 @@ from opentelemetry.sdk import metrics from opentelemetry.sdk.metrics.export import ConsoleMetricsExporter from opentelemetry.sdk.util import get_dict_as_key +from opentelemetry.trace import SpanKind class TestMetricMixin(TestCase): From 8378400fe9b87cb3796c37e6c52130c752df6a83 Mon Sep 17 00:00:00 2001 From: Leighton Date: Tue, 15 Sep 2020 18:41:59 -0400 Subject: [PATCH 10/21] lint --- .../tests/test_requests_integration.py | 41 ++++++++----------- .../tests/test_metric.py | 4 +- 2 files changed, 20 insertions(+), 25 deletions(-) diff --git a/instrumentation/opentelemetry-instrumentation-requests/tests/test_requests_integration.py b/instrumentation/opentelemetry-instrumentation-requests/tests/test_requests_integration.py index e6b9fa9314..a910f3f4b6 100644 --- a/instrumentation/opentelemetry-instrumentation-requests/tests/test_requests_integration.py +++ b/instrumentation/opentelemetry-instrumentation-requests/tests/test_requests_integration.py @@ -22,6 +22,7 @@ from opentelemetry import context, propagators, trace from opentelemetry.instrumentation.requests import RequestsInstrumentor from opentelemetry.sdk import resources +from opentelemetry.sdk.util import get_dict_as_key from opentelemetry.test.mock_textmap import MockTextMapPropagator from opentelemetry.test.test_base import TestBase from opentelemetry.trace.status import StatusCanonicalCode @@ -91,15 +92,13 @@ def test_basic(self): self.assertIsNotNone(RequestsInstrumentor().meter) self.assertEqual(len(RequestsInstrumentor().meter.metrics), 1) recorder = RequestsInstrumentor().meter.metrics.pop() - match_key = tuple( - { - "http.flavor": 11, - "http.method": "GET", - "http.status_code": 200, - "http.status_text": "OK", - "http.url": "http://httpbin.org/status/200", - }.items() - ) + match_key = get_dict_as_key({ + "http.flavor": 11, + "http.method": "GET", + "http.status_code": 200, + "http.status_text": "OK", + "http.url": "http://httpbin.org/status/200", + }) for key in recorder.bound_instruments.keys(): self.assertEqual(key, match_key) # pylint: disable=protected-access @@ -270,12 +269,10 @@ def test_requests_exception_without_response(self, *_, **__): self.assertIsNotNone(RequestsInstrumentor().meter) self.assertEqual(len(RequestsInstrumentor().meter.metrics), 1) recorder = RequestsInstrumentor().meter.metrics.pop() - match_key = tuple( - { - "http.method": "GET", - "http.url": "http://httpbin.org/status/200", - }.items() - ) + match_key = get_dict_as_key({ + "http.method": "GET", + "http.url": "http://httpbin.org/status/200", + }) for key in recorder.bound_instruments.keys(): self.assertEqual(key, match_key) # pylint: disable=protected-access @@ -313,14 +310,12 @@ def test_requests_exception_with_response(self, *_, **__): self.assertIsNotNone(RequestsInstrumentor().meter) self.assertEqual(len(RequestsInstrumentor().meter.metrics), 1) recorder = RequestsInstrumentor().meter.metrics.pop() - match_key = tuple( - { - "http.method": "GET", - "http.status_code": 500, - "http.status_text": "Internal Server Error", - "http.url": "http://httpbin.org/status/200", - }.items() - ) + match_key = get_dict_as_key({ + "http.method": "GET", + "http.status_code": 500, + "http.status_text": "Internal Server Error", + "http.url": "http://httpbin.org/status/200", + }) for key in recorder.bound_instruments.keys(): self.assertEqual(key, match_key) # pylint: disable=protected-access diff --git a/opentelemetry-instrumentation/tests/test_metric.py b/opentelemetry-instrumentation/tests/test_metric.py index 826a63cda0..833866cdc8 100644 --- a/opentelemetry-instrumentation/tests/test_metric.py +++ b/opentelemetry-instrumentation/tests/test_metric.py @@ -14,7 +14,7 @@ # type: ignore import time -from unittest import TestCase, mock +from unittest import TestCase from opentelemetry import metrics as metrics_api from opentelemetry.instrumentation.metric import ( @@ -27,7 +27,7 @@ from opentelemetry.sdk.util import get_dict_as_key from opentelemetry.trace import SpanKind - +# pylint: disable=protected-access class TestMetricMixin(TestCase): @classmethod def setUpClass(cls): From 6db3175c2f163a305f3a3027ad29478acb2af0f5 Mon Sep 17 00:00:00 2001 From: Leighton Date: Tue, 15 Sep 2020 18:47:30 -0400 Subject: [PATCH 11/21] lint --- .../tests/test_requests_integration.py | 40 +++++++++++-------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/instrumentation/opentelemetry-instrumentation-requests/tests/test_requests_integration.py b/instrumentation/opentelemetry-instrumentation-requests/tests/test_requests_integration.py index a910f3f4b6..53b7da94f5 100644 --- a/instrumentation/opentelemetry-instrumentation-requests/tests/test_requests_integration.py +++ b/instrumentation/opentelemetry-instrumentation-requests/tests/test_requests_integration.py @@ -92,13 +92,15 @@ def test_basic(self): self.assertIsNotNone(RequestsInstrumentor().meter) self.assertEqual(len(RequestsInstrumentor().meter.metrics), 1) recorder = RequestsInstrumentor().meter.metrics.pop() - match_key = get_dict_as_key({ - "http.flavor": 11, - "http.method": "GET", - "http.status_code": 200, - "http.status_text": "OK", - "http.url": "http://httpbin.org/status/200", - }) + match_key = get_dict_as_key( + { + "http.flavor": 11, + "http.method": "GET", + "http.status_code": 200, + "http.status_text": "OK", + "http.url": "http://httpbin.org/status/200", + } + ) for key in recorder.bound_instruments.keys(): self.assertEqual(key, match_key) # pylint: disable=protected-access @@ -269,10 +271,12 @@ def test_requests_exception_without_response(self, *_, **__): self.assertIsNotNone(RequestsInstrumentor().meter) self.assertEqual(len(RequestsInstrumentor().meter.metrics), 1) recorder = RequestsInstrumentor().meter.metrics.pop() - match_key = get_dict_as_key({ - "http.method": "GET", - "http.url": "http://httpbin.org/status/200", - }) + match_key = get_dict_as_key( + { + "http.method": "GET", + "http.url": "http://httpbin.org/status/200", + } + ) for key in recorder.bound_instruments.keys(): self.assertEqual(key, match_key) # pylint: disable=protected-access @@ -310,12 +314,14 @@ def test_requests_exception_with_response(self, *_, **__): self.assertIsNotNone(RequestsInstrumentor().meter) self.assertEqual(len(RequestsInstrumentor().meter.metrics), 1) recorder = RequestsInstrumentor().meter.metrics.pop() - match_key = get_dict_as_key({ - "http.method": "GET", - "http.status_code": 500, - "http.status_text": "Internal Server Error", - "http.url": "http://httpbin.org/status/200", - }) + match_key = get_dict_as_key( + { + "http.method": "GET", + "http.status_code": 500, + "http.status_text": "Internal Server Error", + "http.url": "http://httpbin.org/status/200", + } + ) for key in recorder.bound_instruments.keys(): self.assertEqual(key, match_key) # pylint: disable=protected-access From 3157ef85b3920d7a6d27825c8640511e0cb02f19 Mon Sep 17 00:00:00 2001 From: Leighton Date: Tue, 15 Sep 2020 18:51:48 -0400 Subject: [PATCH 12/21] space --- opentelemetry-instrumentation/tests/test_metric.py | 1 + 1 file changed, 1 insertion(+) diff --git a/opentelemetry-instrumentation/tests/test_metric.py b/opentelemetry-instrumentation/tests/test_metric.py index 833866cdc8..6c93c26531 100644 --- a/opentelemetry-instrumentation/tests/test_metric.py +++ b/opentelemetry-instrumentation/tests/test_metric.py @@ -27,6 +27,7 @@ from opentelemetry.sdk.util import get_dict_as_key from opentelemetry.trace import SpanKind + # pylint: disable=protected-access class TestMetricMixin(TestCase): @classmethod From 3b653836a6a462ef2aacfc6925e6b48ee149d532 Mon Sep 17 00:00:00 2001 From: Leighton Date: Tue, 15 Sep 2020 19:00:05 -0400 Subject: [PATCH 13/21] lint --- .../src/opentelemetry/instrumentation/requests/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/__init__.py b/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/__init__.py index 7a188c373b..4452a14978 100644 --- a/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/__init__.py @@ -58,6 +58,7 @@ # pylint: disable=unused-argument +# pylint: disable=R0915 def _instrument(tracer_provider=None, span_callback=None): """Enables tracing of all requests calls that go through :code:`requests.session.Session.request` (this includes @@ -251,6 +252,7 @@ def _instrument(self, **kwargs): kwargs.get("metrics_exporter"), kwargs.get("metrics_interval"), ) + # pylint: disable=W0201 self.metric_recorder = HTTPMetricRecorder(self.meter, SpanKind.CLIENT) def _uninstrument(self, **kwargs): From ea883d3b441389e48b1061c5b13b69fe2d03a211 Mon Sep 17 00:00:00 2001 From: Leighton Date: Sat, 19 Sep 2020 20:51:03 -0400 Subject: [PATCH 14/21] comments --- .../basic_meter/{standard.py => http.py} | 13 ++++--- .../CHANGELOG.md | 2 +- .../instrumentation/requests/__init__.py | 9 ++--- .../tests/test_requests_integration.py | 6 ++-- opentelemetry-instrumentation/CHANGELOG.md | 2 +- .../opentelemetry/instrumentation/metric.py | 34 ++++++++++++------- .../tests/test_metric.py | 33 ++++++------------ 7 files changed, 48 insertions(+), 51 deletions(-) rename docs/examples/basic_meter/{standard.py => http.py} (80%) diff --git a/docs/examples/basic_meter/standard.py b/docs/examples/basic_meter/http.py similarity index 80% rename from docs/examples/basic_meter/standard.py rename to docs/examples/basic_meter/http.py index 8fc8b55d21..84665a56ac 100644 --- a/docs/examples/basic_meter/standard.py +++ b/docs/examples/basic_meter/http.py @@ -13,8 +13,8 @@ # limitations under the License. # """ -This module shows how you can enable collection and exporting of standard -metrics related to instrumentations. +This module shows how you can enable collection and exporting of http metrics +related to instrumentations. """ import requests @@ -29,9 +29,12 @@ # Exporter to export metrics to the console exporter = ConsoleMetricsExporter() -RequestsInstrumentor().instrument( - metrics_exporter=exporter, metrics_interval=5 -) +# Instrument the requests library +RequestsInstrumentor().instrument() + +# Indicate to start collecting and exporting requests related metrics +metrics.get_meter_provider().start_pipeline(RequestsInstrumentor().meter, exporter, 5) + response = requests.get("http://example.com") input("...\n") diff --git a/instrumentation/opentelemetry-instrumentation-requests/CHANGELOG.md b/instrumentation/opentelemetry-instrumentation-requests/CHANGELOG.md index a4eb1a0b35..4706f909fa 100644 --- a/instrumentation/opentelemetry-instrumentation-requests/CHANGELOG.md +++ b/instrumentation/opentelemetry-instrumentation-requests/CHANGELOG.md @@ -6,7 +6,7 @@ ([#1040](https://github.com/open-telemetry/opentelemetry-python/pull/1040)) - Drop support for Python 3.4 ([#1099](https://github.com/open-telemetry/opentelemetry-python/pull/1099)) -- Add support for standard metrics - duration +- Add support for http metrics ([#1116](https://github.com/open-telemetry/opentelemetry-python/pull/1116)) ## Version 0.12b0 diff --git a/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/__init__.py b/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/__init__.py index 4452a14978..060b80494b 100644 --- a/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/__init__.py @@ -173,10 +173,11 @@ def _instrumented_requests_call( ) ) ) - labels["http.status_code"] = result.status_code + labels["http.status_code"] = str(result.status_code) labels["http.status_text"] = result.reason if result.raw and result.raw.version: - labels["http.flavor"] = result.raw.version + labels["http.flavor"] = str(result.raw.version)[:1] \ + + "." + str(result.raw.version)[:-1] if span_callback is not None: span_callback(span, result) @@ -239,8 +240,6 @@ def _instrument(self, **kwargs): **kwargs: Optional arguments ``tracer_provider``: a TracerProvider, defaults to global ``span_callback``: An optional callback invoked before returning the http response. Invoked with Span and requests.Response - ``metrics_exporter``: the metric exporter to send requests related metrics to. - ``metrics_interval``: the export interval for the metric exporter """ _instrument( tracer_provider=kwargs.get("tracer_provider"), @@ -249,8 +248,6 @@ def _instrument(self, **kwargs): self.init_metrics( __name__, __version__, - kwargs.get("metrics_exporter"), - kwargs.get("metrics_interval"), ) # pylint: disable=W0201 self.metric_recorder = HTTPMetricRecorder(self.meter, SpanKind.CLIENT) diff --git a/instrumentation/opentelemetry-instrumentation-requests/tests/test_requests_integration.py b/instrumentation/opentelemetry-instrumentation-requests/tests/test_requests_integration.py index 53b7da94f5..96a141f974 100644 --- a/instrumentation/opentelemetry-instrumentation-requests/tests/test_requests_integration.py +++ b/instrumentation/opentelemetry-instrumentation-requests/tests/test_requests_integration.py @@ -94,9 +94,9 @@ def test_basic(self): recorder = RequestsInstrumentor().meter.metrics.pop() match_key = get_dict_as_key( { - "http.flavor": 11, + "http.flavor": "1.1", "http.method": "GET", - "http.status_code": 200, + "http.status_code": '200', "http.status_text": "OK", "http.url": "http://httpbin.org/status/200", } @@ -317,7 +317,7 @@ def test_requests_exception_with_response(self, *_, **__): match_key = get_dict_as_key( { "http.method": "GET", - "http.status_code": 500, + "http.status_code": '500', "http.status_text": "Internal Server Error", "http.url": "http://httpbin.org/status/200", } diff --git a/opentelemetry-instrumentation/CHANGELOG.md b/opentelemetry-instrumentation/CHANGELOG.md index a95d3d35a0..a2092111ba 100644 --- a/opentelemetry-instrumentation/CHANGELOG.md +++ b/opentelemetry-instrumentation/CHANGELOG.md @@ -4,7 +4,7 @@ - Drop support for Python 3.4 ([#1099](https://github.com/open-telemetry/opentelemetry-python/pull/1099)) -- Add support for standard metrics - duration +- Add support for http metrics ([#1116](https://github.com/open-telemetry/opentelemetry-python/pull/1116)) ## 0.9b0 diff --git a/opentelemetry-instrumentation/src/opentelemetry/instrumentation/metric.py b/opentelemetry-instrumentation/src/opentelemetry/instrumentation/metric.py index d5e2b6149b..088f9e0b2c 100644 --- a/opentelemetry-instrumentation/src/opentelemetry/instrumentation/metric.py +++ b/opentelemetry-instrumentation/src/opentelemetry/instrumentation/metric.py @@ -16,23 +16,27 @@ """ OpenTelemetry Instrumentation Metric mixin """ +import enum from contextlib import contextmanager from time import time +from typing import Dict, Optional from opentelemetry import metrics -from opentelemetry.sdk.metrics import PushController, ValueRecorder +from opentelemetry.sdk.metrics import ValueRecorder from opentelemetry.trace import SpanKind +class HTTPMetricType(enum.Enum): + CLIENT = 0 + SERVER = 1 + # TODO: Add both + + class MetricMixin: """Used to record metrics related to instrumentations.""" - def init_metrics(self, name, version, exporter=None, interval=None): + def init_metrics(self, name: str, version: str): self._meter = metrics.get_meter(name, version) - if exporter and interval: - self._controller = PushController( - meter=self._meter, exporter=exporter, interval=interval - ) @property def meter(self): @@ -42,29 +46,35 @@ def meter(self): class MetricRecorder: """Base class for metric recorders of different types.""" - def __init__(self, meter): + def __init__(self, meter: Optional[metrics.Meter] = None): self._meter = meter class HTTPMetricRecorder(MetricRecorder): """Metric recorder for http instrumentations. Tracks duration.""" - def __init__(self, meter, kind: SpanKind): + def __init__( + self, + meter: Optional[metrics.Meter], + type: HTTPMetricType, + ): super().__init__(meter) - self.kind = kind + self._type = type if self._meter: self._duration = self._meter.create_metric( - name="{}.{}.duration".format("http", self.kind.name.lower()), + name="{}.{}.duration".format("http", self._type.name.lower()), description="measures the duration of the {} HTTP request".format( - "inbound" if self.kind is SpanKind.SERVER else "outbound" + "inbound" if self._type is HTTPMetricType.SERVER else "outbound" ), unit="ms", value_type=float, metric_type=ValueRecorder, ) + # Conventions for recording duration can be found at: + # https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/metrics/semantic_conventions/http-metrics.md @contextmanager - def record_duration(self, labels): + def record_duration(self, labels: Dict[str, str]): start_time = time() try: yield start_time diff --git a/opentelemetry-instrumentation/tests/test_metric.py b/opentelemetry-instrumentation/tests/test_metric.py index 6c93c26531..b670bdfba4 100644 --- a/opentelemetry-instrumentation/tests/test_metric.py +++ b/opentelemetry-instrumentation/tests/test_metric.py @@ -14,18 +14,17 @@ # type: ignore import time -from unittest import TestCase +from unittest import mock, TestCase from opentelemetry import metrics as metrics_api from opentelemetry.instrumentation.metric import ( HTTPMetricRecorder, + HTTPMetricType, MetricMixin, ) from opentelemetry.metrics import set_meter_provider from opentelemetry.sdk import metrics -from opentelemetry.sdk.metrics.export import ConsoleMetricsExporter from opentelemetry.sdk.util import get_dict_as_key -from opentelemetry.trace import SpanKind # pylint: disable=protected-access @@ -47,19 +46,6 @@ def test_init(self): self.assertEqual(meter.instrumentation_info.name, "test") self.assertEqual(meter.instrumentation_info.version, 1.0) - def test_init_exporter(self): - mixin = MetricMixin() - exporter = ConsoleMetricsExporter() - mixin.init_metrics("test", 1.0, exporter, 100.0) - meter = mixin.meter - self.assertTrue(isinstance(meter, metrics.Meter)) - self.assertEqual(meter.instrumentation_info.name, "test") - self.assertEqual(meter.instrumentation_info.version, 1.0) - # pylint: disable=protected-access - self.assertEqual(mixin._controller.exporter, exporter) - self.assertEqual(mixin._controller.meter, meter) - mixin._controller.shutdown() - class TestHTTPMetricRecorder(TestCase): @classmethod @@ -73,9 +59,9 @@ def tearDownClass(cls): def test_ctor(self): meter = metrics_api.get_meter(__name__) - recorder = HTTPMetricRecorder(meter, SpanKind.CLIENT) - self.assertEqual(recorder.kind, SpanKind.CLIENT) + recorder = HTTPMetricRecorder(meter, HTTPMetricType.CLIENT) # pylint: disable=protected-access + self.assertEqual(recorder._type, HTTPMetricType.CLIENT) self.assertTrue(isinstance(recorder._duration, metrics.ValueRecorder)) self.assertEqual(recorder._duration.name, "http.client.duration") self.assertEqual( @@ -85,11 +71,12 @@ def test_ctor(self): def test_record_duration(self): meter = metrics_api.get_meter(__name__) - recorder = HTTPMetricRecorder(meter, SpanKind.CLIENT) + recorder = HTTPMetricRecorder(meter, HTTPMetricType.CLIENT) labels = {"test": "asd"} - with recorder.record_duration(labels): - labels["test2"] = "asd2" - time.sleep(1) + with mock.patch('time.time') as time_patch: + time_patch.return_value = 5.0 + with recorder.record_duration(labels): + labels["test2"] = "asd2" match_key = get_dict_as_key({"test": "asd", "test2": "asd2"}) for key in recorder._duration.bound_instruments.keys(): self.assertEqual(key, match_key) @@ -98,4 +85,4 @@ def test_record_duration(self): for view_data in bound.view_datas: self.assertEqual(view_data.labels, key) self.assertEqual(view_data.aggregator.current.count, 1) - self.assertGreater(view_data.aggregator.current.sum, 0) + self.assertGreaterEqual(view_data.aggregator.current.sum, 0) From b93342c26f260f1815e000d8d05544ef73b9477c Mon Sep 17 00:00:00 2001 From: Leighton Date: Sat, 19 Sep 2020 21:26:49 -0400 Subject: [PATCH 15/21] lint --- docs/examples/basic_meter/http.py | 4 +++- .../instrumentation/requests/__init__.py | 11 ++++++----- .../tests/test_requests_integration.py | 4 ++-- .../src/opentelemetry/instrumentation/metric.py | 8 ++++---- opentelemetry-instrumentation/tests/test_metric.py | 2 +- 5 files changed, 16 insertions(+), 13 deletions(-) diff --git a/docs/examples/basic_meter/http.py b/docs/examples/basic_meter/http.py index 84665a56ac..8fd6c6294c 100644 --- a/docs/examples/basic_meter/http.py +++ b/docs/examples/basic_meter/http.py @@ -33,7 +33,9 @@ RequestsInstrumentor().instrument() # Indicate to start collecting and exporting requests related metrics -metrics.get_meter_provider().start_pipeline(RequestsInstrumentor().meter, exporter, 5) +metrics.get_meter_provider().start_pipeline( + RequestsInstrumentor().meter, exporter, 5 +) response = requests.get("http://example.com") diff --git a/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/__init__.py b/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/__init__.py index 060b80494b..d0336184ed 100644 --- a/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/__init__.py @@ -176,9 +176,11 @@ def _instrumented_requests_call( labels["http.status_code"] = str(result.status_code) labels["http.status_text"] = result.reason if result.raw and result.raw.version: - labels["http.flavor"] = str(result.raw.version)[:1] \ - + "." + str(result.raw.version)[:-1] - + labels["http.flavor"] = ( + str(result.raw.version)[:1] + + "." + + str(result.raw.version)[:-1] + ) if span_callback is not None: span_callback(span, result) @@ -246,8 +248,7 @@ def _instrument(self, **kwargs): span_callback=kwargs.get("span_callback"), ) self.init_metrics( - __name__, - __version__, + __name__, __version__, ) # pylint: disable=W0201 self.metric_recorder = HTTPMetricRecorder(self.meter, SpanKind.CLIENT) diff --git a/instrumentation/opentelemetry-instrumentation-requests/tests/test_requests_integration.py b/instrumentation/opentelemetry-instrumentation-requests/tests/test_requests_integration.py index 96a141f974..2d3636284b 100644 --- a/instrumentation/opentelemetry-instrumentation-requests/tests/test_requests_integration.py +++ b/instrumentation/opentelemetry-instrumentation-requests/tests/test_requests_integration.py @@ -96,7 +96,7 @@ def test_basic(self): { "http.flavor": "1.1", "http.method": "GET", - "http.status_code": '200', + "http.status_code": "200", "http.status_text": "OK", "http.url": "http://httpbin.org/status/200", } @@ -317,7 +317,7 @@ def test_requests_exception_with_response(self, *_, **__): match_key = get_dict_as_key( { "http.method": "GET", - "http.status_code": '500', + "http.status_code": "500", "http.status_text": "Internal Server Error", "http.url": "http://httpbin.org/status/200", } diff --git a/opentelemetry-instrumentation/src/opentelemetry/instrumentation/metric.py b/opentelemetry-instrumentation/src/opentelemetry/instrumentation/metric.py index 088f9e0b2c..6acdbfc898 100644 --- a/opentelemetry-instrumentation/src/opentelemetry/instrumentation/metric.py +++ b/opentelemetry-instrumentation/src/opentelemetry/instrumentation/metric.py @@ -54,9 +54,7 @@ class HTTPMetricRecorder(MetricRecorder): """Metric recorder for http instrumentations. Tracks duration.""" def __init__( - self, - meter: Optional[metrics.Meter], - type: HTTPMetricType, + self, meter: Optional[metrics.Meter], type: HTTPMetricType, ): super().__init__(meter) self._type = type @@ -64,7 +62,9 @@ def __init__( self._duration = self._meter.create_metric( name="{}.{}.duration".format("http", self._type.name.lower()), description="measures the duration of the {} HTTP request".format( - "inbound" if self._type is HTTPMetricType.SERVER else "outbound" + "inbound" + if self._type is HTTPMetricType.SERVER + else "outbound" ), unit="ms", value_type=float, diff --git a/opentelemetry-instrumentation/tests/test_metric.py b/opentelemetry-instrumentation/tests/test_metric.py index b670bdfba4..b75a460b24 100644 --- a/opentelemetry-instrumentation/tests/test_metric.py +++ b/opentelemetry-instrumentation/tests/test_metric.py @@ -73,7 +73,7 @@ def test_record_duration(self): meter = metrics_api.get_meter(__name__) recorder = HTTPMetricRecorder(meter, HTTPMetricType.CLIENT) labels = {"test": "asd"} - with mock.patch('time.time') as time_patch: + with mock.patch("time.time") as time_patch: time_patch.return_value = 5.0 with recorder.record_duration(labels): labels["test2"] = "asd2" From ff4c491db7f9274fda98b4c9bc4402620f1d6230 Mon Sep 17 00:00:00 2001 From: Leighton Date: Sat, 19 Sep 2020 21:38:51 -0400 Subject: [PATCH 16/21] lint --- opentelemetry-instrumentation/tests/test_metric.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opentelemetry-instrumentation/tests/test_metric.py b/opentelemetry-instrumentation/tests/test_metric.py index b75a460b24..9bf47c9b70 100644 --- a/opentelemetry-instrumentation/tests/test_metric.py +++ b/opentelemetry-instrumentation/tests/test_metric.py @@ -14,7 +14,7 @@ # type: ignore import time -from unittest import mock, TestCase +from unittest import TestCase, mock from opentelemetry import metrics as metrics_api from opentelemetry.instrumentation.metric import ( From 195d876af3200ca5a3082906ae275b316eced5c5 Mon Sep 17 00:00:00 2001 From: Leighton Date: Sat, 19 Sep 2020 22:26:44 -0400 Subject: [PATCH 17/21] lint --- opentelemetry-instrumentation/tests/test_metric.py | 1 - 1 file changed, 1 deletion(-) diff --git a/opentelemetry-instrumentation/tests/test_metric.py b/opentelemetry-instrumentation/tests/test_metric.py index 9bf47c9b70..e77bd9c51d 100644 --- a/opentelemetry-instrumentation/tests/test_metric.py +++ b/opentelemetry-instrumentation/tests/test_metric.py @@ -13,7 +13,6 @@ # limitations under the License. # type: ignore -import time from unittest import TestCase, mock from opentelemetry import metrics as metrics_api From 06b7fdedf5b6ba397288b915d74cc3c4e4f5ed49 Mon Sep 17 00:00:00 2001 From: Leighton Date: Sat, 19 Sep 2020 22:59:26 -0400 Subject: [PATCH 18/21] Update metric.py --- .../src/opentelemetry/instrumentation/metric.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/opentelemetry-instrumentation/src/opentelemetry/instrumentation/metric.py b/opentelemetry-instrumentation/src/opentelemetry/instrumentation/metric.py index 6acdbfc898..c6822b429f 100644 --- a/opentelemetry-instrumentation/src/opentelemetry/instrumentation/metric.py +++ b/opentelemetry-instrumentation/src/opentelemetry/instrumentation/metric.py @@ -23,7 +23,6 @@ from opentelemetry import metrics from opentelemetry.sdk.metrics import ValueRecorder -from opentelemetry.trace import SpanKind class HTTPMetricType(enum.Enum): @@ -54,16 +53,16 @@ class HTTPMetricRecorder(MetricRecorder): """Metric recorder for http instrumentations. Tracks duration.""" def __init__( - self, meter: Optional[metrics.Meter], type: HTTPMetricType, + self, meter: Optional[metrics.Meter], http_type: HTTPMetricType, ): super().__init__(meter) - self._type = type + self._http_type = http_type if self._meter: self._duration = self._meter.create_metric( - name="{}.{}.duration".format("http", self._type.name.lower()), + name="{}.{}.duration".format("http", self._http_type.name.lower()), description="measures the duration of the {} HTTP request".format( "inbound" - if self._type is HTTPMetricType.SERVER + if self._http_type is HTTPMetricType.SERVER else "outbound" ), unit="ms", From d24043ab95e967e1ebc48d6fc65ea2b7f92af78a Mon Sep 17 00:00:00 2001 From: Leighton Date: Sat, 19 Sep 2020 23:04:30 -0400 Subject: [PATCH 19/21] lint --- .../src/opentelemetry/instrumentation/metric.py | 4 +++- opentelemetry-instrumentation/tests/test_metric.py | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/opentelemetry-instrumentation/src/opentelemetry/instrumentation/metric.py b/opentelemetry-instrumentation/src/opentelemetry/instrumentation/metric.py index c6822b429f..74445cbff1 100644 --- a/opentelemetry-instrumentation/src/opentelemetry/instrumentation/metric.py +++ b/opentelemetry-instrumentation/src/opentelemetry/instrumentation/metric.py @@ -59,7 +59,9 @@ def __init__( self._http_type = http_type if self._meter: self._duration = self._meter.create_metric( - name="{}.{}.duration".format("http", self._http_type.name.lower()), + name="{}.{}.duration".format( + "http", self._http_type.name.lower() + ), description="measures the duration of the {} HTTP request".format( "inbound" if self._http_type is HTTPMetricType.SERVER diff --git a/opentelemetry-instrumentation/tests/test_metric.py b/opentelemetry-instrumentation/tests/test_metric.py index e77bd9c51d..ea8724fc88 100644 --- a/opentelemetry-instrumentation/tests/test_metric.py +++ b/opentelemetry-instrumentation/tests/test_metric.py @@ -60,7 +60,7 @@ def test_ctor(self): meter = metrics_api.get_meter(__name__) recorder = HTTPMetricRecorder(meter, HTTPMetricType.CLIENT) # pylint: disable=protected-access - self.assertEqual(recorder._type, HTTPMetricType.CLIENT) + self.assertEqual(recorder._http_type, HTTPMetricType.CLIENT) self.assertTrue(isinstance(recorder._duration, metrics.ValueRecorder)) self.assertEqual(recorder._duration.name, "http.client.duration") self.assertEqual( From b21da8495b96d98528e1505dff9ecdd9b39657e9 Mon Sep 17 00:00:00 2001 From: Leighton Date: Fri, 25 Sep 2020 10:16:49 -0400 Subject: [PATCH 20/21] Update __init__.py --- .../src/opentelemetry/instrumentation/requests/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/__init__.py b/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/__init__.py index d0336184ed..4271b3cad0 100644 --- a/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/__init__.py @@ -129,6 +129,7 @@ def _instrumented_requests_call( labels["http.method"] = method labels["http.url"] = url + with get_tracer( __name__, __version__, tracer_provider ).start_as_current_span(span_name, kind=SpanKind.CLIENT) as span: From 6076bab3ede0aa3526db979e9a49fb1d033cb48c Mon Sep 17 00:00:00 2001 From: Leighton Date: Fri, 25 Sep 2020 10:17:11 -0400 Subject: [PATCH 21/21] Update __init__.py --- .../src/opentelemetry/instrumentation/requests/__init__.py | 1 - 1 file changed, 1 deletion(-) diff --git a/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/__init__.py b/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/__init__.py index 4271b3cad0..d0336184ed 100644 --- a/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/__init__.py @@ -129,7 +129,6 @@ def _instrumented_requests_call( labels["http.method"] = method labels["http.url"] = url - with get_tracer( __name__, __version__, tracer_provider ).start_as_current_span(span_name, kind=SpanKind.CLIENT) as span: