python-json-logger 4.0.0.dev0__tar.gz → 4.0.0rc1__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-4.0.0.dev0/src/python_json_logger.egg-info → python_json_logger-4.0.0rc1}/PKG-INFO +1 -1
- {python_json_logger-4.0.0.dev0 → python_json_logger-4.0.0rc1}/pyproject.toml +1 -1
- {python_json_logger-4.0.0.dev0 → python_json_logger-4.0.0rc1/src/python_json_logger.egg-info}/PKG-INFO +1 -1
- {python_json_logger-4.0.0.dev0 → python_json_logger-4.0.0rc1}/src/pythonjsonlogger/core.py +86 -70
- {python_json_logger-4.0.0.dev0 → python_json_logger-4.0.0rc1}/src/pythonjsonlogger/json.py +9 -9
- {python_json_logger-4.0.0.dev0 → python_json_logger-4.0.0rc1}/src/pythonjsonlogger/msgspec.py +6 -6
- {python_json_logger-4.0.0.dev0 → python_json_logger-4.0.0rc1}/src/pythonjsonlogger/orjson.py +6 -6
- {python_json_logger-4.0.0.dev0 → python_json_logger-4.0.0rc1}/tests/test_dictconfig.py +17 -1
- {python_json_logger-4.0.0.dev0 → python_json_logger-4.0.0rc1}/tests/test_formatters.py +45 -9
- {python_json_logger-4.0.0.dev0 → python_json_logger-4.0.0rc1}/LICENSE +0 -0
- {python_json_logger-4.0.0.dev0 → python_json_logger-4.0.0rc1}/MANIFEST.in +0 -0
- {python_json_logger-4.0.0.dev0 → python_json_logger-4.0.0rc1}/NOTICE +0 -0
- {python_json_logger-4.0.0.dev0 → python_json_logger-4.0.0rc1}/README.md +0 -0
- {python_json_logger-4.0.0.dev0 → python_json_logger-4.0.0rc1}/setup.cfg +0 -0
- {python_json_logger-4.0.0.dev0 → python_json_logger-4.0.0rc1}/src/python_json_logger.egg-info/SOURCES.txt +0 -0
- {python_json_logger-4.0.0.dev0 → python_json_logger-4.0.0rc1}/src/python_json_logger.egg-info/dependency_links.txt +0 -0
- {python_json_logger-4.0.0.dev0 → python_json_logger-4.0.0rc1}/src/python_json_logger.egg-info/requires.txt +0 -0
- {python_json_logger-4.0.0.dev0 → python_json_logger-4.0.0rc1}/src/python_json_logger.egg-info/top_level.txt +0 -0
- {python_json_logger-4.0.0.dev0 → python_json_logger-4.0.0rc1}/src/pythonjsonlogger/__init__.py +0 -0
- {python_json_logger-4.0.0.dev0 → python_json_logger-4.0.0rc1}/src/pythonjsonlogger/defaults.py +0 -0
- {python_json_logger-4.0.0.dev0 → python_json_logger-4.0.0rc1}/src/pythonjsonlogger/exception.py +0 -0
- {python_json_logger-4.0.0.dev0 → python_json_logger-4.0.0rc1}/src/pythonjsonlogger/jsonlogger.py +0 -0
- {python_json_logger-4.0.0.dev0 → python_json_logger-4.0.0rc1}/src/pythonjsonlogger/py.typed +0 -0
- {python_json_logger-4.0.0.dev0 → python_json_logger-4.0.0rc1}/src/pythonjsonlogger/utils.py +0 -0
- {python_json_logger-4.0.0.dev0 → python_json_logger-4.0.0rc1}/tests/__init__.py +0 -0
- {python_json_logger-4.0.0.dev0 → python_json_logger-4.0.0rc1}/tests/test_deprecation.py +0 -0
- {python_json_logger-4.0.0.dev0 → python_json_logger-4.0.0rc1}/tests/test_missing.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python-json-logger
|
|
3
|
-
Version: 4.0.
|
|
3
|
+
Version: 4.0.0rc1
|
|
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>
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "python-json-logger"
|
|
7
|
-
version = "4.0.0.
|
|
7
|
+
version = "4.0.0.rc1"
|
|
8
8
|
description = "JSON Log Formatter for the Python Logging Package"
|
|
9
9
|
authors = [
|
|
10
10
|
{name = "Zakaria Zajac", email = "zak@madzak.com"},
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python-json-logger
|
|
3
|
-
Version: 4.0.
|
|
3
|
+
Version: 4.0.0rc1
|
|
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>
|
|
@@ -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,
|
|
@@ -135,7 +118,7 @@ class BaseJsonFormatter(logging.Formatter):
|
|
|
135
118
|
|
|
136
119
|
*Changed in 3.2*: `defaults` argument is no longer ignored.
|
|
137
120
|
|
|
138
|
-
*Added in
|
|
121
|
+
*Added in 3.3*: `exc_info_as_array` and `stack_info_as_array` options are added.
|
|
139
122
|
"""
|
|
140
123
|
|
|
141
124
|
_style: Union[logging.PercentStyle, str] # type: ignore[assignment]
|
|
@@ -145,7 +128,7 @@ class BaseJsonFormatter(logging.Formatter):
|
|
|
145
128
|
# pylint: disable=too-many-arguments,super-init-not-called
|
|
146
129
|
def __init__(
|
|
147
130
|
self,
|
|
148
|
-
fmt: Optional[str] = None,
|
|
131
|
+
fmt: Optional[Union[str, Sequence[str]]] = None,
|
|
149
132
|
datefmt: Optional[str] = None,
|
|
150
133
|
style: str = "%",
|
|
151
134
|
validate: bool = True,
|
|
@@ -162,11 +145,11 @@ class BaseJsonFormatter(logging.Formatter):
|
|
|
162
145
|
) -> None:
|
|
163
146
|
"""
|
|
164
147
|
Args:
|
|
165
|
-
fmt:
|
|
148
|
+
fmt: String format or `Sequence` of field names of fields to log.
|
|
166
149
|
datefmt: format to use when formatting `asctime` field
|
|
167
|
-
style: how to extract log fields from `fmt`
|
|
150
|
+
style: how to extract log fields from `fmt`. Ignored if `fmt` is a `Sequence[str]`.
|
|
168
151
|
validate: validate `fmt` against style, if implementing a custom `style` you
|
|
169
|
-
must set this to `False`.
|
|
152
|
+
must set this to `False`. Ignored if `fmt` is a `Sequence[str]`.
|
|
170
153
|
defaults: a dictionary containing default fields that are added before all other fields and
|
|
171
154
|
may be overridden. The supplied fields are still subject to `rename_fields`.
|
|
172
155
|
prefix: an optional string prefix added at the beginning of
|
|
@@ -192,23 +175,40 @@ class BaseJsonFormatter(logging.Formatter):
|
|
|
192
175
|
- Renaming fields now preserves the order that fields were added in and avoids adding
|
|
193
176
|
missing fields. The original behaviour, missing fields have a value of `None`, is still
|
|
194
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.
|
|
195
185
|
"""
|
|
196
186
|
## logging.Formatter compatibility
|
|
197
187
|
## ---------------------------------------------------------------------
|
|
198
|
-
# Note: validate added in 3.8, defaults added in 3.10
|
|
199
|
-
if
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
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)
|
|
212
212
|
|
|
213
213
|
self.datefmt = datefmt
|
|
214
214
|
|
|
@@ -230,7 +230,6 @@ class BaseJsonFormatter(logging.Formatter):
|
|
|
230
230
|
self.reserved_attrs = set(reserved_attrs if reserved_attrs is not None else RESERVED_ATTRS)
|
|
231
231
|
self.timestamp = timestamp
|
|
232
232
|
|
|
233
|
-
self._required_fields = self.parse()
|
|
234
233
|
self._skip_fields = set(self._required_fields)
|
|
235
234
|
self._skip_fields.update(self.reserved_attrs)
|
|
236
235
|
self.defaults = defaults if defaults is not None else {}
|
|
@@ -269,11 +268,11 @@ class BaseJsonFormatter(logging.Formatter):
|
|
|
269
268
|
if record.stack_info and not message_dict.get("stack_info"):
|
|
270
269
|
message_dict["stack_info"] = self.formatStack(record.stack_info)
|
|
271
270
|
|
|
272
|
-
|
|
273
|
-
self.add_fields(
|
|
274
|
-
|
|
271
|
+
log_data: LogData = {}
|
|
272
|
+
self.add_fields(log_data, record, message_dict)
|
|
273
|
+
log_data = self.process_log_record(log_data)
|
|
275
274
|
|
|
276
|
-
return self.serialize_log_record(
|
|
275
|
+
return self.serialize_log_record(log_data)
|
|
277
276
|
|
|
278
277
|
## JSON Formatter Specific Methods
|
|
279
278
|
## -------------------------------------------------------------------------
|
|
@@ -288,6 +287,18 @@ class BaseJsonFormatter(logging.Formatter):
|
|
|
288
287
|
Returns:
|
|
289
288
|
list of fields to be extracted and serialized
|
|
290
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
|
+
|
|
291
302
|
if isinstance(self._style, logging.StringTemplateStyle):
|
|
292
303
|
formatter_style_pattern = STYLE_STRING_TEMPLATE_REGEX
|
|
293
304
|
|
|
@@ -302,22 +313,21 @@ class BaseJsonFormatter(logging.Formatter):
|
|
|
302
313
|
else:
|
|
303
314
|
raise ValueError(f"Style {self._style!r} is not supported")
|
|
304
315
|
|
|
305
|
-
|
|
306
|
-
return formatter_style_pattern.findall(self._fmt)
|
|
307
|
-
|
|
308
|
-
return []
|
|
316
|
+
return formatter_style_pattern.findall(self._fmt)
|
|
309
317
|
|
|
310
|
-
def serialize_log_record(self,
|
|
311
|
-
"""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
|
|
312
320
|
|
|
313
321
|
Args:
|
|
314
|
-
|
|
322
|
+
log_data: the data
|
|
323
|
+
|
|
324
|
+
*Changed in 4.0*: `log_record` renamed to `log_data`
|
|
315
325
|
"""
|
|
316
|
-
return self.prefix + self.jsonify_log_record(
|
|
326
|
+
return self.prefix + self.jsonify_log_record(log_data)
|
|
317
327
|
|
|
318
328
|
def add_fields(
|
|
319
329
|
self,
|
|
320
|
-
|
|
330
|
+
log_data: Dict[str, Any],
|
|
321
331
|
record: logging.LogRecord,
|
|
322
332
|
message_dict: Dict[str, Any],
|
|
323
333
|
) -> None:
|
|
@@ -326,38 +336,40 @@ class BaseJsonFormatter(logging.Formatter):
|
|
|
326
336
|
This method can be overridden to implement custom logic for adding fields.
|
|
327
337
|
|
|
328
338
|
Args:
|
|
329
|
-
|
|
339
|
+
log_data: data that will be logged
|
|
330
340
|
record: the record to extract data from
|
|
331
341
|
message_dict: dictionary that was logged instead of a message. e.g
|
|
332
342
|
`logger.info({"is_this_message_dict": True})`
|
|
343
|
+
|
|
344
|
+
*Changed in 4.0*: `log_record` renamed to `log_data`
|
|
333
345
|
"""
|
|
334
346
|
for field in self.defaults:
|
|
335
|
-
|
|
347
|
+
log_data[self._get_rename(field)] = self.defaults[field]
|
|
336
348
|
|
|
337
349
|
for field in self._required_fields:
|
|
338
|
-
|
|
350
|
+
log_data[self._get_rename(field)] = record.__dict__.get(field)
|
|
339
351
|
|
|
340
352
|
for data_dict in [self.static_fields, message_dict]:
|
|
341
353
|
for key, value in data_dict.items():
|
|
342
|
-
|
|
354
|
+
log_data[self._get_rename(key)] = value
|
|
343
355
|
|
|
344
356
|
merge_record_extra(
|
|
345
357
|
record,
|
|
346
|
-
|
|
358
|
+
log_data,
|
|
347
359
|
reserved=self._skip_fields,
|
|
348
360
|
rename_fields=self.rename_fields,
|
|
349
361
|
)
|
|
350
362
|
|
|
351
363
|
if self.timestamp:
|
|
352
364
|
key = self.timestamp if isinstance(self.timestamp, str) else "timestamp"
|
|
353
|
-
|
|
365
|
+
log_data[self._get_rename(key)] = datetime.fromtimestamp(
|
|
354
366
|
record.created, tz=timezone.utc
|
|
355
367
|
)
|
|
356
368
|
|
|
357
369
|
if self.rename_fields_keep_missing:
|
|
358
370
|
for field in self.rename_fields.values():
|
|
359
|
-
if field not in
|
|
360
|
-
|
|
371
|
+
if field not in log_data:
|
|
372
|
+
log_data[field] = None
|
|
361
373
|
return
|
|
362
374
|
|
|
363
375
|
def _get_rename(self, key: str) -> str:
|
|
@@ -365,26 +377,30 @@ class BaseJsonFormatter(logging.Formatter):
|
|
|
365
377
|
|
|
366
378
|
# Child Methods
|
|
367
379
|
# ..........................................................................
|
|
368
|
-
def jsonify_log_record(self,
|
|
369
|
-
"""Convert
|
|
380
|
+
def jsonify_log_record(self, log_data: LogData) -> str:
|
|
381
|
+
"""Convert the log data into a JSON string.
|
|
370
382
|
|
|
371
383
|
Child classes MUST override this method.
|
|
372
384
|
|
|
373
385
|
Args:
|
|
374
|
-
|
|
386
|
+
log_data: the data to serialize
|
|
387
|
+
|
|
388
|
+
*Changed in 4.0*: `log_record` renamed to `log_data`
|
|
375
389
|
"""
|
|
376
390
|
raise NotImplementedError()
|
|
377
391
|
|
|
378
|
-
def process_log_record(self,
|
|
379
|
-
"""Custom processing of the
|
|
392
|
+
def process_log_record(self, log_data: LogData) -> LogData:
|
|
393
|
+
"""Custom processing of the data to be logged.
|
|
380
394
|
|
|
381
395
|
Child classes can override this method to alter the log record before it
|
|
382
396
|
is serialized.
|
|
383
397
|
|
|
384
398
|
Args:
|
|
385
|
-
|
|
399
|
+
log_data: incoming data
|
|
400
|
+
|
|
401
|
+
*Changed in 4.0*: `log_record` renamed to `log_data`
|
|
386
402
|
"""
|
|
387
|
-
return
|
|
403
|
+
return log_data
|
|
388
404
|
|
|
389
405
|
def formatException(self, ei) -> Union[str, list[str]]: # type: ignore
|
|
390
406
|
"""Format and return the specified exception information.
|
|
@@ -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,
|
{python_json_logger-4.0.0.dev0 → python_json_logger-4.0.0rc1}/src/pythonjsonlogger/msgspec.py
RENAMED
|
@@ -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")
|
{python_json_logger-4.0.0.dev0 → python_json_logger-4.0.0rc1}/src/pythonjsonlogger/orjson.py
RENAMED
|
@@ -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")
|
|
@@ -19,12 +19,24 @@ import pytest
|
|
|
19
19
|
_LOGGER_COUNT = 0
|
|
20
20
|
EXT_VAL = 999
|
|
21
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
|
+
|
|
22
33
|
LOGGING_CONFIG = {
|
|
23
34
|
"version": 1,
|
|
24
35
|
"disable_existing_loggers": False,
|
|
25
36
|
"formatters": {
|
|
26
37
|
"default": {
|
|
27
38
|
"()": "pythonjsonlogger.json.JsonFormatter",
|
|
39
|
+
"json_default": "ext://tests.test_dictconfig.my_json_default",
|
|
28
40
|
"static_fields": {"ext-val": "ext://tests.test_dictconfig.EXT_VAL"},
|
|
29
41
|
}
|
|
30
42
|
},
|
|
@@ -73,8 +85,12 @@ def env() -> Generator[LoggingEnvironment, None, None]:
|
|
|
73
85
|
### TESTS
|
|
74
86
|
### ============================================================================
|
|
75
87
|
def test_external_reference_support(env: LoggingEnvironment):
|
|
76
|
-
|
|
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()})
|
|
77
92
|
log_json = env.load_json()
|
|
78
93
|
|
|
79
94
|
assert log_json["ext-val"] == EXT_VAL
|
|
95
|
+
assert log_json["dummy"] == "DUMMY"
|
|
80
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")
|
|
File without changes
|
|
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-4.0.0.dev0 → python_json_logger-4.0.0rc1}/src/pythonjsonlogger/__init__.py
RENAMED
|
File without changes
|
{python_json_logger-4.0.0.dev0 → python_json_logger-4.0.0rc1}/src/pythonjsonlogger/defaults.py
RENAMED
|
File without changes
|
{python_json_logger-4.0.0.dev0 → python_json_logger-4.0.0rc1}/src/pythonjsonlogger/exception.py
RENAMED
|
File without changes
|
{python_json_logger-4.0.0.dev0 → python_json_logger-4.0.0rc1}/src/pythonjsonlogger/jsonlogger.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|