Commit 6a65c5df authored by Vinay Sajip's avatar Vinay Sajip

logging: Improved Formatter implementation.

parent ba488d15
...@@ -24,6 +24,7 @@ To use, simply 'import logging' and log away! ...@@ -24,6 +24,7 @@ To use, simply 'import logging' and log away!
""" """
import sys, os, time, io, traceback, warnings, weakref import sys, os, time, io, traceback, warnings, weakref
from string import Template
__all__ = ['BASIC_FORMAT', 'BufferingFormatter', 'CRITICAL', 'DEBUG', 'ERROR', __all__ = ['BASIC_FORMAT', 'BufferingFormatter', 'CRITICAL', 'DEBUG', 'ERROR',
'FATAL', 'FileHandler', 'Filter', 'Formatter', 'Handler', 'INFO', 'FATAL', 'FileHandler', 'Filter', 'Formatter', 'Handler', 'INFO',
...@@ -351,6 +352,49 @@ def makeLogRecord(dict): ...@@ -351,6 +352,49 @@ def makeLogRecord(dict):
# Formatter classes and functions # Formatter classes and functions
#--------------------------------------------------------------------------- #---------------------------------------------------------------------------
class PercentStyle(object):
default_format = '%(message)s'
asctime_format = '%(asctime)s'
def __init__(self, fmt):
self._fmt = fmt or self.default_format
def usesTime(self):
return self._fmt.find(self.asctime_format) >= 0
def format(self, record):
return self._fmt % record.__dict__
class StrFormatStyle(PercentStyle):
default_format = '{message}'
asctime_format = '{asctime}'
def format(self, record):
return self._fmt.format(**record.__dict__)
class StringTemplateStyle(PercentStyle):
default_format = '${message}'
asctime_format = '${asctime}'
def __init__(self, fmt):
self._fmt = fmt or self.default_format
self._tpl = Template(self._fmt)
def usesTime(self):
fmt = self._fmt
return fmt.find('$asctime') >= 0 or fmt.find(self.asctime_format) >= 0
def format(self, record):
return self._tpl.substitute(**record.__dict__)
_STYLES = {
'%': PercentStyle,
'{': StrFormatStyle,
'$': StringTemplateStyle
}
class Formatter(object): class Formatter(object):
""" """
Formatter instances are used to convert a LogRecord to text. Formatter instances are used to convert a LogRecord to text.
...@@ -410,18 +454,11 @@ class Formatter(object): ...@@ -410,18 +454,11 @@ class Formatter(object):
.. versionchanged: 3.2 .. versionchanged: 3.2
Added the ``style`` parameter. Added the ``style`` parameter.
""" """
if style not in ('%', '$', '{'): if style not in _STYLES:
style = '%' raise ValueError('Style must be one of: %s' % ','.join(
self._style = style _STYLES.keys()))
if fmt: self._style = _STYLES[style](fmt)
self._fmt = fmt self._fmt = self._style._fmt
else:
if style == '%':
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):
...@@ -473,25 +510,10 @@ class Formatter(object): ...@@ -473,25 +510,10 @@ 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.
""" """
if self._style == '%': return self._style.usesTime()
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 formatMessage(self, record): def formatMessage(self, record):
style = self._style return self._style.format(record)
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__)
return s
def format(self, record): def format(self, record):
""" """
......
...@@ -1891,6 +1891,11 @@ class FormatterTest(unittest.TestCase): ...@@ -1891,6 +1891,11 @@ class FormatterTest(unittest.TestCase):
self.assertEqual(f.format(r), '${Message with 2 placeholders}') self.assertEqual(f.format(r), '${Message with 2 placeholders}')
f = logging.Formatter('%(random)s') f = logging.Formatter('%(random)s')
self.assertRaises(KeyError, f.format, r) self.assertRaises(KeyError, f.format, r)
self.assertFalse(f.usesTime())
f = logging.Formatter('%(asctime)s')
self.assertTrue(f.usesTime())
f = logging.Formatter('asctime')
self.assertFalse(f.usesTime())
def test_braces(self): def test_braces(self):
"Test {}-formatting" "Test {}-formatting"
...@@ -1899,6 +1904,11 @@ class FormatterTest(unittest.TestCase): ...@@ -1899,6 +1904,11 @@ class FormatterTest(unittest.TestCase):
self.assertEqual(f.format(r), '$%Message with 2 placeholders%$') self.assertEqual(f.format(r), '$%Message with 2 placeholders%$')
f = logging.Formatter('{random}', style='{') f = logging.Formatter('{random}', style='{')
self.assertRaises(KeyError, f.format, r) self.assertRaises(KeyError, f.format, r)
self.assertFalse(f.usesTime())
f = logging.Formatter('{asctime}', style='{')
self.assertTrue(f.usesTime())
f = logging.Formatter('asctime', style='{')
self.assertFalse(f.usesTime())
def test_dollars(self): def test_dollars(self):
"Test $-formatting" "Test $-formatting"
...@@ -1909,6 +1919,13 @@ class FormatterTest(unittest.TestCase): ...@@ -1909,6 +1919,13 @@ class FormatterTest(unittest.TestCase):
self.assertEqual(f.format(r), '$%Message with 2 placeholders%$') self.assertEqual(f.format(r), '$%Message with 2 placeholders%$')
f = logging.Formatter('${random}', style='$') f = logging.Formatter('${random}', style='$')
self.assertRaises(KeyError, f.format, r) self.assertRaises(KeyError, f.format, r)
self.assertFalse(f.usesTime())
f = logging.Formatter('${asctime}', style='$')
self.assertTrue(f.usesTime())
f = logging.Formatter('$asctime', style='$')
self.assertTrue(f.usesTime())
f = logging.Formatter('asctime', style='$')
self.assertFalse(f.usesTime())
class BaseFileTest(BaseTest): class BaseFileTest(BaseTest):
"Base class for handler tests that write log files" "Base class for handler tests that write log files"
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment