Fixed #24526 -- Combined django.request/security loggers with the root logger.

Thanks Carl Meyer for review.
This commit is contained in:
Tim Graham 2015-03-23 18:17:43 -04:00
parent 37682368a6
commit 8efea1b8d5
8 changed files with 75 additions and 49 deletions

View File

@ -616,3 +616,18 @@ def override_script_prefix(prefix):
Decorator or context manager to temporary override the script prefix.
"""
return ScriptPrefix(prefix)
class LoggingCaptureMixin(object):
"""
Capture the output from the 'django' logger and store it on the class's
logger_output attribute.
"""
def setUp(self):
self.logger = logging.getLogger('django')
self.old_stream = self.logger.handlers[0].stream
self.logger_output = six.StringIO()
self.logger.handlers[0].stream = self.logger_output
def tearDown(self):
self.logger.handlers[0].stream = self.old_stream

View File

@ -40,17 +40,7 @@ DEFAULT_LOGGING = {
},
'loggers': {
'django': {
'handlers': ['console'],
},
'django.request': {
'handlers': ['mail_admins'],
'level': 'ERROR',
'propagate': False,
},
'django.security': {
'handlers': ['mail_admins'],
'level': 'ERROR',
'propagate': False,
'handlers': ['console', 'mail_admins'],
},
'py.warnings': {
'handlers': ['console'],

View File

@ -358,6 +358,26 @@ template object. These classes have been combined into
:class:`~django.template.base.Origin` and is now always set regardless of the
engine debug setting.
.. _default-logging-changes-19:
Changes to the default logging configuration
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
To make it easier to write custom logging configurations, Django's default
logging configuration no longer defines 'django.request' and 'django.security'
loggers. Instead, it defines a single 'django' logger with two handlers:
* 'console': filtered at the ``INFO`` level and only active if ``DEBUG=True``.
* 'mail_admins': filtered at the ``ERROR`` level and only active if
``DEBUG=False``.
If you aren't overriding Django's default logging, you should see minimal
changes in behavior, but you might see some new logging to the ``runserver``
console, for example.
If you are overriding Django's default logging, you should check to see how
your configuration merges with the new defaults.
Miscellaneous
~~~~~~~~~~~~~

View File

@ -266,12 +266,12 @@ If you use this example, be sure to change the ``'filename'`` path to a
location that's writable by the user that's running the Django application.
Second, here's an example of how to make the logging system print Django's
logging to the console. It overrides the fact that ``django.request`` and
``django.security`` don't propagate their log entries by default. It may be
useful during local development.
logging to the console. It may be useful during local development.
By default, this config only sends messages of level ``INFO`` or higher to the
console. Django does not log many such messages. Set the environment variable
console (same as Django's default logging config, except that the default only
displays log records when ``DEBUG=True``). Django does not log many such
messages. With this config, however, you can also set the environment variable
``DJANGO_LOG_LEVEL=DEBUG`` to see all of Django's debug logging which is very
verbose as it includes all database queries::
@ -293,6 +293,11 @@ verbose as it includes all database queries::
},
}
.. versionchanged:: 1.9
Django's default logging configuration changed. See :ref:`the release notes
<default-logging-changes-19>` for a description of the changes.
Finally, here's an example of a fairly complex logging setup::
LOGGING = {
@ -525,14 +530,12 @@ a client that does not match :setting:`ALLOWED_HOSTS`, Django will return a 400
response, and an error message will be logged to the
``django.security.DisallowedHost`` logger.
Only the parent ``django.security`` logger is configured by default, and all
child loggers will propagate to the parent logger. The ``django.security``
logger is configured the same as the ``django.request`` logger, and any error
events will be mailed to admins. Requests resulting in a 400 response due to
a ``SuspiciousOperation`` will not be logged to the ``django.request`` logger,
but only to the ``django.security`` logger.
These log events will reach the 'django' logger by default, which mails error
events to admins when ``DEBUG=False``. Requests resulting in a 400 response due
to a ``SuspiciousOperation`` will not be logged to the ``django.request``
logger, but only to the ``django.security`` logger.
To silence a particular type of SuspiciousOperation, you can override that
To silence a particular type of ``SuspiciousOperation``, you can override that
specific logger following this example:
.. code-block:: python
@ -704,20 +707,20 @@ By default, Django configures the following logging:
When :setting:`DEBUG` is ``True``:
* The ``django`` catch-all logger sends all messages at the ``INFO`` level or
higher to the console. Django doesn't make any such logging calls at this
time (all logging is at the ``DEBUG`` level or handled by the
``django.request`` and ``django.security`` loggers).
higher to the console.
* The ``py.warnings`` logger, which handles messages from ``warnings.warn()``,
sends messages to the console.
When :setting:`DEBUG` is ``False``:
* The ``django.request`` and ``django.security`` loggers send messages with
``ERROR`` or ``CRITICAL`` level to :class:`AdminEmailHandler`. These loggers
ignore anything at the ``WARNING`` level or below and log entries aren't
propagated to other loggers (they won't reach the ``django`` catch-all
logger, even when ``DEBUG`` is ``True``).
* The ``django`` logger send messages with ``ERROR`` or ``CRITICAL`` level to
:class:`AdminEmailHandler`.
.. versionchanged:: 1.9
Django's default logging configuration changed. See :ref:`the release notes
<default-logging-changes-19>` for a description of the changes.
See also :ref:`Configuring logging <configuring-logging>` to learn how you can
complement or replace this default logging configuration.

View File

@ -9,7 +9,7 @@ from admin_scripts.tests import AdminScriptTestCase
from django.core import mail
from django.core.files.temp import NamedTemporaryFile
from django.test import RequestFactory, TestCase, override_settings
from django.test.utils import patch_logger
from django.test.utils import LoggingCaptureMixin, patch_logger
from django.utils.deprecation import RemovedInNextVersionWarning
from django.utils.encoding import force_text
from django.utils.log import (
@ -65,26 +65,18 @@ class LoggingFiltersTest(TestCase):
self.assertEqual(filter_.filter("record is not used"), False)
class DefaultLoggingTest(TestCase):
def setUp(self):
self.logger = logging.getLogger('django')
self.old_stream = self.logger.handlers[0].stream
def tearDown(self):
self.logger.handlers[0].stream = self.old_stream
class DefaultLoggingTest(LoggingCaptureMixin, TestCase):
def test_django_logger(self):
"""
The 'django' base logger only output anything when DEBUG=True.
"""
output = StringIO()
self.logger.handlers[0].stream = output
self.logger.error("Hey, this is an error.")
self.assertEqual(output.getvalue(), '')
self.assertEqual(self.logger_output.getvalue(), '')
with self.settings(DEBUG=True):
self.logger.error("Hey, this is an error.")
self.assertEqual(output.getvalue(), 'Hey, this is an error.\n')
self.assertEqual(self.logger_output.getvalue(), 'Hey, this is an error.\n')
class WarningLoggerTests(TestCase):
@ -167,11 +159,10 @@ class CallbackFilterTest(TestCase):
class AdminEmailHandlerTest(TestCase):
logger = logging.getLogger('django.request')
logger = logging.getLogger('django')
def get_admin_email_handler(self, logger):
# Inspired from views/views.py: send_log()
# ensuring the AdminEmailHandler does not get filtered out
# Ensure that AdminEmailHandler does not get filtered out
# even with DEBUG=True.
admin_email_handler = [
h for h in logger.handlers

View File

@ -19,6 +19,7 @@ from django.utils._os import upath
from django.utils.deprecation import (
RemovedInDjango20Warning, RemovedInDjango21Warning,
)
from django.utils.log import DEFAULT_LOGGING
warnings.simplefilter("error", RemovedInDjango20Warning)
warnings.simplefilter("error", RemovedInDjango21Warning)
@ -144,6 +145,11 @@ def setup(verbosity, test_labels):
'auth': 'django.contrib.auth.tests.migrations',
'contenttypes': 'contenttypes_tests.migrations',
}
log_config = DEFAULT_LOGGING
# Filter out non-error logging so we don't have to capture it in lots of
# tests.
log_config['loggers']['django']['level'] = 'ERROR'
settings.LOGGING = log_config
if verbosity > 0:
# Ensure any warnings captured to logging are piped through a verbose

View File

@ -16,6 +16,7 @@ from django.core.files.uploadedfile import SimpleUploadedFile
from django.core.urlresolvers import reverse
from django.template.base import TemplateDoesNotExist
from django.test import RequestFactory, TestCase, override_settings
from django.test.utils import LoggingCaptureMixin
from django.utils import six
from django.utils.encoding import force_bytes, force_text
from django.utils.functional import SimpleLazyObject
@ -48,7 +49,7 @@ class CallableSettingWrapperTests(TestCase):
@override_settings(DEBUG=True, ROOT_URLCONF="view_tests.urls")
class DebugViewTests(TestCase):
class DebugViewTests(LoggingCaptureMixin, TestCase):
def test_files(self):
response = self.client.get('/raises/')
@ -642,7 +643,7 @@ class ExceptionReportTestMixin(object):
@override_settings(ROOT_URLCONF='view_tests.urls')
class ExceptionReporterFilterTests(TestCase, ExceptionReportTestMixin):
class ExceptionReporterFilterTests(ExceptionReportTestMixin, LoggingCaptureMixin, TestCase):
"""
Ensure that sensitive information can be filtered out of error reports.
Refs #14614.
@ -833,7 +834,7 @@ class ExceptionReporterFilterTests(TestCase, ExceptionReportTestMixin):
self.assertNotContains(response, 'should not be displayed', status_code=500)
class AjaxResponseExceptionReporterFilter(TestCase, ExceptionReportTestMixin):
class AjaxResponseExceptionReporterFilter(ExceptionReportTestMixin, LoggingCaptureMixin, TestCase):
"""
Ensure that sensitive information can be filtered out of error reports.

View File

@ -102,7 +102,7 @@ def render_no_template(request):
def send_log(request, exc_info):
logger = logging.getLogger('django.request')
logger = logging.getLogger('django')
# The default logging config has a logging filter to ensure admin emails are
# only sent with DEBUG=False, but since someone might choose to remove that
# filter, we still want to be able to test the behavior of error emails