python-json-logger 4.2.0__py3-none-any.whl → 4.3.0.dev2__py3-none-any.whl

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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-json-logger
3
- Version: 4.2.0
3
+ Version: 4.3.0.dev2
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>
@@ -16,6 +16,7 @@ Classifier: Programming Language :: Python :: 3.11
16
16
  Classifier: Programming Language :: Python :: 3.12
17
17
  Classifier: Programming Language :: Python :: 3.13
18
18
  Classifier: Programming Language :: Python :: 3.14
19
+ Classifier: Programming Language :: Python :: 3.15
19
20
  Classifier: Topic :: System :: Logging
20
21
  Classifier: Typing :: Typed
21
22
  Requires-Python: >=3.10
@@ -1,7 +1,7 @@
1
- python_json_logger-4.2.0.dist-info/licenses/LICENSE,sha256=GOqVF546Xg4k62zhbED7--IxM8JQKTOi91-KPhoFW1Q,1329
1
+ python_json_logger-4.3.0.dev2.dist-info/licenses/LICENSE,sha256=GOqVF546Xg4k62zhbED7--IxM8JQKTOi91-KPhoFW1Q,1329
2
2
  pythonjsonlogger/__init__.py,sha256=2QcveuId10CyXff_BtJ_6kXIarHT5oq66XHHMFiaE8s,419
3
- pythonjsonlogger/core.py,sha256=L41AmWK0t5n-QHazXeOlggNX7uzri750i7SFSpeXZ5Q,16268
4
- pythonjsonlogger/defaults.py,sha256=muj4QzHgUMJH0UwRvgroHKSRfQgHcF9E-EJXhVzAi98,6461
3
+ pythonjsonlogger/core.py,sha256=T02iURAubnMTk0PO0HUOckS93eMfAFapSZcFPXJqB64,17102
4
+ pythonjsonlogger/defaults.py,sha256=VR_Wl092yRjwS0mREjuNn_W4H4sAjtcZAIYFBMYNHe0,6770
5
5
  pythonjsonlogger/exception.py,sha256=r3DXDk7TThscnMVNPpVRaRSFHTnY-ygea6nn-FgjwsI,804
6
6
  pythonjsonlogger/json.py,sha256=6aJSqd2BeysAHkQkxb7dPgFpqsFRf4rvWYgaWF2MEC0,4064
7
7
  pythonjsonlogger/jsonlogger.py,sha256=sfltkYGwRhRvqcpT-kxlPfcMuOc4ngEa4W4NVTiBpMI,417
@@ -9,7 +9,7 @@ pythonjsonlogger/msgspec.py,sha256=tdFes9rLrdns9I_C5ascK9a265iNc4UccLPzc8JbFDI,2
9
9
  pythonjsonlogger/orjson.py,sha256=XWM-jyl6g_40Em8kt188EOspD872xHvYy8aS_CLlssw,2354
10
10
  pythonjsonlogger/py.typed,sha256=4RLptUHQuSqzK6CDbigff8uvWQjwPPQMxikWPkV4OtA,80
11
11
  pythonjsonlogger/utils.py,sha256=qr04nb61TkVw7cJTXAtLBfyH_NBvyYUlR_mehH76-RM,1126
12
- python_json_logger-4.2.0.dist-info/METADATA,sha256=kgcfdHSWOBb_eR3Xg4Or8GzyuDw9HbClYz9cTx4y5GU,2866
13
- python_json_logger-4.2.0.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
14
- python_json_logger-4.2.0.dist-info/top_level.txt,sha256=9G-OsTkbwPgM8t-bXKPmWDBRZv9cbzLIiEDE5EnGk2I,17
15
- python_json_logger-4.2.0.dist-info/RECORD,,
12
+ python_json_logger-4.3.0.dev2.dist-info/METADATA,sha256=q4IajnR8q8pXVltRYOx5V3-7FPkoGkECHgo9XbSb9AI,2922
13
+ python_json_logger-4.3.0.dev2.dist-info/WHEEL,sha256=mOdXR9C3wMVov7cSY8PY-XCzo3SmmWCHvGQr5csJoao,91
14
+ python_json_logger-4.3.0.dev2.dist-info/top_level.txt,sha256=9G-OsTkbwPgM8t-bXKPmWDBRZv9cbzLIiEDE5EnGk2I,17
15
+ python_json_logger-4.3.0.dev2.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (84.0.0)
2
+ Generator: setuptools (84.2.1)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
pythonjsonlogger/core.py CHANGED
@@ -9,6 +9,7 @@ from __future__ import annotations
9
9
  from datetime import datetime, timezone
10
10
  import logging
11
11
  import re
12
+ import string
12
13
  import sys
13
14
  from typing import TypeAlias, Any
14
15
  from collections.abc import Container, Sequence
@@ -64,8 +65,7 @@ if sys.version_info >= (3, 12):
64
65
  STYLE_STRING_TEMPLATE_REGEX = re.compile(
65
66
  r"\$(?:\$|\{(?P<braced>.+?)\}|(?P<named>[_a-z][_a-z0-9]*))", re.IGNORECASE
66
67
  ) # $ style
67
- STYLE_STRING_FORMAT_REGEX = re.compile(r"\{(.+?)\}", re.IGNORECASE) # { style
68
- STYLE_PERCENT_REGEX = re.compile(r"%\((.+?)\)", re.IGNORECASE) # % style
68
+ STYLE_PERCENT_REGEX = re.compile(r"%(?:%|\((?P<named>.+?)\))", re.IGNORECASE) # % style
69
69
 
70
70
  ## Type Aliases
71
71
  ## -----------------------------------------------------------------------------
@@ -153,12 +153,14 @@ class BaseJsonFormatter(logging.Formatter):
153
153
  rename_fields_keep_missing: When renaming fields, include missing fields in the output.
154
154
  static_fields: an optional dict, used to add fields with static values to all logs
155
155
  reserved_attrs: an optional list of fields that will be skipped when
156
- outputting json log record. Defaults to [all log record attributes][pythonjsonlogger.core.RESERVED_ATTRS].
156
+ outputting json log record. Pass a list or tuple, not a string.
157
+ Defaults to [all log record attributes][pythonjsonlogger.core.RESERVED_ATTRS].
157
158
  timestamp: an optional string/boolean field to add a timestamp when
158
159
  outputting the json log record. If string is passed, timestamp will be added
159
160
  to log record using string as key. If True boolean is passed, timestamp key
160
161
  will be "timestamp". Defaults to False/off.
161
- exc_info_as_array: break the exc_info into a list of lines based on line breaks.
162
+ exc_info_as_array: break the exc_info into a list of lines based on line breaks,
163
+ including cached exception text in records received over a transport.
162
164
  stack_info_as_array: break the stack_info into a list of lines based on line breaks.
163
165
 
164
166
  *Changed in 3.1*:
@@ -213,8 +215,9 @@ class BaseJsonFormatter(logging.Formatter):
213
215
  ## ---------------------------------------------------------------------
214
216
  self.prefix = prefix
215
217
 
216
- # We recreate the dict in rename_fields and static_fields to support internal/external
217
- # references which require getting the item to do the conversion.
218
+ # Recreate these dicts so DictConfigurator `ext://` / `cfg://` prefixes
219
+ # are converted (ConvertingDict only converts on `__getitem__`) and so
220
+ # later mutation of the caller's dict cannot change formatter state.
218
221
  # For more details see: https://github.com/nhairs/python-json-logger/pull/45
219
222
  self.rename_fields = (
220
223
  {key: rename_fields[key] for key in rename_fields} if rename_fields is not None else {}
@@ -222,18 +225,26 @@ class BaseJsonFormatter(logging.Formatter):
222
225
  self.static_fields = (
223
226
  {key: static_fields[key] for key in static_fields} if static_fields is not None else {}
224
227
  )
228
+ self.defaults = {key: defaults[key] for key in defaults} if defaults is not None else {}
225
229
 
226
230
  self.rename_fields_keep_missing = rename_fields_keep_missing
227
- self.reserved_attrs = set(reserved_attrs if reserved_attrs is not None else RESERVED_ATTRS)
231
+ if reserved_attrs is None:
232
+ reserved_attrs = RESERVED_ATTRS
233
+ elif isinstance(reserved_attrs, str):
234
+ raise TypeError("reserved_attrs must be a sequence of field names, not a string")
235
+ self.reserved_attrs = set(reserved_attrs)
228
236
  self.timestamp = timestamp
229
237
 
230
238
  self._skip_fields = set(self._required_fields)
231
239
  self._skip_fields.update(self.reserved_attrs)
232
- self.defaults = defaults if defaults is not None else {}
233
240
  self.exc_info_as_array = exc_info_as_array
234
241
  self.stack_info_as_array = stack_info_as_array
235
242
  return
236
243
 
244
+ def usesTime(self) -> bool:
245
+ """Return whether the parsed format fields include `asctime`."""
246
+ return "asctime" in self._required_fields
247
+
237
248
  def format(self, record: logging.LogRecord) -> str:
238
249
  """Formats a log record and serializes to json
239
250
 
@@ -264,7 +275,9 @@ class BaseJsonFormatter(logging.Formatter):
264
275
  if record.exc_info and not message_dict.get("exc_info"):
265
276
  message_dict["exc_info"] = self.formatException(record.exc_info)
266
277
  if not message_dict.get("exc_info") and record.exc_text:
267
- message_dict["exc_info"] = record.exc_text
278
+ message_dict["exc_info"] = (
279
+ record.exc_text.splitlines() if self.exc_info_as_array else record.exc_text
280
+ )
268
281
 
269
282
  # Display formatted record of stack frames
270
283
  # default format is a string returned from :func:`traceback.print_stack`
@@ -311,12 +324,20 @@ class BaseJsonFormatter(logging.Formatter):
311
324
  ]
312
325
 
313
326
  if isinstance(self._style, logging.StrFormatStyle):
314
- return STYLE_STRING_FORMAT_REGEX.findall(self._fmt)
327
+ return [
328
+ field_name
329
+ for _, field_name, _, _ in string.Formatter().parse(self._fmt)
330
+ if field_name
331
+ ]
315
332
 
316
333
  if isinstance(self._style, logging.PercentStyle):
317
334
  # PercentStyle is parent class of StringTemplateStyle and StrFormatStyle
318
335
  # so it must be checked last.
319
- return STYLE_PERCENT_REGEX.findall(self._fmt)
336
+ return [
337
+ match.group("named")
338
+ for match in STYLE_PERCENT_REGEX.finditer(self._fmt)
339
+ if match.group("named")
340
+ ]
320
341
 
321
342
  raise ValueError(f"Style {self._style!r} is not supported")
322
343
 
@@ -152,10 +152,18 @@ def use_exception_default(obj: Any) -> TypeGuard[BaseException]:
152
152
  def exception_default(obj: BaseException) -> str:
153
153
  """Default function for exception instances
154
154
 
155
+ Includes the exception's own notes, if present. Exception groups retain
156
+ their summary without recursively formatting their children.
157
+
155
158
  Args:
156
159
  obj: object to handle
160
+
161
+ *Changed in 4.3*: Includes exception notes if available.
157
162
  """
158
- return f"{obj.__class__.__name__}: {obj}"
163
+ result = f"{obj.__class__.__name__}: {obj}"
164
+ if hasattr(obj, "__notes__"):
165
+ return "\n".join([result, *obj.__notes__])
166
+ return result
159
167
 
160
168
 
161
169
  def use_traceback_default(obj: Any) -> TypeGuard[TracebackType]: