Your data. Anywhere you go.

New Relic for iOS or Android


Download on the App Store    Android App on Google play


New Relic Insights App for iOS


Download on the App Store


Learn more

Close icon

Feature Idea: Starlette/Responder Framework Support

feature-request
python
feature-idea

#1

Rolled a couple new APIs using Responder instead of Flask. I enjoyed it and my async functions work well. Then I realized New Relic doesn’t support it. :frowning:

Can we get this framework supported? Responder was created by Kenneth Reitz (Flask creator) and supports Python’s new asyncio functionality. Responder uses Starlette under the hood.

https://python-responder.org/en/latest/
https://www.starlette.io/


New Relic Edit

  • I want this too
  • I have more info to share (reply below)
  • I have a solution for this

0 voters

We take feature ideas seriously and our product managers review every one when plotting their roadmaps. However, there is no guarantee this feature will be implemented. This post ensures the idea is put on the table and discussed though. So please vote and share your extra details with our team.


#2

Thanks for posting here @vwilson - I’ve gone ahead adding the Feature Idea poll here for you - and I’ve got that filed internally for you too.


#3

Co-signed, Starlette is a really impressive framework (currently running in production for us) & should receive official support.


#4

Thanks for the +1 @reach - I’ll get that added here internally :slight_smile:


#5

I was able to get this working as a starlette middleware:

import newrelic.agent
newrelic.agent.initialize()

class NewRelicMiddleware:
    def __init__(self, app):
        self.app = app

    class NewRelicMiddlewareRetrievers:
        @staticmethod
        def get_transaction_name(middleware, scope, *args, **kwargs):
            # Strip off the leading slash
            name = scope.get('path', '/no-path')
            if name == '/':
                return ''
            return name[1:]

        @staticmethod
        def get_headers(middleware, scope, *args, **kwargs):
            return scope.get('headers', [])

        @staticmethod
        def get_header_values(header_names):
            def retrieve(middleware, scope, *args, **kwargs):
                headers = scope.get('headers', [])
                # Iterate over all header names and get the first one that has a value
                for header_name in header_names:
                    for header in headers:
                        if header[0] == header_name:
                            return header[1].decode('utf-8')
                return None
            return retrieve

        @staticmethod
        def get_scope_property(prop_name):
            def retrieve(middleware, scope, *args, **kwargs):
                return scope.get(prop_name)
            return retrieve

    @newrelic.agent.web_transaction(
        name=NewRelicMiddlewareRetrievers.get_transaction_name,
        scheme=NewRelicMiddlewareRetrievers.get_scope_property('scheme'),
        host=NewRelicMiddlewareRetrievers.get_header_values([b'x-forwarded-host', b'host']),
        request_path=NewRelicMiddlewareRetrievers.get_scope_property('path'),
        request_method=NewRelicMiddlewareRetrievers.get_scope_property('method'),
        query_string=NewRelicMiddlewareRetrievers.get_scope_property('query_string'),
        headers=NewRelicMiddlewareRetrievers.get_headers,
    )
    async def __call__(self, scope, receive, send):
        try:
            await self.app(scope, receive, send)
        except Exception as e:
            raise e from None

I’ve probably done something not-so-great in the middleware itself, but I have confirmed that it does, indeed, work. FYI, I actually did this in FastAPI, but I built a regular starlette middleware to make it a bit more portable.


#6

This is excellent @saville - thanks so much for building that, and for sharing it with the community!