Chaturmind
LearnDSASystem DesignDevOpsEngineering GrowthBlog
Start learning
Chaturmind

Structured learning paths for engineers who want to go deep. Written by practitioners.

Learn

  • Java
  • DSA
  • System Design
  • Spring Boot
  • AI / ML
  • DevOps
  • Engineering Growth

Company

  • Blog
  • Contact

Legal

  • Privacy Policy
  • Terms of Service

© 2026 Chaturmind. All rights reserved.

Built for engineers who want to go deep.


← Low-Level Design & Design Patterns

OOP Fundamentals & SOLID Principles

  • Object-Oriented Design Refresher
  • Single Responsibility & Open/Closed
  • Liskov Substitution & Interface Segregation
  • Dependency Inversion

Creational Design Patterns

  • Singleton Pattern
  • Factory & Abstract Factory
  • Builder Pattern
  • Prototype Pattern

Structural Design Patterns

  • Adapter & Facade
  • Decorator Pattern
  • Composite & Proxy
  • Bridge & Flyweight

Behavioral Design Patterns

  • Strategy Pattern
  • Observer Pattern
  • Command Pattern
  • Template Method
  • State Pattern
  • Chain of Responsibility
  • Iterator & Mediator
  • Visitor Pattern

Machine Coding Exercises

  • Parking Lot — Requirements & Class Design
  • Parking Lot — Implementation
  • Elevator System — Requirements & Class Design
  • Elevator System — Implementation & Scheduling
  • Library Management System
  • Tic-Tac-Toe / Board Game Design
  • Chess Engine Design
  • Vending Machine Design
  • ATM System Design
  • In-Memory Rate Limiter
  • Thread-Safe LRU Cache
  • Connection Pool Design
  • Producer-Consumer Class Design
  • Movie Ticket Booking System
  • Hotel Reservation System
  • Airline Booking / Seat Selection System
  • Splitwise / Expense Sharing System
  • Design a Logging Framework
  • Design a Notification/Observer-Based Pub-Sub
  • Design a Cache with Pluggable Eviction Policy
  • Design Snake and Ladder
  • Design a Card Game Framework
  • Design an Inventory Management System
  • Restaurant Management System
  • Food Delivery Order Matching
  • Design a Shopping Cart & Checkout Flow
  • Design a Recommendation Engine
  • Design a Job Scheduler
  • Design a Circuit Breaker
  • Design a Distributed ID Generator
  • Design a Pub-Sub Message Broker
  • Design Twitter/X
  • Multi-Pattern Problem: Design a Ride Booking Class Model
  • Multi-Pattern Problem: Design a Food Ordering Class Model
  • Design an Authentication/Authorization Module
  • Design a Form/Survey Builder
  • Design a Meeting Room / Calendar Booking System
  • Design a Search/Filter Engine for an E-Commerce Catalog
  • Design a Voting/Polling System
  • Design a Retry Mechanism with Backoff
  • Design a Health Check Aggregator
  • Design a Feature Flag System
  • Design a Config Management Client
  • Design a Leader Election Algorithm
  • Design a Distributed Counter
  • Design a Bloom Filter
  • Design a Consistent Hashing Ring
  • Design a Distributed Tracing Library
  • Design a Metrics Collection Library
  • Design an Audit Logging Framework
  • Design a Plugin/Extension System
  • Design a Workflow Engine
  • Multi-Pattern Capstone: Design a Food Delivery App
Chaturmind
← Low-Level Design & Design Patterns

OOP Fundamentals & SOLID Principles

  • Object-Oriented Design Refresher
  • Single Responsibility & Open/Closed
  • Liskov Substitution & Interface Segregation
  • Dependency Inversion

Creational Design Patterns

  • Singleton Pattern
  • Factory & Abstract Factory
  • Builder Pattern
  • Prototype Pattern

Structural Design Patterns

  • Adapter & Facade
  • Decorator Pattern
  • Composite & Proxy
  • Bridge & Flyweight

Behavioral Design Patterns

  • Strategy Pattern
  • Observer Pattern
  • Command Pattern
  • Template Method
  • State Pattern
  • Chain of Responsibility
  • Iterator & Mediator
  • Visitor Pattern

Machine Coding Exercises

  • Parking Lot — Requirements & Class Design
  • Parking Lot — Implementation
  • Elevator System — Requirements & Class Design
  • Elevator System — Implementation & Scheduling
  • Library Management System
  • Tic-Tac-Toe / Board Game Design
  • Chess Engine Design
  • Vending Machine Design
  • ATM System Design
  • In-Memory Rate Limiter
  • Thread-Safe LRU Cache
  • Connection Pool Design
  • Producer-Consumer Class Design
  • Movie Ticket Booking System
  • Hotel Reservation System
  • Airline Booking / Seat Selection System
  • Splitwise / Expense Sharing System
  • Design a Logging Framework
  • Design a Notification/Observer-Based Pub-Sub
  • Design a Cache with Pluggable Eviction Policy
  • Design Snake and Ladder
  • Design a Card Game Framework
  • Design an Inventory Management System
  • Restaurant Management System
  • Food Delivery Order Matching
  • Design a Shopping Cart & Checkout Flow
  • Design a Recommendation Engine
  • Design a Job Scheduler
  • Design a Circuit Breaker
  • Design a Distributed ID Generator
  • Design a Pub-Sub Message Broker
  • Design Twitter/X
  • Multi-Pattern Problem: Design a Ride Booking Class Model
  • Multi-Pattern Problem: Design a Food Ordering Class Model
  • Design an Authentication/Authorization Module
  • Design a Form/Survey Builder
  • Design a Meeting Room / Calendar Booking System
  • Design a Search/Filter Engine for an E-Commerce Catalog
  • Design a Voting/Polling System
  • Design a Retry Mechanism with Backoff
  • Design a Health Check Aggregator
  • Design a Feature Flag System
  • Design a Config Management Client
  • Design a Leader Election Algorithm
  • Design a Distributed Counter
  • Design a Bloom Filter
  • Design a Consistent Hashing Ring
  • Design a Distributed Tracing Library
  • Design a Metrics Collection Library
  • Design an Audit Logging Framework
  • Design a Plugin/Extension System
  • Design a Workflow Engine
  • Multi-Pattern Capstone: Design a Food Delivery App
HomeLearnSystem DesignLow-Level Design & Design PatternsMachine Coding Exercises
✓ FreeIntermediate· 7 min read

Design an Audit Logging Framework

AuditEvent, AuditLogger, and AuditEventListener using the Observer pattern so multiple sinks (DB, file, external SIEM) consume the same audit stream, and how the class design enforces immutability — distinct from a general-purpose logging framework.

Published September 23, 2026


Design an Audit Logging Framework

This is a distinct exercise from Design a Logging Framework — that one covers GENERAL-PURPOSE application logging (log levels, appenders, formatters, routing by severity). An audit log serves a fundamentally different purpose: an immutable, trustworthy record of WHO did WHAT and WHEN, for compliance and dispute resolution — closer in spirit to Payment — Requirements' auditability point than to debug/info log output.

Core classes

final class AuditEvent { // final, and every field final — see immutability below
    final String actorId; final String action; final String resourceId;
    final Instant timestamp; final Map<String, String> metadata;

    AuditEvent(String actorId, String action, String resourceId, Map<String, String> metadata) {
        this.actorId = actorId; this.action = action; this.resourceId = resourceId;
        this.timestamp = Instant.now(); this.metadata = Map.copyOf(metadata); // defensive, immutable copy
    }
}

interface AuditEventListener { void onEvent(AuditEvent event); }

class AuditLogger {
    List<AuditEventListener> listeners = new CopyOnWriteArrayList<>();
    void registerListener(AuditEventListener listener) { listeners.add(listener); }
    void log(AuditEvent event) { listeners.forEach(l -> l.onEvent(event)); }
}

Observer pattern: multiple independent sinks, one event stream

class DatabaseSink implements AuditEventListener {
    public void onEvent(AuditEvent event) { repository.save(event); } // durable, queryable record
}
class SiemSink implements AuditEventListener {
    public void onEvent(AuditEvent event) { siemClient.forward(event); } // external security monitoring system
}

A single audit event commonly needs to reach MULTIPLE independent destinations simultaneously — a durable database record for internal compliance queries, AND a forward to an external SIEM (Security Information and Event Management) system for security monitoring, potentially also a file for a separate archival requirement. The Observer pattern (identical in shape to Design a Config Management Client's listener registration) is what lets AuditLogger stay completely unaware of WHICH sinks exist or how many — logging one event automatically reaches every registered listener, and adding a new sink is purely additive, no change needed to AuditLogger itself.

How the class design enforces immutability

This is the requirement that most distinguishes an audit log from a general logging framework: an audit record's TRUSTWORTHINESS depends on it being genuinely tamper-evident — AuditEvent being final with every field final, and the constructor taking a DEFENSIVE COPY of the mutable metadata map (Map.copyOf, which produces a genuinely immutable map, not just a reference to the caller's own mutable one) means an AuditEvent, once constructed, CANNOT be altered by any code holding a reference to it — not accidentally, and not through any API the class itself exposes. This is a real, deliberate design constraint, not a stylistic preference — an audit record whose fields COULD be mutated after creation would undermine the entire point of having an audit trail at all.

Follow-up questions this topic invites — and their answers

Q: Does immutability at the Java-object level fully guarantee the audit record can't be tampered with? A: No — it prevents tampering via NORMAL application code holding a reference to the object, but the DATABASE record itself, once persisted, could still theoretically be altered directly (a rogue DBA, a compromised credential) — genuinely tamper-EVIDENT audit trails at the storage layer typically add cryptographic techniques (a hash chain linking each record to the previous one, so any alteration breaks the chain and is detectable) beyond what in-memory object immutability alone provides.

Q: Should a failed sink (e.g. the SIEM forward fails) block the DatabaseSink from succeeding? A: No — each listener should be invoked independently, with one listener's failure isolated from the others (a try/catch around each onEvent call within the dispatch loop, logging the sink-level failure separately) — an audit event failing to reach the SIEM shouldn't prevent it from at least being durably recorded in the database, since losing the record entirely is a worse outcome than one sink temporarily missing it.

Q: How does this framework's immutability requirement compare to Design a Logging Framework's design? A: The general logging framework has no such requirement — a debug log line has no compliance/dispute-resolution role, so there's no need for its class design to defensively guard against mutation; this is precisely the distinction that makes these two exercises genuinely different problems despite superficially similar-sounding names (both 'log' something), not a duplicate of each other.

Q: Should AuditLogger itself validate that an event has all required fields before dispatching? A: Yes, reasonably — validating at CONSTRUCTION time (in AuditEvent's constructor, rejecting a null actorId or action immediately) is generally preferable to validating later at dispatch time, since it fails fast at the point of creation rather than allowing an incomplete event to exist at all, even briefly.

Previous

Design a Metrics Collection Library

Next

Design a Plugin/Extension System

AI Tutor

Lesson: Design an Audit Logging Framework

Quick actions

AI responses can be inaccurate. Verify critical information.