Skip to content

Commit 8d0bada

Browse files
herin049xrmx
andauthored
opentelemetry-instrumentation-requests: add ability to capture custom headers (#3987)
* opentelemetry-instrumentation-requests: add ability to capture request and response headers * update unit tests * update documentation * update CHANGELOG.md * disable 'too-many-lines' check for tests * add 'get_custom_header_attributes' helper to opentelemetry-util-http * fix formatting issues * fix CHANGELOG.md format * Update CHANGELOG.md --------- Co-authored-by: Riccardo Magliocchetti <riccardo.magliocchetti@gmail.com>
1 parent a368369 commit 8d0bada

4 files changed

Lines changed: 392 additions & 1 deletion

File tree

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1313

1414
### Added
1515

16+
- `opentelemetry-instrumentation-requests`: add ability to capture custom headers
17+
([#3987](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3987))
1618
- `opentelemetry-instrumentation-aiohttp-client`: add typechecking for aiohttp-client instrumentation
1719
([#4006](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4006))
1820
- `opentelemetry-instrumentation-flask`: Add support for 3.1+ streaming responses

instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/__init__.py

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,97 @@ def response_hook(span, request_obj, response):
5757
request_hook=request_hook, response_hook=response_hook
5858
)
5959
60+
Capture HTTP request and response headers
61+
*****************************************
62+
You can configure the agent to capture specified HTTP headers as span attributes, according to the
63+
`semantic conventions <https://opentelemetry.io/docs/specs/semconv/http/http-spans/#http-client-span>`_.
64+
65+
Request headers
66+
***************
67+
To capture HTTP request headers as span attributes, set the environment variable
68+
``OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_CLIENT_REQUEST`` to a comma delimited list of HTTP header names.
69+
70+
For example using the environment variable,
71+
::
72+
73+
export OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_CLIENT_REQUEST="content-type,custom_request_header"
74+
75+
will extract ``content-type`` and ``custom_request_header`` from the request headers and add them as span attributes.
76+
77+
Request header names in Requests are case-insensitive. So, giving the header name as ``CUStom-Header`` in the environment
78+
variable will capture the header named ``custom-header``.
79+
80+
Regular expressions may also be used to match multiple headers that correspond to the given pattern. For example:
81+
::
82+
83+
export OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_CLIENT_REQUEST="Accept.*,X-.*"
84+
85+
Would match all request headers that start with ``Accept`` and ``X-``.
86+
87+
To capture all request headers, set ``OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_CLIENT_REQUEST`` to ``".*"``.
88+
::
89+
90+
export OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_CLIENT_REQUEST=".*"
91+
92+
The name of the added span attribute will follow the format ``http.request.header.<header_name>`` where ``<header_name>``
93+
is the normalized HTTP header name (lowercase, with ``-`` replaced by ``_``). The value of the attribute will be a
94+
single item list containing all the header values.
95+
96+
For example:
97+
``http.request.header.custom_request_header = ["<value1>", "<value2>"]``
98+
99+
Response headers
100+
****************
101+
To capture HTTP response headers as span attributes, set the environment variable
102+
``OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_CLIENT_RESPONSE`` to a comma delimited list of HTTP header names.
103+
104+
For example using the environment variable,
105+
::
106+
107+
export OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_CLIENT_RESPONSE="content-type,custom_response_header"
108+
109+
will extract ``content-type`` and ``custom_response_header`` from the response headers and add them as span attributes.
110+
111+
Response header names in Requests are case-insensitive. So, giving the header name as ``CUStom-Header`` in the environment
112+
variable will capture the header named ``custom-header``.
113+
114+
Regular expressions may also be used to match multiple headers that correspond to the given pattern. For example:
115+
::
116+
117+
export OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_CLIENT_RESPONSE="Content.*,X-.*"
118+
119+
Would match all response headers that start with ``Content`` and ``X-``.
120+
121+
To capture all response headers, set ``OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_CLIENT_RESPONSE`` to ``".*"``.
122+
::
123+
124+
export OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_CLIENT_RESPONSE=".*"
125+
126+
The name of the added span attribute will follow the format ``http.response.header.<header_name>`` where ``<header_name>``
127+
is the normalized HTTP header name (lowercase, with ``-`` replaced by ``_``). The value of the attribute will be a
128+
list containing the header values.
129+
130+
For example:
131+
``http.response.header.custom_response_header = ["<value1>", "<value2>"]``
132+
133+
Sanitizing headers
134+
******************
135+
In order to prevent storing sensitive data such as personally identifiable information (PII), session keys, passwords,
136+
etc, set the environment variable ``OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SANITIZE_FIELDS``
137+
to a comma delimited list of HTTP header names to be sanitized.
138+
139+
Regexes may be used, and all header names will be matched in a case-insensitive manner.
140+
141+
For example using the environment variable,
142+
::
143+
144+
export OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SANITIZE_FIELDS=".*session.*,set-cookie"
145+
146+
will replace the value of headers such as ``session-id`` and ``set-cookie`` with ``[REDACTED]`` in the span.
147+
148+
Note:
149+
The environment variable names used to capture HTTP headers are still experimental, and thus are subject to change.
150+
60151
Custom Duration Histogram Boundaries
61152
************************************
62153
To customize the duration histogram bucket boundaries used for HTTP client request duration metrics,
@@ -150,9 +241,16 @@ def response_hook(span, request_obj, response):
150241
from opentelemetry.trace import SpanKind, Tracer, get_tracer
151242
from opentelemetry.trace.span import Span
152243
from opentelemetry.util.http import (
244+
OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_CLIENT_REQUEST,
245+
OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_CLIENT_RESPONSE,
246+
OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SANITIZE_FIELDS,
153247
ExcludeList,
154248
detect_synthetic_user_agent,
249+
get_custom_header_attributes,
250+
get_custom_headers,
155251
get_excluded_urls,
252+
normalise_request_header_name,
253+
normalise_response_header_name,
156254
normalize_user_agent,
157255
parse_excluded_urls,
158256
redact_url,
@@ -202,6 +300,9 @@ def _instrument(
202300
response_hook: _ResponseHookT = None,
203301
excluded_urls: ExcludeList | None = None,
204302
sem_conv_opt_in_mode: _StabilityMode = _StabilityMode.DEFAULT,
303+
captured_request_headers: list[str] | None = None,
304+
captured_response_headers: list[str] | None = None,
305+
sensitive_headers: list[str] | None = None,
205306
):
206307
"""Enables tracing of all requests calls that go through
207308
:code:`requests.session.Session.request` (this includes
@@ -260,6 +361,14 @@ def get_or_create_headers():
260361
span_attributes[USER_AGENT_SYNTHETIC_TYPE] = synthetic_type
261362
if user_agent:
262363
span_attributes[USER_AGENT_ORIGINAL] = user_agent
364+
span_attributes.update(
365+
get_custom_header_attributes(
366+
headers,
367+
captured_request_headers,
368+
sensitive_headers,
369+
normalise_request_header_name,
370+
)
371+
)
263372

264373
metric_labels = {}
265374
_set_http_method(
@@ -352,6 +461,14 @@ def get_or_create_headers():
352461
version_text,
353462
sem_conv_opt_in_mode,
354463
)
464+
span_attributes.update(
465+
get_custom_header_attributes(
466+
result.headers,
467+
captured_response_headers,
468+
sensitive_headers,
469+
normalise_response_header_name,
470+
)
471+
)
355472
for key, val in span_attributes.items():
356473
span.set_attribute(key, val)
357474

@@ -503,6 +620,15 @@ def _instrument(self, **kwargs: Any):
503620
else parse_excluded_urls(excluded_urls)
504621
),
505622
sem_conv_opt_in_mode=semconv_opt_in_mode,
623+
captured_request_headers=get_custom_headers(
624+
OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_CLIENT_REQUEST
625+
),
626+
captured_response_headers=get_custom_headers(
627+
OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_CLIENT_RESPONSE
628+
),
629+
sensitive_headers=get_custom_headers(
630+
OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SANITIZE_FIELDS
631+
),
506632
)
507633

508634
def _uninstrument(self, **kwargs: Any):

0 commit comments

Comments
 (0)