opentelemetry-instrumentation-aiohttp-server 0.47b0.dev0__py3-none-any.whl

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.
@@ -0,0 +1,267 @@
1
+ # Copyright 2020, OpenTelemetry Authors
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ import urllib
16
+ from timeit import default_timer
17
+ from typing import Dict, List, Tuple, Union
18
+
19
+ from aiohttp import web
20
+ from multidict import CIMultiDictProxy
21
+
22
+ from opentelemetry import metrics, trace
23
+ from opentelemetry.instrumentation.aiohttp_server.package import _instruments
24
+ from opentelemetry.instrumentation.aiohttp_server.version import __version__
25
+ from opentelemetry.instrumentation.instrumentor import BaseInstrumentor
26
+ from opentelemetry.instrumentation.utils import (
27
+ http_status_to_status_code,
28
+ is_http_instrumentation_enabled,
29
+ )
30
+ from opentelemetry.propagate import extract
31
+ from opentelemetry.propagators.textmap import Getter
32
+ from opentelemetry.semconv.metrics import MetricInstruments
33
+ from opentelemetry.semconv.trace import SpanAttributes
34
+ from opentelemetry.trace.status import Status, StatusCode
35
+ from opentelemetry.util.http import get_excluded_urls, remove_url_credentials
36
+
37
+ _duration_attrs = [
38
+ SpanAttributes.HTTP_METHOD,
39
+ SpanAttributes.HTTP_HOST,
40
+ SpanAttributes.HTTP_SCHEME,
41
+ SpanAttributes.HTTP_STATUS_CODE,
42
+ SpanAttributes.HTTP_FLAVOR,
43
+ SpanAttributes.HTTP_SERVER_NAME,
44
+ SpanAttributes.NET_HOST_NAME,
45
+ SpanAttributes.NET_HOST_PORT,
46
+ SpanAttributes.HTTP_ROUTE,
47
+ ]
48
+
49
+ _active_requests_count_attrs = [
50
+ SpanAttributes.HTTP_METHOD,
51
+ SpanAttributes.HTTP_HOST,
52
+ SpanAttributes.HTTP_SCHEME,
53
+ SpanAttributes.HTTP_FLAVOR,
54
+ SpanAttributes.HTTP_SERVER_NAME,
55
+ ]
56
+
57
+ tracer = trace.get_tracer(__name__)
58
+ meter = metrics.get_meter(__name__, __version__)
59
+ _excluded_urls = get_excluded_urls("AIOHTTP_SERVER")
60
+
61
+
62
+ def _parse_duration_attrs(req_attrs):
63
+ duration_attrs = {}
64
+ for attr_key in _duration_attrs:
65
+ if req_attrs.get(attr_key) is not None:
66
+ duration_attrs[attr_key] = req_attrs[attr_key]
67
+ return duration_attrs
68
+
69
+
70
+ def _parse_active_request_count_attrs(req_attrs):
71
+ active_requests_count_attrs = {}
72
+ for attr_key in _active_requests_count_attrs:
73
+ if req_attrs.get(attr_key) is not None:
74
+ active_requests_count_attrs[attr_key] = req_attrs[attr_key]
75
+ return active_requests_count_attrs
76
+
77
+
78
+ def get_default_span_details(request: web.Request) -> Tuple[str, dict]:
79
+ """Default implementation for get_default_span_details
80
+ Args:
81
+ request: the request object itself.
82
+ Returns:
83
+ a tuple of the span name, and any attributes to attach to the span.
84
+ """
85
+ span_name = request.path.strip() or f"HTTP {request.method}"
86
+ return span_name, {}
87
+
88
+
89
+ def _get_view_func(request: web.Request) -> str:
90
+ """Returns the name of the request handler.
91
+ Args:
92
+ request: the request object itself.
93
+ Returns:
94
+ a string containing the name of the handler function
95
+ """
96
+ try:
97
+ return request.match_info.handler.__name__
98
+ except AttributeError:
99
+ return "unknown"
100
+
101
+
102
+ def collect_request_attributes(request: web.Request) -> Dict:
103
+ """Collects HTTP request attributes from the ASGI scope and returns a
104
+ dictionary to be used as span creation attributes."""
105
+
106
+ server_host, port, http_url = (
107
+ request.url.host,
108
+ request.url.port,
109
+ str(request.url),
110
+ )
111
+ query_string = request.query_string
112
+ if query_string and http_url:
113
+ if isinstance(query_string, bytes):
114
+ query_string = query_string.decode("utf8")
115
+ http_url += "?" + urllib.parse.unquote(query_string)
116
+
117
+ result = {
118
+ SpanAttributes.HTTP_SCHEME: request.scheme,
119
+ SpanAttributes.HTTP_HOST: server_host,
120
+ SpanAttributes.NET_HOST_PORT: port,
121
+ SpanAttributes.HTTP_ROUTE: _get_view_func(request),
122
+ SpanAttributes.HTTP_FLAVOR: f"{request.version.major}.{request.version.minor}",
123
+ SpanAttributes.HTTP_TARGET: request.path,
124
+ SpanAttributes.HTTP_URL: remove_url_credentials(http_url),
125
+ }
126
+
127
+ http_method = request.method
128
+ if http_method:
129
+ result[SpanAttributes.HTTP_METHOD] = http_method
130
+
131
+ http_host_value_list = (
132
+ [request.host] if not isinstance(request.host, list) else request.host
133
+ )
134
+ if http_host_value_list:
135
+ result[SpanAttributes.HTTP_SERVER_NAME] = ",".join(
136
+ http_host_value_list
137
+ )
138
+ http_user_agent = request.headers.get("user-agent")
139
+ if http_user_agent:
140
+ result[SpanAttributes.HTTP_USER_AGENT] = http_user_agent
141
+
142
+ # remove None values
143
+ result = {k: v for k, v in result.items() if v is not None}
144
+
145
+ return result
146
+
147
+
148
+ def set_status_code(span, status_code: int) -> None:
149
+ """Adds HTTP response attributes to span using the status_code argument."""
150
+
151
+ try:
152
+ status_code = int(status_code)
153
+ except ValueError:
154
+ span.set_status(
155
+ Status(
156
+ StatusCode.ERROR,
157
+ "Non-integer HTTP status: " + repr(status_code),
158
+ )
159
+ )
160
+ else:
161
+ span.set_attribute(SpanAttributes.HTTP_STATUS_CODE, status_code)
162
+ span.set_status(
163
+ Status(http_status_to_status_code(status_code, server_span=True))
164
+ )
165
+
166
+
167
+ class AiohttpGetter(Getter):
168
+ """Extract current trace from headers"""
169
+
170
+ def get(self, carrier, key: str) -> Union[List, None]:
171
+ """Getter implementation to retrieve an HTTP header value from the ASGI
172
+ scope.
173
+
174
+ Args:
175
+ carrier: ASGI scope object
176
+ key: header name in scope
177
+ Returns:
178
+ A list of all header values matching the key, or None if the key
179
+ does not match any header.
180
+ """
181
+ headers: CIMultiDictProxy = carrier.headers
182
+ if not headers:
183
+ return None
184
+ return headers.getall(key, None)
185
+
186
+ def keys(self, carrier: Dict) -> List:
187
+ return list(carrier.keys())
188
+
189
+
190
+ getter = AiohttpGetter()
191
+
192
+
193
+ @web.middleware
194
+ async def middleware(request, handler):
195
+ """Middleware for aiohttp implementing tracing logic"""
196
+ if not is_http_instrumentation_enabled() or _excluded_urls.url_disabled(
197
+ request.url.path
198
+ ):
199
+ return await handler(request)
200
+
201
+ span_name, additional_attributes = get_default_span_details(request)
202
+
203
+ req_attrs = collect_request_attributes(request)
204
+ duration_attrs = _parse_duration_attrs(req_attrs)
205
+ active_requests_count_attrs = _parse_active_request_count_attrs(req_attrs)
206
+
207
+ duration_histogram = meter.create_histogram(
208
+ name=MetricInstruments.HTTP_SERVER_DURATION,
209
+ unit="ms",
210
+ description="Duration of HTTP client requests.",
211
+ )
212
+
213
+ active_requests_counter = meter.create_up_down_counter(
214
+ name=MetricInstruments.HTTP_SERVER_ACTIVE_REQUESTS,
215
+ unit="requests",
216
+ description="measures the number of concurrent HTTP requests those are currently in flight",
217
+ )
218
+
219
+ with tracer.start_as_current_span(
220
+ span_name,
221
+ context=extract(request, getter=getter),
222
+ kind=trace.SpanKind.SERVER,
223
+ ) as span:
224
+ attributes = collect_request_attributes(request)
225
+ attributes.update(additional_attributes)
226
+ span.set_attributes(attributes)
227
+ start = default_timer()
228
+ active_requests_counter.add(1, active_requests_count_attrs)
229
+ try:
230
+ resp = await handler(request)
231
+ set_status_code(span, resp.status)
232
+ except web.HTTPException as ex:
233
+ set_status_code(span, ex.status_code)
234
+ raise
235
+ finally:
236
+ duration = max((default_timer() - start) * 1000, 0)
237
+ duration_histogram.record(duration, duration_attrs)
238
+ active_requests_counter.add(-1, active_requests_count_attrs)
239
+ return resp
240
+
241
+
242
+ class _InstrumentedApplication(web.Application):
243
+ """Insert tracing middleware"""
244
+
245
+ def __init__(self, *args, **kwargs):
246
+ middlewares = kwargs.pop("middlewares", [])
247
+ middlewares.insert(0, middleware)
248
+ kwargs["middlewares"] = middlewares
249
+ super().__init__(*args, **kwargs)
250
+
251
+
252
+ class AioHttpServerInstrumentor(BaseInstrumentor):
253
+ # pylint: disable=protected-access,attribute-defined-outside-init
254
+ """An instrumentor for aiohttp.web.Application
255
+
256
+ See `BaseInstrumentor`
257
+ """
258
+
259
+ def _instrument(self, **kwargs):
260
+ self._original_app = web.Application
261
+ setattr(web, "Application", _InstrumentedApplication)
262
+
263
+ def _uninstrument(self, **kwargs):
264
+ setattr(web, "Application", self._original_app)
265
+
266
+ def instrumentation_dependencies(self):
267
+ return _instruments
@@ -0,0 +1,16 @@
1
+ # Copyright The OpenTelemetry Authors
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+
16
+ _instruments = ("aiohttp ~= 3.0",)
@@ -0,0 +1,15 @@
1
+ # Copyright The OpenTelemetry Authors
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ __version__ = "0.47b0.dev"
@@ -0,0 +1,51 @@
1
+ Metadata-Version: 2.3
2
+ Name: opentelemetry-instrumentation-aiohttp-server
3
+ Version: 0.47b0.dev0
4
+ Summary: Aiohttp server instrumentation for OpenTelemetry
5
+ Project-URL: Homepage, https://github.com/open-telemetry/opentelemetry-python-contrib/tree/main/instrumentation/opentelemetry-instrumentation-aiohttp-server
6
+ Author-email: OpenTelemetry Authors <cncf-opentelemetry-contributors@lists.cncf.io>
7
+ License-Expression: Apache-2.0
8
+ Classifier: Development Status :: 4 - Beta
9
+ Classifier: Intended Audience :: Developers
10
+ Classifier: License :: OSI Approved :: Apache Software License
11
+ Classifier: Programming Language :: Python
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.8
14
+ Classifier: Programming Language :: Python :: 3.9
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Requires-Python: >=3.8
19
+ Requires-Dist: opentelemetry-api~=1.12
20
+ Requires-Dist: opentelemetry-instrumentation==0.47b0.dev
21
+ Requires-Dist: opentelemetry-semantic-conventions==0.47b0.dev
22
+ Requires-Dist: opentelemetry-util-http==0.47b0.dev
23
+ Requires-Dist: wrapt<2.0.0,>=1.0.0
24
+ Provides-Extra: instruments
25
+ Requires-Dist: aiohttp~=3.0; extra == 'instruments'
26
+ Description-Content-Type: text/x-rst
27
+
28
+ OpenTelemetry aiohttp server Integration
29
+ ========================================
30
+
31
+ |pypi|
32
+
33
+ .. |pypi| image:: https://badge.fury.io/py/opentelemetry-instrumentation-aiohttp-client.svg
34
+ :target: https://pypi.org/project/opentelemetry-instrumentation-aiohttp-client/
35
+
36
+ This library allows tracing HTTP requests made by the
37
+ `aiohttp server <https://docs.aiohttp.org/en/stable/server.html>`_ library.
38
+
39
+ Installation
40
+ ------------
41
+
42
+ ::
43
+
44
+ pip install opentelemetry-instrumentation-aiohttp-server
45
+
46
+ References
47
+ ----------
48
+
49
+ * `OpenTelemetry Project <https://opentelemetry.io/>`_
50
+ * `aiohttp client Tracing <https://docs.aiohttp.org/en/stable/tracing_reference.html>`_
51
+ * `OpenTelemetry Python Examples <https://github.com/open-telemetry/opentelemetry-python/tree/main/docs/examples>`_
@@ -0,0 +1,7 @@
1
+ opentelemetry/instrumentation/aiohttp_server/__init__.py,sha256=lDfmu79TfXdwUpc5E1UIJ8yxIb-daxctBEFsFzfk5-k,9189
2
+ opentelemetry/instrumentation/aiohttp_server/package.py,sha256=PWIQEP9IrmelAf66JyWZz_YXvpsjPV-UoQ0uiNcOyjk,637
3
+ opentelemetry/instrumentation/aiohttp_server/version.py,sha256=WyGExSitjpBaXqzM8MYyfzJMJrmgfR2mqqz0baX2nkc,627
4
+ opentelemetry_instrumentation_aiohttp_server-0.47b0.dev0.dist-info/METADATA,sha256=q8GSXDeuL7Meci1AuXV9XHf9UX2mMtu7fjahllMhmr0,2077
5
+ opentelemetry_instrumentation_aiohttp_server-0.47b0.dev0.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
6
+ opentelemetry_instrumentation_aiohttp_server-0.47b0.dev0.dist-info/entry_points.txt,sha256=ZEzTb_GxZ6t2mRmSKFNsbQxtqpLKyTobjeNQtNEgVj8,117
7
+ opentelemetry_instrumentation_aiohttp_server-0.47b0.dev0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.25.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,2 @@
1
+ [opentelemetry_instrumentor]
2
+ aiohttp-server = opentelemetry.instrumentation.aiohttp_server:AioHttpServerInstrumentor