We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Add HTTP user-agent to WSGI instrumentation
As discussed for ASGI in [#716 (comment)](https://github.com/open-telemetry/opentelemetry-python/pull/716/files#r428364828), the WSGI instrumentation should also set an attribute from the user-agent in a HTTP request. 

--- 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-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py`
Content:
```
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 This library provides a WSGI middleware that can be used on any WSGI framework
16 (such as Django / Flask) to track requests timing through OpenTelemetry.
17 
18 Usage (Flask)
19 -------------
20 
21 .. code-block:: python
22 
23     from flask import Flask
24     from opentelemetry.instrumentation.wsgi import OpenTelemetryMiddleware
25 
26     app = Flask(__name__)
27     app.wsgi_app = OpenTelemetryMiddleware(app.wsgi_app)
28 
29     @app.route("/")
30     def hello():
31         return "Hello!"
32 
33     if __name__ == "__main__":
34         app.run(debug=True)
35 
36 
37 Usage (Django)
38 --------------
39 
40 Modify the application's ``wsgi.py`` file as shown below.
41 
42 .. code-block:: python
43 
44     import os
45     from opentelemetry.instrumentation.wsgi import OpenTelemetryMiddleware
46     from django.core.wsgi import get_wsgi_application
47 
48     os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'application.settings')
49 
50     application = get_wsgi_application()
51     application = OpenTelemetryMiddleware(application)
52 
53 API
54 ---
55 """
56 
57 import functools
58 import typing
59 import wsgiref.util as wsgiref_util
60 
61 from opentelemetry import context, propagators, trace
62 from opentelemetry.instrumentation.utils import http_status_to_canonical_code
63 from opentelemetry.instrumentation.wsgi.version import __version__
64 from opentelemetry.trace.status import Status, StatusCanonicalCode
65 
66 _HTTP_VERSION_PREFIX = "HTTP/"
67 
68 
69 def get_header_from_environ(
70     environ: dict, header_name: str
71 ) -> typing.List[str]:
72     """Retrieve a HTTP header value from the PEP3333-conforming WSGI environ.
73 
74     Returns:
75         A list with a single string with the header value if it exists, else an empty list.
76     """
77     environ_key = "HTTP_" + header_name.upper().replace("-", "_")
78     value = environ.get(environ_key)
79     if value is not None:
80         return [value]
81     return []
82 
83 
84 def setifnotnone(dic, key, value):
85     if value is not None:
86         dic[key] = value
87 
88 
89 def collect_request_attributes(environ):
90     """Collects HTTP request attributes from the PEP3333-conforming
91     WSGI environ and returns a dictionary to be used as span creation attributes."""
92 
93     result = {
94         "component": "http",
95         "http.method": environ.get("REQUEST_METHOD"),
96         "http.server_name": environ.get("SERVER_NAME"),
97         "http.scheme": environ.get("wsgi.url_scheme"),
98     }
99 
100     host_port = environ.get("SERVER_PORT")
101     if host_port is not None:
102         result.update({"host.port": int(host_port)})
103 
104     setifnotnone(result, "http.host", environ.get("HTTP_HOST"))
105     target = environ.get("RAW_URI")
106     if target is None:  # Note: `"" or None is None`
107         target = environ.get("REQUEST_URI")
108     if target is not None:
109         result["http.target"] = target
110     else:
111         result["http.url"] = wsgiref_util.request_uri(environ)
112 
113     remote_addr = environ.get("REMOTE_ADDR")
114     if remote_addr:
115         result["net.peer.ip"] = remote_addr
116     remote_host = environ.get("REMOTE_HOST")
117     if remote_host and remote_host != remote_addr:
118         result["net.peer.name"] = remote_host
119 
120     setifnotnone(result, "net.peer.port", environ.get("REMOTE_PORT"))
121     flavor = environ.get("SERVER_PROTOCOL", "")
122     if flavor.upper().startswith(_HTTP_VERSION_PREFIX):
123         flavor = flavor[len(_HTTP_VERSION_PREFIX) :]
124     if flavor:
125         result["http.flavor"] = flavor
126 
127     return result
128 
129 
130 def add_response_attributes(
131     span, start_response_status, response_headers
132 ):  # pylint: disable=unused-argument
133     """Adds HTTP response attributes to span using the arguments
134     passed to a PEP3333-conforming start_response callable."""
135 
136     status_code, status_text = start_response_status.split(" ", 1)
137     span.set_attribute("http.status_text", status_text)
138 
139     try:
140         status_code = int(status_code)
141     except ValueError:
142         span.set_status(
143             Status(
144                 StatusCanonicalCode.UNKNOWN,
145                 "Non-integer HTTP status: " + repr(status_code),
146             )
147         )
148     else:
149         span.set_attribute("http.status_code", status_code)
150         span.set_status(Status(http_status_to_canonical_code(status_code)))
151 
152 
153 def get_default_span_name(environ):
154     """Default implementation for name_callback, returns HTTP {METHOD_NAME}."""
155     return "HTTP {}".format(environ.get("REQUEST_METHOD", "")).strip()
156 
157 
158 class OpenTelemetryMiddleware:
159     """The WSGI application middleware.
160 
161     This class is a PEP 3333 conforming WSGI middleware that starts and
162     annotates spans for any requests it is invoked with.
163 
164     Args:
165         wsgi: The WSGI application callable to forward requests to.
166         name_callback: Callback which calculates a generic span name for an
167                        incoming HTTP request based on the PEP3333 WSGI environ.
168                        Optional: Defaults to get_default_span_name.
169     """
170 
171     def __init__(self, wsgi, name_callback=get_default_span_name):
172         self.wsgi = wsgi
173         self.tracer = trace.get_tracer(__name__, __version__)
174         self.name_callback = name_callback
175 
176     @staticmethod
177     def _create_start_response(span, start_response):
178         @functools.wraps(start_response)
179         def _start_response(status, response_headers, *args, **kwargs):
180             add_response_attributes(span, status, response_headers)
181             return start_response(status, response_headers, *args, **kwargs)
182 
183         return _start_response
184 
185     def __call__(self, environ, start_response):
186         """The WSGI application
187 
188         Args:
189             environ: A WSGI environment.
190             start_response: The WSGI start_response callable.
191         """
192 
193         token = context.attach(
194             propagators.extract(get_header_from_environ, environ)
195         )
196         span_name = self.name_callback(environ)
197 
198         span = self.tracer.start_span(
199             span_name,
200             kind=trace.SpanKind.SERVER,
201             attributes=collect_request_attributes(environ),
202         )
203 
204         try:
205             with self.tracer.use_span(span):
206                 start_response = self._create_start_response(
207                     span, start_response
208                 )
209                 iterable = self.wsgi(environ, start_response)
210                 return _end_span_after_iterating(
211                     iterable, span, self.tracer, token
212                 )
213         except Exception as ex:
214             span.set_status(Status(StatusCanonicalCode.INTERNAL, str(ex)))
215             span.end()
216             context.detach(token)
217             raise
218 
219 
220 # Put this in a subfunction to not delay the call to the wrapped
221 # WSGI application (instrumentation should change the application
222 # behavior as little as possible).
223 def _end_span_after_iterating(iterable, span, tracer, token):
224     try:
225         with tracer.use_span(span):
226             for yielded in iterable:
227                 yield yielded
228     finally:
229         close = getattr(iterable, "close", None)
230         if close:
231             close()
232         span.end()
233         context.detach(token)
234
```
--- 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-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py b/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py
--- a/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py
+++ b/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py
@@ -117,6 +117,10 @@
     if remote_host and remote_host != remote_addr:
         result["net.peer.name"] = remote_host
 
+    user_agent = environ.get("HTTP_USER_AGENT")
+    if user_agent is not None and len(user_agent) > 0:
+        result["http.user_agent"] = user_agent
+
     setifnotnone(result, "net.peer.port", environ.get("REMOTE_PORT"))
     flavor = environ.get("SERVER_PROTOCOL", "")
     if flavor.upper().startswith(_HTTP_VERSION_PREFIX):

{"golden_diff": "diff --git a/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py b/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py\n--- a/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py\n+++ b/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py\n@@ -117,6 +117,10 @@\n     if remote_host and remote_host != remote_addr:\n         result[\"net.peer.name\"] = remote_host\n \n+    user_agent = environ.get(\"HTTP_USER_AGENT\")\n+    if user_agent is not None and len(user_agent) > 0:\n+        result[\"http.user_agent\"] = user_agent\n+\n     setifnotnone(result, \"net.peer.port\", environ.get(\"REMOTE_PORT\"))\n     flavor = environ.get(\"SERVER_PROTOCOL\", \"\")\n     if flavor.upper().startswith(_HTTP_VERSION_PREFIX):\n", "issue": "Add HTTP user-agent to WSGI instrumentation\nAs discussed for ASGI in [#716 (comment)](https://github.com/open-telemetry/opentelemetry-python/pull/716/files#r428364828), the WSGI instrumentation should also set an attribute from the user-agent in a HTTP request. \n", "before_files": [{"content": "# Copyright The 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\"\"\"\nThis library provides a WSGI middleware that can be used on any WSGI framework\n(such as Django / Flask) to track requests timing through OpenTelemetry.\n\nUsage (Flask)\n-------------\n\n.. code-block:: python\n\n    from flask import Flask\n    from opentelemetry.instrumentation.wsgi import OpenTelemetryMiddleware\n\n    app = Flask(__name__)\n    app.wsgi_app = OpenTelemetryMiddleware(app.wsgi_app)\n\n    @app.route(\"/\")\n    def hello():\n        return \"Hello!\"\n\n    if __name__ == \"__main__\":\n        app.run(debug=True)\n\n\nUsage (Django)\n--------------\n\nModify the application's ``wsgi.py`` file as shown below.\n\n.. code-block:: python\n\n    import os\n    from opentelemetry.instrumentation.wsgi import OpenTelemetryMiddleware\n    from django.core.wsgi import get_wsgi_application\n\n    os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'application.settings')\n\n    application = get_wsgi_application()\n    application = OpenTelemetryMiddleware(application)\n\nAPI\n---\n\"\"\"\n\nimport functools\nimport typing\nimport wsgiref.util as wsgiref_util\n\nfrom opentelemetry import context, propagators, trace\nfrom opentelemetry.instrumentation.utils import http_status_to_canonical_code\nfrom opentelemetry.instrumentation.wsgi.version import __version__\nfrom opentelemetry.trace.status import Status, StatusCanonicalCode\n\n_HTTP_VERSION_PREFIX = \"HTTP/\"\n\n\ndef get_header_from_environ(\n    environ: dict, header_name: str\n) -> typing.List[str]:\n    \"\"\"Retrieve a HTTP header value from the PEP3333-conforming WSGI environ.\n\n    Returns:\n        A list with a single string with the header value if it exists, else an empty list.\n    \"\"\"\n    environ_key = \"HTTP_\" + header_name.upper().replace(\"-\", \"_\")\n    value = environ.get(environ_key)\n    if value is not None:\n        return [value]\n    return []\n\n\ndef setifnotnone(dic, key, value):\n    if value is not None:\n        dic[key] = value\n\n\ndef collect_request_attributes(environ):\n    \"\"\"Collects HTTP request attributes from the PEP3333-conforming\n    WSGI environ and returns a dictionary to be used as span creation attributes.\"\"\"\n\n    result = {\n        \"component\": \"http\",\n        \"http.method\": environ.get(\"REQUEST_METHOD\"),\n        \"http.server_name\": environ.get(\"SERVER_NAME\"),\n        \"http.scheme\": environ.get(\"wsgi.url_scheme\"),\n    }\n\n    host_port = environ.get(\"SERVER_PORT\")\n    if host_port is not None:\n        result.update({\"host.port\": int(host_port)})\n\n    setifnotnone(result, \"http.host\", environ.get(\"HTTP_HOST\"))\n    target = environ.get(\"RAW_URI\")\n    if target is None:  # Note: `\"\" or None is None`\n        target = environ.get(\"REQUEST_URI\")\n    if target is not None:\n        result[\"http.target\"] = target\n    else:\n        result[\"http.url\"] = wsgiref_util.request_uri(environ)\n\n    remote_addr = environ.get(\"REMOTE_ADDR\")\n    if remote_addr:\n        result[\"net.peer.ip\"] = remote_addr\n    remote_host = environ.get(\"REMOTE_HOST\")\n    if remote_host and remote_host != remote_addr:\n        result[\"net.peer.name\"] = remote_host\n\n    setifnotnone(result, \"net.peer.port\", environ.get(\"REMOTE_PORT\"))\n    flavor = environ.get(\"SERVER_PROTOCOL\", \"\")\n    if flavor.upper().startswith(_HTTP_VERSION_PREFIX):\n        flavor = flavor[len(_HTTP_VERSION_PREFIX) :]\n    if flavor:\n        result[\"http.flavor\"] = flavor\n\n    return result\n\n\ndef add_response_attributes(\n    span, start_response_status, response_headers\n):  # pylint: disable=unused-argument\n    \"\"\"Adds HTTP response attributes to span using the arguments\n    passed to a PEP3333-conforming start_response callable.\"\"\"\n\n    status_code, status_text = start_response_status.split(\" \", 1)\n    span.set_attribute(\"http.status_text\", status_text)\n\n    try:\n        status_code = int(status_code)\n    except ValueError:\n        span.set_status(\n            Status(\n                StatusCanonicalCode.UNKNOWN,\n                \"Non-integer HTTP status: \" + repr(status_code),\n            )\n        )\n    else:\n        span.set_attribute(\"http.status_code\", status_code)\n        span.set_status(Status(http_status_to_canonical_code(status_code)))\n\n\ndef get_default_span_name(environ):\n    \"\"\"Default implementation for name_callback, returns HTTP {METHOD_NAME}.\"\"\"\n    return \"HTTP {}\".format(environ.get(\"REQUEST_METHOD\", \"\")).strip()\n\n\nclass OpenTelemetryMiddleware:\n    \"\"\"The WSGI application middleware.\n\n    This class is a PEP 3333 conforming WSGI middleware that starts and\n    annotates spans for any requests it is invoked with.\n\n    Args:\n        wsgi: The WSGI application callable to forward requests to.\n        name_callback: Callback which calculates a generic span name for an\n                       incoming HTTP request based on the PEP3333 WSGI environ.\n                       Optional: Defaults to get_default_span_name.\n    \"\"\"\n\n    def __init__(self, wsgi, name_callback=get_default_span_name):\n        self.wsgi = wsgi\n        self.tracer = trace.get_tracer(__name__, __version__)\n        self.name_callback = name_callback\n\n    @staticmethod\n    def _create_start_response(span, start_response):\n        @functools.wraps(start_response)\n        def _start_response(status, response_headers, *args, **kwargs):\n            add_response_attributes(span, status, response_headers)\n            return start_response(status, response_headers, *args, **kwargs)\n\n        return _start_response\n\n    def __call__(self, environ, start_response):\n        \"\"\"The WSGI application\n\n        Args:\n            environ: A WSGI environment.\n            start_response: The WSGI start_response callable.\n        \"\"\"\n\n        token = context.attach(\n            propagators.extract(get_header_from_environ, environ)\n        )\n        span_name = self.name_callback(environ)\n\n        span = self.tracer.start_span(\n            span_name,\n            kind=trace.SpanKind.SERVER,\n            attributes=collect_request_attributes(environ),\n        )\n\n        try:\n            with self.tracer.use_span(span):\n                start_response = self._create_start_response(\n                    span, start_response\n                )\n                iterable = self.wsgi(environ, start_response)\n                return _end_span_after_iterating(\n                    iterable, span, self.tracer, token\n                )\n        except Exception as ex:\n            span.set_status(Status(StatusCanonicalCode.INTERNAL, str(ex)))\n            span.end()\n            context.detach(token)\n            raise\n\n\n# Put this in a subfunction to not delay the call to the wrapped\n# WSGI application (instrumentation should change the application\n# behavior as little as possible).\ndef _end_span_after_iterating(iterable, span, tracer, token):\n    try:\n        with tracer.use_span(span):\n            for yielded in iterable:\n                yield yielded\n    finally:\n        close = getattr(iterable, \"close\", None)\n        if close:\n            close()\n        span.end()\n        context.detach(token)\n", "path": "instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py"}], "after_files": [{"content": "# Copyright The 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\"\"\"\nThis library provides a WSGI middleware that can be used on any WSGI framework\n(such as Django / Flask) to track requests timing through OpenTelemetry.\n\nUsage (Flask)\n-------------\n\n.. code-block:: python\n\n    from flask import Flask\n    from opentelemetry.instrumentation.wsgi import OpenTelemetryMiddleware\n\n    app = Flask(__name__)\n    app.wsgi_app = OpenTelemetryMiddleware(app.wsgi_app)\n\n    @app.route(\"/\")\n    def hello():\n        return \"Hello!\"\n\n    if __name__ == \"__main__\":\n        app.run(debug=True)\n\n\nUsage (Django)\n--------------\n\nModify the application's ``wsgi.py`` file as shown below.\n\n.. code-block:: python\n\n    import os\n    from opentelemetry.instrumentation.wsgi import OpenTelemetryMiddleware\n    from django.core.wsgi import get_wsgi_application\n\n    os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'application.settings')\n\n    application = get_wsgi_application()\n    application = OpenTelemetryMiddleware(application)\n\nAPI\n---\n\"\"\"\n\nimport functools\nimport typing\nimport wsgiref.util as wsgiref_util\n\nfrom opentelemetry import context, propagators, trace\nfrom opentelemetry.instrumentation.utils import http_status_to_canonical_code\nfrom opentelemetry.instrumentation.wsgi.version import __version__\nfrom opentelemetry.trace.status import Status, StatusCanonicalCode\n\n_HTTP_VERSION_PREFIX = \"HTTP/\"\n\n\ndef get_header_from_environ(\n    environ: dict, header_name: str\n) -> typing.List[str]:\n    \"\"\"Retrieve a HTTP header value from the PEP3333-conforming WSGI environ.\n\n    Returns:\n        A list with a single string with the header value if it exists, else an empty list.\n    \"\"\"\n    environ_key = \"HTTP_\" + header_name.upper().replace(\"-\", \"_\")\n    value = environ.get(environ_key)\n    if value is not None:\n        return [value]\n    return []\n\n\ndef setifnotnone(dic, key, value):\n    if value is not None:\n        dic[key] = value\n\n\ndef collect_request_attributes(environ):\n    \"\"\"Collects HTTP request attributes from the PEP3333-conforming\n    WSGI environ and returns a dictionary to be used as span creation attributes.\"\"\"\n\n    result = {\n        \"component\": \"http\",\n        \"http.method\": environ.get(\"REQUEST_METHOD\"),\n        \"http.server_name\": environ.get(\"SERVER_NAME\"),\n        \"http.scheme\": environ.get(\"wsgi.url_scheme\"),\n    }\n\n    host_port = environ.get(\"SERVER_PORT\")\n    if host_port is not None:\n        result.update({\"host.port\": int(host_port)})\n\n    setifnotnone(result, \"http.host\", environ.get(\"HTTP_HOST\"))\n    target = environ.get(\"RAW_URI\")\n    if target is None:  # Note: `\"\" or None is None`\n        target = environ.get(\"REQUEST_URI\")\n    if target is not None:\n        result[\"http.target\"] = target\n    else:\n        result[\"http.url\"] = wsgiref_util.request_uri(environ)\n\n    remote_addr = environ.get(\"REMOTE_ADDR\")\n    if remote_addr:\n        result[\"net.peer.ip\"] = remote_addr\n    remote_host = environ.get(\"REMOTE_HOST\")\n    if remote_host and remote_host != remote_addr:\n        result[\"net.peer.name\"] = remote_host\n\n    user_agent = environ.get(\"HTTP_USER_AGENT\")\n    if user_agent is not None and len(user_agent) > 0:\n        result[\"http.user_agent\"] = user_agent\n\n    setifnotnone(result, \"net.peer.port\", environ.get(\"REMOTE_PORT\"))\n    flavor = environ.get(\"SERVER_PROTOCOL\", \"\")\n    if flavor.upper().startswith(_HTTP_VERSION_PREFIX):\n        flavor = flavor[len(_HTTP_VERSION_PREFIX) :]\n    if flavor:\n        result[\"http.flavor\"] = flavor\n\n    return result\n\n\ndef add_response_attributes(\n    span, start_response_status, response_headers\n):  # pylint: disable=unused-argument\n    \"\"\"Adds HTTP response attributes to span using the arguments\n    passed to a PEP3333-conforming start_response callable.\"\"\"\n\n    status_code, status_text = start_response_status.split(\" \", 1)\n    span.set_attribute(\"http.status_text\", status_text)\n\n    try:\n        status_code = int(status_code)\n    except ValueError:\n        span.set_status(\n            Status(\n                StatusCanonicalCode.UNKNOWN,\n                \"Non-integer HTTP status: \" + repr(status_code),\n            )\n        )\n    else:\n        span.set_attribute(\"http.status_code\", status_code)\n        span.set_status(Status(http_status_to_canonical_code(status_code)))\n\n\ndef get_default_span_name(environ):\n    \"\"\"Default implementation for name_callback, returns HTTP {METHOD_NAME}.\"\"\"\n    return \"HTTP {}\".format(environ.get(\"REQUEST_METHOD\", \"\")).strip()\n\n\nclass OpenTelemetryMiddleware:\n    \"\"\"The WSGI application middleware.\n\n    This class is a PEP 3333 conforming WSGI middleware that starts and\n    annotates spans for any requests it is invoked with.\n\n    Args:\n        wsgi: The WSGI application callable to forward requests to.\n        name_callback: Callback which calculates a generic span name for an\n                       incoming HTTP request based on the PEP3333 WSGI environ.\n                       Optional: Defaults to get_default_span_name.\n    \"\"\"\n\n    def __init__(self, wsgi, name_callback=get_default_span_name):\n        self.wsgi = wsgi\n        self.tracer = trace.get_tracer(__name__, __version__)\n        self.name_callback = name_callback\n\n    @staticmethod\n    def _create_start_response(span, start_response):\n        @functools.wraps(start_response)\n        def _start_response(status, response_headers, *args, **kwargs):\n            add_response_attributes(span, status, response_headers)\n            return start_response(status, response_headers, *args, **kwargs)\n\n        return _start_response\n\n    def __call__(self, environ, start_response):\n        \"\"\"The WSGI application\n\n        Args:\n            environ: A WSGI environment.\n            start_response: The WSGI start_response callable.\n        \"\"\"\n\n        token = context.attach(\n            propagators.extract(get_header_from_environ, environ)\n        )\n        span_name = self.name_callback(environ)\n\n        span = self.tracer.start_span(\n            span_name,\n            kind=trace.SpanKind.SERVER,\n            attributes=collect_request_attributes(environ),\n        )\n\n        try:\n            with self.tracer.use_span(span):\n                start_response = self._create_start_response(\n                    span, start_response\n                )\n                iterable = self.wsgi(environ, start_response)\n                return _end_span_after_iterating(\n                    iterable, span, self.tracer, token\n                )\n        except Exception as ex:\n            span.set_status(Status(StatusCanonicalCode.INTERNAL, str(ex)))\n            span.end()\n            context.detach(token)\n            raise\n\n\n# Put this in a subfunction to not delay the call to the wrapped\n# WSGI application (instrumentation should change the application\n# behavior as little as possible).\ndef _end_span_after_iterating(iterable, span, tracer, token):\n    try:\n        with tracer.use_span(span):\n            for yielded in iterable:\n                yield yielded\n    finally:\n        close = getattr(iterable, \"close\", None)\n        if close:\n            close()\n        span.end()\n        context.detach(token)\n", "path": "instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py"}]}