diff options
author | Bar Harel <bzvi7919@gmail.com> | 2020-06-18 14:18:58 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-06-18 14:18:58 (GMT) |
commit | 8f192d12af82c4dc40730bf59814f6a68f68f950 (patch) | |
tree | a7666641789d3aca6f2323b7fc6f0d642eac250b | |
parent | ddbeb2f3e02a510c5784ffd74c5e09e8c70b5881 (diff) | |
download | cpython-8f192d12af82c4dc40730bf59814f6a68f68f950.zip cpython-8f192d12af82c4dc40730bf59814f6a68f68f950.tar.gz cpython-8f192d12af82c4dc40730bf59814f6a68f68f950.tar.bz2 |
bpo-40884: Added defaults parameter for logging.Formatter (GH-20668)
Docs and tests are underway.
Automerge-Triggered-By: @vsajip
-rw-r--r-- | Doc/library/logging.rst | 10 | ||||
-rw-r--r-- | Lib/logging/__init__.py | 30 | ||||
-rw-r--r-- | Lib/test/test_logging.py | 23 | ||||
-rw-r--r-- | Misc/NEWS.d/next/Library/2020-06-06-02-42-26.bpo-40884.n7fOwS.rst | 3 |
4 files changed, 57 insertions, 9 deletions
diff --git a/Doc/library/logging.rst b/Doc/library/logging.rst index 7267f81..3ff67f7 100644 --- a/Doc/library/logging.rst +++ b/Doc/library/logging.rst @@ -529,7 +529,8 @@ The useful mapping keys in a :class:`LogRecord` are given in the section on :ref:`logrecord-attributes`. -.. class:: Formatter(fmt=None, datefmt=None, style='%', validate=True) +.. class:: Formatter(fmt=None, datefmt=None, style='%', validate=True, *, + defaults=None) Returns a new instance of the :class:`Formatter` class. The instance is initialized with a format string for the message as a whole, as well as a @@ -545,6 +546,10 @@ The useful mapping keys in a :class:`LogRecord` are given in the section on :ref:`formatting-styles` for more information on using {- and $-formatting for log messages. + The *defaults* parameter can be a dictionary with default values to use in + custom fields. For example: + ``logging.Formatter('%(ip)s %(message)s', defaults={"ip": None})`` + .. versionchanged:: 3.2 The *style* parameter was added. @@ -553,6 +558,9 @@ The useful mapping keys in a :class:`LogRecord` are given in the section on will raise a ``ValueError``. For example: ``logging.Formatter('%(asctime)s - %(message)s', style='{')``. + .. versionchanged:: 3.10 + The *defaults* parameter was added. + .. method:: format(record) The record's attribute dictionary is used as the operand to a string diff --git a/Lib/logging/__init__.py b/Lib/logging/__init__.py index 1c446fd..94361ca 100644 --- a/Lib/logging/__init__.py +++ b/Lib/logging/__init__.py @@ -411,8 +411,9 @@ class PercentStyle(object): asctime_search = '%(asctime)' validation_pattern = re.compile(r'%\(\w+\)[#0+ -]*(\*|\d+)?(\.(\*|\d+))?[diouxefgcrsa%]', re.I) - def __init__(self, fmt): + def __init__(self, fmt, *, defaults=None): self._fmt = fmt or self.default_format + self._defaults = defaults def usesTime(self): return self._fmt.find(self.asctime_search) >= 0 @@ -423,7 +424,11 @@ class PercentStyle(object): raise ValueError("Invalid format '%s' for '%s' style" % (self._fmt, self.default_format[0])) def _format(self, record): - return self._fmt % record.__dict__ + if defaults := self._defaults: + values = defaults | record.__dict__ + else: + values = record.__dict__ + return self._fmt % values def format(self, record): try: @@ -441,7 +446,11 @@ class StrFormatStyle(PercentStyle): field_spec = re.compile(r'^(\d+|\w+)(\.\w+|\[[^]]+\])*$') def _format(self, record): - return self._fmt.format(**record.__dict__) + if defaults := self._defaults: + values = defaults | record.__dict__ + else: + values = record.__dict__ + return self._fmt.format(**values) def validate(self): """Validate the input format, ensure it is the correct string formatting style""" @@ -467,8 +476,8 @@ class StringTemplateStyle(PercentStyle): asctime_format = '${asctime}' asctime_search = '${asctime}' - def __init__(self, fmt): - self._fmt = fmt or self.default_format + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) self._tpl = Template(self._fmt) def usesTime(self): @@ -490,7 +499,11 @@ class StringTemplateStyle(PercentStyle): raise ValueError('invalid format: no fields') def _format(self, record): - return self._tpl.substitute(**record.__dict__) + if defaults := self._defaults: + values = defaults | record.__dict__ + else: + values = record.__dict__ + return self._tpl.substitute(**values) BASIC_FORMAT = "%(levelname)s:%(name)s:%(message)s" @@ -546,7 +559,8 @@ class Formatter(object): converter = time.localtime - def __init__(self, fmt=None, datefmt=None, style='%', validate=True): + def __init__(self, fmt=None, datefmt=None, style='%', validate=True, *, + defaults=None): """ Initialize the formatter with specified format strings. @@ -565,7 +579,7 @@ class Formatter(object): if style not in _STYLES: raise ValueError('Style must be one of: %s' % ','.join( _STYLES.keys())) - self._style = _STYLES[style][0](fmt) + self._style = _STYLES[style][0](fmt, defaults=defaults) if validate: self._style.validate() diff --git a/Lib/test/test_logging.py b/Lib/test/test_logging.py index e719d26..2ae00b6 100644 --- a/Lib/test/test_logging.py +++ b/Lib/test/test_logging.py @@ -3710,6 +3710,9 @@ class FormatterTest(unittest.TestCase): 'args': (2, 'placeholders'), } self.variants = { + 'custom': { + 'custom': 1234 + } } def get_record(self, name=None): @@ -3926,6 +3929,26 @@ class FormatterTest(unittest.TestCase): ) self.assertRaises(ValueError, logging.Formatter, '${asctime', style='$') + def test_defaults_parameter(self): + fmts = ['%(custom)s %(message)s', '{custom} {message}', '$custom $message'] + styles = ['%', '{', '$'] + for fmt, style in zip(fmts, styles): + f = logging.Formatter(fmt, style=style, defaults={'custom': 'Default'}) + r = self.get_record() + self.assertEqual(f.format(r), 'Default Message with 2 placeholders') + r = self.get_record("custom") + self.assertEqual(f.format(r), '1234 Message with 2 placeholders') + + # Without default + f = logging.Formatter(fmt, style=style) + r = self.get_record() + self.assertRaises(ValueError, f.format, r) + + # Non-existing default is ignored + f = logging.Formatter(fmt, style=style, defaults={'Non-existing': 'Default'}) + r = self.get_record("custom") + self.assertEqual(f.format(r), '1234 Message with 2 placeholders') + def test_invalid_style(self): self.assertRaises(ValueError, logging.Formatter, None, None, 'x') diff --git a/Misc/NEWS.d/next/Library/2020-06-06-02-42-26.bpo-40884.n7fOwS.rst b/Misc/NEWS.d/next/Library/2020-06-06-02-42-26.bpo-40884.n7fOwS.rst new file mode 100644 index 0000000..64990e8 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-06-06-02-42-26.bpo-40884.n7fOwS.rst @@ -0,0 +1,3 @@ +Added a `defaults` parameter to :class:`logging.Formatter`, to allow +specifying default values for custom fields. Patch by Asaf Alon and Bar +Harel. |