{% extends "base.html" %} {% import "components/macros.html" as ui %} {% block title %}Audit Logging - Documentation{% endblock %} {% block content %}
{% include "docs/sidebar.html" %}
Architecture / Operations

Audit Logging Architecture

A first-class, request-scoped auditing framework built to log and replay critical user mutations (Create, Read, Update, Delete, Search) transparently. Powered by Axum extractors, MongoDB, and JSON payloads.

1. Architectural Philosophy

In enterprise-grade software, logs are not secondary diagnostics. They are an immutable audit trail that ensures accountability and replayability. Every critical event records the state of the entity at the time of modification, who performed it, and their network metadata.

Request-Scoped Context

No manual extraction of IP addresses, headers, or cookies. The system resolves all metadata implicitly via request parts.

State Snapshotting

Snapshots are preserved as raw JSON payloads, making historical state transitions fully auditable and replayable.

Administrator Console

A real-time search interface available to authorized administrators, supporting filtering by user, action, type, and timeline.

2. The AuditLogger Extractor

Rather than cluttering handlers with boilerplate code to parse headers and look up user accounts, developers can leverage the custom Axum AuditLogger extractor. This struct implements Axum's FromRequestParts trait.

Adding logger: AuditLogger to any Axum handler automatically intercepts the request context. Writing a log requires just a single asynchronous call.

use crate::audit::AuditLogger;
use axum::{response::IntoResponse, extract::State};
use serde_json::json;

pub async fn update_task_handler(
    State(repo): State,
    logger: AuditLogger, // <-- Extractor automatically fetches DB, User, IP, UA
    axum::Form(input): axum::Form,
) -> Result {
    // 1. Perform database operation
    let updated_task = repo.update(&input.id, &input).await?;

    // 2. Audit the event (single line, fully request-aware)
    logger.log(
        "Update",                    // action_type
        "Task",                      // entity_type
        Some(updated_task.id),       // entity_id
        Some("Updated task details".into()), // details
        Some(json!(updated_task)),   // payload for replayability
    ).await;

    Ok(Redirect::to("/tasks"))
}

3. Schema Design

Audit logs are persisted in the audit_logs collection in MongoDB. The model captures details on the actor, action, target entity, network details, and the structural payload.

Field Data Type Description
timestamp DateTime<Utc> Chronological timestamp of when the action occurred in UTC.
user_id Option<ObjectId> Reference identifier of the actor. None for guest actions.
username Option<String> Cached username of the user for immediate visual queries.
action_type String The command category (e.g. Create, Update, Delete, View, Login, Search).
entity_type String The resource kind (e.g. Task, User, Developer, Auth).
entity_id Option<ObjectId> The target resource primary key identifier.
details Option<String> Human readable description of the event.
payload Option<serde_json::Value> A snapshot of the affected model state or diff data for complete audit replayability.
ip_address Option<String> Origin client IP address resolved via proxy headers or TCP socket.
user_agent Option<String> Browser details used for authentication forensics.

4. Guidelines for Developers

💡 When to write audit logs?

Audit logs should always be populated during state changes. Whenever writing an endpoint that uses INSERT, UPDATE, or DELETE operations, inject the AuditLogger and record the event after a successful database execution.

🔄 Replayability Principle

The `payload` field is crucial. By storing the JSON serialization of the model BEFORE or AFTER the action, system administrators can reconstruct state history. For deletions, always record the serial snapshot of the deleted model in the payload so it is never permanently lost to audit inquiries.

🗑️ Compliance & Archival Purging

To maintain storage hygiene, administrators can purge logs older than x days (minimum 1 day). To prevent accidental loss of audit history, the system enforces a strict archival download requirement:

  • The purge action queries the records target for deletion and serializes them to JSON format in memory.
  • The database records are deleted, and a new audit log entry recording the purge event details is created to preserve the audit trail chain.
  • The server immediately streams the JSON data as a downloadable attachment, forcing the browser to save the backup locally.
{% endblock %}