Added tests for logging undefined and added it to the docs.
diff --git a/CHANGES b/CHANGES
index 907e1c0..b9606f6 100644
--- a/CHANGES
+++ b/CHANGES
@@ -14,6 +14,8 @@
 - Fixed loop length calculation for some iterators.
 - Changed how Jinja2 enforces strings to be native strings in
   Python 2 to work when people break their default encoding.
+- Added :func:`make_logging_undefined` which returns an undefined
+  object that logs failures into a logger.
 
 Version 2.7.2
 -------------
diff --git a/docs/api.rst b/docs/api.rst
index ae2295f..1fd02c6 100644
--- a/docs/api.rst
+++ b/docs/api.rst
@@ -316,6 +316,11 @@
 
 .. autoclass:: jinja2.StrictUndefined()
 
+There is also a factory function that can decorate undefined objects to
+implement logging on failures:
+
+.. autofunction:: jinja2.make_logging_undefined
+
 Undefined objects are created by calling :attr:`undefined`.
 
 .. admonition:: Implementation
diff --git a/jinja2/__init__.py b/jinja2/__init__.py
index 8434dea..f67fa91 100644
--- a/jinja2/__init__.py
+++ b/jinja2/__init__.py
@@ -42,7 +42,8 @@
      MemcachedBytecodeCache
 
 # undefined types
-from jinja2.runtime import Undefined, DebugUndefined, StrictUndefined
+from jinja2.runtime import Undefined, DebugUndefined, StrictUndefined, \
+     make_logging_undefined
 
 # exceptions
 from jinja2.exceptions import TemplateError, UndefinedError, \
@@ -65,5 +66,5 @@
     'TemplatesNotFound', 'TemplateSyntaxError', 'TemplateAssertionError',
     'ModuleLoader', 'environmentfilter', 'contextfilter', 'Markup', 'escape',
     'environmentfunction', 'contextfunction', 'clear_caches', 'is_undefined',
-    'evalcontextfilter', 'evalcontextfunction'
+    'evalcontextfilter', 'evalcontextfunction', 'make_logging_undefined',
 ]
diff --git a/jinja2/runtime.py b/jinja2/runtime.py
index 65476c4..937e867 100644
--- a/jinja2/runtime.py
+++ b/jinja2/runtime.py
@@ -527,9 +527,24 @@
 
 
 def make_logging_undefined(logger=None, base=None):
-    """Given a logger object this returns a new undefined class that
-    will log all failures into it.  If no logger is given a default
-    logger is created.
+    """Given a logger object this returns a new undefined class that will
+    log certain failures.  It will log iterations and printing.  If no
+    logger is given a default logger is created.
+
+    Example::
+
+        logger = logging.getLogger(__name__)
+        LoggingUndefined = make_logging_undefined(
+            logger=logger,
+            base=Undefined
+        )
+
+    .. versionadded:: 2.8
+
+    :param logger: the logger to use.  If not provided, a default logger
+                   is created.
+    :param base: the base class to add logging functionality to.  This
+                 defaults to :class:`Undefined`.
     """
     if logger is None:
         import logging
@@ -538,23 +553,51 @@
     if base is None:
         base = Undefined
 
-    class LoggingUndefined(base):
-        def __str__(self):
-            if self._undefined_hint is None:
-                if self._undefined_obj is missing:
-                    hint = '%s is undefined' % self._undefined_name
-                elif not isinstance(self._undefined_name, string_types):
-                    hint = '%s has no element %s' % (
-                        object_type_repr(self._undefined_obj),
-                        self._undefined_name)
-                else:
-                    hint = '%s has no attribute %s' % (
-                        object_type_repr(self._undefined_obj),
-                        self._undefined_name)
+    def _log_message(undef):
+        if undef._undefined_hint is None:
+            if undef._undefined_obj is missing:
+                hint = '%s is undefined' % undef._undefined_name
+            elif not isinstance(undef._undefined_name, string_types):
+                hint = '%s has no element %s' % (
+                    object_type_repr(undef._undefined_obj),
+                    undef._undefined_name)
             else:
-                hint = self._undefined_hint
-            logger.error('Template error: %s', hint)
-            return base.__str__(self)
+                hint = '%s has no attribute %s' % (
+                    object_type_repr(undef._undefined_obj),
+                    undef._undefined_name)
+        else:
+            hint = undef._undefined_hint
+        logger.warning('Template variable warning: %s', hint)
+
+    class LoggingUndefined(base):
+
+        def _fail_with_undefined_error(self, *args, **kwargs):
+            try:
+                return base._fail_with_undefined_error(self, *args, **kwargs)
+            except self._undefined_exception as e:
+                logger.error('Template variable error: %s', str(e))
+                raise e
+
+        def __str__(self):
+            rv = base.__str__(self)
+            _log_message(self)
+            return rv
+
+        if PY2:
+            def __unicode__(self):
+                rv = base.__unicode__(self)
+                _log_message(self)
+                return rv
+
+        def __iter__(self):
+            rv = base.__iter__(self)
+            _log_message(self)
+            return rv
+
+        def __nonzero__(self):
+            rv = base.__nonzero__(self)
+            _log_message(self)
+            return rv
 
     return LoggingUndefined
 
diff --git a/jinja2/testsuite/api.py b/jinja2/testsuite/api.py
index 4a0ec5a..ea3739b 100644
--- a/jinja2/testsuite/api.py
+++ b/jinja2/testsuite/api.py
@@ -17,7 +17,7 @@
 
 from jinja2 import Environment, Undefined, DebugUndefined, \
      StrictUndefined, UndefinedError, meta, \
-     is_undefined, Template, DictLoader
+     is_undefined, Template, DictLoader, make_logging_undefined
 from jinja2.utils import Cycler
 
 env = Environment()
@@ -199,6 +199,31 @@
         else:
             assert False, "Expected actual attribute error"
 
+    def test_logging_undefined(self):
+        _messages = []
+        class DebugLogger(object):
+            def warning(self, msg, *args):
+                _messages.append('W:' + msg % args)
+            def error(self, msg, *args):
+                _messages.append('E:' + msg % args)
+
+        logging_undefined = make_logging_undefined(DebugLogger())
+        env = Environment(undefined=logging_undefined)
+        self.assert_equal(env.from_string('{{ missing }}').render(), u'')
+        self.assert_raises(UndefinedError,
+                           env.from_string('{{ missing.attribute }}').render)
+        self.assert_equal(env.from_string('{{ missing|list }}').render(), '[]')
+        self.assert_equal(env.from_string('{{ missing is not defined }}').render(), 'True')
+        self.assert_equal(env.from_string('{{ foo.missing }}').render(foo=42), '')
+        self.assert_equal(env.from_string('{{ not missing }}').render(), 'True')
+        self.assert_equal(_messages, [
+            'W:Template variable warning: missing is undefined',
+            "E:Template variable error: 'missing' is undefined",
+            'W:Template variable warning: missing is undefined',
+            'W:Template variable warning: int object has no attribute missing',
+            'W:Template variable warning: missing is undefined',
+        ])
+
     def test_default_undefined(self):
         env = Environment(undefined=Undefined)
         self.assert_equal(env.from_string('{{ missing }}').render(), u'')