diff --git a/docs/changelog.md b/docs/changelog.md index ccec8cb..749a3b5 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### 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. +- Support comma seperated lists for Formatter `fmt` (`style=","`) e.g. `"asctime,message,levelname"` [#15](https://github.com/nhairs/python-json-logger/issues/15) + - Note that this style is specific to `python-json-logger` and thus care should be taken not to pass this format to other logging Formatter implementations. ### Changed - Rename `pythonjsonlogger.core.LogRecord` and `log_record` arguments to avoid confusion / overlapping with `logging.LogRecord`. [#38](https://github.com/nhairs/python-json-logger/issues/38) diff --git a/src/pythonjsonlogger/core.py b/src/pythonjsonlogger/core.py index 6fd7285..10ac6dc 100644 --- a/src/pythonjsonlogger/core.py +++ b/src/pythonjsonlogger/core.py @@ -175,10 +175,16 @@ def __init__( - Renaming fields now preserves the order that fields were added in and avoids adding missing fields. The original behaviour, missing fields have a value of `None`, is still available by setting `rename_fields_keep_missing` to `True`. + + *Added in 4.0*: + + - `fmt` now supports comma seperated lists (`style=","`). Note that this style is specific + to `python-json-logger` and thus care should be taken to not to pass this format to other + logging Formatter implementations. """ ## logging.Formatter compatibility ## --------------------------------------------------------------------- - # Note: validate added in 3.8, defaults added in 3.10 + # Note: validate added in python 3.8, defaults added in 3.10 if style in logging._STYLES: _style = logging._STYLES[style][0](fmt) # type: ignore[operator] if validate: @@ -186,12 +192,14 @@ def __init__( self._style = _style self._fmt = _style._fmt - elif not validate: + elif style == "," or not validate: self._style = style self._fmt = fmt + # TODO: Validate comma format + else: - raise ValueError(f"Style must be one of: {','.join(logging._STYLES.keys())}") + raise ValueError("Style must be one of: '%{$,'") self.datefmt = datefmt @@ -271,6 +279,12 @@ def parse(self) -> List[str]: Returns: list of fields to be extracted and serialized """ + if self._fmt is None: + return [] + + if isinstance(self._style, str) and self._style == ",": + return [field.strip() for field in self._fmt.split(",") if field.strip()] + if isinstance(self._style, logging.StringTemplateStyle): formatter_style_pattern = STYLE_STRING_TEMPLATE_REGEX @@ -285,10 +299,7 @@ def parse(self) -> List[str]: else: raise ValueError(f"Style {self._style!r} is not supported") - if self._fmt: - return formatter_style_pattern.findall(self._fmt) - - return [] + return formatter_style_pattern.findall(self._fmt) def serialize_log_record(self, log_data: LogData) -> str: """Returns the final representation of the data to be logged diff --git a/tests/test_formatters.py b/tests/test_formatters.py index 35ebe5e..6f8bd17 100644 --- a/tests/test_formatters.py +++ b/tests/test_formatters.py @@ -158,12 +158,22 @@ def test_default_format(env: LoggingEnvironment, class_: type[BaseJsonFormatter] @pytest.mark.parametrize("class_", ALL_FORMATTERS) def test_percentage_format(env: LoggingEnvironment, class_: type[BaseJsonFormatter]): - env.set_formatter( - class_( - # All kind of different styles to check the regex - "[%(levelname)8s] %(message)s %(filename)s:%(lineno)d %(asctime)" - ) - ) + # Note: We use different %s styles in the format to check the regex correctly collects them + env.set_formatter(class_("[%(levelname)8s] %(message)s %(filename)s:%(lineno)d %(asctime)")) + + msg = "testing logging format" + env.logger.info(msg) + log_json = env.load_json() + + assert log_json["message"] == msg + assert log_json.keys() == {"levelname", "message", "filename", "lineno", "asctime"} + return + + +@pytest.mark.parametrize("class_", ALL_FORMATTERS) +def test_comma_format(env: LoggingEnvironment, class_: type[BaseJsonFormatter]): + # Note: we have double comma `,,` to test handling "empty" names + env.set_formatter(class_("levelname,,message,filename,lineno,asctime,", style=",")) msg = "testing logging format" env.logger.info(msg)