What do Django's built-in django.request, django.db.backends, django.security and django.server loggers each record?
answer
- children of the django logger
- responses, queries, attacks, dev server
- 4XX warning, 5XX error
- SQL only when DEBUG is True
basics
~10 sdjango.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 sAll 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 linesLOGGING = {
"version": 1,
"disable_existing_loggers": False,
"handlers": {
"null": {"class": "logging.NullHandler"},
},
"loggers": {
"django.security.DisallowedHost": {
"handlers": ["null"],
"propagate": False,
},
},
}go deeper
Recall that django.request logs failing responses and django.db.backends logs SQL, both as children of the django logger.
Explain the levels and extra attributes of each logger, the DEBUG condition on SQL logging, and why security 400s skip django.request.
Route categories precisely: errors to alerting, security noise silenced per sub-logger, SQL only where DEBUG or test tooling enables it.
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