We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
opentelemetry-instrumentation-aiohttp-client


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `instrumentation/opentelemetry-instrumentation-aiohttp-client/src/opentelemetry/instrumentation/aiohttp_client/__init__.py`
Content:
```
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 """
16 The opentelemetry-instrumentation-aiohttp-client package allows tracing HTTP
17 requests made by the aiohttp client library.
18 
19 Usage
20 -----
21 Explicitly instrumenting a single client session:
22 
23 .. code:: python
24 
25     import aiohttp
26     from opentelemetry.instrumentation.aiohttp_client import create_trace_config
27     import yarl
28 
29     def strip_query_params(url: yarl.URL) -> str:
30         return str(url.with_query(None))
31 
32     async with aiohttp.ClientSession(trace_configs=[create_trace_config(
33             # Remove all query params from the URL attribute on the span.
34             url_filter=strip_query_params,
35     )]) as session:
36         async with session.get(url) as response:
37             await response.text()
38 
39 Instrumenting all client sessions:
40 
41 .. code:: python
42 
43     import aiohttp
44     from opentelemetry.instrumentation.aiohttp_client import (
45         AioHttpClientInstrumentor
46     )
47 
48     # Enable instrumentation
49     AioHttpClientInstrumentor().instrument()
50 
51     # Create a session and make an HTTP get request
52     async with aiohttp.ClientSession() as session:
53         async with session.get(url) as response:
54             await response.text()
55 
56 Configuration
57 -------------
58 
59 Request/Response hooks
60 **********************
61 
62 Utilize request/reponse hooks to execute custom logic to be performed before/after performing a request.
63 
64 .. code-block:: python
65 
66    def request_hook(span: Span, params: aiohttp.TraceRequestStartParams):
67       if span and span.is_recording():
68             span.set_attribute("custom_user_attribute_from_request_hook", "some-value")
69 
70    def response_hook(span: Span, params: typing.Union[
71                 aiohttp.TraceRequestEndParams,
72                 aiohttp.TraceRequestExceptionParams,
73             ]):
74         if span and span.is_recording():
75             span.set_attribute("custom_user_attribute_from_response_hook", "some-value")
76 
77    AioHttpClientInstrumentor().instrument(request_hook=request_hook, response_hook=response_hook)
78 
79 API
80 ---
81 """
82 
83 import types
84 import typing
85 from typing import Collection
86 
87 import aiohttp
88 import wrapt
89 import yarl
90 
91 from opentelemetry import context as context_api
92 from opentelemetry import trace
93 from opentelemetry.instrumentation.aiohttp_client.package import _instruments
94 from opentelemetry.instrumentation.aiohttp_client.version import __version__
95 from opentelemetry.instrumentation.instrumentor import BaseInstrumentor
96 from opentelemetry.instrumentation.utils import (
97     _SUPPRESS_INSTRUMENTATION_KEY,
98     http_status_to_status_code,
99     unwrap,
100 )
101 from opentelemetry.propagate import inject
102 from opentelemetry.semconv.trace import SpanAttributes
103 from opentelemetry.trace import Span, SpanKind, TracerProvider, get_tracer
104 from opentelemetry.trace.status import Status, StatusCode
105 from opentelemetry.util.http import remove_url_credentials
106 
107 _UrlFilterT = typing.Optional[typing.Callable[[yarl.URL], str]]
108 _RequestHookT = typing.Optional[
109     typing.Callable[[Span, aiohttp.TraceRequestStartParams], None]
110 ]
111 _ResponseHookT = typing.Optional[
112     typing.Callable[
113         [
114             Span,
115             typing.Union[
116                 aiohttp.TraceRequestEndParams,
117                 aiohttp.TraceRequestExceptionParams,
118             ],
119         ],
120         None,
121     ]
122 ]
123 
124 
125 def create_trace_config(
126     url_filter: _UrlFilterT = None,
127     request_hook: _RequestHookT = None,
128     response_hook: _ResponseHookT = None,
129     tracer_provider: TracerProvider = None,
130 ) -> aiohttp.TraceConfig:
131     """Create an aiohttp-compatible trace configuration.
132 
133     One span is created for the entire HTTP request, including initial
134     TCP/TLS setup if the connection doesn't exist.
135 
136     By default the span name is set to the HTTP request method.
137 
138     Example usage:
139 
140     .. code:: python
141 
142         import aiohttp
143         from opentelemetry.instrumentation.aiohttp_client import create_trace_config
144 
145         async with aiohttp.ClientSession(trace_configs=[create_trace_config()]) as session:
146             async with session.get(url) as response:
147                 await response.text()
148 
149 
150     :param url_filter: A callback to process the requested URL prior to adding
151         it as a span attribute. This can be useful to remove sensitive data
152         such as API keys or user personal information.
153 
154     :param Callable request_hook: Optional callback that can modify span name and request params.
155     :param Callable response_hook: Optional callback that can modify span name and response params.
156     :param tracer_provider: optional TracerProvider from which to get a Tracer
157 
158     :return: An object suitable for use with :py:class:`aiohttp.ClientSession`.
159     :rtype: :py:class:`aiohttp.TraceConfig`
160     """
161     # `aiohttp.TraceRequestStartParams` resolves to `aiohttp.tracing.TraceRequestStartParams`
162     # which doesn't exist in the aiohttp intersphinx inventory.
163     # Explicitly specify the type for the `request_hook` and `response_hook` param and rtype to work
164     # around this issue.
165 
166     tracer = get_tracer(__name__, __version__, tracer_provider)
167 
168     def _end_trace(trace_config_ctx: types.SimpleNamespace):
169         context_api.detach(trace_config_ctx.token)
170         trace_config_ctx.span.end()
171 
172     async def on_request_start(
173         unused_session: aiohttp.ClientSession,
174         trace_config_ctx: types.SimpleNamespace,
175         params: aiohttp.TraceRequestStartParams,
176     ):
177         if context_api.get_value(_SUPPRESS_INSTRUMENTATION_KEY):
178             trace_config_ctx.span = None
179             return
180 
181         http_method = params.method.upper()
182         request_span_name = f"HTTP {http_method}"
183 
184         trace_config_ctx.span = trace_config_ctx.tracer.start_span(
185             request_span_name,
186             kind=SpanKind.CLIENT,
187         )
188 
189         if callable(request_hook):
190             request_hook(trace_config_ctx.span, params)
191 
192         if trace_config_ctx.span.is_recording():
193             attributes = {
194                 SpanAttributes.HTTP_METHOD: http_method,
195                 SpanAttributes.HTTP_URL: remove_url_credentials(
196                     trace_config_ctx.url_filter(params.url)
197                 )
198                 if callable(trace_config_ctx.url_filter)
199                 else remove_url_credentials(str(params.url)),
200             }
201             for key, value in attributes.items():
202                 trace_config_ctx.span.set_attribute(key, value)
203 
204         trace_config_ctx.token = context_api.attach(
205             trace.set_span_in_context(trace_config_ctx.span)
206         )
207 
208         inject(params.headers)
209 
210     async def on_request_end(
211         unused_session: aiohttp.ClientSession,
212         trace_config_ctx: types.SimpleNamespace,
213         params: aiohttp.TraceRequestEndParams,
214     ):
215         if trace_config_ctx.span is None:
216             return
217 
218         if callable(response_hook):
219             response_hook(trace_config_ctx.span, params)
220 
221         if trace_config_ctx.span.is_recording():
222             trace_config_ctx.span.set_status(
223                 Status(http_status_to_status_code(int(params.response.status)))
224             )
225             trace_config_ctx.span.set_attribute(
226                 SpanAttributes.HTTP_STATUS_CODE, params.response.status
227             )
228         _end_trace(trace_config_ctx)
229 
230     async def on_request_exception(
231         unused_session: aiohttp.ClientSession,
232         trace_config_ctx: types.SimpleNamespace,
233         params: aiohttp.TraceRequestExceptionParams,
234     ):
235         if trace_config_ctx.span is None:
236             return
237 
238         if callable(response_hook):
239             response_hook(trace_config_ctx.span, params)
240 
241         if trace_config_ctx.span.is_recording() and params.exception:
242             trace_config_ctx.span.set_status(Status(StatusCode.ERROR))
243             trace_config_ctx.span.record_exception(params.exception)
244         _end_trace(trace_config_ctx)
245 
246     def _trace_config_ctx_factory(**kwargs):
247         kwargs.setdefault("trace_request_ctx", {})
248         return types.SimpleNamespace(
249             tracer=tracer, url_filter=url_filter, **kwargs
250         )
251 
252     trace_config = aiohttp.TraceConfig(
253         trace_config_ctx_factory=_trace_config_ctx_factory
254     )
255 
256     trace_config.on_request_start.append(on_request_start)
257     trace_config.on_request_end.append(on_request_end)
258     trace_config.on_request_exception.append(on_request_exception)
259 
260     return trace_config
261 
262 
263 def _instrument(
264     tracer_provider: TracerProvider = None,
265     url_filter: _UrlFilterT = None,
266     request_hook: _RequestHookT = None,
267     response_hook: _ResponseHookT = None,
268 ):
269     """Enables tracing of all ClientSessions
270 
271     When a ClientSession gets created a TraceConfig is automatically added to
272     the session's trace_configs.
273     """
274     # pylint:disable=unused-argument
275     def instrumented_init(wrapped, instance, args, kwargs):
276         if context_api.get_value(_SUPPRESS_INSTRUMENTATION_KEY):
277             return wrapped(*args, **kwargs)
278 
279         trace_configs = list(kwargs.get("trace_configs") or ())
280 
281         trace_config = create_trace_config(
282             url_filter=url_filter,
283             request_hook=request_hook,
284             response_hook=response_hook,
285             tracer_provider=tracer_provider,
286         )
287         trace_config._is_instrumented_by_opentelemetry = True
288         trace_configs.append(trace_config)
289 
290         kwargs["trace_configs"] = trace_configs
291         return wrapped(*args, **kwargs)
292 
293     wrapt.wrap_function_wrapper(
294         aiohttp.ClientSession, "__init__", instrumented_init
295     )
296 
297 
298 def _uninstrument():
299     """Disables instrumenting for all newly created ClientSessions"""
300     unwrap(aiohttp.ClientSession, "__init__")
301 
302 
303 def _uninstrument_session(client_session: aiohttp.ClientSession):
304     """Disables instrumentation for the given ClientSession"""
305     # pylint: disable=protected-access
306     trace_configs = client_session._trace_configs
307     client_session._trace_configs = [
308         trace_config
309         for trace_config in trace_configs
310         if not hasattr(trace_config, "_is_instrumented_by_opentelemetry")
311     ]
312 
313 
314 class AioHttpClientInstrumentor(BaseInstrumentor):
315     """An instrumentor for aiohttp client sessions
316 
317     See `BaseInstrumentor`
318     """
319 
320     def instrumentation_dependencies(self) -> Collection[str]:
321         return _instruments
322 
323     def _instrument(self, **kwargs):
324         """Instruments aiohttp ClientSession
325 
326         Args:
327             **kwargs: Optional arguments
328                 ``tracer_provider``: a TracerProvider, defaults to global
329                 ``url_filter``: A callback to process the requested URL prior to adding
330                     it as a span attribute. This can be useful to remove sensitive data
331                     such as API keys or user personal information.
332                 ``request_hook``: An optional callback that is invoked right after a span is created.
333                 ``response_hook``: An optional callback which is invoked right before the span is finished processing a response.
334         """
335         _instrument(
336             tracer_provider=kwargs.get("tracer_provider"),
337             url_filter=kwargs.get("url_filter"),
338             request_hook=kwargs.get("request_hook"),
339             response_hook=kwargs.get("response_hook"),
340         )
341 
342     def _uninstrument(self, **kwargs):
343         _uninstrument()
344 
345     @staticmethod
346     def uninstrument_session(client_session: aiohttp.ClientSession):
347         """Disables instrumentation for the given session"""
348         _uninstrument_session(client_session)
349
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/instrumentation/opentelemetry-instrumentation-aiohttp-client/src/opentelemetry/instrumentation/aiohttp_client/__init__.py b/instrumentation/opentelemetry-instrumentation-aiohttp-client/src/opentelemetry/instrumentation/aiohttp_client/__init__.py
--- a/instrumentation/opentelemetry-instrumentation-aiohttp-client/src/opentelemetry/instrumentation/aiohttp_client/__init__.py
+++ b/instrumentation/opentelemetry-instrumentation-aiohttp-client/src/opentelemetry/instrumentation/aiohttp_client/__init__.py
@@ -180,27 +180,24 @@
 
         http_method = params.method.upper()
         request_span_name = f"HTTP {http_method}"
+        request_url = (
+            remove_url_credentials(trace_config_ctx.url_filter(params.url))
+            if callable(trace_config_ctx.url_filter)
+            else remove_url_credentials(str(params.url))
+        )
+
+        span_attributes = {
+            SpanAttributes.HTTP_METHOD: http_method,
+            SpanAttributes.HTTP_URL: request_url,
+        }
 
         trace_config_ctx.span = trace_config_ctx.tracer.start_span(
-            request_span_name,
-            kind=SpanKind.CLIENT,
+            request_span_name, kind=SpanKind.CLIENT, attributes=span_attributes
         )
 
         if callable(request_hook):
             request_hook(trace_config_ctx.span, params)
 
-        if trace_config_ctx.span.is_recording():
-            attributes = {
-                SpanAttributes.HTTP_METHOD: http_method,
-                SpanAttributes.HTTP_URL: remove_url_credentials(
-                    trace_config_ctx.url_filter(params.url)
-                )
-                if callable(trace_config_ctx.url_filter)
-                else remove_url_credentials(str(params.url)),
-            }
-            for key, value in attributes.items():
-                trace_config_ctx.span.set_attribute(key, value)
-
         trace_config_ctx.token = context_api.attach(
             trace.set_span_in_context(trace_config_ctx.span)
         )

{"golden_diff": "diff --git a/instrumentation/opentelemetry-instrumentation-aiohttp-client/src/opentelemetry/instrumentation/aiohttp_client/__init__.py b/instrumentation/opentelemetry-instrumentation-aiohttp-client/src/opentelemetry/instrumentation/aiohttp_client/__init__.py\n--- a/instrumentation/opentelemetry-instrumentation-aiohttp-client/src/opentelemetry/instrumentation/aiohttp_client/__init__.py\n+++ b/instrumentation/opentelemetry-instrumentation-aiohttp-client/src/opentelemetry/instrumentation/aiohttp_client/__init__.py\n@@ -180,27 +180,24 @@\n \n         http_method = params.method.upper()\n         request_span_name = f\"HTTP {http_method}\"\n+        request_url = (\n+            remove_url_credentials(trace_config_ctx.url_filter(params.url))\n+            if callable(trace_config_ctx.url_filter)\n+            else remove_url_credentials(str(params.url))\n+        )\n+\n+        span_attributes = {\n+            SpanAttributes.HTTP_METHOD: http_method,\n+            SpanAttributes.HTTP_URL: request_url,\n+        }\n \n         trace_config_ctx.span = trace_config_ctx.tracer.start_span(\n-            request_span_name,\n-            kind=SpanKind.CLIENT,\n+            request_span_name, kind=SpanKind.CLIENT, attributes=span_attributes\n         )\n \n         if callable(request_hook):\n             request_hook(trace_config_ctx.span, params)\n \n-        if trace_config_ctx.span.is_recording():\n-            attributes = {\n-                SpanAttributes.HTTP_METHOD: http_method,\n-                SpanAttributes.HTTP_URL: remove_url_credentials(\n-                    trace_config_ctx.url_filter(params.url)\n-                )\n-                if callable(trace_config_ctx.url_filter)\n-                else remove_url_credentials(str(params.url)),\n-            }\n-            for key, value in attributes.items():\n-                trace_config_ctx.span.set_attribute(key, value)\n-\n         trace_config_ctx.token = context_api.attach(\n             trace.set_span_in_context(trace_config_ctx.span)\n         )\n", "issue": "opentelemetry-instrumentation-aiohttp-client\n\n", "before_files": [{"content": "# Copyright 2020, OpenTelemetry Authors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"\nThe opentelemetry-instrumentation-aiohttp-client package allows tracing HTTP\nrequests made by the aiohttp client library.\n\nUsage\n-----\nExplicitly instrumenting a single client session:\n\n.. code:: python\n\n    import aiohttp\n    from opentelemetry.instrumentation.aiohttp_client import create_trace_config\n    import yarl\n\n    def strip_query_params(url: yarl.URL) -> str:\n        return str(url.with_query(None))\n\n    async with aiohttp.ClientSession(trace_configs=[create_trace_config(\n            # Remove all query params from the URL attribute on the span.\n            url_filter=strip_query_params,\n    )]) as session:\n        async with session.get(url) as response:\n            await response.text()\n\nInstrumenting all client sessions:\n\n.. code:: python\n\n    import aiohttp\n    from opentelemetry.instrumentation.aiohttp_client import (\n        AioHttpClientInstrumentor\n    )\n\n    # Enable instrumentation\n    AioHttpClientInstrumentor().instrument()\n\n    # Create a session and make an HTTP get request\n    async with aiohttp.ClientSession() as session:\n        async with session.get(url) as response:\n            await response.text()\n\nConfiguration\n-------------\n\nRequest/Response hooks\n**********************\n\nUtilize request/reponse hooks to execute custom logic to be performed before/after performing a request.\n\n.. code-block:: python\n\n   def request_hook(span: Span, params: aiohttp.TraceRequestStartParams):\n      if span and span.is_recording():\n            span.set_attribute(\"custom_user_attribute_from_request_hook\", \"some-value\")\n\n   def response_hook(span: Span, params: typing.Union[\n                aiohttp.TraceRequestEndParams,\n                aiohttp.TraceRequestExceptionParams,\n            ]):\n        if span and span.is_recording():\n            span.set_attribute(\"custom_user_attribute_from_response_hook\", \"some-value\")\n\n   AioHttpClientInstrumentor().instrument(request_hook=request_hook, response_hook=response_hook)\n\nAPI\n---\n\"\"\"\n\nimport types\nimport typing\nfrom typing import Collection\n\nimport aiohttp\nimport wrapt\nimport yarl\n\nfrom opentelemetry import context as context_api\nfrom opentelemetry import trace\nfrom opentelemetry.instrumentation.aiohttp_client.package import _instruments\nfrom opentelemetry.instrumentation.aiohttp_client.version import __version__\nfrom opentelemetry.instrumentation.instrumentor import BaseInstrumentor\nfrom opentelemetry.instrumentation.utils import (\n    _SUPPRESS_INSTRUMENTATION_KEY,\n    http_status_to_status_code,\n    unwrap,\n)\nfrom opentelemetry.propagate import inject\nfrom opentelemetry.semconv.trace import SpanAttributes\nfrom opentelemetry.trace import Span, SpanKind, TracerProvider, get_tracer\nfrom opentelemetry.trace.status import Status, StatusCode\nfrom opentelemetry.util.http import remove_url_credentials\n\n_UrlFilterT = typing.Optional[typing.Callable[[yarl.URL], str]]\n_RequestHookT = typing.Optional[\n    typing.Callable[[Span, aiohttp.TraceRequestStartParams], None]\n]\n_ResponseHookT = typing.Optional[\n    typing.Callable[\n        [\n            Span,\n            typing.Union[\n                aiohttp.TraceRequestEndParams,\n                aiohttp.TraceRequestExceptionParams,\n            ],\n        ],\n        None,\n    ]\n]\n\n\ndef create_trace_config(\n    url_filter: _UrlFilterT = None,\n    request_hook: _RequestHookT = None,\n    response_hook: _ResponseHookT = None,\n    tracer_provider: TracerProvider = None,\n) -> aiohttp.TraceConfig:\n    \"\"\"Create an aiohttp-compatible trace configuration.\n\n    One span is created for the entire HTTP request, including initial\n    TCP/TLS setup if the connection doesn't exist.\n\n    By default the span name is set to the HTTP request method.\n\n    Example usage:\n\n    .. code:: python\n\n        import aiohttp\n        from opentelemetry.instrumentation.aiohttp_client import create_trace_config\n\n        async with aiohttp.ClientSession(trace_configs=[create_trace_config()]) as session:\n            async with session.get(url) as response:\n                await response.text()\n\n\n    :param url_filter: A callback to process the requested URL prior to adding\n        it as a span attribute. This can be useful to remove sensitive data\n        such as API keys or user personal information.\n\n    :param Callable request_hook: Optional callback that can modify span name and request params.\n    :param Callable response_hook: Optional callback that can modify span name and response params.\n    :param tracer_provider: optional TracerProvider from which to get a Tracer\n\n    :return: An object suitable for use with :py:class:`aiohttp.ClientSession`.\n    :rtype: :py:class:`aiohttp.TraceConfig`\n    \"\"\"\n    # `aiohttp.TraceRequestStartParams` resolves to `aiohttp.tracing.TraceRequestStartParams`\n    # which doesn't exist in the aiohttp intersphinx inventory.\n    # Explicitly specify the type for the `request_hook` and `response_hook` param and rtype to work\n    # around this issue.\n\n    tracer = get_tracer(__name__, __version__, tracer_provider)\n\n    def _end_trace(trace_config_ctx: types.SimpleNamespace):\n        context_api.detach(trace_config_ctx.token)\n        trace_config_ctx.span.end()\n\n    async def on_request_start(\n        unused_session: aiohttp.ClientSession,\n        trace_config_ctx: types.SimpleNamespace,\n        params: aiohttp.TraceRequestStartParams,\n    ):\n        if context_api.get_value(_SUPPRESS_INSTRUMENTATION_KEY):\n            trace_config_ctx.span = None\n            return\n\n        http_method = params.method.upper()\n        request_span_name = f\"HTTP {http_method}\"\n\n        trace_config_ctx.span = trace_config_ctx.tracer.start_span(\n            request_span_name,\n            kind=SpanKind.CLIENT,\n        )\n\n        if callable(request_hook):\n            request_hook(trace_config_ctx.span, params)\n\n        if trace_config_ctx.span.is_recording():\n            attributes = {\n                SpanAttributes.HTTP_METHOD: http_method,\n                SpanAttributes.HTTP_URL: remove_url_credentials(\n                    trace_config_ctx.url_filter(params.url)\n                )\n                if callable(trace_config_ctx.url_filter)\n                else remove_url_credentials(str(params.url)),\n            }\n            for key, value in attributes.items():\n                trace_config_ctx.span.set_attribute(key, value)\n\n        trace_config_ctx.token = context_api.attach(\n            trace.set_span_in_context(trace_config_ctx.span)\n        )\n\n        inject(params.headers)\n\n    async def on_request_end(\n        unused_session: aiohttp.ClientSession,\n        trace_config_ctx: types.SimpleNamespace,\n        params: aiohttp.TraceRequestEndParams,\n    ):\n        if trace_config_ctx.span is None:\n            return\n\n        if callable(response_hook):\n            response_hook(trace_config_ctx.span, params)\n\n        if trace_config_ctx.span.is_recording():\n            trace_config_ctx.span.set_status(\n                Status(http_status_to_status_code(int(params.response.status)))\n            )\n            trace_config_ctx.span.set_attribute(\n                SpanAttributes.HTTP_STATUS_CODE, params.response.status\n            )\n        _end_trace(trace_config_ctx)\n\n    async def on_request_exception(\n        unused_session: aiohttp.ClientSession,\n        trace_config_ctx: types.SimpleNamespace,\n        params: aiohttp.TraceRequestExceptionParams,\n    ):\n        if trace_config_ctx.span is None:\n            return\n\n        if callable(response_hook):\n            response_hook(trace_config_ctx.span, params)\n\n        if trace_config_ctx.span.is_recording() and params.exception:\n            trace_config_ctx.span.set_status(Status(StatusCode.ERROR))\n            trace_config_ctx.span.record_exception(params.exception)\n        _end_trace(trace_config_ctx)\n\n    def _trace_config_ctx_factory(**kwargs):\n        kwargs.setdefault(\"trace_request_ctx\", {})\n        return types.SimpleNamespace(\n            tracer=tracer, url_filter=url_filter, **kwargs\n        )\n\n    trace_config = aiohttp.TraceConfig(\n        trace_config_ctx_factory=_trace_config_ctx_factory\n    )\n\n    trace_config.on_request_start.append(on_request_start)\n    trace_config.on_request_end.append(on_request_end)\n    trace_config.on_request_exception.append(on_request_exception)\n\n    return trace_config\n\n\ndef _instrument(\n    tracer_provider: TracerProvider = None,\n    url_filter: _UrlFilterT = None,\n    request_hook: _RequestHookT = None,\n    response_hook: _ResponseHookT = None,\n):\n    \"\"\"Enables tracing of all ClientSessions\n\n    When a ClientSession gets created a TraceConfig is automatically added to\n    the session's trace_configs.\n    \"\"\"\n    # pylint:disable=unused-argument\n    def instrumented_init(wrapped, instance, args, kwargs):\n        if context_api.get_value(_SUPPRESS_INSTRUMENTATION_KEY):\n            return wrapped(*args, **kwargs)\n\n        trace_configs = list(kwargs.get(\"trace_configs\") or ())\n\n        trace_config = create_trace_config(\n            url_filter=url_filter,\n            request_hook=request_hook,\n            response_hook=response_hook,\n            tracer_provider=tracer_provider,\n        )\n        trace_config._is_instrumented_by_opentelemetry = True\n        trace_configs.append(trace_config)\n\n        kwargs[\"trace_configs\"] = trace_configs\n        return wrapped(*args, **kwargs)\n\n    wrapt.wrap_function_wrapper(\n        aiohttp.ClientSession, \"__init__\", instrumented_init\n    )\n\n\ndef _uninstrument():\n    \"\"\"Disables instrumenting for all newly created ClientSessions\"\"\"\n    unwrap(aiohttp.ClientSession, \"__init__\")\n\n\ndef _uninstrument_session(client_session: aiohttp.ClientSession):\n    \"\"\"Disables instrumentation for the given ClientSession\"\"\"\n    # pylint: disable=protected-access\n    trace_configs = client_session._trace_configs\n    client_session._trace_configs = [\n        trace_config\n        for trace_config in trace_configs\n        if not hasattr(trace_config, \"_is_instrumented_by_opentelemetry\")\n    ]\n\n\nclass AioHttpClientInstrumentor(BaseInstrumentor):\n    \"\"\"An instrumentor for aiohttp client sessions\n\n    See `BaseInstrumentor`\n    \"\"\"\n\n    def instrumentation_dependencies(self) -> Collection[str]:\n        return _instruments\n\n    def _instrument(self, **kwargs):\n        \"\"\"Instruments aiohttp ClientSession\n\n        Args:\n            **kwargs: Optional arguments\n                ``tracer_provider``: a TracerProvider, defaults to global\n                ``url_filter``: A callback to process the requested URL prior to adding\n                    it as a span attribute. This can be useful to remove sensitive data\n                    such as API keys or user personal information.\n                ``request_hook``: An optional callback that is invoked right after a span is created.\n                ``response_hook``: An optional callback which is invoked right before the span is finished processing a response.\n        \"\"\"\n        _instrument(\n            tracer_provider=kwargs.get(\"tracer_provider\"),\n            url_filter=kwargs.get(\"url_filter\"),\n            request_hook=kwargs.get(\"request_hook\"),\n            response_hook=kwargs.get(\"response_hook\"),\n        )\n\n    def _uninstrument(self, **kwargs):\n        _uninstrument()\n\n    @staticmethod\n    def uninstrument_session(client_session: aiohttp.ClientSession):\n        \"\"\"Disables instrumentation for the given session\"\"\"\n        _uninstrument_session(client_session)\n", "path": "instrumentation/opentelemetry-instrumentation-aiohttp-client/src/opentelemetry/instrumentation/aiohttp_client/__init__.py"}], "after_files": [{"content": "# Copyright 2020, OpenTelemetry Authors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"\nThe opentelemetry-instrumentation-aiohttp-client package allows tracing HTTP\nrequests made by the aiohttp client library.\n\nUsage\n-----\nExplicitly instrumenting a single client session:\n\n.. code:: python\n\n    import aiohttp\n    from opentelemetry.instrumentation.aiohttp_client import create_trace_config\n    import yarl\n\n    def strip_query_params(url: yarl.URL) -> str:\n        return str(url.with_query(None))\n\n    async with aiohttp.ClientSession(trace_configs=[create_trace_config(\n            # Remove all query params from the URL attribute on the span.\n            url_filter=strip_query_params,\n    )]) as session:\n        async with session.get(url) as response:\n            await response.text()\n\nInstrumenting all client sessions:\n\n.. code:: python\n\n    import aiohttp\n    from opentelemetry.instrumentation.aiohttp_client import (\n        AioHttpClientInstrumentor\n    )\n\n    # Enable instrumentation\n    AioHttpClientInstrumentor().instrument()\n\n    # Create a session and make an HTTP get request\n    async with aiohttp.ClientSession() as session:\n        async with session.get(url) as response:\n            await response.text()\n\nConfiguration\n-------------\n\nRequest/Response hooks\n**********************\n\nUtilize request/reponse hooks to execute custom logic to be performed before/after performing a request.\n\n.. code-block:: python\n\n   def request_hook(span: Span, params: aiohttp.TraceRequestStartParams):\n      if span and span.is_recording():\n            span.set_attribute(\"custom_user_attribute_from_request_hook\", \"some-value\")\n\n   def response_hook(span: Span, params: typing.Union[\n                aiohttp.TraceRequestEndParams,\n                aiohttp.TraceRequestExceptionParams,\n            ]):\n        if span and span.is_recording():\n            span.set_attribute(\"custom_user_attribute_from_response_hook\", \"some-value\")\n\n   AioHttpClientInstrumentor().instrument(request_hook=request_hook, response_hook=response_hook)\n\nAPI\n---\n\"\"\"\n\nimport types\nimport typing\nfrom typing import Collection\n\nimport aiohttp\nimport wrapt\nimport yarl\n\nfrom opentelemetry import context as context_api\nfrom opentelemetry import trace\nfrom opentelemetry.instrumentation.aiohttp_client.package import _instruments\nfrom opentelemetry.instrumentation.aiohttp_client.version import __version__\nfrom opentelemetry.instrumentation.instrumentor import BaseInstrumentor\nfrom opentelemetry.instrumentation.utils import (\n    _SUPPRESS_INSTRUMENTATION_KEY,\n    http_status_to_status_code,\n    unwrap,\n)\nfrom opentelemetry.propagate import inject\nfrom opentelemetry.semconv.trace import SpanAttributes\nfrom opentelemetry.trace import Span, SpanKind, TracerProvider, get_tracer\nfrom opentelemetry.trace.status import Status, StatusCode\nfrom opentelemetry.util.http import remove_url_credentials\n\n_UrlFilterT = typing.Optional[typing.Callable[[yarl.URL], str]]\n_RequestHookT = typing.Optional[\n    typing.Callable[[Span, aiohttp.TraceRequestStartParams], None]\n]\n_ResponseHookT = typing.Optional[\n    typing.Callable[\n        [\n            Span,\n            typing.Union[\n                aiohttp.TraceRequestEndParams,\n                aiohttp.TraceRequestExceptionParams,\n            ],\n        ],\n        None,\n    ]\n]\n\n\ndef create_trace_config(\n    url_filter: _UrlFilterT = None,\n    request_hook: _RequestHookT = None,\n    response_hook: _ResponseHookT = None,\n    tracer_provider: TracerProvider = None,\n) -> aiohttp.TraceConfig:\n    \"\"\"Create an aiohttp-compatible trace configuration.\n\n    One span is created for the entire HTTP request, including initial\n    TCP/TLS setup if the connection doesn't exist.\n\n    By default the span name is set to the HTTP request method.\n\n    Example usage:\n\n    .. code:: python\n\n        import aiohttp\n        from opentelemetry.instrumentation.aiohttp_client import create_trace_config\n\n        async with aiohttp.ClientSession(trace_configs=[create_trace_config()]) as session:\n            async with session.get(url) as response:\n                await response.text()\n\n\n    :param url_filter: A callback to process the requested URL prior to adding\n        it as a span attribute. This can be useful to remove sensitive data\n        such as API keys or user personal information.\n\n    :param Callable request_hook: Optional callback that can modify span name and request params.\n    :param Callable response_hook: Optional callback that can modify span name and response params.\n    :param tracer_provider: optional TracerProvider from which to get a Tracer\n\n    :return: An object suitable for use with :py:class:`aiohttp.ClientSession`.\n    :rtype: :py:class:`aiohttp.TraceConfig`\n    \"\"\"\n    # `aiohttp.TraceRequestStartParams` resolves to `aiohttp.tracing.TraceRequestStartParams`\n    # which doesn't exist in the aiohttp intersphinx inventory.\n    # Explicitly specify the type for the `request_hook` and `response_hook` param and rtype to work\n    # around this issue.\n\n    tracer = get_tracer(__name__, __version__, tracer_provider)\n\n    def _end_trace(trace_config_ctx: types.SimpleNamespace):\n        context_api.detach(trace_config_ctx.token)\n        trace_config_ctx.span.end()\n\n    async def on_request_start(\n        unused_session: aiohttp.ClientSession,\n        trace_config_ctx: types.SimpleNamespace,\n        params: aiohttp.TraceRequestStartParams,\n    ):\n        if context_api.get_value(_SUPPRESS_INSTRUMENTATION_KEY):\n            trace_config_ctx.span = None\n            return\n\n        http_method = params.method.upper()\n        request_span_name = f\"HTTP {http_method}\"\n        request_url = (\n            remove_url_credentials(trace_config_ctx.url_filter(params.url))\n            if callable(trace_config_ctx.url_filter)\n            else remove_url_credentials(str(params.url))\n        )\n\n        span_attributes = {\n            SpanAttributes.HTTP_METHOD: http_method,\n            SpanAttributes.HTTP_URL: request_url,\n        }\n\n        trace_config_ctx.span = trace_config_ctx.tracer.start_span(\n            request_span_name, kind=SpanKind.CLIENT, attributes=span_attributes\n        )\n\n        if callable(request_hook):\n            request_hook(trace_config_ctx.span, params)\n\n        trace_config_ctx.token = context_api.attach(\n            trace.set_span_in_context(trace_config_ctx.span)\n        )\n\n        inject(params.headers)\n\n    async def on_request_end(\n        unused_session: aiohttp.ClientSession,\n        trace_config_ctx: types.SimpleNamespace,\n        params: aiohttp.TraceRequestEndParams,\n    ):\n        if trace_config_ctx.span is None:\n            return\n\n        if callable(response_hook):\n            response_hook(trace_config_ctx.span, params)\n\n        if trace_config_ctx.span.is_recording():\n            trace_config_ctx.span.set_status(\n                Status(http_status_to_status_code(int(params.response.status)))\n            )\n            trace_config_ctx.span.set_attribute(\n                SpanAttributes.HTTP_STATUS_CODE, params.response.status\n            )\n        _end_trace(trace_config_ctx)\n\n    async def on_request_exception(\n        unused_session: aiohttp.ClientSession,\n        trace_config_ctx: types.SimpleNamespace,\n        params: aiohttp.TraceRequestExceptionParams,\n    ):\n        if trace_config_ctx.span is None:\n            return\n\n        if callable(response_hook):\n            response_hook(trace_config_ctx.span, params)\n\n        if trace_config_ctx.span.is_recording() and params.exception:\n            trace_config_ctx.span.set_status(Status(StatusCode.ERROR))\n            trace_config_ctx.span.record_exception(params.exception)\n        _end_trace(trace_config_ctx)\n\n    def _trace_config_ctx_factory(**kwargs):\n        kwargs.setdefault(\"trace_request_ctx\", {})\n        return types.SimpleNamespace(\n            tracer=tracer, url_filter=url_filter, **kwargs\n        )\n\n    trace_config = aiohttp.TraceConfig(\n        trace_config_ctx_factory=_trace_config_ctx_factory\n    )\n\n    trace_config.on_request_start.append(on_request_start)\n    trace_config.on_request_end.append(on_request_end)\n    trace_config.on_request_exception.append(on_request_exception)\n\n    return trace_config\n\n\ndef _instrument(\n    tracer_provider: TracerProvider = None,\n    url_filter: _UrlFilterT = None,\n    request_hook: _RequestHookT = None,\n    response_hook: _ResponseHookT = None,\n):\n    \"\"\"Enables tracing of all ClientSessions\n\n    When a ClientSession gets created a TraceConfig is automatically added to\n    the session's trace_configs.\n    \"\"\"\n    # pylint:disable=unused-argument\n    def instrumented_init(wrapped, instance, args, kwargs):\n        if context_api.get_value(_SUPPRESS_INSTRUMENTATION_KEY):\n            return wrapped(*args, **kwargs)\n\n        trace_configs = list(kwargs.get(\"trace_configs\") or ())\n\n        trace_config = create_trace_config(\n            url_filter=url_filter,\n            request_hook=request_hook,\n            response_hook=response_hook,\n            tracer_provider=tracer_provider,\n        )\n        trace_config._is_instrumented_by_opentelemetry = True\n        trace_configs.append(trace_config)\n\n        kwargs[\"trace_configs\"] = trace_configs\n        return wrapped(*args, **kwargs)\n\n    wrapt.wrap_function_wrapper(\n        aiohttp.ClientSession, \"__init__\", instrumented_init\n    )\n\n\ndef _uninstrument():\n    \"\"\"Disables instrumenting for all newly created ClientSessions\"\"\"\n    unwrap(aiohttp.ClientSession, \"__init__\")\n\n\ndef _uninstrument_session(client_session: aiohttp.ClientSession):\n    \"\"\"Disables instrumentation for the given ClientSession\"\"\"\n    # pylint: disable=protected-access\n    trace_configs = client_session._trace_configs\n    client_session._trace_configs = [\n        trace_config\n        for trace_config in trace_configs\n        if not hasattr(trace_config, \"_is_instrumented_by_opentelemetry\")\n    ]\n\n\nclass AioHttpClientInstrumentor(BaseInstrumentor):\n    \"\"\"An instrumentor for aiohttp client sessions\n\n    See `BaseInstrumentor`\n    \"\"\"\n\n    def instrumentation_dependencies(self) -> Collection[str]:\n        return _instruments\n\n    def _instrument(self, **kwargs):\n        \"\"\"Instruments aiohttp ClientSession\n\n        Args:\n            **kwargs: Optional arguments\n                ``tracer_provider``: a TracerProvider, defaults to global\n                ``url_filter``: A callback to process the requested URL prior to adding\n                    it as a span attribute. This can be useful to remove sensitive data\n                    such as API keys or user personal information.\n                ``request_hook``: An optional callback that is invoked right after a span is created.\n                ``response_hook``: An optional callback which is invoked right before the span is finished processing a response.\n        \"\"\"\n        _instrument(\n            tracer_provider=kwargs.get(\"tracer_provider\"),\n            url_filter=kwargs.get(\"url_filter\"),\n            request_hook=kwargs.get(\"request_hook\"),\n            response_hook=kwargs.get(\"response_hook\"),\n        )\n\n    def _uninstrument(self, **kwargs):\n        _uninstrument()\n\n    @staticmethod\n    def uninstrument_session(client_session: aiohttp.ClientSession):\n        \"\"\"Disables instrumentation for the given session\"\"\"\n        _uninstrument_session(client_session)\n", "path": "instrumentation/opentelemetry-instrumentation-aiohttp-client/src/opentelemetry/instrumentation/aiohttp_client/__init__.py"}]}