Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Merge pull request #6 from mozilla-services/ra/filtering

Ra/filtering
  • Loading branch information...
commit b18a2f224de577cd4baf2082f5d371c754f8f1da 2 parents 2ed7cd1 + d1c5c91
Victor Ng authored
View
2  docs/conf.py
@@ -43,7 +43,7 @@
# General information about the project.
project = u'metlog-py'
-copyright = u'2012, Rob Miller'
+copyright = u'2012, Mozilla Services'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
View
27 metlog/client.py
@@ -136,23 +136,30 @@ class MetlogClient(object):
env_version = '0.8'
def __init__(self, sender=None, logger='', severity=6,
- disabled_timers=None):
+ disabled_timers=None, filters=None):
"""
:param sender: A sender object used for actual message delivery.
:param logger: Default `logger` value for all sent messages.
:param severity: Default `severity` value for all sent messages.
:param disabled_timers: Sequence of string tokens identifying timers
that should be deactivated.
+ :param filters: A sequence of 2-tuples, each containing a filter
+ callable and the config dict to pass in to the callable
+ on each invocation.
"""
- self.setup(sender, logger, severity, disabled_timers)
+ self.setup(sender, logger, severity, disabled_timers, filters)
- def setup(self, sender=None, logger='', severity=6, disabled_timers=None):
+ def setup(self, sender=None, logger='', severity=6, disabled_timers=None,
+ filters=None):
"""
:param sender: A sender object used for actual message delivery.
:param logger: Default `logger` value for all sent messages.
:param severity: Default `severity` value for all sent messages.
:param disabled_timers: Sequence of string tokens identifying timers
that should be deactivated.
+ :param filters: A sequence of 2-tuples, each containing a filter
+ callable and the config dict to pass in to the callable
+ on each invocation.
"""
if sender is None:
sender = NoSendSender()
@@ -164,10 +171,18 @@ def setup(self, sender=None, logger='', severity=6, disabled_timers=None):
self._disabled_timers = set()
else:
self._disabled_timers = set(disabled_timers)
+ if filters is None:
+ filters = list()
+ self.filters = filters
def send_message(self, msg):
- # Just a handy shortcut so that proxies don't have to talk to
- # the sender attribute
+ """
+ Apply any filters and, if required, pass message along to the sender
+ for delivery.
+ """
+ for filter_fn, config in self.filters:
+ if not filter_fn(self, config, msg):
+ return
self.sender.send_message(msg)
def add_method(self, name, method):
@@ -222,7 +237,7 @@ def metlog(self, type, timestamp=None, logger=None, severity=None,
full_msg = dict(type=type, timestamp=timestamp, logger=logger,
severity=severity, payload=payload, fields=fields,
env_version=self.env_version)
- self.sender.send_message(full_msg)
+ self.send_message(full_msg)
def timing(self, timer, elapsed):
"""
View
64 metlog/filters.py
@@ -0,0 +1,64 @@
+# ***** BEGIN LICENSE BLOCK *****
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this file,
+# You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+# The Initial Developer of the Original Code is the Mozilla Foundation.
+# Portions created by the Initial Developer are Copyright (C) 2012
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+# Rob Miller (rmiller@mozilla.com)
+#
+# ***** END LICENSE BLOCK *****
+"""
+Callables useful as message filters for the MetlogClient. All filters
+should accept three arguments:
+
+:param client: MetlogClient instance
+:param config: Dictionary containing any necessary filter configuration
+ info
+:param msg: Message dictionary
+
+All filters should return a boolean value, True if a message *should* be
+delivered, False if a message *should not* be delivered. Note that the `msg`
+dictionary *may* be mutated by the filter.
+"""
+
+
+def severity_max(client, config, msg):
+ """
+ Filter if message severity is greater than config's `severity` value.
+ """
+ if msg['severity'] > config['severity']:
+ return False
+ return True
+
+
+def type_blacklist(client, config, msg):
+ """
+ Filter if message type is in the config's `types` value.
+ """
+ if msg['type'] in config['types']:
+ return False
+ return True
+
+
+def type_whitelist(client, config, msg):
+ """
+ Filter if message type is NOT in the config's `types` value.
+ """
+ if msg['type'] not in config['types']:
+ return False
+ return True
+
+
+def type_severity_max(client, config, msg):
+ """
+ Filter if message type has specified maximum severity value and message
+ severity is higher than this maximum.
+ """
+ type_spec = config['types'].get(msg['type'])
+ if type_spec is None:
+ return True
+ return severity_max(client, type_spec, msg)
View
93 metlog/tests/test_filters.py
@@ -0,0 +1,93 @@
+# ***** BEGIN LICENSE BLOCK *****
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this file,
+# You can obtain one at http://mozilla.org/MPL/2.0/.
+
+# The Initial Developer of the Original Code is the Mozilla Foundation.
+# Portions created by the Initial Developer are Copyright (C) 2012
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+# Rob Miller (rmiller@mozilla.com)
+#
+# ***** END LICENSE BLOCK *****
+from metlog.client import MetlogClient
+from metlog.client import SEVERITY
+from metlog.senders import DebugCaptureSender
+from nose.tools import eq_, ok_
+import json
+import random
+import threading
+
+
+class TestMetlogClientFilters(object):
+ logger = 'tests'
+
+ def setUp(self):
+ self.sender = DebugCaptureSender()
+ self.client = MetlogClient(self.sender, self.logger)
+ # overwrite the class-wide threadlocal w/ an instance one
+ # so values won't persist btn tests
+ self.client.timer._local = threading.local()
+
+ def tearDown(self):
+ del self.sender
+ del self.client
+
+ def test_severity_max(self):
+ from metlog.filters import severity_max
+ self.client.filters = [(severity_max, {'severity': SEVERITY.ERROR})]
+ payload = 'foo'
+ self.client.debug(payload)
+ self.client.info(payload)
+ self.client.warn(payload)
+ self.client.error(payload)
+ self.client.exception(payload)
+ self.client.critical(payload)
+ # only half of the messages should have gone out
+ eq_(len(self.sender.msgs), 3)
+ # make sure it's the right half
+ for json_msg in self.sender.msgs:
+ msg = json.loads(json_msg)
+ ok_(msg['severity'] <= SEVERITY.ERROR)
+
+ def test_type_blacklist(self):
+ from metlog.filters import type_blacklist
+ self.client.filters = [(type_blacklist, {'types': set(['foo'])})]
+ choices = ['foo', 'bar']
+ notfoos = 0
+ for i in range(10):
+ choice = random.choice(choices)
+ if choice != 'foo':
+ notfoos += 1
+ self.client.metlog(choice, payload='msg')
+ eq_(len(self.sender.msgs), notfoos)
+
+ def test_type_whitelist(self):
+ from metlog.filters import type_whitelist
+ self.client.filters = [(type_whitelist, {'types': set(['foo'])})]
+ choices = ['foo', 'bar']
+ foos = 0
+ for i in range(10):
+ choice = random.choice(choices)
+ if choice == 'foo':
+ foos += 1
+ self.client.metlog(choice, payload='msg')
+ eq_(len(self.sender.msgs), foos)
+
+ def test_type_severity_max(self):
+ from metlog.filters import type_severity_max
+ config = {'types': {'foo': {'severity': 3},
+ 'bar': {'severity': 5},
+ },
+ }
+ self.client.filters = [(type_severity_max, config)]
+ for msgtype in ['foo', 'bar']:
+ for sev in range(8):
+ self.client.metlog(msgtype, severity=sev, payload='msg')
+ eq_(len(self.sender.msgs), 10)
+ msgs = [json.loads(msg) for msg in self.sender.msgs]
+ foos = [msg for msg in msgs if msg['type'] == 'foo']
+ eq_(len(foos), 4)
+ bars = [msg for msg in msgs if msg['type'] == 'bar']
+ eq_(len(bars), 6)
Please sign in to comment.
Something went wrong with that request. Please try again.