Langsung ke konten
Audit Log di Aplikasi SaaS: Daftar Event yang Wajib Saya Catat Setelah Kena Komplain Tenant
Kembali ke Blog
Backend Development·7 menit baca·

Audit Log di Aplikasi SaaS: Daftar Event yang Wajib Saya Catat Setelah Kena Komplain Tenant

Apa yang harus masuk audit log aplikasi SaaS? Panduan praktis dari pengalaman saya: event wajib, skema tabel, dan kesalahan desain audit trail yang paling sering terjadi.

TH
Tomi Hartanto·Senior Software Engineer

Insiden yang membuat saya serius soal audit log terjadi tiga tahun lalu: seorang admin tenant menghapus 40-an artikel sekaligus, lalu menuduh tim kami yang "menghilangkan" datanya. Kami punya soft delete, jadi datanya bisa dipulihkan — tapi kami tidak bisa menjawab pertanyaan paling penting: siapa yang menghapus, kapan, dan dari mana. Jam-jam pertama investigasi kami habis cuma untuk menebak. Setelah itu saya tidak pernah lagi merilis fitur CRUD tanpa audit trail.

Artikel ini bukan teori kepatuhan. Ini daftar konkret yang saya pakai sekarang: event apa yang wajib dicatat, event apa yang tidak perlu (dan justru berbahaya kalau dicatat), dan skema tabel yang terbukti cukup untuk SaaS multi-tenant.

Bedakan Audit Log dan Application Log Dulu

Kesalahan paling umum yang saya lihat: tim menganggap log di CloudWatch atau Loki sudah "cukup untuk audit". Tidak cukup, karena keduanya menjawab pertanyaan berbeda:

  • Application log: untuk debugging. Format bebas, retensi pendek (7–30 hari), boleh hilang.
  • Audit log: untuk menjawab pertanyaan accountability. Format terstruktur, retensi panjang (1–7 tahun tergantung kontrak), dan tidak boleh hilang.

Audit log adalah data bisnis, bukan infrastruktur. Dia harus bisa di-query oleh customer support tanpa minta tolong tim platform, dan harus survive melewati incident yang justru sedang dia audit. Kalau log auditmu ikut lenyap bersama instance yang terbakar, log itu tidak pernah benar-benar ada.

Diagram perbedaan application log dan audit log berdasarkan tujuan dan retensi

Event yang Wajib Dicatat

Setelah beberapa insiden, daftar saya menyempit jadi empat kategori. Prinsipnya satu: catat semua yang mengubah siapa bisa melihat apa, dan semua yang mengubah data secara tidak reversible.

1. Perubahan akses dan identitas

Ini kategori paling kritis di SaaS. Setiap event berikut wajib punya aktor, target, dan timestamp:

  • User di-invite, di-remove, atau role-nya diganti
  • User login sukses dan gagal (login gagal berulang adalah sinyal credential stuffing)
  • API key dibuat, dipakai pertama kali, dicabut
  • Perubahan pengaturan tenant: perubahan plan, perubahan owner, aktif/nonaktif MFA

Kasus nyata: tenant kami pernah ada dua admin yang saling menendang keluar lewat panel member. Tanpa audit log event member.role.changed, kami tidak bisa menengahi. Dengan audit log, kami lihat timeline lengkapnya dalam dua menit dan tinggal memutuskan.

2. Mutasi destruktif terhadap data

Hapus, bulk delete, restore, export massal. Di sini audit log beririsan dengan strategi soft delete vs hard delete yang pernah saya tulis — soft delete menyelamatkan data, tapi audit log yang menyelamatkan konteks penghapusan itu.

Yang sering dilupakan: export. Download 10.000 baris data customer via tombol export adalah exfiltrasi data yang sah secara permission, dan kamu ingin tahu kapan itu terjadi.

3. Perubahan konfigurasi yang berdampak ke seluruh tenant

Webhook URL diganti, integration disconnect, pengaturan billing diubah. Satu perubahan webhook bisa memutus alur order tenant, dan pertanyaan pertama mereka selalu "ini diubah kapan dan siapa".

4. Aksi sistem yang otomatis

Background job yang menghapus dataexpired, proses downgrade plan otomatis, atau service yang mengubah data atas nama user. Kasus Microsoft Copilot yang menulis log audit dengan cara yang tidak konsisten dengan aksi user — dibahas hangat di diskusi di Pistachio — adalah contoh bagaimana aksi otomatis/AI bisa merusak kepercayaan pada audit log kalau aktornya tidak dicatat jujur. Pelajarannya: kolom aktor harus selalu eksplisit, termasuk saat aktornya adalah system atau system.on-behalf-of:user_id.

Event yang TIDAK Perlu Dicatat

Di sisi lain, jangan catat tiap GET. Audit log yang membunuh dirinya sendiri lewat volume:

  • Query baca biasa — itu territory application log
  • Field-level diff yang berisi isi pesan/notes user (bocor data via log!)
  • Event yang bisa direkonstruksi dari data itu sendiri

Satu bug memalukan yang pernah saya buat: diff audit menyimpan before/after kolom catatan medis-tema. Log audit yang seharusnya melindungi privasi justru jadi salinan data sensitif di tempat dengan permission lebih longgar. Sekarang saya hanya menyimpan kolom mana yang berubah, bukan isinya, kecuali memang disyaratkan.

Skema Tabel yang Saya Pakai

Diskusi klasik di Stack Overflow tentang desain database untuk audit logging penuh opsi: audit table per entitas, EAV, atau satu tabel append-only. Setelah coba pendekatan per-entitas dan menyesal (tenant minta filter lintas entitas), saya sekarang pakai satu tabel append-only:

CREATE TABLE audit_log (
  id          BIGSERIAL PRIMARY KEY,
  tenant_id   UUID NOT NULL,
  actor_id    UUID,          -- NULL jika system
  actor_type  TEXT NOT NULL DEFAULT 'user', -- user | system | api_key
  action      TEXT NOT NULL, -- 'article.deleted', 'member.role_changed'
  entity_type TEXT NOT NULL,
  entity_id   TEXT,
  metadata    JSONB NOT NULL DEFAULT '{}',
  ip_address  INET,
  created_at  TIMESTAMPTZ NOT NULL DEFAULT now()
);

CREATE INDEX audit_log_tenant_created_idx
  ON audit_log (tenant_id, created_at DESC);

Keputusan desain yang saya ambil, beserta trade-offnya:

  • Append-only. Tidak ada UPDATE, tidak ada DELETE dari aplikasi. Partisi per bulan, purge lewat prosedur terpisah sesuai retensi kontrak.
  • action sebagai string bertitik, bukan enum. Menambah event baru tidak butuh migrasi enum.
  • metadata JSONB untuk detail, bukan kolom per event. Fleksibel, tapi artinya kamu butuh disiplin dokumentasi format per action.
  • entity_id sebagai TEXT karena kadang ID-nya bukan UUID (misal slug).

Satu hal yang saya putuskan tidak lakukan: menulis audit dari trigger PostgreSQL. PgAudit dan trigger memang menangkap semuanya di level DB, tapi audit level database tidak tahu konsep "siapa user aplikasinya" di setup multi-tenant — dan itu justru informasi yang paling sering ditanya. Solusi saya adalah lapisan aplikasi: audit ditulis di service layer yang sama tempat permission dicek, satu transaksi dengan mutasinya.

Untuk tenant yang butuh jaminan lebih ketat lagi, saya kombinasikan dengan isolasi data ala RLS vs schema-per-tenant — tapi audit table tetap global dengan tenant_id, karena tim internal perlu query lintas tenant saat menyelidiki abuse.

Ilustrasi tabel audit log append-only dengan index tenant dan timestamp

Penulisan Log yang Tidak Bisa Di-skip

Masalah implementasi terbesar bukan skema, tapi kelalaian: developer lupa memanggil audit() di satu code path, dan selesai sudah. Dua mekanisme yang saya pakai:

  1. Utility wajib di layer service. Semua route destruktif (DELETE, bulk action) di-review dengan checklist "panggil audit?". Kalau ada bandwidth, middleware route-level lebih aman: label route, dan middleware otomatis menulis event.

  2. Test yang gagal kalau event hilang. Saya tambah assertion di integration test: aksi X harus menghasilkan baris audit dengan action Y. Kalau ada refactor yang tak sengaja menghilangkan event, CI yang menjerit — bukan customer.

Relaks juga soal performa. Synchronous insert satu baris ke satu tabel dengan satu index itu murah — di load kami di bawah 1ms tambahan per mutasi. Kalau mutasimu ribuan per detik, tulis ke queue dulu, tapi jangan sampai "nanti dulu, belum urgent" — karena audit log yang tidak lengkap lebih buruk daripada tidak ada: dia memberi rasa aman yang palsu.

Yang Saya Lakukan Kalau Mulai dari Nol

Kalau kamu membangun SaaS baru dan belum punya audit log apa pun, urutan prioritas saya:

  1. Mulai dari kategori 1 (identitas dan akses) — ini yang paling sering dibutuhkan saat dispute.
  2. Tambahkan event destruktif. Dua kategori ini sudah menutup 90% kasus.
  3. Baru pikirkan UI untuk tenant melihat log mereka sendiri — banyak SaaS B2B jual ini sebagai fitur "activity trail" di plan enterprise.

Dan satu kebiasaan terakhir: tiap kuartal, coba jawab pertanyaan "admin tenant X menghapus data Y pada tanggal Z — jelaskan" pakai audit logmu sendiri. Kalau butuh lebih dari lima menit untuk menjawab, audit logmu belum selesai.

Sumber

TH

Tomi Hartanto

Senior Software Engineer

Backend developer dengan 8+ tahun pengalaman membangun sistem scalable. Menulis tentang arsitektur backend, database, dan DevOps.