skip to content

What do Django's built-in django.request, django.db.backends, django.security and django.server loggers each record?

level: middleimportance: should knowfreq 44%

answer

  1. children of the django logger
  2. responses, queries, attacks, dev server
  3. 4XX warning, 5XX error
  4. SQL only when DEBUG is True

basics

~10 s

django.request logs 5XX responses as ERROR and 4XX as WARNING; django.db.backends logs each SQL statement at DEBUG, only when DEBUG is True; django.security.* logs security events per type; django.server logs runserver requests.

solid answer

~30 s

All four are children of the `django` logger. `django.request` records response problems: 5XX as `ERROR` with the exception, 4XX as `WARNING`, with `status_code` and `request` on the record, which is what the admin emails use. `django.db.backends` emits one `DEBUG` record per SQL statement with `sql`, `params`, `alias` and `duration`, but only when `DEBUG` is `True`, for performance. `django.security.<Name>` gets a sub-logger per security event type, such as `DisallowedHost` for bad `Host` headers, plus `django.security.csrf`; 400s from a `SuspiciousOperation` go there instead of `django.request`. `django.server` logs `runserver` request lines only and does not propagate by default.

code

python · 13 lines
python
LOGGING = {
    "version": 1,
    "disable_existing_loggers": False,
    "handlers": {
        "null": {"class": "logging.NullHandler"},
    },
    "loggers": {
        "django.security.DisallowedHost": {
            "handlers": ["null"],
            "propagate": False,
        },
    },
}

go deeper

for a junior

Recall that django.request logs failing responses and django.db.backends logs SQL, both as children of the django logger.

for a middle

Explain the levels and extra attributes of each logger, the DEBUG condition on SQL logging, and why security 400s skip django.request.

for a senior

Route categories precisely: errors to alerting, security noise silenced per sub-logger, SQL only where DEBUG or test tooling enables it.

for a principal

Define which Django log categories feed alerting, which feed analysis, and which are deliberately discarded, and review that list as the service grows.

## The django hierarchy Django never logs to the `django` logger itself. It logs to named children, and the `django` logger is the parent where `DEFAULT_LOGGING` attaches its handlers. Knowing which child carries what lets you route, silence or raise the level of one category without touching the rest. ## The loggers interviewers ask about | Logger | What it records | Levels | Extra attributes on the record | |---|---|---|---| | `django.request` | responses from request handling | 5XX as `ERROR`, 4XX as `WARNING` | `status_code`, `request` | | `django.server` | requests served by `runserver` only | 5XX `ERROR`, 4XX `WARNING`, else `INFO` | `status_code`, `request` (a socket) | | `django.db.backends` | every application-level SQL statement | `DEBUG` | `duration`, `sql`, `params`, `alias` | | `django.db.backends.schema` | SQL run by migrations' schema changes | `DEBUG` | `sql`, `params` | | `django.security.<Name>` | security events, one sub-logger per type | mostly `WARNING`; `ERROR` when a `SuspiciousOperation` reaches the handler | varies | | `django.template` | missing context variables | `DEBUG` | none listed | ## django.request - An unhandled exception in a view produces a 500 and an `ERROR` record carrying the exception, which is what `AdminEmailHandler` turns into an email. - 404s and other 4XX responses are `WARNING` records, dropped by the defaults when `DEBUG` is `False`. - Requests rejected for a `SuspiciousOperation` with a 400 are **not** logged here; they go to `django.security` instead. ## django.db.backends - One `DEBUG` record per query, with the SQL, its parameters, the database alias and the **duration** in seconds. - For performance, Django only wraps cursors for logging when `settings.DEBUG` is `True`, whatever levels and handlers you configure. Test tooling can force it for a run, for example `manage.py test --debug-sql`. - Framework-level setup statements are not included. - The `django.db.backends.schema` child is **not** gated by `DEBUG`: migrations log their DDL at `DEBUG` level in every environment, which reaches any handler you attach to `django.db.backends`. SQL run inside `RunPython` is not logged there. ## django.security - Each `SuspiciousOperation` subclass gets its own logger, such as `django.security.DisallowedHost` for a `Host` header not in `ALLOWED_HOSTS`, and `django.security.SuspiciousSession`. - CSRF rejections are logged to `django.security.csrf`, which is not based on `SuspiciousOperation`. - These records propagate to `django`, so a `DisallowedHost` flood from scanners becomes a flood of admin emails. The documented fix is a `NullHandler` on `django.security.DisallowedHost` with `propagate: False`. ## django.server - Emits one line per request under `runserver`, formatted by Django's `ServerFormatter` with a `[{server_time}]` prefix and colour by status. - Has its own handler and `propagate: False` in the defaults. - Production WSGI and ASGI servers do not use it; their access logs are their own. ## Others worth recognising - `django.utils.autoreload`: file changes detected by the development server. - `django.contrib.auth`: `ERROR` when a password-reset email fails to send. - `django.dispatch`: errors raised by signal receivers when dispatched with robust sending. - `django.contrib.sessions`: non-fatal errors from the cached-database session engine. ## Using the extra attributes Because these loggers attach structured attributes to each record, a formatter or filter can use them directly: - `%(status_code)s` in a formatter for `django.request` records; - `record.duration` in a filter for slow queries on `django.db.backends`; - `record.request.path` in a custom filter that ignores a noisy endpoint. Two cautions apply: 1. A formatter that names an attribute only some records carry fails for the others, so give such formatters only to handlers that receive that one logger's records. 2. `request` and `params` can contain personal data; decide deliberately where records carrying them are shipped. ## Using this in LOGGING ```python "loggers": { "django.request": {"handlers": ["errors_file"], "level": "ERROR"}, "django.security.DisallowedHost": {"handlers": ["null"], "propagate": False}, "django.db.backends": {"handlers": ["sql_file"], "level": "DEBUG", "propagate": False}, }, ``` Each entry targets one category precisely, which is far more useful than raising the whole `django` logger to `DEBUG` and wading through everything.

  • Why doesn't a 400 caused by a bad Host header appear in django.request?
    A `Host` header outside `ALLOWED_HOSTS` raises `DisallowedHost`, a `SuspiciousOperation`. Django logs such requests to `django.security.DisallowedHost` and deliberately not to `django.request`, so security noise can be routed or silenced separately from ordinary request errors.
  • Which Django logger records the SQL that migrations run?
    `django.db.backends.schema`, at `DEBUG` level, with `sql` and `params` but no `duration`. It covers schema-changing operations; SQL executed inside a `RunPython` operation is not logged there.

saying these in an interview costs you the question

  • django.request logs every request, including successful ones
  • django.db.backends logs SQL in production once its level is DEBUG
  • DisallowedHost errors are logged to django.request
  • django.server logs requests under any WSGI server
  • Django posts its own messages directly to the django logger