add access logging for gevent_* workers

This commit is contained in:
Randall Leeds 2011-09-18 03:02:12 -07:00
parent 04680f1020
commit 55de904e9d
5 changed files with 43 additions and 25 deletions

View File

@ -93,7 +93,7 @@ class Logger(object):
'r': "%s %s %s" % (environ['REQUEST_METHOD'], 'r': "%s %s %s" % (environ['REQUEST_METHOD'],
environ['RAW_URI'], environ["SERVER_PROTOCOL"]), environ['RAW_URI'], environ["SERVER_PROTOCOL"]),
's': status, 's': status,
'b': str(resp.clength) or '-', 'b': str(resp.response_length) or '-',
'f': environ.get('HTTP_REFERER', '-'), 'f': environ.get('HTTP_REFERER', '-'),
'a': environ.get('HTTP_USER_AGENT', '-'), 'a': environ.get('HTTP_USER_AGENT', '-'),
'T': str(request_time.seconds), 'T': str(request_time.seconds),

View File

@ -61,25 +61,25 @@ class Message(object):
def set_body_reader(self): def set_body_reader(self):
chunked = False chunked = False
clength = None response_length = None
for (name, value) in self.headers: for (name, value) in self.headers:
if name == "CONTENT-LENGTH": if name == "CONTENT-LENGTH":
try: try:
clength = int(value) response_length = int(value)
except ValueError: except ValueError:
clength = None response_length = None
elif name == "TRANSFER-ENCODING": elif name == "TRANSFER-ENCODING":
chunked = value.lower() == "chunked" chunked = value.lower() == "chunked"
elif name == "SEC-WEBSOCKET-KEY1": elif name == "SEC-WEBSOCKET-KEY1":
clength = 8 response_length = 8
if clength is not None or chunked: if response_length is not None or chunked:
break break
if chunked: if chunked:
self.body = Body(ChunkedReader(self, self.unreader)) self.body = Body(ChunkedReader(self, self.unreader))
elif clength is not None: elif response_length is not None:
self.body = Body(LengthReader(self.unreader, clength)) self.body = Body(LengthReader(self.unreader, response_length))
else: else:
self.body = Body(EOFReader(self.unreader)) self.body = Body(EOFReader(self.unreader))

View File

@ -154,7 +154,7 @@ class Response(object):
self.must_close = False self.must_close = False
self.headers = [] self.headers = []
self.headers_sent = False self.headers_sent = False
self.clength = None self.response_length = None
self.sent = 0 self.sent = 0
def force_close(self): def force_close(self):
@ -163,7 +163,7 @@ class Response(object):
def should_close(self): def should_close(self):
if self.must_close or self.req.should_close(): if self.must_close or self.req.should_close():
return True return True
if self.clength is not None or self.chunked: if self.response_length is not None or self.chunked:
return False return False
return True return True
@ -187,7 +187,7 @@ class Response(object):
assert isinstance(name, basestring), "%r is not a string" % name assert isinstance(name, basestring), "%r is not a string" % name
lname = name.lower().strip() lname = name.lower().strip()
if lname == "content-length": if lname == "content-length":
self.clength = int(value) self.response_length = int(value)
elif util.is_hoppish(name): elif util.is_hoppish(name):
if lname == "connection": if lname == "connection":
# handle websocket # handle websocket
@ -203,7 +203,7 @@ class Response(object):
# Only use chunked responses when the client is # Only use chunked responses when the client is
# speaking HTTP/1.1 or newer and there was # speaking HTTP/1.1 or newer and there was
# no Content-Length header set. # no Content-Length header set.
if self.clength is not None: if self.response_length is not None:
return False return False
elif self.req.version <= (1,0): elif self.req.version <= (1,0):
return False return False
@ -242,12 +242,12 @@ class Response(object):
arglen = len(arg) arglen = len(arg)
tosend = arglen tosend = arglen
if self.clength is not None: if self.response_length is not None:
if self.sent >= self.clength: if self.sent >= self.response_length:
# Never write more than self.clength bytes # Never write more than self.response_length bytes
return return
tosend = min(self.clength - self.sent, tosend) tosend = min(self.response_length - self.sent, tosend)
if tosend < arglen: if tosend < arglen:
arg = arg[:tosend] arg = arg[:tosend]
@ -287,8 +287,8 @@ class Response(object):
fo_offset = respiter.filelike.tell() fo_offset = respiter.filelike.tell()
nbytes = max(os.fstat(fileno).st_size - fo_offset, 0) nbytes = max(os.fstat(fileno).st_size - fo_offset, 0)
if self.clength: if self.response_length:
nbytes = min(nbytes, self.clength) nbytes = min(nbytes, self.response_length)
if nbytes == 0: if nbytes == 0:
return return

View File

@ -7,6 +7,7 @@ from __future__ import with_statement
import os import os
import sys import sys
from datetime import datetime
# workaround on osx, disable kqueue # workaround on osx, disable kqueue
if sys.platform == "darwin": if sys.platform == "darwin":
@ -131,8 +132,9 @@ class GeventBaseWorker(Worker):
self.socket.setblocking(1) self.socket.setblocking(1)
pool = Pool(self.worker_connections) pool = Pool(self.worker_connections)
self.server_class.base_env['wsgi.multiprocess'] = (self.cfg.workers > 1) self.server_class.base_env['wsgi.multiprocess'] = (self.cfg.workers > 1)
server = self.server_class(self.socket, application=self.wsgi, server = self.server_class(
spawn=pool, handler_class=self.wsgi_handler) self.socket, application=self.wsgi, spawn=pool, log=self.log,
handler_class=self.wsgi_handler)
server.start() server.start()
try: try:
while self.alive: while self.alive:
@ -165,8 +167,12 @@ class GeventBaseWorker(Worker):
class PyWSGIHandler(pywsgi.WSGIHandler): class PyWSGIHandler(pywsgi.WSGIHandler):
def log_request(self, *args):
pass def log_request(self):
start = datetime.fromtimestamp(self.time_start)
finish = datetime.fromtimestamp(self.time_finish)
response_time = finish - start
self.server.log.access(self, self.environ, response_time)
def get_environ(self): def get_environ(self):
env = super(PyWSGIHandler, self).get_environ() env = super(PyWSGIHandler, self).get_environ()

View File

@ -8,14 +8,26 @@ from gevent import wsgi
class WSGIHandler(wsgi.WSGIHandler): class WSGIHandler(wsgi.WSGIHandler):
def log_request(self, *args):
pass @property
def status(self):
return ' '.join([str(self.code), self.reason])
def log_request(self, length):
self.response_length = length
response_time = datetime.now() - self.time_start
self.server.log.access(self, self.environ, response_time)
def prepare_env(self): def prepare_env(self):
env = super(WSGIHandler, self).prepare_env() env = super(WSGIHandler, self).prepare_env()
env['RAW_URI'] = self.request.uri env['RAW_URI'] = self.request.uri
self.environ = env
return env return env
def handle(self):
self.time_start = datetime.now()
super(WSGIHandler, self).handle()
class WSGIServer(wsgi.WSGIServer): class WSGIServer(wsgi.WSGIServer):
base_env = BASE_WSGI_ENV base_env = BASE_WSGI_ENV