6
6
## Standard Library
7
7
from dataclasses import dataclass
8
8
import datetime
9
+ import enum
9
10
import io
10
11
import json
11
12
import logging
12
13
import sys
13
14
import traceback
15
+ from types import TracebackType
14
16
from typing import Any , Generator
17
+ import uuid
15
18
16
19
## Installed
17
20
from freezegun import freeze_time
@@ -81,6 +84,34 @@ def get_traceback_from_exception_followed_by_log_call(env_: LoggingEnvironment)
81
84
return str_traceback
82
85
83
86
87
+ class SomeClass :
88
+ def __init__ (self , thing : int ):
89
+ self .thing = thing
90
+ return
91
+
92
+
93
+ @dataclass
94
+ class SomeDataclass :
95
+ things : str
96
+ stuff : int
97
+ junk : bool
98
+
99
+
100
+ try :
101
+ raise ValueError
102
+ except ValueError as e :
103
+ STATIC_TRACEBACK = e .__traceback__
104
+ del e
105
+
106
+
107
+ class MultiEnum (enum .Enum ):
108
+ NONE = None
109
+ BOOL = False
110
+ STR = "somestring"
111
+ INT = 99
112
+ BYTES = b"somebytes"
113
+
114
+
84
115
### TESTS
85
116
### ============================================================================
86
117
def test_merge_record_extra ():
@@ -344,13 +375,114 @@ def test_default_encoder_with_timestamp(env: LoggingEnvironment, class_: type[Ba
344
375
env .set_formatter (class_ (timestamp = True ))
345
376
346
377
env .logger .info ("Hello" )
347
- print (env .buffer .getvalue ())
348
378
log_json = env .load_json ()
349
379
350
380
assert log_json ["timestamp" ] == "2017-07-14T02:40:00+00:00"
351
381
return
352
382
353
383
384
+ @pytest .mark .parametrize ("class_" , ALL_FORMATTERS )
385
+ @pytest .mark .parametrize (
386
+ ["obj" , "type_" ],
387
+ [
388
+ ("somestring" , str ),
389
+ (1234 , int ),
390
+ (1234.5 , float ),
391
+ (False , bool ),
392
+ (None , type (None )),
393
+ (b"somebytes" , str ),
394
+ (datetime .time (16 , 45 , 30 , 100 ), str ),
395
+ (datetime .date .today (), str ),
396
+ (datetime .datetime .utcnow (), str ),
397
+ (uuid .uuid4 (), str ),
398
+ (Exception , str ),
399
+ (Exception ("Foo occurred" ), str ),
400
+ (BaseException , str ),
401
+ (BaseException ("BaseFoo occurred" ), str ),
402
+ (STATIC_TRACEBACK , str ),
403
+ (SomeDataclass (things = "le_things" , stuff = 99 , junk = False ), dict ),
404
+ (SomeDataclass , str ),
405
+ (SomeClass , str ),
406
+ (SomeClass (1234 ), str ),
407
+ (MultiEnum .NONE , type (None )),
408
+ (MultiEnum .BOOL , bool ),
409
+ (MultiEnum .STR , str ),
410
+ (MultiEnum .INT , int ),
411
+ (MultiEnum .BYTES , str ),
412
+ (MultiEnum , str ),
413
+ ],
414
+ )
415
+ def test_common_types_encoded (
416
+ env : LoggingEnvironment , class_ : type [BaseJsonFormatter ], obj : object , type_ : type
417
+ ):
418
+ ## Known bad cases
419
+ if class_ is JsonFormatter :
420
+ if obj is SomeDataclass or isinstance (obj , SomeDataclass ) or isinstance (obj , enum .Enum ):
421
+ pytest .xfail ()
422
+
423
+ if pythonjsonlogger .ORJSON_AVAILABLE and class_ is OrjsonFormatter :
424
+ if (
425
+ obj is Exception
426
+ or obj is BaseException
427
+ or isinstance (obj , BaseException )
428
+ or obj is SomeDataclass
429
+ or obj is SomeClass
430
+ or isinstance (obj , SomeClass )
431
+ or isinstance (obj , bytes )
432
+ or isinstance (obj , TracebackType )
433
+ or isinstance (obj , enum .EnumMeta )
434
+ or obj is MultiEnum .BYTES
435
+ ):
436
+ pytest .xfail ()
437
+
438
+ if pythonjsonlogger .MSGSPEC_AVAILABLE and class_ is MsgspecFormatter :
439
+ if (
440
+ obj is Exception
441
+ or obj is BaseException
442
+ or isinstance (obj , BaseException )
443
+ or obj is SomeDataclass
444
+ or obj is SomeClass
445
+ or isinstance (obj , SomeClass )
446
+ or isinstance (obj , TracebackType )
447
+ or isinstance (obj , enum .EnumMeta )
448
+ or (
449
+ isinstance (obj , enum .Enum )
450
+ and obj in {MultiEnum .BYTES , MultiEnum .NONE , MultiEnum .BOOL }
451
+ )
452
+ ):
453
+ pytest .xfail ()
454
+
455
+ ## Test
456
+ env .set_formatter (class_ ())
457
+ extra = {
458
+ "extra" : obj ,
459
+ "extra_dict" : {"item" : obj },
460
+ "extra_list" : [obj ],
461
+ }
462
+ env .logger .info ("hello" , extra = extra )
463
+ log_json = env .load_json ()
464
+
465
+ assert isinstance (log_json ["extra" ], type_ )
466
+ assert isinstance (log_json ["extra_dict" ]["item" ], type_ )
467
+ assert isinstance (log_json ["extra_list" ][0 ], type_ )
468
+ return
469
+
470
+
471
+ @pytest .mark .parametrize ("class_" , ALL_FORMATTERS )
472
+ def test_custom_default (env : LoggingEnvironment , class_ : type [BaseJsonFormatter ]):
473
+ def custom_default (obj ):
474
+ if isinstance (obj , SomeClass ):
475
+ return {"TYPE" : obj .thing }
476
+ return None
477
+
478
+ env .set_formatter (class_ (json_default = custom_default )) # type: ignore[call-arg]
479
+ env .logger .info ("hello" , extra = {"extra" : SomeClass (999 )})
480
+ log_json = env .load_json ()
481
+
482
+ assert log_json ["extra" ] == {"TYPE" : 999 }
483
+ return
484
+
485
+
354
486
## JsonFormatter Specific
355
487
## -----------------------------------------------------------------------------
356
488
def test_json_default_encoder (env : LoggingEnvironment ):
@@ -372,21 +504,6 @@ def test_json_default_encoder(env: LoggingEnvironment):
372
504
return
373
505
374
506
375
- def test_json_custom_default (env : LoggingEnvironment ):
376
- def custom (o ):
377
- return "very custom"
378
-
379
- env .set_formatter (JsonFormatter (json_default = custom ))
380
-
381
- msg = {"adate" : datetime .datetime (1999 , 12 , 31 , 23 , 59 ), "normal" : "value" }
382
- env .logger .info (msg )
383
- log_json = env .load_json ()
384
-
385
- assert log_json ["adate" ] == "very custom"
386
- assert log_json ["normal" ] == "value"
387
- return
388
-
389
-
390
507
def test_json_ensure_ascii_true (env : LoggingEnvironment ):
391
508
env .set_formatter (JsonFormatter ())
392
509
env .logger .info ("Привет" )
0 commit comments