Skip to content
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

Capture custom request/response headers for wsgi and change in passing response_headers in django, pyramid #925

Merged
merged 25 commits into from
Mar 7, 2022

Conversation

ashu658
Copy link
Member

@ashu658 ashu658 commented Feb 17, 2022

Description

  • This PR implements the HTTP request/response headers as span attributes semantic convention for wsgi frameworks.
    User can configure environment variable OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_REQUEST and OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_RESPONSE to capture request/response headers respectively.
  • Changes django Instrumentation to pass response headers instead of response object.
  • Changes pyramid instrumentation to pass response headers as list.

Fixes #918

Type of change

Please delete options that are not relevant.

  • New feature (non-breaking change which adds functionality)
  • This change requires a documentation update

How Has This Been Tested?

Added unit tests for the changes. Also, tested captured headers manually with wsgi based framework by observing the output on jaeger backend.

Does This PR Require a Core Repo Change?

  • No.

Checklist:

See contributing.md for styleguide, changelog guidelines, and more.

  • Followed the style guidelines of this project
  • Changelogs have been updated
  • Unit tests have been added
  • Documentation has been updated

@ashu658 ashu658 requested a review from a team February 17, 2022 09:50
@ashu658 ashu658 changed the title Capture custom header wsgi [WIP] Capture custom request/response headers for wsgi Feb 17, 2022
@ashu658 ashu658 marked this pull request as draft February 17, 2022 09:51
@ashu658 ashu658 force-pushed the capture-custom-header-wsgi branch from 3adf3de to e864f36 Compare February 17, 2022 10:22
@ashu658 ashu658 changed the title [WIP] Capture custom request/response headers for wsgi [WIP] Capture custom request/response headers for wsgi and correctly passing response_headers in django Feb 17, 2022
@ashu658 ashu658 changed the title [WIP] Capture custom request/response headers for wsgi and correctly passing response_headers in django [WIP] Capture custom request/response headers for wsgi and correctly passing response_headers in django, pyramid Feb 17, 2022
@ashu658 ashu658 force-pushed the capture-custom-header-wsgi branch from 447533e to f2b2288 Compare February 17, 2022 14:14
@ashu658 ashu658 marked this pull request as ready for review February 17, 2022 14:25
@ashu658 ashu658 changed the title [WIP] Capture custom request/response headers for wsgi and correctly passing response_headers in django, pyramid Capture custom request/response headers for wsgi and correctly passing response_headers in django, pyramid Feb 17, 2022
@ashu658 ashu658 changed the title Capture custom request/response headers for wsgi and correctly passing response_headers in django, pyramid Capture custom request/response headers for wsgi and change in passing response_headers in django, pyramid Feb 17, 2022

return result


def capture_custom_request_headers(environ, attributes):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any specific reason to pass the attribute dict as a argument and modifying it with new headers rather than returning the headers to calling function?
I think it will be better from readability perspective to update the result dict in a single function rather than passing to called function and update there

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree. It is not clear what the function does without reading the function body.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added suggested changes to improve readability. Please have a look.

attributes[key] = [header_values]


def capture_custom_response_headers(response_headers, span):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same comment as I have given in capture_custom_request_headers()

CHANGELOG.md Outdated Show resolved Hide resolved
from urllib.parse import urlparse, urlunparse

OTEL_CAPTURE_REQUEST_HEADERS = "OTEL_CAPTURE_REQUEST_HEADERS"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want to further split this to client/server like Java does?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it is good to split as this will make the feature consistent between different sdks. Also, will keep the env vars same as java sdk.

Not sure but, when I first read the spec it felt like its only talking about the server side. Please share your thoughts :)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like we are going with the general approach for being consistent with other language sdks like here so it would make sense to follow suit.

@@ -161,7 +161,7 @@ def trace_tween(request):
otel_wsgi.add_response_attributes(
span,
response_or_exception.status,
response_or_exception.headers,
response_or_exception.headerlist,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this safe for all supported version of django?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess you are referring to response.items() in .../instrumentation/django/middleware.py
Yes, I have tested it with lowest supported version of django by running the unit tests (as docs for django dont' say anything about this property below 4.0) and also done same for pyramid.
Do we run tests for different versions of a framework in the build jobs?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was planning to add tests for all wsgi based frameworks in different PR.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was referring to replacing headers with headerlist. Is headerlist attribute available on all supported versions of django?

Copy link
Member Author

@ashu658 ashu658 Mar 3, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

available on all supported versions of django?

I think there is some confusion, the change header to headerlist is for pyramid :)

Is headerlist attribute available on all supported versions

Yes, I checked pyramid docs, headerlist attribute is available in all versions of pyramid we support (>=1.7).


return result


def capture_custom_request_headers(environ, attributes):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree. It is not clear what the function does without reading the function body.

@ashu658 ashu658 force-pushed the capture-custom-header-wsgi branch from 8011ad5 to aedc4e2 Compare February 24, 2022 12:42
@ashu658
Copy link
Member Author

ashu658 commented Mar 2, 2022

Can someone run combine benchmarks from previous build again?

@ashu658 ashu658 requested review from srikanthccv and owais March 2, 2022 11:03
@lzchen
Copy link
Contributor

lzchen commented Mar 2, 2022

@sanketmehta28
Have your comments been addressed?

@sanketmehta28
Copy link
Member

@lzchen:yes I have checked it. They are addressed. I will approve this PR.

1 similar comment
@sanketmehta28
Copy link
Member

@lzchen:yes I have checked it. They are addressed. I will approve this PR.

@ashu658 ashu658 changed the title Capture custom request/response headers for wsgi and change in passing response_headers in django, pyramid [WIP] Capture custom request/response headers for wsgi and change in passing response_headers in django, pyramid Mar 3, 2022
@ashu658
Copy link
Member Author

ashu658 commented Mar 3, 2022

I forgot to add the condition to check if the span is a SERVER span before adding custom headers. Had to do some refactoring to add this condition. Please have a look. Apologies to the reviewers for late changes. 😓

@ashu658 ashu658 requested a review from lzchen March 3, 2022 08:45
@ashu658 ashu658 changed the title [WIP] Capture custom request/response headers for wsgi and change in passing response_headers in django, pyramid Capture custom request/response headers for wsgi and change in passing response_headers in django, pyramid Mar 3, 2022
Copy link
Member

@srikanthccv srikanthccv left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mostly LGTM; few questions.

"http.response.header.content_type": (
"text/plain; charset=utf-8",
),
"http.response.header.content_length": ("100",),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, why is everything a sequence? it doesn't make sense that content_length is list, right?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is part of the spec actually.

The attribute value MUST consist of either multiple header values as an array of strings or a single-item array containing a possibly comma-concatenated string, depending on the way the HTTP library provides access to headers.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

but that is for the multiple header values?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think its for single values also. There is an example in the spec.

http.request.header.content_type=["application/json"]

Copy link
Member

@srikanthccv srikanthccv Mar 4, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's strange but not a blocker for me.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

WSGI: Capture request/response headers as span attributes
5 participants