Skip to content

Commit f4fba8f

Browse files
authored
Merge branch 'main' into pymemcache_instruments_version_bump
2 parents 0c2c470 + dbc3073 commit f4fba8f

File tree

10 files changed

+387
-63
lines changed

10 files changed

+387
-63
lines changed

.github/workflows/test.yml

+47-47
Original file line numberDiff line numberDiff line change
@@ -45,53 +45,53 @@ jobs:
4545
key: v4-build-tox-cache-${{ env.RUN_MATRIX_COMBINATION }}-${{ hashFiles('tox.ini', 'gen-requirements.txt', 'dev-requirements.txt') }}
4646
- name: run tox
4747
run: tox -f ${{ matrix.python-version }}-${{ matrix.package }} -- --benchmark-json=${{ env.RUN_MATRIX_COMBINATION }}-benchmark.json
48-
- name: Find and merge ${{ matrix.package }} benchmarks
49-
# TODO: Add at least one benchmark to every package type to remove this (#249)
50-
if: matrix.package == 'sdkextension' || matrix.package == 'propagator'
51-
run: >-
52-
mkdir -p benchmarks;
53-
jq -s '.[0].benchmarks = ([.[].benchmarks] | add)
54-
| if .[0].benchmarks == null then null else .[0] end'
55-
**/**/tests/*${{ matrix.package }}*-benchmark.json > benchmarks/output_${{ matrix.package }}.json
56-
- name: Upload all benchmarks under same key as an artifact
57-
if: ${{ success() }}
58-
uses: actions/upload-artifact@v2
59-
with:
60-
name: benchmarks
61-
path: benchmarks/output_${{ matrix.package }}.json
62-
combine-benchmarks:
63-
runs-on: ubuntu-latest
64-
needs: build
65-
if: ${{ always() }}
66-
name: Combine benchmarks from previous build job
67-
steps:
68-
- name: Checkout Contrib Repo @ SHA - ${{ github.sha }}
69-
uses: actions/checkout@v2
70-
- name: Download all benchmarks as artifact using key
71-
uses: actions/download-artifact@v2
72-
with:
73-
name: benchmarks
74-
path: benchmarks
75-
- name: Find and merge all benchmarks
76-
run: >-
77-
jq -s '.[0].benchmarks = ([.[].benchmarks] | add)
78-
| if .[0].benchmarks == null then null else .[0] end'
79-
benchmarks/output_*.json > output.json;
80-
- name: Report on benchmark results
81-
uses: benchmark-action/github-action-benchmark@v1
82-
with:
83-
name: OpenTelemetry Python Benchmarks - Python ${{ env[matrix.python-version ]}} - ${{ matrix.package }}
84-
tool: pytest
85-
output-file-path: output.json
86-
github-token: ${{ secrets.GITHUB_TOKEN }}
87-
max-items-in-chart: 100
88-
# Alert with a commit comment on possible performance regression
89-
alert-threshold: 200%
90-
fail-on-alert: true
91-
# Make a commit on `gh-pages` with benchmarks from previous step
92-
auto-push: ${{ github.ref == 'refs/heads/main' }}
93-
gh-pages-branch: gh-pages
94-
benchmark-data-dir-path: benchmarks
48+
# - name: Find and merge ${{ matrix.package }} benchmarks
49+
# # TODO: Add at least one benchmark to every package type to remove this (#249)
50+
# if: matrix.package == 'sdkextension' || matrix.package == 'propagator'
51+
# run: >-
52+
# mkdir -p benchmarks;
53+
# jq -s '.[0].benchmarks = ([.[].benchmarks] | add)
54+
# | if .[0].benchmarks == null then null else .[0] end'
55+
# **/**/tests/*${{ matrix.package }}*-benchmark.json > benchmarks/output_${{ matrix.package }}.json
56+
# - name: Upload all benchmarks under same key as an artifact
57+
# if: ${{ success() }}
58+
# uses: actions/upload-artifact@v2
59+
# with:
60+
# name: benchmarks
61+
# path: benchmarks/output_${{ matrix.package }}.json
62+
# combine-benchmarks:
63+
# runs-on: ubuntu-latest
64+
# needs: build
65+
# if: ${{ always() }}
66+
# name: Combine benchmarks from previous build job
67+
# steps:
68+
# - name: Checkout Contrib Repo @ SHA - ${{ github.sha }}
69+
# uses: actions/checkout@v2
70+
# - name: Download all benchmarks as artifact using key
71+
# uses: actions/download-artifact@v2
72+
# with:
73+
# name: benchmarks
74+
# path: benchmarks
75+
# - name: Find and merge all benchmarks
76+
# run: >-
77+
# jq -s '.[0].benchmarks = ([.[].benchmarks] | add)
78+
# | if .[0].benchmarks == null then null else .[0] end'
79+
# benchmarks/output_*.json > output.json;
80+
# - name: Report on benchmark results
81+
# uses: benchmark-action/github-action-benchmark@v1
82+
# with:
83+
# name: OpenTelemetry Python Benchmarks - Python ${{ env[matrix.python-version ]}} - ${{ matrix.package }}
84+
# tool: pytest
85+
# output-file-path: output.json
86+
# github-token: ${{ secrets.GITHUB_TOKEN }}
87+
# max-items-in-chart: 100
88+
# # Alert with a commit comment on possible performance regression
89+
# alert-threshold: 200%
90+
# fail-on-alert: true
91+
# # Make a commit on `gh-pages` with benchmarks from previous step
92+
# auto-push: ${{ github.ref == 'refs/heads/main' }}
93+
# gh-pages-branch: gh-pages
94+
# benchmark-data-dir-path: benchmarks
9595
misc:
9696
strategy:
9797
fail-fast: false

CHANGELOG.md

+4-4
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
88
## [Unreleased](https://github.com/open-telemetry/opentelemetry-python/compare/v1.10.0-0.29b0...HEAD)
99

1010
### Added
11-
11+
- `opentelemetry-instrumentation-django` Capture custom request/response headers in span attributes
12+
([#1024])(https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1024)
13+
- `opentelemetry-instrumentation-asgi` Capture custom request/response headers in span attributes
14+
([#1004])(https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1004)
1215
- `opentelemetry-instrumentation-psycopg2` extended the sql commenter support of dbapi into psycopg2
1316
([#940](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/940))
1417
- `opentelemetry-instrumentation-flask` Fix non-recording span bug
@@ -24,9 +27,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2427

2528
## [1.10.0-0.29b0](https://github.com/open-telemetry/opentelemetry-python/releases/tag/v1.10.0-0.29b0) - 2022-03-10
2629

27-
- `opentelemetry-instrumentation-wsgi` Capture custom request/response headers in span attributes
28-
([#1004])(https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1004)
29-
3030
- `opentelemetry-instrumentation-wsgi` Capture custom request/response headers in span attributes
3131
([#925])(https://github.com/open-telemetry/opentelemetry-python-contrib/pull/925)
3232
- `opentelemetry-instrumentation-flask` Flask: Capture custom request/response headers in span attributes

docs-requirements.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ asyncpg>=0.12.0
2525
boto~=2.0
2626
botocore~=1.0
2727
celery>=4.0
28-
flask~=1.0
28+
flask~=2.0
2929
falcon~=2.0
3030
grpcio~=1.27
3131
mysql-connector-python~=8.0

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

+46
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,52 @@ def response_hook(span, request, response):
7676
Django Request object: https://docs.djangoproject.com/en/3.1/ref/request-response/#httprequest-objects
7777
Django Response object: https://docs.djangoproject.com/en/3.1/ref/request-response/#httpresponse-objects
7878
79+
Capture HTTP request and response headers
80+
*****************************************
81+
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>`_.
82+
83+
Request headers
84+
***************
85+
To capture predefined HTTP request headers as span attributes, set the environment variable ``OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_REQUEST``
86+
to a comma-separated list of HTTP header names.
87+
88+
For example,
89+
::
90+
91+
export OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_REQUEST="content_type,custom_request_header"
92+
93+
will extract content_type and custom_request_header from request headers and add them as span attributes.
94+
95+
It is recommended that you should give the correct names of the headers to be captured in the environment variable.
96+
Request header names in django are case insensitive. So, giving header name as ``CUStom_Header`` in environment variable will be able capture header with name ``custom-header``.
97+
98+
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 _ ).
99+
The value of the attribute will be single item list containing all the header values.
100+
101+
Example of the added span attribute,
102+
``http.request.header.custom_request_header = ["<value1>,<value2>"]``
103+
104+
Response headers
105+
****************
106+
To capture predefined HTTP response headers as span attributes, set the environment variable ``OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_RESPONSE``
107+
to a comma-separated list of HTTP header names.
108+
109+
For example,
110+
::
111+
112+
export OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_RESPONSE="content_type,custom_response_header"
113+
114+
will extract content_type and custom_response_header from response headers and add them as span attributes.
115+
116+
It is recommended that you should give the correct names of the headers to be captured in the environment variable.
117+
Response header names captured in django are case insensitive. So, giving header name as ``CUStomHeader`` in environment variable will be able capture header with name ``customheader``.
118+
119+
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 _ ).
120+
The value of the attribute will be single item list containing all the header values.
121+
122+
Example of the added span attribute,
123+
``http.response.header.custom_response_header = ["<value1>,<value2>"]``
124+
79125
API
80126
---
81127
"""

instrumentation/opentelemetry-instrumentation-django/src/opentelemetry/instrumentation/django/middleware.py

+35-2
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,19 @@
2828
_start_internal_or_server_span,
2929
extract_attributes_from_object,
3030
)
31+
from opentelemetry.instrumentation.wsgi import (
32+
add_custom_request_headers as wsgi_add_custom_request_headers,
33+
)
34+
from opentelemetry.instrumentation.wsgi import (
35+
add_custom_response_headers as wsgi_add_custom_response_headers,
36+
)
3137
from opentelemetry.instrumentation.wsgi import add_response_attributes
3238
from opentelemetry.instrumentation.wsgi import (
3339
collect_request_attributes as wsgi_collect_request_attributes,
3440
)
3541
from opentelemetry.instrumentation.wsgi import wsgi_getter
3642
from opentelemetry.semconv.trace import SpanAttributes
37-
from opentelemetry.trace import Span, use_span
43+
from opentelemetry.trace import Span, SpanKind, use_span
3844
from opentelemetry.util.http import get_excluded_urls, get_traced_request_attrs
3945

4046
try:
@@ -77,7 +83,13 @@ def __call__(self, request):
7783

7884
# try/except block exclusive for optional ASGI imports.
7985
try:
80-
from opentelemetry.instrumentation.asgi import asgi_getter
86+
from opentelemetry.instrumentation.asgi import asgi_getter, asgi_setter
87+
from opentelemetry.instrumentation.asgi import (
88+
collect_custom_request_headers_attributes as asgi_collect_custom_request_attributes,
89+
)
90+
from opentelemetry.instrumentation.asgi import (
91+
collect_custom_response_headers_attributes as asgi_collect_custom_response_attributes,
92+
)
8193
from opentelemetry.instrumentation.asgi import (
8294
collect_request_attributes as asgi_collect_request_attributes,
8395
)
@@ -213,6 +225,13 @@ def process_request(self, request):
213225
self._traced_request_attrs,
214226
attributes,
215227
)
228+
if span.is_recording() and span.kind == SpanKind.SERVER:
229+
attributes.update(
230+
asgi_collect_custom_request_attributes(carrier)
231+
)
232+
else:
233+
if span.is_recording() and span.kind == SpanKind.SERVER:
234+
wsgi_add_custom_request_headers(span, carrier)
216235

217236
for key, value in attributes.items():
218237
span.set_attribute(key, value)
@@ -257,6 +276,7 @@ def process_exception(self, request, exception):
257276
if self._environ_activation_key in request.META.keys():
258277
request.META[self._environ_exception_key] = exception
259278

279+
# pylint: disable=too-many-branches
260280
def process_response(self, request, response):
261281
if self._excluded_urls.url_disabled(request.build_absolute_uri("?")):
262282
return response
@@ -271,12 +291,25 @@ def process_response(self, request, response):
271291
if activation and span:
272292
if is_asgi_request:
273293
set_status_code(span, response.status_code)
294+
295+
if span.is_recording() and span.kind == SpanKind.SERVER:
296+
custom_headers = {}
297+
for key, value in response.items():
298+
asgi_setter.set(custom_headers, key, value)
299+
300+
custom_res_attributes = (
301+
asgi_collect_custom_response_attributes(custom_headers)
302+
)
303+
for key, value in custom_res_attributes.items():
304+
span.set_attribute(key, value)
274305
else:
275306
add_response_attributes(
276307
span,
277308
f"{response.status_code} {response.reason_phrase}",
278309
response.items(),
279310
)
311+
if span.is_recording() and span.kind == SpanKind.SERVER:
312+
wsgi_add_custom_response_headers(span, response.items())
280313

281314
propagator = get_global_response_propagator()
282315
if propagator:

0 commit comments

Comments
 (0)