Skip to content

Commit 6fbe310

Browse files
committed
added tests for formatters
1 parent dba96ae commit 6fbe310

File tree

6 files changed

+225
-16
lines changed

6 files changed

+225
-16
lines changed

frontera/logger/formatters/json.py

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,7 @@
11
from __future__ import absolute_import
22

33
from pythonjsonlogger.jsonlogger import JsonFormatter
4-
5-
import datetime
6-
from json import JSONEncoder
7-
8-
9-
class DateTimeEncoder(JSONEncoder):
10-
def default(self, obj):
11-
if isinstance(obj, datetime.datetime):
12-
return obj.isoformat()
13-
elif isinstance(obj, datetime.date):
14-
return obj.isoformat()
15-
elif isinstance(obj, datetime.timedelta):
16-
return (datetime.datetime.min + obj).time().isoformat()
17-
else:
18-
return super(DateTimeEncoder, self).default(obj)
4+
from frontera.utils.encoders import DateTimeEncoder
195

206

217
class JSONFormatter(JsonFormatter):

requirements/logging.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
colorlog>=2.4.0
2+
python-json-logger>=0.1.5

requirements/tests.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,4 @@ msgpack-python
1010
kafka-python<=0.9.5
1111
pytest-cov
1212
happybase>=1.0.0
13+
-r logging.txt

setup.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454
],
5555
'logging': [
5656
'colorlog>=2.4.0',
57+
'python-json-logger>=0.1.5'
5758
],
5859
'tldextract': [
5960
'tldextract>=1.5.1',
@@ -80,6 +81,8 @@
8081
"scrapy>=0.24",
8182
"tldextract>=1.5.1",
8283
"SQLAlchemy>=1.0.0",
83-
"cachetools"
84+
"cachetools",
85+
"colorlog>=2.4.0",
86+
"python-json-logger>=0.1.5"
8487
]
8588
)

tests/test_formatters.py

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
import unittest
2+
import re
3+
import json
4+
import datetime
5+
6+
from frontera.logger.formatters.text import DETAILED, SHORT
7+
from frontera.logger.formatters.color import ColorFormatter
8+
from frontera.logger.formatters.json import JSONFormatter
9+
from frontera.logger.formatters import (EVENTS,
10+
CONSOLE,
11+
CONSOLE_MANAGER,
12+
CONSOLE_BACKEND,
13+
CONSOLE_DEBUGGING)
14+
from tests.utils import LoggingCaptureMixin, SetupDefaultLoggingMixin
15+
16+
17+
colors = {
18+
'bold_yellow': '\x1b[01;33m',
19+
'green': '\x1b[32m',
20+
'red': '\x1b[31m',
21+
'reset': '\x1b[0m',
22+
'white': '\x1b[37m',
23+
}
24+
25+
26+
class BaseTestFormatters(SetupDefaultLoggingMixin, LoggingCaptureMixin, unittest.TestCase):
27+
def setUp(self):
28+
super(BaseTestFormatters, self).setUp()
29+
self.default_formatter = self.logger.handlers[0].formatter
30+
31+
def tearDown(self):
32+
super(BaseTestFormatters, self).setUp()
33+
self.logger.handlers[0].formatter = self.default_formatter
34+
35+
def setFormatter(self, formatter):
36+
self.logger.handlers[0].setFormatter(formatter)
37+
38+
39+
class TestFormatterText(BaseTestFormatters):
40+
41+
def test_formatter_detailed(self):
42+
self.setFormatter(DETAILED)
43+
self.logger.debug('debug message')
44+
self.assertRegexpMatches(self.logger_output.getvalue(),
45+
r'\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2},\d+ - frontera - DEBUG - debug message\n')
46+
47+
def test_formatter_short(self):
48+
self.setFormatter(SHORT)
49+
self.logger.debug('debug message')
50+
self.assertEqual(self.logger_output.getvalue(), '[frontera] DEBUG: debug message\n')
51+
52+
53+
class TestFormatterColor(BaseTestFormatters):
54+
55+
def test_formatter_color(self):
56+
c = ColorFormatter(
57+
format="%(log_color)s [%(name)s] %(message)s",
58+
log_colors={
59+
"DEBUG": "white",
60+
"INFO": "green",
61+
"ERROR": "red",
62+
},
63+
log_color_field="levelname")
64+
self.setFormatter(c)
65+
self.logger.debug('debug message')
66+
self.logger.info('info message')
67+
self.logger.error('error message')
68+
self.assertEqual(self.logger_output.getvalue(),
69+
'{white} [frontera] debug message{reset}\n'
70+
'{green} [frontera] info message{reset}\n'
71+
'{red} [frontera] error message{reset}\n'.format(white=colors['white'],
72+
green=colors['green'],
73+
red=colors['red'],
74+
reset=colors['reset']))
75+
76+
def test_formatter_color_datefmt(self):
77+
c = ColorFormatter(
78+
format="%(log_color)s %(asctime)s [%(name)s] %(message)s",
79+
log_colors={
80+
"DEBUG": "white",
81+
"INFO": "green",
82+
"ERROR": "red",
83+
},
84+
datefmt='%d-%m-%Y %H:%M:%S',
85+
log_color_field="levelname")
86+
self.setFormatter(c)
87+
self.logger.debug('debug message')
88+
self.assertRegexpMatches(self.logger_output.getvalue(),
89+
'{white} \d{{2}}-\d{{2}}-\d{{4}} \d{{2}}:\d{{2}}:\d{{2}} '
90+
'\\[frontera\\] debug message{reset}\n'.format(
91+
white=re.escape(colors['white']),
92+
reset=re.escape(colors['reset'])))
93+
94+
95+
class TestFormatterJson(BaseTestFormatters):
96+
97+
def setUp(self):
98+
super(TestFormatterJson, self).setUp()
99+
self.setFormatter(JSONFormatter())
100+
101+
def test_formatter_json_log_text(self):
102+
self.logger.debug('debug message')
103+
self.assertEqual(json.loads(self.logger_output.getvalue())['message'], 'debug message')
104+
105+
def test_formatter_json_log_dict(self):
106+
dct_msg = {
107+
'message': 'debug message',
108+
'extra': 'value',
109+
}
110+
self.logger.debug(dct_msg)
111+
json_log = json.loads(self.logger_output.getvalue())
112+
self.assertEqual(json_log.get('message'), 'debug message')
113+
self.assertEqual(json_log.get('extra'), 'value')
114+
115+
def test_formatter_json_log_datetime_objects(self):
116+
dct_msg = {
117+
'message': 'debug message',
118+
'datetime': datetime.datetime(2016, 9, 19, 23, 59),
119+
'date': datetime.date(2016, 9, 20),
120+
'timedelta': datetime.datetime(2016, 9, 19, 23, 59) - datetime.datetime(2016, 9, 19, 23, 50),
121+
}
122+
self.logger.debug(dct_msg)
123+
json_log = json.loads(self.logger_output.getvalue())
124+
self.assertEqual(json_log.get('message'), 'debug message')
125+
self.assertEqual(json_log.get('datetime'), '2016-09-19T23:59:00')
126+
self.assertEqual(json_log.get('date'), '2016-09-20')
127+
self.assertEqual(json_log.get('timedelta'), '00:09:00')
128+
129+
130+
class TestFormatterMiscellaneous(BaseTestFormatters):
131+
def test_formatter_events(self):
132+
133+
self.setFormatter(EVENTS)
134+
self.logger.debug('starting frontier', extra={'event': 'FRONTIER_START'})
135+
self.assertRegexpMatches(self.logger_output.getvalue(),
136+
r'{bold_yellow}\d{{4}}-\d{{2}}-\d{{2}} \d{{2}}:\d{{2}}:\d{{2}},\d+ '
137+
r'FRONTIER_START starting frontier{reset}\n'.
138+
format(bold_yellow=re.escape(colors['bold_yellow']),
139+
reset=re.escape(colors['reset'])))
140+
141+
def test_formatter_console(self):
142+
self.assert_logs(CONSOLE)
143+
144+
def test_formatter_console_manager(self):
145+
self.assert_logs(CONSOLE_MANAGER)
146+
147+
def test_formatter_console_backend(self):
148+
self.assert_logs(CONSOLE_BACKEND)
149+
150+
def test_formatter_console_debugging(self):
151+
self.assert_logs(CONSOLE_DEBUGGING)
152+
153+
def assert_logs(self, formatter):
154+
self.setFormatter(formatter)
155+
self.logger.debug('debug message')
156+
self.logger.info('info message')
157+
self.logger.error('error message')
158+
self.assertEqual(self.logger_output.getvalue(),
159+
'{white}[frontera] debug message{reset}\n'
160+
'{green}[frontera] info message{reset}\n'
161+
'{red}[frontera] error message{reset}\n'.format(white=colors['white'],
162+
green=colors['green'],
163+
red=colors['red'],
164+
reset=colors['reset']))

tests/utils.py

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import six
2+
import logging
3+
import logging.config
4+
5+
6+
DEFAULT_LOGGING = {
7+
'version': 1,
8+
'disable_existing_loggers': False,
9+
'formatters': {
10+
'message': {
11+
'format': '%(message)s'
12+
}
13+
},
14+
'handlers': {
15+
'console': {
16+
'level': 'DEBUG',
17+
'class': 'logging.StreamHandler',
18+
'formatter': 'message',
19+
}
20+
},
21+
'loggers': {
22+
'frontera': {
23+
'handlers': ['console'],
24+
'level': 'DEBUG',
25+
},
26+
}
27+
}
28+
29+
30+
class LoggingCaptureMixin(object):
31+
"""
32+
Capture the output from the 'frontera' logger and store it on the class's
33+
logger_output attribute.
34+
"""
35+
36+
def setUp(self):
37+
self.logger = logging.getLogger('frontera')
38+
self.old_stream = self.logger.handlers[0].stream
39+
self.logger_output = six.StringIO()
40+
self.logger.handlers[0].stream = self.logger_output
41+
42+
def tearDown(self):
43+
self.logger.handlers[0].stream = self.old_stream
44+
45+
46+
class SetupDefaultLoggingMixin(object):
47+
@classmethod
48+
def setUpClass(cls):
49+
super(SetupDefaultLoggingMixin, cls).setUpClass()
50+
logging.config.dictConfig(DEFAULT_LOGGING)
51+
52+
@classmethod
53+
def tearDownClass(cls):
54+
super(SetupDefaultLoggingMixin, cls).tearDownClass()

0 commit comments

Comments
 (0)