: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
: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.
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
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
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:
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"""
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):
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"
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.
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()
'args': (2, 'placeholders'),
}
self.variants = {
+ 'custom': {
+ 'custom': 1234
+ }
}
def get_record(self, name=None):
)
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')