With DEBUG off, how does a Django project report unhandled exceptions by default, and what must you configure before launch so errors are not lost?
answer
- the debug page is gone
- Django's built-in logging defaults
- a filter that depends on DEBUG
- ADMINS, SERVER_EMAIL, a real mailer
- a stream your platform collects
basics
~20 sBy default Django emails ERROR records from its loggers, including unhandled view exceptions, to ADMINS when DEBUG is False, and discards the rest. Set ADMINS, SERVER_EMAIL and a real mail backend, and add a LOGGING config that writes to collected output.
solid answer
~40 sDjango installs a default logging config before yours: the `django` logger has a `console` handler filtered by `require_debug_true` and a `mail_admins` handler (`AdminEmailHandler`, level `ERROR`) filtered by `require_debug_false`. So with `DEBUG = False`, an unhandled exception is logged on `django.request` at ERROR and emailed to `ADMINS` — and Django's loggers print nothing to the console. If `ADMINS` is empty, the handler returns without sending, and the error is effectively lost. Before launch: set `ADMINS` (a list of address strings since 6.0), a `SERVER_EMAIL` your mail provider accepts (the default is `root@localhost`), and a real backend — a Django 6.1 `startproject` writes `MAILERS` with the console backend. Then add a `LOGGING` config with `disable_existing_loggers: False` that streams to stdout or stderr for your log collector.
code
python · 21 linesADMINS = ['[email protected]']
SERVER_EMAIL = '[email protected]'
MAILERS = {
'default': {
'BACKEND': 'django.core.mail.backends.smtp.EmailBackend',
'OPTIONS': {'host': 'smtp.example.com', 'use_tls': True},
},
}
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'stdout': {'class': 'logging.StreamHandler'},
},
'loggers': {
'django': {'handlers': ['stdout'], 'level': 'INFO'},
'shop': {'handlers': ['stdout'], 'level': 'INFO'},
},
}go deeper
Know that with DEBUG off Django emails unhandled errors to the addresses in ADMINS, so that setting must be filled in.
Explain the default logging config, the require_debug_true and require_debug_false filters, and the settings that decide whether the error email is sent and accepted.
Close every way errors get lost: empty ADMINS, a rejected SERVER_EMAIL, the 6.1 console mailer, and a missing log stream, and verify them before launch.
Decide how errors reach people across services: email for a small team, a log pipeline and error tracker at scale, and who is on the receiving end.
## What happens to an exception when DEBUG is off With `DEBUG = True`, an unhandled exception in a view produces the technical 500 page — the developer sees it immediately. With `DEBUG = False`, the user sees your `500.html` page, and Django records the error through Python's `logging` module on the **`django.request`** logger at level **ERROR**. Where that record goes depends entirely on logging configuration. ## Django's default logging configuration Django applies a built-in configuration (`DEFAULT_LOGGING` in `django.utils.log`) at start-up, then applies your `LOGGING` setting on top of it. The defaults that matter here: | Handler | Attached to | Level | Filter | Effect in production | |---|---|---|---|---| | `console` | `django` logger | INFO | `require_debug_true` | silent: passes only when DEBUG is True | | `mail_admins` | `django` logger | ERROR | `require_debug_false` | active: emails ERROR records to `ADMINS` | | `django.server` | `django.server` logger | INFO | none | request lines from `runserver` only | Because `django.request` propagates to `django`, an unhandled view exception becomes an email to `ADMINS`. Everything else Django logs below ERROR is dropped, and nothing from Django's loggers reaches the console. ## Four ways errors get lost 1. **Empty `ADMINS`.** The default is `[]`. `AdminEmailHandler` returns early when there is no one to email. 2. **Unusable sender.** `SERVER_EMAIL` defaults to `root@localhost`, which many mail providers reject, so the email fails to send. 3. **A mailer that does not send.** In **Django 6.1**, `startproject` writes a `MAILERS` setting whose `default` uses the **console** email backend, so error emails are printed to the process output instead of delivered until you configure a real backend. Older projects configure mail through `EMAIL_BACKEND` and the `EMAIL_*` settings, which `MAILERS` replaces in 7.0. 4. **No log stream.** Without your own `LOGGING`, the platform's log collector sees little from Django itself in production, because the console handler is filtered out. ## The pre-launch configuration - `ADMINS = ['[email protected]']` — since **Django 6.0** a list of address strings; the old `(name, email)` tuples are deprecated. - `SERVER_EMAIL` set to an address your provider will send from; `EMAIL_SUBJECT_PREFIX` (default `'[Django] '`) can tag the environment. - A real mail backend for the alias the error emails use. - A `LOGGING` dictionary that adds a stream handler for the `django` logger and your own apps' loggers, with **`disable_existing_loggers: False`** so Django's own loggers keep working. - Optionally `MANAGERS` plus `BrokenLinkEmailsMiddleware` to be told about broken internal links that produce 404s. Many teams also send errors to a dedicated error-tracking service; that is additive, and the same logging hooks feed it. ## Verifying before launch - Run `python manage.py sendtestemail --admins` from the production environment to prove mail flows. - Trigger a deliberate error on a staging copy with `DEBUG = False` and confirm both the email and the log line arrive. - Note that `check --deploy` checks none of this. ## What interviewers listen for That the candidate knows Django's default config emails errors only when `DEBUG` is off and drops console output, can list the settings that make that email arrive, and adds a real log stream instead of relying on email alone.
- Why does a new LOGGING config sometimes silence Django's own loggers?`LOGGING` is passed to `logging.config.dictConfig`, whose `disable_existing_loggers` defaults to true, disabling every logger that already exists and is not named in your config — including loggers Django created. Set `'disable_existing_loggers': False` so your configuration adds to Django's defaults instead of switching them off.
- How do you prove before launch that error emails will actually arrive?Run `python manage.py sendtestemail --admins` in the production environment; it sends a message to everyone in `ADMINS` through the configured mailer, exercising recipients, sender and backend together. Then cause a deliberate exception on a staging copy with `DEBUG = False` and check that both the email and the log line arrive.
saying these in an interview costs you the question
- With DEBUG off, Django prints tracebacks to the console by default
- Django emails errors to ADMINS even when DEBUG is True
- An empty ADMINS makes Django fall back to SERVER_EMAIL as the recipient
- Adding LOGGING replaces Django's defaults with no side effects on existing loggers
- check --deploy verifies that error emails can be delivered