logging: Allowed filters to be just callables.
diff --git a/Lib/logging/__init__.py b/Lib/logging/__init__.py
index 995d313..551d85e 100644
--- a/Lib/logging/__init__.py
+++ b/Lib/logging/__init__.py
@@ -604,10 +604,23 @@
         The default is to allow the record to be logged; any filter can veto
         this and the record is then dropped. Returns a zero value if a record
         is to be dropped, else non-zero.
+
+        .. versionchanged: 3.2
+
+           Allow filters to be just callables.
         """
         rv = 1
         for f in self.filters:
-            if not f.filter(record):
+            if hasattr(f, 'filter'):
+                result = f.filter(record)
+            elif hasattr(f, '__call__'):
+                try:
+                    result = f(record)
+                except Exception:
+                    result = True # filter failed, assume a pass
+            else:
+                result = False # we don't know what f is
+            if not result:
                 rv = 0
                 break
         return rv
diff --git a/Lib/test/test_logging.py b/Lib/test/test_logging.py
index ea2ea2e..a738d7a 100644
--- a/Lib/test/test_logging.py
+++ b/Lib/test/test_logging.py
@@ -309,6 +309,35 @@
         finally:
             handler.removeFilter(filter_)
 
+    def test_callable_filter(self):
+        # Only messages satisfying the specified criteria pass through the
+        #  filter.
+
+        def filterfunc(record):
+            parts = record.name.split('.')
+            prefix = '.'.join(parts[:2])
+            return prefix == 'spam.eggs'
+
+        handler = self.root_logger.handlers[0]
+        try:
+            handler.addFilter(filterfunc)
+            spam = logging.getLogger("spam")
+            spam_eggs = logging.getLogger("spam.eggs")
+            spam_eggs_fish = logging.getLogger("spam.eggs.fish")
+            spam_bakedbeans = logging.getLogger("spam.bakedbeans")
+
+            spam.info(self.next_message())
+            spam_eggs.info(self.next_message())  # Good.
+            spam_eggs_fish.info(self.next_message())  # Good.
+            spam_bakedbeans.info(self.next_message())
+
+            self.assert_log_lines([
+                ('spam.eggs', 'INFO', '2'),
+                ('spam.eggs.fish', 'INFO', '3'),
+            ])
+        finally:
+            handler.removeFilter(filterfunc)
+
 
 #
 #   First, we define our levels. There can be as many as you want - the only