Docs

Django

Classic-Django recipes: a shared client module, one-line track() in views, login auditing via signals, and a feed-token view plus template.

1. Install and initialize

pip install softechlog, set SOFTECHLOG_SECRET_KEY in your environment (or via django-environ), and create one module-level client:

myproject/softechlog.pyPython SDK
# myproject/softechlog.py — one client, imported everywhere
import os
from softechlog import Softechlog

log = Softechlog(secret_key=os.environ["SOFTECHLOG_SECRET_KEY"])

2. Track in views

members/views.pytrack()
# members/views.py
from myproject.softechlog import log

def invite_member(request, workspace_id):
    invitee = do_invite(workspace_id, request.POST)

    # track() never raises — returns None on failure, logs a warning
    log.track(
        actor={"id": str(request.user.pk),
               "name": request.user.get_full_name(),
               "email": request.user.email},
        action="workspace.member.invited",
        target_type="workspace", target_id=str(workspace_id),
        metadata={"invited_email": invitee.email, "role": invitee.role},
    )
    return JsonResponse({"ok": True}, status=201)
Running async views or Django 5 with ASGI? The client also exposes await log.atrack(...) with identical arguments.

3. Audit auth events with signals

Login and failed-login events are the classic audit-trail requirement, and Django already emits signals for them — no view changes needed:

audit/signals.pydjango signals
# audit/signals.py — catch auth events without touching views
from django.contrib.auth.signals import user_logged_in, user_login_failed
from django.dispatch import receiver
from myproject.softechlog import log

@receiver(user_logged_in)
def on_login(sender, request, user, **kwargs):
    log.track(
        actor={"id": str(user.pk), "email": user.email},
        action="auth.login.succeeded",
        context={"ip_address": request.META.get("REMOTE_ADDR"),
                 "user_agent": request.META.get("HTTP_USER_AGENT")},
    )

@receiver(user_login_failed)
def on_login_failed(sender, credentials, request, **kwargs):
    log.track(
        actor={"id": credentials.get("username", "unknown")},
        action="auth.login.failed",
    )

Register the module in your app's AppConfig.ready() as usual.

4. Feed token view + template

activity/views.pyfeed_token()
# activity/views.py — feed token for the signed-in user
from django.contrib.auth.decorators import login_required
from django.http import JsonResponse
from softechlog import SoftechlogError
from myproject.softechlog import log

@login_required
def activity_token(request):
    try:
        ft = log.feed_token(actor_id=str(request.user.pk), ttl_seconds=3600)
    except SoftechlogError:
        return JsonResponse({"error": "token unavailable"}, status=502)
    return JsonResponse({"token": ft.token, "expires_at": ft.expires_at})
activity.htmltemplate
<!-- templates/settings/activity.html -->
<script src="https://softechlog.com/feed.js"></script>

<softechlog-feed id="feed"></softechlog-feed>
<script>
  fetch('{% url "activity_token" %}')
    .then((r) => r.json())
    .then(({ token }) => document.getElementById('feed').setAttribute('feed-token', token));
</script>