Merge "Allow passing logger object to request"

This commit is contained in:
Jenkins
2015-03-18 21:35:11 +00:00
committed by Gerrit Code Review
3 changed files with 93 additions and 21 deletions

View File

@@ -40,13 +40,16 @@ class Adapter(object):
be attempted for connection errors.
Default None - use session default which
is don't retry.
:param logger: A logging object to use for requests that pass through this
adapter.
:type logger: logging.Logger
"""
@utils.positional()
def __init__(self, session, service_type=None, service_name=None,
interface=None, region_name=None, endpoint_override=None,
version=None, auth=None, user_agent=None,
connect_retries=None):
connect_retries=None, logger=None):
# NOTE(jamielennox): when adding new parameters to adapter please also
# add them to the adapter call in httpclient.HTTPClient.__init__
self.session = session
@@ -59,6 +62,7 @@ class Adapter(object):
self.user_agent = user_agent
self.auth = auth
self.connect_retries = connect_retries
self.logger = logger
def _set_endpoint_filter_kwargs(self, kwargs):
if self.service_type:
@@ -85,6 +89,8 @@ class Adapter(object):
kwargs.setdefault('user_agent', self.user_agent)
if self.connect_retries is not None:
kwargs.setdefault('connect_retries', self.connect_retries)
if self.logger:
kwargs.setdefault('logger', self.logger)
return self.session.request(url, method, **kwargs)

View File

@@ -162,8 +162,8 @@ class Session(object):
@utils.positional()
def _http_log_request(self, url, method=None, data=None,
json=None, headers=None):
if not _logger.isEnabledFor(logging.DEBUG):
json=None, headers=None, logger=_logger):
if not logger.isEnabledFor(logging.DEBUG):
# NOTE(morganfainberg): This whole debug section is expensive,
# there is no need to do the work if we're not going to emit a
# debug log.
@@ -192,12 +192,13 @@ class Session(object):
if data:
string_parts.append("-d '%s'" % data)
_logger.debug(' '.join(string_parts))
logger.debug(' '.join(string_parts))
@utils.positional()
def _http_log_response(self, response=None, json=None,
status_code=None, headers=None, text=None):
if not _logger.isEnabledFor(logging.DEBUG):
status_code=None, headers=None, text=None,
logger=_logger):
if not logger.isEnabledFor(logging.DEBUG):
return
if response:
@@ -220,14 +221,15 @@ class Session(object):
if text:
string_parts.append('\nRESP BODY: %s\n' % text)
_logger.debug(' '.join(string_parts))
logger.debug(' '.join(string_parts))
@utils.positional(enforcement=utils.positional.WARN)
def request(self, url, method, json=None, original_ip=None,
user_agent=None, redirect=None, authenticated=None,
endpoint_filter=None, auth=None, requests_auth=None,
raise_exc=True, allow_reauth=True, log=True,
endpoint_override=None, connect_retries=0, **kwargs):
endpoint_override=None, connect_retries=0, logger=_logger,
**kwargs):
"""Send an HTTP request with the specified characteristics.
Wrapper around `requests.Session.request` to handle tasks such as
@@ -286,6 +288,10 @@ class Session(object):
response. (optional, default True)
:param bool log: If True then log the request and response data to the
debug log. (optional, default True)
:param logger: The logger object to use to log request and responses.
If not provided the keystoneclient.session default
logger will be used.
:type logger: logging.Logger
:param kwargs: any other parameter that can be passed to
requests.Session.request (such as `headers`). Except:
'data' will be overwritten by the data in 'json' param.
@@ -361,7 +367,8 @@ class Session(object):
if log:
self._http_log_request(url, method=method,
data=kwargs.get('data'),
headers=headers)
headers=headers,
logger=logger)
# Force disable requests redirect handling. We will manage this below.
kwargs['allow_redirects'] = False
@@ -370,7 +377,8 @@ class Session(object):
redirect = self.redirect
send = functools.partial(self._send_request,
url, method, redirect, log, connect_retries)
url, method, redirect, log, logger,
connect_retries)
resp = send(**kwargs)
# handle getting a 401 Unauthorized response by invalidating the plugin
@@ -384,14 +392,14 @@ class Session(object):
resp = send(**kwargs)
if raise_exc and resp.status_code >= 400:
_logger.debug('Request returned failure status: %s',
resp.status_code)
logger.debug('Request returned failure status: %s',
resp.status_code)
raise exceptions.from_response(resp, method, url)
return resp
def _send_request(self, url, method, redirect, log, connect_retries,
connect_retry_delay=0.5, **kwargs):
def _send_request(self, url, method, redirect, log, logger,
connect_retries, connect_retry_delay=0.5, **kwargs):
# NOTE(jamielennox): We handle redirection manually because the
# requests lib follows some browser patterns where it will redirect
# POSTs as GETs for certain statuses which is not want we want for an
@@ -419,18 +427,18 @@ class Session(object):
if connect_retries <= 0:
raise
_logger.info(_LI('Failure: %(e)s. Retrying in %(delay).1fs.'),
{'e': e, 'delay': connect_retry_delay})
logger.info(_LI('Failure: %(e)s. Retrying in %(delay).1fs.'),
{'e': e, 'delay': connect_retry_delay})
time.sleep(connect_retry_delay)
return self._send_request(
url, method, redirect, log,
url, method, redirect, log, logger,
connect_retries=connect_retries - 1,
connect_retry_delay=connect_retry_delay * 2,
**kwargs)
if log:
self._http_log_response(response=resp)
self._http_log_response(response=resp, logger=logger)
if resp.status_code in self._REDIRECT_STATUSES:
# be careful here in python True == 1 and False == 0
@@ -446,13 +454,13 @@ class Session(object):
try:
location = resp.headers['location']
except KeyError:
_logger.warn(_LW("Failed to redirect request to %s as new "
"location was not provided."), resp.url)
logger.warn(_LW("Failed to redirect request to %s as new "
"location was not provided."), resp.url)
else:
# NOTE(jamielennox): We don't pass through connect_retry_delay.
# This request actually worked so we can reset the delay count.
new_resp = self._send_request(
location, method, redirect, log,
location, method, redirect, log, logger,
connect_retries=connect_retries,
**kwargs)

View File

@@ -12,6 +12,7 @@
import argparse
import itertools
import logging
import uuid
import mock
@@ -608,6 +609,34 @@ class SessionAuthTests(utils.TestCase):
self.assertEqual(auth.TEST_USER_ID, sess.get_user_id())
self.assertEqual(auth.TEST_PROJECT_ID, sess.get_project_id())
def test_logger_object_passed(self):
logger = logging.getLogger(uuid.uuid4().hex)
logger.setLevel(logging.DEBUG)
logger.propagate = False
io = six.StringIO()
handler = logging.StreamHandler(io)
logger.addHandler(handler)
auth = AuthPlugin()
sess = client_session.Session(auth=auth)
response = uuid.uuid4().hex
self.stub_url('GET',
text=response,
headers={'Content-Type': 'text/html'})
resp = sess.get(self.TEST_URL, logger=logger)
self.assertEqual(response, resp.text)
output = io.getvalue()
self.assertIn(self.TEST_URL, output)
self.assertIn(response, output)
self.assertNotIn(self.TEST_URL, self.logger.output)
self.assertNotIn(response, self.logger.output)
class AdapterTest(utils.TestCase):
@@ -771,6 +800,35 @@ class AdapterTest(utils.TestCase):
self.assertEqual(auth.TEST_USER_ID, adpt.get_user_id())
self.assertEqual(auth.TEST_PROJECT_ID, adpt.get_project_id())
def test_logger_object_passed(self):
logger = logging.getLogger(uuid.uuid4().hex)
logger.setLevel(logging.DEBUG)
logger.propagate = False
io = six.StringIO()
handler = logging.StreamHandler(io)
logger.addHandler(handler)
auth = AuthPlugin()
sess = client_session.Session(auth=auth)
adpt = adapter.Adapter(sess, auth=auth, logger=logger)
response = uuid.uuid4().hex
self.stub_url('GET', text=response,
headers={'Content-Type': 'text/html'})
resp = adpt.get(self.TEST_URL, logger=logger)
self.assertEqual(response, resp.text)
output = io.getvalue()
self.assertIn(self.TEST_URL, output)
self.assertIn(response, output)
self.assertNotIn(self.TEST_URL, self.logger.output)
self.assertNotIn(response, self.logger.output)
class ConfLoadingTests(utils.TestCase):