We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Ignore exception attributes for a given route or status (Pyramid)
It would be convenient to provide a way to specify routes and/or status codes are not exceptional.

A common pattern within Pyramid is to raise the desired response for certain business flows (e.g. raise a 301 to force a redirect, raise a 204 after deleting an entity, etc) rather than properly return. Because the response was raised and handled as an `HTTPException` the span is decorated with exception attributes even though the request was not actually exceptional. This causes many vendors to interpret a false-positive when calculating things like error rate.


There is history of this functionality in other instrumentation:

 - https://ddtrace.readthedocs.io/en/stable/advanced_usage.html#custom-error-codes
 - https://docs.newrelic.com/docs/apm/agents/python-agent/configuration/python-agent-configuration/#error-ignore
 - https://docs.newrelic.com/docs/apm/agents/python-agent/configuration/python-agent-configuration/#error-ignore-status-codes


--- 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-pyramid/src/opentelemetry/instrumentation/pyramid/callbacks.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 from logging import getLogger
16 
17 from pyramid.events import BeforeTraversal
18 from pyramid.httpexceptions import HTTPError, HTTPException
19 from pyramid.settings import asbool
20 from pyramid.tweens import EXCVIEW
21 
22 import opentelemetry.instrumentation.wsgi as otel_wsgi
23 from opentelemetry import context, trace
24 from opentelemetry.instrumentation.propagators import (
25     get_global_response_propagator,
26 )
27 from opentelemetry.instrumentation.pyramid.version import __version__
28 from opentelemetry.instrumentation.utils import _start_internal_or_server_span
29 from opentelemetry.semconv.trace import SpanAttributes
30 from opentelemetry.util._time import _time_ns
31 from opentelemetry.util.http import get_excluded_urls
32 
33 TWEEN_NAME = "opentelemetry.instrumentation.pyramid.trace_tween_factory"
34 SETTING_TRACE_ENABLED = "opentelemetry-pyramid.trace_enabled"
35 
36 _ENVIRON_STARTTIME_KEY = "opentelemetry-pyramid.starttime_key"
37 _ENVIRON_SPAN_KEY = "opentelemetry-pyramid.span_key"
38 _ENVIRON_ACTIVATION_KEY = "opentelemetry-pyramid.activation_key"
39 _ENVIRON_ENABLED_KEY = "opentelemetry-pyramid.tracing_enabled_key"
40 _ENVIRON_TOKEN = "opentelemetry-pyramid.token"
41 
42 _logger = getLogger(__name__)
43 
44 
45 _excluded_urls = get_excluded_urls("PYRAMID")
46 
47 
48 def includeme(config):
49     config.add_settings({SETTING_TRACE_ENABLED: True})
50 
51     config.add_subscriber(_before_traversal, BeforeTraversal)
52     _insert_tween(config)
53 
54 
55 def _insert_tween(config):
56     settings = config.get_settings()
57     tweens = settings.get("pyramid.tweens")
58     # If the list is empty, pyramid does not consider the tweens have been
59     # set explicitly. And if our tween is already there, nothing to do
60     if not tweens or not tweens.strip():
61         # Add our tween just before the default exception handler
62         config.add_tween(TWEEN_NAME, over=EXCVIEW)
63 
64 
65 def _before_traversal(event):
66     request = event.request
67     request_environ = request.environ
68     span_name = otel_wsgi.get_default_span_name(request_environ)
69 
70     enabled = request_environ.get(_ENVIRON_ENABLED_KEY)
71     if enabled is None:
72         _logger.warning(
73             "Opentelemetry pyramid tween 'opentelemetry.instrumentation.pyramid.trace_tween_factory'"
74             "was not called. Make sure that the tween is included in 'pyramid.tweens' if"
75             "the tween list was created manually"
76         )
77         return
78 
79     if not enabled:
80         # Tracing not enabled, return
81         return
82 
83     start_time = request_environ.get(_ENVIRON_STARTTIME_KEY)
84     tracer = trace.get_tracer(__name__, __version__)
85 
86     if request.matched_route:
87         span_name = request.matched_route.pattern
88     else:
89         span_name = otel_wsgi.get_default_span_name(request_environ)
90 
91     span, token = _start_internal_or_server_span(
92         tracer=tracer,
93         span_name=span_name,
94         start_time=start_time,
95         context_carrier=request_environ,
96         context_getter=otel_wsgi.wsgi_getter,
97     )
98 
99     if span.is_recording():
100         attributes = otel_wsgi.collect_request_attributes(request_environ)
101         if request.matched_route:
102             attributes[
103                 SpanAttributes.HTTP_ROUTE
104             ] = request.matched_route.pattern
105         for key, value in attributes.items():
106             span.set_attribute(key, value)
107         if span.kind == trace.SpanKind.SERVER:
108             custom_attributes = (
109                 otel_wsgi.collect_custom_request_headers_attributes(
110                     request_environ
111                 )
112             )
113             if len(custom_attributes) > 0:
114                 span.set_attributes(custom_attributes)
115 
116     activation = trace.use_span(span, end_on_exit=True)
117     activation.__enter__()  # pylint: disable=E1101
118     request_environ[_ENVIRON_ACTIVATION_KEY] = activation
119     request_environ[_ENVIRON_SPAN_KEY] = span
120     if token:
121         request_environ[_ENVIRON_TOKEN] = token
122 
123 
124 def trace_tween_factory(handler, registry):
125     settings = registry.settings
126     enabled = asbool(settings.get(SETTING_TRACE_ENABLED, True))
127 
128     if not enabled:
129         # If disabled, make a tween that signals to the
130         # BeforeTraversal subscriber that tracing is disabled
131         def disabled_tween(request):
132             request.environ[_ENVIRON_ENABLED_KEY] = False
133             return handler(request)
134 
135         return disabled_tween
136 
137     # make a request tracing function
138     # pylint: disable=too-many-branches
139     def trace_tween(request):
140         # pylint: disable=E1101
141         if _excluded_urls.url_disabled(request.url):
142             request.environ[_ENVIRON_ENABLED_KEY] = False
143             # short-circuit when we don't want to trace anything
144             return handler(request)
145 
146         request.environ[_ENVIRON_ENABLED_KEY] = True
147         request.environ[_ENVIRON_STARTTIME_KEY] = _time_ns()
148 
149         response = None
150         status = None
151 
152         try:
153             response = handler(request)
154         except HTTPException as exc:
155             # If the exception is a pyramid HTTPException,
156             # that's still valuable information that isn't necessarily
157             # a 500. For instance, HTTPFound is a 302.
158             # As described in docs, Pyramid exceptions are all valid
159             # response types
160             response = exc
161             raise
162         except BaseException:
163             # In the case that a non-HTTPException is bubbled up we
164             # should infer a internal server error and raise
165             status = "500 InternalServerError"
166             raise
167         finally:
168             span = request.environ.get(_ENVIRON_SPAN_KEY)
169             enabled = request.environ.get(_ENVIRON_ENABLED_KEY)
170             if not span and enabled:
171                 _logger.warning(
172                     "Pyramid environ's OpenTelemetry span missing."
173                     "If the OpenTelemetry tween was added manually, make sure"
174                     "PyramidInstrumentor().instrument_config(config) is called"
175                 )
176             elif enabled:
177                 status = getattr(response, "status", status)
178 
179                 if status is not None:
180                     otel_wsgi.add_response_attributes(
181                         span,
182                         status,
183                         getattr(response, "headerlist", None),
184                     )
185 
186                 if span.is_recording() and span.kind == trace.SpanKind.SERVER:
187                     custom_attributes = (
188                         otel_wsgi.collect_custom_response_headers_attributes(
189                             getattr(response, "headerlist", None)
190                         )
191                     )
192                     if len(custom_attributes) > 0:
193                         span.set_attributes(custom_attributes)
194 
195                 propagator = get_global_response_propagator()
196                 if propagator and hasattr(response, "headers"):
197                     propagator.inject(response.headers)
198 
199                 activation = request.environ.get(_ENVIRON_ACTIVATION_KEY)
200 
201                 # Only considering HTTPClientError and HTTPServerError
202                 # to make sure HTTPRedirection is not reported as error
203                 if isinstance(response, HTTPError):
204                     activation.__exit__(
205                         type(response),
206                         response,
207                         getattr(response, "__traceback__", None),
208                     )
209                 else:
210                     activation.__exit__(None, None, None)
211 
212                 env_token = request.environ.get(_ENVIRON_TOKEN, None)
213                 if env_token is not None:
214                     context.detach(env_token)
215 
216         return response
217 
218     return trace_tween
219
```
--- 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-pyramid/src/opentelemetry/instrumentation/pyramid/callbacks.py b/instrumentation/opentelemetry-instrumentation-pyramid/src/opentelemetry/instrumentation/pyramid/callbacks.py
--- a/instrumentation/opentelemetry-instrumentation-pyramid/src/opentelemetry/instrumentation/pyramid/callbacks.py
+++ b/instrumentation/opentelemetry-instrumentation-pyramid/src/opentelemetry/instrumentation/pyramid/callbacks.py
@@ -15,7 +15,7 @@
 from logging import getLogger
 
 from pyramid.events import BeforeTraversal
-from pyramid.httpexceptions import HTTPError, HTTPException
+from pyramid.httpexceptions import HTTPException, HTTPServerError
 from pyramid.settings import asbool
 from pyramid.tweens import EXCVIEW
 
@@ -198,9 +198,9 @@
 
                 activation = request.environ.get(_ENVIRON_ACTIVATION_KEY)
 
-                # Only considering HTTPClientError and HTTPServerError
-                # to make sure HTTPRedirection is not reported as error
-                if isinstance(response, HTTPError):
+                # Only considering HTTPServerError
+                # to make sure 200, 300 and 400 exceptions are not reported as error
+                if isinstance(response, HTTPServerError):
                     activation.__exit__(
                         type(response),
                         response,

{"golden_diff": "diff --git a/instrumentation/opentelemetry-instrumentation-pyramid/src/opentelemetry/instrumentation/pyramid/callbacks.py b/instrumentation/opentelemetry-instrumentation-pyramid/src/opentelemetry/instrumentation/pyramid/callbacks.py\n--- a/instrumentation/opentelemetry-instrumentation-pyramid/src/opentelemetry/instrumentation/pyramid/callbacks.py\n+++ b/instrumentation/opentelemetry-instrumentation-pyramid/src/opentelemetry/instrumentation/pyramid/callbacks.py\n@@ -15,7 +15,7 @@\n from logging import getLogger\n \n from pyramid.events import BeforeTraversal\n-from pyramid.httpexceptions import HTTPError, HTTPException\n+from pyramid.httpexceptions import HTTPException, HTTPServerError\n from pyramid.settings import asbool\n from pyramid.tweens import EXCVIEW\n \n@@ -198,9 +198,9 @@\n \n                 activation = request.environ.get(_ENVIRON_ACTIVATION_KEY)\n \n-                # Only considering HTTPClientError and HTTPServerError\n-                # to make sure HTTPRedirection is not reported as error\n-                if isinstance(response, HTTPError):\n+                # Only considering HTTPServerError\n+                # to make sure 200, 300 and 400 exceptions are not reported as error\n+                if isinstance(response, HTTPServerError):\n                     activation.__exit__(\n                         type(response),\n                         response,\n", "issue": "Ignore exception attributes for a given route or status (Pyramid)\nIt would be convenient to provide a way to specify routes and/or status codes are not exceptional.\r\n\r\nA common pattern within Pyramid is to raise the desired response for certain business flows (e.g. raise a 301 to force a redirect, raise a 204 after deleting an entity, etc) rather than properly return. Because the response was raised and handled as an `HTTPException` the span is decorated with exception attributes even though the request was not actually exceptional. This causes many vendors to interpret a false-positive when calculating things like error rate.\r\n\r\n\r\nThere is history of this functionality in other instrumentation:\r\n\r\n - https://ddtrace.readthedocs.io/en/stable/advanced_usage.html#custom-error-codes\r\n - https://docs.newrelic.com/docs/apm/agents/python-agent/configuration/python-agent-configuration/#error-ignore\r\n - https://docs.newrelic.com/docs/apm/agents/python-agent/configuration/python-agent-configuration/#error-ignore-status-codes\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\nfrom logging import getLogger\n\nfrom pyramid.events import BeforeTraversal\nfrom pyramid.httpexceptions import HTTPError, HTTPException\nfrom pyramid.settings import asbool\nfrom pyramid.tweens import EXCVIEW\n\nimport opentelemetry.instrumentation.wsgi as otel_wsgi\nfrom opentelemetry import context, trace\nfrom opentelemetry.instrumentation.propagators import (\n    get_global_response_propagator,\n)\nfrom opentelemetry.instrumentation.pyramid.version import __version__\nfrom opentelemetry.instrumentation.utils import _start_internal_or_server_span\nfrom opentelemetry.semconv.trace import SpanAttributes\nfrom opentelemetry.util._time import _time_ns\nfrom opentelemetry.util.http import get_excluded_urls\n\nTWEEN_NAME = \"opentelemetry.instrumentation.pyramid.trace_tween_factory\"\nSETTING_TRACE_ENABLED = \"opentelemetry-pyramid.trace_enabled\"\n\n_ENVIRON_STARTTIME_KEY = \"opentelemetry-pyramid.starttime_key\"\n_ENVIRON_SPAN_KEY = \"opentelemetry-pyramid.span_key\"\n_ENVIRON_ACTIVATION_KEY = \"opentelemetry-pyramid.activation_key\"\n_ENVIRON_ENABLED_KEY = \"opentelemetry-pyramid.tracing_enabled_key\"\n_ENVIRON_TOKEN = \"opentelemetry-pyramid.token\"\n\n_logger = getLogger(__name__)\n\n\n_excluded_urls = get_excluded_urls(\"PYRAMID\")\n\n\ndef includeme(config):\n    config.add_settings({SETTING_TRACE_ENABLED: True})\n\n    config.add_subscriber(_before_traversal, BeforeTraversal)\n    _insert_tween(config)\n\n\ndef _insert_tween(config):\n    settings = config.get_settings()\n    tweens = settings.get(\"pyramid.tweens\")\n    # If the list is empty, pyramid does not consider the tweens have been\n    # set explicitly. And if our tween is already there, nothing to do\n    if not tweens or not tweens.strip():\n        # Add our tween just before the default exception handler\n        config.add_tween(TWEEN_NAME, over=EXCVIEW)\n\n\ndef _before_traversal(event):\n    request = event.request\n    request_environ = request.environ\n    span_name = otel_wsgi.get_default_span_name(request_environ)\n\n    enabled = request_environ.get(_ENVIRON_ENABLED_KEY)\n    if enabled is None:\n        _logger.warning(\n            \"Opentelemetry pyramid tween 'opentelemetry.instrumentation.pyramid.trace_tween_factory'\"\n            \"was not called. Make sure that the tween is included in 'pyramid.tweens' if\"\n            \"the tween list was created manually\"\n        )\n        return\n\n    if not enabled:\n        # Tracing not enabled, return\n        return\n\n    start_time = request_environ.get(_ENVIRON_STARTTIME_KEY)\n    tracer = trace.get_tracer(__name__, __version__)\n\n    if request.matched_route:\n        span_name = request.matched_route.pattern\n    else:\n        span_name = otel_wsgi.get_default_span_name(request_environ)\n\n    span, token = _start_internal_or_server_span(\n        tracer=tracer,\n        span_name=span_name,\n        start_time=start_time,\n        context_carrier=request_environ,\n        context_getter=otel_wsgi.wsgi_getter,\n    )\n\n    if span.is_recording():\n        attributes = otel_wsgi.collect_request_attributes(request_environ)\n        if request.matched_route:\n            attributes[\n                SpanAttributes.HTTP_ROUTE\n            ] = request.matched_route.pattern\n        for key, value in attributes.items():\n            span.set_attribute(key, value)\n        if span.kind == trace.SpanKind.SERVER:\n            custom_attributes = (\n                otel_wsgi.collect_custom_request_headers_attributes(\n                    request_environ\n                )\n            )\n            if len(custom_attributes) > 0:\n                span.set_attributes(custom_attributes)\n\n    activation = trace.use_span(span, end_on_exit=True)\n    activation.__enter__()  # pylint: disable=E1101\n    request_environ[_ENVIRON_ACTIVATION_KEY] = activation\n    request_environ[_ENVIRON_SPAN_KEY] = span\n    if token:\n        request_environ[_ENVIRON_TOKEN] = token\n\n\ndef trace_tween_factory(handler, registry):\n    settings = registry.settings\n    enabled = asbool(settings.get(SETTING_TRACE_ENABLED, True))\n\n    if not enabled:\n        # If disabled, make a tween that signals to the\n        # BeforeTraversal subscriber that tracing is disabled\n        def disabled_tween(request):\n            request.environ[_ENVIRON_ENABLED_KEY] = False\n            return handler(request)\n\n        return disabled_tween\n\n    # make a request tracing function\n    # pylint: disable=too-many-branches\n    def trace_tween(request):\n        # pylint: disable=E1101\n        if _excluded_urls.url_disabled(request.url):\n            request.environ[_ENVIRON_ENABLED_KEY] = False\n            # short-circuit when we don't want to trace anything\n            return handler(request)\n\n        request.environ[_ENVIRON_ENABLED_KEY] = True\n        request.environ[_ENVIRON_STARTTIME_KEY] = _time_ns()\n\n        response = None\n        status = None\n\n        try:\n            response = handler(request)\n        except HTTPException as exc:\n            # If the exception is a pyramid HTTPException,\n            # that's still valuable information that isn't necessarily\n            # a 500. For instance, HTTPFound is a 302.\n            # As described in docs, Pyramid exceptions are all valid\n            # response types\n            response = exc\n            raise\n        except BaseException:\n            # In the case that a non-HTTPException is bubbled up we\n            # should infer a internal server error and raise\n            status = \"500 InternalServerError\"\n            raise\n        finally:\n            span = request.environ.get(_ENVIRON_SPAN_KEY)\n            enabled = request.environ.get(_ENVIRON_ENABLED_KEY)\n            if not span and enabled:\n                _logger.warning(\n                    \"Pyramid environ's OpenTelemetry span missing.\"\n                    \"If the OpenTelemetry tween was added manually, make sure\"\n                    \"PyramidInstrumentor().instrument_config(config) is called\"\n                )\n            elif enabled:\n                status = getattr(response, \"status\", status)\n\n                if status is not None:\n                    otel_wsgi.add_response_attributes(\n                        span,\n                        status,\n                        getattr(response, \"headerlist\", None),\n                    )\n\n                if span.is_recording() and span.kind == trace.SpanKind.SERVER:\n                    custom_attributes = (\n                        otel_wsgi.collect_custom_response_headers_attributes(\n                            getattr(response, \"headerlist\", None)\n                        )\n                    )\n                    if len(custom_attributes) > 0:\n                        span.set_attributes(custom_attributes)\n\n                propagator = get_global_response_propagator()\n                if propagator and hasattr(response, \"headers\"):\n                    propagator.inject(response.headers)\n\n                activation = request.environ.get(_ENVIRON_ACTIVATION_KEY)\n\n                # Only considering HTTPClientError and HTTPServerError\n                # to make sure HTTPRedirection is not reported as error\n                if isinstance(response, HTTPError):\n                    activation.__exit__(\n                        type(response),\n                        response,\n                        getattr(response, \"__traceback__\", None),\n                    )\n                else:\n                    activation.__exit__(None, None, None)\n\n                env_token = request.environ.get(_ENVIRON_TOKEN, None)\n                if env_token is not None:\n                    context.detach(env_token)\n\n        return response\n\n    return trace_tween\n", "path": "instrumentation/opentelemetry-instrumentation-pyramid/src/opentelemetry/instrumentation/pyramid/callbacks.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\nfrom logging import getLogger\n\nfrom pyramid.events import BeforeTraversal\nfrom pyramid.httpexceptions import HTTPException, HTTPServerError\nfrom pyramid.settings import asbool\nfrom pyramid.tweens import EXCVIEW\n\nimport opentelemetry.instrumentation.wsgi as otel_wsgi\nfrom opentelemetry import context, trace\nfrom opentelemetry.instrumentation.propagators import (\n    get_global_response_propagator,\n)\nfrom opentelemetry.instrumentation.pyramid.version import __version__\nfrom opentelemetry.instrumentation.utils import _start_internal_or_server_span\nfrom opentelemetry.semconv.trace import SpanAttributes\nfrom opentelemetry.util._time import _time_ns\nfrom opentelemetry.util.http import get_excluded_urls\n\nTWEEN_NAME = \"opentelemetry.instrumentation.pyramid.trace_tween_factory\"\nSETTING_TRACE_ENABLED = \"opentelemetry-pyramid.trace_enabled\"\n\n_ENVIRON_STARTTIME_KEY = \"opentelemetry-pyramid.starttime_key\"\n_ENVIRON_SPAN_KEY = \"opentelemetry-pyramid.span_key\"\n_ENVIRON_ACTIVATION_KEY = \"opentelemetry-pyramid.activation_key\"\n_ENVIRON_ENABLED_KEY = \"opentelemetry-pyramid.tracing_enabled_key\"\n_ENVIRON_TOKEN = \"opentelemetry-pyramid.token\"\n\n_logger = getLogger(__name__)\n\n\n_excluded_urls = get_excluded_urls(\"PYRAMID\")\n\n\ndef includeme(config):\n    config.add_settings({SETTING_TRACE_ENABLED: True})\n\n    config.add_subscriber(_before_traversal, BeforeTraversal)\n    _insert_tween(config)\n\n\ndef _insert_tween(config):\n    settings = config.get_settings()\n    tweens = settings.get(\"pyramid.tweens\")\n    # If the list is empty, pyramid does not consider the tweens have been\n    # set explicitly. And if our tween is already there, nothing to do\n    if not tweens or not tweens.strip():\n        # Add our tween just before the default exception handler\n        config.add_tween(TWEEN_NAME, over=EXCVIEW)\n\n\ndef _before_traversal(event):\n    request = event.request\n    request_environ = request.environ\n    span_name = otel_wsgi.get_default_span_name(request_environ)\n\n    enabled = request_environ.get(_ENVIRON_ENABLED_KEY)\n    if enabled is None:\n        _logger.warning(\n            \"Opentelemetry pyramid tween 'opentelemetry.instrumentation.pyramid.trace_tween_factory'\"\n            \"was not called. Make sure that the tween is included in 'pyramid.tweens' if\"\n            \"the tween list was created manually\"\n        )\n        return\n\n    if not enabled:\n        # Tracing not enabled, return\n        return\n\n    start_time = request_environ.get(_ENVIRON_STARTTIME_KEY)\n    tracer = trace.get_tracer(__name__, __version__)\n\n    if request.matched_route:\n        span_name = request.matched_route.pattern\n    else:\n        span_name = otel_wsgi.get_default_span_name(request_environ)\n\n    span, token = _start_internal_or_server_span(\n        tracer=tracer,\n        span_name=span_name,\n        start_time=start_time,\n        context_carrier=request_environ,\n        context_getter=otel_wsgi.wsgi_getter,\n    )\n\n    if span.is_recording():\n        attributes = otel_wsgi.collect_request_attributes(request_environ)\n        if request.matched_route:\n            attributes[\n                SpanAttributes.HTTP_ROUTE\n            ] = request.matched_route.pattern\n        for key, value in attributes.items():\n            span.set_attribute(key, value)\n        if span.kind == trace.SpanKind.SERVER:\n            custom_attributes = (\n                otel_wsgi.collect_custom_request_headers_attributes(\n                    request_environ\n                )\n            )\n            if len(custom_attributes) > 0:\n                span.set_attributes(custom_attributes)\n\n    activation = trace.use_span(span, end_on_exit=True)\n    activation.__enter__()  # pylint: disable=E1101\n    request_environ[_ENVIRON_ACTIVATION_KEY] = activation\n    request_environ[_ENVIRON_SPAN_KEY] = span\n    if token:\n        request_environ[_ENVIRON_TOKEN] = token\n\n\ndef trace_tween_factory(handler, registry):\n    settings = registry.settings\n    enabled = asbool(settings.get(SETTING_TRACE_ENABLED, True))\n\n    if not enabled:\n        # If disabled, make a tween that signals to the\n        # BeforeTraversal subscriber that tracing is disabled\n        def disabled_tween(request):\n            request.environ[_ENVIRON_ENABLED_KEY] = False\n            return handler(request)\n\n        return disabled_tween\n\n    # make a request tracing function\n    # pylint: disable=too-many-branches\n    def trace_tween(request):\n        # pylint: disable=E1101\n        if _excluded_urls.url_disabled(request.url):\n            request.environ[_ENVIRON_ENABLED_KEY] = False\n            # short-circuit when we don't want to trace anything\n            return handler(request)\n\n        request.environ[_ENVIRON_ENABLED_KEY] = True\n        request.environ[_ENVIRON_STARTTIME_KEY] = _time_ns()\n\n        response = None\n        status = None\n\n        try:\n            response = handler(request)\n        except HTTPException as exc:\n            # If the exception is a pyramid HTTPException,\n            # that's still valuable information that isn't necessarily\n            # a 500. For instance, HTTPFound is a 302.\n            # As described in docs, Pyramid exceptions are all valid\n            # response types\n            response = exc\n            raise\n        except BaseException:\n            # In the case that a non-HTTPException is bubbled up we\n            # should infer a internal server error and raise\n            status = \"500 InternalServerError\"\n            raise\n        finally:\n            span = request.environ.get(_ENVIRON_SPAN_KEY)\n            enabled = request.environ.get(_ENVIRON_ENABLED_KEY)\n            if not span and enabled:\n                _logger.warning(\n                    \"Pyramid environ's OpenTelemetry span missing.\"\n                    \"If the OpenTelemetry tween was added manually, make sure\"\n                    \"PyramidInstrumentor().instrument_config(config) is called\"\n                )\n            elif enabled:\n                status = getattr(response, \"status\", status)\n\n                if status is not None:\n                    otel_wsgi.add_response_attributes(\n                        span,\n                        status,\n                        getattr(response, \"headerlist\", None),\n                    )\n\n                if span.is_recording() and span.kind == trace.SpanKind.SERVER:\n                    custom_attributes = (\n                        otel_wsgi.collect_custom_response_headers_attributes(\n                            getattr(response, \"headerlist\", None)\n                        )\n                    )\n                    if len(custom_attributes) > 0:\n                        span.set_attributes(custom_attributes)\n\n                propagator = get_global_response_propagator()\n                if propagator and hasattr(response, \"headers\"):\n                    propagator.inject(response.headers)\n\n                activation = request.environ.get(_ENVIRON_ACTIVATION_KEY)\n\n                # Only considering HTTPServerError\n                # to make sure 200, 300 and 400 exceptions are not reported as error\n                if isinstance(response, HTTPServerError):\n                    activation.__exit__(\n                        type(response),\n                        response,\n                        getattr(response, \"__traceback__\", None),\n                    )\n                else:\n                    activation.__exit__(None, None, None)\n\n                env_token = request.environ.get(_ENVIRON_TOKEN, None)\n                if env_token is not None:\n                    context.detach(env_token)\n\n        return response\n\n    return trace_tween\n", "path": "instrumentation/opentelemetry-instrumentation-pyramid/src/opentelemetry/instrumentation/pyramid/callbacks.py"}]}