Skip to content

Commit 27a0bd9

Browse files
authored
Merge branch 'main' into weslayer/boto-span-replace
2 parents 942fa6f + b3f98ab commit 27a0bd9

File tree

15 files changed

+319
-79
lines changed

15 files changed

+319
-79
lines changed

CHANGELOG.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1515

1616
- `opentelemetry-instrumentation-fastapi`: fix wrapping of middlewares
1717
([#3012](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3012))
18+
- `opentelemetry-instrumentation-falcon`: proper bucket boundaries in stable semconv http duration
19+
([#3525](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3525))
20+
- `opentelemetry-instrumentation-wsgi`: add explicit http duration buckets for stable semconv
21+
([#3527](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3527))
22+
- `opentelemetry-instrumentation-asgi`: add explicit http duration buckets for stable semconv
23+
([#3526](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3526))
24+
- `opentelemetry-instrumentation-flask`: proper bucket boundaries in stable semconv http duration
25+
([#3523](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3523))
26+
- `opentelemetry-instrumentation-django`: proper bucket boundaries in stable semconv http duration
27+
([#3524](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3524))
28+
- `opentelemetry-instrumentation-grpc`: support non-list interceptors
29+
([#3520](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3520))
1830

1931
### Breaking changes
2032

instrumentation/opentelemetry-instrumentation-aiohttp-server/src/opentelemetry/instrumentation/aiohttp_server/__init__.py

Lines changed: 41 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -54,29 +54,44 @@ async def hello(request):
5454
)
5555
from opentelemetry.propagate import extract
5656
from opentelemetry.propagators.textmap import Getter
57+
from opentelemetry.semconv._incubating.attributes.http_attributes import (
58+
HTTP_FLAVOR,
59+
HTTP_HOST,
60+
HTTP_METHOD,
61+
HTTP_ROUTE,
62+
HTTP_SCHEME,
63+
HTTP_SERVER_NAME,
64+
HTTP_STATUS_CODE,
65+
HTTP_TARGET,
66+
HTTP_URL,
67+
HTTP_USER_AGENT,
68+
)
69+
from opentelemetry.semconv._incubating.attributes.net_attributes import (
70+
NET_HOST_NAME,
71+
NET_HOST_PORT,
72+
)
5773
from opentelemetry.semconv.metrics import MetricInstruments
58-
from opentelemetry.semconv.trace import SpanAttributes
5974
from opentelemetry.trace.status import Status, StatusCode
6075
from opentelemetry.util.http import get_excluded_urls, remove_url_credentials
6176

6277
_duration_attrs = [
63-
SpanAttributes.HTTP_METHOD,
64-
SpanAttributes.HTTP_HOST,
65-
SpanAttributes.HTTP_SCHEME,
66-
SpanAttributes.HTTP_STATUS_CODE,
67-
SpanAttributes.HTTP_FLAVOR,
68-
SpanAttributes.HTTP_SERVER_NAME,
69-
SpanAttributes.NET_HOST_NAME,
70-
SpanAttributes.NET_HOST_PORT,
71-
SpanAttributes.HTTP_ROUTE,
78+
HTTP_METHOD,
79+
HTTP_HOST,
80+
HTTP_SCHEME,
81+
HTTP_STATUS_CODE,
82+
HTTP_FLAVOR,
83+
HTTP_SERVER_NAME,
84+
NET_HOST_NAME,
85+
NET_HOST_PORT,
86+
HTTP_ROUTE,
7287
]
7388

7489
_active_requests_count_attrs = [
75-
SpanAttributes.HTTP_METHOD,
76-
SpanAttributes.HTTP_HOST,
77-
SpanAttributes.HTTP_SCHEME,
78-
SpanAttributes.HTTP_FLAVOR,
79-
SpanAttributes.HTTP_SERVER_NAME,
90+
HTTP_METHOD,
91+
HTTP_HOST,
92+
HTTP_SCHEME,
93+
HTTP_FLAVOR,
94+
HTTP_SERVER_NAME,
8095
]
8196

8297
tracer = trace.get_tracer(__name__)
@@ -140,29 +155,27 @@ def collect_request_attributes(request: web.Request) -> Dict:
140155
http_url += "?" + urllib.parse.unquote(query_string)
141156

142157
result = {
143-
SpanAttributes.HTTP_SCHEME: request.scheme,
144-
SpanAttributes.HTTP_HOST: server_host,
145-
SpanAttributes.NET_HOST_PORT: port,
146-
SpanAttributes.HTTP_ROUTE: _get_view_func(request),
147-
SpanAttributes.HTTP_FLAVOR: f"{request.version.major}.{request.version.minor}",
148-
SpanAttributes.HTTP_TARGET: request.path,
149-
SpanAttributes.HTTP_URL: remove_url_credentials(http_url),
158+
HTTP_SCHEME: request.scheme,
159+
HTTP_HOST: server_host,
160+
NET_HOST_PORT: port,
161+
HTTP_ROUTE: _get_view_func(request),
162+
HTTP_FLAVOR: f"{request.version.major}.{request.version.minor}",
163+
HTTP_TARGET: request.path,
164+
HTTP_URL: remove_url_credentials(http_url),
150165
}
151166

152167
http_method = request.method
153168
if http_method:
154-
result[SpanAttributes.HTTP_METHOD] = http_method
169+
result[HTTP_METHOD] = http_method
155170

156171
http_host_value_list = (
157172
[request.host] if not isinstance(request.host, list) else request.host
158173
)
159174
if http_host_value_list:
160-
result[SpanAttributes.HTTP_SERVER_NAME] = ",".join(
161-
http_host_value_list
162-
)
175+
result[HTTP_SERVER_NAME] = ",".join(http_host_value_list)
163176
http_user_agent = request.headers.get("user-agent")
164177
if http_user_agent:
165-
result[SpanAttributes.HTTP_USER_AGENT] = http_user_agent
178+
result[HTTP_USER_AGENT] = http_user_agent
166179

167180
# remove None values
168181
result = {k: v for k, v in result.items() if v is not None}
@@ -183,7 +196,7 @@ def set_status_code(span, status_code: int) -> None:
183196
)
184197
)
185198
else:
186-
span.set_attribute(SpanAttributes.HTTP_STATUS_CODE, status_code)
199+
span.set_attribute(HTTP_STATUS_CODE, status_code)
187200
span.set_status(
188201
Status(http_status_to_status_code(status_code, server_span=True))
189202
)

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,7 @@ def client_response_hook(span: Span, scope: dict[str, Any], message: dict[str, A
202202

203203
from opentelemetry import context, trace
204204
from opentelemetry.instrumentation._semconv import (
205+
HTTP_DURATION_HISTOGRAM_BUCKETS_NEW,
205206
_filter_semconv_active_request_count_attr,
206207
_filter_semconv_duration_attrs,
207208
_get_schema_url,
@@ -589,6 +590,7 @@ def __init__(
589590
name=HTTP_SERVER_REQUEST_DURATION,
590591
description="Duration of HTTP server requests.",
591592
unit="s",
593+
explicit_bucket_boundaries_advisory=HTTP_DURATION_HISTOGRAM_BUCKETS_NEW,
592594
)
593595
self.server_response_size_histogram = None
594596
if _report_old(sem_conv_opt_in_mode):

instrumentation/opentelemetry-instrumentation-asgi/tests/test_asgi_middleware.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import opentelemetry.instrumentation.asgi as otel_asgi
2424
from opentelemetry import trace as trace_api
2525
from opentelemetry.instrumentation._semconv import (
26+
HTTP_DURATION_HISTOGRAM_BUCKETS_NEW,
2627
OTEL_SEMCONV_STABILITY_OPT_IN,
2728
_OpenTelemetrySemanticConventionStability,
2829
_server_active_requests_count_attrs_new,
@@ -1245,6 +1246,7 @@ async def test_asgi_metrics(self):
12451246
self.assertTrue(number_data_point_seen and histogram_data_point_seen)
12461247

12471248
async def test_asgi_metrics_new_semconv(self):
1249+
# pylint: disable=too-many-nested-blocks
12481250
app = otel_asgi.OpenTelemetryMiddleware(simple_asgi)
12491251
self.seed_app(app)
12501252
await self.send_default_request()
@@ -1274,6 +1276,11 @@ async def test_asgi_metrics_new_semconv(self):
12741276
for point in data_points:
12751277
if isinstance(point, HistogramDataPoint):
12761278
self.assertEqual(point.count, 3)
1279+
if metric.name == "http.server.request.duration":
1280+
self.assertEqual(
1281+
point.explicit_bounds,
1282+
HTTP_DURATION_HISTOGRAM_BUCKETS_NEW,
1283+
)
12771284
histogram_data_point_seen = True
12781285
if isinstance(point, NumberDataPoint):
12791286
number_data_point_seen = True
@@ -1284,6 +1291,7 @@ async def test_asgi_metrics_new_semconv(self):
12841291
self.assertTrue(number_data_point_seen and histogram_data_point_seen)
12851292

12861293
async def test_asgi_metrics_both_semconv(self):
1294+
# pylint: disable=too-many-nested-blocks
12871295
app = otel_asgi.OpenTelemetryMiddleware(simple_asgi)
12881296
self.seed_app(app)
12891297
await self.send_default_request()
@@ -1313,6 +1321,11 @@ async def test_asgi_metrics_both_semconv(self):
13131321
for point in data_points:
13141322
if isinstance(point, HistogramDataPoint):
13151323
self.assertEqual(point.count, 3)
1324+
if metric.name == "http.server.request.duration":
1325+
self.assertEqual(
1326+
point.explicit_bounds,
1327+
HTTP_DURATION_HISTOGRAM_BUCKETS_NEW,
1328+
)
13161329
histogram_data_point_seen = True
13171330
if isinstance(point, NumberDataPoint):
13181331
number_data_point_seen = True

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,7 @@ def response_hook(span, request, response):
246246
from django.core.exceptions import ImproperlyConfigured
247247

248248
from opentelemetry.instrumentation._semconv import (
249+
HTTP_DURATION_HISTOGRAM_BUCKETS_NEW,
249250
_get_schema_url,
250251
_OpenTelemetrySemanticConventionStability,
251252
_OpenTelemetryStabilitySignalType,
@@ -378,6 +379,7 @@ def _instrument(self, **kwargs):
378379
name=HTTP_SERVER_REQUEST_DURATION,
379380
description="Duration of HTTP server requests.",
380381
unit="s",
382+
explicit_bucket_boundaries_advisory=HTTP_DURATION_HISTOGRAM_BUCKETS_NEW,
381383
)
382384
_DjangoMiddleware._active_request_counter = (
383385
create_http_server_active_requests(meter)

instrumentation/opentelemetry-instrumentation-django/tests/test_middleware.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626

2727
from opentelemetry import trace
2828
from opentelemetry.instrumentation._semconv import (
29+
HTTP_DURATION_HISTOGRAM_BUCKETS_NEW,
30+
HTTP_DURATION_HISTOGRAM_BUCKETS_OLD,
2931
OTEL_SEMCONV_STABILITY_OPT_IN,
3032
_OpenTelemetrySemanticConventionStability,
3133
)
@@ -814,6 +816,10 @@ def test_wsgi_metrics_new_semconv(self):
814816
expected_duration_attributes,
815817
dict(point.attributes),
816818
)
819+
self.assertEqual(
820+
point.explicit_bounds,
821+
HTTP_DURATION_HISTOGRAM_BUCKETS_NEW,
822+
)
817823
if isinstance(point, NumberDataPoint):
818824
number_data_point_seen = True
819825
self.assertEqual(point.value, 0)
@@ -886,6 +892,10 @@ def test_wsgi_metrics_both_semconv(self):
886892
expected_duration_attributes_new,
887893
dict(point.attributes),
888894
)
895+
self.assertEqual(
896+
point.explicit_bounds,
897+
HTTP_DURATION_HISTOGRAM_BUCKETS_NEW,
898+
)
889899
elif metric.name == "http.server.duration":
890900
self.assertAlmostEqual(
891901
duration, point.sum, delta=100
@@ -894,6 +904,10 @@ def test_wsgi_metrics_both_semconv(self):
894904
expected_duration_attributes_old,
895905
dict(point.attributes),
896906
)
907+
self.assertEqual(
908+
point.explicit_bounds,
909+
HTTP_DURATION_HISTOGRAM_BUCKETS_OLD,
910+
)
897911
if isinstance(point, NumberDataPoint):
898912
number_data_point_seen = True
899913
self.assertEqual(point.value, 0)

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,7 @@ def response_hook(span, req, resp):
196196
import opentelemetry.instrumentation.wsgi as otel_wsgi
197197
from opentelemetry import context, trace
198198
from opentelemetry.instrumentation._semconv import (
199+
HTTP_DURATION_HISTOGRAM_BUCKETS_NEW,
199200
_get_schema_url,
200201
_OpenTelemetrySemanticConventionStability,
201202
_OpenTelemetryStabilitySignalType,
@@ -297,6 +298,7 @@ def __init__(self, *args, **kwargs):
297298
name=HTTP_SERVER_REQUEST_DURATION,
298299
description="Duration of HTTP server requests.",
299300
unit="s",
301+
explicit_bucket_boundaries_advisory=HTTP_DURATION_HISTOGRAM_BUCKETS_NEW,
300302
)
301303

302304
self.active_requests_counter = self._otel_meter.create_up_down_counter(

instrumentation/opentelemetry-instrumentation-falcon/tests/test_falcon.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222

2323
from opentelemetry import trace
2424
from opentelemetry.instrumentation._semconv import (
25+
HTTP_DURATION_HISTOGRAM_BUCKETS_NEW,
2526
OTEL_SEMCONV_STABILITY_OPT_IN,
2627
_OpenTelemetrySemanticConventionStability,
2728
_server_active_requests_count_attrs_new,
@@ -550,6 +551,10 @@ def test_falcon_metric_values_new_semconv(self):
550551
self.assertAlmostEqual(
551552
duration, point.sum, delta=10
552553
)
554+
self.assertEqual(
555+
point.explicit_bounds,
556+
HTTP_DURATION_HISTOGRAM_BUCKETS_NEW,
557+
)
553558
if isinstance(point, NumberDataPoint):
554559
self.assertEqual(point.value, 0)
555560
number_data_point_seen = True
@@ -600,6 +605,11 @@ def test_falcon_metric_values_both_semconv(self):
600605
self.assertAlmostEqual(
601606
max(duration_s, 0), point.sum, delta=10
602607
)
608+
self.assertEqual(
609+
point.explicit_bounds,
610+
HTTP_DURATION_HISTOGRAM_BUCKETS_NEW,
611+
)
612+
603613
histogram_data_point_seen = True
604614
if isinstance(point, NumberDataPoint):
605615
self.assertEqual(point.value, 0)

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,7 @@ def response_hook(span: Span, status: str, response_headers: List):
257257
import opentelemetry.instrumentation.wsgi as otel_wsgi
258258
from opentelemetry import context, trace
259259
from opentelemetry.instrumentation._semconv import (
260+
HTTP_DURATION_HISTOGRAM_BUCKETS_NEW,
260261
_get_schema_url,
261262
_OpenTelemetrySemanticConventionStability,
262263
_OpenTelemetryStabilitySignalType,
@@ -583,6 +584,7 @@ def __init__(self, *args, **kwargs):
583584
name=HTTP_SERVER_REQUEST_DURATION,
584585
unit="s",
585586
description="Duration of HTTP server requests.",
587+
explicit_bucket_boundaries_advisory=HTTP_DURATION_HISTOGRAM_BUCKETS_NEW,
586588
)
587589
active_requests_counter = meter.create_up_down_counter(
588590
name=MetricInstruments.HTTP_SERVER_ACTIVE_REQUESTS,
@@ -716,6 +718,7 @@ def instrument_app(
716718
name=HTTP_SERVER_REQUEST_DURATION,
717719
unit="s",
718720
description="Duration of HTTP server requests.",
721+
explicit_bucket_boundaries_advisory=HTTP_DURATION_HISTOGRAM_BUCKETS_NEW,
719722
)
720723
active_requests_counter = meter.create_up_down_counter(
721724
name=MetricInstruments.HTTP_SERVER_ACTIVE_REQUESTS,

instrumentation/opentelemetry-instrumentation-flask/tests/test_programmatic.py

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
from opentelemetry import trace
2222
from opentelemetry.instrumentation._semconv import (
23+
HTTP_DURATION_HISTOGRAM_BUCKETS_NEW,
2324
OTEL_SEMCONV_STABILITY_OPT_IN,
2425
_OpenTelemetrySemanticConventionStability,
2526
_server_active_requests_count_attrs_new,
@@ -522,6 +523,10 @@ def test_flask_metrics_new_semconv(self):
522523
self.assertAlmostEqual(
523524
duration_s, point.sum, places=1
524525
)
526+
self.assertEqual(
527+
point.explicit_bounds,
528+
HTTP_DURATION_HISTOGRAM_BUCKETS_NEW,
529+
)
525530
histogram_data_point_seen = True
526531
if isinstance(point, NumberDataPoint):
527532
number_data_point_seen = True
@@ -552,8 +557,12 @@ def test_flask_metric_values(self):
552557
self.assertEqual(point.value, 0)
553558

554559
def _assert_basic_metric(
555-
self, expected_duration_attributes, expected_requests_count_attributes
560+
self,
561+
expected_duration_attributes,
562+
expected_requests_count_attributes,
563+
expected_histogram_explicit_bounds=None,
556564
):
565+
# pylint: disable=too-many-nested-blocks
557566
metrics_list = self.memory_metrics_reader.get_metrics_data()
558567
for resource_metric in metrics_list.resource_metrics:
559568
for scope_metrics in resource_metric.scope_metrics:
@@ -564,6 +573,11 @@ def _assert_basic_metric(
564573
expected_duration_attributes,
565574
dict(point.attributes),
566575
)
576+
if expected_histogram_explicit_bounds is not None:
577+
self.assertEqual(
578+
expected_histogram_explicit_bounds,
579+
point.explicit_bounds,
580+
)
567581
self.assertEqual(point.count, 1)
568582
elif isinstance(point, NumberDataPoint):
569583
self.assertDictEqual(
@@ -613,6 +627,7 @@ def test_basic_metric_success_new_semconv(self):
613627
self._assert_basic_metric(
614628
expected_duration_attributes,
615629
expected_requests_count_attributes,
630+
expected_histogram_explicit_bounds=HTTP_DURATION_HISTOGRAM_BUCKETS_NEW,
616631
)
617632

618633
def test_basic_metric_nonstandard_http_method_success(self):
@@ -654,6 +669,7 @@ def test_basic_metric_nonstandard_http_method_success_new_semconv(self):
654669
self._assert_basic_metric(
655670
expected_duration_attributes,
656671
expected_requests_count_attributes,
672+
expected_histogram_explicit_bounds=HTTP_DURATION_HISTOGRAM_BUCKETS_NEW,
657673
)
658674

659675
@patch.dict(
@@ -679,6 +695,7 @@ def test_basic_metric_nonstandard_http_method_allowed_success_new_semconv(
679695
self._assert_basic_metric(
680696
expected_duration_attributes,
681697
expected_requests_count_attributes,
698+
expected_histogram_explicit_bounds=HTTP_DURATION_HISTOGRAM_BUCKETS_NEW,
682699
)
683700

684701
def test_metric_uninstrument(self):

0 commit comments

Comments
 (0)