mirror of
https://github.com/sprockets/sprockets.logging.git
synced 2024-11-24 11:19:51 +00:00
commit
16a57b6484
4 changed files with 81 additions and 7 deletions
|
@ -1,6 +1,10 @@
|
||||||
Version History
|
Version History
|
||||||
===============
|
===============
|
||||||
|
|
||||||
|
`1.3.0`_ Aug 28, 2015
|
||||||
|
---------------------
|
||||||
|
- Add the traceback and environment if set
|
||||||
|
|
||||||
`1.2.1`_ Jun 24, 2015
|
`1.2.1`_ Jun 24, 2015
|
||||||
---------------------
|
---------------------
|
||||||
- Fix a potential ``KeyError`` when a HTTP request object is not present.
|
- Fix a potential ``KeyError`` when a HTTP request object is not present.
|
||||||
|
|
|
@ -13,14 +13,16 @@ from __future__ import absolute_import
|
||||||
from logging import config
|
from logging import config
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
import traceback
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from tornado import log
|
from tornado import log
|
||||||
except ImportError:
|
except ImportError:
|
||||||
log = None
|
log = None
|
||||||
|
|
||||||
version_info = (1, 2, 1)
|
version_info = (1, 3, 0)
|
||||||
__version__ = '.'.join(str(v) for v in version_info)
|
__version__ = '.'.join(str(v) for v in version_info)
|
||||||
|
|
||||||
# Shortcut methods and constants to avoid needing to import logging directly
|
# Shortcut methods and constants to avoid needing to import logging directly
|
||||||
|
@ -65,6 +67,28 @@ class JSONRequestFormatter(logging.Formatter):
|
||||||
the log data as JSON.
|
the log data as JSON.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
def extract_exc_record(self, typ, val, tb):
|
||||||
|
"""Create a JSON representation of the traceback given the records
|
||||||
|
exc_info
|
||||||
|
|
||||||
|
:param `Exception` typ: Exception type of the exception being handled
|
||||||
|
:param `Exception` instance val: instance of the Exception class
|
||||||
|
:param `traceback` tb: traceback object with the call stack
|
||||||
|
|
||||||
|
:rtype: dict
|
||||||
|
|
||||||
|
"""
|
||||||
|
exc_record = {'type': typ.__name__,
|
||||||
|
'message': str(val),
|
||||||
|
'stack': []}
|
||||||
|
for file_name, line_no, func_name, txt in traceback.extract_tb(tb):
|
||||||
|
exc_record['stack'].append({'file': file_name,
|
||||||
|
'line': str(line_no),
|
||||||
|
'func': func_name,
|
||||||
|
'text': txt})
|
||||||
|
return exc_record
|
||||||
|
|
||||||
def format(self, record):
|
def format(self, record):
|
||||||
"""Return the log data as JSON
|
"""Return the log data as JSON
|
||||||
|
|
||||||
|
@ -72,6 +96,12 @@ class JSONRequestFormatter(logging.Formatter):
|
||||||
:rtype: str
|
:rtype: str
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
if hasattr(record, 'exc_info'):
|
||||||
|
try:
|
||||||
|
traceback = self.extract_exc_record(*record.exc_info)
|
||||||
|
except:
|
||||||
|
traceback = None
|
||||||
|
|
||||||
output = {'name': record.name,
|
output = {'name': record.name,
|
||||||
'module': record.module,
|
'module': record.module,
|
||||||
'message': record.msg % record.args,
|
'message': record.msg % record.args,
|
||||||
|
@ -81,7 +111,8 @@ class JSONRequestFormatter(logging.Formatter):
|
||||||
'timestamp': self.formatTime(record),
|
'timestamp': self.formatTime(record),
|
||||||
'thread': record.threadName,
|
'thread': record.threadName,
|
||||||
'file': record.filename,
|
'file': record.filename,
|
||||||
'request': record.args}
|
'request': record.args,
|
||||||
|
'traceback': traceback}
|
||||||
for key, value in list(output.items()):
|
for key, value in list(output.items()):
|
||||||
if not value:
|
if not value:
|
||||||
del output[key]
|
del output[key]
|
||||||
|
@ -119,7 +150,8 @@ def tornado_log_function(handler):
|
||||||
'protocol': handler.request.protocol,
|
'protocol': handler.request.protocol,
|
||||||
'query_args': handler.request.query_arguments,
|
'query_args': handler.request.query_arguments,
|
||||||
'remote_ip': handler.request.remote_ip,
|
'remote_ip': handler.request.remote_ip,
|
||||||
'status_code': status_code})
|
'status_code': status_code,
|
||||||
|
'environment': os.environ.get('ENVIRONMENT')})
|
||||||
|
|
||||||
|
|
||||||
def currentframe():
|
def currentframe():
|
||||||
|
|
41
tests.py
41
tests.py
|
@ -1,5 +1,6 @@
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
|
import os
|
||||||
import random
|
import random
|
||||||
import unittest
|
import unittest
|
||||||
import uuid
|
import uuid
|
||||||
|
@ -7,9 +8,10 @@ import uuid
|
||||||
import mock
|
import mock
|
||||||
|
|
||||||
import sprockets.logging
|
import sprockets.logging
|
||||||
|
from tornado import web, testing
|
||||||
|
|
||||||
LOGGER = logging.getLogger(__name__)
|
LOGGER = logging.getLogger(__name__)
|
||||||
|
os.environ['ENVIRONMENT'] = 'testing'
|
||||||
|
|
||||||
class Prototype(object):
|
class Prototype(object):
|
||||||
pass
|
pass
|
||||||
|
@ -91,9 +93,11 @@ class TornadoLogFunctionTestCase(unittest.TestCase):
|
||||||
'protocol': handler.request.protocol,
|
'protocol': handler.request.protocol,
|
||||||
'query_args': handler.request.query_arguments,
|
'query_args': handler.request.query_arguments,
|
||||||
'remote_ip': handler.request.remote_ip,
|
'remote_ip': handler.request.remote_ip,
|
||||||
'status_code': handler.status_code})
|
'status_code': handler.status_code,
|
||||||
|
'environment': os.environ['ENVIRONMENT']})
|
||||||
sprockets.logging.tornado_log_function(handler)
|
sprockets.logging.tornado_log_function(handler)
|
||||||
access_log.assertCalledOnceWith(expectation)
|
access_log.info.assert_called_once_with(*expectation)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class JSONRequestHandlerTestCase(unittest.TestCase):
|
class JSONRequestHandlerTestCase(unittest.TestCase):
|
||||||
|
@ -135,3 +139,34 @@ class JSONRequestHandlerTestCase(unittest.TestCase):
|
||||||
value = json.loads(result)
|
value = json.loads(result)
|
||||||
for key in keys:
|
for key in keys:
|
||||||
self.assertIn(key, value)
|
self.assertIn(key, value)
|
||||||
|
|
||||||
|
|
||||||
|
class JSONRequestFormatterTestCase(testing.AsyncHTTPTestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(JSONRequestFormatterTestCase, self).setUp()
|
||||||
|
self.recorder = RecordingHandler()
|
||||||
|
self.formatter = sprockets.logging.JSONRequestFormatter()
|
||||||
|
self.recorder.setFormatter(self.formatter)
|
||||||
|
web.app_log.addHandler(self.recorder)
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
super(JSONRequestFormatterTestCase, self).tearDown()
|
||||||
|
web.app_log.removeHandler(self.recorder)
|
||||||
|
|
||||||
|
def get_app(self):
|
||||||
|
class JustFail(web.RequestHandler):
|
||||||
|
def get(self):
|
||||||
|
raise RuntimeError('something busted')
|
||||||
|
|
||||||
|
return web.Application([web.url('/', JustFail)])
|
||||||
|
|
||||||
|
def test_that_things_happen(self):
|
||||||
|
self.fetch('/')
|
||||||
|
self.assertEqual(len(self.recorder.log_lines), 1)
|
||||||
|
|
||||||
|
failure_info = json.loads(self.recorder.log_lines[0])
|
||||||
|
self.assertEqual(failure_info['traceback']['type'], 'RuntimeError')
|
||||||
|
self.assertEqual(failure_info['traceback']['message'],
|
||||||
|
'something busted')
|
||||||
|
self.assertEqual(len(failure_info['traceback']['stack']), 2)
|
||||||
|
|
5
tox.ini
5
tox.ini
|
@ -7,4 +7,7 @@ skip_missing_interpreters = true
|
||||||
|
|
||||||
[testenv]
|
[testenv]
|
||||||
commands = nosetests []
|
commands = nosetests []
|
||||||
deps = nose
|
deps =
|
||||||
|
nose
|
||||||
|
mock
|
||||||
|
tornado
|
||||||
|
|
Loading…
Reference in a new issue