Giter Club home page Giter Club logo

django-silk's Introduction

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:

Contents

Requirements

Silk has been tested with:

  • Django: 3.2, 4.2, 5.0
  • Python: 3.8, 3.9, 3.10, 3.11, 3.12

Installation

Via pip into a virtualenv:

pip install django-silk

In settings.py add the following:

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

INSTALLED_APPS = (
    ...
    'silk'
)

Note: The middleware placement is sensitive. If the middleware before silk.middleware.SilkyMiddleware returns from process_request then SilkyMiddleware will never get the chance to execute. Therefore you must ensure that any middleware placed before never returns anything from process_request. See the django docs for more information on this.

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 https://github.com/jazzband/silk/archive/<version>.tar.gz

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:

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:

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

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

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:

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:

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

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'
}]

which is roughly equivalent to:

class MyClass:
    @silk_profile()
    def bar(self):
        pass

The below summarizes the possibilities:

"""
Dynamic function decorator
"""

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

# ... is roughly equivalent to
@silk_profile()
def foo():
    pass

"""
Dynamic method decorator
"""

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

# ... is roughly equivalent to
class MyClass:

    @silk_profile()
    def bar(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 to
def foo():
    with silk_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 """
from another.module import foo

# ...do some stuff
foo()
# ...do some other stuff

,we would profile foo by dynamically decorating my.module.foo as opposed to another.module.foo:

SILKY_DYNAMIC_PROFILING = [{
    'module': 'my.module',
    'function': 'foo'
}]

If we were to apply the dynamic profile to the functions source module another.module.foo after it has already been imported, no profiling would be triggered.

Custom Logic for Profiling

Sometimes 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 your settings.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.

def my_custom_logic(request):
    return 'profile_requests' in request.session

SILKY_PYTHON_PROFILER_FUNC = my_custom_logic # profile only session has recording enabled.

You can also use a lambda.

# profile only session has recording enabled.
SILKY_PYTHON_PROFILER_FUNC = lambda request: 'profile_requests' in request.session

Code Generation

Silk 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.

Configuration

Authentication/Authorisation

By default anybody can access the Silk user interface by heading to /silk/. To enable your Django auth backend place the following in settings.py:

SILKY_AUTHENTICATION = True  # User must login
SILKY_AUTHORISATION = True  # User must have permissions

If SILKY_AUTHORISATION is True, by default Silk will only authorise users with is_staff attribute set to True.

You can customise this using the following in settings.py:

def my_custom_perms(user):
    return user.is_allowed_to_use_silk

SILKY_PERMISSIONS = my_custom_perms

You can also use a lambda.

SILKY_PERMISSIONS = lambda user: user.is_superuser

Request/Response bodies

By 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 limit
SILKY_MAX_RESPONSE_BODY_SIZE = 1024  # If response body>1024 bytes, ignore

Meta-Profiling

Sometimes 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 your settings.py:

SILKY_META = True

Silk 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 Requests

On 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 your settings.py:

Note: This setting is mutually exclusive with SILKY_INTERCEPT_FUNC.

SILKY_INTERCEPT_PERCENT = 50 # log only 50% of requests

Custom Logic for Recording Requests

On 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 your settings.py:

Note: This setting is mutually exclusive with SILKY_INTERCEPT_PERCENT.

def my_custom_logic(request):
    return 'record_requests' in request.session

SILKY_INTERCEPT_FUNC = my_custom_logic # log only session has recording enabled.

You can also use a lambda.

# log only session has recording enabled.
SILKY_INTERCEPT_FUNC = lambda request: 'record_requests' in request.session

Limiting request/response data

To 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**4

The 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 = 10

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

python manage.py silk_request_garbage_collect

Enable query analysis

To 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 = True

Warning: This setting may cause the database to execute the same query twice, depending on the backend. For instance, EXPLAIN ANALYZE in Postgres will actually 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 body

By 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 modified

SILKY_SENSITIVE_KEYS = {'custom-password'}

Clearing logged data

A management command will wipe out all logged data:

python manage.py silk_clear_request_log

Contributing

Jazzband

This is a Jazzband project. By contributing you agree to abide by the Contributor Code of Conduct and follow the guidelines.

Development Environment

Silk features a project named project that can be used for silk development. It has the silk code symlinked so you can work on the sample project and on the silk package at the same time.

In order to setup local development you should first install all the dependencies for the test project. From the root of the project directory:

pip install -r requirements.txt

You will also need to install silk's dependencies. From the root of the git repository:

pip install -e .

At this point your virtual environment should have everything it needs to run both the sample project and silk successfully.

Before running, you must set the DB_ENGINE and DB_NAME environment variables:

export DB_ENGINE=sqlite3
export DB_NAME=db.sqlite3

For other combinations, check tox.ini.

Now from the root of the sample project apply the migrations

python manage.py migrate

Now from the root of the sample project directory start the django server

python manage.py runserver

Running the tests

cd project
python manage.py test

Happy profiling!

django-silk's People

Contributors

albertyw avatar alkalit avatar archmonger avatar auvipy avatar avelis avatar brmc avatar chris7 avatar chrono avatar daadu avatar danielbradburn avatar egichuri avatar erwinjunge avatar florisdenhengst avatar glujan avatar hanleyhansen avatar hramezani avatar jezdez avatar joaofrancese avatar jorl17 avatar joshdata avatar karabijavad avatar mbeacom avatar mtford90 avatar nasirhjafri avatar pre-commit-ci[bot] avatar regzon avatar sebcorbin avatar trik avatar ulgens avatar wrhector avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

django-silk's Issues

Enable/disable profiler on-the-fly (without stopping Django)

It would be useful to have a mechanism to start / stop profiler when it is at production (IMO).
I am not sure if it is possible or not, but a page for authenticated users that disables profiling or may be updating SILKY_INTERCEPT_PERCENT to 100 may be suitable for the task.

Enhance filtering

At the moment filtering is rather basic. If Silk is used for long enough it eventually becomes impossible to narrow down the requests that you want to look at, especially if is running in a busy QA/Production environment. I'm thinking of adding some way in which to "build up" filters as opposed to just having static filters like the current version. The most important will be a way in which to narrow down requests by path/view and time.

Problems with `six.moves.urllib`

On a fresh new python 2.7 virtualenv I installed silk and I got this:

Traceback (most recent call last):
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/wsgiref/handlers.py", line 85, in run
    self.result = application(self.environ, self.start_response)
  File "/Users/peterbe/dev/MOZILLA/AIRMOZILLA/airmozilla/vendor/lib/python/django/contrib/staticfiles/handlers.py", line 72, in __call__
    return self.application(environ, start_response)
  File "/Users/peterbe/dev/MOZILLA/AIRMOZILLA/airmozilla/vendor/lib/python/django/core/handlers/wsgi.py", line 255, in __call__
    response = self.get_response(request)
  File "/Users/peterbe/dev/MOZILLA/AIRMOZILLA/airmozilla/vendor/lib/python/django/core/handlers/base.py", line 176, in get_response
    response = self.handle_uncaught_exception(request, resolver, sys.exc_info())
  File "/Users/peterbe/dev/MOZILLA/AIRMOZILLA/airmozilla/vendor/lib/python/django/core/handlers/base.py", line 90, in get_response
    response = middleware_method(request)
  File "/Users/peterbe/virtualenvs/airmozilla2.7/lib/python2.7/site-packages/silk/middleware.py", line 70, in process_request
    if _should_intercept(request):
  File "/Users/peterbe/virtualenvs/airmozilla2.7/lib/python2.7/site-packages/silk/middleware.py", line 34, in _should_intercept
    fpath = silky_reverse('summary')
  File "/Users/peterbe/virtualenvs/airmozilla2.7/lib/python2.7/site-packages/silk/middleware.py", line 23, in silky_reverse
    r = reverse('silk:%s' % name, *args, **kwargs)
  File "/Users/peterbe/dev/MOZILLA/AIRMOZILLA/airmozilla/vendor/lib/python/django/core/urlresolvers.py", line 491, in reverse
    app_list = resolver.app_dict[ns]
  File "/Users/peterbe/dev/MOZILLA/AIRMOZILLA/airmozilla/vendor/lib/python/django/core/urlresolvers.py", line 330, in app_dict
    self._populate()
  File "/Users/peterbe/dev/MOZILLA/AIRMOZILLA/airmozilla/vendor/lib/python/django/core/urlresolvers.py", line 268, in _populate
    for pattern in reversed(self.url_patterns):
  File "/Users/peterbe/dev/MOZILLA/AIRMOZILLA/airmozilla/vendor/lib/python/django/core/urlresolvers.py", line 366, in url_patterns
    patterns = getattr(self.urlconf_module, "urlpatterns", self.urlconf_module)
  File "/Users/peterbe/dev/MOZILLA/AIRMOZILLA/airmozilla/vendor/lib/python/django/core/urlresolvers.py", line 361, in urlconf_module
    self._urlconf_module = import_module(self.urlconf_name)
  File "/Users/peterbe/dev/MOZILLA/AIRMOZILLA/airmozilla/vendor/lib/python/django/utils/importlib.py", line 35, in import_module
    __import__(name)
  File "/Users/peterbe/dev/MOZILLA/AIRMOZILLA/airmozilla/airmozilla/urls.py", line 38, in <module>
    urlpatterns += patterns('', url(r'^silk', include('silk.urls', namespace='silk')))
  File "/Users/peterbe/dev/MOZILLA/AIRMOZILLA/airmozilla/vendor/lib/python/django/conf/urls/__init__.py", line 25, in include
    urlconf_module = import_module(urlconf_module)
  File "/Users/peterbe/dev/MOZILLA/AIRMOZILLA/airmozilla/vendor/lib/python/django/utils/importlib.py", line 35, in import_module
    __import__(name)
  File "/Users/peterbe/virtualenvs/airmozilla2.7/lib/python2.7/site-packages/silk/urls.py", line 3, in <module>
    from silk.views.profile_detail import ProfilingDetailView
  File "/Users/peterbe/virtualenvs/airmozilla2.7/lib/python2.7/site-packages/silk/views/profile_detail.py", line 4, in <module>
    from silk.auth import login_possibly_required, permissions_possibly_required
  File "/Users/peterbe/virtualenvs/airmozilla2.7/lib/python2.7/site-packages/silk/auth.py", line 12, in <module>
    from six.moves.urllib.parse import urlparse
ImportError: No module named urllib.parse

I tried pip install -U six which then installed six==1.7.2 but that didn't help :(

Pip install direct from repo fails

> pip install -e git+https://github.com/mtford90/silk.git#egg=silk

Obtaining silk from git+https://github.com/mtford90/silk.git#egg=silk
  Cloning https://github.com/mtford90/silk.git to /Users/me/.virtualenvs/proj/src/silk
  Running setup.py (path:/Users/me/.virtualenvs/proj/src/silk/setup.py) egg_info for package silk
    Traceback (most recent call last):
      File "<string>", line 17, in <module>
    IOError: [Errno 2] No such file or directory: '/Users/me/.virtualenvs/proj/src/silk/setup.py'
    Complete output from command python setup.py egg_info:
    Traceback (most recent call last):

  File "<string>", line 17, in <module>

IOError: [Errno 2] No such file or directory: '/Users/me/.virtualenvs/proj/src/silk/setup.py'

Post-processing static assets fails due to missing font files

The file silk/static/silk/lib/bootstrap.min.css references font files that have a relative path that exists outside of the repo. For example:

@font-face{font-family:'Glyphicons Halflings';src:url('../../../../../../../Downloads/bootstrap-datetimepicker-2.3.1/sample%20in%20bootstrap%20v3/bootstrap/fonts/glyphicons-halflings-regular.eot'); ...}

This causes post-processing to fail since django can't find the file for cache busting (if you're using CachedStaticFilesStorage):

Post-processing 'silk/lib/bootstrap.min.css' failed!

Traceback (most recent call last):
  File "./manage.py", line 20, in <module>
    execute_from_command_line(sys.argv)
  File "/usr/local/lib/python2.7/dist-packages/django/core/management/__init__.py", line 385, in execute_from_command_line
    utility.execute()
  File "/usr/local/lib/python2.7/dist-packages/django/core/management/__init__.py", line 377, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "/usr/local/lib/python2.7/dist-packages/django/core/management/base.py", line 288, in run_from_argv
    self.execute(*args, **options.__dict__)
  File "/usr/local/lib/python2.7/dist-packages/django/core/management/base.py", line 338, in execute
    output = self.handle(*args, **options)
  File "/usr/local/lib/python2.7/dist-packages/django/core/management/base.py", line 533, in handle
    return self.handle_noargs(**options)
  File "/usr/local/lib/python2.7/dist-packages/django/contrib/staticfiles/management/commands/collectstatic.py", line 168, in handle_noargs
    collected = self.collect()
  File "/usr/local/lib/python2.7/dist-packages/django/contrib/staticfiles/management/commands/collectstatic.py", line 120, in collect
    raise processed
ValueError: The file 'Downloads/bootstrap-datetimepicker-2.3.1/sample in bootstrap v3/bootstrap/fonts/glyphicons-halflings-regular.eot' could not be found with <pipeline.storage.PipelineCachedStorage object at 0x3191c10>.

Silk's static assets are served from the wrong path

This could just be my setup, but Silk's template uses file paths like:

<link rel="stylesheet" href="/static/silk/css/fonts.css"/>

On my machine, this translates to a request for http://127.0.0.1:8000/static/silk/css/fonts.css which doesn't load. I would've expected Silk to use my existing STATIC_URL as a prefix for these files.

(I realise Silk is probably meant for production not local usage, but I'm curious about what it does before I deploy it anywhere)

Silk fails on binary staticfiles content

When I'm in production mode I'll let Nginx go straight to the filesystem on these things but when in debug mode I add this to my root urls.py:

if settings.DEBUG:
    urlpatterns += patterns('', url(r'^silk', include('silk.urls', namespace='silk')))
    urlpatterns += static(
        settings.MEDIA_URL,
        document_root=settings.MEDIA_ROOT
    )

Any request on something going through /cache/foo/bar.jpg will cause this traceback.

Traceback (most recent call last):
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/wsgiref/handlers.py", line 85, in run
    self.result = application(self.environ, self.start_response)
  File "/Users/peterbe/virtualenvs/dailycookie/lib/python2.7/site-packages/django/contrib/staticfiles/handlers.py", line 67, in __call__
    return self.application(environ, start_response)
  File "/Users/peterbe/virtualenvs/dailycookie/lib/python2.7/site-packages/django/core/handlers/wsgi.py", line 206, in __call__
    response = self.get_response(request)
  File "/Users/peterbe/virtualenvs/dailycookie/lib/python2.7/site-packages/django/core/handlers/base.py", line 205, in get_response
    response = self.handle_uncaught_exception(request, resolver, sys.exc_info())
  File "/Users/peterbe/virtualenvs/dailycookie/lib/python2.7/site-packages/django/core/handlers/base.py", line 201, in get_response
    response = middleware_method(request, response)
  File "/Users/peterbe/virtualenvs/dailycookie/lib/python2.7/site-packages/silk/middleware.py", line 183, in process_response
    silky_response.save()
  File "/Users/peterbe/virtualenvs/dailycookie/lib/python2.7/site-packages/django/db/models/base.py", line 545, in save
    force_update=force_update, update_fields=update_fields)
  File "/Users/peterbe/virtualenvs/dailycookie/lib/python2.7/site-packages/django/db/models/base.py", line 573, in save_base
    updated = self._save_table(raw, cls, force_insert, force_update, using, update_fields)
  File "/Users/peterbe/virtualenvs/dailycookie/lib/python2.7/site-packages/django/db/models/base.py", line 635, in _save_table
    forced_update)
  File "/Users/peterbe/virtualenvs/dailycookie/lib/python2.7/site-packages/django/db/models/base.py", line 679, in _do_update
    return filtered._update(values) > 0
  File "/Users/peterbe/virtualenvs/dailycookie/lib/python2.7/site-packages/django/db/models/query.py", line 507, in _update
    return query.get_compiler(self.db).execute_sql(None)
  File "/Users/peterbe/virtualenvs/dailycookie/lib/python2.7/site-packages/django/db/models/sql/compiler.py", line 975, in execute_sql
    cursor = super(SQLUpdateCompiler, self).execute_sql(result_type)
  File "/Users/peterbe/virtualenvs/dailycookie/lib/python2.7/site-packages/silk/sql.py", line 49, in execute_sql
    return self._execute_sql(*args, **kwargs)
  File "/Users/peterbe/virtualenvs/dailycookie/lib/python2.7/site-packages/django/db/models/sql/compiler.py", line 781, in execute_sql
    cursor.execute(sql, params)
  File "/Users/peterbe/virtualenvs/dailycookie/lib/python2.7/site-packages/django/db/backends/util.py", line 73, in execute
    sql = self.db.ops.last_executed_query(self.cursor, sql, params)
  File "/Users/peterbe/virtualenvs/dailycookie/lib/python2.7/site-packages/django/db/backends/postgresql_psycopg2/operations.py", line 214, in last_executed_query
    return cursor.query.decode('utf-8')
  File "/Users/peterbe/virtualenvs/dailycookie/lib/python2.7/encodings/utf_8.py", line 16, in decode
    return codecs.utf_8_decode(input, errors, True)
UnicodeDecodeError: 'utf8' codec can't decode byte 0xff in position 82: invalid start byte

Incorrect string value: '\xCE\xBB, \xCF\x86...' for column 'raw_body' at row 1

Happens when I visit any page after installing silk.

Environment:


Request Method: GET
Request URL: http://localhost:8000/

Django Version: 1.6.5
Python Version: 2.7.6
Installed Applications:
('django.contrib.sessions',
 'django.contrib.messages',
 'django.contrib.staticfiles',
 'django.contrib.humanize',
 'django.contrib.sitemaps',
 'observatory',
 'blog',
 'silk')
Installed Middleware:
('silk.middleware.SilkyMiddleware',
 'django.middleware.common.CommonMiddleware',
 'django.contrib.sessions.middleware.SessionMiddleware',
 'django.middleware.csrf.CsrfViewMiddleware',
 'django.contrib.auth.middleware.AuthenticationMiddleware',
 'django.contrib.messages.middleware.MessageMiddleware',
 'django.middleware.locale.LocaleMiddleware')


Traceback:
File "/srv/atlas/env/local/lib/python2.7/site-packages/django/core/handlers/base.py" in get_response
  199.                 response = middleware_method(request, response)
File "/srv/atlas/env/local/lib/python2.7/site-packages/silk/middleware.py" in process_response
  96.             self._process_response(response)
File "/srv/atlas/env/local/lib/python2.7/site-packages/silk/middleware.py" in _process_response
  86.                 silk_response.save()
File "/srv/atlas/env/local/lib/python2.7/site-packages/django/db/models/base.py" in save
  545.                        force_update=force_update, update_fields=update_fields)
File "/srv/atlas/env/local/lib/python2.7/site-packages/django/db/models/base.py" in save_base
  573.             updated = self._save_table(raw, cls, force_insert, force_update, using, update_fields)
File "/srv/atlas/env/local/lib/python2.7/site-packages/django/db/models/base.py" in _save_table
  635.                                       forced_update)
File "/srv/atlas/env/local/lib/python2.7/site-packages/django/db/models/base.py" in _do_update
  679.         return filtered._update(values) > 0
File "/srv/atlas/env/local/lib/python2.7/site-packages/django/db/models/query.py" in _update
  510.         return query.get_compiler(self.db).execute_sql(None)
File "/srv/atlas/env/local/lib/python2.7/site-packages/django/db/models/sql/compiler.py" in execute_sql
  980.         cursor = super(SQLUpdateCompiler, self).execute_sql(result_type)
File "/srv/atlas/env/local/lib/python2.7/site-packages/silk/sql.py" in execute_sql
  42.             return self._execute_sql(*args, **kwargs)
File "/srv/atlas/env/local/lib/python2.7/site-packages/django/db/models/sql/compiler.py" in execute_sql
  786.         cursor.execute(sql, params)
File "/srv/atlas/env/local/lib/python2.7/site-packages/django/db/backends/util.py" in execute
  69.             return super(CursorDebugWrapper, self).execute(sql, params)
File "/srv/atlas/env/local/lib/python2.7/site-packages/django/db/backends/util.py" in execute
  53.                 return self.cursor.execute(sql, params)
File "/srv/atlas/env/local/lib/python2.7/site-packages/django/db/backends/mysql/base.py" in execute
  124.             return self.cursor.execute(query, args)
File "/srv/atlas/env/local/lib/python2.7/site-packages/MySQLdb/cursors.py" in execute
  207.         if not self._defer_warnings: self._warning_check()
File "/srv/atlas/env/local/lib/python2.7/site-packages/MySQLdb/cursors.py" in _warning_check
  117.                     warn(w[-1], self.Warning, 3)

Exception Type: Warning at /
Exception Value: Incorrect string value: '\xCE\xBB, \xCF\x86...' for column 'raw_body' at row 1

raw_body seems to contain the page content, the generated sql looks like this:

u'UPDATE `silk_response` SET `request_id` = 4, `status_code` = 200, `raw_body` = <!doctype html>\n<html lang="en">\n<head>\n  <meta charset="utf-8">\n  <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">\n\n  <base href="/">\n\n  <title>The Atlas of Economic Complexity | Home</title>\n  <meta name="description" content="Networks in Macro Economics with World Trade Data">\n  <link rel="search" type="application/opensearchdescription+xml" href="/opensearch.xml" title="Atlas of Economic Complexity" />\n\n  <link rel="shortcut icon" type="image/x-icon" href="media/img/favicon.ico">\n\n  <!-- Load non-standard fonts -->\n  <link href=\'http://fonts.googleapis.com/css?family=Open+Sans+Condensed:300\' rel=\'stylesheet\' type=\'text/css\'>\n  <link href=\'http://fonts.googleapis.com/css?family=PT+Sans+Narrow\' rel=\'stylesheet\' type=\'text/css\' />\n  <link href=\'http://fonts.googleapis.com/css?family=Cardo\' rel=\'stylesheet\' type=\'text/css\' />\n  <link rel="stylesheet" href="media/css/normalize.css">\n  <link rel="stylesheet" href="media/css/style.css">\t\n  <link rel="stylesheet" href="media/js/libs/chosen/chosen.css" />\n\n  <!--[if lt IE 9]>\n    <script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>\n  <![endif]-->\n\n   \n\n<style>\n\n.carousel {\n  position: relative;\n}\n\n.carousel-inner {\n  position: relative;\n  width: 100%;\n  overflow: hidden;\n}\n\n.carousel-inner > .item {\n  position: relative;\n  display: none;\n  -webkit-transition: 0.6s ease-in-out left;\n          transition: 0.6s ease-in-out left;\n}\n\n.carousel-inner > .item > img,\n.carousel-inner > .item > a > img {\n  display: block;\n  height: auto;\n  max-width: 100%;\n  line-height: 1;\n}\n\n.carousel-inner > .active,\n.carousel-inner > .next,\n.carousel-inner > .prev {\n  display: block;\n}\n\n.carousel-inner > .active {\n  left: 0;\n}\n\n.carousel-inner > .next,\n.carousel-inner > .prev {\n  position: absolute;\n  top: 0;\n  width: 100%;\n}\n\n.carousel-inner > .next {\n  left: 100%;\n}\n\n.carousel-inner > .prev {\n  left: -100%;\n}\n\n.carousel-inner > .next.left,\n.carousel-inner > .prev.right {\n  left: 0;\n}\n\n.carousel-inner > .active.left {\n  left: -100%;\n}\n\n.carousel-inner > .active.right {\n  left: 100%;\n}\n\n.carousel-control {\n  position: absolute;\n  top: 0;\n  bottom: 0;\n  left: 0;\n  width: 15%;\n  font-size: 50px;\n  color: #ffffff;\n  text-align: center;\n/*  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6);\n  opacity: 0.5;\n  filter: alpha(opacity=50);\n  */\n}\n\n.carousel-control.left {\n  /*background-color:black;*/\n  opacity: 0;\n  background-repeat: repeat-x;\n  /*\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=\'#80000000\', endColorstr=\'#00000000\', GradientType=1);\n  */\n}\n\n.carousel-control.right {\n  right: 0;\n  left: auto;\n  /*background-color:black;*/\n  opacity: 0;\n  background-repeat: repeat-x;\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=\'#00000000\', endColorstr=\'#80000000\', GradientType=1);\n}\n\n\n.carousel-control:hover,\n.carousel-control:focus {\n  color: black;\n  text-decoration: none;\n  opacity: 0.9;\n\n  margin-top:-100px;\n  /*\n  filter: alpha(opacity=90);\n   */\n}\n\n.carousel-control .icon-prev,\n.carousel-control .icon-next,\n.carousel-control .glyphicon-chevron-left,\n.carousel-control .glyphicon-chevron-right {\n  position: absolute;\n  top: 50%;\n  left: 50%;\n  z-index: 5;\n  display: inline-block;\n}\n\n.carousel-control .icon-prev,\n.carousel-control .icon-next {\n  width: 20px;\n  height: 20px;\n  margin-top: -10px;\n  margin-left: -10px;\n  font-family: serif;\n}\n\n.carousel-control .icon-prev:before {\n  content: \'\\2039\';\n  font-size: 100px;\n  color:gray;\n}\n\n.carousel-control .icon-next:before {\n  content: \'\\203a\';\n  font-size: 100px;\n  color:gray;\n}\n\n.carousel-indicators {\n  position: absolute;\n  bottom: 10px;\n  left: 50%;\n  z-index: 15;\n  width: 60%;\n  padding-left: 0;\n  margin-left: -30%;\n  text-align: center;\n  list-style: none;\n}\n\n.carousel-indicators li {\n  display: inline-block;\n  width: 10px;\n  height: 10px;\n  margin: 1px;\n  text-indent: -999px;\n  cursor: pointer;\n  border: 1px solid #ffffff;\n  border-radius: 10px;\n}\n\n.carousel-indicators .active {\n  width: 12px;\n  height: 12px;\n  margin: 0;\n  background-color: lightgray;\n}\n\n.carousel-caption {\n  position: absolute;\n  bottom: 0px;\n  z-index: 10;\n  padding-top: 0px;\n  padding-bottom: 0px;\n  color: #ffffff;\n  text-align: center;\n /* text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6);*/\n  padding-left:0px;\n  margin:0px; background-color: white; opacity:.8; width:300px; left:0px; \n}\n\n.carousel-caption .btn {\n  text-shadow: none;\n}\n\n@media screen and (min-width: 768px) {\n  .carousel-control .icon-prev,\n  .carousel-control .icon-next {\n    width: 30px;\n    height: 30px;\n    margin-top: -15px;\n    margin-left: -15px;\n    font-size: 30px;\n  }\n  .carousel-caption {\n    right: 0%;\n    left: 0%;\n    width: 100%;\n   \n  }\n  .carousel-indicators {\n    bottom: 20px;\n  }\n}\n\n.products-tmb, a.products-tmb {\n  float:left;\n  width: 50px;\n  height: 50px;\n  margin: 5px;\n  width: 30px; \n  height:30px; \n}\n\n.products-tmb:hover {\n  border: solid 2px #ccc;\n\n}\n\n#main #content .didyouknow_container{\n\theight: 415px;\n}\n.app_title {\n  display: none;\n}\n#main {\n  border: none;\n}\n\n#main #content {\n  border-radius: 10px;\n  margin: 10px 0 0 0;\n  width: 998px;\n}\n\n#main #content .didyouknow_container{\n  height: 450px;\n}\n\n#main #content .didyouknow{\n  display: none;\n}\n\n#main #content .headline {\n  background: rgba(255, 255, 255, 0.7);\n  margin: 0 0 10px 0;\n  overflow: hidden;\n  width: 100%;\n}\n#main #content .headline img.pin{\n  float: left;\n  margin: 0 25px 0 0;\n  width: 80px;\n}\n#main #content .headline h2{\n  font-size: 45px;\n  font-weight: 100;\n  float: left;\n  margin: 0;\n  width: 400px;\n  text-transform: uppercase;\n}\n\n#main #content img.pin{\n  float: left;\n  margin: 0 10px 0 0;\n  width: 80px;\n}\n#main #content h2{\n  font-size: 50px;\n  font-weight: 100;\n  float: left;\n  margin: 0;\n  width: 260px;\n  text-transform: uppercase;\n}\n\n#main #content .text{\n  float: left;\n  margin: 0 30px 0 0;\n  width: 365px;\n}\n#main #content .text h3{\n  line-height: 32px;\n  margin: 20px 0 10px 0;\n  font-size: 32px;\n  font-weight: 100;\n}\n#main #content .text p{\n  font-size: 22px;\n}\n#main #content .text ul{\n  font-size: 18px;\n  list-style: circle;\n  line-height: 26px;\n}\n#main #content .app {\n  margin: 10px 0 0 0;\n}\n#main #content .app h3{\n  font-size: 28px;\n  font-weight: normal;\n  margin: 0 0 0 0;\n  text-align: center;\n}\n#main #content .other_apps h3{\n  font-size: 28px;\n  font-weight: normal;\n  margin: 0px 0 10px 0;\n}\n#main #content .other_apps div{\n  float: left;\n  margin: 0 27px 15px 0;\n}\n#main #content .other_apps div a {\n  display: inline-block;\n  border: solid 2px white;\n  padding: 10px;\n}\n#main #content .other_apps div a:hover{\n  border: solid 2px #000;\n}\n#main #content .other_apps div a.credits:hover{\n  border: solid 2px white;\n}\n#main #content .other_apps div img{\n  width: 150px;\n}\n#main #content .other_apps div:last-child{\n  margin: 0;\n}\n\n/* New stuff */\n.other_apps:hover {\n   border: solid 2px gray;\n}\n\n.other_apps_first:hover {\n  background-color:white ;\n   border: solid 2px black;\n\n}\n\n\n.other_apps {\n\n  border: 1px solid white;\n}\n\n.earth_teaser {\n  background:url(\'media/img/home/teaser_earth_fade.png\') no-repeat;\n  background-size:420px 300px;\n  margin-left:9px; \n  border:2px solid white; \n  margin-top:10px;\n  width:420px;\n  height:300px;\n}      \n\n\n.earth_teaser:hover {\n  background:url(\'media/img/home/teaser_earth.png\') no-repeat;\n  background-size:420px 300px;\n  margin-left:9px; \n  border:2px solid white; \n  margin-top:10px;\n  width:420px;\n  height:300px;\n}      \n\n\n.products_teaser:hover {\n  background:url(\'media/img/home/teaser_products.png\') no-repeat;\n  background-size:420px 300px;\n  margin-left:9px; \n  border:2px solid white; \n  margin-top:10px;\n  width:420px;\n  height:300px;\n}     \n\n.products_teaser {\n  background:url(\'media/img/home/teaser_products_fade.png\') no-repeat;\n  background-size:420px 300px;\n  margin-left:9px; \n  border:2px solid white; \n  margin-top:10px;\n  width:420px;\n  height:300px;\n}      \n\nfooter.credits, footer.logos {\n  margin-left:0px;\n}\n\n</style>\n\n<!-- Hidden Modal Window for Video -->\n\n  <div id="videoModal">\n    <a href="#" id="exit">Close</a>\n    <div id="videoModalInner">\n        <iframe width="853" height="480" src="//www.youtube.com/embed/0JC24CBVsdo" frameborder="0" allowfullscreen></iframe>\n    </div>\n  </div>\n  \n</head>\n<body>\n  <div id="container">\n    <header>\n      <div class="logo" style="background-color: #fafafa; width:205px; height:66px;">\n        <a href="" title="Home" style="text-decoration: none;">\n        <h1 style="background: url(media/img/all/logo_rainbow.png) repeat-x 100% 100%; padding-bottom:22px; color: black; font-size:53px;">The Atlas</h1>\n        <h2 style="font-size:18px; margin-top:-33px; text-align:center; margin-left:0px; letter-spacing:1px; color:white; text-transform:uppercase; text-decoration:none; background-color: #a0a6a9;">of Economic Complexity</h2></a> \n      </div>  \n      <nav>\n        <ul>\n          <li><a href="explore/">Explore</a></li>\n          <li><a href="stories">Stories</a></li>\n          <li><a href="rankings">Rankings</a></li>\n          <li><a id="newBook" href="book">New Book</a></li>\n          <li><a href="about">About</a></li>\n        </ul>\n      </nav> \n      <br class="clear">\n    </header>\n\n    <div id="main" role="main" style="margin-top:70px">\n      <div class="app_title" id="title" ><h2 style="margin-left:300px; width:800px" id="text_title"></h2></div>\n\n    <style>\n      #content  h3, #content h2, h4 {\n        text-transform: uppercase;\n      }\n\n      #content h3:not(:first-child), #content h4:not(:first-child) {\n        margin: 50px 0px 0px 0px;\n      }\n    </style>\n      <!-- main content -->\n      \n  <div id="content">\n\n  <div id="carousel-example-generic" class="carousel slide" style="width:535px; height:300px;float:left; margin-left:20px; margin-top:20px; margin-right:15px; border:solid 1px #dddddd;">\n  <!-- Indicators -->\n  <!--\n  <ol class="carousel-indicators">\n    <li data-target="#carousel-example-generic" data-slide-to="0" class="active"></li>\n    <li data-target="#carousel-example-generic" data-slide-to="1"></li>\n    <li data-target="#carousel-example-generic" data-slide-to="2"></li>\n  </ol>\n  ->>\n  <!-- Wrapper for slides -->\n  <div class="carousel-inner">\n    <div class="item active">\n      <a href="explore/product_space/export/usa/all/show/2012/">\n        <img src="media/img/home/us_imports_ps.png" alt="..." style="width:550px; height:300px">\n        <div class="carousel-caption">\n          <h3 style="font-size:22px; color:gray;">What did the United States import in 2011?</h3>\n          <p></p>\n        </div>\n      </a>\n    </div>\n    <div class="item">\n      <a href="explore/stacked/export/usa/all/show/1996.2011.1/">\n        <img src="media/img/home/us_exports.png" alt="..." style="width:540px; height:300px">\n        <div class="carousel-caption">\n          <h3 style="font-size:22px; color:gray;">What did the United States export between 1996 and 2011?</h3>\n          <p></p>\n        </div>\n      </a>\n    </div>\n    <!--\n    <div class="item">\n      <a href="explore/map/export/show/all/0101/2011/">\n        <img src="media/img/home/horses_export.png" alt="..." style="width:550px; height:300px">\n        <div class="carousel-caption">\n          <h3 style="font-size:22px; color:gray;">Who exported Horses in 2011?</h3>\n          <p></p>\n        </div>\n      </a>\n    </div>\n    -->\n  </div>\n\n  <!-- Controls -->\n  <a class="left carousel-control" href="#carousel-example-generic" data-slide="prev">\n    <span class="icon-prev"></span>\n  </a>\n  <a class="right carousel-control" href="#carousel-example-generic" data-slide="next">\n    <span class="icon-next"></span>\n  </a>\n</div>\n\n    <div style="float:left; width:350px; margin-bottom:30px; font-size: 30px; font-style:italic; background: url(media/img/home/quote.jpg) no-repeat; background-size:100px; padding-left:40px; padding-top:40px; color:gray">\n      Explore Growth Opportunities\n    </div>\n\n  <a id="videoLaunch" href="#">\n\n    <div class="other_apps_first other_apps" style="float:left;width:370px; height:200px; padding:5px; margin-left:20px;border:1px solid lightgray; background-color:#fafafa;">\n     \n      <div style="width:210px; height:160px; text-align:justify;">\n        <h3 style="text-transform: none; font-size:30px">What is  <span style="text-transform: uppercase;">THE ATLAS?</span></h3> \n        <p style="text-transform: none; font-size:16px">This interactive tool can generate more than 20 million visualizations and map the path of diversification and prosperity for 128 countries. Learn more about the theory of Economic Complexity.</p>\n      </div>\n      <div style="background:url(\'media/img/home/teaser_video_obs.png\') no-repeat; background-size:130px 130px; width:130px; height:160px; margin-top:10px"></div>   \n      \n    </div>\n  \n  </a>\n\n  <p style="clear:both"></p>\n\n    <div class="other_apps" style="float:left; width:455px; margin:20px; border: solid 1px lightgray;">\n  \n  <a href="explore/tree_map/export/usa/all/show/2010/">    <h3 style="float:left; margin-left:50px;font-size:30px; text-transform: none;">Browse Countries \xbb</h3>\n\n      <!--<div class="earth_teaser" style="margin-top:0px"></div>-->\n      <div id="map" style="width:450px; height:250px"></div>\n      \n   \n  </a>\n   <div style="text-align:right; float:right; color:lightgray;" >Map transition by <a href="http://www.jasondavies.com/maps/transition/" style="text-decoration:none;color:lightgray; padding:0px; padding-top:10px" class="credits">Jason Davies</a></div>\n   </div> \n \n<style>\n\n\n\n\n</style>\n\n    <div class="other_apps" style="float:left; width:455px; margin:20px; border: solid 1px lightgray;">\n      <a href="explore/tree_map/export/show/all/8703/2010/"><h3 style="float:left; margin-left:50px;font-size:30px; text-transform: none;">Browse Products \xbb</h3></a><p style="clear:both">\n      <div style="padding-left:20px; padding-bottom:30px">\n        <a href="explore/tree_map/export/show/all/0106/2010/" class="products-tmb" style="background: url(\'media/img/icons/community_0.png\') no-repeat; background-size:50px;"></a>\n        <a href="explore/tree_map/export/show/all/8703/2010/" class="products-tmb" style="background: url(\'media/img/icons/community_10.png\') no-repeat; background-size:50px;"></a>\n        <a href="explore/tree_map/export/show/all/8509/2010/" class="products-tmb" style="background: url(\'media/img/icons/community_11.png\') no-repeat; background-size:50px;"></a>\n        <a href="explore/tree_map/export/show/all/8802/2010/" class="products-tmb" style="background: url(\'media/img/icons/community_12.png\') no-repeat; background-size:50px;"></a>\n        <a href="explore/tree_map/export/show/all/8703/2010/" class="products-tmb" style="background: url(\'media/img/icons/community_13.png\') no-repeat; background-size:50px;"></a>\n        <a href="explore/tree_map/export/show/all/8703/2010/" class="products-tmb" style="background: url(\'media/img/icons/community_14.png\') no-repeat; background-size:50px;"></a>\n        <a href="explore/tree_map/export/show/all/8703/2010/" class="products-tmb" style="background: url(\'media/img/icons/community_15.png\') no-repeat; background-size:50px;"></a>\n        <a href="explore/tree_map/export/show/all/8703/2010/" class="products-tmb" style="background: url(\'media/img/icons/community_20.png\') no-repeat; background-size:50px;"></a>\n        <a href="explore/tree_map/export/show/all/8703/2010/" class="products-tmb" style="background: url(\'media/img/icons/community_21.png\') no-repeat; background-size:50px;"></a>\n        <a href="explore/tree_map/export/show/all/8703/2010/" class="products-tmb" style="background: url(\'media/img/icons/community_22.png\') no-repeat; background-size:50px;"></a>\n        <a href="explore/tree_map/export/show/all/8703/2010/" class="products-tmb" style="background: url(\'media/img/icons/community_30.png\') no-repeat; background-size:50px;"></a>\n        <a href="explore/tree_map/export/show/all/8703/2010/" class="products-tmb" style="background: url(\'media/img/icons/community_31.png\') no-repeat; background-size:50px;"></a>\n        <a href="explore/tree_map/export/show/all/8703/2010/" class="products-tmb" style="background: url(\'media/img/icons/community_40.png\') no-repeat; background-size:50px;"></a>\n        <a href="explore/tree_map/export/show/all/8703/2010/" class="products-tmb" style="background: url(\'media/img/icons/community_41.png\') no-repeat; background-size:50px;"></a>\n        <a href="explore/tree_map/export/show/all/4704/2010/" class="products-tmb" style="background: url(\'media/img/icons/community_42.png\') no-repeat; background-size:50px;"></a>\n        <a href="explore/tree_map/export/show/all/1121/2010/" class="products-tmb" style="background: url(\'media/img/icons/community_43.png\') no-repeat; background-size:50px;"></a>\n        <a href="explore/tree_map/export/show/all/5417/2010/" class="products-tmb" style="background: url(\'media/img/icons/community_44.png\') no-repeat; background-size:50px;"></a>\n        <a href="explore/tree_map/export/show/all/8703/2010/" class="products-tmb" style="background: url(\'media/img/icons/community_50.png\') no-repeat; background-size:50px;"></a>\n        <a href="explore/tree_map/export/show/all/8703/2010/" class="products-tmb" style="background: url(\'media/img/icons/community_51.png\') no-repeat; background-size:50px;"></a>\n        <a href="explore/tree_map/export/show/all/2709/2010/" class="products-tmb" style="background: url(\'media/img/icons/community_52.png\') no-repeat; background-size:50px;"></a>\n        <a href="explore/tree_map/export/show/all/8703/2010/" class="products-tmb" style="background: url(\'media/img/icons/community_54.png\') no-repeat; background-size:50px;"></a>\n        <a href="explore/tree_map/export/show/all/8703/2010/" class="products-tmb" style="background: url(\'media/img/icons/community_60.png\') no-repeat; background-size:50px;"></a>\n        <a href="explore/tree_map/export/show/all/8703/2010/" class="products-tmb" style="background: url(\'media/img/icons/community_61.png\') no-repeat; background-size:50px;"></a>\n        <a href="explore/tree_map/export/show/all/1001/2010/" class="products-tmb" style="background: url(\'media/img/icons/community_70.png\') no-repeat; background-size:50px;"></a>\n        \n      </div>\n    </div> \n\n\n\n\n    </div> <!-- END #main -->     \n\n  </div> <!-- END #container -->\n\n  <script src="media/js/libs/jquery/jquery-1.10.2.min.js"></script>\t\n\t<script src="media/js/libs/chosen/chosen.jquery.min.js" ></script>\n  <script src="media/js/libs/bootstrap/js/bootstrap.js"></script>\n\n  <script>\n\t\n  \t// language select dropdown\n  \t$(".language_select select").chosen();\n  \t$(".language_select .chzn-search").hide();\n  \t$(".language_select select").chosen().change(function(){\n  \t\t// set session data to new language\n  \t\tvar language = $(this).val();\n  \t\twindow.location = "/set_language/"+language+"/";\n  \t});\n  \t\n    // google analytics\n  \n    (function(i,s,o,g,r,a,m){i[\'GoogleAnalyticsObject\']=r;i[r]=i[r]||function(){\n    (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),\n    m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)\n    })(window,document,\'script\',\'//www.google-analytics.com/analytics.js\',\'ga\');\n\n    ga(\'create\', \'UA-41291966-1\', \'harvard.edu\');\n    ga(\'send\', \'pageview\');\n\n    // current nav styling\n  \t$(function(){\n  \t\tvar current_page = document.location.pathname.split("/")[1];\n  \t\t$("nav ul a").each(function(i, el){\n  \t\t\tif($(el).text().toLowerCase() == current_page){\n  \t\t\t\t$(el).addClass("current");\n  \t\t\t}\n  \t\t})\n  \t})\n\t</script>\n\n<style>\n#sticky {\n  padding: 0.5ex;\n  width: 600px;\n  border-radius: 0.5ex;\n}\n#sticky.stick {\n  position: fixed;\n  top: 0;\n  z-index: 10000;\n  border-radius: 0 0 0.5em 0.5em;\n}\n</style>\n\n<script>\nfunction sticky_relocate() {\n  var window_top = $(window).scrollTop();\n  var div_top = $(\'#sticky-anchor\').offset().top;\n  if (window_top > div_top) {\n    $(\'#sticky\').addClass(\'stick\');\n  } else {\n    $(\'#sticky\').removeClass(\'stick\');\n  }\n}\n\n$(function() {\n  $(window).scroll(sticky_relocate);\n  sticky_relocate();\n});\n</script>\n\n\t\n<script src="media/js/libs/jquery/jquery-1.10.2.js"></script>\n<script type="text/javascript">\n    $(document).ready(function(){\n      $("#videoLaunch").click(function(){\n        $("#videoModal").css("display","block");\n        $("#videoModal").animate({opacity:1.0},400);        \n      });\n      $("#exit, #videoModal").click(function(){\n        $("#videoModal").animate({opacity:0.0},200,function(){\n          $("#videoModal").css("display", "none");\n        });\n      });\n    });\n</script>\n<script>\n  // Use to be: Rotate featured video randomly\n  // -> Will be re-used for showing stories randomly\n  // var vid = Math.round(Math.random());\n // var vid = 0;\n //  $(".didyouknow").eq(vid).show();\n  // Set explore URL to be the default country aka where the \n  // user\'s IP address says they are\n // $("nav a").eq(0).attr("href", "explore/tree_map/export/usa/all/show/2012/")\n</script>\n\n\n<script src="media/js/libs/d3/d3.v3.min.js"></script>\n<script src="media/js/libs/d3/d3.geo.projection.min.js"></script>\n<script src="media/js/libs/d3/topojson.min.js"></script>\n<script src="media/js/libs/bootstrap/js/bootstrap.js"></script>\n\n<script>\n$(function() {\n\n  $(\'#myModal\').modal({\n    show: true,\n    keyboard: false\n    });\n\n  $("#carousel-example-generic").carousel({ interval: 2000 });\n\n});\n</script>\n\n<script>\n\nvar ratio = window.devicePixelRatio || 1,\n    width = 450 * ratio,\n    height = 250 * ratio;\n\nvar options = [\n  {name: "Van der Grinten IV", projection: d3.geo.vanDerGrinten4().scale(120)}\n];\n\nvar initial = [-71, 42, 0],\n    rotate = initial.slice(),\n    pause = 0,\n    velocity = [.010, -.002],\n    t0 = Date.now();\n\nvar \n    i = 0,\n    n = options.length - 1;\n\nvar projection = d3.geo.vanDerGrinten4().scale(127); //options[i].projection;\n\nvar graticule = d3.geo.graticule()(),\n    land,\n    boundaries;\n\nvar canvas = d3.select("#map").append("canvas")\n    .attr("width", width)\n    .attr("height", height)\n    .style("width", width / ratio + "px")\n    .style("height", height / ratio + "px")\n    .call(d3.behavior.drag()\n      .origin(function() { return {x: rotate[0], y: -rotate[1]}; })\n      .on("dragstart", function() { pause |= 1; })\n      .on("drag", function(d) {\n        rotate[0] = d3.event.x;\n        rotate[1] = -d3.event.y;\n        projection.rotate(rotate);\n        redraw(path);\n      })\n      .on("dragend", function() {\n        pause &= ~1;\n        t0 = Date.now();\n        initial = rotate.slice();\n      }));\n\nvar context = canvas.node().getContext("2d");\ncontext.fillStyle = "#f9f9f9";\ncontext.strokeStyle = "#000";\n\nvar path = d3.geo.path()\n    .projection(projection)\n    .context(context);\n\nvar menu = d3.select("#projection-menu")\n    .on("change", change);\n\nmenu.selectAll("option")\n    .data(options)\n  .enter().append("option")\n    .text(function(d) { return d.name; });\n\n\nfunction change() {\n  clearInterval(interval);\n  update(options[this.selectedIndex]);\n}\n\nfunction pathTween(projection0, projection1, rotate) {\n  projection0.rotate([0, 0, 0]);\n  projection1.rotate([0, 0, 0]);\n  var t = 0,\n      projection = d3.geo.projection(function(\u03bb, \u03c6) {\n          \u03bb *= 180 / Math.PI, \u03c6 *= 180 / Math.PI;\n          var p0 = projection0([\u03bb, \u03c6]), p1 = projection1([\u03bb, \u03c6]);\n          return [(1 - t) * p0[0] + t * p1[0], (1 - t) * -p0[1] + t * -p1[1]];\n        })\n        .rotate(rotate)\n        .scale(1)\n        .translate([width / 2, height / 2])\n        .clipExtent(projection0.clipExtent()),\n      path = d3.geo.path().projection(projection).context(context);\n  return function() {\n    return function(u) {\n      t = u;\n      redraw(path);\n    };\n  };\n}\n\nfunction update(option) {\n  pause |= 2;\n  canvas.transition()\n      .duration(750)\n      .tween("path", pathTween(projection, projection = option.projection, initial = rotate.slice()))\n      .each("end", function() {\n        pause &= ~2;\n        t0 = Date.now();\n      });\n  path.projection(projection);\n}\n\nd3.timer(function() {\n  if (pause) return;\n  var t = Date.now() - t0;\n  rotate[0] = initial[0] + velocity[0] * t;\n  rotate[1] = initial[1] + velocity[1] * t;\n  projection.rotate(rotate);\n  redraw(path);\n});\n\nfunction redraw(path) {\n  context.clearRect(0, 0, width, height);\n  context.lineWidth = .5 * ratio;\n  if (land) {\n    context.strokeStyle = "#000";\n    context.beginPath(), path(land), context.fill(), context.stroke();\n    context.beginPath(), path(boundaries), context.stroke();\n  }\n  context.strokeStyle = "#999";\n  context.beginPath(), path(graticule), context.stroke();\n  context.lineWidth = 2.5 * ratio, context.strokeStyle = "#000";\n  context.beginPath(), path({type: "Sphere"}), context.stroke();\n}\n\nd3.json("media/js/libs/d3/world-110m.json", function(error, world) {\n  land = topojson.feature(world, world.objects.land);\n  boundaries = topojson.mesh(world, world.objects.countries, function(a, b) { return a !== b; });\n});\n\n</script>\n\n\n</body>\n</html>\n, `body` = , `encoded_headers` = {"Content-Type": "text/html; charset=utf-8", "Vary": "Accept-Language, Cookie", "Content-Language": "en"} WHERE `silk_response`.`id` = 4 '

Migration error

I'm running Django 1.6.10 and silk 0.4. Here is the trace:

Running migrations for silk:
 - Migrating forwards to 0002_auto__add_field_request_pyprofile__add_index_request_start_time.
 > silk:0001_initial
 ! Error found during real run of migration! Aborting.

 ! Since you have a database that does not support running
 ! schema-altering statements in transactions, we have had 
 ! to leave it in an interim state between migrations.

! You *might* be able to recover with:   = DROP TABLE `silk_request` CASCADE; []
   = DROP TABLE `silk_response` CASCADE; []
   = DROP TABLE `silk_sqlquery` CASCADE; []
   = DROP TABLE `silk_profile` CASCADE; []
   = DROP TABLE `silk_profile_queries` CASCADE; []

 ! The South developers regret this has happened, and would
 ! like to gently persuade you to consider a slightly
 ! easier-to-deal-with DBMS (one that supports DDL transactions)
 ! NOTE: The error which caused the migration to fail is further up.
Error in migration: silk:0001_initial
Traceback (most recent call last):
  File "manage.py", line 10, in <module>
    execute_from_command_line(sys.argv)
  File "/Users/ian/.virtualenv/flashnotes/lib/python2.7/site-packages/django/core/management/__init__.py", line 399, in execute_from_command_line
    utility.execute()
  File "/Users/ian/.virtualenv/flashnotes/lib/python2.7/site-packages/django/core/management/__init__.py", line 392, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "/Users/ian/.virtualenv/flashnotes/lib/python2.7/site-packages/django/core/management/base.py", line 242, in run_from_argv
    self.execute(*args, **options.__dict__)
  File "/Users/ian/.virtualenv/flashnotes/lib/python2.7/site-packages/django/core/management/base.py", line 285, in execute
    output = self.handle(*args, **options)
  File "/Users/ian/.virtualenv/flashnotes/lib/python2.7/site-packages/south/management/commands/migrate.py", line 111, in handle
    ignore_ghosts = ignore_ghosts,
  File "/Users/ian/.virtualenv/flashnotes/lib/python2.7/site-packages/south/migration/__init__.py", line 220, in migrate_app
    success = migrator.migrate_many(target, workplan, database)
  File "/Users/ian/.virtualenv/flashnotes/lib/python2.7/site-packages/south/migration/migrators.py", line 254, in migrate_many
    result = migrator.__class__.migrate_many(migrator, target, migrations, database)
  File "/Users/ian/.virtualenv/flashnotes/lib/python2.7/site-packages/south/migration/migrators.py", line 329, in migrate_many
    result = self.migrate(migration, database)
  File "/Users/ian/.virtualenv/flashnotes/lib/python2.7/site-packages/south/migration/migrators.py", line 133, in migrate
    result = self.run(migration, database)
  File "/Users/ian/.virtualenv/flashnotes/lib/python2.7/site-packages/south/migration/migrators.py", line 114, in run
    return self.run_migration(migration, database)
  File "/Users/ian/.virtualenv/flashnotes/lib/python2.7/site-packages/south/migration/migrators.py", line 85, in run_migration
    south.db.db.execute_deferred_sql()
  File "/Users/ian/.virtualenv/flashnotes/lib/python2.7/site-packages/south/db/generic.py", line 318, in execute_deferred_sql
    self.execute(sql)
  File "/Users/ian/.virtualenv/flashnotes/lib/python2.7/site-packages/south/db/generic.py", line 282, in execute
    cursor.execute(sql, params)
  File "/Users/ian/.virtualenv/flashnotes/lib/python2.7/site-packages/django/db/backends/util.py", line 69, in execute
    return super(CursorDebugWrapper, self).execute(sql, params)
  File "/Users/ian/.virtualenv/flashnotes/lib/python2.7/site-packages/django/db/backends/util.py", line 53, in execute
    return self.cursor.execute(sql, params)
  File "/Users/ian/.virtualenv/flashnotes/lib/python2.7/site-packages/django/db/backends/mysql/base.py", line 124, in execute
    return self.cursor.execute(query, args)
  File "/Users/ian/.virtualenv/flashnotes/lib/python2.7/site-packages/MySQLdb/cursors.py", line 176, in execute
    if not self._defer_warnings: self._warning_check()
  File "/Users/ian/.virtualenv/flashnotes/lib/python2.7/site-packages/MySQLdb/cursors.py", line 92, in _warning_check
    warn(w[-1], self.Warning, 3)
_mysql_exceptions.Warning: Specified key was too long; max key length is 767 bytes

Support binary response bodies

Currently binary response bodies such as images are ignored and cannot be displayed in the silk UI. Can probably take advantage of BinaryField to support this

Middleware problem?? No request model was available when processing response. Did something go wrong in process_request/process_view?

I would like to get your suggestions on the Silk.

I'm deploying the django-silk 0.5 into existing django (version 1.6.9) framework environment for checking web application performance.
So I added a null logger for silk in the settings.py of django framework. Before it I used to get No handlers could be found for logger "silk" error. After adding it, no more such error. But still not sure it is correct solutions. Plase suggest on this

I have two ways to launch my web app: using run server manage.py and using nginx (Our production is using uwsgi and nginx).
When I use the first method, I can successfully launch my web application.

But I am facing error "The requested URL The requested URL /..../.../accounts/login/ was not found on this server" when I use nginx which is supported to be used.
I still cannot figure out the meaning of error in my uwsgi log "No request model was available when processing response. Did something go wrong in process_request/process_view?".

Also Suspect the middleware is not supporting correctly.

Still cannot find out any solutions.
Please suggest.

'thread._local' object has no attribute 'temp_identifier' (should log a warning stating that this is likely a middleware issue)

I've just installed silk and configured it as it says on your tutorial. However, as soon as I enable the middleware, my site breaks with this error inside the get_identifier method of DataCollector class. Why would this happen? As soon as I disable silk middleware, the site works again. Also, I had to add a logger handler for silk before that since it was throwing an error because of that too. This tool looks promising, I hope you can fix this asap.

Tests broken

Since the way in which request ids are constructed changed, tests are now broken

AttributeError: 'thread._local' object has no attribute 'temp_identifier'

Hi there

With both 0.1.1 and git HEAD, i'm getting the error pasted below.

Django 1.6.5 on OSX Python 2.7.5

Doesn't make a difference whether I'm using runserver or django-devserver for my local server.

Cheers
Steve

AttributeError: 'thread._local' object has no attribute 'temp_identifier'
Traceback (most recent call last):
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/wsgiref/handlers.py", line 85, in run
    self.result = application(self.environ, self.start_response)
  File "/Volumes/HDD/Users/steve/.virtualenvs/yj/lib/python2.7/site-packages/django/contrib/staticfiles/handlers.py", line 67, in __call__
    return self.application(environ, start_response)
  File "/Volumes/HDD/Users/steve/.virtualenvs/yj/lib/python2.7/site-packages/django/core/handlers/wsgi.py", line 206, in __call__
    response = self.get_response(request)
  File "/Volumes/HDD/Users/steve/.virtualenvs/yj/lib/python2.7/site-packages/django/core/handlers/base.py", line 194, in get_response
    response = self.handle_uncaught_exception(request, resolver, sys.exc_info())
  File "/Volumes/HDD/Users/steve/.virtualenvs/yj/lib/python2.7/site-packages/django/core/handlers/base.py", line 224, in handle_uncaught_exception
    'request': request
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/logging/__init__.py", line 1175, in error
    self._log(ERROR, msg, args, **kwargs)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/logging/__init__.py", line 1268, in _log
    self.handle(record)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/logging/__init__.py", line 1278, in handle
    self.callHandlers(record)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/logging/__init__.py", line 1318, in callHandlers
    hdlr.handle(record)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/logging/__init__.py", line 749, in handle
    self.emit(record)
  File "/Volumes/HDD/Users/steve/.virtualenvs/yj/lib/python2.7/site-packages/errordite/handlers.py", line 90, in emit
    payload = self.enrich_errordite_payload(payload, record)
  File "/Volumes/HDD/Users/steve/.virtualenvs/yj/lib/python2.7/site-packages/django_errordite/handlers.py", line 43, in enrich_errordite_payload
    if rq.user.is_anonymous():
  File "/Volumes/HDD/Users/steve/.virtualenvs/yj/lib/python2.7/site-packages/django/utils/functional.py", line 213, in inner
    self._setup()
  File "/Volumes/HDD/Users/steve/.virtualenvs/yj/lib/python2.7/site-packages/django/utils/functional.py", line 298, in _setup
    self._wrapped = self._setupfunc()
  File "/Volumes/HDD/Users/steve/.virtualenvs/yj/lib/python2.7/site-packages/django/contrib/auth/middleware.py", line 18, in <lambda>
    request.user = SimpleLazyObject(lambda: get_user(request))
  File "/Volumes/HDD/Users/steve/.virtualenvs/yj/lib/python2.7/site-packages/django/contrib/auth/middleware.py", line 10, in get_user
    request._cached_user = auth.get_user(request)
  File "/Volumes/HDD/Users/steve/.virtualenvs/yj/lib/python2.7/site-packages/django/contrib/auth/__init__.py", line 140, in get_user
    user_id = request.session[SESSION_KEY]
  File "/Volumes/HDD/Users/steve/.virtualenvs/yj/lib/python2.7/site-packages/django/contrib/sessions/backends/base.py", line 47, in __getitem__
    return self._session[key]
  File "/Volumes/HDD/Users/steve/.virtualenvs/yj/lib/python2.7/site-packages/django/contrib/sessions/backends/base.py", line 173, in _get_session
    self._session_cache = self.load()
  File "/Volumes/HDD/Users/steve/.virtualenvs/yj/lib/python2.7/site-packages/django/contrib/sessions/backends/db.py", line 20, in load
    expire_date__gt=timezone.now()
  File "/Volumes/HDD/Users/steve/.virtualenvs/yj/lib/python2.7/site-packages/django/db/models/manager.py", line 151, in get
    return self.get_queryset().get(*args, **kwargs)
  File "/Volumes/HDD/Users/steve/.virtualenvs/yj/lib/python2.7/site-packages/django/db/models/query.py", line 304, in get
    num = len(clone)
  File "/Volumes/HDD/Users/steve/.virtualenvs/yj/lib/python2.7/site-packages/django/db/models/query.py", line 77, in __len__
    self._fetch_all()
  File "/Volumes/HDD/Users/steve/.virtualenvs/yj/lib/python2.7/site-packages/django/db/models/query.py", line 857, in _fetch_all
    self._result_cache = list(self.iterator())
  File "/Volumes/HDD/Users/steve/.virtualenvs/yj/lib/python2.7/site-packages/django/db/models/query.py", line 220, in iterator
    for row in compiler.results_iter():
  File "/Volumes/HDD/Users/steve/.virtualenvs/yj/lib/python2.7/site-packages/django/db/models/sql/compiler.py", line 713, in results_iter
    for rows in self.execute_sql(MULTI):
  File "/Volumes/HDD/Users/steve/.virtualenvs/yj/src/django-silk/silk/sql.py", line 48, in execute_sql
    DataCollector().register_query(query_dict)
  File "/Volumes/HDD/Users/steve/.virtualenvs/yj/src/django-silk/silk/collector.py", line 72, in register_query
    ident = self.get_identifier()
  File "/Volumes/HDD/Users/steve/.virtualenvs/yj/src/django-silk/silk/collector.py", line 26, in get_identifier
    self.local.temp_identifier += 1
AttributeError: 'thread._local' object has no attribute 'temp_identifier'

static files not found problem

I use uwsgi to run my django server.
In order to open silk admin panel properly the static files of silk must be copied to static folder, or i fixed this issue like that.
Is there simething I miss? or do we always need to copy the files

Check to see if process_request of SilkyMiddleware has been called, and issue warnings on middleware placement if not

When I use django.middleware.locale.LocaleMiddleware together with silk, I'm able to trigger the following error by going to /admin/ and /example_app/ (you need to go to both of them to trigger the error):

AttributeError: 'thread._local' object has no attribute 'temp_identifier'

Trace and settings:
https://gist.github.com/grumpi/f0867ac818b6c193fa62

Modified example app to trigger this behavior:
https://github.com/grumpi/silk/tree/localemiddleware-bug

I tested this on Django 1.7.1 and Django 1.6.5. It looks like, somehow self.local.temp_identifier isn't set up properly. Might or might not be related to the duplicate key issue.

Conform to charset flag in Content-Type header of request/response

At the moment we attempt UTF-8 decoding on responses and no decoding at all on request bodies. We should be checking for the charset flag within the Content-Type header e.g. Content-Type: text/html; charset=utf-8 and then apply that decoding. Not sure what to default to if that isn't present though, probably just attempt both ascii and UTF-8?

This is what is causing issue #19

Aggregation

It would be really useful to be able to aggregate requests/profiles based on the selected filtering. Perhaps displayed in a graph as a time series. Perhaps these aggregations could be built up and compared or something like that, as it's quite hard to get the overall picture atm.

Collapse profiles with the same name, in the same request

e.g. if the same function/block is getting profiled again and again it's not very useful just to get a huge list of each individual hit. it would be nice to have a collapse feature that allows grouping of identical profiles. This is similar to the output of cProfile's percall column. The number of individual calls to a function are profiled and percall gives us the average per call.

Ability to clear old runs

It's now starting to get quite messy when I visit /silk/. I wish there was a button to clear it all out. Or a management command at least.

Weird "Silk middleware has not been installed correctly" error

Hello, I'm encountering a weird problem with Silk.

First off: the silk middleware is at the top of my middleware stack (I've tried other positions as well) so nothing should be able to stop it from running. What happens though is that the first request issued when the server has started passes through fine. The second request gets stuck in the Silk middleware.

In line 94 of middleware.py:process_request, it throws a RuntimeError exception at

    request_model = RequestModelFactory(request).construct_request_model()

I dug a little deeper and it appears as though what fails is

    request_model = models.Request.objects.create(
            path=path,
            encoded_headers=self.encoded_headers(),
            method=self.request.method,
            query_params=query_params,
            view_name=view_name,
            body=body)

at lines 159โ€“165 in model_factory.py:construct_request_model. I had this open in PDB, so I tried manually issuing the call to models.Request.objects.create, and got the same result. It appears as though it happens at some point when the Request model is save()d, but I haven't been able to figure out more accurately what happens there.

This is the error that is eventually thrown by save()

*** RuntimeError: Silk middleware has not been installed correctly. Ordering must ensure that Silk middleware can execute process_request and process_response. If an earlier middleware returns from either of these methods, Silk will not have the chance to inspect the request/response objects.

And it's worth noting that this does not happen the first request that is issued to the server, only all the ones after that!

I'm not sure how much of this is my fault, nor what I'm doing wrong if anything, but I figured it might be something you'd like to know about if it's an issue in Silk. This is with Django 1.5.9.

I'm don't know if this matters at all, but I noticed that configure() which creates the thread-local storage is called after the RequestModel is created. From what I can tell, the RuntimeError is issued when the thread-local storage is not yet created. On the other hand, configure must be called after the requestmodel is created because it takes it as a parameter.

Silky doesn't work when django.middleware.gzip.GZipMiddleware is enabled

When the 'django.middleware.gzip.GZipMiddleware' is enabled the following error happens:
File "...r/lib/python2.7/site-packages/silk/sql.py", line 39, in execute_sql
sql_query = q % params
UnicodeDecodeError: 'ascii' codec can't decode byte 0x8b in position 1: ordinal not in range(128)

Django 1.5 support broken

Use of atomic instead of commit_on_success appears to break django 1.5. Need to add some sort of shim

Switch to PyPI for managing releases

As your dependencies are being pulled from the Python Package Index, is there any reason why silk relies on Github tags for managing releases rather than PyPI?

Migrations do not work with Django 1.5.9

Hi, I'm using Django 1.5.9 and wanted to integrate silk since it should support Django 1.5 (see README).
Now, after I ran migrate I get this:

UnknownMigration: Migration 'silk:0001_initial' probably doesn't exist.
Traceback (most recent call last):
  File "/home/kai/IdeaProjects/core-tms/env/local/lib/python2.7/site-packages/south/migration/base.py", line 302, in migration
    migration = __import__(full_name, {}, {}, ['Migration'])
  File "/home/kai/IdeaProjects/core-tms/env/local/lib/python2.7/site-packages/silk/migrations/0001_initial.py", line 4, in <module>
    from django.db import models, migrations
ImportError: cannot import name migrations

The migrations module was integrated in Django 1.7 I think, so how do I use silk with Django 1.5?
Thanks!

No data profiled

I am not sure but for me the URL of 'summary' is /silk, so I had to change this code in middleware.py:

def _should_intercept(request):
    """we want to avoid recording any requests/sql queries etc that belong to Silky"""
    fpath = silky_reverse('summary')
    path = '/'.join(fpath.split('/')[0:-1])
    silky = request.path.startswith(path)
    ignored = request.path in SilkyConfig().SILKY_IGNORE_PATHS
    return not (silky or ignored)

To this code:

def _should_intercept(request):
    """we want to avoid recording any requests/sql queries etc that belong to Silky"""
    fpath = silky_reverse('summary')
    silky = request.path.startswith(fpath)
    ignored = request.path in SilkyConfig().SILKY_IGNORE_PATHS
    return not (silky or ignored)

request: timestamp on list of requests

on the main /silk page that shows a block for each request, it would be helpful to have the request timestamp so you can more easily identify groups of requests (i.e. load/ajax) and ensure you are looking at the right profile

UTC time in templates

Maybe should add "expects_localtime=True" to filter silk_date_time?

# templatetags/filters.py

@register.filter(expects_localtime=True)
def silk_date_time(dt):
    return _silk_date_time(dt)

Incorrect interface for execute_sql

Hello,

I had some problem related to the function interface of execute_sql in silk/sql.py.

Your interface is:

def execute_sql(self, *args, **kwargs)

With some code to retrieve the result_type keyword:

kwargs.get('result_type', 'multi')

However the interface in Django is different (https://github.com/django/django/blob/master/django/db/models/sql/compiler.py):

def execute_sql(self, result_type=MULTI)

In the case where we call without specifying the keyword:
execute_sql('single')

Your code doesn't give the expected results.

Deploying silk site-wide

I have two questions:

  1. Is it possible to use silk site-wide, ie without having to add a decorator before each view? If yes, how do we do it?
  2. Is it recommended to use silk in production? If any if you is already using it in production, would you care to tell me what's the performance overhead?

Thanks.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.