-
Notifications
You must be signed in to change notification settings - Fork 609
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
Falcon 3 support #644
Falcon 3 support #644
Conversation
@ocelotl take a look when you have a chance. I'm seeing the following failure:
This doesn't make much sense to me, I didn't change the filenames... I'm also seeing lint and generate failures. I ran Also, |
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.
Nice. Looks good. Left one comment.
@@ -140,11 +140,22 @@ def instrumentation_dependencies(self) -> Collection[str]: | |||
return _instruments | |||
|
|||
def _instrument(self, **kwargs): | |||
self._original_falcon_api = None | |||
self._original_falcon_app = None |
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.
can we use only one attribute for both 2 and 3, and conditionally read/write falcon.App or falcon.API?
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.
Sure, if you prefer that
@adriangb what command is that traceback from? I just ran |
I think you probably need to update these lines as well: https://github.com/open-telemetry/opentelemetry-python-contrib/blob/main/tox.ini#L248 |
|
Makes sense, thank you, updated |
Co-authored-by: Owais Lone <[email protected]>
Co-authored-by: Owais Lone <[email protected]>
Co-authored-by: Owais Lone <[email protected]>
Thanks for the help @owais Looks like the tests are actually broken now. The tests access a private attribute of |
Looks like Falcon changed how it handles middlewares internally so we need to update test code so it can correctly fetch the injected Otel middleware for both 2 and 3. |
Yes, I agree. However I think I think it's unfair to blame this on Falcon: the test breaks because it's fragile, and it's fragile because it accesses private attributes of a 3rd party library. They could have made the change in a patch bump, we should be thankful that they seem to have done it during a major version bump... So while it may be possible to fix this here, I think the overall approach of monkey patching stuff (in the library, in tests) and accessing private stuff in 3rd party libraries is fundamentally flawed and brittle. It's also going to be hard to fix this test: I still can't run this locally (because as we've discussed before the local setup instructions are broken; and no one has been able to give me a series of valid steps of cloning repos into repos and installing stuff that works). It looks like this portion of the code was authored by you (@owais ) and @lonewolf3739. I'd appreciate a walkthrough of why this is being done, what |
I'm not blaming anything. I was merely pointing out what needs to be fixed in order to make the test pass again.
Looking at the test code, it need a reference to the injected middleware which can be accessed from a falcon app instance with the help of |
Is there an issue for this I can look at and answer? |
@@ -193,7 +211,6 @@ def __call__(self, env, start_response): | |||
env[_ENVIRON_ACTIVATION_KEY] = activation | |||
|
|||
def _start_response(status, response_headers, *args, **kwargs): | |||
otel_wsgi.add_response_attributes(span, status, response_headers) |
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.
This was a pretty insidious bug that was hard to figure out.
If you look at the middleware, it is already setting span.status
. This re sets it. otel_wsgi.add_response_attributes
sets the same status code as the middleware, but does not set the description. So after this line executes span.status.description
is always None. But none of the tests were checking this, so it was not caught.
But what about test_500
then? Well, in Falcon 2 (but not 3) super().__call__(...)
would re-raise the error, triggering the except
clause right below this that skips calling _start_response
and thus otel_wsgi.add_response_attributes(span, status, response_headers)
is never called and span.status.description
is carried over from the middleware.
I'll add an assertion for span.status.description
to other tests so that this can be avoided in the future.
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.
Checks added in 8eda7b6
def _handle_exception( | ||
self, req, resp, ex, params | ||
): # pylint: disable=C0103 | ||
# Falcon 3 does not execute middleware within the context of the exception | ||
# so we capture the exception here and save it into the env dict | ||
_, exc, _ = exc_info() | ||
req.env[_ENVIRON_EXC] = exc | ||
return super()._handle_exception(req, resp, ex, params) |
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 couldn't figure out another way to do this other than accessing this private method, FYI
# Falcon 3 does not execute middleware within the context of the exception | ||
# so we capture the exception here and save it into the env dict | ||
_, exc, _ = exc_info() | ||
req.env[_ENVIRON_EXC] = exc |
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'm using the already defined but previously unused key _ENVIRON_EXC
to store the status info.
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.
and this works for both 2 and 3?
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 _handle_exception
also exists in Falcon 2, so it seems to work 😃
if hasattr(falcon, "App"): | ||
# Falcon 3 | ||
_instrument_app = "App" | ||
else: | ||
# Falcon 2 | ||
_instrument_app = "API" |
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 centralized this logic up here so we don't duplicate it below
|
||
|
||
class _InstrumentedFalconAPI(falcon.API): | ||
class _InstrumentedFalconAPI(getattr(falcon, _instrument_app)): |
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 really dislike using a getattr
in a class def, but it works and it avoids a deprecation warning for Falcon 3 users.
@owais this is now working 😄 |
I opened #683 to discuss further |
@owais I merged in main. Could we move forward with this? Thanks. |
thanks for updating the branch 😄 |
LGTM but giving others time to review as well. |
@adriangb please resolve conflcits and we'll merge this. |
Head branch was pushed to by a user without write access
Done 😄 , let's let the tests run and make sure everything is still ✅
At this point in time I have enough OSS work on my hands, but I will certainly consider it. I appreciate the offer. |
No description provided.