Skip to content

Instantly share code, notes, and snippets.

@countermeasure
Created April 14, 2016 07:40
Show Gist options
  • Save countermeasure/3aa795b70931797789a7dc85f6858db0 to your computer and use it in GitHub Desktop.
Save countermeasure/3aa795b70931797789a7dc85f6858db0 to your computer and use it in GitHub Desktop.
Django logging
# Add this to your common app in a file called `backends.py`
from logging import getLogger
from django.core.mail.backends.filebased import EmailBackend as FBEmailBackend
from django.core.mail.backends.smtp import EmailBackend as SmtpEmailBackend
class LoggingFileBasedEmailBackend(FBEmailBackend):
"""A wrapper around ``filebased.EmailBackend`` that logs every email.
The ``write_message`` function is is duplicate of the matching function in
``filebased.EmailBackend``, but with some logging added."""
def __init__(self, *args, **kwargs):
super(LoggingFileBasedEmailBackend, self).__init__(*args, **kwargs)
self.logger = getLogger('django.mail')
def write_message(self, message):
self.stream.write(message.message().as_bytes() + b'\n')
self.stream.write(b'-' * 79)
self.stream.write(b'\n')
log_entry = 'Recipient: %s | Subject: \'%s\' | Status: Sent' % (
'; '.join(message.recipients()),
message.subject,
)
self.logger.info(log_entry)
class LoggingSmtpEmailBackend(SmtpEmailBackend):
"""A wrapper around ``smtp.EmailBackend`` that logs every email. The
``send_messages`` function is a duplicate of the matching function in
``smtp.EmailBackend``, but with some logging added."""
def __init__(self, *args, **kwargs):
super(LoggingSmtpEmailBackend, self).__init__(*args, **kwargs)
self.logger = getLogger('django.mail')
def send_messages(self, email_messages):
if not email_messages:
return
with self._lock:
new_conn_created = self.open()
if not self.connection:
# We failed silently on open().
# Trying to send would be pointless.
return
num_sent = 0
for message in email_messages:
sent = self._send(message)
log_entry = 'Recipient: %s | Subject: \'%s\'' % (
'; '.join(message.recipients()),
message.subject,
)
if sent:
num_sent += 1
log_entry += ' | Status: Sent'
self.logger.info(log_entry)
else:
log_entry += ' | Status: Failed to send'
self.logger.warn(log_entry)
if new_conn_created:
self.close()
return num_sent
# Add this to your authentication app in a file called `loggers.py`
from logging import getLogger
logger = getLogger('django.events')
def log_successful_login(user):
"""Logs a successful login."""
logger.info(
'User \'%s\' (id: %s) logged in.' % (
user.get_full_name(),
user.pk,
)
)
def log_failed_login_attempt(subdomain, email):
"""Logs a failed attempt to login."""
logger.warning(
'Failed login attempt with email \'%s\'.' % email
)
# Add this to your common app in a file called `middleware.py`
import json
from logging import getLogger
class LoggingMiddleware(object):
"""Middleware to log every request."""
def __init__(self):
self.logger = getLogger('django.all_requests')
def process_string(self, key, obj):
"""Accepts a key and an object.
If the object is a string longer than 100 characters, truncates it.
If the object looks like a password or is a CSRF token, omits it."""
if isinstance(obj, basestring):
if (
'password' in key.lower() or
key.lower() == 'csrfmiddlewaretoken'
):
return '**omitted**'
else:
return '%s...' % obj[:100] if len(obj) > 100 else obj
else:
return object
def process_request(self, request):
log_entry = 'User: %s -- %s -- %s' % (
request.user.pk,
request.method,
request.build_absolute_uri(),
)
# Include data from POST or PUT requests in the log entry.
if request.method in ('POST', 'PUT'):
log_entry += ' -- '
# Handle form data.
if request.POST:
request_string = '{'
for key, value in request.POST.iteritems():
request_string += (
'%s: \'%s\', ' % (key, self.process_string(key, value))
)
request_string = '%s%s' % (request_string[0: -2], '}')
log_entry += request_string
# Handle json data from the API.
else:
api_data = json.loads(request.body)
for key, value in api_data.iteritems():
api_data[key] = self.process_string(key, value)
log_entry += json.dumps(api_data)
self.logger.info(log_entry)
return None
# Add this to your `settings.py`
# Logging
# https://docs.djangoproject.com/en/1.9/topics/logging/
if ENVIRONMENT == 'development':
LOG_DIR = os.path.join(BASE_DIR, 'media', 'logs/')
else:
LOG_DIR = '/home/econvenor/logs_django/'
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'basic': {
'format': '[%(asctime)s] -- %(message)s',
},
'events': {
'format': '[%(asctime)s] %(levelname)s -- %(message)s',
},
},
'handlers': {
'all_requests': {
'level': 'INFO',
'class': 'logging.FileHandler',
'filename': os.path.join(LOG_DIR, 'all_requests.log'),
'formatter': 'basic',
},
'events': {
'level': 'INFO',
'class': 'logging.FileHandler',
'filename': os.path.join(LOG_DIR, 'events.log'),
'formatter': 'events',
},
'mail': {
'level': 'INFO',
'class': 'logging.FileHandler',
'filename': os.path.join(LOG_DIR, 'mail.log'),
'formatter': 'events',
},
},
'loggers': {
'django.all_requests': {
'handlers': ['all_requests'],
'level': 'INFO',
'propagate': True,
},
'django.events': {
'handlers': ['events'],
'level': 'INFO',
'propagate': True,
},
'django.mail': {
'handlers': ['mail'],
'level': 'INFO',
'propagate': True,
},
},
}
# Email settings
if ENVIRONMENT == 'development':
EMAIL_BACKEND = 'common.backends.LoggingFileBasedEmailBackend'
else:
EMAIL_BACKEND = 'common.backends.LoggingSmtpEmailBackend'
# Add this to `views.py` in your authentication app
from django.contrib.auth import (
authenticate,
login,
)
from django.shortcuts import (
redirect,
render,
)
from .loggers import (
log_failed_login_attempt,
log_successful_login,
)
def user_login(request):
"""
Logs a user in.
"""
if request.method == 'POST':
email = request.POST['email'].lower()
password = request.POST['password']
user = authenticate(email=email, password=password)
if user is not None:
if user.is_active:
login(request, user)
log_successful_login(user)
return redirect('accounts-dash')
else:
error = 'Your account is not active.'
else:
log_failed_login_attempt(email)
error = 'The credentials you entered are not valid. Try again.'
return render(request, 'user_login.html', {'error': error})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment