Skip to content

Commit 9ee61bf

Browse files
Dan RogersCircleCI
Dan Rogers
authored and
CircleCI
committed
Add support for regular expression matching and sanitizing of headers in Pyramid. (open-telemetry#1414)
1 parent c273015 commit 9ee61bf

File tree

4 files changed

+100
-41
lines changed

4 files changed

+100
-41
lines changed

Diff for: CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2727
([#1323](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1323))
2828
- `opentelemetry-instrumentation-wsgi` Add support for regular expression matching and sanitization of HTTP headers.
2929
([#1402](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1402))
30+
- `opentelemetry-instrumentation-pyramid` Add support for regular expression matching and sanitization of HTTP headers.
31+
([#1414](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1414))
3032

3133
### Fixed
3234

Diff for: instrumentation/opentelemetry-instrumentation-pyramid/src/opentelemetry/instrumentation/pyramid/__init__.py

+63-23
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@
5555
---------------------------------
5656
5757
If you use Method 2 and then set tweens for your application with the ``pyramid.tweens`` setting,
58-
you need to add ``opentelemetry.instrumentation.pyramid.trace_tween_factory`` explicitly to the list,
58+
you need to explicitly add ``opentelemetry.instrumentation.pyramid.trace_tween_factory`` to the list,
5959
*as well as* instrumenting the config as shown above.
6060
6161
For example:
@@ -79,8 +79,9 @@
7979
8080
Exclude lists
8181
*************
82-
To exclude certain URLs from being tracked, set the environment variable ``OTEL_PYTHON_PYRAMID_EXCLUDED_URLS``
83-
(or ``OTEL_PYTHON_EXCLUDED_URLS`` as fallback) with comma delimited regexes representing which URLs to exclude.
82+
To exclude certain URLs from tracking, set the environment variable ``OTEL_PYTHON_PYRAMID_EXCLUDED_URLS``
83+
(or ``OTEL_PYTHON_EXCLUDED_URLS`` to cover all instrumentations) to a string of comma delimited regexes that match the
84+
URLs.
8485
8586
For example,
8687
@@ -92,54 +93,93 @@
9293
9394
Capture HTTP request and response headers
9495
*****************************************
95-
You can configure the agent to capture predefined HTTP headers as span attributes, according to the `semantic convention <https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/http.md#http-request-and-response-headers>`_.
96+
You can configure the agent to capture specified HTTP headers as span attributes, according to the
97+
`semantic convention <https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/http.md#http-request-and-response-headers>`_.
9698
9799
Request headers
98100
***************
99-
To capture predefined HTTP request headers as span attributes, set the environment variable ``OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_REQUEST``
100-
to a comma-separated list of HTTP header names.
101+
To capture HTTP request headers as span attributes, set the environment variable
102+
``OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_REQUEST`` to a comma delimited list of HTTP header names.
101103
102104
For example,
103-
104105
::
105106
106107
export OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_REQUEST="content-type,custom_request_header"
107108
108-
will extract ``content-type`` and ``custom_request_header`` from request headers and add them as span attributes.
109+
will extract ``content-type`` and ``custom_request_header`` from the request headers and add them as span attributes.
110+
111+
Request header names in Pyramid are case-insensitive and ``-`` characters are replaced by ``_``. So, giving the header
112+
name as ``CUStom_Header`` in the environment 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_SERVER_REQUEST="Accept.*,X-.*"
118+
119+
Would match all request headers that start with ``Accept`` and ``X-``.
109120
110-
It is recommended that you should give the correct names of the headers to be captured in the environment variable.
111-
Request header names in pyramid are case insensitive and - characters are replaced by _. So, giving header name as ``CUStom_Header`` in environment variable will be able capture header with name ``custom-header``.
121+
To capture all request headers, set ``OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_REQUEST`` to ``".*"``.
122+
::
123+
124+
export OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_REQUEST=".*"
112125
113-
The name of the added span attribute will follow the format ``http.request.header.<header_name>`` where ``<header_name>`` being the normalized HTTP header name (lowercase, with - characters replaced by _ ).
114-
The value of the attribute will be single item list containing all the header values.
126+
The name of the added span attribute will follow the format ``http.request.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+
single item list containing all the header values.
115129
116-
Example of the added span attribute,
130+
For example:
117131
``http.request.header.custom_request_header = ["<value1>,<value2>"]``
118132
119133
Response headers
120134
****************
121-
To capture predefined HTTP response headers as span attributes, set the environment variable ``OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_RESPONSE``
122-
to a comma-separated list of HTTP header names.
135+
To capture HTTP response headers as span attributes, set the environment variable
136+
``OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_RESPONSE`` to a comma delimited list of HTTP header names.
123137
124138
For example,
125-
126139
::
127140
128141
export OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_RESPONSE="content-type,custom_response_header"
129142
130-
will extract ``content-type`` and ``custom_response_header`` from response headers and add them as span attributes.
143+
will extract ``content-type`` and ``custom_response_header`` from the response headers and add them as span attributes.
144+
145+
Response header names in Pyramid are case-insensitive. So, giving the header name as ``CUStom-Header`` in the environment
146+
variable will capture the header named ``custom-header``.
147+
148+
Regular expressions may also be used to match multiple headers that correspond to the given pattern. For example:
149+
::
150+
151+
export OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_RESPONSE="Content.*,X-.*"
152+
153+
Would match all response headers that start with ``Content`` and ``X-``.
131154
132-
It is recommended that you should give the correct names of the headers to be captured in the environment variable.
133-
Response header names captured in pyramid are case insensitive. So, giving header name as ``CUStomHeader`` in environment variable will be able capture header with name ``customheader``.
155+
To capture all response headers, set ``OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_RESPONSE`` to ``".*"``.
156+
::
157+
158+
export OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_RESPONSE=".*"
134159
135-
The name of the added span attribute will follow the format ``http.response.header.<header_name>`` where ``<header_name>`` being the normalized HTTP header name (lowercase, with - characters replaced by _ ).
136-
The value of the attribute will be single item list containing all the header values.
160+
The name of the added span attribute will follow the format ``http.response.header.<header_name>`` where ``<header_name>``
161+
is the normalized HTTP header name (lowercase, with ``-`` replaced by ``_``). The value of the attribute will be a
162+
single item list containing all the header values.
137163
138-
Example of the added span attribute,
164+
For example:
139165
``http.response.header.custom_response_header = ["<value1>,<value2>"]``
140166
167+
Sanitizing headers
168+
******************
169+
In order to prevent storing sensitive data such as personally identifiable information (PII), session keys, passwords,
170+
etc, set the environment variable ``OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SANITIZE_FIELDS``
171+
to a comma delimited list of HTTP header names to be sanitized. Regexes may be used, and all header names will be
172+
matched in a case-insensitive manner.
173+
174+
For example,
175+
::
176+
177+
export OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SANITIZE_FIELDS=".*session.*,set-cookie"
178+
179+
will replace the value of headers such as ``session-id`` and ``set-cookie`` with ``[REDACTED]`` in the span.
180+
141181
Note:
142-
Environment variable names to capture http headers are still experimental, and thus are subject to change.
182+
The environment variable names used to capture HTTP headers are still experimental, and thus are subject to change.
143183
144184
API
145185
---

Diff for: instrumentation/opentelemetry-instrumentation-pyramid/tests/pyramid_base_test.py

+3
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@ def _custom_response_header_endpoint(request):
4040
"content-type": "text/plain; charset=utf-8",
4141
"content-length": "7",
4242
"my-custom-header": "my-custom-value-1,my-custom-header-2",
43+
"my-custom-regex-header-1": "my-custom-regex-value-1,my-custom-regex-value-2",
44+
"My-Custom-Regex-Header-2": "my-custom-regex-value-3,my-custom-regex-value-4",
45+
"my-secret-header": "my-secret-value",
4346
"dont-capture-me": "test-value",
4447
}
4548
return Response("Testing", headers=headers)

Diff for: instrumentation/opentelemetry-instrumentation-pyramid/tests/test_automatic.py

+32-18
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
from opentelemetry.trace import SpanKind
2929
from opentelemetry.trace.status import StatusCode
3030
from opentelemetry.util.http import (
31+
OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SANITIZE_FIELDS,
3132
OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_REQUEST,
3233
OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_RESPONSE,
3334
_active_requests_count_attrs,
@@ -285,24 +286,23 @@ def test_with_existing_span(self):
285286
)
286287

287288

289+
@patch.dict(
290+
"os.environ",
291+
{
292+
OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SANITIZE_FIELDS: ".*my-secret.*",
293+
OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_REQUEST: "Custom-Test-Header-1,Custom-Test-Header-2,invalid-header,Regex-Test-Header-.*,Regex-Invalid-Test-Header-.*,.*my-secret.*",
294+
OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_RESPONSE: "content-type,content-length,my-custom-header,invalid-header,my-custom-regex-header-.*,invalid-regex-header-.*,.*my-secret.*",
295+
},
296+
)
288297
class TestCustomRequestResponseHeaders(InstrumentationTest, WsgiTestBase):
289298
def setUp(self):
290299
super().setUp()
291300
PyramidInstrumentor().instrument()
292301
self.config = Configurator()
293302
self._common_initialization(self.config)
294-
self.env_patch = patch.dict(
295-
"os.environ",
296-
{
297-
OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_REQUEST: "Custom-Test-Header-1,Custom-Test-Header-2,invalid-header",
298-
OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_RESPONSE: "content-type,content-length,my-custom-header,invalid-header",
299-
},
300-
)
301-
self.env_patch.start()
302303

303304
def tearDown(self) -> None:
304305
super().tearDown()
305-
self.env_patch.stop()
306306
with self.disable_logging():
307307
PyramidInstrumentor().uninstrument()
308308

@@ -311,6 +311,9 @@ def test_custom_request_header_added_in_server_span(self):
311311
"Custom-Test-Header-1": "Test Value 1",
312312
"Custom-Test-Header-2": "TestValue2,TestValue3",
313313
"Custom-Test-Header-3": "TestValue4",
314+
"Regex-Test-Header-1": "Regex Test Value 1",
315+
"regex-test-header-2": "RegexTestValue2,RegexTestValue3",
316+
"My-Secret-Header": "My Secret Value",
314317
}
315318
resp = self.client.get("/hello/123", headers=headers)
316319
self.assertEqual(200, resp.status_code)
@@ -320,6 +323,11 @@ def test_custom_request_header_added_in_server_span(self):
320323
"http.request.header.custom_test_header_2": (
321324
"TestValue2,TestValue3",
322325
),
326+
"http.request.header.regex_test_header_1": ("Regex Test Value 1",),
327+
"http.request.header.regex_test_header_2": (
328+
"RegexTestValue2,RegexTestValue3",
329+
),
330+
"http.request.header.my_secret_header": ("[REDACTED]",),
323331
}
324332
not_expected = {
325333
"http.request.header.custom_test_header_3": ("TestValue4",),
@@ -361,6 +369,13 @@ def test_custom_response_header_added_in_server_span(self):
361369
"http.response.header.my_custom_header": (
362370
"my-custom-value-1,my-custom-header-2",
363371
),
372+
"http.response.header.my_custom_regex_header_1": (
373+
"my-custom-regex-value-1,my-custom-regex-value-2",
374+
),
375+
"http.response.header.my_custom_regex_header_2": (
376+
"my-custom-regex-value-3,my-custom-regex-value-4",
377+
),
378+
"http.response.header.my_secret_header": ("[REDACTED]",),
364379
}
365380
not_expected = {
366381
"http.response.header.dont_capture_me": ("test-value",)
@@ -390,6 +405,14 @@ def test_custom_response_header_not_added_in_internal_span(self):
390405
self.assertNotIn(key, span.attributes)
391406

392407

408+
@patch.dict(
409+
"os.environ",
410+
{
411+
OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SANITIZE_FIELDS: ".*my-secret.*",
412+
OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_REQUEST: "Custom-Test-Header-1,Custom-Test-Header-2,invalid-header,Regex-Test-Header-.*,Regex-Invalid-Test-Header-.*,.*my-secret.*",
413+
OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_RESPONSE: "content-type,content-length,my-custom-header,invalid-header,my-custom-regex-header-.*,invalid-regex-header-.*,.*my-secret.*",
414+
},
415+
)
393416
class TestCustomHeadersNonRecordingSpan(InstrumentationTest, WsgiTestBase):
394417
def setUp(self):
395418
super().setUp()
@@ -401,18 +424,9 @@ def setUp(self):
401424
PyramidInstrumentor().instrument()
402425
self.config = Configurator()
403426
self._common_initialization(self.config)
404-
self.env_patch = patch.dict(
405-
"os.environ",
406-
{
407-
OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_REQUEST: "Custom-Test-Header-1,Custom-Test-Header-2,invalid-header",
408-
OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_RESPONSE: "content-type,content-length,my-custom-header,invalid-header",
409-
},
410-
)
411-
self.env_patch.start()
412427

413428
def tearDown(self) -> None:
414429
super().tearDown()
415-
self.env_patch.stop()
416430
with self.disable_logging():
417431
PyramidInstrumentor().uninstrument()
418432

0 commit comments

Comments
 (0)