mirror of
https://github.com/jazzband/django-auditlog.git
synced 2026-03-16 22:20:26 +00:00
Merge pull request #29 from jjkester/threadlocals
Use threadlocals and log remote address
This commit is contained in:
commit
ef4b93a551
5 changed files with 46 additions and 11 deletions
|
|
@ -25,7 +25,8 @@ Actors
|
|||
------
|
||||
|
||||
When using automatic logging, the actor is empty by default. However, auditlog can set the actor from the current
|
||||
request automatically. This does not need any custom code, adding a middleware class is enough.
|
||||
request automatically. This does not need any custom code, adding a middleware class is enough. When an actor is logged
|
||||
the remote address of that actor will be logged as well.
|
||||
|
||||
To enable the automatic logging of the actors, simply add the following to your ``MIDDLEWARE_CLASSES`` setting in your
|
||||
project's configuration file::
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
from __future__ import unicode_literals
|
||||
|
||||
import threading
|
||||
import time
|
||||
|
||||
from django.conf import settings
|
||||
|
|
@ -9,6 +10,9 @@ from django.db.models.loading import get_model
|
|||
from auditlog.models import LogEntry
|
||||
|
||||
|
||||
threadlocal = threading.local()
|
||||
|
||||
|
||||
class AuditlogMiddleware(object):
|
||||
"""
|
||||
Middleware to couple the request's user to log items. This is accomplished by currying the signal receiver with the
|
||||
|
|
@ -20,19 +24,27 @@ class AuditlogMiddleware(object):
|
|||
Gets the current user from the request and prepares and connects a signal receiver with the user already
|
||||
attached to it.
|
||||
"""
|
||||
# Initialize thread local storage
|
||||
threadlocal.auditlog = {
|
||||
'signal_duid': (self.__class__, time.time()),
|
||||
'remote_addr': request.META.get('REMOTE_ADDR'),
|
||||
}
|
||||
|
||||
# In case of proxy, set 'original' address
|
||||
if request.META.get('HTTP_X_FORWARDED_FOR'):
|
||||
threadlocal.auditlog['remote_addr'] = request.META.get('HTTP_X_FORWARDED_FOR').split(',')[0]
|
||||
|
||||
# Connect signal for automatic logging
|
||||
if hasattr(request, 'user') and hasattr(request.user, 'is_authenticated') and request.user.is_authenticated():
|
||||
user = request.user
|
||||
request.auditlog_ts = time.time()
|
||||
set_actor = curry(self.set_actor, user)
|
||||
pre_save.connect(set_actor, sender=LogEntry, dispatch_uid=(self.__class__, request.auditlog_ts), weak=False)
|
||||
set_actor = curry(self.set_actor, request.user)
|
||||
pre_save.connect(set_actor, sender=LogEntry, dispatch_uid=threadlocal.auditlog['signal_duid'], weak=False)
|
||||
|
||||
def process_response(self, request, response):
|
||||
"""
|
||||
Disconnects the signal receiver to prevent it from staying active.
|
||||
"""
|
||||
# Disconnecting the signal receiver is required because it will not be garbage collected (non-weak reference)
|
||||
if hasattr(request, 'auditlog_ts'):
|
||||
pre_save.disconnect(sender=LogEntry, dispatch_uid=(self.__class__, request.auditlog_ts))
|
||||
if hasattr(threadlocal, 'auditlog'):
|
||||
pre_save.disconnect(sender=LogEntry, dispatch_uid=threadlocal.auditlog['signal_duid'])
|
||||
|
||||
return response
|
||||
|
||||
|
|
@ -40,8 +52,8 @@ class AuditlogMiddleware(object):
|
|||
"""
|
||||
Disconnects the signal receiver to prevent it from staying active in case of an exception.
|
||||
"""
|
||||
if hasattr(request, 'auditlog_ts'):
|
||||
pre_save.disconnect(sender=LogEntry, dispatch_uid=(self.__class__, request.auditlog_ts))
|
||||
if hasattr(threadlocal, 'auditlog'):
|
||||
pre_save.disconnect(sender=LogEntry, dispatch_uid=threadlocal.auditlog['signal_duid'])
|
||||
|
||||
return None
|
||||
|
||||
|
|
@ -58,3 +70,5 @@ class AuditlogMiddleware(object):
|
|||
auth_user_model = get_model('auth', 'user')
|
||||
if sender == LogEntry and isinstance(user, auth_user_model) and instance.actor is None:
|
||||
instance.actor = user
|
||||
if hasattr(threadlocal, 'auditlog'):
|
||||
instance.remote_addr = threading.local().auditlog['remote_addr']
|
||||
|
|
|
|||
19
src/auditlog/migrations/0003_logentry_remote_addr.py
Normal file
19
src/auditlog/migrations/0003_logentry_remote_addr.py
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('auditlog', '0002_auto_support_long_primary_keys'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='logentry',
|
||||
name='remote_addr',
|
||||
field=models.GenericIPAddressField(null=True, verbose_name='remote address', blank=True),
|
||||
),
|
||||
]
|
||||
|
|
@ -143,6 +143,7 @@ class LogEntry(models.Model):
|
|||
action = models.PositiveSmallIntegerField(choices=Action.choices, verbose_name=_("action"))
|
||||
changes = models.TextField(blank=True, verbose_name=_("change message"))
|
||||
actor = models.ForeignKey(settings.AUTH_USER_MODEL, blank=True, null=True, on_delete=models.SET_NULL, related_name='+', verbose_name=_("actor"))
|
||||
remote_addr = models.GenericIPAddressField(blank=True, null=True, verbose_name=_("remote address"))
|
||||
timestamp = models.DateTimeField(auto_now_add=True, verbose_name=_("timestamp"))
|
||||
additional_data = JSONField(blank=True, null=True, verbose_name=_("additional data"))
|
||||
|
||||
|
|
|
|||
|
|
@ -19,6 +19,6 @@ MIDDLEWARE_CLASSES = (
|
|||
DATABASES = {
|
||||
'default': {
|
||||
'ENGINE': 'django.db.backends.sqlite3',
|
||||
'NAME': 'auditlog_tests',
|
||||
'NAME': 'auditlog_tests.db',
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue