Triggers. Here is my approach:
- Create one audit table for each critical table that requires audit trial
- Audit table will include all columns from source table + columns audit record info such as who, when and the action
- Trigger for UPDATE and DELETE only, the INSERT operation will have the pristine record in source table itself
- Before update or delete, copy the original record + audit info to audit table
- (Optionally - for UPDATE only:) To know which column got updated, use either UPDATE(ColumnName) or COLUMNS_UPDATED() built in SQL function to determine the affected columns
Auditing this way keeps the current status in the source table and all the history in audit table and easily identified by the key columns.