Add LoggingMixin

This commit is contained in:
Dave Shawley 2015-09-23 08:45:39 -04:00
parent fe62b027e6
commit f1034a37e8
5 changed files with 82 additions and 2 deletions
HISTORY.rst
docs
sprockets/mixins/correlation
tests.py

View file

@ -5,10 +5,11 @@ Version History
~~~~~~~~
- Make ``sprockets.mixins.correlation.HandlerMixin.prepare`` async
- Add ``sprockets.mixins.correlation.CorrelationAdapter``
- Add ``sprockets.mixins.correlation.LoggingMixin``
`1.0.1`_ (31-Mar-2015)
~~~~~~~~~~~~~~~~~~~~~~
- Adds ``sprockets.mixins.correlation.HandlerMixin``
- Add ``sprockets.mixins.correlation.HandlerMixin``
.. _`1.1.0`: https://github.com/sprockets/sprockets.mixins.correlation/compare/1.0.1...1.1.0

View file

@ -9,6 +9,9 @@ API Documentation
.. autoclass:: sprockets.mixins.correlation.CorrelationAdapter
:members:
.. autoclass:: sprockets.mixins.correlation.LoggingMixin
:members:
Contributing to this Library
----------------------------

View file

@ -1,5 +1,5 @@
try:
from .mixins import CorrelationAdapter, HandlerMixin
from .mixins import CorrelationAdapter, HandlerMixin, LoggingMixin
# the following makes importing the module possible when tornado
# is not installed in the active environment
@ -10,6 +10,9 @@ except ImportError as exc: # pragma no cover
def HandlerMixin(*args, **kwargs):
raise exc
def LoggingMixin(*args, **kwargs):
raise exc
version_info = (1, 1, 0)
__version__ = '.'.join(str(v) for v in version_info[:3])

View file

@ -90,6 +90,41 @@ class HandlerMixin(object):
return self.request.headers.get(name, default)
class LoggingMixin(HandlerMixin):
"""
Add a correlated logger to a tornado.web.RequestHandler.
This mix-in adds a correlation id attribute by extending
:class:`.HandlerMixin` and then adds an adapted logger
attribute that will appends the correlation ID to every
message.
.. attribute:: logger
A :class:`.CorrelationAdapter` instance that appends the
current correlation id to every message if it is set.
If a ``logger`` attribute is present when ``super.initialize``
returns, then it will be adapted. If not, a new logger
is created using :func:`logging.getLogger` with the object's
class name as the logger name.
"""
def initialize(self):
super(LoggingMixin, self).initialize()
logger = getattr(self, 'logger',
logging.getLogger(self.__class__.__name__))
self.logger = CorrelationAdapter(logger)
@gen.coroutine
def prepare(self):
maybe_future = super(LoggingMixin, self).prepare()
if maybe_future:
yield maybe_future
if not self._finished:
self.logger.correlation_id = self.correlation_id
class CorrelationAdapter(logging.LoggerAdapter):
"""
Adapt a :class:`logging.Logger` to include a correlation ID.

View file

@ -1,3 +1,4 @@
import logging
import uuid
from tornado import gen, testing, web
@ -21,6 +22,23 @@ class CorrelatedRequestHandler(correlation.HandlerMixin, AsyncPreparer):
self.write('status {0}'.format(status_code))
class LoggingRequestHandler(correlation.LoggingMixin, web.RequestHandler):
def get(self):
self.logger.info('request made')
self.set_status(204)
class RecordingHandler(logging.Handler):
def __init__(self):
logging.Handler.__init__(self)
self.emitted = []
def emit(self, record):
self.emitted.append((record, self.format(record)))
class CorrelationMixinTests(testing.AsyncHTTPTestCase):
def get_app(self):
@ -44,3 +62,23 @@ class CorrelationMixinTests(testing.AsyncHTTPTestCase):
headers={'Correlation-Id': correlation_id})
response = self.wait()
self.assertEqual(response.headers['correlation-id'], correlation_id)
class LoggingMixinTests(testing.AsyncHTTPTestCase):
def get_app(self):
return web.Application([(r'/log', LoggingRequestHandler)])
def setUp(self):
super(LoggingMixinTests, self).setUp()
self.recorder = RecordingHandler()
logging.getLogger().addHandler(self.recorder)
def tearDown(self):
super(LoggingMixinTests, self).tearDown()
logging.getLogger().removeHandler(self.recorder)
def test_that_correlation_id_is_logged(self):
self.fetch('/log')
record, msg = self.recorder.emitted[0]
self.assertTrue(msg.startswith('request made {CID'))