A fully featured APM for django using django.
- Know each view is getting more requests/errors;
- Keep track of response times;
- Registers errors and notify you/your team on slack/discord with the exception, traceback and logs;
- Keep track of requests headers/query and payloads (on errors), making it easy to reproduce them in a separate environment.
- Notify your team using celery if desired;
Notifications example: On slack: On discord:
Install the package using your favorite packaging tool: pip / poetry / pdm, etc.
-
Add
djapm.apm.apps.ApmConfig
toINSTALLED_APPS
in your django settings. Make sure to add it beforedjango.contrib.admin
. Otherwise, the dashboard link will not show up in Django admin. If you're going to use theapm_api_view
: Don't forget to addrest_framework
to yourINSTALLED_APPS
. It also requires thedjango.contrib.sites
app installed. It must come before thedjango.contrib.admin
for the dashboard link show up. -
Adding the middlewares
-
Adding the ApmMetricsMiddleware middleware (optional). If you want to track the requests timing, add the
djapm.apm.middlewares.ApmMetricsMiddleware
toMIDDLEWARE
in your django settings. Note that this will store into your database all requests that use the decoratorapm_api_view
(This decorator is explained later), no cleanup is done. Is up to you to clean the registers periodically if desired. It's better to keep this middleware the closest to the top of yourMIDDLEWARE
as possible , this way theellapsed
time is closer to the reality. -
Adding the ErrorTraceMiddleware (optional). If you want to track errors and receive notifications, add the
djapm.apm.middlewares.ErrorTraceMiddleware
toMIDDLEWARE
in your django settings. We recommend that you keep this middleware closest to the bottom of yourMIDDLEWARE
as possible. This middleware will notify any exception raised on your view to all integrations added (the setup is explained later).
-
-
Including the URLs
djapm
comes with a dashboard that's accessible through the django-admin. It only allows superusers to view it. To enable the dashboard include the following in your rooturls.py
file:from django.urls import include urlpatterns = [ # Your other patterns... path("apm/", include("djapm.apm.urls")), ]
Now go to the admin, you should be able to see the dashboard link.
The dashboard shows the data collected from the middlewares. It uses
chart.js
to render the graphics. The data is get from the database via API. Example: -
Upgrading your views django-apm comes with 3 decorators:
apm_api_view
,apm_view
andapm_admin_view
. Also, it comes with a ClassBasedView:ApmView
and a ModelAdmin for tracking POST requests:ApmModelAdmin
. Each of these adds some attributes to therequest
, they are:id
(str
): A string UUID4;logger
(logging.Logger
): A logger that you can use to log to the sdout and keep track of all logs emitted._log_handler
(djapm.apm.log.ApmStreamHandler
): We use this handler so we can capture all logs emitted by your view; Do not use this directly._json
(Any
): An alias torest_framework.Request.data
we store this because it's only available at the view, and if not stored, we wouldn't be able to track the payload in the middleware.
If you enabled the
ApmMetricsMiddleware
, this request and response will be saved to the database. If you enabled theErrorTraceMiddleware
any errors uncaught raised by your view will be notified to you. This does not includes therest_framework.serializers.ValidationError
, sincerest_framework
itself already handles this exception.So, let's get started with each decorator:
-
apm_api_view
: This decorator is an extension of therest*framework.decorators.api_view
decorator, so you don't need to wrap your view with therest_framework
decorator.Example api view:
from djapm.apm.decorators import apm_api_view from djapm.apm.types import ApmRequest @apm_api_view(["GET"]) def your_view(request: ApmRequest, **kwargs): logger = request.logger req_id = request.id ...
What's changed from the
rest_framework
function-based view: Therequest
parameter is now of the typeApmRequest
, it inherits fromrest_framework.request.Request
so you still have access to all it's attributes. You receive alogger
and aid
that you can use. Thelogger
was created just before your view using a default, if you need to use a different logger you can pass the logger name in the decorator. -
apm_view
: Add this decorator to all your django regular function-based view's that you want to keep track of.Example django view:
from djapm.apm.decorators import apm_view from djapm.apm.types import PatchedHttpRequest @apm_view() def your_django_regular_view(request: PatchedHttpRequest, **kwargs): ...
-
apm_admin_view
: Add this decorator to all your django regular admin view's that you want to keep track of.Example django admin view:
from django.contrib import admin from djapm.apm.decorators import apm_admin_view from djapm.apm.types import PatchedHttpRequest class YourAdmin(admin.ModelAdmin): @apm_admin_view() def your_custom_admin_view(self, request: PatchedHttpRequest, **kwargs): ... # don't forget to override the `get_urls` the same way as when you did with your custom view.
-
The class-based-view (CBV) version:
If you want to keep track of your CBVs. Inherit the
ApmView
in your view, so we can add the required attributes before your view gets called.Example django CBV view:
from django.views.generic import CreateView # or any other view from djapm.apm.views import ApmView from djapm.apm.types import PatchedHttpRequest class YourCreateView(CreateView, ApmView): ... def post(self, request: PatchedHttpRequest, **kwargs): ...
-
The Django Admin POST requests: If you want to keep track of your admin model Creations, inherit from
ApmModelAdmin
instead of the regularadmin.ModelAdmin
. Example:from django.contrib import admin from djapm.apm.admin import ApmModelAdmin from yourapp.models import AModel @admin.register(AModel) class YourModelAdmin(ApmModelAdmin): ...
-
Running the migrations:
Since we rely on the database, you must migrate your database:
python manage.py migrate
. This will create these models:ApiRequest
- Keep tracks of requests, including headers, query parameters;ApiResponse
- Keep tracks of responses, including status_code, ellapsed time.ErrorTrace
- Keep tracks of errors on your views.RequestLog
- Keep track of all logs that were logged from therequest.logger
;Integration
- Keep track of all your integrations;NotificationReceiver
- Keep track of all notifications receivers of an integration.
-
Create an Integration Now go to the admin, you should now see the models registered there. Go to integrations and create at least one integration. Current supported integrations:
-
Slack; Uses the Slack App key to send messages through to it's API. Here's a guide on how to create your app and get the key. Scopes required:
channels:read
,chat:write
. After you get this configured. Install the slack app into the workspace that should send messages. Later, allow/invite the bot to a conversation/channel. When creating theIntegration
on the admin, the slack integration supports both thename
andid
of a channel conversation. Thename
of the channel is the visible name that appear on slack, note that using the name will require that we fetch theid
on the slack api. Theid
of the channel can be retrieved on Slack. Click with the mouse's right button and click onCopy
->Copy Link
, theid
is the last part of the URL. The same applies for a user. -
Discord; Uses a Discord Bot account to send messages through it's API. Here's a guide on how to create your Bot account. Required scopes:
guilds
,SEND_MESSAGES
. When creating theIntegration
on the admin, the discord integration supports both thename
andid
of a channel conversation. Thename
of the channel is the visible name that appear on discord, note that using the name will require that we fetch theid
on the discord api. So we need to: Get your Botguilds
, for eachguild
get it'schannels
and lookup for a channel with the exact name that was provided. The message is sent to the exact first channel found on any guild, so use a specific name. Theid
of the channel can be retrieved on Discord. Click with the mouse's right button and click onCopy Link
, theid
is the last part of the URL. The same applies for a user.That's it! You're setup to keep track of your Requests. Also, take a look into the source code
example
folder, that contains a django-project that uses thedjapm
package.
-
-
Optional settings
django-apm
has a set of optional configurations, that can be set on your django settings module. Checkout them below:APM_DEFAULT_LOGGER_NAME
: The default logger name that will be used if None is supplied to theapm_view_decorator
. Defaults to:apm_api_view
;APM_REQUEST_SAVE_HEADERS
: Boolean that when set toTrue
will save the request headers. Defaults toTrue
;APM_REQUEST_SAVE_QUERY_PARAMETERS
: Boolean that when set toTrue
will save the request query parameters as json. Defaults toTrue
;APM_REQUEST_SAVE_QUERY_STRING
: Boolean that when set toTrue
will save the request raw query string. Defaults toTrue
;APM_NOTIFY_USING_CELERY
: Boolean that when set toTrue
will dispatch a celery task when notificating. Since the process of notificating Integration can take a long time, we suggest you to set this toTrue
. Defaults toFalse
.
Since djapm
uses the database to register, get metrics, this may lead to a lot of storage being used. djapm
doesn't do cleanups, if you use celery-beat it may be useful to have a scheduled task to do the cleanup. Most of the data displayed on the dashboard is from the last 7 days.
You also may find useful to use a separate database for this metrics, errors.
The ellapsed time displayed, registered is not precise from what your clients may be having. Since we only start keeping track of the time when the request first enters the ApmMetricsMiddleware
.
- Include support for ViewSets.