log HTTP errors in access log. close #317

This commit is contained in:
Benoit Chesneau 2012-04-26 18:17:55 +02:00
parent 1c27d369b4
commit 5f11713678
8 changed files with 35 additions and 15 deletions

View File

@ -21,6 +21,5 @@ def app(environ, start_response):
('Content-Length', str(len(data))), ('Content-Length', str(len(data))),
('X-Gunicorn-Version', __version__) ('X-Gunicorn-Version', __version__)
] ]
start_response(status, response_headers) start_response(status, response_headers)
return iter([data]) return iter([data])

View File

@ -273,7 +273,7 @@ def validate_post_request(val):
elif largs == 2: elif largs == 2:
return wrap_post_request(val) return wrap_post_request(val)
else: else:
raise TypeError("Value must have an arity of: 3") raise TypeError("Value must have an arity of: 3")

View File

@ -157,7 +157,7 @@ class Logger(object):
status = resp.status.split(None, 1)[0] status = resp.status.split(None, 1)[0]
atoms = { atoms = {
'h': environ['REMOTE_ADDR'], 'h': environ.get('REMOTE_ADDR', '-'),
'l': '-', 'l': '-',
'u': '-', # would be cool to get username from basic auth header 'u': '-', # would be cool to get username from basic auth header
't': self.now(), 't': self.now(),

View File

@ -39,10 +39,9 @@ class FileWrapper:
return data return data
raise IndexError raise IndexError
def create(req, sock, client, server, cfg):
resp = Response(req, sock)
environ = { def default_environ(req, sock, cfg):
return {
"wsgi.input": req.body, "wsgi.input": req.body,
"wsgi.errors": sys.stderr, "wsgi.errors": sys.stderr,
"wsgi.version": (1, 0), "wsgi.version": (1, 0),
@ -58,6 +57,12 @@ def create(req, sock, client, server, cfg):
"SERVER_PROTOCOL": "HTTP/%s" % ".".join(map(str, req.version)) "SERVER_PROTOCOL": "HTTP/%s" % ".".join(map(str, req.version))
} }
def create(req, sock, client, server, cfg):
resp = Response(req, sock)
environ = default_environ(req, sock, cfg)
# authors should be aware that REMOTE_HOST and REMOTE_ADDR # authors should be aware that REMOTE_HOST and REMOTE_ADDR
# may not qualify the remote addr: # may not qualify the remote addr:
# http://www.ietf.org/rfc/rfc3875 # http://www.ietf.org/rfc/rfc3875

View File

@ -125,8 +125,9 @@ def load_class(uri, default="sync", section="gunicorn.workers"):
return pkg_resources.load_entry_point("gunicorn", return pkg_resources.load_entry_point("gunicorn",
section, uri) section, uri)
except ImportError: except ImportError, e:
raise RuntimeError("class uri invalid or not found") raise RuntimeError("class uri invalid or not found: [%s]",
str(e))
klass = components.pop(-1) klass = components.pop(-1)
mod = __import__('.'.join(components)) mod = __import__('.'.join(components))
for comp in components[1:]: for comp in components[1:]:

View File

@ -26,6 +26,7 @@ class AsyncWorker(base.Worker):
raise NotImplementedError() raise NotImplementedError()
def handle(self, client, addr): def handle(self, client, addr):
req = None
try: try:
parser = http.RequestParser(self.cfg, client) parser = http.RequestParser(self.cfg, client)
try: try:
@ -38,6 +39,8 @@ class AsyncWorker(base.Worker):
self.handle_request(req, client, addr) self.handle_request(req, client, addr)
except StopIteration, e: except StopIteration, e:
self.log.debug("Closing connection. %s", e) self.log.debug("Closing connection. %s", e)
except Exception, e:
self.handle_error(req, client, addr, e)
except socket.error, e: except socket.error, e:
if e[0] not in (errno.EPIPE, errno.ECONNRESET): if e[0] not in (errno.EPIPE, errno.ECONNRESET):
self.log.exception("Socket error processing request.") self.log.exception("Socket error processing request.")
@ -47,14 +50,14 @@ class AsyncWorker(base.Worker):
else: else:
self.log.debug("Ignoring EPIPE") self.log.debug("Ignoring EPIPE")
except Exception, e: except Exception, e:
self.handle_error(client, e) self.handle_error(req, client, addr, e)
finally: finally:
util.close(client) util.close(client)
def handle_request(self, req, sock, addr): def handle_request(self, req, sock, addr):
request_start = datetime.now()
try: try:
self.cfg.pre_request(self, req) self.cfg.pre_request(self, req)
request_start = datetime.now()
resp, environ = wsgi.create(req, sock, addr, self.address, self.cfg) resp, environ = wsgi.create(req, sock, addr, self.address, self.cfg)
self.nr += 1 self.nr += 1
if self.alive and self.nr >= self.max_requests: if self.alive and self.nr >= self.max_requests:

View File

@ -3,6 +3,7 @@
# This file is part of gunicorn released under the MIT license. # This file is part of gunicorn released under the MIT license.
# See the NOTICE for more information. # See the NOTICE for more information.
from datetime import datetime
import os import os
import signal import signal
import sys import sys
@ -11,11 +12,10 @@ import traceback
from gunicorn import util from gunicorn import util
from gunicorn.workers.workertmp import WorkerTmp from gunicorn.workers.workertmp import WorkerTmp
from gunicorn.http.errors import InvalidHeader, InvalidHeaderName, \ from gunicorn.http.errors import InvalidHeader, InvalidHeaderName, \
InvalidRequestLine, InvalidRequestMethod, InvalidHTTPVersion, \ InvalidRequestLine, InvalidRequestMethod, InvalidHTTPVersion, \
LimitRequestLine, LimitRequestHeaders LimitRequestLine, LimitRequestHeaders
from gunicorn.http.wsgi import default_environ, Response
class Worker(object): class Worker(object):
@ -125,7 +125,8 @@ class Worker(object):
self.alive = False self.alive = False
sys.exit(0) sys.exit(0)
def handle_error(self, client, exc): def handle_error(self, req, client, addr, exc):
request_start = datetime.now()
if isinstance(exc, (InvalidRequestLine, InvalidRequestMethod, if isinstance(exc, (InvalidRequestLine, InvalidRequestMethod,
InvalidHTTPVersion, InvalidHeader, InvalidHeaderName,)): InvalidHTTPVersion, InvalidHeader, InvalidHeaderName,)):
@ -157,6 +158,16 @@ class Worker(object):
reason = "Internal Server Error" reason = "Internal Server Error"
mesg = "" mesg = ""
if req is not None:
request_time = datetime.now() - request_start
environ = default_environ(req, client, self.cfg)
environ['REMOTE_ADDR'] = addr[0]
environ['REMOTE_PORT'] = str(addr[1])
resp = Response(req, client)
resp.status = "%s %s" % (status_int, reason)
resp.response_length = len(mesg)
self.log.access(resp, req, environ, request_time)
if self.debug: if self.debug:
tb = traceback.format_exc() tb = traceback.format_exc()
mesg += "<h2>Traceback:</h2>\n<pre>%s</pre>" % tb mesg += "<h2>Traceback:</h2>\n<pre>%s</pre>" % tb

View File

@ -65,6 +65,7 @@ class SyncWorker(base.Worker):
raise raise
def handle(self, client, addr): def handle(self, client, addr):
req = None
try: try:
parser = http.RequestParser(self.cfg, client) parser = http.RequestParser(self.cfg, client)
req = parser.next() req = parser.next()
@ -77,7 +78,7 @@ class SyncWorker(base.Worker):
else: else:
self.log.debug("Ignoring EPIPE") self.log.debug("Ignoring EPIPE")
except Exception, e: except Exception, e:
self.handle_error(client, e) self.handle_error(req, client, addr, e)
finally: finally:
util.close(client) util.close(client)
@ -113,7 +114,7 @@ class SyncWorker(base.Worker):
raise raise
except Exception, e: except Exception, e:
# Only send back traceback in HTTP in debug mode. # Only send back traceback in HTTP in debug mode.
self.handle_error(client, e) self.handle_error(req, client, addr, e)
return return
finally: finally:
try: try: