-
Notifications
You must be signed in to change notification settings - Fork 835
urllib3: add support for capturing client headers #4050
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
xrmx
wants to merge
4
commits into
open-telemetry:main
Choose a base branch
from
xrmx:capture-client-headers-urllib3
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+484
−0
Open
Changes from all commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -88,6 +88,97 @@ def response_hook( | |||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| will exclude requests such as ``https://site/client/123/info`` and ``https://site/xyz/healthcheck``. | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| Capture HTTP request and response headers | ||||||||||||||||||||||||||||||
| ***************************************** | ||||||||||||||||||||||||||||||
| You can configure the agent to capture specified HTTP headers as span attributes, according to the | ||||||||||||||||||||||||||||||
| `semantic conventions <https://opentelemetry.io/docs/specs/semconv/http/http-spans/#http-client-span>`_. | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| Request headers | ||||||||||||||||||||||||||||||
| *************** | ||||||||||||||||||||||||||||||
| To capture HTTP request headers as span attributes, set the environment variable | ||||||||||||||||||||||||||||||
| ``OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_CLIENT_REQUEST`` to a comma delimited list of HTTP header names. | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| For example using the environment variable, | ||||||||||||||||||||||||||||||
| :: | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| export OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_CLIENT_REQUEST="content-type,custom_request_header" | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| will extract ``content-type`` and ``custom_request_header`` from the request headers and add them as span attributes. | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| Request header names in aiohttp are case-insensitive. So, giving the header name as ``CUStom-Header`` in the environment | ||||||||||||||||||||||||||||||
| variable will capture the header named ``custom-header``. | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| Regular expressions may also be used to match multiple headers that correspond to the given pattern. For example: | ||||||||||||||||||||||||||||||
| :: | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| export OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_CLIENT_REQUEST="Accept.*,X-.*" | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| Would match all request headers that start with ``Accept`` and ``X-``. | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| To capture all request headers, set ``OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_CLIENT_REQUEST`` to ``".*"``. | ||||||||||||||||||||||||||||||
| :: | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| export OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_CLIENT_REQUEST=".*" | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| The name of the added span attribute will follow the format ``http.request.header.<header_name>`` where ``<header_name>`` | ||||||||||||||||||||||||||||||
| is the normalized HTTP header name (lowercase, with ``-`` replaced by ``_``). The value of the attribute will be a | ||||||||||||||||||||||||||||||
| single item list containing all the header values. | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| For example: | ||||||||||||||||||||||||||||||
| ``http.request.header.custom_request_header = ["<value1>", "<value2>"]`` | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| Response headers | ||||||||||||||||||||||||||||||
| **************** | ||||||||||||||||||||||||||||||
| To capture HTTP response headers as span attributes, set the environment variable | ||||||||||||||||||||||||||||||
| ``OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_CLIENT_RESPONSE`` to a comma delimited list of HTTP header names. | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| For example using the environment variable, | ||||||||||||||||||||||||||||||
| :: | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| export OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_CLIENT_RESPONSE="content-type,custom_response_header" | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| will extract ``content-type`` and ``custom_response_header`` from the response headers and add them as span attributes. | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| Response header names in aiohttp are case-insensitive. So, giving the header name as ``CUStom-Header`` in the environment | ||||||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||||||||||||||
| variable will capture the header named ``custom-header``. | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| Regular expressions may also be used to match multiple headers that correspond to the given pattern. For example: | ||||||||||||||||||||||||||||||
| :: | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| export OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_CLIENT_RESPONSE="Content.*,X-.*" | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| Would match all response headers that start with ``Content`` and ``X-``. | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| To capture all response headers, set ``OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_CLIENT_RESPONSE`` to ``".*"``. | ||||||||||||||||||||||||||||||
| :: | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| export OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_CLIENT_RESPONSE=".*" | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| The name of the added span attribute will follow the format ``http.response.header.<header_name>`` where ``<header_name>`` | ||||||||||||||||||||||||||||||
| is the normalized HTTP header name (lowercase, with ``-`` replaced by ``_``). The value of the attribute will be a | ||||||||||||||||||||||||||||||
| list containing the header values. | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| For example: | ||||||||||||||||||||||||||||||
| ``http.response.header.custom_response_header = ["<value1>", "<value2>"]`` | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| Sanitizing headers | ||||||||||||||||||||||||||||||
| ****************** | ||||||||||||||||||||||||||||||
| In order to prevent storing sensitive data such as personally identifiable information (PII), session keys, passwords, | ||||||||||||||||||||||||||||||
| etc, set the environment variable ``OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SANITIZE_FIELDS`` | ||||||||||||||||||||||||||||||
| to a comma delimited list of HTTP header names to be sanitized. | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| Regexes may be used, and all header names will be matched in a case-insensitive manner. | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| For example using the environment variable, | ||||||||||||||||||||||||||||||
| :: | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| export OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SANITIZE_FIELDS=".*session.*,set-cookie" | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| will replace the value of headers such as ``session-id`` and ``set-cookie`` with ``[REDACTED]`` in the span. | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| Note: | ||||||||||||||||||||||||||||||
| The environment variable names used to capture HTTP headers are still experimental, and thus are subject to change. | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| API | ||||||||||||||||||||||||||||||
| --- | ||||||||||||||||||||||||||||||
| """ | ||||||||||||||||||||||||||||||
|
|
@@ -142,8 +233,15 @@ def response_hook( | |||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||
| from opentelemetry.trace import Span, SpanKind, Tracer, get_tracer | ||||||||||||||||||||||||||||||
| from opentelemetry.util.http import ( | ||||||||||||||||||||||||||||||
| OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_CLIENT_REQUEST, | ||||||||||||||||||||||||||||||
| OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_CLIENT_RESPONSE, | ||||||||||||||||||||||||||||||
| OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SANITIZE_FIELDS, | ||||||||||||||||||||||||||||||
| ExcludeList, | ||||||||||||||||||||||||||||||
| get_custom_header_attributes, | ||||||||||||||||||||||||||||||
| get_custom_headers, | ||||||||||||||||||||||||||||||
| get_excluded_urls, | ||||||||||||||||||||||||||||||
| normalise_request_header_name, | ||||||||||||||||||||||||||||||
| normalise_response_header_name, | ||||||||||||||||||||||||||||||
| parse_excluded_urls, | ||||||||||||||||||||||||||||||
| sanitize_method, | ||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||
|
|
@@ -278,6 +376,7 @@ def _instrument(self, **kwargs): | |||||||||||||||||||||||||||||
| response_size_histogram_new = ( | ||||||||||||||||||||||||||||||
| create_http_client_response_body_size(meter) | ||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| _instrument( | ||||||||||||||||||||||||||||||
| tracer, | ||||||||||||||||||||||||||||||
| duration_histogram_old, | ||||||||||||||||||||||||||||||
|
|
@@ -295,6 +394,24 @@ def _instrument(self, **kwargs): | |||||||||||||||||||||||||||||
| else parse_excluded_urls(excluded_urls) | ||||||||||||||||||||||||||||||
| ), | ||||||||||||||||||||||||||||||
| sem_conv_opt_in_mode=sem_conv_opt_in_mode, | ||||||||||||||||||||||||||||||
| captured_request_headers=kwargs.get( | ||||||||||||||||||||||||||||||
| "captured_request_headers", | ||||||||||||||||||||||||||||||
| get_custom_headers( | ||||||||||||||||||||||||||||||
| OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_CLIENT_REQUEST | ||||||||||||||||||||||||||||||
| ), | ||||||||||||||||||||||||||||||
| ), | ||||||||||||||||||||||||||||||
| captured_response_headers=kwargs.get( | ||||||||||||||||||||||||||||||
| "captured_response_headers", | ||||||||||||||||||||||||||||||
| get_custom_headers( | ||||||||||||||||||||||||||||||
| OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_CLIENT_RESPONSE | ||||||||||||||||||||||||||||||
| ), | ||||||||||||||||||||||||||||||
| ), | ||||||||||||||||||||||||||||||
| sensitive_headers=kwargs.get( | ||||||||||||||||||||||||||||||
| "sensitive_headers", | ||||||||||||||||||||||||||||||
| get_custom_headers( | ||||||||||||||||||||||||||||||
| OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SANITIZE_FIELDS | ||||||||||||||||||||||||||||||
| ), | ||||||||||||||||||||||||||||||
| ), | ||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| def _uninstrument(self, **kwargs): | ||||||||||||||||||||||||||||||
|
|
@@ -321,7 +438,11 @@ def _instrument( | |||||||||||||||||||||||||||||
| url_filter: _UrlFilterT = None, | ||||||||||||||||||||||||||||||
| excluded_urls: ExcludeList = None, | ||||||||||||||||||||||||||||||
| sem_conv_opt_in_mode: _StabilityMode = _StabilityMode.DEFAULT, | ||||||||||||||||||||||||||||||
| captured_request_headers: typing.Optional[list[str]] = None, | ||||||||||||||||||||||||||||||
| captured_response_headers: typing.Optional[list[str]] = None, | ||||||||||||||||||||||||||||||
| sensitive_headers: typing.Optional[list[str]] = None, | ||||||||||||||||||||||||||||||
| ): | ||||||||||||||||||||||||||||||
| # pylint: disable=too-many-locals | ||||||||||||||||||||||||||||||
| def instrumented_urlopen(wrapped, instance, args, kwargs): | ||||||||||||||||||||||||||||||
| if not is_http_instrumentation_enabled(): | ||||||||||||||||||||||||||||||
| return wrapped(*args, **kwargs) | ||||||||||||||||||||||||||||||
|
|
@@ -345,6 +466,15 @@ def instrumented_urlopen(wrapped, instance, args, kwargs): | |||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||
| _set_http_url(span_attributes, url, sem_conv_opt_in_mode) | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| span_attributes.update( | ||||||||||||||||||||||||||||||
| get_custom_header_attributes( | ||||||||||||||||||||||||||||||
| headers, | ||||||||||||||||||||||||||||||
| captured_request_headers, | ||||||||||||||||||||||||||||||
| sensitive_headers, | ||||||||||||||||||||||||||||||
| normalise_request_header_name, | ||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| with ( | ||||||||||||||||||||||||||||||
| tracer.start_as_current_span( | ||||||||||||||||||||||||||||||
| span_name, kind=SpanKind.CLIENT, attributes=span_attributes | ||||||||||||||||||||||||||||||
|
|
@@ -402,6 +532,16 @@ def instrumented_urlopen(wrapped, instance, args, kwargs): | |||||||||||||||||||||||||||||
| sem_conv_opt_in_mode, | ||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| if span.is_recording(): | ||||||||||||||||||||||||||||||
| response_headers_to_set = get_custom_header_attributes( | ||||||||||||||||||||||||||||||
| response.headers, | ||||||||||||||||||||||||||||||
| captured_response_headers, | ||||||||||||||||||||||||||||||
| sensitive_headers, | ||||||||||||||||||||||||||||||
| normalise_response_header_name, | ||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||
| for header, value in response_headers_to_set.items(): | ||||||||||||||||||||||||||||||
| span.set_attribute(header, value) | ||||||||||||||||||||||||||||||
|
Comment on lines
+536
to
+543
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: any reason not to just do this
Suggested change
|
||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| return response | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| wrapt.wrap_function_wrapper( | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.