mirror of
https://github.com/python/cpython.git
synced 2025-12-07 17:57:56 +00:00
logging: Added style option to Formatter to allow %, {} or himBHformatting.
This commit is contained in:
parent
7e9065cf8c
commit
a39c571061
4 changed files with 103 additions and 12 deletions
|
|
@ -301,17 +301,29 @@ Formatters
|
||||||
Formatter objects configure the final order, structure, and contents of the log
|
Formatter objects configure the final order, structure, and contents of the log
|
||||||
message. Unlike the base :class:`logging.Handler` class, application code may
|
message. Unlike the base :class:`logging.Handler` class, application code may
|
||||||
instantiate formatter classes, although you could likely subclass the formatter
|
instantiate formatter classes, although you could likely subclass the formatter
|
||||||
if your application needs special behavior. The constructor takes two optional
|
if your application needs special behavior. The constructor takes three
|
||||||
arguments: a message format string and a date format string. If there is no
|
optional arguments -- a message format string, a date format string and a style
|
||||||
message format string, the default is to use the raw message. If there is no
|
indicator.
|
||||||
date format string, the default date format is::
|
|
||||||
|
.. method:: logging.Formatter.__init__(fmt=None, datefmt=None, style='%')
|
||||||
|
|
||||||
|
If there is no message format string, the default is to use the
|
||||||
|
raw message. If there is no date format string, the default date format is::
|
||||||
|
|
||||||
%Y-%m-%d %H:%M:%S
|
%Y-%m-%d %H:%M:%S
|
||||||
|
|
||||||
with the milliseconds tacked on at the end.
|
with the milliseconds tacked on at the end. The ``style`` is one of `%`, '{'
|
||||||
|
or '$'. If one of these is not specified, then '%' will be used.
|
||||||
|
|
||||||
The message format string uses ``%(<dictionary key>)s`` styled string
|
If the ``style`` is '%', the message format string uses
|
||||||
substitution; the possible keys are documented in :ref:`formatter-objects`.
|
``%(<dictionary key>)s`` styled string substitution; the possible keys are
|
||||||
|
documented in :ref:`formatter-objects`. If the style is '{', the message format
|
||||||
|
string is assumed to be compatible with :meth:`str.format` (using keyword
|
||||||
|
arguments), while if the style is '$' then the message format string should
|
||||||
|
conform to what is expected by :meth:`string.Template.substitute`.
|
||||||
|
|
||||||
|
.. versionchanged:: 3.2
|
||||||
|
Added the ``style`` parameter.
|
||||||
|
|
||||||
The following message format string will log the time in a human-readable
|
The following message format string will log the time in a human-readable
|
||||||
format, the severity of the message, and the contents of the message, in that
|
format, the severity of the message, and the contents of the message, in that
|
||||||
|
|
|
||||||
|
|
@ -395,18 +395,33 @@ class Formatter(object):
|
||||||
|
|
||||||
converter = time.localtime
|
converter = time.localtime
|
||||||
|
|
||||||
def __init__(self, fmt=None, datefmt=None):
|
def __init__(self, fmt=None, datefmt=None, style='%'):
|
||||||
"""
|
"""
|
||||||
Initialize the formatter with specified format strings.
|
Initialize the formatter with specified format strings.
|
||||||
|
|
||||||
Initialize the formatter either with the specified format string, or a
|
Initialize the formatter either with the specified format string, or a
|
||||||
default as described above. Allow for specialized date formatting with
|
default as described above. Allow for specialized date formatting with
|
||||||
the optional datefmt argument (if omitted, you get the ISO8601 format).
|
the optional datefmt argument (if omitted, you get the ISO8601 format).
|
||||||
|
|
||||||
|
Use a style parameter of '%', '{' or '$' to specify that you want to
|
||||||
|
use one of %-formatting, :meth:`str.format` (``{}``) formatting or
|
||||||
|
:class:`string.Template` formatting in your format string.
|
||||||
|
|
||||||
|
.. versionchanged: 3.2
|
||||||
|
Added the ``style`` parameter.
|
||||||
"""
|
"""
|
||||||
|
if style not in ('%', '$', '{'):
|
||||||
|
style = '%'
|
||||||
|
self._style = style
|
||||||
if fmt:
|
if fmt:
|
||||||
self._fmt = fmt
|
self._fmt = fmt
|
||||||
else:
|
else:
|
||||||
|
if style == '%':
|
||||||
self._fmt = "%(message)s"
|
self._fmt = "%(message)s"
|
||||||
|
elif style == '{':
|
||||||
|
self._fmt = '{message}'
|
||||||
|
else:
|
||||||
|
self._fmt = '${message}'
|
||||||
self.datefmt = datefmt
|
self.datefmt = datefmt
|
||||||
|
|
||||||
def formatTime(self, record, datefmt=None):
|
def formatTime(self, record, datefmt=None):
|
||||||
|
|
@ -432,7 +447,7 @@ class Formatter(object):
|
||||||
s = time.strftime(datefmt, ct)
|
s = time.strftime(datefmt, ct)
|
||||||
else:
|
else:
|
||||||
t = time.strftime("%Y-%m-%d %H:%M:%S", ct)
|
t = time.strftime("%Y-%m-%d %H:%M:%S", ct)
|
||||||
s = "%s,%03d" % (t, record.msecs)
|
s = "%s,%03d" % (t, record.msecs) # the use of % here is internal
|
||||||
return s
|
return s
|
||||||
|
|
||||||
def formatException(self, ei):
|
def formatException(self, ei):
|
||||||
|
|
@ -458,7 +473,14 @@ class Formatter(object):
|
||||||
"""
|
"""
|
||||||
Check if the format uses the creation time of the record.
|
Check if the format uses the creation time of the record.
|
||||||
"""
|
"""
|
||||||
return self._fmt.find("%(asctime)") >= 0
|
if self._style == '%':
|
||||||
|
result = self._fmt.find("%(asctime)") >= 0
|
||||||
|
elif self._style == '$':
|
||||||
|
result = self._fmt.find("{asctime}") >= 0
|
||||||
|
else:
|
||||||
|
result = self._fmt.find("$asctime") >= 0 or \
|
||||||
|
self._fmt.find("${asctime}") >= 0
|
||||||
|
return result
|
||||||
|
|
||||||
def format(self, record):
|
def format(self, record):
|
||||||
"""
|
"""
|
||||||
|
|
@ -476,7 +498,14 @@ class Formatter(object):
|
||||||
record.message = record.getMessage()
|
record.message = record.getMessage()
|
||||||
if self.usesTime():
|
if self.usesTime():
|
||||||
record.asctime = self.formatTime(record, self.datefmt)
|
record.asctime = self.formatTime(record, self.datefmt)
|
||||||
|
style = self._style
|
||||||
|
if style == '%':
|
||||||
s = self._fmt % record.__dict__
|
s = self._fmt % record.__dict__
|
||||||
|
elif style == '{':
|
||||||
|
s = self._fmt.format(**record.__dict__)
|
||||||
|
else:
|
||||||
|
from string import Template
|
||||||
|
s = Template(self._fmt).substitute(**record.__dict__)
|
||||||
if record.exc_info:
|
if record.exc_info:
|
||||||
# Cache the traceback text to avoid converting it multiple times
|
# Cache the traceback text to avoid converting it multiple times
|
||||||
# (it's constant anyway)
|
# (it's constant anyway)
|
||||||
|
|
|
||||||
|
|
@ -1863,6 +1863,53 @@ class QueueHandlerTest(BaseTest):
|
||||||
self.assertEqual(data.name, self.que_logger.name)
|
self.assertEqual(data.name, self.que_logger.name)
|
||||||
self.assertEqual((data.msg, data.args), (msg, None))
|
self.assertEqual((data.msg, data.args), (msg, None))
|
||||||
|
|
||||||
|
class FormatterTest(unittest.TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
self.common = {
|
||||||
|
'name': 'formatter.test',
|
||||||
|
'level': logging.DEBUG,
|
||||||
|
'pathname': os.path.join('path', 'to', 'dummy.ext'),
|
||||||
|
'lineno': 42,
|
||||||
|
'exc_info': None,
|
||||||
|
'func': None,
|
||||||
|
'msg': 'Message with %d %s',
|
||||||
|
'args': (2, 'placeholders'),
|
||||||
|
}
|
||||||
|
self.variants = {
|
||||||
|
}
|
||||||
|
|
||||||
|
def get_record(self, name=None):
|
||||||
|
result = dict(self.common)
|
||||||
|
if name is not None:
|
||||||
|
result.update(self.variants[name])
|
||||||
|
return logging.makeLogRecord(result)
|
||||||
|
|
||||||
|
def test_percent(self):
|
||||||
|
"Test %-formatting"
|
||||||
|
r = self.get_record()
|
||||||
|
f = logging.Formatter('${%(message)s}')
|
||||||
|
self.assertEqual(f.format(r), '${Message with 2 placeholders}')
|
||||||
|
f = logging.Formatter('%(random)s')
|
||||||
|
self.assertRaises(KeyError, f.format, r)
|
||||||
|
|
||||||
|
def test_braces(self):
|
||||||
|
"Test {}-formatting"
|
||||||
|
r = self.get_record()
|
||||||
|
f = logging.Formatter('$%{message}%$', style='{')
|
||||||
|
self.assertEqual(f.format(r), '$%Message with 2 placeholders%$')
|
||||||
|
f = logging.Formatter('{random}', style='{')
|
||||||
|
self.assertRaises(KeyError, f.format, r)
|
||||||
|
|
||||||
|
def test_dollars(self):
|
||||||
|
"Test $-formatting"
|
||||||
|
r = self.get_record()
|
||||||
|
f = logging.Formatter('$message', style='$')
|
||||||
|
self.assertEqual(f.format(r), 'Message with 2 placeholders')
|
||||||
|
f = logging.Formatter('$$%${message}%$$', style='$')
|
||||||
|
self.assertEqual(f.format(r), '$%Message with 2 placeholders%$')
|
||||||
|
f = logging.Formatter('${random}', style='$')
|
||||||
|
self.assertRaises(KeyError, f.format, r)
|
||||||
|
|
||||||
class BaseFileTest(BaseTest):
|
class BaseFileTest(BaseTest):
|
||||||
"Base class for handler tests that write log files"
|
"Base class for handler tests that write log files"
|
||||||
|
|
||||||
|
|
@ -1945,6 +1992,7 @@ def test_main():
|
||||||
CustomLevelsAndFiltersTest, MemoryHandlerTest,
|
CustomLevelsAndFiltersTest, MemoryHandlerTest,
|
||||||
ConfigFileTest, SocketHandlerTest, MemoryTest,
|
ConfigFileTest, SocketHandlerTest, MemoryTest,
|
||||||
EncodingTest, WarningsTest, ConfigDictTest, ManagerTest,
|
EncodingTest, WarningsTest, ConfigDictTest, ManagerTest,
|
||||||
|
FormatterTest,
|
||||||
LogRecordClassTest, ChildLoggerTest, QueueHandlerTest,
|
LogRecordClassTest, ChildLoggerTest, QueueHandlerTest,
|
||||||
RotatingFileHandlerTest,
|
RotatingFileHandlerTest,
|
||||||
#TimedRotatingFileHandlerTest
|
#TimedRotatingFileHandlerTest
|
||||||
|
|
|
||||||
|
|
@ -51,6 +51,8 @@ Core and Builtins
|
||||||
Library
|
Library
|
||||||
-------
|
-------
|
||||||
|
|
||||||
|
- logging: Added style option to Formatter to allow %, {} or $-formatting.
|
||||||
|
|
||||||
- Issue #5178: Added tempfile.TemporaryDirectory class that can be used
|
- Issue #5178: Added tempfile.TemporaryDirectory class that can be used
|
||||||
as a context manager.
|
as a context manager.
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue