Repository: open-telemetry/opentelemetry-python-contrib

Issue:
Allow configuring header extraction via runtime parameters
Currently, web framework instrumentations only allow configuring header extraction via environment variables. I want to add the option to configure them via constructor parameters. I think the code should be quite straightforward but I want to check if this would be an acceptable change before making a  PR. Some background on why it wasn't implemented in the first place might also be helpful if it's something other than 'no one asked for it yet'.


Discussion:
@lzchen @srikanthccv any thoughts? I don't see any reason why it couldn't be added as a runtime parameter as well.
I think we followed what Java did at the time and used the same environment variables for consistency. We already support configuring some capabilities such as excluding URLs with construction params. This change is fine. Please send a pull request. 

Resolving patch:
diff --git a/instrumentation/opentelemetry-instrumentation-asgi/src/opentelemetry/instrumentation/asgi/__init__.py b/instrumentation/opentelemetry-instrumentation-asgi/src/opentelemetry/instrumentation/asgi/__init__.py
--- a/instrumentation/opentelemetry-instrumentation-asgi/src/opentelemetry/instrumentation/asgi/__init__.py
+++ b/instrumentation/opentelemetry-instrumentation-asgi/src/opentelemetry/instrumentation/asgi/__init__.py
@@ -189,11 +189,13 @@ def client_response_hook(span: Span, message: dict):
 ---
 """
 
+from __future__ import annotations
+
 import typing
 import urllib
 from functools import wraps
 from timeit import default_timer
-from typing import Tuple
+from typing import Any, Awaitable, Callable, Tuple, cast
 
 from asgiref.compatibility import guarantee_single_callable
 
@@ -332,55 +334,28 @@ def collect_request_attributes(scope):
     return result
 
 
-def collect_custom_request_headers_attributes(scope):
-    """returns custom HTTP request headers to be added into SERVER span as span attributes
-    Refer specification https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/http.md#http-request-and-response-headers
+def collect_custom_headers_attributes(
+    scope_or_response_message: dict[str, Any],
+    sanitize: SanitizeValue,
+    header_regexes: list[str],
+    normalize_names: Callable[[str], str],
+) -> dict[str, str]:
     """
+    Returns custom HTTP request or response headers to be added into SERVER span as span attributes.
 
-    sanitize = SanitizeValue(
-        get_custom_headers(
-            OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SANITIZE_FIELDS
-        )
-    )
-
-    # Decode headers before processing.
-    headers = {
-        _key.decode("utf8"): _value.decode("utf8")
-        for (_key, _value) in scope.get("headers")
-    }
-
-    return sanitize.sanitize_header_values(
-        headers,
-        get_custom_headers(
-            OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_REQUEST
-        ),
-        normalise_request_header_name,
-    )
-
-
-def collect_custom_response_headers_attributes(message):
-    """returns custom HTTP response headers to be added into SERVER span as span attributes
-    Refer specification https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/http.md#http-request-and-response-headers
+    Refer specifications:
+     - https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/http.md#http-request-and-response-headers
     """
-
-    sanitize = SanitizeValue(
-        get_custom_headers(
-            OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SANITIZE_FIELDS
-        )
-    )
-
     # Decode headers before processing.
-    headers = {
+    headers: dict[str, str] = {
         _key.decode("utf8"): _value.decode("utf8")
-        for (_key, _value) in message.get("headers")
+        for (_key, _value) in scope_or_response_message.get("headers")
+        or cast("list[tuple[bytes, bytes]]", [])
     }
-
     return sanitize.sanitize_header_values(
         headers,
-        get_custom_headers(
-            OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_RESPONSE
-        ),
-        normalise_response_header_name,
+        header_regexes,
+        normalize_names,
     )
 
 
@@ -493,6 +468,9 @@ def __init__(
         tracer_provider=None,
         meter_provider=None,
         meter=None,
+        http_capture_headers_server_request: list[str] | None = None,
+        http_capture_headers_server_response: list[str] | None = None,
+        http_capture_headers_sanitize_fields: list[str] | None = None,
     ):
         self.app = guarantee_single_callable(app)
         self.tracer = trace.get_tracer(
@@ -540,7 +518,41 @@ def __init__(
         self.client_response_hook = client_response_hook
         self.content_length_header = None
 
-    async def __call__(self, scope, receive, send):
+        # Environment variables as constructor parameters
+        self.http_capture_headers_server_request = (
+            http_capture_headers_server_request
+            or (
+                get_custom_headers(
+                    OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_REQUEST
+                )
+            )
+            or None
+        )
+        self.http_capture_headers_server_response = (
+            http_capture_headers_server_response
+            or (
+                get_custom_headers(
+                    OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_RESPONSE
+                )
+            )
+            or None
+        )
+        self.http_capture_headers_sanitize_fields = SanitizeValue(
+            http_capture_headers_sanitize_fields
+            or (
+                get_custom_headers(
+                    OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SANITIZE_FIELDS
+                )
+            )
+            or []
+        )
+
+    async def __call__(
+        self,
+        scope: dict[str, Any],
+        receive: Callable[[], Awaitable[dict[str, Any]]],
+        send: Callable[[dict[str, Any]], Awaitable[None]],
+    ) -> None:
         """The ASGI application
 
         Args:
@@ -583,7 +595,14 @@ async def __call__(self, scope, receive, send):
 
                     if current_span.kind == trace.SpanKind.SERVER:
                         custom_attributes = (
-                            collect_custom_request_headers_attributes(scope)
+                            collect_custom_headers_attributes(
+                                scope,
+                                self.http_capture_headers_sanitize_fields,
+                                self.http_capture_headers_server_request,
+                                normalise_request_header_name,
+                            )
+                            if self.http_capture_headers_server_request
+                            else {}
                         )
                         if len(custom_attributes) > 0:
                             current_span.set_attributes(custom_attributes)
@@ -658,7 +677,7 @@ def _get_otel_send(
         expecting_trailers = False
 
         @wraps(send)
-        async def otel_send(message):
+        async def otel_send(message: dict[str, Any]):
             nonlocal expecting_trailers
             with self.tracer.start_as_current_span(
                 " ".join((server_span_name, scope["type"], "send"))
@@ -685,7 +704,14 @@ async def otel_send(message):
                         and "headers" in message
                     ):
                         custom_response_attributes = (
-                            collect_custom_response_headers_attributes(message)
+                            collect_custom_headers_attributes(
+                                message,
+                                self.http_capture_headers_sanitize_fields,
+                                self.http_capture_headers_server_response,
+                                normalise_response_header_name,
+                            )
+                            if self.http_capture_headers_server_response
+                            else {}
                         )
                         if len(custom_response_attributes) > 0:
                             server_span.set_attributes(
diff --git a/instrumentation/opentelemetry-instrumentation-django/src/opentelemetry/instrumentation/django/middleware/otel_middleware.py b/instrumentation/opentelemetry-instrumentation-django/src/opentelemetry/instrumentation/django/middleware/otel_middleware.py
--- a/instrumentation/opentelemetry-instrumentation-django/src/opentelemetry/instrumentation/django/middleware/otel_middleware.py
+++ b/instrumentation/opentelemetry-instrumentation-django/src/opentelemetry/instrumentation/django/middleware/otel_middleware.py
@@ -43,10 +43,17 @@
 from opentelemetry.semconv.trace import SpanAttributes
 from opentelemetry.trace import Span, SpanKind, use_span
 from opentelemetry.util.http import (
+    OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SANITIZE_FIELDS,
+    OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_REQUEST,
+    OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_RESPONSE,
+    SanitizeValue,
     _parse_active_request_count_attrs,
     _parse_duration_attrs,
+    get_custom_headers,
     get_excluded_urls,
     get_traced_request_attrs,
+    normalise_request_header_name,
+    normalise_response_header_name,
 )
 
 try:
@@ -91,10 +98,7 @@ def __call__(self, request):
 try:
     from opentelemetry.instrumentation.asgi import asgi_getter, asgi_setter
     from opentelemetry.instrumentation.asgi import (
-        collect_custom_request_headers_attributes as asgi_collect_custom_request_attributes,
-    )
-    from opentelemetry.instrumentation.asgi import (
-        collect_custom_response_headers_attributes as asgi_collect_custom_response_attributes,
+        collect_custom_headers_attributes as asgi_collect_custom_headers_attributes,
     )
     from opentelemetry.instrumentation.asgi import (
         collect_request_attributes as asgi_collect_request_attributes,
@@ -108,7 +112,6 @@ def __call__(self, request):
     set_status_code = None
     _is_asgi_supported = False
 
-
 _logger = getLogger(__name__)
 _attributes_by_preference = [
     [
@@ -249,7 +252,18 @@ def process_request(self, request):
                 )
                 if span.is_recording() and span.kind == SpanKind.SERVER:
                     attributes.update(
-                        asgi_collect_custom_request_attributes(carrier)
+                        asgi_collect_custom_headers_attributes(
+                            carrier,
+                            SanitizeValue(
+                                get_custom_headers(
+                                    OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SANITIZE_FIELDS
+                                )
+                            ),
+                            get_custom_headers(
+                                OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_REQUEST
+                            ),
+                            normalise_request_header_name,
+                        )
                     )
             else:
                 if span.is_recording() and span.kind == SpanKind.SERVER:
@@ -336,8 +350,17 @@ def process_response(self, request, response):
                     for key, value in response.items():
                         asgi_setter.set(custom_headers, key, value)
 
-                    custom_res_attributes = (
-                        asgi_collect_custom_response_attributes(custom_headers)
+                    custom_res_attributes = asgi_collect_custom_headers_attributes(
+                        custom_headers,
+                        SanitizeValue(
+                            get_custom_headers(
+                                OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SANITIZE_FIELDS
+                            )
+                        ),
+                        get_custom_headers(
+                            OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_RESPONSE
+                        ),
+                        normalise_response_header_name,
                     )
                     for key, value in custom_res_attributes.items():
                         span.set_attribute(key, value)
diff --git a/util/opentelemetry-util-http/src/opentelemetry/util/http/__init__.py b/util/opentelemetry-util-http/src/opentelemetry/util/http/__init__.py
--- a/util/opentelemetry-util-http/src/opentelemetry/util/http/__init__.py
+++ b/util/opentelemetry-util-http/src/opentelemetry/util/http/__init__.py
@@ -12,11 +12,13 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+from __future__ import annotations
+
 from os import environ
 from re import IGNORECASE as RE_IGNORECASE
 from re import compile as re_compile
 from re import search
-from typing import Iterable, List, Optional
+from typing import Callable, Iterable, Optional
 from urllib.parse import urlparse, urlunparse
 
 from opentelemetry.semconv.trace import SpanAttributes
@@ -84,9 +86,12 @@ def sanitize_header_value(self, header: str, value: str) -> str:
         )
 
     def sanitize_header_values(
-        self, headers: dict, header_regexes: list, normalize_function: callable
-    ) -> dict:
-        values = {}
+        self,
+        headers: dict[str, str],
+        header_regexes: list[str],
+        normalize_function: Callable[[str], str],
+    ) -> dict[str, str]:
+        values: dict[str, str] = {}
 
         if header_regexes:
             header_regexes_compiled = re_compile(
@@ -216,14 +221,14 @@ def sanitize_method(method: Optional[str]) -> Optional[str]:
     return "UNKNOWN"
 
 
-def get_custom_headers(env_var: str) -> List[str]:
-    custom_headers = environ.get(env_var, [])
+def get_custom_headers(env_var: str) -> list[str]:
+    custom_headers = environ.get(env_var, None)
     if custom_headers:
-        custom_headers = [
+        return [
             custom_headers.strip()
             for custom_headers in custom_headers.split(",")
         ]
-    return custom_headers
+    return []
 
 
 def _parse_active_request_count_attrs(req_attrs):


Test patch:
diff --git a/instrumentation/opentelemetry-instrumentation-asgi/tests/test_asgi_custom_headers.py b/instrumentation/opentelemetry-instrumentation-asgi/tests/test_asgi_custom_headers.py
--- a/instrumentation/opentelemetry-instrumentation-asgi/tests/test_asgi_custom_headers.py
+++ b/instrumentation/opentelemetry-instrumentation-asgi/tests/test_asgi_custom_headers.py
@@ -1,4 +1,4 @@
-from unittest import mock
+import os
 
 import opentelemetry.instrumentation.asgi as otel_asgi
 from opentelemetry.test.asgitestutil import AsgiTestBase
@@ -72,21 +72,22 @@ async def websocket_app_with_custom_headers(scope, receive, send):
             break
 
 
-@mock.patch.dict(
-    "os.environ",
-    {
-        OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SANITIZE_FIELDS: ".*my-secret.*",
-        OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_REQUEST: "Custom-Test-Header-1,Custom-Test-Header-2,Custom-Test-Header-3,Regex-Test-Header-.*,Regex-Invalid-Test-Header-.*,.*my-secret.*",
-        OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_RESPONSE: "Custom-Test-Header-1,Custom-Test-Header-2,Custom-Test-Header-3,my-custom-regex-header-.*,invalid-regex-header-.*,.*my-secret.*",
-    },
-)
 class TestCustomHeaders(AsgiTestBase, TestBase):
+    constructor_params = {}
+    __test__ = False
+
+    def __init_subclass__(cls) -> None:
+        if cls is not TestCustomHeaders:
+            cls.__test__ = True
+
     def setUp(self):
         super().setUp()
         self.tracer_provider, self.exporter = TestBase.create_tracer_provider()
         self.tracer = self.tracer_provider.get_tracer(__name__)
         self.app = otel_asgi.OpenTelemetryMiddleware(
-            simple_asgi, tracer_provider=self.tracer_provider
+            simple_asgi,
+            tracer_provider=self.tracer_provider,
+            **self.constructor_params,
         )
 
     def test_http_custom_request_headers_in_span_attributes(self):
@@ -148,7 +149,9 @@ def test_http_custom_request_headers_not_in_span_attributes(self):
 
     def test_http_custom_response_headers_in_span_attributes(self):
         self.app = otel_asgi.OpenTelemetryMiddleware(
-            http_app_with_custom_headers, tracer_provider=self.tracer_provider
+            http_app_with_custom_headers,
+            tracer_provider=self.tracer_provider,
+            **self.constructor_params,
         )
         self.seed_app(self.app)
         self.send_default_request()
@@ -175,7 +178,9 @@ def test_http_custom_response_headers_in_span_attributes(self):
 
     def test_http_custom_response_headers_not_in_span_attributes(self):
         self.app = otel_asgi.OpenTelemetryMiddleware(
-            http_app_with_custom_headers, tracer_provider=self.tracer_provider
+            http_app_with_custom_headers,
+            tracer_provider=self.tracer_provider,
+            **self.constructor_params,
         )
         self.seed_app(self.app)
         self.send_default_request()
@@ -277,6 +282,7 @@ def test_websocket_custom_response_headers_in_span_attributes(self):
         self.app = otel_asgi.OpenTelemetryMiddleware(
             websocket_app_with_custom_headers,
             tracer_provider=self.tracer_provider,
+            **self.constructor_params,
         )
         self.seed_app(self.app)
         self.send_input({"type": "websocket.connect"})
@@ -317,6 +323,7 @@ def test_websocket_custom_response_headers_not_in_span_attributes(self):
         self.app = otel_asgi.OpenTelemetryMiddleware(
             websocket_app_with_custom_headers,
             tracer_provider=self.tracer_provider,
+            **self.constructor_params,
         )
         self.seed_app(self.app)
         self.send_input({"type": "websocket.connect"})
@@ -333,3 +340,46 @@ def test_websocket_custom_response_headers_not_in_span_attributes(self):
             if span.kind == SpanKind.SERVER:
                 for key, _ in not_expected.items():
                     self.assertNotIn(key, span.attributes)
+
+
+SANITIZE_FIELDS_TEST_VALUE = ".*my-secret.*"
+SERVER_REQUEST_TEST_VALUE = "Custom-Test-Header-1,Custom-Test-Header-2,Custom-Test-Header-3,Regex-Test-Header-.*,Regex-Invalid-Test-Header-.*,.*my-secret.*"
+SERVER_RESPONSE_TEST_VALUE = "Custom-Test-Header-1,Custom-Test-Header-2,Custom-Test-Header-3,my-custom-regex-header-.*,invalid-regex-header-.*,.*my-secret.*"
+
+
+class TestCustomHeadersEnv(TestCustomHeaders):
+    def setUp(self):
+        os.environ.update(
+            {
+                OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SANITIZE_FIELDS: SANITIZE_FIELDS_TEST_VALUE,
+                OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_REQUEST: SERVER_REQUEST_TEST_VALUE,
+                OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_RESPONSE: SERVER_RESPONSE_TEST_VALUE,
+            }
+        )
+        super().setUp()
+
+    def tearDown(self):
+        os.environ.pop(
+            OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SANITIZE_FIELDS, None
+        )
+        os.environ.pop(
+            OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_REQUEST, None
+        )
+        os.environ.pop(
+            OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_RESPONSE, None
+        )
+        super().tearDown()
+
+
+class TestCustomHeadersConstructor(TestCustomHeaders):
+    constructor_params = {
+        "http_capture_headers_sanitize_fields": SANITIZE_FIELDS_TEST_VALUE.split(
+            ","
+        ),
+        "http_capture_headers_server_request": SERVER_REQUEST_TEST_VALUE.split(
+            ","
+        ),
+        "http_capture_headers_server_response": SERVER_RESPONSE_TEST_VALUE.split(
+            ","
+        ),
+    }
diff --git a/instrumentation/opentelemetry-instrumentation-asgi/tests/test_asgi_middleware.py b/instrumentation/opentelemetry-instrumentation-asgi/tests/test_asgi_middleware.py
--- a/instrumentation/opentelemetry-instrumentation-asgi/tests/test_asgi_middleware.py
+++ b/instrumentation/opentelemetry-instrumentation-asgi/tests/test_asgi_middleware.py
@@ -983,18 +983,16 @@ class TestAsgiApplicationRaisingError(AsgiTestBase):
     def tearDown(self):
         pass
 
-    @mock.patch(
-        "opentelemetry.instrumentation.asgi.collect_custom_request_headers_attributes",
-        side_effect=ValueError("whatever"),
-    )
-    def test_asgi_issue_1883(
-        self, mock_collect_custom_request_headers_attributes
-    ):
+    def test_asgi_issue_1883(self):
         """
         Test that exception UnboundLocalError local variable 'start' referenced before assignment is not raised
         See https://github.com/open-telemetry/opentelemetry-python-contrib/issues/1883
         """
-        app = otel_asgi.OpenTelemetryMiddleware(simple_asgi)
+
+        async def bad_app(_scope, _receive, _send):
+            raise ValueError("whatever")
+
+        app = otel_asgi.OpenTelemetryMiddleware(bad_app)
         self.seed_app(app)
         self.send_default_request()
         try:
diff --git a/instrumentation/opentelemetry-instrumentation-starlette/tests/test_starlette_instrumentation.py b/instrumentation/opentelemetry-instrumentation-starlette/tests/test_starlette_instrumentation.py
--- a/instrumentation/opentelemetry-instrumentation-starlette/tests/test_starlette_instrumentation.py
+++ b/instrumentation/opentelemetry-instrumentation-starlette/tests/test_starlette_instrumentation.py
@@ -467,15 +467,18 @@ async def _(websocket: WebSocket) -> None:
         return app
 
 
-@patch.dict(
-    "os.environ",
-    {
-        OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SANITIZE_FIELDS: ".*my-secret.*",
-        OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_REQUEST: "Custom-Test-Header-1,Custom-Test-Header-2,Custom-Test-Header-3,Regex-Test-Header-.*,Regex-Invalid-Test-Header-.*,.*my-secret.*",
-        OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_RESPONSE: "Custom-Test-Header-1,Custom-Test-Header-2,Custom-Test-Header-3,my-custom-regex-header-.*,invalid-regex-header-.*,.*my-secret.*",
-    },
-)
 class TestHTTPAppWithCustomHeaders(TestBaseWithCustomHeaders):
+    @patch.dict(
+        "os.environ",
+        {
+            OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SANITIZE_FIELDS: ".*my-secret.*",
+            OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_REQUEST: "Custom-Test-Header-1,Custom-Test-Header-2,Custom-Test-Header-3,Regex-Test-Header-.*,Regex-Invalid-Test-Header-.*,.*my-secret.*",
+            OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_RESPONSE: "Custom-Test-Header-1,Custom-Test-Header-2,Custom-Test-Header-3,my-custom-regex-header-.*,invalid-regex-header-.*,.*my-secret.*",
+        },
+    )
+    def setUp(self) -> None:
+        super().setUp()
+
     def test_custom_request_headers_in_span_attributes(self):
         expected = {
             "http.request.header.custom_test_header_1": (
@@ -590,15 +593,18 @@ def test_custom_response_headers_not_in_span_attributes(self):
             self.assertNotIn(key, server_span.attributes)
 
 
-@patch.dict(
-    "os.environ",
-    {
-        OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SANITIZE_FIELDS: ".*my-secret.*",
-        OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_REQUEST: "Custom-Test-Header-1,Custom-Test-Header-2,Custom-Test-Header-3,Regex-Test-Header-.*,Regex-Invalid-Test-Header-.*,.*my-secret.*",
-        OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_RESPONSE: "Custom-Test-Header-1,Custom-Test-Header-2,Custom-Test-Header-3,my-custom-regex-header-.*,invalid-regex-header-.*,.*my-secret.*",
-    },
-)
 class TestWebSocketAppWithCustomHeaders(TestBaseWithCustomHeaders):
+    @patch.dict(
+        "os.environ",
+        {
+            OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SANITIZE_FIELDS: ".*my-secret.*",
+            OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_REQUEST: "Custom-Test-Header-1,Custom-Test-Header-2,Custom-Test-Header-3,Regex-Test-Header-.*,Regex-Invalid-Test-Header-.*,.*my-secret.*",
+            OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_RESPONSE: "Custom-Test-Header-1,Custom-Test-Header-2,Custom-Test-Header-3,my-custom-regex-header-.*,invalid-regex-header-.*,.*my-secret.*",
+        },
+    )
+    def setUp(self) -> None:
+        super().setUp()
+
     def test_custom_request_headers_in_span_attributes(self):
         expected = {
             "http.request.header.custom_test_header_1": (