Using Django 1.5.1:
DEBUG = False
LOGGING = {
'version': 1,
'disable_existing_loggers': True,
'formatters': {
'verbose': {
'format': '%(levelname)s %(asctime)s %(module)s %(message)s'
},
},
'handlers': {
'console': {
'level': 'DEBUG',
'class': 'logging.StreamHandler',
'formatter': 'verbose',
},
},
'loggers': {
# root logger
'': {
'handlers': ['console'],
},
#'django.request': {
# 'handlers': ['console'],
# 'level': 'DEBUG',
# 'propagate': False,
#},
}
}
If I uncomment the commented lines and call a view which has 1/0
, the traceback is printed to the console:
ERROR 2013-11-29 13:33:23,102 base Internal Server Error: /comment/*******/
Traceback (most recent call last):
...
File "*****/comments/views.py", line 10, in post
1/0
ZeroDivisionError: integer division or modulo by zero
WARNING 2013-11-29 13:33:23,103 csrf Forbidden (CSRF cookie not set.): /comment/******/
[29/Nov/2013 13:33:23] "POST /comment/******/ HTTP/1.0" 500 27
But if the lines stay commented, no traceback is printed to the console, just:
[29/Nov/2013 13:33:23] "POST /comment/******/ HTTP/1.0" 500 27
I thought if django.request
logger is not configured, it would propagate to the root logger, which prints everything to console.
I didn't find any information that django.request
is special.
Why it doesn't work?
Here I read:
Prior to Django 1.5, the LOGGING setting always overwrote the default Django logging configuration. From Django 1.5 forward, it is possible to get the project’s logging configuration merged with Django’s defaults, hence you can decide if you want to add to, or replace the existing configuration.
If the disable_existing_loggers key in the LOGGING dictConfig is set to True (which is the default) the default configuration is completely overridden. Alternatively you can redefine some or all of the loggers by setting disable_existing_loggers to False.
In django/utils/log.py
:
# Default logging for Django. This sends an email to the site admins on every
# HTTP 500 error. Depending on DEBUG, all other log records are either sent to
# the console (DEBUG=True) or discarded by mean of the NullHandler (DEBUG=False).
DEFAULT_LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'filters': {
'require_debug_false': {
'()': 'django.utils.log.RequireDebugFalse',
},
'require_debug_true': {
'()': 'django.utils.log.RequireDebugTrue',
},
},
'handlers': {
'console':{
'level': 'INFO',
'filters': ['require_debug_true'],
'class': 'logging.StreamHandler',
},
'null': {
'class': 'django.utils.log.NullHandler',
},
'mail_admins': {
'level': 'ERROR',
'filters': ['require_debug_false'],
'class': 'django.utils.log.AdminEmailHandler'
}
},
'loggers': {
'django': {
'handlers': ['console'],
},
'django.request': {
'handlers': ['mail_admins'],
'level': 'ERROR',
'propagate': False,
},
'py.warnings': {
'handlers': ['console'],
},
}
}
So by default django.request
has propagate = False
. But In my case I have 'disable_existing_loggers': True
.
Ok, so the behavior is "correct", but not expected.
django/conf/__init__.py:65
:What is happening is that default logging configuration is applied and
django.request
logger is created. Then my customLOGGING
configuration is applied withdisable_existing_loggers = True
, but Python doesn't delete already existing loggerdjango.request
, but only disables it.So I have to manually reconfigure
django.request
logger in my configuration. :(For Django-2.1 I found that the logging configuration is more concise:
As quoted in the docs:
This is in accordance with the docs of
propagate
which state that:Therefore I decided not to prevent Django from configuring logging. I wanted to stop sending emails to the admins because I use sentry, and I just configured the root logger to use the
console
andfile
handlers, according to the django docs examples:Which results in:
Not tested in production yet, but it seems that it will work as expected.
The solution is to prevent Django from configuring logging and handle it ourselves. Fortunately this is easy. In
settings.py
:UPDATE ~March 2015: Django has clarified their documentation:
For posterity and detail: The explanation? Most of the confusion I think comes down to Django's poor explanation of
disable_existing_loggers
, which says that when True, "the default configuration is completely overridden". In your own answer you discovered that is not correct; what's happening is that the existing loggers, which Django already configures, are disabled not replaced.The Python logging documentation explains it better (emphasis added):
Based on Django docs we think, "override the defaults with my own LOGGING configuration and anything I don't specify will bubble up". I've tripped over this expectation as well. The behavior we expect is along the lines of replace_existing_loggers (which isn't a real thing). Instead the Django loggers are shut up not bubbled up.
We need to prevent the setup of these Django loggers in the first place and here the Django docs are more helpful:
Django will still use its loggers but since they are not handled (and then disabled) by the configuration, those loggers will bubble up as expected. A simple test with the above settings: