]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
logging: Added style option to Formatter to allow %, {} or himBHformatting.
authorVinay Sajip <vinay_sajip@yahoo.co.uk>
Mon, 25 Oct 2010 13:57:39 +0000 (13:57 +0000)
committerVinay Sajip <vinay_sajip@yahoo.co.uk>
Mon, 25 Oct 2010 13:57:39 +0000 (13:57 +0000)
Doc/library/logging.rst
Lib/logging/__init__.py
Lib/test/test_logging.py
Misc/NEWS

index cb613cee0cf29e925c2eca0e81a5e9a37b9582d0..902373810872294193beb0b4de2b684b9d9dd264 100644 (file)
@@ -301,17 +301,29 @@ Formatters
 Formatter objects configure the final order, structure, and contents of the log
 message.  Unlike the base :class:`logging.Handler` class, application code may
 instantiate formatter classes, although you could likely subclass the formatter
-if your application needs special behavior.  The constructor takes two optional
-arguments: a message format string and a date format string.  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::
+if your application needs special behavior.  The constructor takes three
+optional arguments -- a message format string, a date format string and a style
+indicator.
+
+.. 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
 
-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
-substitution; the possible keys are documented in :ref:`formatter-objects`.
+If the ``style`` is '%', the message format string uses
+``%(<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
 format, the severity of the message, and the contents of the message, in that
index 7f217d4c90fa6063aa5497be26613ddf83cc772a..0e29cf355c0f7f433a30158cb66a388fa9b1e56c 100644 (file)
@@ -395,18 +395,33 @@ class Formatter(object):
 
     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 either with the specified format string, or a
         default as described above. Allow for specialized date formatting with
         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:
             self._fmt = fmt
         else:
-            self._fmt = "%(message)s"
+            if style == '%':
+                self._fmt = "%(message)s"
+            elif style == '{':
+                self._fmt = '{message}'
+            else:
+                self._fmt = '${message}'
         self.datefmt = datefmt
 
     def formatTime(self, record, datefmt=None):
@@ -432,7 +447,7 @@ class Formatter(object):
             s = time.strftime(datefmt, ct)
         else:
             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
 
     def formatException(self, ei):
@@ -458,7 +473,14 @@ class Formatter(object):
         """
         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):
         """
@@ -476,7 +498,14 @@ class Formatter(object):
         record.message = record.getMessage()
         if self.usesTime():
             record.asctime = self.formatTime(record, self.datefmt)
-        s = self._fmt % record.__dict__
+        style = self._style
+        if style == '%':
+            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:
             # Cache the traceback text to avoid converting it multiple times
             # (it's constant anyway)
index a738d7a8758d181dd67f1f0b57ad7900c30a398d..9aa6af3a6adb02580a200878d3e7ae3c34110fe4 100644 (file)
@@ -1863,6 +1863,53 @@ class QueueHandlerTest(BaseTest):
         self.assertEqual(data.name, self.que_logger.name)
         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):
     "Base class for handler tests that write log files"
 
@@ -1945,6 +1992,7 @@ def test_main():
                  CustomLevelsAndFiltersTest, MemoryHandlerTest,
                  ConfigFileTest, SocketHandlerTest, MemoryTest,
                  EncodingTest, WarningsTest, ConfigDictTest, ManagerTest,
+                 FormatterTest,
                  LogRecordClassTest, ChildLoggerTest, QueueHandlerTest,
                  RotatingFileHandlerTest,
                  #TimedRotatingFileHandlerTest
index e3df59acc0925b94c23918e212b29cddfae03e17..d9098d5307c49f5a600e51906a24956d32541197 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -51,6 +51,8 @@ Core and Builtins
 Library
 -------
 
+- logging: Added style option to Formatter to allow %, {} or $-formatting.
+
 - Issue #5178: Added tempfile.TemporaryDirectory class that can be used
   as a context manager.