Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion colorlog/formatter.py
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,12 @@ def __init__(self, fmt: typing.Mapping[str, str], **kwargs: typing.Any) -> None:
- fmt (dict):
A mapping of log levels (represented as strings, e.g. 'WARNING') to
format strings. (*New in version 2.7.0)

Levels that are not present in the mapping fall back to a default
formatter, so records logged at custom or unlisted levels are
formatted rather than raising a ``KeyError``. To customise the
fallback, provide a format string under the special ``"DEFAULT"``
key; otherwise the default format for the chosen ``style`` is used.
(All other parameters are the same as in colorlog.ColoredFormatter)

Example:
Expand All @@ -229,9 +235,15 @@ def __init__(self, fmt: typing.Mapping[str, str], **kwargs: typing.Any) -> None:
self.formatters = {
level: ColoredFormatter(fmt=f, **kwargs) for level, f in fmt.items()
}
# Used for any level not present in ``fmt``. An explicit "DEFAULT" entry
# takes precedence; otherwise fall back to the default format string.
self.default_formatter = self.formatters.get(
"DEFAULT", ColoredFormatter(**kwargs)
)

def format(self, record: logging.LogRecord) -> str:
return self.formatters[record.levelname].format(record)
formatter = self.formatters.get(record.levelname, self.default_formatter)
return formatter.format(record)


# Provided for backwards compatibility. The features provided by this subclass are now
Expand Down
24 changes: 24 additions & 0 deletions colorlog/tests/test_colorlog.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"""Test the colorlog.colorlog module."""

import logging
import sys
import unittest.mock

Expand Down Expand Up @@ -100,6 +101,29 @@ def test_level_formatter(self, create_and_test_logger):
},
)

def test_unlisted_level_uses_default(self):
"""A level missing from fmt must not raise; it uses the fallback."""
logging.addLevelName(25, "NOTICE")
formatter = colorlog.LevelFormatter(fmt={"INFO": "%(message)s"})
record = logging.LogRecord(
"test", 25, "path", 1, "a notice message", None, None
)
assert "a notice message" in formatter.format(record)

def test_explicit_default_key(self):
"""A 'DEFAULT' entry overrides the fallback format for unlisted levels."""
logging.addLevelName(25, "NOTICE")
formatter = colorlog.LevelFormatter(
fmt={
"INFO": "%(message)s",
"DEFAULT": "DEFAULT: %(message)s",
}
)
record = logging.LogRecord(
"test", 25, "path", 1, "a notice message", None, None
)
assert "DEFAULT: a notice message" in formatter.format(record)


def test_ttycolorlog(create_and_test_logger, monkeypatch):
monkeypatch.setattr(sys.stderr, "isatty", lambda: True)
Expand Down
Loading