Skip to content

Commit d99bca7

Browse files
authored
Merge pull request #101 from arcivanov/issue_100
Allow formatter to use attribute exclusion set
2 parents af4fd66 + b9a6bd2 commit d99bca7

File tree

2 files changed

+74
-21
lines changed

2 files changed

+74
-21
lines changed

fluent/handler.py

+36-18
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,14 @@ class FluentRecordFormatter(logging.Formatter, object):
2929
key is not found. Put None if not found.
3030
:param format_json: if True, will attempt to parse message as json. If not,
3131
will use message as-is. Defaults to True
32+
:param exclude_attrs: switches this formatter into a mode where all attributes
33+
except the ones specified by `exclude_attrs` are logged with the record as is.
34+
If `None`, operates as before, otherwise `fmt` is ignored.
35+
Can be a `list`, `tuple` or a `set`.
3236
"""
3337

34-
def __init__(self, fmt=None, datefmt=None, style='%', fill_missing_fmt_key=False, format_json=True):
38+
def __init__(self, fmt=None, datefmt=None, style='%', fill_missing_fmt_key=False, format_json=True,
39+
exclude_attrs=None):
3540
super(FluentRecordFormatter, self).__init__(None, datefmt)
3641

3742
if sys.version_info[0:2] >= (3, 2) and style != '%':
@@ -55,10 +60,15 @@ def __init__(self, fmt=None, datefmt=None, style='%', fill_missing_fmt_key=False
5560
'sys_module': '%(module)s',
5661
}
5762

58-
if not fmt:
59-
self._fmt_dict = basic_fmt_dict
63+
if exclude_attrs is not None:
64+
self._exc_attrs = set(exclude_attrs)
65+
self._fmt_dict = None
6066
else:
61-
self._fmt_dict = fmt
67+
self._exc_attrs = None
68+
if not fmt:
69+
self._fmt_dict = basic_fmt_dict
70+
else:
71+
self._fmt_dict = fmt
6272

6373
if format_json:
6474
self._format_msg = self._format_msg_json
@@ -81,25 +91,33 @@ def format(self, record):
8191

8292
# Apply format
8393
data = {}
84-
for key, value in self._fmt_dict.items():
85-
try:
86-
if self.__style:
87-
value = self.__style(value).format(record)
88-
else:
89-
value = value % record.__dict__
90-
except KeyError as exc:
91-
value = None
92-
if not self.fill_missing_fmt_key:
93-
raise exc
94-
95-
data[key] = value
94+
if self._exc_attrs is not None:
95+
for key, value in record.__dict__.items():
96+
if key not in self._exc_attrs:
97+
data[key] = value
98+
else:
99+
for key, value in self._fmt_dict.items():
100+
try:
101+
if self.__style:
102+
value = self.__style(value).format(record)
103+
else:
104+
value = value % record.__dict__
105+
except KeyError as exc:
106+
value = None
107+
if not self.fill_missing_fmt_key:
108+
raise exc
109+
110+
data[key] = value
96111

97112
self._structuring(data, record)
98113
return data
99114

100115
def usesTime(self):
101-
return any([value.find('%(asctime)') >= 0
102-
for value in self._fmt_dict.values()])
116+
if self._exc_attrs is not None:
117+
return super(FluentRecordFormatter, self).usesTime()
118+
else:
119+
return any([value.find('%(asctime)') >= 0
120+
for value in self._fmt_dict.values()])
103121

104122
def _structuring(self, data, record):
105123
""" Melds `msg` into `data`.

tests/test_handler.py

+38-3
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
1-
# -*- coding: utf-8 -*-
1+
#  -*- coding: utf-8 -*-
22

33
import logging
44
import sys
55
import unittest
66

77
import fluent.handler
8-
98
from tests import mockserver
109

1110

@@ -63,6 +62,42 @@ def test_custom_fmt(self):
6362
self.assertTrue('lineno' in data[0][2])
6463
self.assertTrue('emitted_at' in data[0][2])
6564

65+
def test_exclude_attrs(self):
66+
handler = fluent.handler.FluentHandler('app.follow', port=self._port)
67+
68+
logging.basicConfig(level=logging.INFO)
69+
log = logging.getLogger('fluent.test')
70+
handler.setFormatter(
71+
fluent.handler.FluentRecordFormatter(exclude_attrs=[])
72+
)
73+
log.addHandler(handler)
74+
log.info({'sample': 'value'})
75+
handler.close()
76+
77+
data = self.get_data()
78+
self.assertTrue('name' in data[0][2])
79+
self.assertEqual('fluent.test', data[0][2]['name'])
80+
self.assertTrue('lineno' in data[0][2])
81+
82+
def test_exclude_attrs_with_extra(self):
83+
handler = fluent.handler.FluentHandler('app.follow', port=self._port)
84+
85+
logging.basicConfig(level=logging.INFO)
86+
log = logging.getLogger('fluent.test')
87+
handler.setFormatter(
88+
fluent.handler.FluentRecordFormatter(exclude_attrs=[])
89+
)
90+
log.addHandler(handler)
91+
log.info("Test with value '%s'", "test value", extra={"x": 1234})
92+
handler.close()
93+
94+
data = self.get_data()
95+
self.assertTrue('name' in data[0][2])
96+
self.assertEqual('fluent.test', data[0][2]['name'])
97+
self.assertTrue('lineno' in data[0][2])
98+
self.assertEqual("Test with value 'test value'", data[0][2]['message'])
99+
self.assertEqual(1234, data[0][2]['x'])
100+
66101
@unittest.skipUnless(sys.version_info[0:2] >= (3, 2), 'supported with Python 3.2 or above')
67102
def test_custom_fmt_with_format_style(self):
68103
handler = fluent.handler.FluentHandler('app.follow', port=self._port)
@@ -135,7 +170,7 @@ def test_custom_field_fill_missing_fmt_key_is_true(self):
135170
fluent.handler.FluentRecordFormatter(fmt={
136171
'name': '%(name)s',
137172
'custom_field': '%(custom_field)s'
138-
},
173+
},
139174
fill_missing_fmt_key=True
140175
)
141176
)

0 commit comments

Comments
 (0)