Skip to content

Commit

Permalink
New Inclusion class
Browse files Browse the repository at this point in the history
  • Loading branch information
twaugh committed Oct 12, 2015
1 parent 1b46dae commit 78678cc
Show file tree
Hide file tree
Showing 3 changed files with 87 additions and 24 deletions.
3 changes: 2 additions & 1 deletion journal_brief/cli/main.py
Expand Up @@ -96,7 +96,8 @@ def show_stats(self, entries, exclusions):
strf = "{FREQ:>10} {EXCLUSION}"
print(strf.format(FREQ='FREQUENCY', EXCLUSION='EXCLUSION'))
for stat in stats:
print(strf.format(FREQ=stat.hits, EXCLUSION=repr(stat.exclusion)))
print(strf.format(FREQ=stat.hits,
EXCLUSION=repr(dict(stat.exclusion))))

def stream_output(self, stream, formatters, jfilter):
try:
Expand Down
79 changes: 57 additions & 22 deletions journal_brief/filter.py
Expand Up @@ -29,38 +29,81 @@
ExclusionStatistics = namedtuple('ExclusionStatistics', ['hits', 'exclusion'])


class Exclusion(dict):
class FilterRule(dict):
"""
str (field) -> list (str values)
A mapping of field names to values that are significant for that field
"""

def __init__(self, mapping, comment=None):
def __init__(self, mapping):
assert isinstance(mapping, dict)

# Make sure everything is interpreted as a string
str_mapping = {}
log.debug("new exclusion rule:")
for field, matches in mapping.items():
if field == 'PRIORITY':
str_mapping[field] = [PRIORITY_MAP[match] for match in matches]
else:
str_mapping[field] = [str(match) for match in matches]

log.debug("%s=%r", field, str_mapping[field])
super(FilterRule, self).__init__(str_mapping)

def __str__(self):
return yaml.dump([dict(self)],
indent=2,
default_flow_style=False)

def value_matches(self, field, index, match, value):
return match == value

def matches(self, entry):
for field, matches in self.items():
is_match = False
for index, match in enumerate(matches):
if self.value_matches(field, index, match, entry.get(field)):
is_match = True
break

if not is_match:
return False

return True


class Inclusion(FilterRule):
"""
Filter rule for including entries
"""

def __repr__(self):
return "Inclusion(%s)" % super(Inclusion, self).__repr__()


class Exclusion(FilterRule):
"""
Filter rule for excluding entries
"""

def __init__(self, mapping, comment=None):
super(Exclusion, self).__init__(mapping)

# Make sure everything is interpreted as a string
log.debug("new exclusion rule:")
for field, matches in mapping.items():
log.debug("%s=%r", field, matches)

super(Exclusion, self).__init__(str_mapping)
self.hits = 0
self.regexp = {} # field -> index -> compiled regexp
self.comment = comment

def __repr__(self):
return "Exclusion(%s)" % super(Exclusion, self).__repr__()

def __str__(self):
ret = ''
if self.comment:
ret += '# {0}\n'.format(self.comment)

ret += yaml.dump([dict(self)],
indent=2,
default_flow_style=False)
ret += super(Exclusion, self).__str__()
return ret

def value_matches(self, field, index, match, value):
Expand All @@ -87,20 +130,12 @@ def value_matches(self, field, index, match, value):
return match == value

def matches(self, entry):
for field, matches in self.items():
is_match = False
for index, match in enumerate(matches):
if self.value_matches(field, index, match, entry.get(field)):
is_match = True
log.debug("matched %s[%d]", field, index)
break

if not is_match:
return False
matched = super(Exclusion, self).matches(entry)
if matched:
log.debug("excluding entry")
self.hits += 1

log.debug("excluding entry")
self.hits += 1
return True
return matched


class JournalFilter(Iterator):
Expand Down
29 changes: 28 additions & 1 deletion tests/test_filter.py
Expand Up @@ -20,7 +20,7 @@
from flexmock import flexmock
from io import StringIO
from journal_brief import JournalFilter
from journal_brief.filter import Exclusion
from journal_brief.filter import Inclusion, Exclusion
import logging
from systemd import journal
import yaml
Expand All @@ -29,6 +29,33 @@
logging.basicConfig(level=logging.DEBUG)


class TestInclusion(object):
def test_and(self):
inclusion = Inclusion({'MESSAGE': ['include this'],
'SYSLOG_IDENTIFIER': ['from this']})
assert inclusion.matches({'MESSAGE': 'include this',
'SYSLOG_IDENTIFIER': 'from this',
'IGNORE': 'ignore this'})
assert not inclusion.matches({'MESSAGE': 'include this'})

def test_or(self):
inclusion = Inclusion({'MESSAGE': ['include this', 'or this']})
assert inclusion.matches({'MESSAGE': 'include this',
'IGNORE': 'ignore this'})
assert not inclusion.matches({'MESSAGE': 'not this',
'IGNORE': 'ignore this'})

def test_and_or(self):
inclusion = Inclusion({'MESSAGE': ['include this', 'or this'],
'SYSLOG_IDENTIFIER': ['from this']})
assert inclusion.matches({'MESSAGE': 'include this',
'SYSLOG_IDENTIFIER': 'from this',
'IGNORE': 'ignore this'})
assert not inclusion.matches({'MESSAGE': 'include this',
'SYSLOG_IDENTIFIER': 'at your peril',
'IGNORE': 'ignore this'})


class TestExclusion(object):
def test_and(self):
exclusion = Exclusion({'MESSAGE': ['exclude this'],
Expand Down

0 comments on commit 78678cc

Please sign in to comment.