Content-Length: 8290 | pFad | http://github.com/nhairs/python-json-logger/pull/52.diff
thub.com diff --git a/docs/changelog.md b/docs/changelog.md index 03a9b71..c51d7ff 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -4,12 +4,18 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [UNRELEASED] +## [4.0.0](https://github.com/nhairs/python-json-logger/compare/v3.3.3...v4.0.0) - UNRELEASED ### Added - Support `DictConfigurator` prefixes for `rename_fields` and `static_fields`. [#45](https://github.com/nhairs/python-json-logger/pull/45) - Allows using values like `ext://sys.stderr` in `fileConfig`/`dictConfig` value fields. +### Removed +- Remove support for providing strings instead of objects when instantiating formatters. Instead use the `DictConfigurator` `ext://` prefix format when using `fileConfig`/`dictConfig`. [#47](https://github.com/nhairs/python-json-logger/issues/47) + - Affects `pythonjsonlogger.json.JsonFormatter`: `json_default`, `json_encoder`, `json_serializer`. + - Affects `pythonjsonlogger.orjson.OrjsonFormatter`: `json_default`. + - Affects `pythonjsonlogger.msgspec.MsgspecFormatter`: `json_default`. + Thanks @rubensa ## [3.3.0](https://github.com/nhairs/python-json-logger/compare/v3.2.1...v3.3.0) - 2025-03-06 diff --git a/docs/cookbook.md b/docs/cookbook.md index c0755d5..94ebd17 100644 --- a/docs/cookbook.md +++ b/docs/cookbook.md @@ -148,6 +148,7 @@ formatters: default: "()": pythonjsonlogger.json.JsonFormatter format: "%(asctime)s %(levelname)s %(name)s %(module)s %(funcName)s %(lineno)s %(message)s" + json_default: ext://logging_config.my_json_default rename_fields: "asctime": "timestamp" "levelname": "status" @@ -178,13 +179,23 @@ loggers: propagate: no ``` -You'll notice that we are using `ext://...` for the `static_fields`. This will load data from other modules such as the one below. +You'll notice that we are using `ext://...` for `json_default` and`static_fields`. This will load data from other modules such as the one below. ```python title="logging_config.py" import importlib.metadata import os +class Dummy: + pass + + +def my_json_default(obj: Any) -> Any: + if isinstance(obj, Dummy): + return "DUMMY" + return obj + + def get_version_metadata(): # https://stackoverflow.com/a/78082532 version = importlib.metadata.version(PROJECT_NAME) diff --git a/src/pythonjsonlogger/core.py b/src/pythonjsonlogger/core.py index a00510b..b18f154 100644 --- a/src/pythonjsonlogger/core.py +++ b/src/pythonjsonlogger/core.py @@ -7,11 +7,10 @@ ## Standard Library from datetime import datetime, timezone -import importlib import logging import re import sys -from typing import Optional, Union, Callable, List, Dict, Container, Any, Sequence +from typing import Optional, Union, List, Dict, Container, Any, Sequence if sys.version_info >= (3, 10): from typing import TypeAlias @@ -72,31 +71,12 @@ ## Type Aliases ## ----------------------------------------------------------------------------- -OptionalCallableOrStr: TypeAlias = Optional[Union[Callable, str]] -"""Type alias""" - LogRecord: TypeAlias = Dict[str, Any] """Type alias""" ### FUNCTIONS ### ============================================================================ -def str_to_object(obj: Any) -> Any: - """Import strings to an object, leaving non-strings as-is. - - Args: - obj: the object or string to process - - *New in 3.1* - """ - - if not isinstance(obj, str): - return obj - - module_name, attribute_name = obj.rsplit(".", 1) - return getattr(importlib.import_module(module_name), attribute_name) - - def merge_record_extra( record: logging.LogRecord, target: Dict, diff --git a/src/pythonjsonlogger/json.py b/src/pythonjsonlogger/json.py index 21e78d0..27b5efc 100644 --- a/src/pythonjsonlogger/json.py +++ b/src/pythonjsonlogger/json.py @@ -67,9 +67,9 @@ class JsonFormatter(core.BaseJsonFormatter): def __init__( self, *args, - json_default: core.OptionalCallableOrStr = None, - json_encoder: core.OptionalCallableOrStr = None, - json_serializer: Union[Callable, str] = json.dumps, + 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, @@ -87,9 +87,9 @@ def __init__( """ super().__init__(*args, **kwargs) - self.json_default = core.str_to_object(json_default) - self.json_encoder = core.str_to_object(json_encoder) - self.json_serializer = core.str_to_object(json_serializer) + 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: diff --git a/src/pythonjsonlogger/msgspec.py b/src/pythonjsonlogger/msgspec.py index 8646f85..208254c 100644 --- a/src/pythonjsonlogger/msgspec.py +++ b/src/pythonjsonlogger/msgspec.py @@ -6,7 +6,7 @@ from __future__ import annotations ## Standard Library -from typing import Any +from typing import Any, Optional, Callable ## Installed @@ -43,7 +43,7 @@ class MsgspecFormatter(core.BaseJsonFormatter): def __init__( self, *args, - json_default: core.OptionalCallableOrStr = msgspec_default, + json_default: Optional[Callable] = msgspec_default, **kwargs, ) -> None: """ @@ -54,7 +54,7 @@ def __init__( """ super().__init__(*args, **kwargs) - self.json_default = core.str_to_object(json_default) + self.json_default = json_default self._encoder = msgspec.json.Encoder(enc_hook=self.json_default) return diff --git a/src/pythonjsonlogger/orjson.py b/src/pythonjsonlogger/orjson.py index 16db842..ebbcd48 100644 --- a/src/pythonjsonlogger/orjson.py +++ b/src/pythonjsonlogger/orjson.py @@ -6,7 +6,7 @@ from __future__ import annotations ## Standard Library -from typing import Any +from typing import Any, Optional, Callable ## Installed @@ -45,7 +45,7 @@ class OrjsonFormatter(core.BaseJsonFormatter): def __init__( self, *args, - json_default: core.OptionalCallableOrStr = orjson_default, + json_default: Optional[Callable] = orjson_default, json_indent: bool = False, **kwargs, ) -> None: @@ -58,7 +58,7 @@ def __init__( """ super().__init__(*args, **kwargs) - self.json_default = core.str_to_object(json_default) + self.json_default = json_default self.json_indent = json_indent return diff --git a/tests/test_dictconfig.py b/tests/test_dictconfig.py index e956c03..c14cc75 100644 --- a/tests/test_dictconfig.py +++ b/tests/test_dictconfig.py @@ -19,12 +19,24 @@ _LOGGER_COUNT = 0 EXT_VAL = 999 + +class Dummy: + pass + + +def my_json_default(obj: Any) -> Any: + if isinstance(obj, Dummy): + return "DUMMY" + return obj + + LOGGING_CONFIG = { "version": 1, "disable_existing_loggers": False, "formatters": { "default": { "()": "pythonjsonlogger.json.JsonFormatter", + "json_default": "ext://tests.test_dictconfig.my_json_default", "static_fields": {"ext-val": "ext://tests.test_dictconfig.EXT_VAL"}, } }, @@ -73,8 +85,12 @@ def env() -> Generator[LoggingEnvironment, None, None]: ### TESTS ### ============================================================================ def test_external_reference_support(env: LoggingEnvironment): - env.logger.info("hello") + + assert logging.root.handlers[0].formatter.json_default is my_json_default # type: ignore[union-attr] + + env.logger.info("hello", extra={"dummy": Dummy()}) log_json = env.load_json() assert log_json["ext-val"] == EXT_VAL + assert log_json["dummy"] == "DUMMY" returnFetched URL: http://github.com/nhairs/python-json-logger/pull/52.diff
Alternative Proxies: