python-json-logger 3.2.1.dev1__tar.gz → 4.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {python_json_logger-3.2.1.dev1/src/python_json_logger.egg-info → python_json_logger-4.0.0}/PKG-INFO +5 -4
- {python_json_logger-3.2.1.dev1 → python_json_logger-4.0.0}/README.md +1 -0
- {python_json_logger-3.2.1.dev1 → python_json_logger-4.0.0}/pyproject.toml +2 -3
- {python_json_logger-3.2.1.dev1 → python_json_logger-4.0.0/src/python_json_logger.egg-info}/PKG-INFO +5 -4
- {python_json_logger-3.2.1.dev1 → python_json_logger-4.0.0}/src/python_json_logger.egg-info/SOURCES.txt +1 -0
- {python_json_logger-3.2.1.dev1 → python_json_logger-4.0.0}/src/python_json_logger.egg-info/requires.txt +0 -5
- {python_json_logger-3.2.1.dev1 → python_json_logger-4.0.0}/src/pythonjsonlogger/core.py +120 -71
- {python_json_logger-3.2.1.dev1 → python_json_logger-4.0.0}/src/pythonjsonlogger/json.py +9 -9
- {python_json_logger-3.2.1.dev1 → python_json_logger-4.0.0}/src/pythonjsonlogger/msgspec.py +6 -6
- {python_json_logger-3.2.1.dev1 → python_json_logger-4.0.0}/src/pythonjsonlogger/orjson.py +6 -6
- {python_json_logger-3.2.1.dev1 → python_json_logger-4.0.0}/tests/test_deprecation.py +17 -0
- python_json_logger-4.0.0/tests/test_dictconfig.py +96 -0
- {python_json_logger-3.2.1.dev1 → python_json_logger-4.0.0}/tests/test_formatters.py +102 -11
- {python_json_logger-3.2.1.dev1 → python_json_logger-4.0.0}/LICENSE +0 -0
- {python_json_logger-3.2.1.dev1 → python_json_logger-4.0.0}/MANIFEST.in +0 -0
- {python_json_logger-3.2.1.dev1 → python_json_logger-4.0.0}/NOTICE +0 -0
- {python_json_logger-3.2.1.dev1 → python_json_logger-4.0.0}/setup.cfg +0 -0
- {python_json_logger-3.2.1.dev1 → python_json_logger-4.0.0}/src/python_json_logger.egg-info/dependency_links.txt +0 -0
- {python_json_logger-3.2.1.dev1 → python_json_logger-4.0.0}/src/python_json_logger.egg-info/top_level.txt +0 -0
- {python_json_logger-3.2.1.dev1 → python_json_logger-4.0.0}/src/pythonjsonlogger/__init__.py +0 -0
- {python_json_logger-3.2.1.dev1 → python_json_logger-4.0.0}/src/pythonjsonlogger/defaults.py +0 -0
- {python_json_logger-3.2.1.dev1 → python_json_logger-4.0.0}/src/pythonjsonlogger/exception.py +0 -0
- {python_json_logger-3.2.1.dev1 → python_json_logger-4.0.0}/src/pythonjsonlogger/jsonlogger.py +0 -0
- {python_json_logger-3.2.1.dev1 → python_json_logger-4.0.0}/src/pythonjsonlogger/py.typed +0 -0
- {python_json_logger-3.2.1.dev1 → python_json_logger-4.0.0}/src/pythonjsonlogger/utils.py +0 -0
- {python_json_logger-3.2.1.dev1 → python_json_logger-4.0.0}/tests/__init__.py +0 -0
- {python_json_logger-3.2.1.dev1 → python_json_logger-4.0.0}/tests/test_missing.py +0 -0
{python_json_logger-3.2.1.dev1/src/python_json_logger.egg-info → python_json_logger-4.0.0}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: python-json-logger
|
|
3
|
-
Version:
|
|
3
|
+
Version: 4.0.0
|
|
4
4
|
Summary: JSON Log Formatter for the Python Logging Package
|
|
5
5
|
Author-email: Zakaria Zajac <zak@madzak.com>, Nicholas Hairs <info+python-json-logger@nicholashairs.com>
|
|
6
6
|
Maintainer-email: Nicholas Hairs <info+python-json-logger@nicholashairs.com>
|
|
@@ -27,8 +27,7 @@ License-File: NOTICE
|
|
|
27
27
|
Requires-Dist: typing_extensions; python_version < "3.10"
|
|
28
28
|
Provides-Extra: dev
|
|
29
29
|
Requires-Dist: orjson; implementation_name != "pypy" and extra == "dev"
|
|
30
|
-
Requires-Dist: msgspec;
|
|
31
|
-
Requires-Dist: msgspec-python313-pre; (implementation_name != "pypy" and python_version == "3.13") and extra == "dev"
|
|
30
|
+
Requires-Dist: msgspec; implementation_name != "pypy" and extra == "dev"
|
|
32
31
|
Requires-Dist: validate-pyproject[all]; extra == "dev"
|
|
33
32
|
Requires-Dist: black; extra == "dev"
|
|
34
33
|
Requires-Dist: pylint; extra == "dev"
|
|
@@ -46,9 +45,11 @@ Requires-Dist: mkdocstrings[python]; extra == "dev"
|
|
|
46
45
|
Requires-Dist: mkdocs-gen-files; extra == "dev"
|
|
47
46
|
Requires-Dist: mkdocs-literate-nav; extra == "dev"
|
|
48
47
|
Requires-Dist: mike; extra == "dev"
|
|
48
|
+
Dynamic: license-file
|
|
49
49
|
|
|
50
50
|
[](https://pypi.python.org/pypi/python-json-logger/)
|
|
51
51
|
[](https://pypi.python.org/pypi/python-json-logger/)
|
|
52
|
+
[](https://pypi.python.org/pypi/python-json-logger/)
|
|
52
53
|
[](https://github.com/nhairs/python-json-logger)
|
|
53
54
|
[](https://github.com/nhairs/python-json-logger)
|
|
54
55
|

|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
[](https://pypi.python.org/pypi/python-json-logger/)
|
|
2
2
|
[](https://pypi.python.org/pypi/python-json-logger/)
|
|
3
|
+
[](https://pypi.python.org/pypi/python-json-logger/)
|
|
3
4
|
[](https://github.com/nhairs/python-json-logger)
|
|
4
5
|
[](https://github.com/nhairs/python-json-logger)
|
|
5
6
|

|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "python-json-logger"
|
|
7
|
-
version = "
|
|
7
|
+
version = "4.0.0"
|
|
8
8
|
description = "JSON Log Formatter for the Python Logging Package"
|
|
9
9
|
authors = [
|
|
10
10
|
{name = "Zakaria Zajac", email = "zak@madzak.com"},
|
|
@@ -47,8 +47,7 @@ GitHub = "https://github.com/nhairs/python-json-logger"
|
|
|
47
47
|
dev = [
|
|
48
48
|
## Optional but required for dev
|
|
49
49
|
"orjson;implementation_name!='pypy'",
|
|
50
|
-
"msgspec;implementation_name!='pypy'
|
|
51
|
-
"msgspec-python313-pre;implementation_name!='pypy' and python_version=='3.13'",
|
|
50
|
+
"msgspec;implementation_name!='pypy'",
|
|
52
51
|
## Lint
|
|
53
52
|
"validate-pyproject[all]",
|
|
54
53
|
"black",
|
{python_json_logger-3.2.1.dev1 → python_json_logger-4.0.0/src/python_json_logger.egg-info}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: python-json-logger
|
|
3
|
-
Version:
|
|
3
|
+
Version: 4.0.0
|
|
4
4
|
Summary: JSON Log Formatter for the Python Logging Package
|
|
5
5
|
Author-email: Zakaria Zajac <zak@madzak.com>, Nicholas Hairs <info+python-json-logger@nicholashairs.com>
|
|
6
6
|
Maintainer-email: Nicholas Hairs <info+python-json-logger@nicholashairs.com>
|
|
@@ -27,8 +27,7 @@ License-File: NOTICE
|
|
|
27
27
|
Requires-Dist: typing_extensions; python_version < "3.10"
|
|
28
28
|
Provides-Extra: dev
|
|
29
29
|
Requires-Dist: orjson; implementation_name != "pypy" and extra == "dev"
|
|
30
|
-
Requires-Dist: msgspec;
|
|
31
|
-
Requires-Dist: msgspec-python313-pre; (implementation_name != "pypy" and python_version == "3.13") and extra == "dev"
|
|
30
|
+
Requires-Dist: msgspec; implementation_name != "pypy" and extra == "dev"
|
|
32
31
|
Requires-Dist: validate-pyproject[all]; extra == "dev"
|
|
33
32
|
Requires-Dist: black; extra == "dev"
|
|
34
33
|
Requires-Dist: pylint; extra == "dev"
|
|
@@ -46,9 +45,11 @@ Requires-Dist: mkdocstrings[python]; extra == "dev"
|
|
|
46
45
|
Requires-Dist: mkdocs-gen-files; extra == "dev"
|
|
47
46
|
Requires-Dist: mkdocs-literate-nav; extra == "dev"
|
|
48
47
|
Requires-Dist: mike; extra == "dev"
|
|
48
|
+
Dynamic: license-file
|
|
49
49
|
|
|
50
50
|
[](https://pypi.python.org/pypi/python-json-logger/)
|
|
51
51
|
[](https://pypi.python.org/pypi/python-json-logger/)
|
|
52
|
+
[](https://pypi.python.org/pypi/python-json-logger/)
|
|
52
53
|
[](https://github.com/nhairs/python-json-logger)
|
|
53
54
|
[](https://github.com/nhairs/python-json-logger)
|
|
54
55
|

|
|
@@ -22,12 +22,7 @@ mike
|
|
|
22
22
|
|
|
23
23
|
[dev:implementation_name != "pypy"]
|
|
24
24
|
orjson
|
|
25
|
-
|
|
26
|
-
[dev:implementation_name != "pypy" and python_version < "3.13"]
|
|
27
25
|
msgspec
|
|
28
26
|
|
|
29
|
-
[dev:implementation_name != "pypy" and python_version == "3.13"]
|
|
30
|
-
msgspec-python313-pre
|
|
31
|
-
|
|
32
27
|
[dev:python_version < "3.9"]
|
|
33
28
|
backports.zoneinfo
|
|
@@ -7,11 +7,10 @@ from __future__ import annotations
|
|
|
7
7
|
|
|
8
8
|
## Standard Library
|
|
9
9
|
from datetime import datetime, timezone
|
|
10
|
-
import importlib
|
|
11
10
|
import logging
|
|
12
11
|
import re
|
|
13
12
|
import sys
|
|
14
|
-
from typing import Optional, Union,
|
|
13
|
+
from typing import Optional, Union, List, Dict, Container, Any, Sequence
|
|
15
14
|
|
|
16
15
|
if sys.version_info >= (3, 10):
|
|
17
16
|
from typing import TypeAlias
|
|
@@ -72,31 +71,15 @@ STYLE_PERCENT_REGEX = re.compile(r"%\((.+?)\)", re.IGNORECASE) # % style
|
|
|
72
71
|
|
|
73
72
|
## Type Aliases
|
|
74
73
|
## -----------------------------------------------------------------------------
|
|
75
|
-
|
|
76
|
-
"""Type alias
|
|
74
|
+
LogData: TypeAlias = Dict[str, Any]
|
|
75
|
+
"""Type alias
|
|
77
76
|
|
|
78
|
-
|
|
79
|
-
"""
|
|
77
|
+
*Changed in 4.0*: renamed from `LogRecord` to `LogData`
|
|
78
|
+
"""
|
|
80
79
|
|
|
81
80
|
|
|
82
81
|
### FUNCTIONS
|
|
83
82
|
### ============================================================================
|
|
84
|
-
def str_to_object(obj: Any) -> Any:
|
|
85
|
-
"""Import strings to an object, leaving non-strings as-is.
|
|
86
|
-
|
|
87
|
-
Args:
|
|
88
|
-
obj: the object or string to process
|
|
89
|
-
|
|
90
|
-
*New in 3.1*
|
|
91
|
-
"""
|
|
92
|
-
|
|
93
|
-
if not isinstance(obj, str):
|
|
94
|
-
return obj
|
|
95
|
-
|
|
96
|
-
module_name, attribute_name = obj.rsplit(".", 1)
|
|
97
|
-
return getattr(importlib.import_module(module_name), attribute_name)
|
|
98
|
-
|
|
99
|
-
|
|
100
83
|
def merge_record_extra(
|
|
101
84
|
record: logging.LogRecord,
|
|
102
85
|
target: Dict,
|
|
@@ -134,6 +117,8 @@ class BaseJsonFormatter(logging.Formatter):
|
|
|
134
117
|
*New in 3.1*
|
|
135
118
|
|
|
136
119
|
*Changed in 3.2*: `defaults` argument is no longer ignored.
|
|
120
|
+
|
|
121
|
+
*Added in 3.3*: `exc_info_as_array` and `stack_info_as_array` options are added.
|
|
137
122
|
"""
|
|
138
123
|
|
|
139
124
|
_style: Union[logging.PercentStyle, str] # type: ignore[assignment]
|
|
@@ -143,7 +128,7 @@ class BaseJsonFormatter(logging.Formatter):
|
|
|
143
128
|
# pylint: disable=too-many-arguments,super-init-not-called
|
|
144
129
|
def __init__(
|
|
145
130
|
self,
|
|
146
|
-
fmt: Optional[str] = None,
|
|
131
|
+
fmt: Optional[Union[str, Sequence[str]]] = None,
|
|
147
132
|
datefmt: Optional[str] = None,
|
|
148
133
|
style: str = "%",
|
|
149
134
|
validate: bool = True,
|
|
@@ -155,14 +140,16 @@ class BaseJsonFormatter(logging.Formatter):
|
|
|
155
140
|
reserved_attrs: Optional[Sequence[str]] = None,
|
|
156
141
|
timestamp: Union[bool, str] = False,
|
|
157
142
|
defaults: Optional[Dict[str, Any]] = None,
|
|
143
|
+
exc_info_as_array: bool = False,
|
|
144
|
+
stack_info_as_array: bool = False,
|
|
158
145
|
) -> None:
|
|
159
146
|
"""
|
|
160
147
|
Args:
|
|
161
|
-
fmt:
|
|
148
|
+
fmt: String format or `Sequence` of field names of fields to log.
|
|
162
149
|
datefmt: format to use when formatting `asctime` field
|
|
163
|
-
style: how to extract log fields from `fmt`
|
|
150
|
+
style: how to extract log fields from `fmt`. Ignored if `fmt` is a `Sequence[str]`.
|
|
164
151
|
validate: validate `fmt` against style, if implementing a custom `style` you
|
|
165
|
-
must set this to `False`.
|
|
152
|
+
must set this to `False`. Ignored if `fmt` is a `Sequence[str]`.
|
|
166
153
|
defaults: a dictionary containing default fields that are added before all other fields and
|
|
167
154
|
may be overridden. The supplied fields are still subject to `rename_fields`.
|
|
168
155
|
prefix: an optional string prefix added at the beginning of
|
|
@@ -177,6 +164,8 @@ class BaseJsonFormatter(logging.Formatter):
|
|
|
177
164
|
outputting the json log record. If string is passed, timestamp will be added
|
|
178
165
|
to log record using string as key. If True boolean is passed, timestamp key
|
|
179
166
|
will be "timestamp". Defaults to False/off.
|
|
167
|
+
exc_info_as_array: break the exc_info into a list of lines based on line breaks.
|
|
168
|
+
stack_info_as_array: break the stack_info into a list of lines based on line breaks.
|
|
180
169
|
|
|
181
170
|
*Changed in 3.1*:
|
|
182
171
|
|
|
@@ -186,39 +175,66 @@ class BaseJsonFormatter(logging.Formatter):
|
|
|
186
175
|
- Renaming fields now preserves the order that fields were added in and avoids adding
|
|
187
176
|
missing fields. The original behaviour, missing fields have a value of `None`, is still
|
|
188
177
|
available by setting `rename_fields_keep_missing` to `True`.
|
|
178
|
+
|
|
179
|
+
*Added in 4.0*:
|
|
180
|
+
|
|
181
|
+
- `fmt` now supports comma seperated lists (`style=","`). Note that this style is specific
|
|
182
|
+
to `python-json-logger` and thus care should be taken to not to pass this format to other
|
|
183
|
+
logging Formatter implementations.
|
|
184
|
+
- `fmt` now supports sequences of strings (e.g. lists and tuples) of field names.
|
|
189
185
|
"""
|
|
190
186
|
## logging.Formatter compatibility
|
|
191
187
|
## ---------------------------------------------------------------------
|
|
192
|
-
# Note: validate added in 3.8, defaults added in 3.10
|
|
193
|
-
if
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
188
|
+
# Note: validate added in python 3.8, defaults added in 3.10
|
|
189
|
+
if fmt is None or isinstance(fmt, str):
|
|
190
|
+
if style in logging._STYLES:
|
|
191
|
+
_style = logging._STYLES[style][0](fmt) # type: ignore[operator]
|
|
192
|
+
if validate:
|
|
193
|
+
_style.validate()
|
|
194
|
+
self._style = _style
|
|
195
|
+
self._fmt = _style._fmt
|
|
196
|
+
|
|
197
|
+
elif style == "," or not validate:
|
|
198
|
+
self._style = style
|
|
199
|
+
self._fmt = fmt
|
|
200
|
+
# TODO: Validate comma format
|
|
201
|
+
|
|
202
|
+
else:
|
|
203
|
+
raise ValueError("Style must be one of: '%{$,'")
|
|
204
|
+
|
|
205
|
+
self._required_fields = self.parse()
|
|
206
|
+
|
|
207
|
+
# Note: we do this check second as string is still a Sequence[str]
|
|
208
|
+
elif isinstance(fmt, Sequence):
|
|
209
|
+
self._style = "__sequence__"
|
|
210
|
+
self._fmt = str(fmt)
|
|
211
|
+
self._required_fields = list(fmt)
|
|
206
212
|
|
|
207
213
|
self.datefmt = datefmt
|
|
208
214
|
|
|
209
215
|
## JSON Logging specific
|
|
210
216
|
## ---------------------------------------------------------------------
|
|
211
217
|
self.prefix = prefix
|
|
212
|
-
|
|
218
|
+
|
|
219
|
+
# We recreate the dict in rename_fields and static_fields to support internal/external
|
|
220
|
+
# references which require getting the item to do the conversion.
|
|
221
|
+
# For more details see: https://github.com/nhairs/python-json-logger/pull/45
|
|
222
|
+
self.rename_fields = (
|
|
223
|
+
{key: rename_fields[key] for key in rename_fields} if rename_fields is not None else {}
|
|
224
|
+
)
|
|
225
|
+
self.static_fields = (
|
|
226
|
+
{key: static_fields[key] for key in static_fields} if static_fields is not None else {}
|
|
227
|
+
)
|
|
228
|
+
|
|
213
229
|
self.rename_fields_keep_missing = rename_fields_keep_missing
|
|
214
|
-
self.static_fields = static_fields if static_fields is not None else {}
|
|
215
230
|
self.reserved_attrs = set(reserved_attrs if reserved_attrs is not None else RESERVED_ATTRS)
|
|
216
231
|
self.timestamp = timestamp
|
|
217
232
|
|
|
218
|
-
self._required_fields = self.parse()
|
|
219
233
|
self._skip_fields = set(self._required_fields)
|
|
220
234
|
self._skip_fields.update(self.reserved_attrs)
|
|
221
235
|
self.defaults = defaults if defaults is not None else {}
|
|
236
|
+
self.exc_info_as_array = exc_info_as_array
|
|
237
|
+
self.stack_info_as_array = stack_info_as_array
|
|
222
238
|
return
|
|
223
239
|
|
|
224
240
|
def format(self, record: logging.LogRecord) -> str:
|
|
@@ -252,11 +268,11 @@ class BaseJsonFormatter(logging.Formatter):
|
|
|
252
268
|
if record.stack_info and not message_dict.get("stack_info"):
|
|
253
269
|
message_dict["stack_info"] = self.formatStack(record.stack_info)
|
|
254
270
|
|
|
255
|
-
|
|
256
|
-
self.add_fields(
|
|
257
|
-
|
|
271
|
+
log_data: LogData = {}
|
|
272
|
+
self.add_fields(log_data, record, message_dict)
|
|
273
|
+
log_data = self.process_log_record(log_data)
|
|
258
274
|
|
|
259
|
-
return self.serialize_log_record(
|
|
275
|
+
return self.serialize_log_record(log_data)
|
|
260
276
|
|
|
261
277
|
## JSON Formatter Specific Methods
|
|
262
278
|
## -------------------------------------------------------------------------
|
|
@@ -271,6 +287,18 @@ class BaseJsonFormatter(logging.Formatter):
|
|
|
271
287
|
Returns:
|
|
272
288
|
list of fields to be extracted and serialized
|
|
273
289
|
"""
|
|
290
|
+
if self._fmt is None:
|
|
291
|
+
return []
|
|
292
|
+
|
|
293
|
+
if isinstance(self._style, str):
|
|
294
|
+
if self._style == "__sequence__":
|
|
295
|
+
raise RuntimeError("Must not call parse when fmt is a sequence of strings")
|
|
296
|
+
|
|
297
|
+
if self._style == ",":
|
|
298
|
+
return [field.strip() for field in self._fmt.split(",") if field.strip()]
|
|
299
|
+
|
|
300
|
+
raise ValueError(f"Style {self._style!r} is not supported")
|
|
301
|
+
|
|
274
302
|
if isinstance(self._style, logging.StringTemplateStyle):
|
|
275
303
|
formatter_style_pattern = STYLE_STRING_TEMPLATE_REGEX
|
|
276
304
|
|
|
@@ -285,22 +313,21 @@ class BaseJsonFormatter(logging.Formatter):
|
|
|
285
313
|
else:
|
|
286
314
|
raise ValueError(f"Style {self._style!r} is not supported")
|
|
287
315
|
|
|
288
|
-
|
|
289
|
-
return formatter_style_pattern.findall(self._fmt)
|
|
290
|
-
|
|
291
|
-
return []
|
|
316
|
+
return formatter_style_pattern.findall(self._fmt)
|
|
292
317
|
|
|
293
|
-
def serialize_log_record(self,
|
|
294
|
-
"""Returns the final representation of the
|
|
318
|
+
def serialize_log_record(self, log_data: LogData) -> str:
|
|
319
|
+
"""Returns the final representation of the data to be logged
|
|
295
320
|
|
|
296
321
|
Args:
|
|
297
|
-
|
|
322
|
+
log_data: the data
|
|
323
|
+
|
|
324
|
+
*Changed in 4.0*: `log_record` renamed to `log_data`
|
|
298
325
|
"""
|
|
299
|
-
return self.prefix + self.jsonify_log_record(
|
|
326
|
+
return self.prefix + self.jsonify_log_record(log_data)
|
|
300
327
|
|
|
301
328
|
def add_fields(
|
|
302
329
|
self,
|
|
303
|
-
|
|
330
|
+
log_data: Dict[str, Any],
|
|
304
331
|
record: logging.LogRecord,
|
|
305
332
|
message_dict: Dict[str, Any],
|
|
306
333
|
) -> None:
|
|
@@ -309,38 +336,40 @@ class BaseJsonFormatter(logging.Formatter):
|
|
|
309
336
|
This method can be overridden to implement custom logic for adding fields.
|
|
310
337
|
|
|
311
338
|
Args:
|
|
312
|
-
|
|
339
|
+
log_data: data that will be logged
|
|
313
340
|
record: the record to extract data from
|
|
314
341
|
message_dict: dictionary that was logged instead of a message. e.g
|
|
315
342
|
`logger.info({"is_this_message_dict": True})`
|
|
343
|
+
|
|
344
|
+
*Changed in 4.0*: `log_record` renamed to `log_data`
|
|
316
345
|
"""
|
|
317
346
|
for field in self.defaults:
|
|
318
|
-
|
|
347
|
+
log_data[self._get_rename(field)] = self.defaults[field]
|
|
319
348
|
|
|
320
349
|
for field in self._required_fields:
|
|
321
|
-
|
|
350
|
+
log_data[self._get_rename(field)] = record.__dict__.get(field)
|
|
322
351
|
|
|
323
352
|
for data_dict in [self.static_fields, message_dict]:
|
|
324
353
|
for key, value in data_dict.items():
|
|
325
|
-
|
|
354
|
+
log_data[self._get_rename(key)] = value
|
|
326
355
|
|
|
327
356
|
merge_record_extra(
|
|
328
357
|
record,
|
|
329
|
-
|
|
358
|
+
log_data,
|
|
330
359
|
reserved=self._skip_fields,
|
|
331
360
|
rename_fields=self.rename_fields,
|
|
332
361
|
)
|
|
333
362
|
|
|
334
363
|
if self.timestamp:
|
|
335
364
|
key = self.timestamp if isinstance(self.timestamp, str) else "timestamp"
|
|
336
|
-
|
|
365
|
+
log_data[self._get_rename(key)] = datetime.fromtimestamp(
|
|
337
366
|
record.created, tz=timezone.utc
|
|
338
367
|
)
|
|
339
368
|
|
|
340
369
|
if self.rename_fields_keep_missing:
|
|
341
370
|
for field in self.rename_fields.values():
|
|
342
|
-
if field not in
|
|
343
|
-
|
|
371
|
+
if field not in log_data:
|
|
372
|
+
log_data[field] = None
|
|
344
373
|
return
|
|
345
374
|
|
|
346
375
|
def _get_rename(self, key: str) -> str:
|
|
@@ -348,23 +377,43 @@ class BaseJsonFormatter(logging.Formatter):
|
|
|
348
377
|
|
|
349
378
|
# Child Methods
|
|
350
379
|
# ..........................................................................
|
|
351
|
-
def jsonify_log_record(self,
|
|
352
|
-
"""Convert
|
|
380
|
+
def jsonify_log_record(self, log_data: LogData) -> str:
|
|
381
|
+
"""Convert the log data into a JSON string.
|
|
353
382
|
|
|
354
383
|
Child classes MUST override this method.
|
|
355
384
|
|
|
356
385
|
Args:
|
|
357
|
-
|
|
386
|
+
log_data: the data to serialize
|
|
387
|
+
|
|
388
|
+
*Changed in 4.0*: `log_record` renamed to `log_data`
|
|
358
389
|
"""
|
|
359
390
|
raise NotImplementedError()
|
|
360
391
|
|
|
361
|
-
def process_log_record(self,
|
|
362
|
-
"""Custom processing of the
|
|
392
|
+
def process_log_record(self, log_data: LogData) -> LogData:
|
|
393
|
+
"""Custom processing of the data to be logged.
|
|
363
394
|
|
|
364
395
|
Child classes can override this method to alter the log record before it
|
|
365
396
|
is serialized.
|
|
366
397
|
|
|
367
398
|
Args:
|
|
368
|
-
|
|
399
|
+
log_data: incoming data
|
|
400
|
+
|
|
401
|
+
*Changed in 4.0*: `log_record` renamed to `log_data`
|
|
402
|
+
"""
|
|
403
|
+
return log_data
|
|
404
|
+
|
|
405
|
+
def formatException(self, ei) -> Union[str, list[str]]: # type: ignore
|
|
406
|
+
"""Format and return the specified exception information.
|
|
407
|
+
|
|
408
|
+
If exc_info_as_array is set to True, This method returns an array of strings.
|
|
409
|
+
"""
|
|
410
|
+
exception_info_str = super().formatException(ei)
|
|
411
|
+
return exception_info_str.splitlines() if self.exc_info_as_array else exception_info_str
|
|
412
|
+
|
|
413
|
+
def formatStack(self, stack_info) -> Union[str, list[str]]: # type: ignore
|
|
414
|
+
"""Format and return the specified stack information.
|
|
415
|
+
|
|
416
|
+
If stack_info_as_array is set to True, This method returns an array of strings.
|
|
369
417
|
"""
|
|
370
|
-
|
|
418
|
+
stack_info_str = super().formatStack(stack_info)
|
|
419
|
+
return stack_info_str.splitlines() if self.stack_info_as_array else stack_info_str
|
|
@@ -67,9 +67,9 @@ class JsonFormatter(core.BaseJsonFormatter):
|
|
|
67
67
|
def __init__(
|
|
68
68
|
self,
|
|
69
69
|
*args,
|
|
70
|
-
json_default:
|
|
71
|
-
json_encoder:
|
|
72
|
-
json_serializer:
|
|
70
|
+
json_default: Optional[Callable] = None,
|
|
71
|
+
json_encoder: Optional[Callable] = None,
|
|
72
|
+
json_serializer: Callable = json.dumps,
|
|
73
73
|
json_indent: Optional[Union[int, str]] = None,
|
|
74
74
|
json_ensure_ascii: bool = True,
|
|
75
75
|
**kwargs,
|
|
@@ -87,19 +87,19 @@ class JsonFormatter(core.BaseJsonFormatter):
|
|
|
87
87
|
"""
|
|
88
88
|
super().__init__(*args, **kwargs)
|
|
89
89
|
|
|
90
|
-
self.json_default =
|
|
91
|
-
self.json_encoder =
|
|
92
|
-
self.json_serializer =
|
|
90
|
+
self.json_default = json_default
|
|
91
|
+
self.json_encoder = json_encoder
|
|
92
|
+
self.json_serializer = json_serializer
|
|
93
93
|
self.json_indent = json_indent
|
|
94
94
|
self.json_ensure_ascii = json_ensure_ascii
|
|
95
95
|
if not self.json_encoder and not self.json_default:
|
|
96
96
|
self.json_encoder = JsonEncoder
|
|
97
97
|
return
|
|
98
98
|
|
|
99
|
-
def jsonify_log_record(self,
|
|
100
|
-
"""Returns a json string of the log
|
|
99
|
+
def jsonify_log_record(self, log_data: core.LogData) -> str:
|
|
100
|
+
"""Returns a json string of the log data."""
|
|
101
101
|
return self.json_serializer(
|
|
102
|
-
|
|
102
|
+
log_data,
|
|
103
103
|
default=self.json_default,
|
|
104
104
|
cls=self.json_encoder,
|
|
105
105
|
indent=self.json_indent,
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
from __future__ import annotations
|
|
7
7
|
|
|
8
8
|
## Standard Library
|
|
9
|
-
from typing import Any
|
|
9
|
+
from typing import Any, Optional, Callable
|
|
10
10
|
|
|
11
11
|
## Installed
|
|
12
12
|
|
|
@@ -43,7 +43,7 @@ class MsgspecFormatter(core.BaseJsonFormatter):
|
|
|
43
43
|
def __init__(
|
|
44
44
|
self,
|
|
45
45
|
*args,
|
|
46
|
-
json_default:
|
|
46
|
+
json_default: Optional[Callable] = msgspec_default,
|
|
47
47
|
**kwargs,
|
|
48
48
|
) -> None:
|
|
49
49
|
"""
|
|
@@ -54,10 +54,10 @@ class MsgspecFormatter(core.BaseJsonFormatter):
|
|
|
54
54
|
"""
|
|
55
55
|
super().__init__(*args, **kwargs)
|
|
56
56
|
|
|
57
|
-
self.json_default =
|
|
57
|
+
self.json_default = json_default
|
|
58
58
|
self._encoder = msgspec.json.Encoder(enc_hook=self.json_default)
|
|
59
59
|
return
|
|
60
60
|
|
|
61
|
-
def jsonify_log_record(self,
|
|
62
|
-
"""Returns a json string of the log
|
|
63
|
-
return self._encoder.encode(
|
|
61
|
+
def jsonify_log_record(self, log_data: core.LogData) -> str:
|
|
62
|
+
"""Returns a json string of the log data."""
|
|
63
|
+
return self._encoder.encode(log_data).decode("utf8")
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
from __future__ import annotations
|
|
7
7
|
|
|
8
8
|
## Standard Library
|
|
9
|
-
from typing import Any
|
|
9
|
+
from typing import Any, Optional, Callable
|
|
10
10
|
|
|
11
11
|
## Installed
|
|
12
12
|
|
|
@@ -45,7 +45,7 @@ class OrjsonFormatter(core.BaseJsonFormatter):
|
|
|
45
45
|
def __init__(
|
|
46
46
|
self,
|
|
47
47
|
*args,
|
|
48
|
-
json_default:
|
|
48
|
+
json_default: Optional[Callable] = orjson_default,
|
|
49
49
|
json_indent: bool = False,
|
|
50
50
|
**kwargs,
|
|
51
51
|
) -> None:
|
|
@@ -58,14 +58,14 @@ class OrjsonFormatter(core.BaseJsonFormatter):
|
|
|
58
58
|
"""
|
|
59
59
|
super().__init__(*args, **kwargs)
|
|
60
60
|
|
|
61
|
-
self.json_default =
|
|
61
|
+
self.json_default = json_default
|
|
62
62
|
self.json_indent = json_indent
|
|
63
63
|
return
|
|
64
64
|
|
|
65
|
-
def jsonify_log_record(self,
|
|
66
|
-
"""Returns a json string of the log
|
|
65
|
+
def jsonify_log_record(self, log_data: core.LogData) -> str:
|
|
66
|
+
"""Returns a json string of the log data."""
|
|
67
67
|
opt = orjson.OPT_NON_STR_KEYS
|
|
68
68
|
if self.json_indent:
|
|
69
69
|
opt |= orjson.OPT_INDENT_2
|
|
70
70
|
|
|
71
|
-
return orjson.dumps(
|
|
71
|
+
return orjson.dumps(log_data, default=self.json_default, option=opt).decode("utf8")
|
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
from __future__ import annotations
|
|
5
5
|
|
|
6
6
|
## Standard Library
|
|
7
|
+
import subprocess
|
|
8
|
+
import sys
|
|
7
9
|
|
|
8
10
|
## Installed
|
|
9
11
|
import pytest
|
|
@@ -26,3 +28,18 @@ def test_jsonlogger_reserved_attrs_deprecated():
|
|
|
26
28
|
# a DeprecationWarning and we specifically want the one for RESERVED_ATTRS
|
|
27
29
|
pythonjsonlogger.json.RESERVED_ATTRS
|
|
28
30
|
return
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@pytest.mark.parametrize(
|
|
34
|
+
"command",
|
|
35
|
+
[
|
|
36
|
+
"from pythonjsonlogger import jsonlogger",
|
|
37
|
+
"import pythonjsonlogger.jsonlogger",
|
|
38
|
+
"from pythonjsonlogger.jsonlogger import JsonFormatter",
|
|
39
|
+
"from pythonjsonlogger.jsonlogger import RESERVED_ATTRS",
|
|
40
|
+
],
|
|
41
|
+
)
|
|
42
|
+
def test_import(command: str):
|
|
43
|
+
output = subprocess.check_output([sys.executable, "-c", f"{command};print('OK')"])
|
|
44
|
+
assert output.strip() == b"OK"
|
|
45
|
+
return
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
### IMPORTS
|
|
2
|
+
### ============================================================================
|
|
3
|
+
## Future
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
## Standard Library
|
|
7
|
+
from dataclasses import dataclass
|
|
8
|
+
import io
|
|
9
|
+
import json
|
|
10
|
+
import logging
|
|
11
|
+
import logging.config
|
|
12
|
+
from typing import Any, Generator
|
|
13
|
+
|
|
14
|
+
## Installed
|
|
15
|
+
import pytest
|
|
16
|
+
|
|
17
|
+
### SETUP
|
|
18
|
+
### ============================================================================
|
|
19
|
+
_LOGGER_COUNT = 0
|
|
20
|
+
EXT_VAL = 999
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class Dummy:
|
|
24
|
+
pass
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def my_json_default(obj: Any) -> Any:
|
|
28
|
+
if isinstance(obj, Dummy):
|
|
29
|
+
return "DUMMY"
|
|
30
|
+
return obj
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
LOGGING_CONFIG = {
|
|
34
|
+
"version": 1,
|
|
35
|
+
"disable_existing_loggers": False,
|
|
36
|
+
"formatters": {
|
|
37
|
+
"default": {
|
|
38
|
+
"()": "pythonjsonlogger.json.JsonFormatter",
|
|
39
|
+
"json_default": "ext://tests.test_dictconfig.my_json_default",
|
|
40
|
+
"static_fields": {"ext-val": "ext://tests.test_dictconfig.EXT_VAL"},
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
"handlers": {
|
|
44
|
+
"default": {
|
|
45
|
+
"level": "DEBUG",
|
|
46
|
+
"formatter": "default",
|
|
47
|
+
"class": "logging.StreamHandler",
|
|
48
|
+
"stream": "ext://sys.stdout", # Default is stderr
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
"loggers": {
|
|
52
|
+
"": {"handlers": ["default"], "level": "WARNING", "propagate": False}, # root logger
|
|
53
|
+
},
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
@dataclass
|
|
58
|
+
class LoggingEnvironment:
|
|
59
|
+
logger: logging.Logger
|
|
60
|
+
buffer: io.StringIO
|
|
61
|
+
|
|
62
|
+
def load_json(self) -> Any:
|
|
63
|
+
return json.loads(self.buffer.getvalue())
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
@pytest.fixture
|
|
67
|
+
def env() -> Generator[LoggingEnvironment, None, None]:
|
|
68
|
+
global _LOGGER_COUNT # pylint: disable=global-statement
|
|
69
|
+
_LOGGER_COUNT += 1
|
|
70
|
+
logging.config.dictConfig(LOGGING_CONFIG)
|
|
71
|
+
default_formatter = logging.root.handlers[0].formatter
|
|
72
|
+
logger = logging.getLogger(f"pythonjsonlogger.tests.{_LOGGER_COUNT}")
|
|
73
|
+
logger.setLevel(logging.DEBUG)
|
|
74
|
+
buffer = io.StringIO()
|
|
75
|
+
handler = logging.StreamHandler(buffer)
|
|
76
|
+
handler.setFormatter(default_formatter)
|
|
77
|
+
logger.addHandler(handler)
|
|
78
|
+
yield LoggingEnvironment(logger=logger, buffer=buffer)
|
|
79
|
+
logger.removeHandler(handler)
|
|
80
|
+
logger.setLevel(logging.NOTSET)
|
|
81
|
+
buffer.close()
|
|
82
|
+
return
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
### TESTS
|
|
86
|
+
### ============================================================================
|
|
87
|
+
def test_external_reference_support(env: LoggingEnvironment):
|
|
88
|
+
|
|
89
|
+
assert logging.root.handlers[0].formatter.json_default is my_json_default # type: ignore[union-attr]
|
|
90
|
+
|
|
91
|
+
env.logger.info("hello", extra={"dummy": Dummy()})
|
|
92
|
+
log_json = env.load_json()
|
|
93
|
+
|
|
94
|
+
assert log_json["ext-val"] == EXT_VAL
|
|
95
|
+
assert log_json["dummy"] == "DUMMY"
|
|
96
|
+
return
|
|
@@ -158,12 +158,48 @@ def test_default_format(env: LoggingEnvironment, class_: type[BaseJsonFormatter]
|
|
|
158
158
|
|
|
159
159
|
@pytest.mark.parametrize("class_", ALL_FORMATTERS)
|
|
160
160
|
def test_percentage_format(env: LoggingEnvironment, class_: type[BaseJsonFormatter]):
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
)
|
|
161
|
+
# Note: We use different %s styles in the format to check the regex correctly collects them
|
|
162
|
+
env.set_formatter(class_("[%(levelname)8s] %(message)s %(filename)s:%(lineno)d %(asctime)"))
|
|
163
|
+
|
|
164
|
+
msg = "testing logging format"
|
|
165
|
+
env.logger.info(msg)
|
|
166
|
+
log_json = env.load_json()
|
|
167
|
+
|
|
168
|
+
assert log_json["message"] == msg
|
|
169
|
+
assert log_json.keys() == {"levelname", "message", "filename", "lineno", "asctime"}
|
|
170
|
+
return
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
@pytest.mark.parametrize("class_", ALL_FORMATTERS)
|
|
174
|
+
def test_comma_format(env: LoggingEnvironment, class_: type[BaseJsonFormatter]):
|
|
175
|
+
# Note: we have double comma `,,` to test handling "empty" names
|
|
176
|
+
env.set_formatter(class_("levelname,,message,filename,lineno,asctime,", style=","))
|
|
177
|
+
|
|
178
|
+
msg = "testing logging format"
|
|
179
|
+
env.logger.info(msg)
|
|
180
|
+
log_json = env.load_json()
|
|
181
|
+
|
|
182
|
+
assert log_json["message"] == msg
|
|
183
|
+
assert log_json.keys() == {"levelname", "message", "filename", "lineno", "asctime"}
|
|
184
|
+
return
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
@pytest.mark.parametrize("class_", ALL_FORMATTERS)
|
|
188
|
+
def test_sequence_list_format(env: LoggingEnvironment, class_: type[BaseJsonFormatter]):
|
|
189
|
+
env.set_formatter(class_(["levelname", "message", "filename", "lineno", "asctime"]))
|
|
190
|
+
|
|
191
|
+
msg = "testing logging format"
|
|
192
|
+
env.logger.info(msg)
|
|
193
|
+
log_json = env.load_json()
|
|
194
|
+
|
|
195
|
+
assert log_json["message"] == msg
|
|
196
|
+
assert log_json.keys() == {"levelname", "message", "filename", "lineno", "asctime"}
|
|
197
|
+
return
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
@pytest.mark.parametrize("class_", ALL_FORMATTERS)
|
|
201
|
+
def test_sequence_tuple_format(env: LoggingEnvironment, class_: type[BaseJsonFormatter]):
|
|
202
|
+
env.set_formatter(class_(("levelname", "message", "filename", "lineno", "asctime")))
|
|
167
203
|
|
|
168
204
|
msg = "testing logging format"
|
|
169
205
|
env.logger.info(msg)
|
|
@@ -380,9 +416,9 @@ def test_log_extra(env: LoggingEnvironment, class_: type[BaseJsonFormatter]):
|
|
|
380
416
|
def test_custom_logic_adds_field(env: LoggingEnvironment, class_: type[BaseJsonFormatter]):
|
|
381
417
|
class CustomJsonFormatter(class_): # type: ignore[valid-type,misc]
|
|
382
418
|
|
|
383
|
-
def process_log_record(self,
|
|
384
|
-
|
|
385
|
-
return super().process_log_record(
|
|
419
|
+
def process_log_record(self, log_data):
|
|
420
|
+
log_data["custom"] = "value"
|
|
421
|
+
return super().process_log_record(log_data)
|
|
386
422
|
|
|
387
423
|
env.set_formatter(CustomJsonFormatter())
|
|
388
424
|
env.logger.info("message")
|
|
@@ -568,8 +604,14 @@ def test_common_types_encoded(
|
|
|
568
604
|
if pythonjsonlogger.MSGSPEC_AVAILABLE and class_ is MsgspecFormatter:
|
|
569
605
|
# Dataclass: https://github.com/jcrist/msgspec/issues/681
|
|
570
606
|
# Enum: https://github.com/jcrist/msgspec/issues/680
|
|
571
|
-
|
|
572
|
-
|
|
607
|
+
# These have been fixed in msgspec 0.19.0, however they also dropped python 3.8 support.
|
|
608
|
+
# https://github.com/jcrist/msgspec/releases/tag/0.19.0
|
|
609
|
+
if sys.version_info < (3, 9) and (
|
|
610
|
+
obj is SomeDataclass
|
|
611
|
+
or (
|
|
612
|
+
isinstance(obj, enum.Enum)
|
|
613
|
+
and obj in {MultiEnum.BYTES, MultiEnum.NONE, MultiEnum.BOOL}
|
|
614
|
+
)
|
|
573
615
|
):
|
|
574
616
|
pytest.xfail()
|
|
575
617
|
|
|
@@ -616,6 +658,55 @@ def test_custom_default(env: LoggingEnvironment, class_: type[BaseJsonFormatter]
|
|
|
616
658
|
return
|
|
617
659
|
|
|
618
660
|
|
|
661
|
+
@pytest.mark.parametrize("class_", ALL_FORMATTERS)
|
|
662
|
+
def test_exc_info_as_array(env: LoggingEnvironment, class_: type[BaseJsonFormatter]):
|
|
663
|
+
env.set_formatter(class_(exc_info_as_array=True))
|
|
664
|
+
|
|
665
|
+
try:
|
|
666
|
+
raise Exception("Error")
|
|
667
|
+
except BaseException:
|
|
668
|
+
env.logger.exception("Error occurs")
|
|
669
|
+
log_json = env.load_json()
|
|
670
|
+
|
|
671
|
+
assert isinstance(log_json["exc_info"], list)
|
|
672
|
+
return
|
|
673
|
+
|
|
674
|
+
|
|
675
|
+
@pytest.mark.parametrize("class_", ALL_FORMATTERS)
|
|
676
|
+
def test_exc_info_as_array_no_exc_info(env: LoggingEnvironment, class_: type[BaseJsonFormatter]):
|
|
677
|
+
env.set_formatter(class_(exc_info_as_array=True))
|
|
678
|
+
|
|
679
|
+
env.logger.info("hello")
|
|
680
|
+
log_json = env.load_json()
|
|
681
|
+
|
|
682
|
+
assert "exc_info" not in log_json
|
|
683
|
+
return
|
|
684
|
+
|
|
685
|
+
|
|
686
|
+
@pytest.mark.parametrize("class_", ALL_FORMATTERS)
|
|
687
|
+
def test_stack_info_as_array(env: LoggingEnvironment, class_: type[BaseJsonFormatter]):
|
|
688
|
+
env.set_formatter(class_(stack_info_as_array=True))
|
|
689
|
+
|
|
690
|
+
env.logger.info("hello", stack_info=True)
|
|
691
|
+
log_json = env.load_json()
|
|
692
|
+
|
|
693
|
+
assert isinstance(log_json["stack_info"], list)
|
|
694
|
+
return
|
|
695
|
+
|
|
696
|
+
|
|
697
|
+
@pytest.mark.parametrize("class_", ALL_FORMATTERS)
|
|
698
|
+
def test_stack_info_as_array_no_stack_info(
|
|
699
|
+
env: LoggingEnvironment, class_: type[BaseJsonFormatter]
|
|
700
|
+
):
|
|
701
|
+
env.set_formatter(class_(stack_info_as_array=True))
|
|
702
|
+
|
|
703
|
+
env.logger.info("hello", stack_info=False)
|
|
704
|
+
log_json = env.load_json()
|
|
705
|
+
|
|
706
|
+
assert "stack_info" not in log_json
|
|
707
|
+
return
|
|
708
|
+
|
|
709
|
+
|
|
619
710
|
## JsonFormatter Specific
|
|
620
711
|
## -----------------------------------------------------------------------------
|
|
621
712
|
def test_json_ensure_ascii_true(env: LoggingEnvironment):
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_json_logger-3.2.1.dev1 → python_json_logger-4.0.0}/src/pythonjsonlogger/exception.py
RENAMED
|
File without changes
|
{python_json_logger-3.2.1.dev1 → python_json_logger-4.0.0}/src/pythonjsonlogger/jsonlogger.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|