Add accurate timestamps in proxy log

Add accurate timestamps to the proxy-logging middleware log lines for
the start and end of a request. We use 9 digits of precision since on
some systems the clock resolution can be as fine as 1 ns.

The goal is to allow for log processing that can use the more accurate
timestamps to correlate requests, computing number of requests in
flight at a given historical point in time.

Change-Id: I61e8784b1c455d629f1299207fc4fc7e4a134814
Signed-off-by: Peter Portante <peter.portante@redhat.com>
This commit is contained in:
Peter Portante 2013-09-19 16:56:07 -04:00
parent 6cd32eb3a9
commit d4e02a2e8b
2 changed files with 28 additions and 19 deletions

View File

@ -131,7 +131,7 @@ class ProxyLoggingMiddleware(object):
return value return value
def log_request(self, req, status_int, bytes_received, bytes_sent, def log_request(self, req, status_int, bytes_received, bytes_sent,
request_time): start_time, end_time):
""" """
Log a request. Log a request.
@ -139,7 +139,8 @@ class ProxyLoggingMiddleware(object):
:param status_int: integer code for the response status :param status_int: integer code for the response status
:param bytes_received: bytes successfully read from the request body :param bytes_received: bytes successfully read from the request body
:param bytes_sent: bytes yielded to the WSGI server :param bytes_sent: bytes yielded to the WSGI server
:param request_time: time taken to satisfy the request, in seconds :param start_time: timestamp request started
:param end_time: timestamp request completed
""" """
if self.req_already_logged(req): if self.req_already_logged(req):
return return
@ -152,12 +153,17 @@ class ProxyLoggingMiddleware(object):
logged_headers = '\n'.join('%s: %s' % (k, v) logged_headers = '\n'.join('%s: %s' % (k, v)
for k, v in req.headers.items()) for k, v in req.headers.items())
method = self.method_from_req(req) method = self.method_from_req(req)
end_gmtime_str = time.strftime('%d/%b/%Y/%H/%M/%S',
time.gmtime(end_time))
duration_time_str = "%.4f" % (end_time - start_time)
start_time_str = "%.9f" % start_time
end_time_str = "%.9f" % end_time
self.access_logger.info(' '.join( self.access_logger.info(' '.join(
quote(str(x) if x else '-', QUOTE_SAFE) quote(str(x) if x else '-', QUOTE_SAFE)
for x in ( for x in (
get_remote_client(req), get_remote_client(req),
req.remote_addr, req.remote_addr,
time.strftime('%d/%b/%Y/%H/%M/%S', time.gmtime()), end_gmtime_str,
method, method,
the_request, the_request,
req.environ.get('SERVER_PROTOCOL'), req.environ.get('SERVER_PROTOCOL'),
@ -170,9 +176,11 @@ class ProxyLoggingMiddleware(object):
req.headers.get('etag', None), req.headers.get('etag', None),
req.environ.get('swift.trans_id'), req.environ.get('swift.trans_id'),
logged_headers, logged_headers,
'%.4f' % request_time, duration_time_str,
req.environ.get('swift.source'), req.environ.get('swift.source'),
','.join(req.environ.get('swift.log_info') or ''), ','.join(req.environ.get('swift.log_info') or ''),
start_time_str,
end_time_str
))) )))
self.mark_req_logged(req) self.mark_req_logged(req)
# Log timing and bytes-transfered data to StatsD # Log timing and bytes-transfered data to StatsD
@ -181,7 +189,7 @@ class ProxyLoggingMiddleware(object):
# down (egregious errors will get logged by the proxy server itself). # down (egregious errors will get logged by the proxy server itself).
if metric_name: if metric_name:
self.access_logger.timing(metric_name + '.timing', self.access_logger.timing(metric_name + '.timing',
request_time * 1000) (end_time - start_time) * 1000)
self.access_logger.update_stats(metric_name + '.xfer', self.access_logger.update_stats(metric_name + '.xfer',
bytes_received + bytes_sent) bytes_received + bytes_sent)
@ -263,7 +271,7 @@ class ProxyLoggingMiddleware(object):
status_int = status_int_for_logging(client_disconnect) status_int = status_int_for_logging(client_disconnect)
self.log_request( self.log_request(
req, status_int, input_proxy.bytes_received, bytes_sent, req, status_int, input_proxy.bytes_received, bytes_sent,
time.time() - start_time) start_time, time.time())
try: try:
iterable = self.app(env, my_start_response) iterable = self.app(env, my_start_response)
@ -271,8 +279,8 @@ class ProxyLoggingMiddleware(object):
req = Request(env) req = Request(env)
status_int = status_int_for_logging(start_status=500) status_int = status_int_for_logging(start_status=500)
self.log_request( self.log_request(
req, status_int, input_proxy.bytes_received, 0, req, status_int, input_proxy.bytes_received, 0, start_time,
time.time() - start_time) time.time())
raise raise
else: else:
return iter_response(iterable) return iter_response(iterable)

View File

@ -14,7 +14,6 @@
# limitations under the License. # limitations under the License.
import unittest import unittest
import time
from urllib import unquote from urllib import unquote
import cStringIO as StringIO import cStringIO as StringIO
from logging.handlers import SysLogHandler from logging.handlers import SysLogHandler
@ -152,7 +151,8 @@ class TestProxyLogging(unittest.TestCase):
app = proxy_logging.ProxyLoggingMiddleware(FakeApp(), {}) app = proxy_logging.ProxyLoggingMiddleware(FakeApp(), {})
app.access_logger = FakeLogger() app.access_logger = FakeLogger()
req = Request.blank(bad_path, environ={'REQUEST_METHOD': 'GET'}) req = Request.blank(bad_path, environ={'REQUEST_METHOD': 'GET'})
app.log_request(req, 123, 7, 13, 2.71828182846) now = 10000.0
app.log_request(req, 123, 7, 13, now, now + 2.71828182846)
self.assertEqual([], app.access_logger.log_dict['timing']) self.assertEqual([], app.access_logger.log_dict['timing'])
self.assertEqual([], app.access_logger.log_dict['update_stats']) self.assertEqual([], app.access_logger.log_dict['update_stats'])
@ -254,7 +254,8 @@ class TestProxyLogging(unittest.TestCase):
app = proxy_logging.ProxyLoggingMiddleware(FakeApp(), {}) app = proxy_logging.ProxyLoggingMiddleware(FakeApp(), {})
app.access_logger = FakeLogger() app.access_logger = FakeLogger()
req = Request.blank('/v1/a/', environ={'REQUEST_METHOD': method}) req = Request.blank('/v1/a/', environ={'REQUEST_METHOD': method})
app.log_request(req, 299, 11, 3, 1.17) now = 10000.0
app.log_request(req, 299, 11, 3, now, now + 1.17)
self.assertTiming('account.%s.299.timing' % exp_method, app, self.assertTiming('account.%s.299.timing' % exp_method, app,
exp_timing=1.17 * 1000) exp_timing=1.17 * 1000)
self.assertUpdateStats('account.%s.299.xfer' % exp_method, self.assertUpdateStats('account.%s.299.xfer' % exp_method,
@ -280,7 +281,8 @@ class TestProxyLogging(unittest.TestCase):
app.access_logger = FakeLogger() app.access_logger = FakeLogger()
req = Request.blank('/v1/a/c', req = Request.blank('/v1/a/c',
environ={'REQUEST_METHOD': method}) environ={'REQUEST_METHOD': method})
app.log_request(req, 911, 4, 43, 1.01) now = 10000.0
app.log_request(req, 911, 4, 43, now, now + 1.01)
self.assertTiming('container.%s.911.timing' % exp_method, app, self.assertTiming('container.%s.911.timing' % exp_method, app,
exp_timing=1.01 * 1000) exp_timing=1.01 * 1000)
self.assertUpdateStats('container.%s.911.xfer' % exp_method, self.assertUpdateStats('container.%s.911.xfer' % exp_method,
@ -706,16 +708,13 @@ class TestProxyLogging(unittest.TestCase):
app = proxy_logging.ProxyLoggingMiddleware(FakeApp(), {}) app = proxy_logging.ProxyLoggingMiddleware(FakeApp(), {})
app.access_logger = FakeLogger() app.access_logger = FakeLogger()
req = Request.blank('/', environ={'REQUEST_METHOD': 'GET'}) req = Request.blank('/', environ={'REQUEST_METHOD': 'GET'})
with mock.patch.multiple('time', with mock.patch('time.time',
time=mock.MagicMock( mock.MagicMock(
side_effect=[10000000.0, 10000001.0]), side_effect=[10000000.0, 10000001.0])):
gmtime=mock.MagicMock(
side_effect=[
time.gmtime(10000001.00001)])):
resp = app(req.environ, start_response) resp = app(req.environ, start_response)
resp_body = ''.join(resp) resp_body = ''.join(resp)
log_parts = self._log_parts(app) log_parts = self._log_parts(app)
self.assertEquals(len(log_parts), 18) self.assertEquals(len(log_parts), 20)
self.assertEquals(log_parts[0], '-') self.assertEquals(log_parts[0], '-')
self.assertEquals(log_parts[1], '-') self.assertEquals(log_parts[1], '-')
self.assertEquals(log_parts[2], '26/Apr/1970/17/46/41') self.assertEquals(log_parts[2], '26/Apr/1970/17/46/41')
@ -735,6 +734,8 @@ class TestProxyLogging(unittest.TestCase):
self.assertEquals(log_parts[15], '1.0000') self.assertEquals(log_parts[15], '1.0000')
self.assertEquals(log_parts[16], '-') self.assertEquals(log_parts[16], '-')
self.assertEquals(log_parts[17], '-') self.assertEquals(log_parts[17], '-')
self.assertEquals(log_parts[18], '10000000.000000000')
self.assertEquals(log_parts[19], '10000001.000000000')
if __name__ == '__main__': if __name__ == '__main__':