|  | 
| 17 | 17 | except ImportError: | 
| 18 | 18 |     from io import StringIO | 
| 19 | 19 | 
 | 
| 20 |  | -from json_log_formatter import JSONFormatter, VerboseJSONFormatter | 
|  | 20 | +from json_log_formatter import JSONFormatter, VerboseJSONFormatter, FlatJSONFormatter | 
| 21 | 21 | 
 | 
| 22 | 22 | log_buffer = StringIO() | 
| 23 | 23 | json_handler = logging.StreamHandler(log_buffer) | 
| @@ -336,3 +336,96 @@ def test_stack_info_is_none(self): | 
| 336 | 336 |         logger.error('An error has occured') | 
| 337 | 337 |         json_record = json.loads(log_buffer.getvalue()) | 
| 338 | 338 |         self.assertIsNone(json_record['stack_info']) | 
|  | 339 | + | 
|  | 340 | + | 
|  | 341 | +class FlatJSONFormatterTest(TestCase): | 
|  | 342 | +    def setUp(self): | 
|  | 343 | +        json_handler.setFormatter(FlatJSONFormatter()) | 
|  | 344 | + | 
|  | 345 | +    def test_given_time_is_used_in_log_record(self): | 
|  | 346 | +        logger.info('Sign up', extra={'time': DATETIME}) | 
|  | 347 | +        expected_time = '"time": "2015-09-01T06:09:42.797203"' | 
|  | 348 | +        self.assertIn(expected_time, log_buffer.getvalue()) | 
|  | 349 | + | 
|  | 350 | +    def test_current_time_is_used_by_default_in_log_record(self): | 
|  | 351 | +        logger.info('Sign up', extra={'fizz': 'bazz'}) | 
|  | 352 | +        self.assertNotIn(DATETIME_ISO, log_buffer.getvalue()) | 
|  | 353 | + | 
|  | 354 | +    def test_message_and_time_are_in_json_record_when_extra_is_blank(self): | 
|  | 355 | +        logger.info('Sign up') | 
|  | 356 | +        json_record = json.loads(log_buffer.getvalue()) | 
|  | 357 | +        expected_fields = set([ | 
|  | 358 | +            'message', | 
|  | 359 | +            'time', | 
|  | 360 | +        ]) | 
|  | 361 | +        self.assertTrue(expected_fields.issubset(json_record)) | 
|  | 362 | + | 
|  | 363 | +    def test_message_and_time_and_extra_are_in_json_record_when_extra_is_provided(self): | 
|  | 364 | +        logger.info('Sign up', extra={'fizz': 'bazz'}) | 
|  | 365 | +        json_record = json.loads(log_buffer.getvalue()) | 
|  | 366 | +        expected_fields = set([ | 
|  | 367 | +            'message', | 
|  | 368 | +            'time', | 
|  | 369 | +            'fizz', | 
|  | 370 | +        ]) | 
|  | 371 | +        self.assertTrue(expected_fields.issubset(json_record)) | 
|  | 372 | + | 
|  | 373 | +    def test_exc_info_is_logged(self): | 
|  | 374 | +        try: | 
|  | 375 | +            raise ValueError('something wrong') | 
|  | 376 | +        except ValueError: | 
|  | 377 | +            logger.error('Request failed', exc_info=True) | 
|  | 378 | +        json_record = json.loads(log_buffer.getvalue()) | 
|  | 379 | +        self.assertIn( | 
|  | 380 | +            'Traceback (most recent call last)', | 
|  | 381 | +            json_record['exc_info'] | 
|  | 382 | +        ) | 
|  | 383 | + | 
|  | 384 | +    def test_builtin_types_are_serialized(self): | 
|  | 385 | +        logger.log(level=logging.ERROR, msg='Payment was sent', extra={ | 
|  | 386 | +            'first_name': 'bob', | 
|  | 387 | +            'amount': 0.00497265, | 
|  | 388 | +            'context': { | 
|  | 389 | +                'tags': ['fizz', 'bazz'], | 
|  | 390 | +            }, | 
|  | 391 | +            'things': ('a', 'b'), | 
|  | 392 | +            'ok': True, | 
|  | 393 | +            'none': None, | 
|  | 394 | +        }) | 
|  | 395 | + | 
|  | 396 | +        json_record = json.loads(log_buffer.getvalue()) | 
|  | 397 | +        self.assertEqual(json_record['first_name'], 'bob') | 
|  | 398 | +        self.assertEqual(json_record['amount'], 0.00497265) | 
|  | 399 | +        self.assertEqual(json_record['context'], "{'tags': ['fizz', 'bazz']}") | 
|  | 400 | +        self.assertEqual(json_record['things'], "('a', 'b')") | 
|  | 401 | +        self.assertEqual(json_record['ok'], True) | 
|  | 402 | +        self.assertEqual(json_record['none'], None) | 
|  | 403 | + | 
|  | 404 | +    def test_decimal_is_serialized_as_string(self): | 
|  | 405 | +        logger.log(level=logging.ERROR, msg='Payment was sent', extra={ | 
|  | 406 | +            'amount': Decimal('0.00497265') | 
|  | 407 | +        }) | 
|  | 408 | +        expected_amount = '"amount": "0.00497265"' | 
|  | 409 | +        self.assertIn(expected_amount, log_buffer.getvalue()) | 
|  | 410 | + | 
|  | 411 | +    def test_django_wsgi_request_is_serialized_as_dict(self): | 
|  | 412 | +        request = WSGIRequest({ | 
|  | 413 | +            'PATH_INFO': 'bogus', | 
|  | 414 | +            'REQUEST_METHOD': 'bogus', | 
|  | 415 | +            'CONTENT_TYPE': 'text/html; charset=utf8', | 
|  | 416 | +            'wsgi.input': BytesIO(b''), | 
|  | 417 | +        }) | 
|  | 418 | + | 
|  | 419 | +        logger.log(level=logging.ERROR, msg='Django response error', extra={ | 
|  | 420 | +            'status_code': 500, | 
|  | 421 | +            'request': request, | 
|  | 422 | +            'dict': { | 
|  | 423 | +                'request': request, | 
|  | 424 | +            }, | 
|  | 425 | +            'list': [request], | 
|  | 426 | +        }) | 
|  | 427 | +        json_record = json.loads(log_buffer.getvalue()) | 
|  | 428 | +        self.assertEqual(json_record['status_code'], 500) | 
|  | 429 | +        self.assertEqual(json_record['request'], "<WSGIRequest: BOGUS '/bogus'>") | 
|  | 430 | +        self.assertEqual(json_record['dict'], "{'request': <WSGIRequest: BOGUS '/bogus'>}") | 
|  | 431 | +        self.assertEqual(json_record['list'], "[<WSGIRequest: BOGUS '/bogus'>]") | 
0 commit comments