Fixed #18773 -- Added logging for template variable resolving

Added a django.template logger without a default handler. Added
logging if there is an exception while resolving variables in a
template.
This commit is contained in:
Caroline Simpson 2014-04-14 17:18:03 -04:00 committed by Tim Graham
parent 0c91a419f8
commit dc5b01ad05
6 changed files with 93 additions and 0 deletions

View File

@ -121,6 +121,7 @@ class AdminField(object):
self.field = form[field] # A django.forms.BoundField instance self.field = form[field] # A django.forms.BoundField instance
self.is_first = is_first # Whether this field is first on the line self.is_first = is_first # Whether this field is first on the line
self.is_checkbox = isinstance(self.field.field.widget, forms.CheckboxInput) self.is_checkbox = isinstance(self.field.field.widget, forms.CheckboxInput)
self.is_readonly = False
def label_tag(self): def label_tag(self):
classes = [] classes = []

View File

@ -51,6 +51,7 @@ u'<html></html>'
from __future__ import unicode_literals from __future__ import unicode_literals
import logging
import re import re
import warnings import warnings
from functools import partial from functools import partial
@ -125,6 +126,8 @@ libraries = {}
# global list of libraries to load by default for a new parser # global list of libraries to load by default for a new parser
builtins = [] builtins = []
logger = logging.getLogger('django.template')
class TemplateSyntaxError(Exception): class TemplateSyntaxError(Exception):
pass pass
@ -209,6 +212,7 @@ class Template(object):
try: try:
if context.template is None: if context.template is None:
with context.bind_template(self): with context.bind_template(self):
context.template_name = self.name
return self._render(context) return self._render(context)
else: else:
return self._render(context) return self._render(context)
@ -893,6 +897,9 @@ class Variable(object):
else: else:
raise raise
except Exception as e: except Exception as e:
template_name = getattr(context, 'template_name', 'unknown')
logger.debug('{} - {}'.format(template_name, e))
if getattr(e, 'silent_variable_failure', False): if getattr(e, 'silent_variable_failure', False):
current = context.template.engine.string_if_invalid current = context.template.engine.string_if_invalid
else: else:

View File

@ -141,6 +141,7 @@ class Context(BaseContext):
self._current_app = current_app self._current_app = current_app
self.use_l10n = use_l10n self.use_l10n = use_l10n
self.use_tz = use_tz self.use_tz = use_tz
self.template_name = "unknown"
self.render_context = RenderContext() self.render_context = RenderContext()
# Set to the original template -- as opposed to extended or included # Set to the original template -- as opposed to extended or included
# templates -- during rendering, see bind_template. # templates -- during rendering, see bind_template.

View File

@ -188,6 +188,9 @@ Templates
* Added a :meth:`Context.setdefault() <django.template.Context.setdefault>` * Added a :meth:`Context.setdefault() <django.template.Context.setdefault>`
method. method.
* A warning will now be logged for missing context variables. These messages
will be logged to the :ref:`django.template <django-template-logger>` logger.
Requests and Responses Requests and Responses
^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^

View File

@ -478,6 +478,16 @@ Messages to this logger have the following extra context:
* ``request``: The request object that generated the logging * ``request``: The request object that generated the logging
message. message.
.. _django-template-logger:
``django.template``
~~~~~~~~~~~~~~~~~~~
.. versionadded:: 1.9
Log messages related to the rendering of templates. Missing context variables
are logged as ``DEBUG`` messages if :setting:`DEBUG` is `True`.
.. _django-db-logger: .. _django-db-logger:
``django.db.backends`` ``django.db.backends``

View File

@ -0,0 +1,71 @@
from __future__ import unicode_literals
import logging
from django.template import Template, Variable, VariableDoesNotExist
from django.test import SimpleTestCase
class TestHandler(logging.Handler):
def __init__(self):
super(TestHandler, self).__init__()
self.log_record = None
def emit(self, record):
self.log_record = record
class VariableResolveLoggingTests(SimpleTestCase):
def setUp(self):
self.test_handler = TestHandler()
self.logger = logging.getLogger('django.template')
self.original_level = self.logger.level
self.logger.addHandler(self.test_handler)
self.logger.setLevel(logging.DEBUG)
def tearDown(self):
self.logger.removeHandler(self.test_handler)
self.logger.level = self.original_level
def test_log_on_variable_does_not_exist_silent(self):
class TestObject(object):
class SilentDoesNotExist(Exception):
silent_variable_failure = True
@property
def template_name(self):
return "template"
@property
def template(self):
return Template('')
@property
def article(self):
raise TestObject.SilentDoesNotExist("Attribute does not exist.")
def __iter__(self):
return iter(attr for attr in dir(TestObject) if attr[:2] != "__")
def __getitem__(self, item):
return self.__dict__[item]
Variable('article').resolve(TestObject())
self.assertEqual(
self.test_handler.log_record.msg,
'template - Attribute does not exist.'
)
def test_log_on_variable_does_not_exist_not_silent(self):
with self.assertRaises(VariableDoesNotExist):
Variable('article.author').resolve({'article': {'section': 'News'}})
self.assertEqual(
self.test_handler.log_record.msg,
'unknown - Failed lookup for key [author] in %r' %
("{%r: %r}" % ('section', 'News'), )
)
def test_no_log_when_variable_exists(self):
Variable('article.section').resolve({'article': {'section': 'News'}})
self.assertIsNone(self.test_handler.log_record)