1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
|
"""JSON formatter using the standard library's `json` for encoding.
Module contains the `JsonFormatter` and a custom `JsonEncoder` which supports a greater
variety of types.
"""
### IMPORTS
### ============================================================================
## Future
from __future__ import annotations
## Standard Library
import datetime
import json
from typing import Any, Callable, Optional, Union
import warnings
## Application
from . import core
from . import defaults as d
### CLASSES
### ============================================================================
class JsonEncoder(json.JSONEncoder):
"""A custom encoder extending [json.JSONEncoder](https://docs.python.org/3/library/json.html#json.JSONEncoder)"""
def default(self, o: Any) -> Any:
if d.use_datetime_any(o):
return self.format_datetime_obj(o)
if d.use_exception_default(o):
return d.exception_default(o)
if d.use_traceback_default(o):
return d.traceback_default(o)
if d.use_enum_default(o):
return d.enum_default(o)
if d.use_bytes_default(o):
return d.bytes_default(o)
if d.use_dataclass_default(o):
return d.dataclass_default(o)
if d.use_type_default(o):
return d.type_default(o)
try:
return super().default(o)
except TypeError:
return d.unknown_default(o)
def format_datetime_obj(self, o: datetime.time | datetime.date | datetime.datetime) -> str:
"""Format datetime objects found in `self.default`
This allows subclasses to change the datetime format without understanding the
internals of the default method.
"""
return d.datetime_any(o)
class JsonFormatter(core.BaseJsonFormatter):
"""JSON formatter using the standard library's [`json`](https://docs.python.org/3/library/json.html) for encoding"""
def __init__(
self,
*args,
json_default: core.OptionalCallableOrStr = None,
json_encoder: core.OptionalCallableOrStr = None,
json_serializer: Union[Callable, str] = json.dumps,
json_indent: Optional[Union[int, str]] = None,
json_ensure_ascii: bool = True,
**kwargs,
) -> None:
"""
Args:
args: see [BaseJsonFormatter][pythonjsonlogger.core.BaseJsonFormatter]
json_default: a function for encoding non-standard objects
json_encoder: custom JSON encoder
json_serializer: a [`json.dumps`](https://docs.python.org/3/library/json.html#json.dumps)-compatible callable
that will be used to serialize the log record.
json_indent: indent parameter for the `json_serializer`
json_ensure_ascii: `ensure_ascii` parameter for the `json_serializer`
kwargs: see [BaseJsonFormatter][pythonjsonlogger.core.BaseJsonFormatter]
"""
super().__init__(*args, **kwargs)
self.json_default = core.str_to_object(json_default)
self.json_encoder = core.str_to_object(json_encoder)
self.json_serializer = core.str_to_object(json_serializer)
self.json_indent = json_indent
self.json_ensure_ascii = json_ensure_ascii
if not self.json_encoder and not self.json_default:
self.json_encoder = JsonEncoder
return
def jsonify_log_record(self, log_record: core.LogRecord) -> str:
"""Returns a json string of the log record."""
return self.json_serializer(
log_record,
default=self.json_default,
cls=self.json_encoder,
indent=self.json_indent,
ensure_ascii=self.json_ensure_ascii,
)
### DEPRECATED COMPATIBILITY
### ============================================================================
def __getattr__(name: str):
if name == "RESERVED_ATTRS":
warnings.warn(
"RESERVED_ATTRS has been moved to pythonjsonlogger.core",
DeprecationWarning,
)
return core.RESERVED_ATTRS
raise AttributeError(f"module {__name__} has no attribute {name}")
|