python-json-logger 4.0.0rc1__tar.gz → 4.2.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-4.0.0rc1/src/python_json_logger.egg-info → python_json_logger-4.2.0}/PKG-INFO +4 -28
- {python_json_logger-4.0.0rc1 → python_json_logger-4.2.0}/pyproject.toml +8 -12
- {python_json_logger-4.0.0rc1 → python_json_logger-4.2.0/src/python_json_logger.egg-info}/PKG-INFO +4 -28
- {python_json_logger-4.0.0rc1 → python_json_logger-4.2.0}/src/python_json_logger.egg-info/SOURCES.txt +0 -1
- {python_json_logger-4.0.0rc1 → python_json_logger-4.2.0}/src/pythonjsonlogger/core.py +47 -42
- {python_json_logger-4.0.0rc1 → python_json_logger-4.2.0}/src/pythonjsonlogger/defaults.py +1 -7
- {python_json_logger-4.0.0rc1 → python_json_logger-4.2.0}/src/pythonjsonlogger/json.py +5 -4
- {python_json_logger-4.0.0rc1 → python_json_logger-4.2.0}/src/pythonjsonlogger/msgspec.py +3 -2
- {python_json_logger-4.0.0rc1 → python_json_logger-4.2.0}/src/pythonjsonlogger/orjson.py +4 -3
- {python_json_logger-4.0.0rc1 → python_json_logger-4.2.0}/tests/test_dictconfig.py +3 -2
- {python_json_logger-4.0.0rc1 → python_json_logger-4.2.0}/tests/test_formatters.py +180 -7
- python_json_logger-4.0.0rc1/src/python_json_logger.egg-info/requires.txt +0 -28
- {python_json_logger-4.0.0rc1 → python_json_logger-4.2.0}/LICENSE +0 -0
- {python_json_logger-4.0.0rc1 → python_json_logger-4.2.0}/MANIFEST.in +0 -0
- {python_json_logger-4.0.0rc1 → python_json_logger-4.2.0}/NOTICE +0 -0
- {python_json_logger-4.0.0rc1 → python_json_logger-4.2.0}/README.md +0 -0
- {python_json_logger-4.0.0rc1 → python_json_logger-4.2.0}/setup.cfg +0 -0
- {python_json_logger-4.0.0rc1 → python_json_logger-4.2.0}/src/python_json_logger.egg-info/dependency_links.txt +0 -0
- {python_json_logger-4.0.0rc1 → python_json_logger-4.2.0}/src/python_json_logger.egg-info/top_level.txt +0 -0
- {python_json_logger-4.0.0rc1 → python_json_logger-4.2.0}/src/pythonjsonlogger/__init__.py +0 -0
- {python_json_logger-4.0.0rc1 → python_json_logger-4.2.0}/src/pythonjsonlogger/exception.py +0 -0
- {python_json_logger-4.0.0rc1 → python_json_logger-4.2.0}/src/pythonjsonlogger/jsonlogger.py +0 -0
- {python_json_logger-4.0.0rc1 → python_json_logger-4.2.0}/src/pythonjsonlogger/py.typed +0 -0
- {python_json_logger-4.0.0rc1 → python_json_logger-4.2.0}/src/pythonjsonlogger/utils.py +0 -0
- {python_json_logger-4.0.0rc1 → python_json_logger-4.2.0}/tests/__init__.py +0 -0
- {python_json_logger-4.0.0rc1 → python_json_logger-4.2.0}/tests/test_deprecation.py +0 -0
- {python_json_logger-4.0.0rc1 → python_json_logger-4.2.0}/tests/test_missing.py +0 -0
{python_json_logger-4.0.0rc1/src/python_json_logger.egg-info → python_json_logger-4.2.0}/PKG-INFO
RENAMED
|
@@ -1,50 +1,26 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python-json-logger
|
|
3
|
-
Version: 4.0
|
|
3
|
+
Version: 4.2.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>
|
|
7
|
-
License: BSD-2-Clause
|
|
7
|
+
License-Expression: BSD-2-Clause
|
|
8
8
|
Project-URL: Homepage, https://nhairs.github.io/python-json-logger
|
|
9
9
|
Project-URL: GitHub, https://github.com/nhairs/python-json-logger
|
|
10
10
|
Classifier: Development Status :: 6 - Mature
|
|
11
11
|
Classifier: Intended Audience :: Developers
|
|
12
|
-
Classifier: License :: OSI Approved :: BSD License
|
|
13
12
|
Classifier: Operating System :: OS Independent
|
|
14
13
|
Classifier: Programming Language :: Python :: 3 :: Only
|
|
15
|
-
Classifier: Programming Language :: Python :: 3.8
|
|
16
|
-
Classifier: Programming Language :: Python :: 3.9
|
|
17
14
|
Classifier: Programming Language :: Python :: 3.10
|
|
18
15
|
Classifier: Programming Language :: Python :: 3.11
|
|
19
16
|
Classifier: Programming Language :: Python :: 3.12
|
|
20
17
|
Classifier: Programming Language :: Python :: 3.13
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
21
19
|
Classifier: Topic :: System :: Logging
|
|
22
20
|
Classifier: Typing :: Typed
|
|
23
|
-
Requires-Python: >=3.
|
|
21
|
+
Requires-Python: >=3.10
|
|
24
22
|
Description-Content-Type: text/markdown
|
|
25
23
|
License-File: LICENSE
|
|
26
|
-
License-File: NOTICE
|
|
27
|
-
Requires-Dist: typing_extensions; python_version < "3.10"
|
|
28
|
-
Provides-Extra: dev
|
|
29
|
-
Requires-Dist: orjson; implementation_name != "pypy" and extra == "dev"
|
|
30
|
-
Requires-Dist: msgspec; implementation_name != "pypy" and extra == "dev"
|
|
31
|
-
Requires-Dist: validate-pyproject[all]; extra == "dev"
|
|
32
|
-
Requires-Dist: black; extra == "dev"
|
|
33
|
-
Requires-Dist: pylint; extra == "dev"
|
|
34
|
-
Requires-Dist: mypy; extra == "dev"
|
|
35
|
-
Requires-Dist: pytest; extra == "dev"
|
|
36
|
-
Requires-Dist: freezegun; extra == "dev"
|
|
37
|
-
Requires-Dist: backports.zoneinfo; python_version < "3.9" and extra == "dev"
|
|
38
|
-
Requires-Dist: tzdata; extra == "dev"
|
|
39
|
-
Requires-Dist: build; extra == "dev"
|
|
40
|
-
Requires-Dist: mkdocs; extra == "dev"
|
|
41
|
-
Requires-Dist: mkdocs-material>=8.5; extra == "dev"
|
|
42
|
-
Requires-Dist: mkdocs-awesome-pages-plugin; extra == "dev"
|
|
43
|
-
Requires-Dist: mdx_truly_sane_lists; extra == "dev"
|
|
44
|
-
Requires-Dist: mkdocstrings[python]; extra == "dev"
|
|
45
|
-
Requires-Dist: mkdocs-gen-files; extra == "dev"
|
|
46
|
-
Requires-Dist: mkdocs-literate-nav; extra == "dev"
|
|
47
|
-
Requires-Dist: mike; extra == "dev"
|
|
48
24
|
Dynamic: license-file
|
|
49
25
|
|
|
50
26
|
[](https://pypi.python.org/pypi/python-json-logger/)
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "python-json-logger"
|
|
7
|
-
version = "4.
|
|
7
|
+
version = "4.2.0"
|
|
8
8
|
description = "JSON Log Formatter for the Python Logging Package"
|
|
9
9
|
authors = [
|
|
10
10
|
{name = "Zakaria Zajac", email = "zak@madzak.com"},
|
|
@@ -15,26 +15,22 @@ maintainers = [
|
|
|
15
15
|
]
|
|
16
16
|
|
|
17
17
|
# Dependency Information
|
|
18
|
-
requires-python = ">=3.
|
|
19
|
-
dependencies = [
|
|
20
|
-
"typing_extensions;python_version<'3.10'",
|
|
21
|
-
]
|
|
18
|
+
requires-python = ">=3.10"
|
|
22
19
|
|
|
23
20
|
# Extra information
|
|
24
21
|
readme = "README.md"
|
|
25
|
-
license =
|
|
22
|
+
license = "BSD-2-Clause"
|
|
23
|
+
license-files = ["LICENSE"]
|
|
26
24
|
classifiers = [
|
|
27
25
|
"Development Status :: 6 - Mature",
|
|
28
26
|
"Intended Audience :: Developers",
|
|
29
|
-
"License :: OSI Approved :: BSD License",
|
|
30
27
|
"Operating System :: OS Independent",
|
|
31
28
|
"Programming Language :: Python :: 3 :: Only",
|
|
32
|
-
"Programming Language :: Python :: 3.8",
|
|
33
|
-
"Programming Language :: Python :: 3.9",
|
|
34
29
|
"Programming Language :: Python :: 3.10",
|
|
35
30
|
"Programming Language :: Python :: 3.11",
|
|
36
31
|
"Programming Language :: Python :: 3.12",
|
|
37
32
|
"Programming Language :: Python :: 3.13",
|
|
33
|
+
"Programming Language :: Python :: 3.14",
|
|
38
34
|
"Topic :: System :: Logging",
|
|
39
35
|
"Typing :: Typed",
|
|
40
36
|
]
|
|
@@ -43,7 +39,7 @@ classifiers = [
|
|
|
43
39
|
Homepage = "https://nhairs.github.io/python-json-logger"
|
|
44
40
|
GitHub = "https://github.com/nhairs/python-json-logger"
|
|
45
41
|
|
|
46
|
-
[
|
|
42
|
+
[dependency-groups]
|
|
47
43
|
dev = [
|
|
48
44
|
## Optional but required for dev
|
|
49
45
|
"orjson;implementation_name!='pypy'",
|
|
@@ -52,11 +48,11 @@ dev = [
|
|
|
52
48
|
"validate-pyproject[all]",
|
|
53
49
|
"black",
|
|
54
50
|
"pylint",
|
|
55
|
-
"mypy",
|
|
51
|
+
"mypy<2; implementation_name == 'pypy' and python_version < '3.11'",
|
|
52
|
+
"mypy; implementation_name != 'pypy' or python_version >= '3.11'",
|
|
56
53
|
## Test
|
|
57
54
|
"pytest",
|
|
58
55
|
"freezegun",
|
|
59
|
-
"backports.zoneinfo;python_version<'3.9'",
|
|
60
56
|
"tzdata",
|
|
61
57
|
## Build
|
|
62
58
|
"build",
|
{python_json_logger-4.0.0rc1 → python_json_logger-4.2.0/src/python_json_logger.egg-info}/PKG-INFO
RENAMED
|
@@ -1,50 +1,26 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python-json-logger
|
|
3
|
-
Version: 4.0
|
|
3
|
+
Version: 4.2.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>
|
|
7
|
-
License: BSD-2-Clause
|
|
7
|
+
License-Expression: BSD-2-Clause
|
|
8
8
|
Project-URL: Homepage, https://nhairs.github.io/python-json-logger
|
|
9
9
|
Project-URL: GitHub, https://github.com/nhairs/python-json-logger
|
|
10
10
|
Classifier: Development Status :: 6 - Mature
|
|
11
11
|
Classifier: Intended Audience :: Developers
|
|
12
|
-
Classifier: License :: OSI Approved :: BSD License
|
|
13
12
|
Classifier: Operating System :: OS Independent
|
|
14
13
|
Classifier: Programming Language :: Python :: 3 :: Only
|
|
15
|
-
Classifier: Programming Language :: Python :: 3.8
|
|
16
|
-
Classifier: Programming Language :: Python :: 3.9
|
|
17
14
|
Classifier: Programming Language :: Python :: 3.10
|
|
18
15
|
Classifier: Programming Language :: Python :: 3.11
|
|
19
16
|
Classifier: Programming Language :: Python :: 3.12
|
|
20
17
|
Classifier: Programming Language :: Python :: 3.13
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
21
19
|
Classifier: Topic :: System :: Logging
|
|
22
20
|
Classifier: Typing :: Typed
|
|
23
|
-
Requires-Python: >=3.
|
|
21
|
+
Requires-Python: >=3.10
|
|
24
22
|
Description-Content-Type: text/markdown
|
|
25
23
|
License-File: LICENSE
|
|
26
|
-
License-File: NOTICE
|
|
27
|
-
Requires-Dist: typing_extensions; python_version < "3.10"
|
|
28
|
-
Provides-Extra: dev
|
|
29
|
-
Requires-Dist: orjson; implementation_name != "pypy" and extra == "dev"
|
|
30
|
-
Requires-Dist: msgspec; implementation_name != "pypy" and extra == "dev"
|
|
31
|
-
Requires-Dist: validate-pyproject[all]; extra == "dev"
|
|
32
|
-
Requires-Dist: black; extra == "dev"
|
|
33
|
-
Requires-Dist: pylint; extra == "dev"
|
|
34
|
-
Requires-Dist: mypy; extra == "dev"
|
|
35
|
-
Requires-Dist: pytest; extra == "dev"
|
|
36
|
-
Requires-Dist: freezegun; extra == "dev"
|
|
37
|
-
Requires-Dist: backports.zoneinfo; python_version < "3.9" and extra == "dev"
|
|
38
|
-
Requires-Dist: tzdata; extra == "dev"
|
|
39
|
-
Requires-Dist: build; extra == "dev"
|
|
40
|
-
Requires-Dist: mkdocs; extra == "dev"
|
|
41
|
-
Requires-Dist: mkdocs-material>=8.5; extra == "dev"
|
|
42
|
-
Requires-Dist: mkdocs-awesome-pages-plugin; extra == "dev"
|
|
43
|
-
Requires-Dist: mdx_truly_sane_lists; extra == "dev"
|
|
44
|
-
Requires-Dist: mkdocstrings[python]; extra == "dev"
|
|
45
|
-
Requires-Dist: mkdocs-gen-files; extra == "dev"
|
|
46
|
-
Requires-Dist: mkdocs-literate-nav; extra == "dev"
|
|
47
|
-
Requires-Dist: mike; extra == "dev"
|
|
48
24
|
Dynamic: license-file
|
|
49
25
|
|
|
50
26
|
[](https://pypi.python.org/pypi/python-json-logger/)
|
{python_json_logger-4.0.0rc1 → python_json_logger-4.2.0}/src/python_json_logger.egg-info/SOURCES.txt
RENAMED
|
@@ -6,7 +6,6 @@ pyproject.toml
|
|
|
6
6
|
src/python_json_logger.egg-info/PKG-INFO
|
|
7
7
|
src/python_json_logger.egg-info/SOURCES.txt
|
|
8
8
|
src/python_json_logger.egg-info/dependency_links.txt
|
|
9
|
-
src/python_json_logger.egg-info/requires.txt
|
|
10
9
|
src/python_json_logger.egg-info/top_level.txt
|
|
11
10
|
src/pythonjsonlogger/__init__.py
|
|
12
11
|
src/pythonjsonlogger/core.py
|
|
@@ -10,12 +10,8 @@ from datetime import datetime, timezone
|
|
|
10
10
|
import logging
|
|
11
11
|
import re
|
|
12
12
|
import sys
|
|
13
|
-
from typing import
|
|
14
|
-
|
|
15
|
-
if sys.version_info >= (3, 10):
|
|
16
|
-
from typing import TypeAlias
|
|
17
|
-
else:
|
|
18
|
-
from typing_extensions import TypeAlias
|
|
13
|
+
from typing import TypeAlias, Any
|
|
14
|
+
from collections.abc import Container, Sequence
|
|
19
15
|
|
|
20
16
|
## Installed
|
|
21
17
|
|
|
@@ -24,7 +20,7 @@ else:
|
|
|
24
20
|
|
|
25
21
|
### CONSTANTS
|
|
26
22
|
### ============================================================================
|
|
27
|
-
RESERVED_ATTRS:
|
|
23
|
+
RESERVED_ATTRS: list[str] = [
|
|
28
24
|
"args",
|
|
29
25
|
"asctime",
|
|
30
26
|
"created",
|
|
@@ -65,13 +61,15 @@ if sys.version_info >= (3, 12):
|
|
|
65
61
|
RESERVED_ATTRS.sort()
|
|
66
62
|
|
|
67
63
|
|
|
68
|
-
STYLE_STRING_TEMPLATE_REGEX = re.compile(
|
|
64
|
+
STYLE_STRING_TEMPLATE_REGEX = re.compile(
|
|
65
|
+
r"\$(?:\$|\{(?P<braced>.+?)\}|(?P<named>[_a-z][_a-z0-9]*))", re.IGNORECASE
|
|
66
|
+
) # $ style
|
|
69
67
|
STYLE_STRING_FORMAT_REGEX = re.compile(r"\{(.+?)\}", re.IGNORECASE) # { style
|
|
70
68
|
STYLE_PERCENT_REGEX = re.compile(r"%\((.+?)\)", re.IGNORECASE) # % style
|
|
71
69
|
|
|
72
70
|
## Type Aliases
|
|
73
71
|
## -----------------------------------------------------------------------------
|
|
74
|
-
LogData: TypeAlias =
|
|
72
|
+
LogData: TypeAlias = dict[str, Any]
|
|
75
73
|
"""Type alias
|
|
76
74
|
|
|
77
75
|
*Changed in 4.0*: renamed from `LogRecord` to `LogData`
|
|
@@ -82,10 +80,10 @@ LogData: TypeAlias = Dict[str, Any]
|
|
|
82
80
|
### ============================================================================
|
|
83
81
|
def merge_record_extra(
|
|
84
82
|
record: logging.LogRecord,
|
|
85
|
-
target:
|
|
83
|
+
target: dict[Any, Any],
|
|
86
84
|
reserved: Container[str],
|
|
87
|
-
rename_fields:
|
|
88
|
-
) ->
|
|
85
|
+
rename_fields: dict[str, str] | None = None,
|
|
86
|
+
) -> dict[Any, Any]:
|
|
89
87
|
"""
|
|
90
88
|
Merges extra attributes from LogRecord object into target dictionary
|
|
91
89
|
|
|
@@ -115,31 +113,27 @@ class BaseJsonFormatter(logging.Formatter):
|
|
|
115
113
|
Must not be used directly.
|
|
116
114
|
|
|
117
115
|
*New in 3.1*
|
|
118
|
-
|
|
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.
|
|
122
116
|
"""
|
|
123
117
|
|
|
124
|
-
_style:
|
|
118
|
+
_style: logging.PercentStyle | str # type: ignore[assignment]
|
|
125
119
|
|
|
126
120
|
## Parent Methods
|
|
127
121
|
## -------------------------------------------------------------------------
|
|
128
122
|
# pylint: disable=too-many-arguments,super-init-not-called
|
|
129
123
|
def __init__(
|
|
130
124
|
self,
|
|
131
|
-
fmt:
|
|
132
|
-
datefmt:
|
|
125
|
+
fmt: str | Sequence[str] | None = None,
|
|
126
|
+
datefmt: str | None = None,
|
|
133
127
|
style: str = "%",
|
|
134
128
|
validate: bool = True,
|
|
135
129
|
*,
|
|
136
130
|
prefix: str = "",
|
|
137
|
-
rename_fields:
|
|
131
|
+
rename_fields: dict[str, str] | None = None,
|
|
138
132
|
rename_fields_keep_missing: bool = False,
|
|
139
|
-
static_fields:
|
|
140
|
-
reserved_attrs:
|
|
141
|
-
timestamp:
|
|
142
|
-
defaults:
|
|
133
|
+
static_fields: dict[str, Any] | None = None,
|
|
134
|
+
reserved_attrs: Sequence[str] | None = None,
|
|
135
|
+
timestamp: bool | str = False,
|
|
136
|
+
defaults: dict[str, Any] | None = None,
|
|
143
137
|
exc_info_as_array: bool = False,
|
|
144
138
|
stack_info_as_array: bool = False,
|
|
145
139
|
) -> None:
|
|
@@ -176,16 +170,19 @@ class BaseJsonFormatter(logging.Formatter):
|
|
|
176
170
|
missing fields. The original behaviour, missing fields have a value of `None`, is still
|
|
177
171
|
available by setting `rename_fields_keep_missing` to `True`.
|
|
178
172
|
|
|
173
|
+
*Changed in 3.2*: `defaults` argument is no longer ignored.
|
|
174
|
+
|
|
175
|
+
*Added in 3.3*: `exc_info_as_array` and `stack_info_as_array` options are added.
|
|
176
|
+
|
|
179
177
|
*Added in 4.0*:
|
|
180
178
|
|
|
181
|
-
- `fmt` now supports comma
|
|
179
|
+
- `fmt` now supports comma separated lists (`style=","`). Note that this style is specific
|
|
182
180
|
to `python-json-logger` and thus care should be taken to not to pass this format to other
|
|
183
181
|
logging Formatter implementations.
|
|
184
182
|
- `fmt` now supports sequences of strings (e.g. lists and tuples) of field names.
|
|
185
183
|
"""
|
|
186
184
|
## logging.Formatter compatibility
|
|
187
185
|
## ---------------------------------------------------------------------
|
|
188
|
-
# Note: validate added in python 3.8, defaults added in 3.10
|
|
189
186
|
if fmt is None or isinstance(fmt, str):
|
|
190
187
|
if style in logging._STYLES:
|
|
191
188
|
_style = logging._STYLES[style][0](fmt) # type: ignore[operator]
|
|
@@ -240,14 +237,20 @@ class BaseJsonFormatter(logging.Formatter):
|
|
|
240
237
|
def format(self, record: logging.LogRecord) -> str:
|
|
241
238
|
"""Formats a log record and serializes to json
|
|
242
239
|
|
|
240
|
+
When `record.msg` is a `dict`, adding `exc_info` and `stack_info` does not
|
|
241
|
+
modify the caller's dict.
|
|
242
|
+
|
|
243
243
|
Args:
|
|
244
244
|
record: the record to format
|
|
245
|
+
|
|
246
|
+
*Changed in 4.2.0*: a `dict` `record.msg` is copied instead of modified
|
|
247
|
+
in place.
|
|
245
248
|
"""
|
|
246
|
-
message_dict:
|
|
249
|
+
message_dict: dict[str, Any] = {}
|
|
247
250
|
# TODO: logging.LogRecord.msg and logging.LogRecord.message in typeshed
|
|
248
251
|
# are always type of str. We shouldn't need to override that.
|
|
249
252
|
if isinstance(record.msg, dict):
|
|
250
|
-
message_dict = record.msg
|
|
253
|
+
message_dict = record.msg.copy()
|
|
251
254
|
record.message = ""
|
|
252
255
|
else:
|
|
253
256
|
record.message = record.getMessage()
|
|
@@ -276,7 +279,7 @@ class BaseJsonFormatter(logging.Formatter):
|
|
|
276
279
|
|
|
277
280
|
## JSON Formatter Specific Methods
|
|
278
281
|
## -------------------------------------------------------------------------
|
|
279
|
-
def parse(self) ->
|
|
282
|
+
def parse(self) -> list[str]:
|
|
280
283
|
"""Parses format string looking for substitutions
|
|
281
284
|
|
|
282
285
|
This method is responsible for returning a list of fields (as strings)
|
|
@@ -300,20 +303,22 @@ class BaseJsonFormatter(logging.Formatter):
|
|
|
300
303
|
raise ValueError(f"Style {self._style!r} is not supported")
|
|
301
304
|
|
|
302
305
|
if isinstance(self._style, logging.StringTemplateStyle):
|
|
303
|
-
|
|
306
|
+
# String templates support both ${name} and $name, and $$ is an escaped literal
|
|
307
|
+
return [
|
|
308
|
+
match.group("braced") or match.group("named")
|
|
309
|
+
for match in STYLE_STRING_TEMPLATE_REGEX.finditer(self._fmt)
|
|
310
|
+
if match.group("braced") or match.group("named")
|
|
311
|
+
]
|
|
304
312
|
|
|
305
|
-
|
|
306
|
-
|
|
313
|
+
if isinstance(self._style, logging.StrFormatStyle):
|
|
314
|
+
return STYLE_STRING_FORMAT_REGEX.findall(self._fmt)
|
|
307
315
|
|
|
308
|
-
|
|
316
|
+
if isinstance(self._style, logging.PercentStyle):
|
|
309
317
|
# PercentStyle is parent class of StringTemplateStyle and StrFormatStyle
|
|
310
318
|
# so it must be checked last.
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
else:
|
|
314
|
-
raise ValueError(f"Style {self._style!r} is not supported")
|
|
319
|
+
return STYLE_PERCENT_REGEX.findall(self._fmt)
|
|
315
320
|
|
|
316
|
-
|
|
321
|
+
raise ValueError(f"Style {self._style!r} is not supported")
|
|
317
322
|
|
|
318
323
|
def serialize_log_record(self, log_data: LogData) -> str:
|
|
319
324
|
"""Returns the final representation of the data to be logged
|
|
@@ -327,9 +332,9 @@ class BaseJsonFormatter(logging.Formatter):
|
|
|
327
332
|
|
|
328
333
|
def add_fields(
|
|
329
334
|
self,
|
|
330
|
-
log_data:
|
|
335
|
+
log_data: dict[str, Any],
|
|
331
336
|
record: logging.LogRecord,
|
|
332
|
-
message_dict:
|
|
337
|
+
message_dict: dict[str, Any],
|
|
333
338
|
) -> None:
|
|
334
339
|
"""Extract fields from a LogRecord for logging
|
|
335
340
|
|
|
@@ -402,7 +407,7 @@ class BaseJsonFormatter(logging.Formatter):
|
|
|
402
407
|
"""
|
|
403
408
|
return log_data
|
|
404
409
|
|
|
405
|
-
def formatException(self, ei) ->
|
|
410
|
+
def formatException(self, ei) -> str | list[str]: # type: ignore[override]
|
|
406
411
|
"""Format and return the specified exception information.
|
|
407
412
|
|
|
408
413
|
If exc_info_as_array is set to True, This method returns an array of strings.
|
|
@@ -410,7 +415,7 @@ class BaseJsonFormatter(logging.Formatter):
|
|
|
410
415
|
exception_info_str = super().formatException(ei)
|
|
411
416
|
return exception_info_str.splitlines() if self.exc_info_as_array else exception_info_str
|
|
412
417
|
|
|
413
|
-
def formatStack(self, stack_info) ->
|
|
418
|
+
def formatStack(self, stack_info) -> str | list[str]: # type: ignore[override]
|
|
414
419
|
"""Format and return the specified stack information.
|
|
415
420
|
|
|
416
421
|
If stack_info_as_array is set to True, This method returns an array of strings.
|
|
@@ -16,17 +16,11 @@ import base64
|
|
|
16
16
|
import dataclasses
|
|
17
17
|
import datetime
|
|
18
18
|
import enum
|
|
19
|
-
import sys
|
|
20
19
|
from types import TracebackType
|
|
21
|
-
from typing import Any
|
|
20
|
+
from typing import Any, TypeGuard
|
|
22
21
|
import traceback
|
|
23
22
|
import uuid
|
|
24
23
|
|
|
25
|
-
if sys.version_info >= (3, 10):
|
|
26
|
-
from typing import TypeGuard
|
|
27
|
-
else:
|
|
28
|
-
from typing_extensions import TypeGuard
|
|
29
|
-
|
|
30
24
|
## Installed
|
|
31
25
|
|
|
32
26
|
## Application
|
|
@@ -12,7 +12,8 @@ from __future__ import annotations
|
|
|
12
12
|
## Standard Library
|
|
13
13
|
import datetime
|
|
14
14
|
import json
|
|
15
|
-
from typing import Any
|
|
15
|
+
from typing import Any
|
|
16
|
+
from collections.abc import Callable
|
|
16
17
|
import warnings
|
|
17
18
|
|
|
18
19
|
## Application
|
|
@@ -67,10 +68,10 @@ class JsonFormatter(core.BaseJsonFormatter):
|
|
|
67
68
|
def __init__(
|
|
68
69
|
self,
|
|
69
70
|
*args,
|
|
70
|
-
json_default:
|
|
71
|
-
json_encoder:
|
|
71
|
+
json_default: Callable | None = None,
|
|
72
|
+
json_encoder: Callable | None = None,
|
|
72
73
|
json_serializer: Callable = json.dumps,
|
|
73
|
-
json_indent:
|
|
74
|
+
json_indent: int | str | None = None,
|
|
74
75
|
json_ensure_ascii: bool = True,
|
|
75
76
|
**kwargs,
|
|
76
77
|
) -> None:
|
|
@@ -6,7 +6,8 @@
|
|
|
6
6
|
from __future__ import annotations
|
|
7
7
|
|
|
8
8
|
## Standard Library
|
|
9
|
-
from typing import Any
|
|
9
|
+
from typing import Any
|
|
10
|
+
from collections.abc import Callable
|
|
10
11
|
|
|
11
12
|
## Installed
|
|
12
13
|
|
|
@@ -43,7 +44,7 @@ class MsgspecFormatter(core.BaseJsonFormatter):
|
|
|
43
44
|
def __init__(
|
|
44
45
|
self,
|
|
45
46
|
*args,
|
|
46
|
-
json_default:
|
|
47
|
+
json_default: Callable | None = msgspec_default,
|
|
47
48
|
**kwargs,
|
|
48
49
|
) -> None:
|
|
49
50
|
"""
|
|
@@ -6,7 +6,8 @@
|
|
|
6
6
|
from __future__ import annotations
|
|
7
7
|
|
|
8
8
|
## Standard Library
|
|
9
|
-
from typing import Any
|
|
9
|
+
from typing import Any
|
|
10
|
+
from collections.abc import Callable
|
|
10
11
|
|
|
11
12
|
## Installed
|
|
12
13
|
|
|
@@ -15,7 +16,7 @@ from . import core
|
|
|
15
16
|
from . import defaults as d
|
|
16
17
|
from .utils import package_is_available
|
|
17
18
|
|
|
18
|
-
# We import
|
|
19
|
+
# We import orjson after checking it is available
|
|
19
20
|
package_is_available("orjson", throw_error=True)
|
|
20
21
|
import orjson # pylint: disable=wrong-import-position,wrong-import-order
|
|
21
22
|
|
|
@@ -45,7 +46,7 @@ class OrjsonFormatter(core.BaseJsonFormatter):
|
|
|
45
46
|
def __init__(
|
|
46
47
|
self,
|
|
47
48
|
*args,
|
|
48
|
-
json_default:
|
|
49
|
+
json_default: Callable | None = orjson_default,
|
|
49
50
|
json_indent: bool = False,
|
|
50
51
|
**kwargs,
|
|
51
52
|
) -> None:
|
|
@@ -9,7 +9,8 @@ import io
|
|
|
9
9
|
import json
|
|
10
10
|
import logging
|
|
11
11
|
import logging.config
|
|
12
|
-
from typing import Any
|
|
12
|
+
from typing import Any
|
|
13
|
+
from collections.abc import Generator
|
|
13
14
|
|
|
14
15
|
## Installed
|
|
15
16
|
import pytest
|
|
@@ -64,7 +65,7 @@ class LoggingEnvironment:
|
|
|
64
65
|
|
|
65
66
|
|
|
66
67
|
@pytest.fixture
|
|
67
|
-
def env() -> Generator[LoggingEnvironment
|
|
68
|
+
def env() -> Generator[LoggingEnvironment]:
|
|
68
69
|
global _LOGGER_COUNT # pylint: disable=global-statement
|
|
69
70
|
_LOGGER_COUNT += 1
|
|
70
71
|
logging.config.dictConfig(LOGGING_CONFIG)
|
|
@@ -13,13 +13,10 @@ import logging
|
|
|
13
13
|
import sys
|
|
14
14
|
import traceback
|
|
15
15
|
from types import TracebackType
|
|
16
|
-
from typing import Any
|
|
16
|
+
from typing import Any
|
|
17
|
+
from collections.abc import Generator
|
|
17
18
|
import uuid
|
|
18
|
-
|
|
19
|
-
if sys.version_info >= (3, 9):
|
|
20
|
-
import zoneinfo
|
|
21
|
-
else:
|
|
22
|
-
from backports import zoneinfo
|
|
19
|
+
import zoneinfo
|
|
23
20
|
|
|
24
21
|
## Installed
|
|
25
22
|
import freezegun
|
|
@@ -63,7 +60,7 @@ class LoggingEnvironment:
|
|
|
63
60
|
|
|
64
61
|
|
|
65
62
|
@pytest.fixture
|
|
66
|
-
def env() -> Generator[LoggingEnvironment
|
|
63
|
+
def env() -> Generator[LoggingEnvironment]:
|
|
67
64
|
global _LOGGER_COUNT # pylint: disable=global-statement
|
|
68
65
|
_LOGGER_COUNT += 1
|
|
69
66
|
logger = logging.getLogger(f"pythonjsonlogger.tests.{_LOGGER_COUNT}")
|
|
@@ -170,6 +167,22 @@ def test_percentage_format(env: LoggingEnvironment, class_: type[BaseJsonFormatt
|
|
|
170
167
|
return
|
|
171
168
|
|
|
172
169
|
|
|
170
|
+
@pytest.mark.parametrize("class_", ALL_FORMATTERS)
|
|
171
|
+
def test_string_template_format(env: LoggingEnvironment, class_: type[BaseJsonFormatter]):
|
|
172
|
+
# Note: string templates support both $name and ${name}, and $$ is an escaped literal $
|
|
173
|
+
env.set_formatter(
|
|
174
|
+
class_("$$literal $levelname ${message} $filename ${lineno} $asctime", style="$")
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
msg = "testing logging format"
|
|
178
|
+
env.logger.info(msg)
|
|
179
|
+
log_json = env.load_json()
|
|
180
|
+
|
|
181
|
+
assert log_json["message"] == msg
|
|
182
|
+
assert log_json.keys() == {"levelname", "message", "filename", "lineno", "asctime"}
|
|
183
|
+
return
|
|
184
|
+
|
|
185
|
+
|
|
173
186
|
@pytest.mark.parametrize("class_", ALL_FORMATTERS)
|
|
174
187
|
def test_comma_format(env: LoggingEnvironment, class_: type[BaseJsonFormatter]):
|
|
175
188
|
# Note: we have double comma `,,` to test handling "empty" names
|
|
@@ -396,6 +409,23 @@ def test_log_dict_defaults(env: LoggingEnvironment, class_: type[BaseJsonFormatt
|
|
|
396
409
|
return
|
|
397
410
|
|
|
398
411
|
|
|
412
|
+
@pytest.mark.parametrize("class_", ALL_FORMATTERS)
|
|
413
|
+
def test_log_dict_not_modified(env: LoggingEnvironment, class_: type[BaseJsonFormatter]):
|
|
414
|
+
env.set_formatter(class_())
|
|
415
|
+
|
|
416
|
+
msg = {"text": "testing logging", "nested": {"more": "data"}}
|
|
417
|
+
try:
|
|
418
|
+
raise ValueError("test")
|
|
419
|
+
except ValueError:
|
|
420
|
+
env.logger.exception(msg, stack_info=True)
|
|
421
|
+
log_json = env.load_json()
|
|
422
|
+
|
|
423
|
+
assert log_json["exc_info"]
|
|
424
|
+
assert log_json["stack_info"]
|
|
425
|
+
assert msg == {"text": "testing logging", "nested": {"more": "data"}}
|
|
426
|
+
return
|
|
427
|
+
|
|
428
|
+
|
|
399
429
|
@pytest.mark.parametrize("class_", ALL_FORMATTERS)
|
|
400
430
|
def test_log_extra(env: LoggingEnvironment, class_: type[BaseJsonFormatter]):
|
|
401
431
|
env.set_formatter(class_())
|
|
@@ -558,6 +588,7 @@ def test_default_encoder_with_timestamp(env: LoggingEnvironment, class_: type[Ba
|
|
|
558
588
|
(False, bool, False),
|
|
559
589
|
(None, type(None), None),
|
|
560
590
|
(b"some-bytes", str, "c29tZS1ieXRlcw=="),
|
|
591
|
+
(b"fancy-bytes-\xf0\xf1", str, "ZmFuY3ktYnl0ZXMt8PE="),
|
|
561
592
|
(datetime.time(16, 45, 30, 100), str, "16:45:30.000100"),
|
|
562
593
|
(datetime.date(2024, 5, 5), str, "2024-05-05"),
|
|
563
594
|
(datetime.datetime(2024, 5, 5, 16, 45, 30, 100), str, "2024-05-05T16:45:30.000100"),
|
|
@@ -683,6 +714,148 @@ def test_exc_info_as_array_no_exc_info(env: LoggingEnvironment, class_: type[Bas
|
|
|
683
714
|
return
|
|
684
715
|
|
|
685
716
|
|
|
717
|
+
@pytest.mark.skipif(
|
|
718
|
+
sys.version_info < (3, 11), reason="Exception notes require Python 3.11 or higher"
|
|
719
|
+
)
|
|
720
|
+
@pytest.mark.parametrize("class_", ALL_FORMATTERS)
|
|
721
|
+
def test_exception_notes(env: LoggingEnvironment, class_: type[BaseJsonFormatter]):
|
|
722
|
+
env.set_formatter(class_())
|
|
723
|
+
|
|
724
|
+
try:
|
|
725
|
+
err = ValueError("my error")
|
|
726
|
+
err.add_note("this is a note")
|
|
727
|
+
raise err
|
|
728
|
+
except ValueError:
|
|
729
|
+
env.logger.exception("oops")
|
|
730
|
+
|
|
731
|
+
log_json = env.load_json()
|
|
732
|
+
assert "ValueError: my error" in log_json["exc_info"]
|
|
733
|
+
assert "this is a note" in log_json["exc_info"]
|
|
734
|
+
return
|
|
735
|
+
|
|
736
|
+
|
|
737
|
+
@pytest.mark.skipif(
|
|
738
|
+
sys.version_info < (3, 11), reason="Exception notes require Python 3.11 or higher"
|
|
739
|
+
)
|
|
740
|
+
@pytest.mark.parametrize("class_", ALL_FORMATTERS)
|
|
741
|
+
def test_exception_notes_as_array(env: LoggingEnvironment, class_: type[BaseJsonFormatter]):
|
|
742
|
+
env.set_formatter(class_(exc_info_as_array=True))
|
|
743
|
+
|
|
744
|
+
try:
|
|
745
|
+
err = ValueError("my error")
|
|
746
|
+
err.add_note("this is a note")
|
|
747
|
+
raise err
|
|
748
|
+
except ValueError:
|
|
749
|
+
env.logger.exception("oops")
|
|
750
|
+
|
|
751
|
+
log_json = env.load_json()
|
|
752
|
+
assert isinstance(log_json["exc_info"], list)
|
|
753
|
+
full_traceback = "\n".join(log_json["exc_info"])
|
|
754
|
+
assert "ValueError: my error" in full_traceback
|
|
755
|
+
assert "this is a note" in full_traceback
|
|
756
|
+
return
|
|
757
|
+
|
|
758
|
+
|
|
759
|
+
@pytest.mark.skipif(
|
|
760
|
+
sys.version_info < (3, 11), reason="Exception groups require Python 3.11 or higher"
|
|
761
|
+
)
|
|
762
|
+
@pytest.mark.parametrize("class_", ALL_FORMATTERS)
|
|
763
|
+
def test_exception_groups(env: LoggingEnvironment, class_: type[BaseJsonFormatter]):
|
|
764
|
+
env.set_formatter(class_())
|
|
765
|
+
|
|
766
|
+
try:
|
|
767
|
+
raise ExceptionGroup("my group", [ValueError("err 1"), TypeError("err 2")])
|
|
768
|
+
except ExceptionGroup:
|
|
769
|
+
env.logger.exception("oops")
|
|
770
|
+
|
|
771
|
+
log_json = env.load_json()
|
|
772
|
+
assert "ExceptionGroup: my group" in log_json["exc_info"]
|
|
773
|
+
assert "ValueError: err 1" in log_json["exc_info"]
|
|
774
|
+
assert "TypeError: err 2" in log_json["exc_info"]
|
|
775
|
+
return
|
|
776
|
+
|
|
777
|
+
|
|
778
|
+
@pytest.mark.skipif(
|
|
779
|
+
sys.version_info < (3, 11), reason="Exception groups require Python 3.11 or higher"
|
|
780
|
+
)
|
|
781
|
+
@pytest.mark.parametrize("class_", ALL_FORMATTERS)
|
|
782
|
+
def test_exception_groups_as_array(env: LoggingEnvironment, class_: type[BaseJsonFormatter]):
|
|
783
|
+
env.set_formatter(class_(exc_info_as_array=True))
|
|
784
|
+
|
|
785
|
+
try:
|
|
786
|
+
raise ExceptionGroup("my group", [ValueError("err 1"), TypeError("err 2")])
|
|
787
|
+
except ExceptionGroup:
|
|
788
|
+
env.logger.exception("oops")
|
|
789
|
+
|
|
790
|
+
log_json = env.load_json()
|
|
791
|
+
assert isinstance(log_json["exc_info"], list)
|
|
792
|
+
full_traceback = "\n".join(log_json["exc_info"])
|
|
793
|
+
assert "ExceptionGroup: my group" in full_traceback
|
|
794
|
+
assert "ValueError: err 1" in full_traceback
|
|
795
|
+
assert "TypeError: err 2" in full_traceback
|
|
796
|
+
return
|
|
797
|
+
|
|
798
|
+
|
|
799
|
+
@pytest.mark.skipif(
|
|
800
|
+
sys.version_info < (3, 11), reason="Exception groups and notes require Python 3.11 or higher"
|
|
801
|
+
)
|
|
802
|
+
@pytest.mark.parametrize("class_", ALL_FORMATTERS)
|
|
803
|
+
def test_exception_groups_with_notes(env: LoggingEnvironment, class_: type[BaseJsonFormatter]):
|
|
804
|
+
env.set_formatter(class_())
|
|
805
|
+
|
|
806
|
+
try:
|
|
807
|
+
err1 = ValueError("err 1")
|
|
808
|
+
err1.add_note("note 1")
|
|
809
|
+
err2 = TypeError("err 2")
|
|
810
|
+
err2.add_note("note 2")
|
|
811
|
+
eg = ExceptionGroup("my group", [err1, err2])
|
|
812
|
+
eg.add_note("group note")
|
|
813
|
+
raise eg
|
|
814
|
+
except ExceptionGroup:
|
|
815
|
+
env.logger.exception("oops")
|
|
816
|
+
|
|
817
|
+
log_json = env.load_json()
|
|
818
|
+
assert "ExceptionGroup: my group" in log_json["exc_info"]
|
|
819
|
+
assert "group note" in log_json["exc_info"]
|
|
820
|
+
assert "ValueError: err 1" in log_json["exc_info"]
|
|
821
|
+
assert "note 1" in log_json["exc_info"]
|
|
822
|
+
assert "TypeError: err 2" in log_json["exc_info"]
|
|
823
|
+
assert "note 2" in log_json["exc_info"]
|
|
824
|
+
return
|
|
825
|
+
|
|
826
|
+
|
|
827
|
+
@pytest.mark.skipif(
|
|
828
|
+
sys.version_info < (3, 11), reason="Exception groups and notes require Python 3.11 or higher"
|
|
829
|
+
)
|
|
830
|
+
@pytest.mark.parametrize("class_", ALL_FORMATTERS)
|
|
831
|
+
def test_exception_groups_with_notes_as_array(
|
|
832
|
+
env: LoggingEnvironment, class_: type[BaseJsonFormatter]
|
|
833
|
+
):
|
|
834
|
+
env.set_formatter(class_(exc_info_as_array=True))
|
|
835
|
+
|
|
836
|
+
try:
|
|
837
|
+
err1 = ValueError("err 1")
|
|
838
|
+
err1.add_note("note 1")
|
|
839
|
+
err2 = TypeError("err 2")
|
|
840
|
+
err2.add_note("note 2")
|
|
841
|
+
eg = ExceptionGroup("my group", [err1, err2])
|
|
842
|
+
eg.add_note("group note")
|
|
843
|
+
raise eg
|
|
844
|
+
except ExceptionGroup:
|
|
845
|
+
env.logger.exception("oops")
|
|
846
|
+
|
|
847
|
+
log_json = env.load_json()
|
|
848
|
+
assert isinstance(log_json["exc_info"], list)
|
|
849
|
+
full_traceback = "\n".join(log_json["exc_info"])
|
|
850
|
+
assert "ExceptionGroup: my group" in full_traceback
|
|
851
|
+
assert "group note" in full_traceback
|
|
852
|
+
assert "ValueError: err 1" in full_traceback
|
|
853
|
+
assert "note 1" in full_traceback
|
|
854
|
+
assert "TypeError: err 2" in full_traceback
|
|
855
|
+
assert "note 2" in full_traceback
|
|
856
|
+
return
|
|
857
|
+
|
|
858
|
+
|
|
686
859
|
@pytest.mark.parametrize("class_", ALL_FORMATTERS)
|
|
687
860
|
def test_stack_info_as_array(env: LoggingEnvironment, class_: type[BaseJsonFormatter]):
|
|
688
861
|
env.set_formatter(class_(stack_info_as_array=True))
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
[:python_version < "3.10"]
|
|
3
|
-
typing_extensions
|
|
4
|
-
|
|
5
|
-
[dev]
|
|
6
|
-
validate-pyproject[all]
|
|
7
|
-
black
|
|
8
|
-
pylint
|
|
9
|
-
mypy
|
|
10
|
-
pytest
|
|
11
|
-
freezegun
|
|
12
|
-
tzdata
|
|
13
|
-
build
|
|
14
|
-
mkdocs
|
|
15
|
-
mkdocs-material>=8.5
|
|
16
|
-
mkdocs-awesome-pages-plugin
|
|
17
|
-
mdx_truly_sane_lists
|
|
18
|
-
mkdocstrings[python]
|
|
19
|
-
mkdocs-gen-files
|
|
20
|
-
mkdocs-literate-nav
|
|
21
|
-
mike
|
|
22
|
-
|
|
23
|
-
[dev:implementation_name != "pypy"]
|
|
24
|
-
orjson
|
|
25
|
-
msgspec
|
|
26
|
-
|
|
27
|
-
[dev:python_version < "3.9"]
|
|
28
|
-
backports.zoneinfo
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|