I am following this tutorial ("With a logging filter that raises exceptions") to catch all invalid variable names in Django templates.
myproject/mysite/logging.py:
import loggingclass MissingVariableError(Exception):""" A variable was missing from a template. Used as an alternative to django.template.base.VariableDoesNotExist, because that exception has some meaning within the template engine."""class MissingVariableErrorFilter(logging.Filter):""" Take log messages from Django for missing template variables and turn them into exceptions.""" ignored_prefixes = ("admin/", ) def filter(self, record): if record.msg.startswith("Exception while resolving variable "): variable_name, template_name = record.args if not template_name.startswith(self.ignored_prefixes): raise MissingVariableError( f"{variable_name!r} missing in {template_name!r}" ) from None return False
myproject/settings.py:
LOGGING = {"version": 1,"disable_existing_loggers": False,"filters": {"missing_variable_error": {"()": "www.logging.MissingVariableErrorFilter", }, },"handlers": {"console": {"class": "logging.StreamHandler", }, },"root": {"handlers": ["console"],"level": "WARNING", },"loggers": {"django": {"handlers": ["console"],"level": 'INFO',"propagate": False, },"django.template": {"level": "DEBUG","filters": ["missing_variable_error"], }, },}
When I load the main page I get this in the log
Traceback (most recent call last):... File "/install_dir/site/myproject/mysite/logging.py", line 27, in filter raise MissingVariableError(www.logging.MissingVariableError: 'exception_notes' missing in 'unknown'
Problem 1: exception_notes
does not appear in my site (see note below)
Problem 2: this stack trace is very large and noisy.
Also, the site serves an unfriendly error (this is actually what I do want, sometimes, in debug mode):
#> curl localhostA server error occurred. Please contact the administrator.
(This exeception_notes
seems to be from /python3.10/site-packages/django/views/templates/technical_500.html
or /lib/python3.10/site-packages/django/views/templates/technical_500.txt
rather than my own files, but that's not really the issue -- I can filter using if 'site-packages/django' in record.pathname
although that's a bit hacky.)
I can get better results if I change MissingVariableErrorFilter.filter
to simply
def filter (self, record): return True
which according to this
Is the specified record to be logged? Returns zero for no, nonzero for yes. If deemed appropriate, the record may be modified in-place by this method.
sounds like what should be happening anyway, but doesn't. With this, the site loads as normal (with the missing template variables rendering as empty) but there is this in the log
www_1 | Exception while resolving variable 'form' in template 'index.html'.www_1 | Traceback (most recent call last):www_1 | File "/django-template-venv/lib/python3.10/site-packages/django/template/base.py", line 880, in _resolve_lookupwww_1 | current = current[bit]www_1 | File "/django-template-venv/lib/python3.10/site-packages/django/template/context.py", line 83, in __getitem__www_1 | raise KeyError(key)www_1 | KeyError: 'form'www_1 | www_1 | During handling of the above exception, another exception occurred:www_1 | www_1 | Traceback (most recent call last):www_1 | File "/django-template-venv/lib/python3.10/site-packages/django/template/base.py", line 886, in _resolve_lookupwww_1 | if isinstance(current, BaseContext) and getattr(www_1 | AttributeError: type object 'RequestContext' has no attribute 'form'www_1 | www_1 | During handling of the above exception, another exception occurred:www_1 | www_1 | Traceback (most recent call last):www_1 | File "/django-template-venv/lib/python3.10/site-packages/django/template/base.py", line 896, in _resolve_lookupwww_1 | current = current[int(bit)]www_1 | ValueError: invalid literal for int() with base 10: 'form'www_1 | www_1 | During handling of the above exception, another exception occurred:www_1 | www_1 | Traceback (most recent call last):www_1 | File "/django-template-venv/lib/python3.10/site-packages/django/template/base.py", line 903, in _resolve_lookupwww_1 | raise VariableDoesNotExist(www_1 | django.template.base.VariableDoesNotExist: Failed lookup for key [form] in [{'True': True, 'False': False, 'None': None}, {'csrf_token': <SimpleLazyObject: 'tuuBGDK1DEzWyNikznwXo85aK8gtQqtRE1knF43l2oZQsGk0CLBMvmewePqvEoBq'>, 'request': <WSGIRequest: GET '/'>, 'user': <SimpleLazyObject: <django.contrib.auth.models.AnonymousUser object at 0x7f2cd093e8f0>>, 'perms': PermWrapper(<SimpleLazyObject: <django.contrib.auth.models.AnonymousUser object at 0x7f2cd093e8f0>>), 'messages': <FallbackStorage: request=<WSGIRequest: GET '/'>>, 'DEFAULT_MESSAGE_LEVELS': {'DEBUG': 10, 'INFO': 20, 'SUCCESS': 25, 'WARNING': 30, 'ERROR': 40}, 'debug': True}, {}, {'request': <WSGIRequest: GET '/'>}, {'user': <SimpleLazyObject: <django.contrib.auth.models.AnonymousUser object at 0x7f2cd093e8f0>>}]
Problem 3: this is better, but I only really need the first (Exception while resolving variable 'form' in template 'index.html'.
) and last (django.template.base.VariableDoesNotExist: Failed lookup for key [form] in ...
) lines.
Having a filter
that simply does return True
sounds like it should be redundant, but if I remove django.template
from LOGGERS
LOGGING = {"version": 1,"disable_existing_loggers": False,"handlers": {"console": {"class": "logging.StreamHandler", }, },"root": {"handlers": ["console"],"level": "WARNING", },"loggers": {"django": {"handlers": ["console"],"level": log_level,"propagate": False, }, },}
Then I get nothing in the log about the missing template variable at all.
How do I set up logging to:
- print the name and location of missing template variables (e.g. "foo on index.html line 123")
- also print the context given to the template
- optionally (e.g. in debug mode) raise an exception which makes the page load obviously fail
?