1
0
mirror of https://github.com/django/django.git synced 2025-03-31 19:46:42 +00:00

Fixed #27271 -- Fixed a crash in runserver logging.

Allowed ServerFormatter to handle simple string messages or messages with
formatting arguments. The formatter will set the server_time variable on
the log record if it's required by the format string but wasn't passed in
extra parameters.
This commit is contained in:
Sergey Fursov 2016-09-26 01:25:38 +03:00 committed by Tim Graham
parent b29c6c96c7
commit 6709ea4ae9
4 changed files with 68 additions and 13 deletions

View File

@ -164,28 +164,32 @@ class ServerFormatter(logging.Formatter):
super(ServerFormatter, self).__init__(*args, **kwargs) super(ServerFormatter, self).__init__(*args, **kwargs)
def format(self, record): def format(self, record):
args = record.args
msg = record.msg msg = record.msg
status_code = getattr(record, 'status_code', None)
if len(args) == 0: if status_code:
msg = self.style.HTTP_BAD_REQUEST(msg) if 200 <= status_code < 300:
else:
if args[1][0] == '2':
# Put 2XX first, since it should be the common case # Put 2XX first, since it should be the common case
msg = self.style.HTTP_SUCCESS(msg) msg = self.style.HTTP_SUCCESS(msg)
elif args[1][0] == '1': elif 100 <= status_code < 200:
msg = self.style.HTTP_INFO(msg) msg = self.style.HTTP_INFO(msg)
elif args[1] == '304': elif status_code == 304:
msg = self.style.HTTP_NOT_MODIFIED(msg) msg = self.style.HTTP_NOT_MODIFIED(msg)
elif args[1][0] == '3': elif 300 <= status_code < 400:
msg = self.style.HTTP_REDIRECT(msg) msg = self.style.HTTP_REDIRECT(msg)
elif args[1] == '404': elif status_code == 404:
msg = self.style.HTTP_NOT_FOUND(msg) msg = self.style.HTTP_NOT_FOUND(msg)
elif args[1][0] == '4': elif 400 <= status_code < 500:
msg = self.style.HTTP_BAD_REQUEST(msg) msg = self.style.HTTP_BAD_REQUEST(msg)
else: else:
# Any 5XX, or any other response # Any 5XX, or any other status code
msg = self.style.HTTP_SERVER_ERROR(msg) msg = self.style.HTTP_SERVER_ERROR(msg)
if self.uses_server_time() and not hasattr(record, 'server_time'):
record.server_time = self.formatTime(record, self.datefmt)
record.msg = msg record.msg = msg
return super(ServerFormatter, self).format(record) return super(ServerFormatter, self).format(record)
def uses_server_time(self):
return self._fmt.find('%(server_time)') >= 0

View File

@ -22,3 +22,6 @@ Bugfixes
``SplitDateTimeWidget``, ``SelectDateWidget``, and ``SplitArrayWidget`` ``SplitDateTimeWidget``, ``SelectDateWidget``, and ``SplitArrayWidget``
(:ticket:`27186`). Custom widgets affected by this issue may need to (:ticket:`27186`). Custom widgets affected by this issue may need to
implement a :meth:`~django.forms.Widget.value_omitted_from_data` method. implement a :meth:`~django.forms.Widget.value_omitted_from_data` method.
* Fixed a crash in ``runserver`` logging during a "Broken pipe" error
(:ticket:`27271`).

View File

@ -8,7 +8,8 @@ from threading import Thread
from django.core.exceptions import FieldError from django.core.exceptions import FieldError
from django.db import DatabaseError, IntegrityError, connection from django.db import DatabaseError, IntegrityError, connection
from django.test import ( from django.test import (
SimpleTestCase, TestCase, TransactionTestCase, ignore_warnings, skipUnlessDBFeature, SimpleTestCase, TestCase, TransactionTestCase, ignore_warnings,
skipUnlessDBFeature,
) )
from django.utils.encoding import DjangoUnicodeDecodeError from django.utils.encoding import DjangoUnicodeDecodeError

View File

@ -3,19 +3,22 @@ from __future__ import unicode_literals
import logging import logging
import warnings import warnings
from contextlib import contextmanager
from admin_scripts.tests import AdminScriptTestCase from admin_scripts.tests import AdminScriptTestCase
from django.conf import settings from django.conf import settings
from django.core import mail from django.core import mail
from django.core.files.temp import NamedTemporaryFile from django.core.files.temp import NamedTemporaryFile
from django.core.management import color
from django.db import connection from django.db import connection
from django.test import RequestFactory, SimpleTestCase, override_settings from django.test import RequestFactory, SimpleTestCase, override_settings
from django.test.utils import LoggingCaptureMixin, patch_logger from django.test.utils import LoggingCaptureMixin, patch_logger
from django.utils import six
from django.utils.deprecation import RemovedInNextVersionWarning from django.utils.deprecation import RemovedInNextVersionWarning
from django.utils.log import ( from django.utils.log import (
DEFAULT_LOGGING, AdminEmailHandler, CallbackFilter, RequireDebugFalse, DEFAULT_LOGGING, AdminEmailHandler, CallbackFilter, RequireDebugFalse,
RequireDebugTrue, RequireDebugTrue, ServerFormatter,
) )
from .logconfig import MyEmailBackend from .logconfig import MyEmailBackend
@ -513,3 +516,47 @@ class SchemaLoggerTests(SimpleTestCase):
}}, }},
)] )]
) )
class LogFormattersTests(SimpleTestCase):
def test_server_formatter_styles(self):
color_style = color.make_style('')
formatter = ServerFormatter()
formatter.style = color_style
log_msg = 'log message'
status_code_styles = [
(200, 'HTTP_SUCCESS'),
(100, 'HTTP_INFO'),
(304, 'HTTP_NOT_MODIFIED'),
(300, 'HTTP_REDIRECT'),
(404, 'HTTP_NOT_FOUND'),
(400, 'HTTP_BAD_REQUEST'),
(500, 'HTTP_SERVER_ERROR'),
]
for status_code, style in status_code_styles:
record = logging.makeLogRecord({'msg': log_msg, 'status_code': status_code})
self.assertEqual(formatter.format(record), getattr(color_style, style)(log_msg))
record = logging.makeLogRecord({'msg': log_msg})
self.assertEqual(formatter.format(record), log_msg)
def test_server_formatter_default_format(self):
server_time = '2016-09-25 10:20:30'
log_msg = 'log message'
logger = logging.getLogger('django.server')
@contextmanager
def patch_django_server_logger():
old_stream = logger.handlers[0].stream
new_stream = six.StringIO()
logger.handlers[0].stream = new_stream
yield new_stream
logger.handlers[0].stream = old_stream
with patch_django_server_logger() as logger_output:
logger.info(log_msg, extra={'server_time': server_time})
self.assertEqual('[%s] %s\n' % (server_time, log_msg), logger_output.getvalue())
with patch_django_server_logger() as logger_output:
logger.info(log_msg)
six.assertRegex(self, logger_output.getvalue(), r'^\[[-:,.\s\d]+\] %s' % log_msg)