Membuat Audit Log
24 Oct 2012Dalam membuat aplikasi bisnis, kita sering diminta membuat audit log.
Apa itu audit log?
Audit log adalah catatan mengenai perubahan data dalam aplikasi. Yang dicatat biasanya :
- kolom mana yang berubah
- siapa yang mengubah
- diubah dari apa menjadi apa
- kapan dia berubah
Dengan menggunakan Hibernate Envers, audit log ini bisa dibuat dengan mudah sekali. Hibernate Envers adalah salah satu modul tambahan Hibernate untuk keperluan ini. Tentunya aplikasi kita harus menggunakan Hibernate supaya bisa memanfaatkan fitur ini.
Konfigurasi Awal
Konfigurasi dan setup awal sangat mudah, yaitu:
- Tambahkan JAR hibernate-envers ke dalam aplikasi
- Tambahkan anotasi
@Auditeddi class@Entitykita.
Contoh perubahan ini bisa dilihat di sini.
Audit Log Sederhana
Entity class yang sudah ditambahi dengan annotation @Audited tampak seperti ini:
@Entity @Audited
@Table(name="m_kategori")
public class Kategori {
@Id @GeneratedValue(strategy= GenerationType.AUTO)
private Integer id;
@Column(nullable=false, unique=true)
private String kode;
private String nama;
// getter setter tidak ditampilkan
}
Pada awalnya, mapping di atas akan menghasilkan skema database seperti ini:
CREATE TABLE `m_kategori` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`kode` varchar(255) NOT NULL,
`nama` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `kode` (`kode`)
) ENGINE=InnoDB;
Setelah ditambahi @Audited, Envers akan menambahkan satu tabel untuk keperluan audit log m_kategori dengan nama tabel ditambahi akhiran _AUD dan memiliki skema seperti ini:
CREATE TABLE `m_kategori_AUD` (
`id` int(11) NOT NULL,
`REV` int(11) NOT NULL,
`REVTYPE` tinyint(4) DEFAULT NULL,
`kode` varchar(255) DEFAULT NULL,
`nama` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`,`REV`),
KEY `FK4263149C625F360` (`REV`),
CONSTRAINT `FK4263149C625F360` FOREIGN KEY (`REV`) REFERENCES `REVINFO` (`id`)
) ENGINE=InnoDB;
Kolom dalam tabelnya tidak jauh berbeda, dengan perbedaan sebagai berikut:
-
tambahan kolom
REVyang menunjukkan urutan perubahan. NilaiREVini tidak berurut karena penambahannya (increment) sharing dengan semua tabel lain. Bisa sajaREV1mencatat perubahan dim_kategorisedangkanREV2mencatat perubahanm_produk. -
kolom
REVTYPEyang menunjukkan jenis perubahan. Nilainya0untuk insert record baru,1untuk modifikasi, dan2untuk hapus -
kolom
idtidak lagi menjadi primary key sendirian, tapi bersama dengan kolomREV
Kita juga lihat ada relasi ke tabel REVINFO. Skemanya sebagai berikut:
CREATE TABLE `REVINFO` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`timestamp` bigint(20) NOT NULL
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
Kolom id merupakan nilai yang bertambah terus (auto increment). Tabel ini akan diisi setiap kali ada revisi di tabel yang dipantau oleh Envers seperti tabel m_kategori kita di atas.
Tabel ini dishare oleh semua tabel audit log sehingga untuk satu tabel yang sama, nomer REV belum tentu berurutan. Bisa saja diselingi oleh nomer REV yang berkaitan dengan tabel lain. Kolom timestamp menunjukkan kapan revisi terjadi.
Untuk lebih memahami penggunaan tabel-tabel ini, mari kita lihat isi datanya.
Pertama, kita jalankan kode berikut dari aplikasi kita:
@Test
public void testCrudKategori() {
Kategori k = new Kategori();
k.setKode("K-999");
k.setNama("Kategori 999");
assertNull(k.getId());
belajarService.simpan(k);
assertNotNull(k.getId());
k.setKode("K-999-X");
k.setNama("Kategori 999-X");
belajarService.simpan(k);
Kategori kx = belajarService.cariKategoriById(k.getId());
assertEquals("K-999-X", kx.getKode());
assertEquals("Kategori 999-X", kx.getNama());
belajarService.hapus(k);
assertNull(belajarService.cariKategoriById(k.getId()));
}
Operasi di atas akan mengisi tabel audit sebagai berikut:
select * from m_kategori_AUD order by id,REV,REVTYPE;
+-----+-----+---------+---------+-----------------------+
| id | REV | REVTYPE | kode | nama |
+-----+-----+---------+---------+-----------------------+
| 101 | 2 | 0 | K-999 | Kategori 999 |
| 101 | 3 | 1 | K-999-X | Kategori 999-X |
| 101 | 4 | 2 | NULL | NULL |
+-----+-----+---------+---------+-----------------------+
Data dengan REV 1 dan 2 tidak ditampilkan karena mencatat record lain.
Dari isi tabel ini, kita bisa melihat bahwa terjadi tiga kali operasi pada record kategori dengan id 101, yaitu:
- Insert record baru.
REVTYPEberisi0, artinya record baru. Kolom id, kode, dan nama berisi nilai yang pertama diisi. - Update kode dan nama.
REVTYPEberisi1, artinya modifikasi. Kolom kode dan nama diisi dengan nilai setelah modifikasi - Delete record.
REVTYPEberisi2, artinya hapus. Kolom kode dan nama diisi null karena datanya sudah tidak relevan (karena record dihapus).
Berikut isi tabel REVINFO
select * from REVINFO;
+----+---------------+
| id | timestamp |
+----+---------------+
| 2 | 1351521839648 |
| 3 | 1351521839718 |
| 4 | 1351521839801 |
+----+---------------+
Karena hanya mencatat timestamp saja, maka tidak ada yang perlu dijelaskan mengenai isi tabel REVINFO.
Penambahan informasi yang ingin dicatat
Secara default, Hibernate Envers hanya mencatat data yang berubah dan kapan dia berubah. Untuk menambahkan catatan tentang username, perlu ada sedikit coding seperti bisa dilihat di sini. Kita harus :
- Extends
DefaultRevisionEntitydan menambahkan kolom username - Membuat
RevisionListeneruntuk mengisi kolom username tersebut
Data username biasanya diambilkan dari user yang sedang login di aplikasi (dan dengan sendirinya dialah yang melakukan perubahan data).
Setelah dilakukan dua modifikasi di atas, skema REVINFO menjadi sebagai berikut:
CREATE TABLE `REVINFO` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`timestamp` bigint(20) NOT NULL,
`username` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
Berikut yang sudah terisi data:
select * from REVINFO;
+----+---------------+----------+
| id | timestamp | username |
+----+---------------+----------+
| 1 | 1351521839220 | endy |
| 2 | 1351521839648 | endy |
| 3 | 1351521839718 | endy |
| 4 | 1351521839801 | endy |
| 5 | 1351521840174 | endy |
| 6 | 1351521840571 | endy |
| 7 | 1351521840959 | endy |
| 8 | 1351521841351 | endy |
+----+---------------+----------+
Penyesuaian Lain
Selain penambahan kolom yang ingin dicatat, biasanya kita juga ingin melakukan penyesuaian lain seperti:
- akhiran di nama tabel audit (
_AUD) - nama kolom nomer revisi (
REV) - nama kolom jenis revisi (
REVTYPE) - dan sebagainya
Keterangan apa saja yang bisa diubah dan cara mengubahnya dapat dilihat di dokumentasi konfigurasi Envers.
Contoh kode yang lengkap bisa diambil di repository saya di Github.