@@ -135,8 +135,15 @@ def response_hook(span: Span, request: Request, response: HTTPResponse):
135135)
136136from opentelemetry .trace import Span , SpanKind , Tracer , get_tracer
137137from opentelemetry .util .http import (
138+ OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_CLIENT_REQUEST ,
139+ OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_CLIENT_RESPONSE ,
140+ OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SANITIZE_FIELDS ,
138141 ExcludeList ,
142+ get_custom_header_attributes ,
143+ get_custom_headers ,
139144 get_excluded_urls ,
145+ normalise_request_header_name ,
146+ normalise_response_header_name ,
140147 parse_excluded_urls ,
141148 redact_url ,
142149 sanitize_method ,
@@ -169,6 +176,9 @@ def _instrument(self, **kwargs: Any):
169176 ``response_hook``: An optional callback which is invoked right before the span is finished processing a response
170177 ``excluded_urls``: A string containing a comma-delimited
171178 list of regexes used to exclude URLs from tracking
179+ ``captured_request_headers``: A comma-separated list of regexes to match against request headers to capture
180+ ``captured_response_headers``: A comma-separated list of regexes to match against response headers to capture
181+ ``sensitive_headers``: A comma-separated list of regexes to match against captured headers to be sanitized
172182 """
173183 # initialize semantic conventions opt-in if needed
174184 _OpenTelemetrySemanticConventionStability ._initialize ()
@@ -205,6 +215,24 @@ def _instrument(self, **kwargs: Any):
205215 else parse_excluded_urls (excluded_urls )
206216 ),
207217 sem_conv_opt_in_mode = sem_conv_opt_in_mode ,
218+ captured_request_headers = kwargs .get (
219+ "captured_request_headers" ,
220+ get_custom_headers (
221+ OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_CLIENT_REQUEST
222+ ),
223+ ),
224+ captured_response_headers = kwargs .get (
225+ "captured_response_headers" ,
226+ get_custom_headers (
227+ OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_CLIENT_RESPONSE
228+ ),
229+ ),
230+ sensitive_headers = kwargs .get (
231+ "sensitive_headers" ,
232+ get_custom_headers (
233+ OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SANITIZE_FIELDS
234+ ),
235+ ),
208236 )
209237
210238 def _uninstrument (self , ** kwargs : Any ):
@@ -223,6 +251,9 @@ def _instrument(
223251 response_hook : _ResponseHookT = None ,
224252 excluded_urls : ExcludeList | None = None ,
225253 sem_conv_opt_in_mode : _StabilityMode = _StabilityMode .DEFAULT ,
254+ captured_request_headers : list [str ] | None = None ,
255+ captured_response_headers : list [str ] | None = None ,
256+ sensitive_headers : list [str ] | None = None ,
226257):
227258 """Enables tracing of all requests calls that go through
228259 :code:`urllib.Client._make_request`"""
@@ -275,6 +306,15 @@ def _instrumented_open_call(
275306 )
276307 _set_http_url (labels , url , sem_conv_opt_in_mode )
277308
309+ labels .update (
310+ get_custom_header_attributes (
311+ request .headers ,
312+ captured_request_headers ,
313+ sensitive_headers ,
314+ normalise_request_header_name ,
315+ )
316+ )
317+
278318 with tracer .start_as_current_span (
279319 span_name , kind = SpanKind .CLIENT , attributes = labels
280320 ) as span :
@@ -310,6 +350,16 @@ def _instrumented_open_call(
310350 labels , f"{ ver_ [:1 ]} .{ ver_ [:- 1 ]} " , sem_conv_opt_in_mode
311351 )
312352
353+ if span .is_recording ():
354+ response_headers_to_set = get_custom_header_attributes (
355+ result .headers ,
356+ captured_response_headers ,
357+ sensitive_headers ,
358+ normalise_response_header_name ,
359+ )
360+ for header , value in response_headers_to_set .items ():
361+ span .set_attribute (header , value )
362+
313363 if exception is not None and _report_new (sem_conv_opt_in_mode ):
314364 span .set_attribute (ERROR_TYPE , type (exception ).__qualname__ )
315365 labels [ERROR_TYPE ] = type (exception ).__qualname__
0 commit comments