go-auditGo Audit
Features

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

  1. The BeforeUpdate / BeforeDelete hook fires (unless DataAudit.SkipOldValues is set). Go Audit issues a SELECT for the affected row(s) and captures it as the "old" snapshot.
  2. The ORM performs the write.
  3. The AfterUpdate / AfterDelete hook fires. Go Audit compares the old snapshot with the new values field-by-field using reflect.DeepEqual.
  4. Only the fields whose values changed are written into old_values and new_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

Actionold_valuesnew_values
createnullall fields
updateonly changed fieldsonly changed fields
deleteall fieldsnull
soft_deleteall fieldsupdated fields (e.g. deleted_at)
restorecurrent statetarget 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-UPDATE SELECT is skipped; old_values will be empty but the record is still written.
  • The session skips hooks (e.g. gorm.Session{SkipHooks: true}) — hooks don't fire at all.

On this page