Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,22 @@ def response_hook(span, request_obj, response):
request_hook=request_hook, response_hook=response_hook
)

Custom Duration Histogram Boundaries
************************************
To customize the duration histogram bucket boundaries used for HTTP client request duration metrics,
you can provide a list of values when instrumenting:

.. code:: python

import requests
from opentelemetry.instrumentation.requests import RequestsInstrumentor

custom_boundaries = [0.0, 5.0, 10.0, 25.0, 50.0, 100.0]

RequestsInstrumentor().instrument(
duration_histogram_boundaries=custom_boundaries
)

Exclude lists
*************
To exclude certain URLs from being tracked, set the environment variable ``OTEL_PYTHON_REQUESTS_EXCLUDED_URLS``
Expand Down Expand Up @@ -87,6 +103,8 @@ def response_hook(span, request_obj, response):
from requests.structures import CaseInsensitiveDict

from opentelemetry.instrumentation._semconv import (
HTTP_DURATION_HISTOGRAM_BUCKETS_NEW,
HTTP_DURATION_HISTOGRAM_BUCKETS_OLD,
_client_duration_attrs_new,
_client_duration_attrs_old,
_filter_semconv_duration_attrs,
Expand Down Expand Up @@ -410,8 +428,8 @@ def _instrument(self, **kwargs: Any):
``tracer_provider``: a TracerProvider, defaults to global
``request_hook``: An optional callback that is invoked right after a span is created.
``response_hook``: An optional callback which is invoked right before the span is finished processing a response.
``excluded_urls``: A string containing a comma-delimited
list of regexes used to exclude URLs from tracking
``excluded_urls``: A string containing a comma-delimited list of regexes used to exclude URLs from tracking
``duration_histogram_boundaries``: A list of float values representing the explicit bucket boundaries for the duration histogram.
"""
semconv_opt_in_mode = _OpenTelemetrySemanticConventionStability._get_opentelemetry_stability_opt_in_mode(
_OpenTelemetryStabilitySignalType.HTTP,
Expand All @@ -426,6 +444,9 @@ def _instrument(self, **kwargs: Any):
)
excluded_urls = kwargs.get("excluded_urls")
meter_provider = kwargs.get("meter_provider")
duration_histogram_boundaries = kwargs.get(
"duration_histogram_boundaries"
)
meter = get_meter(
__name__,
__version__,
Expand All @@ -438,13 +459,17 @@ def _instrument(self, **kwargs: Any):
name=MetricInstruments.HTTP_CLIENT_DURATION,
unit="ms",
description="measures the duration of the outbound HTTP request",
explicit_bucket_boundaries_advisory=duration_histogram_boundaries
or HTTP_DURATION_HISTOGRAM_BUCKETS_OLD,
)
duration_histogram_new = None
if _report_new(semconv_opt_in_mode):
duration_histogram_new = meter.create_histogram(
name=HTTP_CLIENT_REQUEST_DURATION,
unit="s",
description="Duration of HTTP client requests.",
explicit_bucket_boundaries_advisory=duration_histogram_boundaries
or HTTP_DURATION_HISTOGRAM_BUCKETS_NEW,
)
_instrument(
tracer,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
import opentelemetry.instrumentation.requests
from opentelemetry import trace
from opentelemetry.instrumentation._semconv import (
HTTP_DURATION_HISTOGRAM_BUCKETS_NEW,
HTTP_DURATION_HISTOGRAM_BUCKETS_OLD,
OTEL_SEMCONV_STABILITY_OPT_IN,
_OpenTelemetrySemanticConventionStability,
)
Expand Down Expand Up @@ -123,6 +125,7 @@ def setUp(self):
def tearDown(self):
super().tearDown()
self.env_patch.stop()
_OpenTelemetrySemanticConventionStability._initialized = False
RequestsInstrumentor().uninstrument()
httpretty.disable()

Expand Down Expand Up @@ -730,6 +733,7 @@ def setUp(self):
def tearDown(self):
super().tearDown()
self.env_patch.stop()
_OpenTelemetrySemanticConventionStability._initialized = False
RequestsInstrumentor().uninstrument()
httpretty.disable()

Expand Down Expand Up @@ -762,6 +766,10 @@ def test_basic_metric_success(self):
"measures the duration of the outbound HTTP request",
)
for data_point in metric.data.data_points:
self.assertEqual(
data_point.explicit_bounds,
HTTP_DURATION_HISTOGRAM_BUCKETS_OLD,
)
self.assertDictEqual(
expected_attributes, dict(data_point.attributes)
)
Expand All @@ -788,6 +796,10 @@ def test_basic_metric_new_semconv(self):
metric.description, "Duration of HTTP client requests."
)
for data_point in metric.data.data_points:
self.assertEqual(
data_point.explicit_bounds,
HTTP_DURATION_HISTOGRAM_BUCKETS_NEW,
)
self.assertDictEqual(
expected_attributes, dict(data_point.attributes)
)
Expand Down Expand Up @@ -833,6 +845,38 @@ def test_basic_metric_both_semconv(self):
)
self.assertEqual(data_point.count, 1)

def test_custom_histogram_boundaries(self):
RequestsInstrumentor().uninstrument()
custom_boundaries = (0.0, 1.0, 2.0, 5.0, 10.0, 20.0, 50.0, 100.0)
meter_provider, memory_reader = self.create_meter_provider()
RequestsInstrumentor().instrument(
meter_provider=meter_provider,
duration_histogram_boundaries=custom_boundaries,
)

self.perform_request(self.URL)
metrics = memory_reader.get_metrics_data().resource_metrics[0]
self.assertEqual(len(metrics.scope_metrics), 1)
data_point = metrics.scope_metrics[0].metrics[0].data.data_points[0]
self.assertEqual(data_point.explicit_bounds, custom_boundaries)
self.assertEqual(data_point.count, 1)

def test_custom_histogram_boundaries_new_semconv(self):
RequestsInstrumentor().uninstrument()
custom_boundaries = (0.0, 5.0, 10.0, 25.0, 50.0, 100.0)
meter_provider, memory_reader = self.create_meter_provider()
RequestsInstrumentor().instrument(
meter_provider=meter_provider,
duration_histogram_boundaries=custom_boundaries,
)

self.perform_request(self.URL)
metrics = memory_reader.get_metrics_data().resource_metrics[0]
self.assertEqual(len(metrics.scope_metrics), 1)
data_point = metrics.scope_metrics[0].metrics[0].data.data_points[0]
self.assertEqual(data_point.explicit_bounds, custom_boundaries)
self.assertEqual(data_point.count, 1)

def test_basic_metric_non_recording_span(self):
expected_attributes = {
SpanAttributes.HTTP_STATUS_CODE: 200,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,43 @@
from opentelemetry.semconv.trace import SpanAttributes
from opentelemetry.trace.status import Status, StatusCode

# Values defined in milliseconds
HTTP_DURATION_HISTOGRAM_BUCKETS_OLD = (
0.0,
5.0,
10.0,
25.0,
50.0,
75.0,
100.0,
250.0,
500.0,
750.0,
1000.0,
2500.0,
5000.0,
7500.0,
10000.0,
)

# Values defined in seconds
HTTP_DURATION_HISTOGRAM_BUCKETS_NEW = (
0.005,
0.01,
0.025,
0.05,
0.075,
0.1,
0.25,
0.5,
0.75,
1,
2.5,
5,
7.5,
10,
)

# These lists represent attributes for metrics that are currently supported

_client_duration_attrs_old = [
Expand Down