diff --git a/docs/changelog.md b/docs/changelog.md index e96421c..624cdf3 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` arguemtns 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 e3d5ab8..2f2bcd2 100644 --- a/src/pythonjsonlogger/core.py +++ b/src/pythonjsonlogger/core.py @@ -175,6 +175,12 @@ 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 ## --------------------------------------------------------------------- @@ -186,7 +192,7 @@ def __init__( self._style = _style self._fmt = _style._fmt - elif not validate: + elif style == "," or not validate: self._style = style self._fmt = fmt @@ -271,6 +277,18 @@ def parse(self) -> List[str]: Returns: list of fields to be extracted and serialized """ + if self._fmt is None: + # TODO: does it matter that we do this before checking if the style is valid? + # (we already (mostly) check for valid style names in __init__ + return [] + + if isinstance(self._style, str) and self._style == ",": + # TODO: should we check that there are no empty fields? + # If yes we should do this in __init__ where we validate other styles? + # Do we drop empty fields? + # etc + 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 +303,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..0a0f458 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: All kind of different %s styles to check the regex + 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)