Permalink
Browse files

initial interface for a request hook, sample interface and example fo…

…r logging
  • Loading branch information...
1 parent 221085e commit 46abff4eac6b47c1c18559674467829c8b637381 @dkavanagh dkavanagh committed Feb 24, 2014
Showing with 58 additions and 0 deletions.
  1. +10 −0 boto/connection.py
  2. +39 −0 boto/requestlog.py
  3. +9 −0 boto/utils.py
View
@@ -45,6 +45,7 @@
from __future__ import with_statement
import base64
+from datetime import datetime
import errno
import httplib
import os
@@ -568,6 +569,7 @@ def __init__(self, host, aws_access_key_id=None,
host, config, self.provider, self._required_auth_capability())
if getattr(self, 'AuthServiceName', None) is not None:
self.auth_service_name = self.AuthServiceName
+ self.request_hook = None
def __repr__(self):
return '%s:%s' % (self.__class__.__name__, self.host)
@@ -860,6 +862,9 @@ def set_host_header(self, request):
except AttributeError:
request.headers['Host'] = self.host.split(':', 1)[0]
+ def set_request_hook(self, hook):
+ self.request_hook = hook
+
def _mexe(self, request, sender=None, override_num_retries=None,
retry_handler=None):
"""
@@ -902,6 +907,7 @@ def _mexe(self, request, sender=None, override_num_retries=None,
if 's3' not in self._required_auth_capability():
if not getattr(self, 'anon', False):
self.set_host_header(request)
+ request.start_time = datetime.now()
if callable(sender):
response = sender(connection, request.method, request.path,
request.body, request.headers)
@@ -942,6 +948,8 @@ def _mexe(self, request, sender=None, override_num_retries=None,
else:
self.put_http_connection(request.host, request.port,
self.is_secure, connection)
+ if self.request_hook is not None:
+ self.request_hook.handle_request_data(request, response)
return response
else:
scheme, request.host, request.path, \
@@ -982,6 +990,8 @@ def _mexe(self, request, sender=None, override_num_retries=None,
# and stil haven't succeeded. So, if we have a response object,
# use it to raise an exception.
# Otherwise, raise the exception that must have already happened.
+ if self.request_hook is not None:
+ self.request_hook.handle_request_data(request, response, error=True)
if response:
raise BotoServerError(response.status, response.reason, body)
elif e:
View
@@ -0,0 +1,39 @@
+
+from datetime import datetime
+from threading import Thread
+import Queue
+
+from boto.utils import RequestHook
+
+class RequestLogger(RequestHook):
+ """
+ This class implements a request logger that uses a single thread to
+ write to a log file.
+ """
+ def __init__(self):
+ self.request_log_file = open('/tmp/request_log.csv', 'w')
+ self.request_log_queue = Queue.Queue(100)
+ Thread(target=self._request_log_worker).start()
+
+
+ def handle_request_data(self, request, response, error=False):
+ len = 0 if error else response.getheader('Content-Length')
+ now = datetime.now()
+ time = now.strftime('%Y-%m-%d %H:%M:%S')
+ td = (now - request.start_time)
+ duration = (td.microseconds + long(td.seconds + td.days*24*3600) * 1e6) / 1e6
+
+ # write output including timestamp, status code, response time, response size, request action
+ self.request_log_queue.put("'%s', '%s', '%s', '%s', '%s'\n" % (time, response.status, duration, len, request.params['Action']))
+
+
+ def _request_log_worker(self):
+ while True:
+ try:
+ item = self.request_log_queue.get(True)
+ self.request_log_file.write(item)
+ self.request_log_file.flush()
+ self.request_log_queue.task_done()
+ except:
+ import traceback; traceback.print_exc(file=sys.stdout)
+
View
@@ -1025,3 +1025,12 @@ def merge_headers_by_name(name, headers):
matching_headers = find_matching_headers(name, headers)
return ','.join(str(headers[h]) for h in matching_headers
if headers[h] is not None)
+
+class RequestHook(object):
+ """
+ This can be extended and supplied to the connection object
+ to gain access to request and response object after the request completes.
+ One use for this would be to implement some specific request logging.
+ """
+ def handle_request_data(self, request, response, error=False):
+ pass

0 comments on commit 46abff4

Please sign in to comment.