Django: logging template errors - django

When I make an error in a django template {{placeholder}}, I get no error, just blank space in the output where I was expecting content. Is there a way to see something in my logs when this occurs, preferably using logging.warning or logging.error?

Yes, there is. Just add in your development settings.py:
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'console': {
'class': 'logging.StreamHandler',
},
},
'loggers': {
'django.template': {
'handlers': ['console'],
'level': os.getenv('DJANGO_LOG_LEVEL', 'DEBUG'),
},
},
}
As roboslone stated, Django 1.9 did introduce it. The snippet is very similar to the second of Configuring logging examples in Django docs.

The only thing Django provides for handling unknown context variables in TEMPLATE_STRING_IF_INVALID. You're going to have to do some deeper hacking of the template engine if you want better than that.

In Django >= 1.8, TEMPLATE_STRING_IF_INVALID has been deprecated in favor of string_if_invalid in settings.TEMPLATES.
If you want to do a little more than depend on DEBUG messages from the django.template logger, you can fool the following code in django.template.base.FilterExpression.render():
if '%s' in string_if_invalid:
return string_if_invalid % self.var
With a class like the following:
class InvalidString(object):
def __mod__(self, other):
log.error('Missing template variable: "%s"', other)
# ... do other interesting things ...
return u''
def __contains__(self, item):
return item == '%s'
And set string_if_invalid in settings.TEMPLATES:
TEMPLATES = [{
'OPTIONS': {'string_if_invalid': InvalidString()}
# ...
}]

Related

Receive django error debug report by email :

Here is my configuration in django settings :
MAILER_LIST = ['toto#toto.com']
EMAIL_HOST = 'toto.smtp.com'
EMAIL_HOST_USER = 'toto#toto.com'
EMAIL_HOST_PASSWORD = 'tata'
EMAIL_PORT = 587
EMAIL_USE_TLS = True
DEFAULT_FROM_EMAIL = 'toto#toto.com'
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'mail_admins': {
'level': 'DEBUG',
'class': 'django.utils.log.AdminEmailHandler',
'filters': [],
}
},
'loggers': {
'django.request': {
'handlers': ['mail_admins'],
'level': 'DEBUG',
'propagate': True,
},
}
}
i've try to debug with :
from django.core.mail import EmailMessage
email = EmailMessage('Hello', 'World', to=['toto#toto.com'])
email.send()
And i get the test email if i put this in my settings.
i would like to receive this error report by email (it's just an example and i've added this error in my code to test the mail report) :
What am i missing to get the debug log by email ? The test is sending the email so it's not an email configuration problem ...
I would like to get the report by email and still show the debug page on django. And get the email event if debug is true or Not.
So i've set DEBUG = True in my settings.
Thanks and regards
As said in another answers if you want use django build-in AdminEmailHandler, then you need provide ADMINS and MANAGERS instead of MAILER_LIST in your settings.py. Like this:
ADMINS = ['toto#toto.com'] # better to use another mail than EMAIL_HOST_USER
MANAGERS = ADMINS
Django's utils.log have two options for processing your DEBUG value: RequireDebugFalse and RequireDebugTrue.
So if you want send error emails to your admins (ADMINS variable in settings.py) while debug, then you may use similar settings:
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'filters': {
'require_debug_true': {
'()': 'django.utils.log.RequireDebugTrue' # log while DEBUG=True
}
},
'handlers': {
'debug_mail_admins': {
'level': 'ERROR',
'class': 'django.utils.log.AdminEmailHandler',
'filters': [require_debug_true],
}
},
'loggers': {
'django.request': {
'handlers': ['debug_mail_admins'],
'level': 'ERROR',
'propagate': True,
},
}
}
Upd.:
Also you can use logging.handlers.SMTPHandler. Then you can write something similar to this code: https://code.djangoproject.com/ticket/15917
Django handles this for you, you can add
MANAGERS = ['mail#mail.com']
or look into integrating sentry for a more robust error reporting, sentry is free too
You should use ADMINS:
ADMINS = ['notifications#example.com']
A list of all the people who get code error notifications. When DEBUG=False and AdminEmailHandler is configured in LOGGING (done by default), Django emails these people the details of exceptions raised in the request/response cycle.
More info here
Other than what has already been said in other answers, do not forget to set SERVER_EMAIL in your settings (docs).
It's the email address that error messages come from; it's similar to DEFAULT_FROM_EMAIL but SERVER_EMAIL is used only for error messages. Default value is 'root#localhost' and if you are using a provider like sendgrid your emails will be blocked.

How to setup django logging to console

I know there is a very similar question. That one is six years old and the answer in that one doesn't help me. All I want is to know how to configure django so that it can log to the console.
This are my settings:
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'console': {
'class': 'logging.StreamHandler',
},
},
'loggers': {
'django': {
'handlers': ['console'],
'level': os.getenv('DJANGO_LOG_LEVEL', 'INFO'),
},
},
}
And in my view I have this:
class Home(TemplateView):
template_name = "inicio/magic_py.html"
def get_context_data(self, **kwargs):
logger = logging.getLogger("django")
logger.debug("home!!!!!!")
print("home?")
The console doesn't show the log.debug, it only shows the print. What am I missing? please help.
I use django 1.10
DEBUG log level is lower than INFO so your logs are being filtered out, you'll need to either lower your log level to DEBUG or you need to log using logger.info() or higher.

Use Logging SocketHandler with Django

I've configured my logging as so:
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'console': {
'class': 'logging.StreamHandler',
},
'cute':{
'class': 'logging.handlers.SocketHandler',
'host': '127.0.0.1',
'port': 19996
},
},
'loggers': {
'django': {
'handlers': ['cute'],
'level': os.getenv('DJANGO_LOG_LEVEL', 'INFO'),
},
},
}
But when I try and log out I get an error in the console:
TypeError: an integer is required (got type socket)
This seems to be happening within an attempt to pickle the log message, I think.
What is going on, and how can I get the SocketHandler to work?
There is a bug report about it. The request object cannot be pickled and the log fails.
My loggers were like yours before I got the same error as you did. Since my code contains apps that don't work with request, I partially fixed my problem creating a log for django.request without socket_handler
'django.request': {
'handlers': ['defaultfile', 'console'],
'level': 'WARNING',
'propagate': False,
},
However the bug report also suggest to create a custom SocketHandler removing request:
from logging.handlers import SocketHandler as _SocketHandler
class DjangoSocketHandler(_SocketHandler):
def emit(self, record):
if hasattr(record, 'request'):
record.request = None
return super().emit(record)
I haven't tried this yet, but it could be a way to go.

django.request logger not working for get_object_or_404

I have this code for UserDetailsView in Django Rest Framework. I am using django 1.9 and DRF 3.
class UserDetailsView(RetrieveUpdateAPIView):
"""
API endpoint that allows a user to be viewed or edited.
"""
serializer_class = UserDetailsSerializer
permission_classes = (IsAuthenticated,)
def get_object(self):
pk = self.kwargs.get('pk', None)
if not pk or (pk == str(self.request.user.pk)):
return self.request.user
else:
try:
return get_object_or_404(User, id=pk)
except ValueError:
return get_object_or_404(User, username=pk)
I have have my django logger configured as per these settings.
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'simple': {
'format': '[%(levelname)s] %(message)s'
},
},
'handlers': {
'default': {
'level': 'DEBUG',
'class': 'logging.handlers.RotatingFileHandler',
'filename': 'logs/backend_common.log',
'maxBytes': 1024*1024*10,
'backupCount': 10,
'formatter': 'simple',
},
'request_handler': {
'level': 'DEBUG',
'class': 'logging.handlers.RotatingFileHandler',
'filename': 'logs/backend_requests.log',
'maxBytes': 1024*1024*10,
'backupCount': 10,
'formatter': 'simple',
},
'mail_admins': {
'level': 'ERROR',
'class': 'django.utils.log.AdminEmailHandler',
}
},
'loggers': {
'': {
'handlers': ['default'],
'propagate': True,
},
'django.request': {
'handlers': ['request_handler', 'mail_admins'],
'level': 'DEBUG',
'propagate': False,
}
}
}
Now, all django 4xx, 5xx error status code should ideally be logged into backend_requests.log file and they are except 404 status resulting from get_object_or_404. I think 404 errors resulting from this view should also get logged. Any help is highly appreciated.
There are actually two issues here.
The first is a bug in Django, which was fixed just a few days ago. The bit of code that was logging 404s was running a little too early. The next release of Django will work and your 404s will be logged as you would expect.
For other exceptions however, the problem is as follows. Django will log errors if the exceptions that caused them are bubbled up to the core request handler. However if the view or some middleware catches the exception and handles it, then this portion of Django's code never gets called. This is what is happening with DRF:
REST framework's views handle various exceptions, and deal with returning appropriate error responses.
The handled exceptions are:
Subclasses of APIException raised inside REST framework.
Django's Http404 exception.
Django's PermissionDenied exception.
In each case, REST framework will return a response with an appropriate status code and content-type. The body of the response will include any additional details regarding the nature of the error.
Because DRF catches the exception and returns a response object to Django, Django will just render the response and not log the error. This makes sense - it is possible for some middleware to take what was originally a 404 but return a different response instead (the flatpage middleware is a good example).
If you want to log exceptions handled by DRF you can specify your own EXCEPTION_HANDLER in the DRF config, or write your own middleware that logs the errors.
Note that 5xx exceptions are not handled by DRF and these should still propagate up to Django. These should be getting logged for you even now.

Manually fire Django 1.3's traceback/exception log

I'm using djutils's async decorator, which has the nasty side effect of not sending traceback emails when an exception is raised, since it runs on a separate thread.
It does, however, have the following place to put a logger.
def worker_thread():
while 1:
func, args, kwargs = queue.get()
try:
func(*args, **kwargs)
except:
pass # <-- log error here
finally:
queue.task_done()
I've confirmed this will work, but even with the try/except removed, it won't trip Django's traceback logger.
While it'd be pretty easy to tell it to write to a db/file on exception, I'd really like it to send a regular traceback as defined in settings. How can I do that?
Edit: answer seems to involve django.utils.log.AdminEmailHandler - but I'm having a hard time finding an example.
Edit 2: Here's my current (99% likely to be wrong) attempt.
from django.utils.log import AdminEmailHandler
def worker_thread():
while 1:
func, args, kwargs = queue.get()
try:
func(*args, **kwargs)
except:
import logging
from django.conf import settings
print settings.EMAIL_HOST
logger = logging.getLogger("async.logger")
logger.exception("Async exploded")
AdminEmailHandler
pass # <-- log error here
finally:
queue.task_done()
first, configure your logging settings i settings.py:
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'mail_admins': {
'level': 'ERROR',
'class': 'django.utils.log.AdminEmailHandler'
}
},
'loggers': {
'myproject': {
'handlers': ['mail_admins'],
'level': 'INFO',
'propagate': True,
},
'django.request': {
'handlers': ['mail_admins'],
'level': 'ERROR',
'propagate': True,
},
}
}
from now, all loggers which starts with 'myproject' should use AdminEmailHandler
your code should look like this:
import logging
logger = logging.getLogger('myproject.optional.path')
# example
# logger = logging.getLogger('myprojects.myapp.views')
def worker_thread():
while 1:
func, args, kwargs = queue.get()
try:
func(*args, **kwargs)
except:
logger.exception("Async exploded")
finally:
queue.task_done()