opentelemetry-instrumentation-aiohttp-client 0.54b0__tar.gz → 0.55b0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {opentelemetry_instrumentation_aiohttp_client-0.54b0 → opentelemetry_instrumentation_aiohttp_client-0.55b0}/PKG-INFO +5 -6
- {opentelemetry_instrumentation_aiohttp_client-0.54b0 → opentelemetry_instrumentation_aiohttp_client-0.55b0}/pyproject.toml +4 -5
- {opentelemetry_instrumentation_aiohttp_client-0.54b0 → opentelemetry_instrumentation_aiohttp_client-0.55b0}/src/opentelemetry/instrumentation/aiohttp_client/__init__.py +120 -3
- {opentelemetry_instrumentation_aiohttp_client-0.54b0 → opentelemetry_instrumentation_aiohttp_client-0.55b0}/src/opentelemetry/instrumentation/aiohttp_client/package.py +1 -1
- {opentelemetry_instrumentation_aiohttp_client-0.54b0 → opentelemetry_instrumentation_aiohttp_client-0.55b0}/src/opentelemetry/instrumentation/aiohttp_client/version.py +1 -1
- {opentelemetry_instrumentation_aiohttp_client-0.54b0 → opentelemetry_instrumentation_aiohttp_client-0.55b0}/tests/test_aiohttp_client_integration.py +311 -80
- {opentelemetry_instrumentation_aiohttp_client-0.54b0 → opentelemetry_instrumentation_aiohttp_client-0.55b0}/.gitignore +0 -0
- {opentelemetry_instrumentation_aiohttp_client-0.54b0 → opentelemetry_instrumentation_aiohttp_client-0.55b0}/LICENSE +0 -0
- {opentelemetry_instrumentation_aiohttp_client-0.54b0 → opentelemetry_instrumentation_aiohttp_client-0.55b0}/README.rst +0 -0
- {opentelemetry_instrumentation_aiohttp_client-0.54b0 → opentelemetry_instrumentation_aiohttp_client-0.55b0}/tests/__init__.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: opentelemetry-instrumentation-aiohttp-client
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.55b0
|
4
4
|
Summary: OpenTelemetry aiohttp client instrumentation
|
5
5
|
Project-URL: Homepage, https://github.com/open-telemetry/opentelemetry-python-contrib/tree/main/instrumentation/opentelemetry-instrumentation-aiohttp-client
|
6
6
|
Project-URL: Repository, https://github.com/open-telemetry/opentelemetry-python-contrib
|
@@ -12,17 +12,16 @@ Classifier: Intended Audience :: Developers
|
|
12
12
|
Classifier: License :: OSI Approved :: Apache Software License
|
13
13
|
Classifier: Programming Language :: Python
|
14
14
|
Classifier: Programming Language :: Python :: 3
|
15
|
-
Classifier: Programming Language :: Python :: 3.8
|
16
15
|
Classifier: Programming Language :: Python :: 3.9
|
17
16
|
Classifier: Programming Language :: Python :: 3.10
|
18
17
|
Classifier: Programming Language :: Python :: 3.11
|
19
18
|
Classifier: Programming Language :: Python :: 3.12
|
20
19
|
Classifier: Programming Language :: Python :: 3.13
|
21
|
-
Requires-Python: >=3.
|
20
|
+
Requires-Python: >=3.9
|
22
21
|
Requires-Dist: opentelemetry-api~=1.12
|
23
|
-
Requires-Dist: opentelemetry-instrumentation==0.
|
24
|
-
Requires-Dist: opentelemetry-semantic-conventions==0.
|
25
|
-
Requires-Dist: opentelemetry-util-http==0.
|
22
|
+
Requires-Dist: opentelemetry-instrumentation==0.55b0
|
23
|
+
Requires-Dist: opentelemetry-semantic-conventions==0.55b0
|
24
|
+
Requires-Dist: opentelemetry-util-http==0.55b0
|
26
25
|
Requires-Dist: wrapt<2.0.0,>=1.0.0
|
27
26
|
Provides-Extra: instruments
|
28
27
|
Requires-Dist: aiohttp~=3.0; extra == 'instruments'
|
@@ -8,7 +8,7 @@ dynamic = ["version"]
|
|
8
8
|
description = "OpenTelemetry aiohttp client instrumentation"
|
9
9
|
readme = "README.rst"
|
10
10
|
license = "Apache-2.0"
|
11
|
-
requires-python = ">=3.
|
11
|
+
requires-python = ">=3.9"
|
12
12
|
authors = [
|
13
13
|
{ name = "OpenTelemetry Authors", email = "cncf-opentelemetry-contributors@lists.cncf.io" },
|
14
14
|
]
|
@@ -18,7 +18,6 @@ classifiers = [
|
|
18
18
|
"License :: OSI Approved :: Apache Software License",
|
19
19
|
"Programming Language :: Python",
|
20
20
|
"Programming Language :: Python :: 3",
|
21
|
-
"Programming Language :: Python :: 3.8",
|
22
21
|
"Programming Language :: Python :: 3.9",
|
23
22
|
"Programming Language :: Python :: 3.10",
|
24
23
|
"Programming Language :: Python :: 3.11",
|
@@ -27,9 +26,9 @@ classifiers = [
|
|
27
26
|
]
|
28
27
|
dependencies = [
|
29
28
|
"opentelemetry-api ~= 1.12",
|
30
|
-
"opentelemetry-instrumentation == 0.
|
31
|
-
"opentelemetry-semantic-conventions == 0.
|
32
|
-
"opentelemetry-util-http == 0.
|
29
|
+
"opentelemetry-instrumentation == 0.55b0",
|
30
|
+
"opentelemetry-semantic-conventions == 0.55b0",
|
31
|
+
"opentelemetry-util-http == 0.55b0",
|
33
32
|
"wrapt >= 1.0.0, < 2.0.0",
|
34
33
|
]
|
35
34
|
|
@@ -90,7 +90,9 @@ API
|
|
90
90
|
|
91
91
|
import types
|
92
92
|
import typing
|
93
|
+
from timeit import default_timer
|
93
94
|
from typing import Collection
|
95
|
+
from urllib.parse import urlparse
|
94
96
|
|
95
97
|
import aiohttp
|
96
98
|
import wrapt
|
@@ -99,11 +101,20 @@ import yarl
|
|
99
101
|
from opentelemetry import context as context_api
|
100
102
|
from opentelemetry import trace
|
101
103
|
from opentelemetry.instrumentation._semconv import (
|
104
|
+
HTTP_DURATION_HISTOGRAM_BUCKETS_NEW,
|
105
|
+
HTTP_DURATION_HISTOGRAM_BUCKETS_OLD,
|
106
|
+
_client_duration_attrs_new,
|
107
|
+
_client_duration_attrs_old,
|
108
|
+
_filter_semconv_duration_attrs,
|
102
109
|
_get_schema_url,
|
103
110
|
_OpenTelemetrySemanticConventionStability,
|
104
111
|
_OpenTelemetryStabilitySignalType,
|
105
112
|
_report_new,
|
113
|
+
_report_old,
|
114
|
+
_set_http_host_client,
|
106
115
|
_set_http_method,
|
116
|
+
_set_http_net_peer_name_client,
|
117
|
+
_set_http_peer_port_client,
|
107
118
|
_set_http_url,
|
108
119
|
_set_status,
|
109
120
|
_StabilityMode,
|
@@ -115,8 +126,13 @@ from opentelemetry.instrumentation.utils import (
|
|
115
126
|
is_instrumentation_enabled,
|
116
127
|
unwrap,
|
117
128
|
)
|
129
|
+
from opentelemetry.metrics import MeterProvider, get_meter
|
118
130
|
from opentelemetry.propagate import inject
|
119
131
|
from opentelemetry.semconv.attributes.error_attributes import ERROR_TYPE
|
132
|
+
from opentelemetry.semconv.metrics import MetricInstruments
|
133
|
+
from opentelemetry.semconv.metrics.http_metrics import (
|
134
|
+
HTTP_CLIENT_REQUEST_DURATION,
|
135
|
+
)
|
120
136
|
from opentelemetry.trace import Span, SpanKind, TracerProvider, get_tracer
|
121
137
|
from opentelemetry.trace.status import Status, StatusCode
|
122
138
|
from opentelemetry.util.http import remove_url_credentials, sanitize_method
|
@@ -172,11 +188,14 @@ def _set_http_status_code_attribute(
|
|
172
188
|
)
|
173
189
|
|
174
190
|
|
191
|
+
# pylint: disable=too-many-locals
|
192
|
+
# pylint: disable=too-many-statements
|
175
193
|
def create_trace_config(
|
176
194
|
url_filter: _UrlFilterT = None,
|
177
195
|
request_hook: _RequestHookT = None,
|
178
196
|
response_hook: _ResponseHookT = None,
|
179
197
|
tracer_provider: TracerProvider = None,
|
198
|
+
meter_provider: MeterProvider = None,
|
180
199
|
sem_conv_opt_in_mode: _StabilityMode = _StabilityMode.DEFAULT,
|
181
200
|
) -> aiohttp.TraceConfig:
|
182
201
|
"""Create an aiohttp-compatible trace configuration.
|
@@ -205,6 +224,7 @@ def create_trace_config(
|
|
205
224
|
:param Callable request_hook: Optional callback that can modify span name and request params.
|
206
225
|
:param Callable response_hook: Optional callback that can modify span name and response params.
|
207
226
|
:param tracer_provider: optional TracerProvider from which to get a Tracer
|
227
|
+
:param meter_provider: optional Meter provider to use
|
208
228
|
|
209
229
|
:return: An object suitable for use with :py:class:`aiohttp.ClientSession`.
|
210
230
|
:rtype: :py:class:`aiohttp.TraceConfig`
|
@@ -214,20 +234,70 @@ def create_trace_config(
|
|
214
234
|
# Explicitly specify the type for the `request_hook` and `response_hook` param and rtype to work
|
215
235
|
# around this issue.
|
216
236
|
|
237
|
+
schema_url = _get_schema_url(sem_conv_opt_in_mode)
|
238
|
+
|
217
239
|
tracer = get_tracer(
|
218
240
|
__name__,
|
219
241
|
__version__,
|
220
242
|
tracer_provider,
|
221
|
-
schema_url=
|
243
|
+
schema_url=schema_url,
|
244
|
+
)
|
245
|
+
|
246
|
+
meter = get_meter(
|
247
|
+
__name__,
|
248
|
+
__version__,
|
249
|
+
meter_provider,
|
250
|
+
schema_url,
|
222
251
|
)
|
223
252
|
|
224
|
-
|
253
|
+
start_time = 0
|
254
|
+
|
255
|
+
duration_histogram_old = None
|
256
|
+
if _report_old(sem_conv_opt_in_mode):
|
257
|
+
duration_histogram_old = meter.create_histogram(
|
258
|
+
name=MetricInstruments.HTTP_CLIENT_DURATION,
|
259
|
+
unit="ms",
|
260
|
+
description="measures the duration of the outbound HTTP request",
|
261
|
+
explicit_bucket_boundaries_advisory=HTTP_DURATION_HISTOGRAM_BUCKETS_OLD,
|
262
|
+
)
|
263
|
+
duration_histogram_new = None
|
264
|
+
if _report_new(sem_conv_opt_in_mode):
|
265
|
+
duration_histogram_new = meter.create_histogram(
|
266
|
+
name=HTTP_CLIENT_REQUEST_DURATION,
|
267
|
+
unit="s",
|
268
|
+
description="Duration of HTTP client requests.",
|
269
|
+
explicit_bucket_boundaries_advisory=HTTP_DURATION_HISTOGRAM_BUCKETS_NEW,
|
270
|
+
)
|
271
|
+
|
225
272
|
metric_attributes = {}
|
226
273
|
|
227
274
|
def _end_trace(trace_config_ctx: types.SimpleNamespace):
|
275
|
+
elapsed_time = max(default_timer() - trace_config_ctx.start_time, 0)
|
228
276
|
context_api.detach(trace_config_ctx.token)
|
229
277
|
trace_config_ctx.span.end()
|
230
278
|
|
279
|
+
if trace_config_ctx.duration_histogram_old is not None:
|
280
|
+
duration_attrs_old = _filter_semconv_duration_attrs(
|
281
|
+
metric_attributes,
|
282
|
+
_client_duration_attrs_old,
|
283
|
+
_client_duration_attrs_new,
|
284
|
+
_StabilityMode.DEFAULT,
|
285
|
+
)
|
286
|
+
trace_config_ctx.duration_histogram_old.record(
|
287
|
+
max(round(elapsed_time * 1000), 0),
|
288
|
+
attributes=duration_attrs_old,
|
289
|
+
)
|
290
|
+
if trace_config_ctx.duration_histogram_new is not None:
|
291
|
+
duration_attrs_new = _filter_semconv_duration_attrs(
|
292
|
+
metric_attributes,
|
293
|
+
_client_duration_attrs_old,
|
294
|
+
_client_duration_attrs_new,
|
295
|
+
_StabilityMode.HTTP,
|
296
|
+
)
|
297
|
+
trace_config_ctx.duration_histogram_new.record(
|
298
|
+
elapsed_time, attributes=duration_attrs_new
|
299
|
+
)
|
300
|
+
|
231
301
|
async def on_request_start(
|
232
302
|
unused_session: aiohttp.ClientSession,
|
233
303
|
trace_config_ctx: types.SimpleNamespace,
|
@@ -237,6 +307,7 @@ def create_trace_config(
|
|
237
307
|
trace_config_ctx.span = None
|
238
308
|
return
|
239
309
|
|
310
|
+
trace_config_ctx.start_time = default_timer()
|
240
311
|
method = params.method
|
241
312
|
request_span_name = _get_span_name(method)
|
242
313
|
request_url = (
|
@@ -252,8 +323,44 @@ def create_trace_config(
|
|
252
323
|
sanitize_method(method),
|
253
324
|
sem_conv_opt_in_mode,
|
254
325
|
)
|
326
|
+
_set_http_method(
|
327
|
+
metric_attributes,
|
328
|
+
method,
|
329
|
+
sanitize_method(method),
|
330
|
+
sem_conv_opt_in_mode,
|
331
|
+
)
|
255
332
|
_set_http_url(span_attributes, request_url, sem_conv_opt_in_mode)
|
256
333
|
|
334
|
+
try:
|
335
|
+
parsed_url = urlparse(request_url)
|
336
|
+
if parsed_url.hostname:
|
337
|
+
_set_http_host_client(
|
338
|
+
metric_attributes,
|
339
|
+
parsed_url.hostname,
|
340
|
+
sem_conv_opt_in_mode,
|
341
|
+
)
|
342
|
+
_set_http_net_peer_name_client(
|
343
|
+
metric_attributes,
|
344
|
+
parsed_url.hostname,
|
345
|
+
sem_conv_opt_in_mode,
|
346
|
+
)
|
347
|
+
if _report_new(sem_conv_opt_in_mode):
|
348
|
+
_set_http_host_client(
|
349
|
+
span_attributes,
|
350
|
+
parsed_url.hostname,
|
351
|
+
sem_conv_opt_in_mode,
|
352
|
+
)
|
353
|
+
if parsed_url.port:
|
354
|
+
_set_http_peer_port_client(
|
355
|
+
metric_attributes, parsed_url.port, sem_conv_opt_in_mode
|
356
|
+
)
|
357
|
+
if _report_new(sem_conv_opt_in_mode):
|
358
|
+
_set_http_peer_port_client(
|
359
|
+
span_attributes, parsed_url.port, sem_conv_opt_in_mode
|
360
|
+
)
|
361
|
+
except ValueError:
|
362
|
+
pass
|
363
|
+
|
257
364
|
trace_config_ctx.span = trace_config_ctx.tracer.start_span(
|
258
365
|
request_span_name, kind=SpanKind.CLIENT, attributes=span_attributes
|
259
366
|
)
|
@@ -298,6 +405,7 @@ def create_trace_config(
|
|
298
405
|
exc_type = type(params.exception).__qualname__
|
299
406
|
if _report_new(sem_conv_opt_in_mode):
|
300
407
|
trace_config_ctx.span.set_attribute(ERROR_TYPE, exc_type)
|
408
|
+
metric_attributes[ERROR_TYPE] = exc_type
|
301
409
|
|
302
410
|
trace_config_ctx.span.set_status(
|
303
411
|
Status(StatusCode.ERROR, exc_type)
|
@@ -312,7 +420,12 @@ def create_trace_config(
|
|
312
420
|
def _trace_config_ctx_factory(**kwargs):
|
313
421
|
kwargs.setdefault("trace_request_ctx", {})
|
314
422
|
return types.SimpleNamespace(
|
315
|
-
tracer=tracer,
|
423
|
+
tracer=tracer,
|
424
|
+
url_filter=url_filter,
|
425
|
+
start_time=start_time,
|
426
|
+
duration_histogram_old=duration_histogram_old,
|
427
|
+
duration_histogram_new=duration_histogram_new,
|
428
|
+
**kwargs,
|
316
429
|
)
|
317
430
|
|
318
431
|
trace_config = aiohttp.TraceConfig(
|
@@ -328,6 +441,7 @@ def create_trace_config(
|
|
328
441
|
|
329
442
|
def _instrument(
|
330
443
|
tracer_provider: TracerProvider = None,
|
444
|
+
meter_provider: MeterProvider = None,
|
331
445
|
url_filter: _UrlFilterT = None,
|
332
446
|
request_hook: _RequestHookT = None,
|
333
447
|
response_hook: _ResponseHookT = None,
|
@@ -357,6 +471,7 @@ def _instrument(
|
|
357
471
|
request_hook=request_hook,
|
358
472
|
response_hook=response_hook,
|
359
473
|
tracer_provider=tracer_provider,
|
474
|
+
meter_provider=meter_provider,
|
360
475
|
sem_conv_opt_in_mode=sem_conv_opt_in_mode,
|
361
476
|
)
|
362
477
|
trace_config._is_instrumented_by_opentelemetry = True
|
@@ -401,6 +516,7 @@ class AioHttpClientInstrumentor(BaseInstrumentor):
|
|
401
516
|
Args:
|
402
517
|
**kwargs: Optional arguments
|
403
518
|
``tracer_provider``: a TracerProvider, defaults to global
|
519
|
+
``meter_provider``: a MeterProvider, defaults to global
|
404
520
|
``url_filter``: A callback to process the requested URL prior to adding
|
405
521
|
it as a span attribute. This can be useful to remove sensitive data
|
406
522
|
such as API keys or user personal information.
|
@@ -415,6 +531,7 @@ class AioHttpClientInstrumentor(BaseInstrumentor):
|
|
415
531
|
)
|
416
532
|
_instrument(
|
417
533
|
tracer_provider=kwargs.get("tracer_provider"),
|
534
|
+
meter_provider=kwargs.get("meter_provider"),
|
418
535
|
url_filter=kwargs.get("url_filter"),
|
419
536
|
request_hook=kwargs.get("request_hook"),
|
420
537
|
response_hook=kwargs.get("response_hook"),
|
@@ -12,6 +12,8 @@
|
|
12
12
|
# See the License for the specific language governing permissions and
|
13
13
|
# limitations under the License.
|
14
14
|
|
15
|
+
# pylint: disable=too-many-lines
|
16
|
+
|
15
17
|
import asyncio
|
16
18
|
import contextlib
|
17
19
|
import typing
|
@@ -28,6 +30,8 @@ from http_server_mock import HttpServerMock
|
|
28
30
|
from opentelemetry import trace as trace_api
|
29
31
|
from opentelemetry.instrumentation import aiohttp_client
|
30
32
|
from opentelemetry.instrumentation._semconv import (
|
33
|
+
HTTP_DURATION_HISTOGRAM_BUCKETS_NEW,
|
34
|
+
HTTP_DURATION_HISTOGRAM_BUCKETS_OLD,
|
31
35
|
OTEL_SEMCONV_STABILITY_OPT_IN,
|
32
36
|
_OpenTelemetrySemanticConventionStability,
|
33
37
|
_StabilityMode,
|
@@ -36,6 +40,20 @@ from opentelemetry.instrumentation.aiohttp_client import (
|
|
36
40
|
AioHttpClientInstrumentor,
|
37
41
|
)
|
38
42
|
from opentelemetry.instrumentation.utils import suppress_instrumentation
|
43
|
+
from opentelemetry.semconv._incubating.attributes.http_attributes import (
|
44
|
+
HTTP_HOST,
|
45
|
+
HTTP_METHOD,
|
46
|
+
HTTP_STATUS_CODE,
|
47
|
+
HTTP_URL,
|
48
|
+
)
|
49
|
+
from opentelemetry.semconv._incubating.attributes.net_attributes import (
|
50
|
+
NET_PEER_NAME,
|
51
|
+
NET_PEER_PORT,
|
52
|
+
)
|
53
|
+
from opentelemetry.semconv._incubating.attributes.server_attributes import (
|
54
|
+
SERVER_ADDRESS,
|
55
|
+
SERVER_PORT,
|
56
|
+
)
|
39
57
|
from opentelemetry.semconv.attributes.error_attributes import ERROR_TYPE
|
40
58
|
from opentelemetry.semconv.attributes.http_attributes import (
|
41
59
|
HTTP_REQUEST_METHOD,
|
@@ -43,7 +61,6 @@ from opentelemetry.semconv.attributes.http_attributes import (
|
|
43
61
|
HTTP_RESPONSE_STATUS_CODE,
|
44
62
|
)
|
45
63
|
from opentelemetry.semconv.attributes.url_attributes import URL_FULL
|
46
|
-
from opentelemetry.semconv.trace import SpanAttributes
|
47
64
|
from opentelemetry.test.test_base import TestBase
|
48
65
|
from opentelemetry.trace import Span, StatusCode
|
49
66
|
from opentelemetry.util._importlib_metadata import entry_points
|
@@ -84,7 +101,7 @@ class TestAioHttpIntegration(TestBase):
|
|
84
101
|
super().setUp()
|
85
102
|
_OpenTelemetrySemanticConventionStability._initialized = False
|
86
103
|
|
87
|
-
def
|
104
|
+
def _assert_spans(self, spans, num_spans=1):
|
88
105
|
finished_spans = self.memory_exporter.get_finished_spans()
|
89
106
|
self.assertEqual(num_spans, len(finished_spans))
|
90
107
|
self.assertEqual(
|
@@ -99,6 +116,11 @@ class TestAioHttpIntegration(TestBase):
|
|
99
116
|
spans,
|
100
117
|
)
|
101
118
|
|
119
|
+
def _assert_metrics(self, num_metrics: int = 1):
|
120
|
+
metrics = self.get_sorted_metrics()
|
121
|
+
self.assertEqual(len(metrics), num_metrics)
|
122
|
+
return metrics
|
123
|
+
|
102
124
|
@staticmethod
|
103
125
|
def _http_request(
|
104
126
|
trace_config,
|
@@ -126,6 +148,7 @@ class TestAioHttpIntegration(TestBase):
|
|
126
148
|
return run_with_test_server(client_request, url, handler)
|
127
149
|
|
128
150
|
def test_status_codes(self):
|
151
|
+
index = 0
|
129
152
|
for status_code, span_status in self._test_status_codes:
|
130
153
|
with self.subTest(status_code=status_code):
|
131
154
|
path = "test-path?query=param#foobar"
|
@@ -136,15 +159,34 @@ class TestAioHttpIntegration(TestBase):
|
|
136
159
|
)
|
137
160
|
url = f"http://{host}:{port}/{path}"
|
138
161
|
attributes = {
|
139
|
-
|
140
|
-
|
141
|
-
|
162
|
+
HTTP_METHOD: "GET",
|
163
|
+
HTTP_URL: url,
|
164
|
+
HTTP_STATUS_CODE: status_code,
|
142
165
|
}
|
166
|
+
|
143
167
|
spans = [("GET", (span_status, None), attributes)]
|
144
|
-
self.
|
168
|
+
self._assert_spans(spans)
|
145
169
|
self.memory_exporter.clear()
|
170
|
+
metrics = self._assert_metrics(1)
|
171
|
+
duration_data_point = metrics[0].data.data_points[index]
|
172
|
+
self.assertEqual(
|
173
|
+
dict(duration_data_point.attributes),
|
174
|
+
{
|
175
|
+
HTTP_STATUS_CODE: status_code,
|
176
|
+
HTTP_METHOD: "GET",
|
177
|
+
HTTP_HOST: host,
|
178
|
+
NET_PEER_NAME: host,
|
179
|
+
NET_PEER_PORT: port,
|
180
|
+
},
|
181
|
+
)
|
182
|
+
self.assertEqual(
|
183
|
+
duration_data_point.explicit_bounds,
|
184
|
+
HTTP_DURATION_HISTOGRAM_BUCKETS_OLD,
|
185
|
+
)
|
186
|
+
index += 1
|
146
187
|
|
147
188
|
def test_status_codes_new_semconv(self):
|
189
|
+
index = 0
|
148
190
|
for status_code, span_status in self._test_status_codes:
|
149
191
|
with self.subTest(status_code=status_code):
|
150
192
|
path = "test-path?query=param#foobar"
|
@@ -160,14 +202,39 @@ class TestAioHttpIntegration(TestBase):
|
|
160
202
|
HTTP_REQUEST_METHOD: "GET",
|
161
203
|
URL_FULL: url,
|
162
204
|
HTTP_RESPONSE_STATUS_CODE: status_code,
|
205
|
+
SERVER_ADDRESS: host,
|
206
|
+
SERVER_PORT: port,
|
163
207
|
}
|
164
208
|
if status_code >= 400:
|
165
209
|
attributes[ERROR_TYPE] = str(status_code.value)
|
166
210
|
spans = [("GET", (span_status, None), attributes)]
|
167
|
-
self.
|
211
|
+
self._assert_spans(spans)
|
168
212
|
self.memory_exporter.clear()
|
213
|
+
metrics = self._assert_metrics(1)
|
214
|
+
duration_data_point = metrics[0].data.data_points[index]
|
215
|
+
self.assertEqual(
|
216
|
+
duration_data_point.attributes.get(
|
217
|
+
HTTP_RESPONSE_STATUS_CODE
|
218
|
+
),
|
219
|
+
status_code,
|
220
|
+
)
|
221
|
+
self.assertEqual(
|
222
|
+
duration_data_point.attributes.get(HTTP_REQUEST_METHOD),
|
223
|
+
"GET",
|
224
|
+
)
|
225
|
+
if status_code >= 400:
|
226
|
+
self.assertEqual(
|
227
|
+
duration_data_point.attributes.get(ERROR_TYPE),
|
228
|
+
str(status_code.value),
|
229
|
+
)
|
230
|
+
self.assertEqual(
|
231
|
+
duration_data_point.explicit_bounds,
|
232
|
+
HTTP_DURATION_HISTOGRAM_BUCKETS_NEW,
|
233
|
+
)
|
234
|
+
index += 1
|
169
235
|
|
170
236
|
def test_status_codes_both_semconv(self):
|
237
|
+
index = 0
|
171
238
|
for status_code, span_status in self._test_status_codes:
|
172
239
|
with self.subTest(status_code=status_code):
|
173
240
|
path = "test-path?query=param#foobar"
|
@@ -181,18 +248,79 @@ class TestAioHttpIntegration(TestBase):
|
|
181
248
|
url = f"http://{host}:{port}/{path}"
|
182
249
|
attributes = {
|
183
250
|
HTTP_REQUEST_METHOD: "GET",
|
184
|
-
|
251
|
+
HTTP_METHOD: "GET",
|
252
|
+
HTTP_HOST: host,
|
185
253
|
URL_FULL: url,
|
186
|
-
|
254
|
+
HTTP_URL: url,
|
187
255
|
HTTP_RESPONSE_STATUS_CODE: status_code,
|
188
|
-
|
256
|
+
HTTP_STATUS_CODE: status_code,
|
257
|
+
SERVER_ADDRESS: host,
|
258
|
+
SERVER_PORT: port,
|
259
|
+
NET_PEER_PORT: port,
|
189
260
|
}
|
261
|
+
|
190
262
|
if status_code >= 400:
|
191
263
|
attributes[ERROR_TYPE] = str(status_code.value)
|
192
264
|
|
193
265
|
spans = [("GET", (span_status, None), attributes)]
|
194
|
-
self.
|
266
|
+
self._assert_spans(spans, 1)
|
195
267
|
self.memory_exporter.clear()
|
268
|
+
metrics = self._assert_metrics(2)
|
269
|
+
duration_data_point = metrics[0].data.data_points[index]
|
270
|
+
self.assertEqual(
|
271
|
+
duration_data_point.attributes.get(HTTP_STATUS_CODE),
|
272
|
+
status_code,
|
273
|
+
)
|
274
|
+
self.assertEqual(
|
275
|
+
duration_data_point.attributes.get(HTTP_METHOD),
|
276
|
+
"GET",
|
277
|
+
)
|
278
|
+
self.assertEqual(
|
279
|
+
duration_data_point.attributes.get(ERROR_TYPE),
|
280
|
+
None,
|
281
|
+
)
|
282
|
+
duration_data_point = metrics[1].data.data_points[index]
|
283
|
+
self.assertEqual(
|
284
|
+
duration_data_point.attributes.get(
|
285
|
+
HTTP_RESPONSE_STATUS_CODE
|
286
|
+
),
|
287
|
+
status_code,
|
288
|
+
)
|
289
|
+
self.assertEqual(
|
290
|
+
duration_data_point.attributes.get(HTTP_REQUEST_METHOD),
|
291
|
+
"GET",
|
292
|
+
)
|
293
|
+
if status_code >= 400:
|
294
|
+
self.assertEqual(
|
295
|
+
duration_data_point.attributes.get(ERROR_TYPE),
|
296
|
+
str(status_code.value),
|
297
|
+
)
|
298
|
+
index += 1
|
299
|
+
|
300
|
+
def test_metrics(self):
|
301
|
+
with self.subTest(status_code=200):
|
302
|
+
host, port = self._http_request(
|
303
|
+
trace_config=aiohttp_client.create_trace_config(),
|
304
|
+
url="/test-path?query=param#foobar",
|
305
|
+
status_code=200,
|
306
|
+
)
|
307
|
+
metrics = self._assert_metrics(1)
|
308
|
+
self.assertEqual(len(metrics[0].data.data_points), 1)
|
309
|
+
duration_data_point = metrics[0].data.data_points[0]
|
310
|
+
self.assertEqual(
|
311
|
+
dict(metrics[0].data.data_points[0].attributes),
|
312
|
+
{
|
313
|
+
HTTP_STATUS_CODE: 200,
|
314
|
+
HTTP_METHOD: "GET",
|
315
|
+
HTTP_HOST: host,
|
316
|
+
NET_PEER_NAME: host,
|
317
|
+
NET_PEER_PORT: port,
|
318
|
+
},
|
319
|
+
)
|
320
|
+
self.assertEqual(duration_data_point.count, 1)
|
321
|
+
self.assertTrue(duration_data_point.min > 0)
|
322
|
+
self.assertTrue(duration_data_point.max > 0)
|
323
|
+
self.assertTrue(duration_data_point.sum > 0)
|
196
324
|
|
197
325
|
def test_schema_url(self):
|
198
326
|
with self.subTest(status_code=200):
|
@@ -292,16 +420,12 @@ class TestAioHttpIntegration(TestBase):
|
|
292
420
|
(span.status.status_code, span.status.description),
|
293
421
|
(StatusCode.UNSET, None),
|
294
422
|
)
|
423
|
+
self.assertEqual(span.attributes[HTTP_METHOD], method)
|
295
424
|
self.assertEqual(
|
296
|
-
span.attributes[
|
297
|
-
)
|
298
|
-
self.assertEqual(
|
299
|
-
span.attributes[SpanAttributes.HTTP_URL],
|
425
|
+
span.attributes[HTTP_URL],
|
300
426
|
f"http://{host}:{port}{path}",
|
301
427
|
)
|
302
|
-
self.assertEqual(
|
303
|
-
span.attributes[SpanAttributes.HTTP_STATUS_CODE], HTTPStatus.OK
|
304
|
-
)
|
428
|
+
self.assertEqual(span.attributes[HTTP_STATUS_CODE], HTTPStatus.OK)
|
305
429
|
self.assertIn("response_hook_attr", span.attributes)
|
306
430
|
self.assertEqual(span.attributes["response_hook_attr"], "value")
|
307
431
|
self.memory_exporter.clear()
|
@@ -319,15 +443,15 @@ class TestAioHttpIntegration(TestBase):
|
|
319
443
|
status_code=HTTPStatus.OK,
|
320
444
|
)
|
321
445
|
|
322
|
-
self.
|
446
|
+
self._assert_spans(
|
323
447
|
[
|
324
448
|
(
|
325
449
|
"GET",
|
326
450
|
(StatusCode.UNSET, None),
|
327
451
|
{
|
328
|
-
|
329
|
-
|
330
|
-
|
452
|
+
HTTP_METHOD: "GET",
|
453
|
+
HTTP_URL: f"http://{host}:{port}/some/path",
|
454
|
+
HTTP_STATUS_CODE: int(HTTPStatus.OK),
|
331
455
|
},
|
332
456
|
)
|
333
457
|
]
|
@@ -353,14 +477,14 @@ class TestAioHttpIntegration(TestBase):
|
|
353
477
|
with self.assertRaises(aiohttp.ClientConnectorError):
|
354
478
|
loop.run_until_complete(do_request(url))
|
355
479
|
|
356
|
-
self.
|
480
|
+
self._assert_spans(
|
357
481
|
[
|
358
482
|
(
|
359
483
|
"GET",
|
360
484
|
(expected_status, "ClientConnectorError"),
|
361
485
|
{
|
362
|
-
|
363
|
-
|
486
|
+
HTTP_METHOD: "GET",
|
487
|
+
HTTP_URL: url,
|
364
488
|
},
|
365
489
|
)
|
366
490
|
]
|
@@ -379,18 +503,29 @@ class TestAioHttpIntegration(TestBase):
|
|
379
503
|
span = self.memory_exporter.get_finished_spans()[0]
|
380
504
|
self.assertEqual(len(span.events), 1)
|
381
505
|
self.assertEqual(span.events[0].name, "exception")
|
382
|
-
self.
|
506
|
+
self._assert_spans(
|
383
507
|
[
|
384
508
|
(
|
385
509
|
"GET",
|
386
510
|
(StatusCode.ERROR, "ServerDisconnectedError"),
|
387
511
|
{
|
388
|
-
|
389
|
-
|
512
|
+
HTTP_METHOD: "GET",
|
513
|
+
HTTP_URL: f"http://{host}:{port}/test",
|
390
514
|
},
|
391
515
|
)
|
392
516
|
]
|
393
517
|
)
|
518
|
+
metrics = self._assert_metrics(1)
|
519
|
+
duration_data_point = metrics[0].data.data_points[0]
|
520
|
+
self.assertEqual(
|
521
|
+
dict(duration_data_point.attributes),
|
522
|
+
{
|
523
|
+
HTTP_METHOD: "GET",
|
524
|
+
HTTP_HOST: host,
|
525
|
+
NET_PEER_NAME: host,
|
526
|
+
NET_PEER_PORT: port,
|
527
|
+
},
|
528
|
+
)
|
394
529
|
|
395
530
|
def test_basic_exception_new_semconv(self):
|
396
531
|
async def request_handler(request):
|
@@ -406,7 +541,7 @@ class TestAioHttpIntegration(TestBase):
|
|
406
541
|
span = self.memory_exporter.get_finished_spans()[0]
|
407
542
|
self.assertEqual(len(span.events), 1)
|
408
543
|
self.assertEqual(span.events[0].name, "exception")
|
409
|
-
self.
|
544
|
+
self._assert_spans(
|
410
545
|
[
|
411
546
|
(
|
412
547
|
"GET",
|
@@ -415,10 +550,23 @@ class TestAioHttpIntegration(TestBase):
|
|
415
550
|
HTTP_REQUEST_METHOD: "GET",
|
416
551
|
URL_FULL: f"http://{host}:{port}/test",
|
417
552
|
ERROR_TYPE: "ServerDisconnectedError",
|
553
|
+
SERVER_ADDRESS: host,
|
554
|
+
SERVER_PORT: port,
|
418
555
|
},
|
419
556
|
)
|
420
557
|
]
|
421
558
|
)
|
559
|
+
metrics = self._assert_metrics(1)
|
560
|
+
duration_data_point = metrics[0].data.data_points[0]
|
561
|
+
self.assertEqual(
|
562
|
+
dict(duration_data_point.attributes),
|
563
|
+
{
|
564
|
+
HTTP_REQUEST_METHOD: "GET",
|
565
|
+
ERROR_TYPE: "ServerDisconnectedError",
|
566
|
+
SERVER_ADDRESS: host,
|
567
|
+
SERVER_PORT: port,
|
568
|
+
},
|
569
|
+
)
|
422
570
|
|
423
571
|
def test_basic_exception_both_semconv(self):
|
424
572
|
async def request_handler(request):
|
@@ -434,7 +582,7 @@ class TestAioHttpIntegration(TestBase):
|
|
434
582
|
span = self.memory_exporter.get_finished_spans()[0]
|
435
583
|
self.assertEqual(len(span.events), 1)
|
436
584
|
self.assertEqual(span.events[0].name, "exception")
|
437
|
-
self.
|
585
|
+
self._assert_spans(
|
438
586
|
[
|
439
587
|
(
|
440
588
|
"GET",
|
@@ -443,12 +591,37 @@ class TestAioHttpIntegration(TestBase):
|
|
443
591
|
HTTP_REQUEST_METHOD: "GET",
|
444
592
|
URL_FULL: f"http://{host}:{port}/test",
|
445
593
|
ERROR_TYPE: "ServerDisconnectedError",
|
446
|
-
|
447
|
-
|
594
|
+
HTTP_METHOD: "GET",
|
595
|
+
HTTP_URL: f"http://{host}:{port}/test",
|
596
|
+
HTTP_HOST: host,
|
597
|
+
SERVER_ADDRESS: host,
|
598
|
+
SERVER_PORT: port,
|
599
|
+
NET_PEER_PORT: port,
|
448
600
|
},
|
449
601
|
)
|
450
602
|
]
|
451
603
|
)
|
604
|
+
metrics = self._assert_metrics(2)
|
605
|
+
duration_data_point = metrics[0].data.data_points[0]
|
606
|
+
self.assertEqual(
|
607
|
+
dict(duration_data_point.attributes),
|
608
|
+
{
|
609
|
+
HTTP_METHOD: "GET",
|
610
|
+
HTTP_HOST: host,
|
611
|
+
NET_PEER_NAME: host,
|
612
|
+
NET_PEER_PORT: port,
|
613
|
+
},
|
614
|
+
)
|
615
|
+
duration_data_point = metrics[1].data.data_points[0]
|
616
|
+
self.assertEqual(
|
617
|
+
dict(duration_data_point.attributes),
|
618
|
+
{
|
619
|
+
HTTP_REQUEST_METHOD: "GET",
|
620
|
+
ERROR_TYPE: "ServerDisconnectedError",
|
621
|
+
SERVER_ADDRESS: host,
|
622
|
+
SERVER_PORT: port,
|
623
|
+
},
|
624
|
+
)
|
452
625
|
|
453
626
|
def test_timeout(self):
|
454
627
|
async def request_handler(request):
|
@@ -463,14 +636,14 @@ class TestAioHttpIntegration(TestBase):
|
|
463
636
|
timeout=aiohttp.ClientTimeout(sock_read=0.01),
|
464
637
|
)
|
465
638
|
|
466
|
-
self.
|
639
|
+
self._assert_spans(
|
467
640
|
[
|
468
641
|
(
|
469
642
|
"GET",
|
470
643
|
(StatusCode.ERROR, "SocketTimeoutError"),
|
471
644
|
{
|
472
|
-
|
473
|
-
|
645
|
+
HTTP_METHOD: "GET",
|
646
|
+
HTTP_URL: f"http://{host}:{port}/test_timeout",
|
474
647
|
},
|
475
648
|
)
|
476
649
|
]
|
@@ -490,14 +663,14 @@ class TestAioHttpIntegration(TestBase):
|
|
490
663
|
max_redirects=2,
|
491
664
|
)
|
492
665
|
|
493
|
-
self.
|
666
|
+
self._assert_spans(
|
494
667
|
[
|
495
668
|
(
|
496
669
|
"GET",
|
497
670
|
(StatusCode.ERROR, "TooManyRedirects"),
|
498
671
|
{
|
499
|
-
|
500
|
-
|
672
|
+
HTTP_METHOD: "GET",
|
673
|
+
HTTP_URL: f"http://{host}:{port}/test_too_many_redirects",
|
501
674
|
},
|
502
675
|
)
|
503
676
|
]
|
@@ -526,17 +699,15 @@ class TestAioHttpIntegration(TestBase):
|
|
526
699
|
loop = asyncio.get_event_loop()
|
527
700
|
loop.run_until_complete(do_request(url))
|
528
701
|
|
529
|
-
self.
|
702
|
+
self._assert_spans(
|
530
703
|
[
|
531
704
|
(
|
532
705
|
"HTTP",
|
533
706
|
(StatusCode.ERROR, None),
|
534
707
|
{
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
HTTPStatus.METHOD_NOT_ALLOWED
|
539
|
-
),
|
708
|
+
HTTP_METHOD: "_OTHER",
|
709
|
+
HTTP_URL: url,
|
710
|
+
HTTP_STATUS_CODE: int(HTTPStatus.METHOD_NOT_ALLOWED),
|
540
711
|
},
|
541
712
|
)
|
542
713
|
]
|
@@ -570,7 +741,7 @@ class TestAioHttpIntegration(TestBase):
|
|
570
741
|
loop = asyncio.get_event_loop()
|
571
742
|
loop.run_until_complete(do_request(url))
|
572
743
|
|
573
|
-
self.
|
744
|
+
self._assert_spans(
|
574
745
|
[
|
575
746
|
(
|
576
747
|
"HTTP",
|
@@ -583,6 +754,8 @@ class TestAioHttpIntegration(TestBase):
|
|
583
754
|
),
|
584
755
|
HTTP_REQUEST_METHOD_ORIGINAL: "NONSTANDARD",
|
585
756
|
ERROR_TYPE: "405",
|
757
|
+
SERVER_ADDRESS: "localhost",
|
758
|
+
SERVER_PORT: 5000,
|
586
759
|
},
|
587
760
|
)
|
588
761
|
]
|
@@ -613,17 +786,15 @@ class TestAioHttpIntegration(TestBase):
|
|
613
786
|
loop = asyncio.get_event_loop()
|
614
787
|
loop.run_until_complete(do_request(url))
|
615
788
|
|
616
|
-
self.
|
789
|
+
self._assert_spans(
|
617
790
|
[
|
618
791
|
(
|
619
792
|
"GET",
|
620
793
|
(StatusCode.UNSET, None),
|
621
794
|
{
|
622
|
-
|
623
|
-
|
624
|
-
|
625
|
-
),
|
626
|
-
SpanAttributes.HTTP_STATUS_CODE: int(HTTPStatus.OK),
|
795
|
+
HTTP_METHOD: "GET",
|
796
|
+
HTTP_URL: ("http://localhost:5000/status/200"),
|
797
|
+
HTTP_STATUS_CODE: int(HTTPStatus.OK),
|
627
798
|
},
|
628
799
|
)
|
629
800
|
]
|
@@ -656,7 +827,7 @@ class TestAioHttpClientInstrumentor(TestBase):
|
|
656
827
|
|
657
828
|
return default_request
|
658
829
|
|
659
|
-
def
|
830
|
+
def _assert_spans(self, num_spans: int):
|
660
831
|
finished_spans = self.memory_exporter.get_finished_spans()
|
661
832
|
self.assertEqual(num_spans, len(finished_spans))
|
662
833
|
if num_spans == 0:
|
@@ -665,18 +836,36 @@ class TestAioHttpClientInstrumentor(TestBase):
|
|
665
836
|
return finished_spans[0]
|
666
837
|
return finished_spans
|
667
838
|
|
839
|
+
def _assert_metrics(self, num_metrics: int = 1):
|
840
|
+
metrics = self.get_sorted_metrics()
|
841
|
+
self.assertEqual(len(metrics), num_metrics)
|
842
|
+
return metrics
|
843
|
+
|
668
844
|
def test_instrument(self):
|
669
845
|
host, port = run_with_test_server(
|
670
846
|
self.get_default_request(), self.URL, self.default_handler
|
671
847
|
)
|
672
|
-
span = self.
|
848
|
+
span = self._assert_spans(1)
|
673
849
|
self.assertEqual("GET", span.name)
|
674
|
-
self.assertEqual("GET", span.attributes[
|
850
|
+
self.assertEqual("GET", span.attributes[HTTP_METHOD])
|
675
851
|
self.assertEqual(
|
676
852
|
f"http://{host}:{port}/test-path",
|
677
|
-
span.attributes[
|
853
|
+
span.attributes[HTTP_URL],
|
854
|
+
)
|
855
|
+
self.assertEqual(200, span.attributes[HTTP_STATUS_CODE])
|
856
|
+
metrics = self._assert_metrics(1)
|
857
|
+
duration_data_point = metrics[0].data.data_points[0]
|
858
|
+
self.assertEqual(duration_data_point.count, 1)
|
859
|
+
self.assertEqual(
|
860
|
+
dict(duration_data_point.attributes),
|
861
|
+
{
|
862
|
+
HTTP_HOST: host,
|
863
|
+
HTTP_STATUS_CODE: 200,
|
864
|
+
HTTP_METHOD: "GET",
|
865
|
+
NET_PEER_NAME: host,
|
866
|
+
NET_PEER_PORT: port,
|
867
|
+
},
|
678
868
|
)
|
679
|
-
self.assertEqual(200, span.attributes[SpanAttributes.HTTP_STATUS_CODE])
|
680
869
|
|
681
870
|
def test_instrument_new_semconv(self):
|
682
871
|
AioHttpClientInstrumentor().uninstrument()
|
@@ -687,7 +876,7 @@ class TestAioHttpClientInstrumentor(TestBase):
|
|
687
876
|
host, port = run_with_test_server(
|
688
877
|
self.get_default_request(), self.URL, self.default_handler
|
689
878
|
)
|
690
|
-
span = self.
|
879
|
+
span = self._assert_spans(1)
|
691
880
|
self.assertEqual("GET", span.name)
|
692
881
|
self.assertEqual("GET", span.attributes[HTTP_REQUEST_METHOD])
|
693
882
|
self.assertEqual(
|
@@ -695,6 +884,18 @@ class TestAioHttpClientInstrumentor(TestBase):
|
|
695
884
|
span.attributes[URL_FULL],
|
696
885
|
)
|
697
886
|
self.assertEqual(200, span.attributes[HTTP_RESPONSE_STATUS_CODE])
|
887
|
+
metrics = self._assert_metrics(1)
|
888
|
+
duration_data_point = metrics[0].data.data_points[0]
|
889
|
+
self.assertEqual(duration_data_point.count, 1)
|
890
|
+
self.assertEqual(
|
891
|
+
dict(duration_data_point.attributes),
|
892
|
+
{
|
893
|
+
HTTP_RESPONSE_STATUS_CODE: 200,
|
894
|
+
HTTP_REQUEST_METHOD: "GET",
|
895
|
+
SERVER_ADDRESS: host,
|
896
|
+
SERVER_PORT: port,
|
897
|
+
},
|
898
|
+
)
|
698
899
|
|
699
900
|
def test_instrument_both_semconv(self):
|
700
901
|
AioHttpClientInstrumentor().uninstrument()
|
@@ -706,17 +907,47 @@ class TestAioHttpClientInstrumentor(TestBase):
|
|
706
907
|
self.get_default_request(), self.URL, self.default_handler
|
707
908
|
)
|
708
909
|
url = f"http://{host}:{port}/test-path"
|
709
|
-
|
710
|
-
HTTP_REQUEST_METHOD: "GET",
|
711
|
-
SpanAttributes.HTTP_METHOD: "GET",
|
712
|
-
URL_FULL: url,
|
713
|
-
SpanAttributes.HTTP_URL: url,
|
714
|
-
HTTP_RESPONSE_STATUS_CODE: 200,
|
715
|
-
SpanAttributes.HTTP_STATUS_CODE: 200,
|
716
|
-
}
|
717
|
-
span = self.assert_spans(1)
|
910
|
+
span = self._assert_spans(1)
|
718
911
|
self.assertEqual("GET", span.name)
|
719
|
-
self.assertEqual(
|
912
|
+
self.assertEqual(
|
913
|
+
dict(span.attributes),
|
914
|
+
{
|
915
|
+
HTTP_REQUEST_METHOD: "GET",
|
916
|
+
HTTP_METHOD: "GET",
|
917
|
+
HTTP_HOST: host,
|
918
|
+
URL_FULL: url,
|
919
|
+
HTTP_URL: url,
|
920
|
+
HTTP_RESPONSE_STATUS_CODE: 200,
|
921
|
+
HTTP_STATUS_CODE: 200,
|
922
|
+
SERVER_ADDRESS: host,
|
923
|
+
SERVER_PORT: port,
|
924
|
+
NET_PEER_PORT: port,
|
925
|
+
},
|
926
|
+
)
|
927
|
+
metrics = self._assert_metrics(2)
|
928
|
+
duration_data_point = metrics[0].data.data_points[0]
|
929
|
+
self.assertEqual(duration_data_point.count, 1)
|
930
|
+
self.assertEqual(
|
931
|
+
dict(duration_data_point.attributes),
|
932
|
+
{
|
933
|
+
HTTP_STATUS_CODE: 200,
|
934
|
+
HTTP_METHOD: "GET",
|
935
|
+
HTTP_HOST: host,
|
936
|
+
NET_PEER_NAME: host,
|
937
|
+
NET_PEER_PORT: port,
|
938
|
+
},
|
939
|
+
)
|
940
|
+
duration_data_point = metrics[1].data.data_points[0]
|
941
|
+
self.assertEqual(duration_data_point.count, 1)
|
942
|
+
self.assertEqual(
|
943
|
+
dict(duration_data_point.attributes),
|
944
|
+
{
|
945
|
+
HTTP_RESPONSE_STATUS_CODE: 200,
|
946
|
+
HTTP_REQUEST_METHOD: "GET",
|
947
|
+
SERVER_ADDRESS: host,
|
948
|
+
SERVER_PORT: port,
|
949
|
+
},
|
950
|
+
)
|
720
951
|
|
721
952
|
def test_instrument_with_custom_trace_config(self):
|
722
953
|
trace_config = aiohttp.TraceConfig()
|
@@ -733,7 +964,7 @@ class TestAioHttpClientInstrumentor(TestBase):
|
|
733
964
|
await session.get(TestAioHttpClientInstrumentor.URL)
|
734
965
|
|
735
966
|
run_with_test_server(make_request, self.URL, self.default_handler)
|
736
|
-
self.
|
967
|
+
self._assert_spans(1)
|
737
968
|
|
738
969
|
def test_every_request_by_new_session_creates_one_span(self):
|
739
970
|
async def make_request(server: aiohttp.test_utils.TestServer):
|
@@ -747,7 +978,7 @@ class TestAioHttpClientInstrumentor(TestBase):
|
|
747
978
|
run_with_test_server(
|
748
979
|
make_request, self.URL, self.default_handler
|
749
980
|
)
|
750
|
-
self.
|
981
|
+
self._assert_spans(1)
|
751
982
|
|
752
983
|
def test_instrument_with_existing_trace_config(self):
|
753
984
|
trace_config = aiohttp.TraceConfig()
|
@@ -764,7 +995,7 @@ class TestAioHttpClientInstrumentor(TestBase):
|
|
764
995
|
await session.get(TestAioHttpClientInstrumentor.URL)
|
765
996
|
|
766
997
|
run_with_test_server(create_session, self.URL, self.default_handler)
|
767
|
-
self.
|
998
|
+
self._assert_spans(1)
|
768
999
|
|
769
1000
|
def test_no_op_tracer_provider(self):
|
770
1001
|
AioHttpClientInstrumentor().uninstrument()
|
@@ -784,13 +1015,13 @@ class TestAioHttpClientInstrumentor(TestBase):
|
|
784
1015
|
self.get_default_request(), self.URL, self.default_handler
|
785
1016
|
)
|
786
1017
|
|
787
|
-
self.
|
1018
|
+
self._assert_spans(0)
|
788
1019
|
|
789
1020
|
AioHttpClientInstrumentor().instrument()
|
790
1021
|
run_with_test_server(
|
791
1022
|
self.get_default_request(), self.URL, self.default_handler
|
792
1023
|
)
|
793
|
-
self.
|
1024
|
+
self._assert_spans(1)
|
794
1025
|
|
795
1026
|
def test_uninstrument_session(self):
|
796
1027
|
async def uninstrument_request(server: aiohttp.test_utils.TestServer):
|
@@ -802,19 +1033,19 @@ class TestAioHttpClientInstrumentor(TestBase):
|
|
802
1033
|
run_with_test_server(
|
803
1034
|
uninstrument_request, self.URL, self.default_handler
|
804
1035
|
)
|
805
|
-
self.
|
1036
|
+
self._assert_spans(0)
|
806
1037
|
|
807
1038
|
run_with_test_server(
|
808
1039
|
self.get_default_request(), self.URL, self.default_handler
|
809
1040
|
)
|
810
|
-
self.
|
1041
|
+
self._assert_spans(1)
|
811
1042
|
|
812
1043
|
def test_suppress_instrumentation(self):
|
813
1044
|
with suppress_instrumentation():
|
814
1045
|
run_with_test_server(
|
815
1046
|
self.get_default_request(), self.URL, self.default_handler
|
816
1047
|
)
|
817
|
-
self.
|
1048
|
+
self._assert_spans(0)
|
818
1049
|
|
819
1050
|
@staticmethod
|
820
1051
|
async def suppressed_request(server: aiohttp.test_utils.TestServer):
|
@@ -826,7 +1057,7 @@ class TestAioHttpClientInstrumentor(TestBase):
|
|
826
1057
|
run_with_test_server(
|
827
1058
|
self.suppressed_request, self.URL, self.default_handler
|
828
1059
|
)
|
829
|
-
self.
|
1060
|
+
self._assert_spans(0)
|
830
1061
|
|
831
1062
|
def test_suppress_instrumentation_with_server_exception(self):
|
832
1063
|
# pylint:disable=unused-argument
|
@@ -836,7 +1067,7 @@ class TestAioHttpClientInstrumentor(TestBase):
|
|
836
1067
|
run_with_test_server(
|
837
1068
|
self.suppressed_request, self.URL, raising_handler
|
838
1069
|
)
|
839
|
-
self.
|
1070
|
+
self._assert_spans(0)
|
840
1071
|
|
841
1072
|
def test_url_filter(self):
|
842
1073
|
def strip_query_params(url: yarl.URL) -> str:
|
@@ -849,10 +1080,10 @@ class TestAioHttpClientInstrumentor(TestBase):
|
|
849
1080
|
host, port = run_with_test_server(
|
850
1081
|
self.get_default_request(url), url, self.default_handler
|
851
1082
|
)
|
852
|
-
span = self.
|
1083
|
+
span = self._assert_spans(1)
|
853
1084
|
self.assertEqual(
|
854
1085
|
f"http://{host}:{port}/test-path",
|
855
|
-
span.attributes[
|
1086
|
+
span.attributes[HTTP_URL],
|
856
1087
|
)
|
857
1088
|
|
858
1089
|
def test_hooks(self):
|
@@ -877,7 +1108,7 @@ class TestAioHttpClientInstrumentor(TestBase):
|
|
877
1108
|
run_with_test_server(
|
878
1109
|
self.get_default_request(url), url, self.default_handler
|
879
1110
|
)
|
880
|
-
span = self.
|
1111
|
+
span = self._assert_spans(1)
|
881
1112
|
self.assertEqual("GET - /test-path", span.name)
|
882
1113
|
self.assertIn("response_hook_attr", span.attributes)
|
883
1114
|
self.assertEqual(span.attributes["response_hook_attr"], "value")
|
File without changes
|
File without changes
|
File without changes
|