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

Starlette: use route name as transaction name if available #957

Merged
merged 7 commits into from
Nov 17, 2020

Conversation

beniwohli
Copy link
Contributor

Unfortunately, we need to do some matching here that Starlette does
too, but currently isn't exposed. If/when encode/starlette#804
is merged, we can re-use the route from there.

closes #833

Unfortunately, we need to do some matching here that Starlette does
too, but currently isn't exposed. If/when encode/starlette#804
is merged, we can re-use the route from there.

closes elastic#833
@apmmachine
Copy link
Contributor

apmmachine commented Oct 28, 2020

🐛 Flaky test report

❕ There are test failures but not known flaky tests.

Expand to view the summary

Test stats 🧪

Test Results
Failed 2
Passed 11379
Skipped 8294
Total 19675

Genuine test errors 2

💔 There are test failures but not known flaky tests, most likely a genuine test failure.

  • Name: Initializing / Test / windows-2.7-none / test_client_shutdown_sync[sending_elasticapm_client0] – tests.client.client_tests
  • Name: Initializing / Test / Python-pypy-2-1 / test_skip_instrument_env_var – tests.instrumentation.base_tests

@apmmachine
Copy link
Contributor

apmmachine commented Oct 28, 2020

💚 Build Succeeded

the below badges are clickable and redirect to their specific view in the CI or DOCS
Pipeline View Test View Changes Artifacts preview

Expand to view the summary

Build stats

  • Build Cause: [Pull request #957 updated]

  • Start Time: 2020-11-17T14:29:25.101+0000

  • Duration: 21 min 22 sec

Test stats 🧪

Test Results
Failed 0
Passed 12103
Skipped 8375
Total 20478

💚 Flaky test report

Tests succeeded.

Expand to view the summary

Test stats 🧪

Test Results
Failed 0
Passed 12103
Skipped 8375
Total 20478

@HenrikOssipoff
Copy link
Contributor

@beniwohli As promised I've taken this for a spin!

It works nicely, and is miles better than the previous implementation! Thank you :)

There's a small thing that I guess is likely working as intended, but from a user perspective isn't the best experience.

I made these two simple routes as a test:

app = Starlette(routes=[
    Route('/users/', list_users),
    Route('/users/{pk:int}/', get_user),
])

That is, I forced trailing slashes to my URLs. Attempting to make a request to the URL without the slash causes a redirect in Starlette, but the new code to get the route name couldn't match the route (obviously), causing this mismatch (the ID is listed instead of {pk:int}):

Skærmbillede 2020-10-28 kl  19 44 13

This means if you had a big site, your transaction list would still be really cluttered with redirects containing the actual value for the URL path param.

Not sure how easily that's fixable, but it would greatly improve the user experience :)

Copy link
Contributor

@basepi basepi left a comment

Choose a reason for hiding this comment

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

This looks like a huge improvement as is, one potential solution to the problems that @HenrikOssipoff is seeing below.

Comment on lines 198 to 208
def get_route_name(self, request: Request) -> str:
path = None
routes = request.scope["app"].routes
for route in routes:
match, _ = route.matches(request.scope)
if match == Match.FULL:
path = route.path
break
elif match == Match.PARTIAL and path is None:
path = route.path
return path
Copy link
Contributor

Choose a reason for hiding this comment

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

Based on @HenrikOssipoff's comment, I wonder if path is None when we go to return, should we try appending a "/" to path to attempt to get a match that way? That should solve the matching issues he's seeing, right? Slightly more overhead, but I still think it's worth it to avoid all of those redirect one-off transaction names.

@beniwohli
Copy link
Contributor Author

@HenrikOssipoff @basepi I've implemented a fix for the issue with trailing slashes. In the end I decided to create a separate "bucket" for these transactions and not simply try to find a match with/without trailing slash. The latter would mix "real" requests with redirect requests, and that would give a false impression on the real requests' performance (as redirects will be extremely fast).

WDYT?

for route in routes:
match, _ = route.matches(redirect_scope)
if match != Match.NONE:
route_name = "redirect trailing slashes"
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
route_name = "redirect trailing slashes"
route_name = "<internal redirect>"

What do you think about this? I'm not sure the "trailing slashes" will mean anything to the average user.

I do like collecting all of these transactions in a separate bucket so that they don't affect the stats for the routes to which they redirect, I'm just not sure of the name.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I struggled with naming, and you're probably right that my choice isn't super understandable by users not familiar with the internals of Starlette routing (especially because the Router.redirect_slashes flag seems to be undocumented and not configurable when instantiating a Starlette application). <internal redirect> seems to be very broad though, as we won't put any other kinds of redirects into that name bucket...

Copy link
Contributor

Choose a reason for hiding this comment

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

Eh, I don't feel strongly. We can keep it as-is.

Copy link
Contributor

@HenrikOssipoff HenrikOssipoff Nov 16, 2020

Choose a reason for hiding this comment

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

Love the changes! Can't wait to see this in master.

As a user, I would probably expect the following behaviour:

Suggested change
route_name = "redirect trailing slashes"
route_name = route.path[:-1]

I think that would be somewhat in line with what we see with other frameworks, that expose the route information on the request objects (I could be wrong though). This way, the routes are still collected in a separate bucket, but each "redirect route" has its own.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@HenrikOssipoff I implemented your suggestion now. I'm a bit concerned that it could spam the transaction list with lots of these transaction names that are only redirects to real transactions, but I guess that's only a problem if the wrong URLs are used consistently.

# Conflicts:
#	elasticapm/contrib/starlette/__init__.py
#	tests/contrib/asyncio/starlette_tests.py
@beniwohli beniwohli merged commit f180b1f into elastic:master Nov 17, 2020
@beniwohli beniwohli deleted the starlette-route-names branch November 17, 2020 15:36
beniwohli added a commit to beniwohli/apm-agent-python that referenced this pull request Sep 14, 2021
* Starlette: use route name as transaction name if available

Unfortunately, we need to do some matching here that Starlette does
too, but currently isn't exposed. If/when encode/starlette#804
is merged, we can re-use the route from there.

closes elastic#833

* detect trailing slash redirects from Starlette and give them a dedicated transaction name

* adapt original route for slash redirects
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.

Consider using route information instead of URL for Starlette transactions
4 participants