We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Flask instrumentation example doesn't work
**Describe your environment**
Running on MacOS against the latest `mater` version of the `opentelemetry-*` packages.

**Steps to reproduce**
Instrument the app as described in the [docs](https://open-telemetry.github.io/opentelemetry-python/ext/flask/flask.html) and [docstring](https://github.com/open-telemetry/opentelemetry-python/blob/master/ext/opentelemetry-ext-flask/src/opentelemetry/ext/flask/__init__.py) -

```python
from flask import Flask
from opentelemetry.ext.flask import instrument_app
    
app = Flask(__name__)
instrument_app(app)
````

**What is the expected behavior?**
I would expect the flask app to start without any errors when running it locally.

**What is the actual behavior?**
I get an error running the app -

```
    from opentelemetry.ext.flask import instrument_app
E   ImportError: cannot import name 'instrument_app'
```

This is consistent with the fact that the `instrument_app` symbol no longer appears to be in [`opentelemetry.ext.flask`](https://github.com/open-telemetry/opentelemetry-python/blob/master/ext/opentelemetry-ext-flask/src/opentelemetry/ext/flask/__init__.py).

<hr>
The last time I ran this code was against v0.3 of the packages, so I imagine a lot has changed since then. It would be helpful to document the new correct way to instrument flask apps as it isn't currently clear to me what the correct way to do this is from looking at the docs and the code. Thanks! 


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `ext/opentelemetry-ext-flask/src/opentelemetry/ext/flask/__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 # Note: This package is not named "flask" because of
16 # https://github.com/PyCQA/pylint/issues/2648
17 
18 """
19 This library builds on the OpenTelemetry WSGI middleware to track web requests
20 in Flask applications. In addition to opentelemetry-ext-wsgi, it supports
21 flask-specific features such as:
22 
23 * The Flask endpoint name is used as the Span name.
24 * The ``http.route`` Span attribute is set so that one can see which URL rule
25   matched a request.
26 
27 Usage
28 -----
29 
30 .. code-block:: python
31 
32     from flask import Flask
33     from opentelemetry.ext.flask import instrument_app
34 
35     app = Flask(__name__)
36     instrument_app(app)  # This is where the magic happens. ✨
37 
38     @app.route("/")
39     def hello():
40         return "Hello!"
41 
42     if __name__ == "__main__":
43         app.run(debug=True)
44 
45 API
46 ---
47 """
48 
49 import logging
50 
51 import flask
52 
53 import opentelemetry.ext.wsgi as otel_wsgi
54 from opentelemetry import context, propagators, trace
55 from opentelemetry.auto_instrumentation.instrumentor import BaseInstrumentor
56 from opentelemetry.ext.flask.version import __version__
57 from opentelemetry.util import time_ns
58 
59 logger = logging.getLogger(__name__)
60 
61 _ENVIRON_STARTTIME_KEY = "opentelemetry-flask.starttime_key"
62 _ENVIRON_SPAN_KEY = "opentelemetry-flask.span_key"
63 _ENVIRON_ACTIVATION_KEY = "opentelemetry-flask.activation_key"
64 _ENVIRON_TOKEN = "opentelemetry-flask.token"
65 
66 
67 class _InstrumentedFlask(flask.Flask):
68     def __init__(self, *args, **kwargs):
69 
70         super().__init__(*args, **kwargs)
71 
72         # Single use variable here to avoid recursion issues.
73         wsgi = self.wsgi_app
74 
75         def wrapped_app(environ, start_response):
76             # We want to measure the time for route matching, etc.
77             # In theory, we could start the span here and use
78             # update_name later but that API is "highly discouraged" so
79             # we better avoid it.
80             environ[_ENVIRON_STARTTIME_KEY] = time_ns()
81 
82             def _start_response(status, response_headers, *args, **kwargs):
83                 span = flask.request.environ.get(_ENVIRON_SPAN_KEY)
84                 if span:
85                     otel_wsgi.add_response_attributes(
86                         span, status, response_headers
87                     )
88                 else:
89                     logger.warning(
90                         "Flask environ's OpenTelemetry span "
91                         "missing at _start_response(%s)",
92                         status,
93                     )
94 
95                 return start_response(
96                     status, response_headers, *args, **kwargs
97                 )
98 
99             return wsgi(environ, _start_response)
100 
101         self.wsgi_app = wrapped_app
102 
103         @self.before_request
104         def _before_flask_request():
105             environ = flask.request.environ
106             span_name = (
107                 flask.request.endpoint
108                 or otel_wsgi.get_default_span_name(environ)
109             )
110             token = context.attach(
111                 propagators.extract(otel_wsgi.get_header_from_environ, environ)
112             )
113 
114             tracer = trace.get_tracer(__name__, __version__)
115 
116             attributes = otel_wsgi.collect_request_attributes(environ)
117             if flask.request.url_rule:
118                 # For 404 that result from no route found, etc, we
119                 # don't have a url_rule.
120                 attributes["http.route"] = flask.request.url_rule.rule
121             span = tracer.start_span(
122                 span_name,
123                 kind=trace.SpanKind.SERVER,
124                 attributes=attributes,
125                 start_time=environ.get(_ENVIRON_STARTTIME_KEY),
126             )
127             activation = tracer.use_span(span, end_on_exit=True)
128             activation.__enter__()
129             environ[_ENVIRON_ACTIVATION_KEY] = activation
130             environ[_ENVIRON_SPAN_KEY] = span
131             environ[_ENVIRON_TOKEN] = token
132 
133         @self.teardown_request
134         def _teardown_flask_request(exc):
135             activation = flask.request.environ.get(_ENVIRON_ACTIVATION_KEY)
136             if not activation:
137                 logger.warning(
138                     "Flask environ's OpenTelemetry activation missing"
139                     "at _teardown_flask_request(%s)",
140                     exc,
141                 )
142                 return
143 
144             if exc is None:
145                 activation.__exit__(None, None, None)
146             else:
147                 activation.__exit__(
148                     type(exc), exc, getattr(exc, "__traceback__", None)
149                 )
150             context.detach(flask.request.environ.get(_ENVIRON_TOKEN))
151 
152 
153 class FlaskInstrumentor(BaseInstrumentor):
154     """A instrumentor for flask.Flask
155 
156     See `BaseInstrumentor`
157     """
158 
159     def __init__(self):
160         super().__init__()
161         self._original_flask = None
162 
163     def _instrument(self):
164         self._original_flask = flask.Flask
165         flask.Flask = _InstrumentedFlask
166 
167     def _uninstrument(self):
168         flask.Flask = self._original_flask
169
```
--- 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/ext/opentelemetry-ext-flask/src/opentelemetry/ext/flask/__init__.py b/ext/opentelemetry-ext-flask/src/opentelemetry/ext/flask/__init__.py
--- a/ext/opentelemetry-ext-flask/src/opentelemetry/ext/flask/__init__.py
+++ b/ext/opentelemetry-ext-flask/src/opentelemetry/ext/flask/__init__.py
@@ -29,11 +29,11 @@
 
 .. code-block:: python
 
+    from opentelemetry.ext.flask import FlaskInstrumentor
+    FlaskInstrumentor().instrument()  # This needs to be executed before importing Flask
     from flask import Flask
-    from opentelemetry.ext.flask import instrument_app
 
     app = Flask(__name__)
-    instrument_app(app)  # This is where the magic happens. ✨
 
     @app.route("/")
     def hello():

{"golden_diff": "diff --git a/ext/opentelemetry-ext-flask/src/opentelemetry/ext/flask/__init__.py b/ext/opentelemetry-ext-flask/src/opentelemetry/ext/flask/__init__.py\n--- a/ext/opentelemetry-ext-flask/src/opentelemetry/ext/flask/__init__.py\n+++ b/ext/opentelemetry-ext-flask/src/opentelemetry/ext/flask/__init__.py\n@@ -29,11 +29,11 @@\n \n .. code-block:: python\n \n+    from opentelemetry.ext.flask import FlaskInstrumentor\n+    FlaskInstrumentor().instrument()  # This needs to be executed before importing Flask\n     from flask import Flask\n-    from opentelemetry.ext.flask import instrument_app\n \n     app = Flask(__name__)\n-    instrument_app(app)  # This is where the magic happens. \u2728\n \n     @app.route(\"/\")\n     def hello():\n", "issue": "Flask instrumentation example doesn't work\n**Describe your environment**\r\nRunning on MacOS against the latest `mater` version of the `opentelemetry-*` packages.\r\n\r\n**Steps to reproduce**\r\nInstrument the app as described in the [docs](https://open-telemetry.github.io/opentelemetry-python/ext/flask/flask.html) and [docstring](https://github.com/open-telemetry/opentelemetry-python/blob/master/ext/opentelemetry-ext-flask/src/opentelemetry/ext/flask/__init__.py) -\r\n\r\n```python\r\nfrom flask import Flask\r\nfrom opentelemetry.ext.flask import instrument_app\r\n    \r\napp = Flask(__name__)\r\ninstrument_app(app)\r\n````\r\n\r\n**What is the expected behavior?**\r\nI would expect the flask app to start without any errors when running it locally.\r\n\r\n**What is the actual behavior?**\r\nI get an error running the app -\r\n\r\n```\r\n    from opentelemetry.ext.flask import instrument_app\r\nE   ImportError: cannot import name 'instrument_app'\r\n```\r\n\r\nThis is consistent with the fact that the `instrument_app` symbol no longer appears to be in [`opentelemetry.ext.flask`](https://github.com/open-telemetry/opentelemetry-python/blob/master/ext/opentelemetry-ext-flask/src/opentelemetry/ext/flask/__init__.py).\r\n\r\n<hr>\r\nThe last time I ran this code was against v0.3 of the packages, so I imagine a lot has changed since then. It would be helpful to document the new correct way to instrument flask apps as it isn't currently clear to me what the correct way to do this is from looking at the docs and the code. Thanks! \r\n\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\n# Note: This package is not named \"flask\" because of\n# https://github.com/PyCQA/pylint/issues/2648\n\n\"\"\"\nThis library builds on the OpenTelemetry WSGI middleware to track web requests\nin Flask applications. In addition to opentelemetry-ext-wsgi, it supports\nflask-specific features such as:\n\n* The Flask endpoint name is used as the Span name.\n* The ``http.route`` Span attribute is set so that one can see which URL rule\n  matched a request.\n\nUsage\n-----\n\n.. code-block:: python\n\n    from flask import Flask\n    from opentelemetry.ext.flask import instrument_app\n\n    app = Flask(__name__)\n    instrument_app(app)  # This is where the magic happens. \u2728\n\n    @app.route(\"/\")\n    def hello():\n        return \"Hello!\"\n\n    if __name__ == \"__main__\":\n        app.run(debug=True)\n\nAPI\n---\n\"\"\"\n\nimport logging\n\nimport flask\n\nimport opentelemetry.ext.wsgi as otel_wsgi\nfrom opentelemetry import context, propagators, trace\nfrom opentelemetry.auto_instrumentation.instrumentor import BaseInstrumentor\nfrom opentelemetry.ext.flask.version import __version__\nfrom opentelemetry.util import time_ns\n\nlogger = logging.getLogger(__name__)\n\n_ENVIRON_STARTTIME_KEY = \"opentelemetry-flask.starttime_key\"\n_ENVIRON_SPAN_KEY = \"opentelemetry-flask.span_key\"\n_ENVIRON_ACTIVATION_KEY = \"opentelemetry-flask.activation_key\"\n_ENVIRON_TOKEN = \"opentelemetry-flask.token\"\n\n\nclass _InstrumentedFlask(flask.Flask):\n    def __init__(self, *args, **kwargs):\n\n        super().__init__(*args, **kwargs)\n\n        # Single use variable here to avoid recursion issues.\n        wsgi = self.wsgi_app\n\n        def wrapped_app(environ, start_response):\n            # We want to measure the time for route matching, etc.\n            # In theory, we could start the span here and use\n            # update_name later but that API is \"highly discouraged\" so\n            # we better avoid it.\n            environ[_ENVIRON_STARTTIME_KEY] = time_ns()\n\n            def _start_response(status, response_headers, *args, **kwargs):\n                span = flask.request.environ.get(_ENVIRON_SPAN_KEY)\n                if span:\n                    otel_wsgi.add_response_attributes(\n                        span, status, response_headers\n                    )\n                else:\n                    logger.warning(\n                        \"Flask environ's OpenTelemetry span \"\n                        \"missing at _start_response(%s)\",\n                        status,\n                    )\n\n                return start_response(\n                    status, response_headers, *args, **kwargs\n                )\n\n            return wsgi(environ, _start_response)\n\n        self.wsgi_app = wrapped_app\n\n        @self.before_request\n        def _before_flask_request():\n            environ = flask.request.environ\n            span_name = (\n                flask.request.endpoint\n                or otel_wsgi.get_default_span_name(environ)\n            )\n            token = context.attach(\n                propagators.extract(otel_wsgi.get_header_from_environ, environ)\n            )\n\n            tracer = trace.get_tracer(__name__, __version__)\n\n            attributes = otel_wsgi.collect_request_attributes(environ)\n            if flask.request.url_rule:\n                # For 404 that result from no route found, etc, we\n                # don't have a url_rule.\n                attributes[\"http.route\"] = flask.request.url_rule.rule\n            span = tracer.start_span(\n                span_name,\n                kind=trace.SpanKind.SERVER,\n                attributes=attributes,\n                start_time=environ.get(_ENVIRON_STARTTIME_KEY),\n            )\n            activation = tracer.use_span(span, end_on_exit=True)\n            activation.__enter__()\n            environ[_ENVIRON_ACTIVATION_KEY] = activation\n            environ[_ENVIRON_SPAN_KEY] = span\n            environ[_ENVIRON_TOKEN] = token\n\n        @self.teardown_request\n        def _teardown_flask_request(exc):\n            activation = flask.request.environ.get(_ENVIRON_ACTIVATION_KEY)\n            if not activation:\n                logger.warning(\n                    \"Flask environ's OpenTelemetry activation missing\"\n                    \"at _teardown_flask_request(%s)\",\n                    exc,\n                )\n                return\n\n            if exc is None:\n                activation.__exit__(None, None, None)\n            else:\n                activation.__exit__(\n                    type(exc), exc, getattr(exc, \"__traceback__\", None)\n                )\n            context.detach(flask.request.environ.get(_ENVIRON_TOKEN))\n\n\nclass FlaskInstrumentor(BaseInstrumentor):\n    \"\"\"A instrumentor for flask.Flask\n\n    See `BaseInstrumentor`\n    \"\"\"\n\n    def __init__(self):\n        super().__init__()\n        self._original_flask = None\n\n    def _instrument(self):\n        self._original_flask = flask.Flask\n        flask.Flask = _InstrumentedFlask\n\n    def _uninstrument(self):\n        flask.Flask = self._original_flask\n", "path": "ext/opentelemetry-ext-flask/src/opentelemetry/ext/flask/__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\n# Note: This package is not named \"flask\" because of\n# https://github.com/PyCQA/pylint/issues/2648\n\n\"\"\"\nThis library builds on the OpenTelemetry WSGI middleware to track web requests\nin Flask applications. In addition to opentelemetry-ext-wsgi, it supports\nflask-specific features such as:\n\n* The Flask endpoint name is used as the Span name.\n* The ``http.route`` Span attribute is set so that one can see which URL rule\n  matched a request.\n\nUsage\n-----\n\n.. code-block:: python\n\n    from opentelemetry.ext.flask import FlaskInstrumentor\n    FlaskInstrumentor().instrument()  # This needs to be executed before importing Flask\n    from flask import Flask\n\n    app = Flask(__name__)\n\n    @app.route(\"/\")\n    def hello():\n        return \"Hello!\"\n\n    if __name__ == \"__main__\":\n        app.run(debug=True)\n\nAPI\n---\n\"\"\"\n\nimport logging\n\nimport flask\n\nimport opentelemetry.ext.wsgi as otel_wsgi\nfrom opentelemetry import context, propagators, trace\nfrom opentelemetry.auto_instrumentation.instrumentor import BaseInstrumentor\nfrom opentelemetry.ext.flask.version import __version__\nfrom opentelemetry.util import time_ns\n\nlogger = logging.getLogger(__name__)\n\n_ENVIRON_STARTTIME_KEY = \"opentelemetry-flask.starttime_key\"\n_ENVIRON_SPAN_KEY = \"opentelemetry-flask.span_key\"\n_ENVIRON_ACTIVATION_KEY = \"opentelemetry-flask.activation_key\"\n_ENVIRON_TOKEN = \"opentelemetry-flask.token\"\n\n\nclass _InstrumentedFlask(flask.Flask):\n    def __init__(self, *args, **kwargs):\n\n        super().__init__(*args, **kwargs)\n\n        # Single use variable here to avoid recursion issues.\n        wsgi = self.wsgi_app\n\n        def wrapped_app(environ, start_response):\n            # We want to measure the time for route matching, etc.\n            # In theory, we could start the span here and use\n            # update_name later but that API is \"highly discouraged\" so\n            # we better avoid it.\n            environ[_ENVIRON_STARTTIME_KEY] = time_ns()\n\n            def _start_response(status, response_headers, *args, **kwargs):\n                span = flask.request.environ.get(_ENVIRON_SPAN_KEY)\n                if span:\n                    otel_wsgi.add_response_attributes(\n                        span, status, response_headers\n                    )\n                else:\n                    logger.warning(\n                        \"Flask environ's OpenTelemetry span \"\n                        \"missing at _start_response(%s)\",\n                        status,\n                    )\n\n                return start_response(\n                    status, response_headers, *args, **kwargs\n                )\n\n            return wsgi(environ, _start_response)\n\n        self.wsgi_app = wrapped_app\n\n        @self.before_request\n        def _before_flask_request():\n            environ = flask.request.environ\n            span_name = (\n                flask.request.endpoint\n                or otel_wsgi.get_default_span_name(environ)\n            )\n            token = context.attach(\n                propagators.extract(otel_wsgi.get_header_from_environ, environ)\n            )\n\n            tracer = trace.get_tracer(__name__, __version__)\n\n            attributes = otel_wsgi.collect_request_attributes(environ)\n            if flask.request.url_rule:\n                # For 404 that result from no route found, etc, we\n                # don't have a url_rule.\n                attributes[\"http.route\"] = flask.request.url_rule.rule\n            span = tracer.start_span(\n                span_name,\n                kind=trace.SpanKind.SERVER,\n                attributes=attributes,\n                start_time=environ.get(_ENVIRON_STARTTIME_KEY),\n            )\n            activation = tracer.use_span(span, end_on_exit=True)\n            activation.__enter__()\n            environ[_ENVIRON_ACTIVATION_KEY] = activation\n            environ[_ENVIRON_SPAN_KEY] = span\n            environ[_ENVIRON_TOKEN] = token\n\n        @self.teardown_request\n        def _teardown_flask_request(exc):\n            activation = flask.request.environ.get(_ENVIRON_ACTIVATION_KEY)\n            if not activation:\n                logger.warning(\n                    \"Flask environ's OpenTelemetry activation missing\"\n                    \"at _teardown_flask_request(%s)\",\n                    exc,\n                )\n                return\n\n            if exc is None:\n                activation.__exit__(None, None, None)\n            else:\n                activation.__exit__(\n                    type(exc), exc, getattr(exc, \"__traceback__\", None)\n                )\n            context.detach(flask.request.environ.get(_ENVIRON_TOKEN))\n\n\nclass FlaskInstrumentor(BaseInstrumentor):\n    \"\"\"A instrumentor for flask.Flask\n\n    See `BaseInstrumentor`\n    \"\"\"\n\n    def __init__(self):\n        super().__init__()\n        self._original_flask = None\n\n    def _instrument(self):\n        self._original_flask = flask.Flask\n        flask.Flask = _InstrumentedFlask\n\n    def _uninstrument(self):\n        flask.Flask = self._original_flask\n", "path": "ext/opentelemetry-ext-flask/src/opentelemetry/ext/flask/__init__.py"}]}