# Trace Django requests `KoloMiddleware` records one trace per Django request. It is one of several ways to capture a trace: to trace a management command, a test run or a script in a Django project, use [`KOLO=1` or `kolo run`](trace-function-or-script.md) instead. ## Set up the middleware 1. Install Kolo using `pip install kolo`. 2. Add `"kolo.middleware.KoloMiddleware"` to the top of your `MIDDLEWARE` list in `settings.py`: ```python # settings.py MIDDLEWARE = [ "kolo.middleware.KoloMiddleware", "django.middleware.security.SecurityMiddleware", ... ] ``` 3. Start your server with `python manage.py runserver`. Kolo confirms it is active: ```text View recent requests at http://127.0.0.1:8000/_kolo/ ``` 4. Make a request to any page. ## Read the trace Each request is saved as its own trace and emitted to `.kolo/traces/`. A request trace contains the request and response, the view and the functions in your project that it called, and the SQL queries and outbound HTTP requests made along the way. ```bash kolo trace list # one line per request: status, method, path, trace ID kolo cat # call tree of the most recent request grep -rn "auth_user" .kolo/traces/ # find a query, function or value across recent requests ``` Kolo also serves a web UI for browsing traces at `/_kolo/` on your development server, for example `http://localhost:8000/_kolo/`. ### Which SQL is recorded Kolo records the statements the Django ORM compiles and runs: queryset reads, `save()`, `create()`, `update()`, `delete()`, `bulk_create()` and `raw()`. A query that raises is recorded too. When Kolo traces with `sys.monitoring` (Python 3.12 and later) it is marked with the exception (`[raised IntegrityError]`). Some statements do not go through the ORM's `execute_sql` and are not recorded, so a trace can show fewer statements than `connection.queries` or `assertNumQueries` count: - On Django 6.0 and later, the `UPDATE ... RETURNING` that `save()` runs when a field is assigned an expression (`obj.count = F("count") + 1`). - Transaction control: `SAVEPOINT`, `RELEASE SAVEPOINT`, `BEGIN`, `COMMIT` and `ROLLBACK` (for example from `transaction.atomic()`). - SQL your code or a library runs directly on a cursor (`connection.cursor().execute(...)`, `executemany`, `callproc`), including migrations and schema changes. To see every statement, add Django's cursor wrapper to `include_frames`. Each `CursorWrapper.execute` call then appears as a function call with its `sql` and `params`: ```toml [filters] include_frames = ["django/db/backends/utils"] ``` ## When the middleware is active `KoloMiddleware` is for local development. By default it only records requests when Django's `DEBUG` setting is `True`, and it only ever serves the `/_kolo/` web UI when `DEBUG` is `True`. With `DEBUG = False`, requests to `/_kolo/` fall through to your own URL configuration. Setting the `KOLO_DISABLE` environment variable (to anything other than `0` or `false`) turns off both recording and the web UI. The middleware also stops recording while another profiler is active in the process. If you would rather not have the middleware installed outside development at all, add it conditionally: ```python # settings.py if DEBUG: MIDDLEWARE.insert(0, "kolo.middleware.KoloMiddleware") ``` To skip some URLs, set `ignore_request_paths` in the [filters](../reference/config.md#filters) config.