Package: django-silk
SilkSilk 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:ContentsRequirementsInstallationFeaturesConfigurationAuthentication/AuthorisationRequest/Response bodiesMeta-ProfilingRecording a fraction of requestsLimiting request/response dataClearing logged dataContributingDevelopment EnvironmentRequirementsSilk has been tested with:Django: 3.2, 4.1, 4.2, 5.0Python: 3.8, 3.9, 3.10, 3.11, 3.12InstallationVia pip into avirtualenv:pipinstalldjango-silkInsettings.pyadd the following:MIDDLEWARE=[...'silk.middleware.SilkyMiddleware',...]INSTALLED_APPS=(...'silk')Note:The middleware placement is sensitive. If the middleware beforesilk.middleware.SilkyMiddlewarereturns fromprocess_requestthenSilkyMiddlewarewill never get the chance to execute. Therefore you must ensure that any middleware placed before never returns anything fromprocess_request. See thedjango docsfor more information on this.Note:If you are usingdjango.middleware.gzip.GZipMiddleware, place thatbeforesilk.middleware.SilkyMiddleware, otherwise you will get an encoding error.If you want to use custom middleware, for example you developed the subclass ofsilk.middleware.SilkyMiddleware, so you can use this combination of settings:# Specify the path where is the custom middleware placedSILKY_MIDDLEWARE_CLASS='path.to.your.middleware.MyCustomSilkyMiddleware'# Use this variable in list of middlewareMIDDLEWARE=[...SILKY_MIDDLEWARE_CLASS,...]To enable access to the user interface add the following to yoururls.py:urlpatterns+=[path('silk/',include('silk.urls',namespace='silk'))]before running migrate:pythonmanage.pymigrate

pythonmanage.pycollectstaticSilk will automatically begin interception of requests and you can proceed to add profiling
if required. The UI can be reached at/silk/Alternative InstallationViagithub tags:pipinstallhttps://github.com/jazzband/silk/archive/<version>.tar.gzYou can install from master using the following, but please be aware that the version in master
may not be working for all versions specified inrequirementspipinstall-egit+https://github.com/jazzband/django-silk.git#egg=django-silkFeaturesSilk primarily consists of:Middleware for intercepting Requests/ResponsesA wrapper around SQL execution for profiling of database queriesA 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 InspectionThe 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:It records things like:Time takenNum. queriesTime spent on queriesRequest/Response headersRequest/Response bodiesand so on.Further details on each request are also available by clicking the relevant request:SQL InspectionSilk 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):Before diving into the stack trace to figure out where this request is coming from:ProfilingTurn on the SILKY_PYTHON_PROFILER setting to use Python's built-incProfileprofiler. 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,cProfilecannot run concurrently sodjango-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=TrueIf you would like to also generate a binary.proffile set the following:SILKY_PYTHON_PROFILER_BINARY=TrueWhen enabled, a graph visualisation generated usinggprof2dotandviz.jsis shown in the profile detail page:A custom storage class can be used for the saved generated binary.proffiles:# For Django >= 4.2 and Django-Silk >= 5.1.0:# See https://docs.djangoproject.com/en/5.0/ref/settings/#std-setting-STORAGESSTORAGES={'SILKY_STORAGE':{'BACKEND':'path.to.StorageClass',},# ...}# For Django < 4.2 or Django-Silk < 5.1.0SILKY_STORAGE_CLASS='path.to.StorageClass'The default storage class issilk.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.proffile for every request. This file can be used for further analysis usingsnakevizor other cProfile toolsTo 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=TrueSilk can also be used to profile specific blocks of code/functions. It provides a decorator and a context
manager for this purpose.For example:fromsilk.profiling.profilerimportsilk_profile@silk_profile(name='View Blog Post')defpost(request,post_id):p=Post.objects.get(pk=post_id)returnrender(request,'post.html',{'post':p})Whenever a blog post is viewed we get an entry within the Silk UI:Silk profiling not only provides execution time, but also collects SQL queries executed within the block in the same fashion as with requests:DecoratorThe silk decorator can be applied to both functions and methodsfromsilk.profiling.profilerimportsilk_profile# Profile a view function@silk_profile(name='View Blog Post')defpost(request,post_id):p=Post.objects.get(pk=post_id)returnrender(request,'post.html',{'post':p})# Profile a method in a view classclassMyView(View):@silk_profile(name='View Blog Post')defget(self,request):p=Post.objects.get(pk=post_id)returnrender(request,'post.html',{'post':p})Context ManagerUsing a context manager means we can add additional context to the name which can be useful for
narrowing down slowness to particular database records.defpost(request,post_id):withsilk_profile(name='View Blog Post #%d'%self.pk):p=Post.objects.get(pk=post_id)returnrender(request,'post.html',{'post':p})Dynamic ProfilingOne 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 tosettings.pyto apply a decorator at runtime:SILKY_DYNAMIC_PROFILING=[{'module':'path.to.module','function':'MyClass.bar'}]which is roughly equivalent to:classMyClass:@silk_profile()defbar(self):passThe below summarizes the possibilities:"""Dynamic function decorator"""SILKY_DYNAMIC_PROFILING=[{'module':'path.to.module','function':'foo'}]# ... is roughly equivalent to@silk_profile()deffoo():pass"""Dynamic method decorator"""SILKY_DYNAMIC_PROFILING=[{'module':'path.to.module','function':'MyClass.bar'}]# ... is roughly equivalent toclassMyClass:@silk_profile()defbar(self):pass"""Dynamic code block profiling"""SILKY_DYNAMIC_PROFILING=[{'module':'path.to.module','function':'foo',# Line numbers are relative to the function as opposed to the file in which it resides'start_line':1,'end_line':2,'name':'Slow Foo'}]# ... is roughly equivalent todeffoo():withsilk_profile(name='Slow Foo'):print(1)print(2)print(3)print(4)Note that dynamic profiling behaves in a similar fashion to that of the python mock framework in that
we modify the function in-place e.g:""" my.module """fromanother.moduleimportfoo# ...do some stufffoo()# ...do some other stuff,we would profilefooby dynamically decoratingmy.module.fooas opposed toanother.module.foo:SILKY_DYNAMIC_PROFILING=[{'module':'my.module','function':'foo'}]If we were to apply the dynamic profile to the functions source moduleanother.module.fooafterit has already been imported, no profiling would be triggered.Custom Logic for ProfilingSometimes you may want to dynamically control when the profiler runs. You can write your own logic for when to enable the profiler. To do this add the following to yoursettings.py:This setting is mutually exclusive with SILKY_PYTHON_PROFILER and will be used over it if present. It will work with SILKY_DYNAMIC_PROFILING.defmy_custom_logic(request):return'profile_requests'inrequest.sessionSILKY_PYTHON_PROFILER_FUNC=my_custom_logic# profile only session has recording enabled.You can also use alambda.# profile only session has recording enabled.SILKY_PYTHON_PROFILER_FUNC=lambdarequest:'profile_requests'inrequest.sessionCode GenerationSilk currently generates two bits of code per request:Both are intended for use in replaying the request. The curl command can be used to replay via command-line and the python code can be used within a Django unit test or simply as a standalone script.ConfigurationAuthentication/AuthorisationBy default anybody can access the Silk user interface by heading to/silk/. To enable your Django
auth backend place the following insettings.py:SILKY_AUTHENTICATION=True# User must loginSILKY_AUTHORISATION=True# User must have permissionsIfSILKY_AUTHORISATIONisTrue, by default Silk will only authorise users withis_staffattribute set toTrue.You can customise this using the following insettings.py:defmy_custom_perms(user):returnuser.is_allowed_to_use_silkSILKY_PERMISSIONS=my_custom_permsYou can also use alambda.SILKY_PERMISSIONS=lambdauser:user.is_superuserRequest/Response bodiesBy default, Silk will save down the request and response bodies for each request for future viewing
no matter how large. If Silk is used in production under heavy volume with large bodies this can have
a huge impact on space/time performance. This behaviour can be configured with the following options:SILKY_MAX_REQUEST_BODY_SIZE=-1# Silk takes anything <0 as no limitSILKY_MAX_RESPONSE_BODY_SIZE=1024# If response body>1024 bytes, ignoreMeta-ProfilingSometimes it is useful to be able to see what effect Silk is having on the request/response time. To do this add
the following to yoursettings.py:SILKY_META=TrueSilk will then record how long it takes to save everything down to the database at the end of each
request:Note that in the above screenshot, this means that the request took 29ms (22ms from Django and 7ms from Silk)Recording a Fraction of RequestsOn high-load sites it may be helpful to only record a fraction of the requests that are made. To do this add the following to yoursettings.py:Note: This setting is mutually exclusive with SILKY_INTERCEPT_FUNC.SILKY_INTERCEPT_PERCENT=50# log only 50% of requestsCustom Logic for Recording RequestsOn high-load sites it may also be helpful to write your own logic for when to intercept requests. To do this add the following to yoursettings.py:Note: This setting is mutually exclusive with SILKY_INTERCEPT_PERCENT.defmy_custom_logic(request):return'record_requests'inrequest.sessionSILKY_INTERCEPT_FUNC=my_custom_logic# log only session has recording enabled.You can also use alambda.# log only session has recording enabled.SILKY_INTERCEPT_FUNC=lambdarequest:'record_requests'inrequest.sessionLimiting request/response dataTo make sure silky garbage collects old request/response data, a config var can be set to limit the number of request/response rows it stores.SILKY_MAX_RECORDED_REQUESTS=10**4The garbage collection is only run on a percentage of requests to reduce overhead.  It can be adjusted with this config:SILKY_MAX_RECORDED_REQUESTS_CHECK_PERCENT=10In case you want decouple silk's garbage collection from your webserver's request processing, set SILKY_MAX_RECORDED_REQUESTS_CHECK_PERCENT=0 and trigger it manually, e.g. in a cron job:pythonmanage.pysilk_request_garbage_collectEnable query analysisTo enable query analysis when supported by the dbms a config var can be set in order to execute queries with the analyze features.SILKY_ANALYZE_QUERIES=TrueWarning:This setting may cause the database to execute the same query twice, depending on the backend. For instance,EXPLAIN ANALYZEin Postgres willactually execute the query, which may result in unexpected data updates. Set this to True with caution.To pass additional params for profiling when supported by the dbms (e.g. VERBOSE, FORMAT JSON), you can do this in the following manner.SILKY_EXPLAIN_FLAGS={'format':'JSON','costs':True}Masking sensitive data on request bodyBy default, Silk is filtering values that contains the following keys (they are case insensitive)SILKY_SENSITIVE_KEYS={'username','api','token','key','secret','password','signature'}But sometimes, you might want to have your own sensitive keywords, then above configuration can be modifiedSILKY_SENSITIVE_KEYS={'custom-password'}Clearing logged dataA management command will wipe out all logged data:pythonmanage.pysilk_clear_request_logContributingThis is aJazzbandproject. By contributing you agree to abide by theContributor Code of Conductand follow theguidelines.Development EnvironmentSilk features a project namedprojectthat can be used forsilkdevelopment. It has thesilkcode symlinked so
you can work on the sampleprojectand on thesilkpackage at the same time.In order to setup local development you should first install all the dependencies for the testproject. From the
root of theprojectdirectory:pipinstall-rrequirements.txtYou will also need to installsilk's dependencies. From the root of the git repository:pipinstall-e.At this point your virtual environment should have everything it needs to run both the sampleprojectandsilksuccessfully.Before running, you must set theDB_ENGINEandDB_NAMEenvironment variables:exportDB_ENGINE=sqlite3exportDB_NAME=db.sqlite3For other combinations, checktox.ini.Now from the root of the sampleprojectapply the migrationspythonmanage.pymigrateNow from the root of the sampleprojectdirectory start the django serverpythonmanage.pyrunserverRunning the testscdproject
pythonmanage.pytestHappy profiling!