Audit Trail vs Audit Log: Key Differences
TL;DR
You have TWO audit systems that serve different purposes:| Feature | AuditTrail (PostgreSQL) | AuditLog (Audit Service) |
|---|---|---|
| Purpose | Database-level change tracking | Business-level action logging |
| Scope | Row-level CRUD operations | Cross-entity business actions |
| Location | Centralized audit_trails table | Single audit_log table |
| Automatic | ✅ Yes (via AFTER triggers) | ❌ No (manual integration) |
| Granularity | Entity-level (leave tables) | Action-level |
| Use Case | ”What changed in this record?" | "Who approved this leave request?“ |
1. AuditTrail (PostgreSQL AFTER Triggers)
What It Is:
Database-level change tracking - automatically captures row-level CRUD operations via PostgreSQL AFTER triggers. This MVP implementation applies to leave tables (leave_requests, leave_balances, leave_types) with full row snapshots in JSONB format.Location:
Schema:
Storage:
- Stored in single centralized table:
audit_trails - All leave-related changes in one place for easy querying
How It Works:
Automatically - triggered by PostgreSQL AFTER triggers on write operations:What It Captures:
- ✅ Complete row snapshots (full before/after state as JSONB)
- ✅ When it happened (timestamp to microsecond precision)
- ✅ What operation (INSERT/UPDATE/DELETE)
- ✅ Which row (row_id for easy filtering)
- ✅ Transactional consistency (audit entry rolls back if transaction fails)
What It DOESN’T Capture:
- ❌ Who made the change (user context is in the row data, but not metadata)
- ❌ Why the change was made (no business context)
- ❌ Cross-entity actions (each table audited independently)
- ❌ User details (name, role, IP, user agent)
- ❌ Failed operations (only committed changes are logged)
- ❌ Identity context (application must determine user from authorization)
Example Data:
2. AuditLog (New - Audit Service)
What It Is:
Business-level action logging - manually captures business actions with full context.Location:
Schema:
Storage:
- Stored in single centralized table:
audit_log - All business actions across all entities
How It Works:
Manual - you must explicitly call it:What It Captures:
- ✅ Business actions (approve, reject, export, etc.)
- ✅ User context (name, role, IP, user agent)
- ✅ Why (details, comments, reasons)
- ✅ Cross-entity context (can log multiple entities in one action)
- ✅ Failed attempts (can log even if operation fails)
- ✅ Non-CRUD actions (export, view, login, etc.)
What It DOESN’T Capture:
- ❌ Automatic - requires manual integration
- ❌ Full entity snapshots (only selective fields)
Example Data:
When to Use Which?
Use AuditTrail (BaseRepository) For:
- ✅ Data recovery - “What was the value before?”
- ✅ Compliance - “Show all changes to this record”
- ✅ Debugging - “Who changed this field?”
- ✅ Automatic tracking - No code changes needed
Use AuditLog (Audit Service) For:
- ✅ Security auditing - “Who accessed what from where?”
- ✅ Business reporting - “How many approvals did each manager do?”
- ✅ Compliance reports - “Show all leave approvals in Q4”
- ✅ User activity - “What did this user do today?”
- ✅ Failed attempts - “Who tried to approve without permission?”
Comparison Examples
Scenario: Manager approves a leave request
AuditTrail Captures:
AuditLog Captures:
Recommendation: Use BOTH!
They Complement Each Other:
Example Integration:
leave_requests_audithas the request changeleave_balances_audithas the balance changeaudit_loghas the approval action linking both
Phase 1: PostgreSQL Audit Trails (Current)
Scope: Leave Tables Only
- ✅
leave_requests- auto-audited via trigger - ✅
leave_balances- auto-audited via trigger - ✅
leave_types- auto-audited via trigger - ❌ Other tables (orders, customers, products) - Phase 2
Implementation Details:
- Trigger function:
fn_audit_trail()(shared across all audited tables) - Per-table triggers:
trg_audit_leave_requests,trg_audit_leave_balances,trg_audit_leave_types - Idempotent on every startup (safe to re-run migrations)
- Composite indexes for efficient querying by table + date or table + row
Query Examples:
See PostgreSQL Audit Trail Query Examples for comprehensive patterns.Phase 2: Expansion Plan (Future)
Extend to Other Tables:
- Orders and order items (business-critical)
- Customers (CRM operations)
- Products (inventory changes)
Async Audit Option:
- Event-based outbox pattern for high-volume tables
- Keep sync approach for leave (human-scale operations)
Using Both Systems Together
Current State:
- ✅ AuditTrail (PostgreSQL) - working for leave tables
- ⚠️ AuditLog - infrastructure only (needs manual integration)
Integration Pattern:
- PostgreSQL AuditTrail captures data changes automatically
-
AuditLog (manual) adds business context and cross-entity tracking:
-
Query Both for comprehensive auditing:
Summary
| Question | PostgreSQL AuditTrail | AuditLog (Biz Logic) |
|---|---|---|
| Already working? | ✅ Yes (Phase 1) | ❌ No (needs integration) |
| Automatic? | ✅ Yes (triggers) | ❌ No |
| What changed? | ✅ Complete JSONB snapshots | ⚠️ Selective fields |
| Who did it? | ⚠️ In row data (approver_id) | ✅ Full user context |
| Why? | ❌ No | ✅ Yes (details, comments) |
| Where from? | ❌ No | ✅ Yes (IP, user agent) |
| Business actions? | ❌ No | ✅ Yes |
| Cross-entity? | ❌ No (per-table) | ✅ Yes |
| Queryable? | ✅ SQL (centralized) | ✅ SQL (centralized) |
Migration from MongoDB to PostgreSQL
What Changed in the Migration:
Before (MongoDB):- AuditTrail stored in per-collection tables (
leave_requests_audit,leave_balances_audit) - Populated by application BaseRepository code
- No cross-collection view of all changes
- AuditTrail stored in single
audit_trailstable - Populated automatically by database triggers
- Easy to query all changes across leave tables
- No application code changes needed
- Safe to re-run migrations on every startup
No Breaking Changes:
- AuditLog (Audit Service) remains unchanged
- Query patterns are slightly different (now using
audit_trailstable instead of per-collection) - Same information captured (before/after snapshots)
- Same compliance and debugging value