Give AlbumentationsX a star on GitHub — it powers this leaderboard

Star on GitHub

django-silk

Silky smooth profiling for the Django Framework

Rank: #2478Downloads: 2,474,907 (30 days)Stars: 4,932Forks: 355

Description

Silk

GitHub Actions GitHub Actions PyPI Download PyPI Python Versions Supported Django versions Jazzband

Silk is a live profiling and inspection tool for the Django framework. Silk intercepts and stores HTTP requests and database queries before presenting them in a user interface for further inspection:

<img src="https://raw.githubusercontent.com/jazzband/django-silk/master/screenshots/1.png" width="720px"/>

Contents

Requirements

Silk has been tested with:

  • Django: 4.2, 5.1, 5.2
  • Python: 3.9, 3.10, 3.11, 3.12, 3.13

Installation

Via pip into a virtualenv:

pip install django-silk

To including optional formatting of python snippets:

pip install django-silk[formatting]

In settings.py add the following:

MIDDLEWARE = [
    ...
    'silk.middleware.SilkyMiddleware',
    ...
]

TEMPLATES = [{
    ...
    'OPTIONS': {
        'context_processors': [
            ...
            'django.template.context_processors.request',
        ],
    },
}]


INSTALLED_APPS = (
    ...
    'silk'
)

Note: The order of middleware is sensitive. If any middleware placed before silk.middleware.SilkyMiddleware returns a response without invoking its get_response, the SilkyMiddleware won’t run. To avoid this, ensure that middleware preceding SilkyMiddleware does not bypass or return a response without calling its get_response. For further details, check out the Django documentation.

Note: If you are using django.middleware.gzip.GZipMiddleware, place that before silk.middleware.SilkyMiddleware, otherwise you will get an encoding error.

If you want to use custom middleware, for example you developed the subclass of silk.middleware.SilkyMiddleware, so you can use this combination of settings:

# Specify the path where is the custom middleware placed
SILKY_MIDDLEWARE_CLASS = 'path.to.your.middleware.MyCustomSilkyMiddleware'

# Use this variable in list of middleware
MIDDLEWARE = [
    ...
    SILKY_MIDDLEWARE_CLASS,
    ...
]

To enable access to the user interface add the following to your urls.py:

urlpatterns += [path('silk/', include('silk.urls', namespace='silk'))]

before running migrate:

python manage.py migrate

python manage.py collectstatic

Silk will automatically begin interception of requests and you can proceed to add profiling if required. The UI can be reached at /silk/

Alternative Installation

Via github tags:

pip install git+https://github.com/jazzband/django-silk.git@<version>#egg=django_silk

You can install from master using the following, but please be aware that the version in master may not be working for all versions specified in requirements

pip install -e git+https://github.com/jazzband/django-silk.git#egg=django_silk

Features

Silk primarily consists of:

  • Middleware for intercepting Requests/Responses
  • A wrapper around SQL execution for profiling of database queries
  • A context manager/decorator for profiling blocks of code and functions either manually or dynamically.
  • A user interface for inspection and visualisation of the above.

Request Inspection

The Silk middleware intercepts and stores requests and responses in the configured database. These requests can then be filtered and inspecting using Silk's UI through the request overview:

<img src="https://raw.githubusercontent.com/jazzband/django-silk/master/screenshots/1.png" width="720px"/>

It records things like:

  • Time taken
  • Num. queries
  • Time spent on queries
  • Request/Response headers
  • Request/Response bodies

and so on.

Further details on each request are also available by clicking the relevant request:

<img src="https://raw.githubusercontent.com/jazzband/django-silk/master/screenshots/2.png" width="720px"/>

SQL Inspection

Silk also intercepts SQL queries that are generated by each request. We can get a summary on things like the tables involved, number of joins and execution time (the table can be sorted by clicking on a column header):

<img src="https://raw.githubusercontent.com/jazzband/django-silk/master/screenshots/3.png" width="720px"/>

Before diving into the stack trace to figure out where this request is coming from:

<img src="https://raw.githubusercontent.com/jazzband/django-silk/master/screenshots/5.png" width="720px"/>

Profiling

Turn on the SILKY_PYTHON_PROFILER setting to use Python's built-in cProfile profiler. Each request will be separately profiled and the profiler's output will be available on the request's Profiling page in the Silk UI. Note that as of Python 3.12, cProfile cannot run concurrently so django-silk under Python 3.12 and later will not profile if another profile is running (even its own profiler in another thread).

SILKY_PYTHON_PROFILER = True

If you would like to also generate a binary .prof file set the following:

SILKY_PYTHON_PROFILER_BINARY = True

When enabled, a graph visualisation generated using gprof2dot and viz.js is shown in the profile detail page:

<img src="https://raw.githubusercontent.com/jazzband/django-silk/master/screenshots/10.png" width="720px"/>

A custom storage class can be used for the saved generated binary .prof files:

# For Django >= 4.2 and Django-Silk >= 5.1.0:
# See https://docs.djangoproject.com/en/5.0/ref/settings/#std-setting-STORAGES
STORAGES = {
    'SILKY_STORAGE': {
        'BACKEND': 'path.to.StorageClass',
    },
    # ...
}

# For Django < 4.2 or Django-Silk < 5.1.0
SILKY_STORAGE_CLASS = 'path.to.StorageClass'

The default storage class is silk.storage.ProfilerResultStorage, and when using that you can specify a path of your choosing. You must ensure the specified directory exists.

# If this is not set, MEDIA_ROOT will be used.
SILKY_PYTHON_PROFILER_RESULT_PATH = '/path/to/profiles/'

A download button will become available with a binary .prof file for every request. This file can be used for further analysis using snakeviz or other cProfile tools

To retrieve which endpoint generates a specific profile file it is possible to add a stub of the request path in the file name with the following:

SILKY_PYTHON_PROFILER_EXTENDED_FILE_NAME = True

Silk can also be used to profile specific blocks of code/functions. It provides a decorator and a context manager for this purpose.

For example:

from silk.profiling.profiler import silk_profile


@silk_profile(name='View Blog Post')
def post(request, post_id):
    p = Post.objects.get(pk=post_id)
    return render(request, 'post.html', {
        'post': p
    })

Whenever a blog post is viewed we get an entry within the Silk UI:

<img src="https://raw.githubusercontent.com/jazzband/django-silk/master/screenshots/7.png" width="720px"/>

Silk profiling not only provides execution time, but also collects SQL queries executed within the block in the same fashion as with requests:

<img src="https://raw.githubusercontent.com/jazzband/django-silk/master/screenshots/8.png" width="720px"/>

Decorator

The silk decorator can be applied to both functions and methods

from silk.profiling.profiler import silk_profile


# Profile a view function
@silk_profile(name='View Blog Post')
def post(request, post_id):
    p = Post.objects.get(pk=post_id)
    return render(request, 'post.html', {
        'post': p
    })


# Profile a method in a view class
class MyView(View):
    @silk_profile(name='View Blog Post')
    def get(self, request):
        p = Post.objects.get(pk=post_id)
        return render(request, 'post.html', {
            'post': p
        })

Context Manager

Using a context manager means we can add additional context to the name which can be useful for narrowing down slowness to particular database records.

def post(request, post_id):
    with silk_profile(name='View Blog Post #%d' % self.pk):
        p = Post.objects.get(pk=post_id)
        return render(request, 'post.html', {
            'post': p
        })

Dynamic Profiling

One of Silk's more interesting features is dynamic profiling. If for example we wanted to profile a function in a dependency to which we only have read-only access (e.g. system python libraries owned by root) we can add the following to settings.py to apply a decorator at runtime:

SILKY_DYNAMIC_PROFILING = [{
    'module': 'path.to.module',
    'function': 'MyClass.bar'
}]