You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
120 lines
4.0 KiB
Python
120 lines
4.0 KiB
Python
"""JSON formatter using the standard library's `json` for encoding.
|
|
|
|
Module contains the `JsonFormatter` and a custom `JsonEncoder` which supports a greater
|
|
variety of types.
|
|
"""
|
|
|
|
### IMPORTS
|
|
### ============================================================================
|
|
## Future
|
|
from __future__ import annotations
|
|
|
|
## Standard Library
|
|
import datetime
|
|
import json
|
|
from typing import Any, Callable, Optional, Union
|
|
import warnings
|
|
|
|
## Application
|
|
from . import core
|
|
from . import defaults as d
|
|
|
|
|
|
### CLASSES
|
|
### ============================================================================
|
|
class JsonEncoder(json.JSONEncoder):
|
|
"""A custom encoder extending [json.JSONEncoder](https://docs.python.org/3/library/json.html#json.JSONEncoder)"""
|
|
|
|
def default(self, o: Any) -> Any:
|
|
if d.use_datetime_any(o):
|
|
return self.format_datetime_obj(o)
|
|
|
|
if d.use_exception_default(o):
|
|
return d.exception_default(o)
|
|
|
|
if d.use_traceback_default(o):
|
|
return d.traceback_default(o)
|
|
|
|
if d.use_enum_default(o):
|
|
return d.enum_default(o)
|
|
|
|
if d.use_bytes_default(o):
|
|
return d.bytes_default(o)
|
|
|
|
if d.use_dataclass_default(o):
|
|
return d.dataclass_default(o)
|
|
|
|
if d.use_type_default(o):
|
|
return d.type_default(o)
|
|
|
|
try:
|
|
return super().default(o)
|
|
except TypeError:
|
|
return d.unknown_default(o)
|
|
|
|
def format_datetime_obj(self, o: datetime.time | datetime.date | datetime.datetime) -> str:
|
|
"""Format datetime objects found in `self.default`
|
|
|
|
This allows subclasses to change the datetime format without understanding the
|
|
internals of the default method.
|
|
"""
|
|
return d.datetime_any(o)
|
|
|
|
|
|
class JsonFormatter(core.BaseJsonFormatter):
|
|
"""JSON formatter using the standard library's [`json`](https://docs.python.org/3/library/json.html) for encoding"""
|
|
|
|
def __init__(
|
|
self,
|
|
*args,
|
|
json_default: Optional[Callable] = None,
|
|
json_encoder: Optional[Callable] = None,
|
|
json_serializer: Callable = json.dumps,
|
|
json_indent: Optional[Union[int, str]] = None,
|
|
json_ensure_ascii: bool = True,
|
|
**kwargs,
|
|
) -> None:
|
|
"""
|
|
Args:
|
|
args: see [BaseJsonFormatter][pythonjsonlogger.core.BaseJsonFormatter]
|
|
json_default: a function for encoding non-standard objects
|
|
json_encoder: custom JSON encoder
|
|
json_serializer: a [`json.dumps`](https://docs.python.org/3/library/json.html#json.dumps)-compatible callable
|
|
that will be used to serialize the log record.
|
|
json_indent: indent parameter for the `json_serializer`
|
|
json_ensure_ascii: `ensure_ascii` parameter for the `json_serializer`
|
|
kwargs: see [BaseJsonFormatter][pythonjsonlogger.core.BaseJsonFormatter]
|
|
"""
|
|
super().__init__(*args, **kwargs)
|
|
|
|
self.json_default = json_default
|
|
self.json_encoder = json_encoder
|
|
self.json_serializer = json_serializer
|
|
self.json_indent = json_indent
|
|
self.json_ensure_ascii = json_ensure_ascii
|
|
if not self.json_encoder and not self.json_default:
|
|
self.json_encoder = JsonEncoder
|
|
return
|
|
|
|
def jsonify_log_record(self, log_data: core.LogData) -> str:
|
|
"""Returns a json string of the log data."""
|
|
return self.json_serializer(
|
|
log_data,
|
|
default=self.json_default,
|
|
cls=self.json_encoder,
|
|
indent=self.json_indent,
|
|
ensure_ascii=self.json_ensure_ascii,
|
|
)
|
|
|
|
|
|
### DEPRECATED COMPATIBILITY
|
|
### ============================================================================
|
|
def __getattr__(name: str):
|
|
if name == "RESERVED_ATTRS":
|
|
warnings.warn(
|
|
"RESERVED_ATTRS has been moved to pythonjsonlogger.core",
|
|
DeprecationWarning,
|
|
)
|
|
return core.RESERVED_ATTRS
|
|
raise AttributeError(f"module {__name__} has no attribute {name}")
|