1
0
mirror of https://github.com/django/django.git synced 2025-10-26 07:06:08 +00:00

Fixed #25684 -- Made runserver use logging for request/response output.

Thanks andreif for the contributing to the patch.
This commit is contained in:
Flavio Curella
2015-11-06 10:19:41 -06:00
committed by Tim Graham
parent 3792e291e6
commit 0bc5cd6280
6 changed files with 180 additions and 48 deletions

View File

@@ -9,13 +9,13 @@ been reviewed for security issues. DON'T USE IT FOR PRODUCTION USE!
from __future__ import unicode_literals
import logging
import socket
import sys
from wsgiref import simple_server
from django.core.exceptions import ImproperlyConfigured
from django.core.handlers.wsgi import ISO_8859_1, UTF_8
from django.core.management.color import color_style
from django.core.wsgi import get_wsgi_application
from django.utils import six
from django.utils.encoding import uri_to_iri
@@ -24,6 +24,8 @@ from django.utils.six.moves import socketserver
__all__ = ('WSGIServer', 'WSGIRequestHandler')
logger = logging.getLogger('django.server')
def get_internal_wsgi_application():
"""
@@ -80,7 +82,7 @@ class WSGIServer(simple_server.WSGIServer, object):
def handle_error(self, request, client_address):
if is_broken_pipe_error():
sys.stderr.write("- Broken pipe from %s\n" % (client_address,))
logger.info("- Broken pipe from %s\n", client_address)
else:
super(WSGIServer, self).handle_error(request, client_address)
@@ -94,47 +96,39 @@ class ServerHandler(simple_server.ServerHandler, object):
class WSGIRequestHandler(simple_server.WSGIRequestHandler, object):
def __init__(self, *args, **kwargs):
self.style = color_style()
super(WSGIRequestHandler, self).__init__(*args, **kwargs)
def address_string(self):
# Short-circuit parent method to not call socket.getfqdn
return self.client_address[0]
def log_message(self, format, *args):
msg = "[%s] " % self.log_date_time_string()
try:
msg += "%s\n" % (format % args)
except UnicodeDecodeError:
# e.g. accessing the server via SSL on Python 2
msg += "\n"
# Utilize terminal colors, if available
if args[1][0] == '2':
# Put 2XX first, since it should be the common case
msg = self.style.HTTP_SUCCESS(msg)
elif args[1][0] == '1':
msg = self.style.HTTP_INFO(msg)
elif args[1] == '304':
msg = self.style.HTTP_NOT_MODIFIED(msg)
elif args[1][0] == '3':
msg = self.style.HTTP_REDIRECT(msg)
elif args[1] == '404':
msg = self.style.HTTP_NOT_FOUND(msg)
elif args[1][0] == '4':
extra = {
'request': self.request,
'server_time': self.log_date_time_string(),
}
if args[1][0] == '4':
# 0x16 = Handshake, 0x03 = SSL 3.0 or TLS 1.x
if args[0].startswith(str('\x16\x03')):
msg = ("You're accessing the development server over HTTPS, "
"but it only supports HTTP.\n")
msg = self.style.HTTP_BAD_REQUEST(msg)
else:
# Any 5XX, or any other response
msg = self.style.HTTP_SERVER_ERROR(msg)
extra['status_code'] = 500
logger.error(
"You're accessing the development server over HTTPS, but "
"it only supports HTTP.\n", extra=extra,
)
return
sys.stderr.write(msg)
if args[1].isdigit() and len(args[1]) == 3:
status_code = int(args[1])
extra['status_code'] = status_code
if status_code >= 500:
level = logger.error
elif status_code >= 400:
level = logger.warning
else:
level = logger.info
else:
level = logger.info
level(format, *args, extra=extra)
def get_environ(self):
# Strip all headers with underscores in the name before constructing

View File

@@ -9,6 +9,7 @@ from copy import copy
from django.conf import settings
from django.core import mail
from django.core.mail import get_connection
from django.core.management.color import color_style
from django.utils.deprecation import RemovedInNextVersionWarning
from django.utils.module_loading import import_string
from django.views.debug import ExceptionReporter
@@ -28,12 +29,23 @@ DEFAULT_LOGGING = {
'()': 'django.utils.log.RequireDebugTrue',
},
},
'formatters': {
'django.server': {
'()': 'django.utils.log.ServerFormatter',
'format': '[%(server_time)s] %(message)s',
}
},
'handlers': {
'console': {
'level': 'INFO',
'filters': ['require_debug_true'],
'class': 'logging.StreamHandler',
},
'django.server': {
'level': 'INFO',
'class': 'logging.StreamHandler',
'formatter': 'django.server',
},
'mail_admins': {
'level': 'ERROR',
'filters': ['require_debug_false'],
@@ -45,6 +57,11 @@ DEFAULT_LOGGING = {
'handlers': ['console', 'mail_admins'],
'level': 'INFO',
},
'django.server': {
'handlers': ['django.server'],
'level': 'INFO',
'propagate': False,
},
'py.warnings': {
'handlers': ['console'],
},
@@ -155,3 +172,36 @@ class RequireDebugFalse(logging.Filter):
class RequireDebugTrue(logging.Filter):
def filter(self, record):
return settings.DEBUG
class ServerFormatter(logging.Formatter):
def __init__(self, *args, **kwargs):
self.style = color_style()
super(ServerFormatter, self).__init__(*args, **kwargs)
def format(self, record):
args = record.args
msg = record.msg
if len(args) == 0:
msg = self.style.HTTP_BAD_REQUEST(msg)
else:
if args[1][0] == '2':
# Put 2XX first, since it should be the common case
msg = self.style.HTTP_SUCCESS(msg)
elif args[1][0] == '1':
msg = self.style.HTTP_INFO(msg)
elif args[1] == '304':
msg = self.style.HTTP_NOT_MODIFIED(msg)
elif args[1][0] == '3':
msg = self.style.HTTP_REDIRECT(msg)
elif args[1] == '404':
msg = self.style.HTTP_NOT_FOUND(msg)
elif args[1][0] == '4':
msg = self.style.HTTP_BAD_REQUEST(msg)
else:
# Any 5XX, or any other response
msg = self.style.HTTP_SERVER_ERROR(msg)
record.msg = msg
return super(ServerFormatter, self).format(record)