diff --git a/fluent/handler.py b/fluent/handler.py index 78eb1d8..e4d34ea 100644 --- a/fluent/handler.py +++ b/fluent/handler.py @@ -24,19 +24,36 @@ class FluentRecordFormatter(logging.Formatter, object): :param fmt: a dict with format string as values to map to provided keys. :param datefmt: strftime()-compatible date/time format string. - :param style: (NOT USED) + :param style: '%', '{' or '$' (used only with Python 3.2 or above) :param fill_missing_fmt_key: if True, do not raise a KeyError if the format key is not found. Put None if not found.s """ def __init__(self, fmt=None, datefmt=None, style='%', fill_missing_fmt_key=False): super(FluentRecordFormatter, self).__init__(None, datefmt) - if not fmt: - self._fmt_dict = { + if sys.version_info[0:2] >= (3, 2) and style != '%': + self.__style, basic_fmt_dict = { + '{': (logging.StrFormatStyle, { + 'sys_host': '{hostname}', + 'sys_name': '{name}', + 'sys_module': '{module}', + }), + '$': (logging.StringTemplateStyle, { + 'sys_host': '${hostname}', + 'sys_name': '${name}', + 'sys_module': '${module}', + }), + }[style] + else: + self.__style = None + basic_fmt_dict = { 'sys_host': '%(hostname)s', 'sys_name': '%(name)s', 'sys_module': '%(module)s', } + + if not fmt: + self._fmt_dict = basic_fmt_dict else: self._fmt_dict = fmt @@ -58,7 +75,10 @@ def format(self, record): data = {} for key, value in self._fmt_dict.items(): try: - value = value % record.__dict__ + if self.__style: + value = self.__style(value).format(record) + else: + value = value % record.__dict__ except KeyError as exc: value = None if not self.fill_missing_fmt_key: diff --git a/tests/test_handler.py b/tests/test_handler.py index 9180231..8a35537 100644 --- a/tests/test_handler.py +++ b/tests/test_handler.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- import logging +import sys import unittest import fluent.handler @@ -62,6 +63,52 @@ def test_custom_fmt(self): self.assertTrue('lineno' in data[0][2]) self.assertTrue('emitted_at' in data[0][2]) + @unittest.skipUnless(sys.version_info[0:2] >= (3, 2), 'supported with Python 3.2 or above') + def test_custom_fmt_with_format_style(self): + handler = fluent.handler.FluentHandler('app.follow', port=self._port) + + logging.basicConfig(level=logging.INFO) + log = logging.getLogger('fluent.test') + handler.setFormatter( + fluent.handler.FluentRecordFormatter(fmt={ + 'name': '{name}', + 'lineno': '{lineno}', + 'emitted_at': '{asctime}', + }, style='{') + ) + log.addHandler(handler) + log.info({'sample': 'value'}) + handler.close() + + data = self.get_data() + self.assertTrue('name' in data[0][2]) + self.assertEqual('fluent.test', data[0][2]['name']) + self.assertTrue('lineno' in data[0][2]) + self.assertTrue('emitted_at' in data[0][2]) + + @unittest.skipUnless(sys.version_info[0:2] >= (3, 2), 'supported with Python 3.2 or above') + def test_custom_fmt_with_template_style(self): + handler = fluent.handler.FluentHandler('app.follow', port=self._port) + + logging.basicConfig(level=logging.INFO) + log = logging.getLogger('fluent.test') + handler.setFormatter( + fluent.handler.FluentRecordFormatter(fmt={ + 'name': '${name}', + 'lineno': '${lineno}', + 'emitted_at': '${asctime}', + }, style='$') + ) + log.addHandler(handler) + log.info({'sample': 'value'}) + handler.close() + + data = self.get_data() + self.assertTrue('name' in data[0][2]) + self.assertEqual('fluent.test', data[0][2]['name']) + self.assertTrue('lineno' in data[0][2]) + self.assertTrue('emitted_at' in data[0][2]) + def test_custom_field_raise_exception(self): handler = fluent.handler.FluentHandler('app.follow', port=self._port)