Skip to content

Commit 9bc7764

Browse files
emdnetoxrmx
andauthored
requests: support explicit_bucket_boundaries_advisory in duration metrics (#3464)
* requests: add explicit_bucket_boundaries_advisory support for duration metrics Signed-off-by: emdneto <[email protected]> * requests duration histogram stable semconv Signed-off-by: emdneto <[email protected]> * fix examples Signed-off-by: emdneto <[email protected]> * add changelog Signed-off-by: emdneto <[email protected]> * rename buckets variable Signed-off-by: emdneto <[email protected]> --------- Signed-off-by: emdneto <[email protected]> Co-authored-by: Riccardo Magliocchetti <[email protected]>
1 parent abafc21 commit 9bc7764

File tree

4 files changed

+110
-2
lines changed

4 files changed

+110
-2
lines changed

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1313

1414
### Added
1515

16+
- `opentelemetry-instrumentation-requests` Support explicit_bucket_boundaries_advisory in duration metrics
17+
([#3464](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3464))
1618
- `opentelemetry-instrumentation-redis` Add support for redis client-specific instrumentation.
1719
([#3143](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3143))
1820

instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/__init__.py

+27-2
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,22 @@ def response_hook(span, request_obj, response):
5757
request_hook=request_hook, response_hook=response_hook
5858
)
5959
60+
Custom Duration Histogram Boundaries
61+
************************************
62+
To customize the duration histogram bucket boundaries used for HTTP client request duration metrics,
63+
you can provide a list of values when instrumenting:
64+
65+
.. code:: python
66+
67+
import requests
68+
from opentelemetry.instrumentation.requests import RequestsInstrumentor
69+
70+
custom_boundaries = [0.0, 5.0, 10.0, 25.0, 50.0, 100.0]
71+
72+
RequestsInstrumentor().instrument(
73+
duration_histogram_boundaries=custom_boundaries
74+
)
75+
6076
Exclude lists
6177
*************
6278
To exclude certain URLs from being tracked, set the environment variable ``OTEL_PYTHON_REQUESTS_EXCLUDED_URLS``
@@ -87,6 +103,8 @@ def response_hook(span, request_obj, response):
87103
from requests.structures import CaseInsensitiveDict
88104

89105
from opentelemetry.instrumentation._semconv import (
106+
HTTP_DURATION_HISTOGRAM_BUCKETS_NEW,
107+
HTTP_DURATION_HISTOGRAM_BUCKETS_OLD,
90108
_client_duration_attrs_new,
91109
_client_duration_attrs_old,
92110
_filter_semconv_duration_attrs,
@@ -410,8 +428,8 @@ def _instrument(self, **kwargs: Any):
410428
``tracer_provider``: a TracerProvider, defaults to global
411429
``request_hook``: An optional callback that is invoked right after a span is created.
412430
``response_hook``: An optional callback which is invoked right before the span is finished processing a response.
413-
``excluded_urls``: A string containing a comma-delimited
414-
list of regexes used to exclude URLs from tracking
431+
``excluded_urls``: A string containing a comma-delimited list of regexes used to exclude URLs from tracking
432+
``duration_histogram_boundaries``: A list of float values representing the explicit bucket boundaries for the duration histogram.
415433
"""
416434
semconv_opt_in_mode = _OpenTelemetrySemanticConventionStability._get_opentelemetry_stability_opt_in_mode(
417435
_OpenTelemetryStabilitySignalType.HTTP,
@@ -426,6 +444,9 @@ def _instrument(self, **kwargs: Any):
426444
)
427445
excluded_urls = kwargs.get("excluded_urls")
428446
meter_provider = kwargs.get("meter_provider")
447+
duration_histogram_boundaries = kwargs.get(
448+
"duration_histogram_boundaries"
449+
)
429450
meter = get_meter(
430451
__name__,
431452
__version__,
@@ -438,13 +459,17 @@ def _instrument(self, **kwargs: Any):
438459
name=MetricInstruments.HTTP_CLIENT_DURATION,
439460
unit="ms",
440461
description="measures the duration of the outbound HTTP request",
462+
explicit_bucket_boundaries_advisory=duration_histogram_boundaries
463+
or HTTP_DURATION_HISTOGRAM_BUCKETS_OLD,
441464
)
442465
duration_histogram_new = None
443466
if _report_new(semconv_opt_in_mode):
444467
duration_histogram_new = meter.create_histogram(
445468
name=HTTP_CLIENT_REQUEST_DURATION,
446469
unit="s",
447470
description="Duration of HTTP client requests.",
471+
explicit_bucket_boundaries_advisory=duration_histogram_boundaries
472+
or HTTP_DURATION_HISTOGRAM_BUCKETS_NEW,
448473
)
449474
_instrument(
450475
tracer,

instrumentation/opentelemetry-instrumentation-requests/tests/test_requests_integration.py

+44
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
import opentelemetry.instrumentation.requests
2424
from opentelemetry import trace
2525
from opentelemetry.instrumentation._semconv import (
26+
HTTP_DURATION_HISTOGRAM_BUCKETS_NEW,
27+
HTTP_DURATION_HISTOGRAM_BUCKETS_OLD,
2628
OTEL_SEMCONV_STABILITY_OPT_IN,
2729
_OpenTelemetrySemanticConventionStability,
2830
)
@@ -123,6 +125,7 @@ def setUp(self):
123125
def tearDown(self):
124126
super().tearDown()
125127
self.env_patch.stop()
128+
_OpenTelemetrySemanticConventionStability._initialized = False
126129
RequestsInstrumentor().uninstrument()
127130
httpretty.disable()
128131

@@ -730,6 +733,7 @@ def setUp(self):
730733
def tearDown(self):
731734
super().tearDown()
732735
self.env_patch.stop()
736+
_OpenTelemetrySemanticConventionStability._initialized = False
733737
RequestsInstrumentor().uninstrument()
734738
httpretty.disable()
735739

@@ -762,6 +766,10 @@ def test_basic_metric_success(self):
762766
"measures the duration of the outbound HTTP request",
763767
)
764768
for data_point in metric.data.data_points:
769+
self.assertEqual(
770+
data_point.explicit_bounds,
771+
HTTP_DURATION_HISTOGRAM_BUCKETS_OLD,
772+
)
765773
self.assertDictEqual(
766774
expected_attributes, dict(data_point.attributes)
767775
)
@@ -788,6 +796,10 @@ def test_basic_metric_new_semconv(self):
788796
metric.description, "Duration of HTTP client requests."
789797
)
790798
for data_point in metric.data.data_points:
799+
self.assertEqual(
800+
data_point.explicit_bounds,
801+
HTTP_DURATION_HISTOGRAM_BUCKETS_NEW,
802+
)
791803
self.assertDictEqual(
792804
expected_attributes, dict(data_point.attributes)
793805
)
@@ -833,6 +845,38 @@ def test_basic_metric_both_semconv(self):
833845
)
834846
self.assertEqual(data_point.count, 1)
835847

848+
def test_custom_histogram_boundaries(self):
849+
RequestsInstrumentor().uninstrument()
850+
custom_boundaries = (0.0, 1.0, 2.0, 5.0, 10.0, 20.0, 50.0, 100.0)
851+
meter_provider, memory_reader = self.create_meter_provider()
852+
RequestsInstrumentor().instrument(
853+
meter_provider=meter_provider,
854+
duration_histogram_boundaries=custom_boundaries,
855+
)
856+
857+
self.perform_request(self.URL)
858+
metrics = memory_reader.get_metrics_data().resource_metrics[0]
859+
self.assertEqual(len(metrics.scope_metrics), 1)
860+
data_point = metrics.scope_metrics[0].metrics[0].data.data_points[0]
861+
self.assertEqual(data_point.explicit_bounds, custom_boundaries)
862+
self.assertEqual(data_point.count, 1)
863+
864+
def test_custom_histogram_boundaries_new_semconv(self):
865+
RequestsInstrumentor().uninstrument()
866+
custom_boundaries = (0.0, 5.0, 10.0, 25.0, 50.0, 100.0)
867+
meter_provider, memory_reader = self.create_meter_provider()
868+
RequestsInstrumentor().instrument(
869+
meter_provider=meter_provider,
870+
duration_histogram_boundaries=custom_boundaries,
871+
)
872+
873+
self.perform_request(self.URL)
874+
metrics = memory_reader.get_metrics_data().resource_metrics[0]
875+
self.assertEqual(len(metrics.scope_metrics), 1)
876+
data_point = metrics.scope_metrics[0].metrics[0].data.data_points[0]
877+
self.assertEqual(data_point.explicit_bounds, custom_boundaries)
878+
self.assertEqual(data_point.count, 1)
879+
836880
def test_basic_metric_non_recording_span(self):
837881
expected_attributes = {
838882
SpanAttributes.HTTP_STATUS_CODE: 200,

opentelemetry-instrumentation/src/opentelemetry/instrumentation/_semconv.py

+37
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,43 @@
4747
from opentelemetry.semconv.trace import SpanAttributes
4848
from opentelemetry.trace.status import Status, StatusCode
4949

50+
# Values defined in milliseconds
51+
HTTP_DURATION_HISTOGRAM_BUCKETS_OLD = (
52+
0.0,
53+
5.0,
54+
10.0,
55+
25.0,
56+
50.0,
57+
75.0,
58+
100.0,
59+
250.0,
60+
500.0,
61+
750.0,
62+
1000.0,
63+
2500.0,
64+
5000.0,
65+
7500.0,
66+
10000.0,
67+
)
68+
69+
# Values defined in seconds
70+
HTTP_DURATION_HISTOGRAM_BUCKETS_NEW = (
71+
0.005,
72+
0.01,
73+
0.025,
74+
0.05,
75+
0.075,
76+
0.1,
77+
0.25,
78+
0.5,
79+
0.75,
80+
1,
81+
2.5,
82+
5,
83+
7.5,
84+
10,
85+
)
86+
5087
# These lists represent attributes for metrics that are currently supported
5188

5289
_client_duration_attrs_old = [

0 commit comments

Comments
 (0)