Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1 from sprockets/implement-context-filter
Implement context filter
- Loading branch information
Showing
15 changed files
with
205 additions
and
59 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,4 +3,4 @@ Version History | |
|
||
Next Release | ||
------------ | ||
- implement greatness | ||
- Added :class:`sprockets.logging.ContextFilter` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
sphinx-rtd-theme | ||
sphinxcontrib-httpdomain |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
import logging | ||
import sys | ||
|
||
import sprockets.logging | ||
|
||
|
||
formatter = logging.Formatter('%(levelname)s %(message)s {%(context)s}') | ||
handler = logging.StreamHandler(sys.stdout) | ||
handler.setFormatter(formatter) | ||
handler.addFilter(sprockets.logging.ContextFilter(properties=['context'])) | ||
logging.Logger.root.addHandler(handler) | ||
logging.Logger.root.setLevel(logging.DEBUG) | ||
|
||
# Outputs: INFO Hi there {None} | ||
logging.info('Hi there') | ||
|
||
# Outputs: INFO No KeyError {bah} | ||
logging.info('No KeyError', extra={'context': 'bah'}) | ||
|
||
# Outputs: INFO Now with context! {foo} | ||
adapted = logging.LoggerAdapter(logging.Logger.root, extra={'context': 'foo'}) | ||
adapted.info('Now with context!') |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
import logging.config | ||
import signal | ||
import uuid | ||
|
||
from tornado import ioloop, web | ||
import sprockets.logging | ||
|
||
|
||
LOG_CONFIG = { | ||
'version': 1, | ||
'handlers': { | ||
'console': { | ||
'class': 'logging.StreamHandler', | ||
'stream': 'ext://sys.stdout', | ||
'formatter': 'simple', | ||
'filters': ['context'], | ||
}, | ||
}, | ||
'formatters': { | ||
'simple': { | ||
'class': 'logging.Formatter', | ||
'format': '%(levelname)s %(name)s: %(message)s [%(context)s]', | ||
}, | ||
}, | ||
'filters': { | ||
'context': { | ||
'()': 'sprockets.logging.ContextFilter', | ||
'properties': ['context'], | ||
}, | ||
}, | ||
'loggers': { | ||
'tornado': { | ||
'level': 'DEBUG', | ||
}, | ||
}, | ||
'root': { | ||
'handlers': ['console'], | ||
'level': 'DEBUG', | ||
}, | ||
'incremental': False, | ||
} | ||
|
||
|
||
class RequestHandler(web.RequestHandler): | ||
|
||
def __init__(self, *args, **kwargs): | ||
self.parent_log = kwargs.pop('parent_log') | ||
super(RequestHandler, self).__init__(*args, **kwargs) | ||
|
||
def prepare(self): | ||
uniq_id = self.request.headers.get('X-UniqID', uuid.uuid4().hex) | ||
self.logger = logging.LoggerAdapter( | ||
self.parent_log.getChild('RequestHandler'), | ||
extra={'context': uniq_id}) | ||
|
||
def get(self, object_id): | ||
self.logger.debug('fetchin %s', object_id) | ||
self.set_status(200) | ||
return self.finish() | ||
|
||
def sig_handler(signo, frame): | ||
logging.info('caught signal %d, stopping IO loop', signo) | ||
iol = ioloop.IOLoop.instance() | ||
iol.add_callback_from_signal(iol.stop) | ||
|
||
if __name__ == '__main__': | ||
logging.config.dictConfig(LOG_CONFIG) | ||
logger = logging.getLogger('app') | ||
app = web.Application([ | ||
web.url('/(?P<object_id>\w+)', RequestHandler, | ||
kwargs={'parent_log': logger}), | ||
]) | ||
app.listen(8000) | ||
signal.signal(signal.SIGINT, sig_handler) | ||
signal.signal(signal.SIGTERM, sig_handler) | ||
ioloop.IOLoop.instance().start() | ||
logger.info('IO loop stopped, exiting') |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,39 @@ | ||
""" | ||
Make good log output easier. | ||
- :class:`ContextFilter` adds fixed properties to a log record | ||
""" | ||
from __future__ import absolute_import | ||
|
||
import logging | ||
|
||
|
||
version_info = (0, 0, 0) | ||
__version__ = '.'.join(str(v) for v in version_info) | ||
|
||
|
||
class ContextFilter(logging.Filter): | ||
""" | ||
Ensures that properties exist on a LogRecord. | ||
:param list|None properties: optional list of properties that | ||
will be added to LogRecord instances if they are missing | ||
This filter implementation will ensure that a set of properties | ||
exists on every log record which means that you can always refer | ||
to custom properties in a format string. Without this, referring | ||
to a property that is not explicitly passed in will result in an | ||
ugly ``KeyError`` exception. | ||
""" | ||
|
||
def __init__(self, name='', properties=None): | ||
logging.Filter.__init__(self, name) | ||
self.properties = list(properties) if properties else [] | ||
|
||
def filter(self, record): | ||
for property_name in self.properties: | ||
if not hasattr(record, property_name): | ||
setattr(record, property_name, None) | ||
return True |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
import logging | ||
import uuid | ||
import unittest | ||
|
||
import sprockets.logging | ||
|
||
|
||
class Prototype(object): | ||
pass | ||
|
||
|
||
class RecordingHandler(logging.FileHandler): | ||
def __init__(self): | ||
logging.FileHandler.__init__(self, filename='/dev/null') | ||
self.log_lines = [] | ||
|
||
def format(self, record): | ||
log_line = logging.FileHandler.format(self, record) | ||
self.log_lines.append(log_line) | ||
return log_line | ||
|
||
|
||
class ContextFilterTests(unittest.TestCase): | ||
|
||
def setUp(self): | ||
super(ContextFilterTests, self).setUp() | ||
self.logger = logging.getLogger(uuid.uuid4().hex) | ||
self.handler = RecordingHandler() | ||
self.logger.addHandler(self.handler) | ||
|
||
def test_that_filter_blocks_key_errors(self): | ||
formatter = logging.Formatter('%(message)s [%(context)s]') | ||
self.handler.setFormatter(formatter) | ||
self.handler.addFilter(sprockets.logging.ContextFilter( | ||
properties=['context'])) | ||
self.logger.info('hi there') | ||
|
||
def test_that_filter_does_not_overwrite_extras(self): | ||
formatter = logging.Formatter('%(message)s [%(context)s]') | ||
self.handler.setFormatter(formatter) | ||
self.handler.addFilter(sprockets.logging.ContextFilter( | ||
properties=['context'])) | ||
self.logger.info('hi there', extra={'context': 'foo'}) | ||
self.assertEqual(self.handler.log_lines[-1], 'hi there [foo]') |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,10 @@ | ||
[tox] | ||
envlist = py27,py34 | ||
indexserver = | ||
default = https://pypi.python.org/simple | ||
default = https://pypi.python.org/simple | ||
toxworkdir = build/tox | ||
skip_missing_interpreters = true | ||
|
||
[testenv] | ||
commands = nosetests [] | ||
deps = -rtest-requirements.txt | ||
deps = nose |