Auto Diff
How Go Audit captures old vs new values automatically.
Go Audit computes the difference between a record's state before and after an update, and stores only the fields that changed. You don't write any diff code — it happens inside the ORM hooks.
How It Works
- The
BeforeUpdate/BeforeDeletehook fires (unlessDataAudit.SkipOldValuesis set). Go Audit issues aSELECTfor the affected row(s) and captures it as the "old" snapshot. - The ORM performs the write.
- The
AfterUpdate/AfterDeletehook fires. Go Audit compares the old snapshot with the new values field-by-field usingreflect.DeepEqual. - Only the fields whose values changed are written into
old_valuesandnew_values.
Example Output
Given an update that only changes email, the audit record looks like:
{
"action": "update",
"old_values": { "email": "old@example.com" },
"new_values": { "email": "new@example.com" }
}name, created_at, and every other unchanged field are omitted, so
the payload stays small even for wide tables.
Per-Action Behavior
| Action | old_values | new_values |
|---|---|---|
create | null | all fields |
update | only changed fields | only changed fields |
delete | all fields | null |
soft_delete | all fields | updated fields (e.g. deleted_at) |
restore | current state | target historical state |
Nested Values
Diffing uses reflect.DeepEqual on each top-level column value. If a
column holds a nested struct or JSON blob, the whole value appears
in old_values/new_values when any nested field changes — Go Audit
does not produce partial diffs inside a single column.
Excluded Fields
Fields listed in DataAudit.ExcludeFields are dropped from both
old_values and new_values before persistence. Matching is
case-sensitive on the map key (which is normally the DB column name
produced by the ORM).
DataAudit: audit.DataAuditConfig{
ExcludeFields: []string{"password", "token"},
}With that config, no record ever mentions the password or token
fields — they simply don't appear. If every field changed was in
ExcludeFields, the audit entry is skipped entirely.
When Diff Is Skipped
- No fields changed after exclusion — the UPDATE record is suppressed entirely (no noise from no-op updates).
- The entity is in
ExcludeEntities— no record is written. DataAudit.SkipOldValues == true— the pre-UPDATESELECTis skipped;old_valueswill be empty but the record is still written.- The session skips hooks (e.g.
gorm.Session{SkipHooks: true}) — hooks don't fire at all.
Quickstart
Get go-audit running in under 5 minutes. This guide walks through installation, configuration and recording your first audit log entry.
API Call Logging
Capture every outgoing or third-party API request with header and body redaction, smart truncation, and automatic correlation to the surrounding business transaction.