Skip to content

Commit

Permalink
Restoring metrics in requests (#1110)
Browse files Browse the repository at this point in the history
  • Loading branch information
ashu658 committed Jun 25, 2022
1 parent 0007c90 commit f7fd1e0
Show file tree
Hide file tree
Showing 5 changed files with 110 additions and 4 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
([#1127](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1127))
- Add metric instrumentation for WSGI
([#1128](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1128))
- `opentelemetry-instrumentation-requests` Restoring metrics in requests
([#1110](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1110)


## [1.12.0rc1-0.31b0](https://github.com/open-telemetry/opentelemetry-python/releases/tag/v1.12.0rc1-0.31b0) - 2022-05-17
Expand Down
2 changes: 1 addition & 1 deletion instrumentation/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
| [opentelemetry-instrumentation-pyramid](./opentelemetry-instrumentation-pyramid) | pyramid >= 1.7 | No
| [opentelemetry-instrumentation-redis](./opentelemetry-instrumentation-redis) | redis >= 2.6 | No
| [opentelemetry-instrumentation-remoulade](./opentelemetry-instrumentation-remoulade) | remoulade >= 0.50 | No
| [opentelemetry-instrumentation-requests](./opentelemetry-instrumentation-requests) | requests ~= 2.0 | No
| [opentelemetry-instrumentation-requests](./opentelemetry-instrumentation-requests) | requests ~= 2.0 | Yes
| [opentelemetry-instrumentation-sklearn](./opentelemetry-instrumentation-sklearn) | scikit-learn ~= 0.24.0 | No
| [opentelemetry-instrumentation-sqlalchemy](./opentelemetry-instrumentation-sqlalchemy) | sqlalchemy | No
| [opentelemetry-instrumentation-sqlite3](./opentelemetry-instrumentation-sqlite3) | sqlite3 | No
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,9 @@

import functools
import types
from typing import Collection
from timeit import default_timer
from typing import Callable, Collection, Iterable, Optional
from urllib.parse import urlparse

from requests.models import Response
from requests.sessions import Session
Expand All @@ -67,9 +69,11 @@
_SUPPRESS_INSTRUMENTATION_KEY,
http_status_to_status_code,
)
from opentelemetry.metrics import Histogram, get_meter
from opentelemetry.propagate import inject
from opentelemetry.semconv.trace import SpanAttributes
from opentelemetry.trace import SpanKind, get_tracer
from opentelemetry.trace import SpanKind, Tracer, get_tracer
from opentelemetry.trace.span import Span
from opentelemetry.trace.status import Status
from opentelemetry.util.http import (
get_excluded_urls,
Expand All @@ -84,7 +88,11 @@
# pylint: disable=unused-argument
# pylint: disable=R0915
def _instrument(
tracer, span_callback=None, name_callback=None, excluded_urls=None
tracer: Tracer,
duration_histogram: Histogram,
span_callback: Optional[Callable[[Span, Response], str]] = None,
name_callback: Optional[Callable[[str, str], str]] = None,
excluded_urls: Iterable[str] = None,
):
"""Enables tracing of all requests calls that go through
:code:`requests.session.Session.request` (this includes
Expand Down Expand Up @@ -140,6 +148,7 @@ def call_wrapped():
request.method, request.url, call_wrapped, get_or_create_headers
)

# pylint: disable-msg=too-many-locals,too-many-branches
def _instrumented_requests_call(
method: str, url: str, call_wrapped, get_or_create_headers
):
Expand All @@ -164,6 +173,23 @@ def _instrumented_requests_call(
SpanAttributes.HTTP_URL: url,
}

metric_labels = {
SpanAttributes.HTTP_METHOD: method,
}

try:
parsed_url = urlparse(url)
metric_labels[SpanAttributes.HTTP_SCHEME] = parsed_url.scheme
if parsed_url.hostname:
metric_labels[SpanAttributes.HTTP_HOST] = parsed_url.hostname
metric_labels[
SpanAttributes.NET_PEER_NAME
] = parsed_url.hostname
if parsed_url.port:
metric_labels[SpanAttributes.NET_PEER_PORT] = parsed_url.port
except ValueError:
pass

with tracer.start_as_current_span(
span_name, kind=SpanKind.CLIENT, attributes=span_attributes
) as span, set_ip_on_next_http_connection(span):
Expand All @@ -175,12 +201,18 @@ def _instrumented_requests_call(
token = context.attach(
context.set_value(_SUPPRESS_HTTP_INSTRUMENTATION_KEY, True)
)

start_time = default_timer()

try:
result = call_wrapped() # *** PROCEED
except Exception as exc: # pylint: disable=W0703
exception = exc
result = getattr(exc, "response", None)
finally:
elapsed_time = max(
round((default_timer() - start_time) * 1000), 0
)
context.detach(token)

if isinstance(result, Response):
Expand All @@ -191,9 +223,23 @@ def _instrumented_requests_call(
span.set_status(
Status(http_status_to_status_code(result.status_code))
)

metric_labels[
SpanAttributes.HTTP_STATUS_CODE
] = result.status_code

if result.raw is not None:
version = getattr(result.raw, "version", None)
if version:
metric_labels[SpanAttributes.HTTP_FLAVOR] = (
"1.1" if version == 11 else "1.0"
)

if span_callback is not None:
span_callback(span, result)

duration_histogram.record(elapsed_time, attributes=metric_labels)

if exception is not None:
raise exception.with_traceback(exception.__traceback__)

Expand Down Expand Up @@ -258,8 +304,20 @@ def _instrument(self, **kwargs):
tracer_provider = kwargs.get("tracer_provider")
tracer = get_tracer(__name__, __version__, tracer_provider)
excluded_urls = kwargs.get("excluded_urls")
meter_provider = kwargs.get("meter_provider")
meter = get_meter(
__name__,
__version__,
meter_provider,
)
duration_histogram = meter.create_histogram(
name="http.client.duration",
unit="ms",
description="measures the duration of the outbound HTTP request",
)
_instrument(
tracer,
duration_histogram,
span_callback=kwargs.get("span_callback"),
name_callback=kwargs.get("name_callback"),
excluded_urls=_excluded_urls_from_env
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,5 @@


_instruments = ("requests ~= 2.0",)

_supports_metrics = True
Original file line number Diff line number Diff line change
Expand Up @@ -487,3 +487,47 @@ def perform_request(url: str, session: requests.Session = None):
request = requests.Request("GET", url)
prepared_request = session.prepare_request(request)
return session.send(prepared_request)


class TestRequestsIntergrationMetric(TestBase):
URL = "http://examplehost:8000/status/200"

def setUp(self):
super().setUp()
RequestsInstrumentor().instrument(meter_provider=self.meter_provider)

httpretty.enable()
httpretty.register_uri(httpretty.GET, self.URL, body="Hello!")

def tearDown(self):
super().tearDown()
RequestsInstrumentor().uninstrument()
httpretty.disable()

@staticmethod
def perform_request(url: str) -> requests.Response:
return requests.get(url)

def test_basic_metric_success(self):
self.perform_request(self.URL)

expected_attributes = {
"http.status_code": 200,
"http.host": "examplehost",
"net.peer.port": 8000,
"net.peer.name": "examplehost",
"http.method": "GET",
"http.flavor": "1.1",
"http.scheme": "http",
}

for (
resource_metrics
) in self.memory_metrics_reader.get_metrics_data().resource_metrics:
for scope_metrics in resource_metrics.scope_metrics:
for metric in scope_metrics.metrics:
for data_point in metric.data.data_points:
self.assertDictEqual(
expected_attributes, dict(data_point.attributes)
)
self.assertEqual(data_point.count, 1)

0 comments on commit f7fd1e0

Please sign in to comment.