WSGI/ASGI Instrumentation

The WSGI and ASGI standards provide a common layer for the development of web frameworks and servers in Python. Most popular Python web frameworks follow these standards, allowing for any Python web application to be deployed with any Python web server.

ASGI support was added in version 0.1.4 of the AppSignal for Python package.

AppSignal supports instrumentation for generic WSGI and ASGI web applications through OpenTelemetry.

AppSignal also supports popular web frameworks directly, such as Django, Flask, FastAPI or Starlette. If you're using a web framework we support directly, follow the instructions for that framework instead of the generic WSGI/ASGI instructions on this page.

Install the Instrumentation

Don't forget to install the AppSignal for Python package in your application first.

First, install either the opentelemetry-instrumentation-wsgi or the opentelemetry-instrumentation-asgi package, depending the gateway interface used in the application. To add it to your project, add the following line to your requirements.txt file:

# requirements.txt # For a WSGI application: opentelemetry-instrumentation-wsgi # For an ASGI application: opentelemetry-instrumentation-asgi


In your application's entry point (usually named or, before your application is initialised, call appsignal.start() to start your application:

from __appsignal__ import appsignal appsignal.start() # ... the rest of your code goes here ...

Then, import OpenTelemetryMiddleware from the corresponding OpenTelemetry instrumentation, and wrap your application with it:

# For a WSGI application: from opentelemetry.instrumentation.wsgi import OpenTelemetryMiddleware # For an ASGI application: from opentelemetry.instrumentation.asgi import OpenTelemetryMiddleware my_app = # ... your WSGI/ASGI application goes here ... my_app = OpenTelemetryMiddleware(my_app)

Grouping routes

By default, a generic WSGI or ASGI instrumentation will not have the necessary context to understand how routing is structured in your application. As such, the performance samples that AppSignal receives will not be grouped correctly. You must add routing information to the OpenTelemetry span manually.

One way to do so is by following OpenTelemetry's HTTP semantic conventions, setting the http.route attribute in your route handlers:

from opentelemetry.trace import get_current_span def get_user_route_handler(): get_current_span().set_attribute("http.route", "/users/:user_id") # ... your route handler goes here ...

AppSignal will automatically use the value of this attribute and use it to group requests, along with the request method. For example, a GET request that is handled by this method will appear as GET /users/:user_id in the AppSignal performance samples panel.

If you prefer to group requests in some other way (for example, by referring to the method or class that implements them) you can use the appsignal.root_name attribute:

from opentelemetry.trace import get_current_span class UserHandlers: def show(): get_current_span().set_attribute("appsignal.root_name", "UserHandlers#show") # ... your route handler goes here ...

By using appsignal.root_name, the request method will not be used. The value provided (in this case, UserHandlers#show) will be used to group all requests handled by this method.