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
rfb

#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:


ASGI External Trace
#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!


split this topic #7

A post was split to a new topic: Error in ASGI app


#8

When I try to get the current transaction, I receive None. Is that the case when using this middleware?

Ps: transaction is only available inside the __call__ method, which is strange as it is necessary to trace external methods.


#9

I haven’t yet tried to retrieve the current transaction with the middleware. It should be available as normal though. Perhaps NR support could help to find out why the transaction would not be available? How are you retrieving it?


#10

Hey @victor.lima I see that my colleague @BT21 is working with you in your other thread on this similar topic:

Let us know how that goes for you :smiley:


#11

Hi!

I’m following the steps here: current_transaction

the transaction is only available inside the middleware in the case that there are other middlewares applied after! I’m discussing in another topic if you want to follow!