-
Notifications
You must be signed in to change notification settings - Fork 651
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
Add Flask integration based on WSGI ext #206
Add Flask integration based on WSGI ext #206
Conversation
span.set_attribute("http.url", url) | ||
|
||
|
||
def add_response_attributes( |
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.
Since this is now a public API (no underscore & docstring), I thought it is better to add the response_haders argument, since we might very well need it in the future, should we ever want to capture certain response headers as span attributes.
Why is a Flask integration needed in addition to the base WSGI integration? Flask supports WSGI middlewares through the mechanism described in the example document. |
The flask integration has (only) two advantages:
In addition, it also has an easier syntax to enable (you don't have to know about I'll copy that to the PR description, as it should have been there from the beginning. Thanks for asking that important question, @a-feld 👍 |
That makes sense, thanks for clarifying @Oberon00 ! Those bullet points definitely seem worth pursuing! I wonder if there's any way to layer / compose those additions so that we maintain separation between the WSGI middleware and the flask specific features? One approach I can think of is to use the We could continue the approach of inserting the WSGI middleware + before_request / after_request hooks with a I also agree with your assessment that it makes sense to split the flask integration into its own package! |
But using |
host += ":" + port | ||
|
||
# NOTE: Nonstandard (but see | ||
# https://github.com/open-telemetry/opentelemetry-specification/pull/263) |
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.
since this will likely be merged anyway (3 approvals), maybe just remove the comment?
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.
Not yet, according to that spec PR the http.host must only be set if there is an http.host header.
# //foo which looks like scheme-relative but isn't. | ||
url = environ["wsgi.url_scheme"] + "://" + host + url | ||
elif not url.startswith(environ["wsgi.url_scheme"] + ":"): | ||
# Something fishy is in RAW_URL. Let's fall back to request_uri() |
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.
when does this happen? Just out of curiosity. Sounds like a common occurence.
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.
Why do you think this is common? Personally, I don't really know how often that occurs in practice, but I imagine a buggy client could send a line like GET foobar HTTP/1.0
instead of GET /foobar HTTP/1.0
.
ext/opentelemetry-ext-wsgi/src/opentelemetry/ext/wsgi/flask_util.py
Outdated
Show resolved
Hide resolved
otel_wsgi.add_request_attributes(span, environ) | ||
if flask_request.url_rule: | ||
# For 404 that result from no route found, etc, we don't have a url_rule. | ||
span.set_attribute("http.route", flask_request.url_rule.rule) |
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.
should this default to the method then? as recommended in the opentelemetry-specification#270?
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.
open-telemetry/opentelemetry-specification#270 only talks about the span name, the http.route attribute is unrelated.
ext/opentelemetry-ext-wsgi/src/opentelemetry/ext/wsgi/flask_util.py
Outdated
Show resolved
Hide resolved
ext/opentelemetry-ext-flask/src/opentelemetry/ext/flask_util/version.py
Outdated
Show resolved
Hide resolved
Anyone care to review this? |
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.
The middleware changes LGTM, but I've got some reservations about the package structure changes.
I'd be interested to get another pass from @a-feld after your last round of changes.
from opentelemetry.ext.flask_util import instrument_app | ||
|
||
app = Flask(__name__) | ||
instrument_app(app) # This is where the magic happens. ✨ |
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.
🤬
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.
You think I should tone down the emojis? 😁
opentelemetry-ext-wsgi | ||
|
||
[options.extras_require] | ||
test = flask~=1.0 |
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.
That's interesting, do you think we should specify all our test dependencies this way?
This is a bit weird since the tests still wouldn't be included in the distribution if you do pip install 'opentelemetry-ext-flask[test]'
, this is only useful for editable installs.
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.
Hmm, I'm not sure. Since we now have separate coverage & test targets, in the interest of not growing the toxfile too much, I think I'll keep it here.
ext/test-util/README.txt
Outdated
@@ -0,0 +1,3 @@ | |||
This (non-installable) directory should be added to the PYTHONPATH of any unit tests that require it. |
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.
I think the cure is worse than the disease here. It may be inevitable that we'll have enough shared code between tests that we'll have to do something like this, but for now duplicating WsgiTestBase
seems less bad than forcing users to edit the PYTHONPATH.
I don't think we should expect that people will only run the tests via tox
. Doing e.g. pytest ext/opentelemetry-ext-flask/tests/test_flask_integration.py
is much faster while developing.
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.
I changed this to an ordinary installable package, so no manual PYTHONPATH manipulations required anymore.
ext/opentelemetry-ext-flask/src/opentelemetry/ext/flask_util/__init__.py
Outdated
Show resolved
Hide resolved
Conflicts: tox.ini
Pylint seems to grok it now.
Codecov Report
@@ Coverage Diff @@
## master #206 +/- ##
==========================================
+ Coverage 85.65% 85.66% +0.01%
==========================================
Files 33 33
Lines 1610 1612 +2
Branches 180 180
==========================================
+ Hits 1379 1381 +2
Misses 185 185
Partials 46 46
Continue to review full report at Codecov.
|
@open-telemetry/python-approvers I think this is now ready for another review, I hope I could address all issues satisfyingly. |
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.
Looks good overall. Only blocking comment is the change to the example in the README
Co-Authored-By: alrex <[email protected]>
Technically, this branch is ready to be merged, even though @toumorokoshi' review is outdated. Personally I think the PR is ready. Also, I'd like to get this merged before working on updating the WSGI extension to follow open-telemetry/opentelemetry-specification#263 to avoid merge conflicts. |
otel_wsgi.get_header_from_environ, environ | ||
) | ||
|
||
tracer = trace.tracer() |
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.
(non-blocking comment) There was a mention in the past about allowing users to optionally specify their own Tracer
instead of relying on the global one. Probably something to consider adding in the future ;)
|
||
tracer = trace.tracer() | ||
|
||
span = tracer.create_span( |
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.
We will be changing create_span()
to not exist in the public API, IIRC?
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.
Yes, PR #290 adapted the code, whoever is merged second will have to resolve merge conflicts.
…n-telemetry#206) * fix(basic-tracer): performance.now() is relative from start time * refactor(time): tuples * refactor(span): change name of toHrTime * fix(span): duration default * fix(hrtime): conver to tuple * fix(basic-tracer): nanosecond accuracy * feat(core): move time utils to core * feat(core): add millis and nanos converter * test(basic-tracer): fix span time tests
* fix: performance.now() is relative from start time * fix: grpc plugin tests * fix: import performance
The flask integration has (only) two advantages over the plain WSGI middleware approach:
In addition, it also has an easier syntax to enable (you don't have to know about Flask.wsgi_app).
(above summary copied from #206 (comment))
I did not add flask to the dependencies (except the test extra) because it seems kinda useless: To use the extension, you have to pass a Flask object which makes it obvious that you need Flask. If you think that's too odd, I will split the flask integration into new distribution that depends on the WSGI integration.