Troubleshooting
Common issues and how to fix them.
Audit logs not being created
Checklist:
- Data audit enabled?
DataAudit.Enabledmust betruein the config.audit.Newreturns an error if neither data nor API audit is enabled, but it's easy to enable only API and forget data. - Plugin registered?
gormDB.Use(auditgorm.Plugin(auditor))must be called before any CRUD. Same story for Bun (auditbun.Register) and Ent (client.Use). - Migrated?
auditor.AutoMigrate(ctx)must have run at least once so theaudit_logstable exists. - Entity excluded? Verify the entity isn't in
ExcludeEntities. - Context passed through? Use
gormDB.WithContext(ctx)— yourUserFuncneeds the context to extract user info. - Hooks not skipped?
gormDB.Session(&gorm.Session{SkipHooks: true})disables audit writes.
old_values is always empty on update
DataAudit.SkipOldValuesis set totrue. That's the main cause.- The
BeforeUpdatehook didn't run — usually aSkipHooks: truesomewhere in the session chain. - For
Updates(map[string]any{...}), make sure the query includes the primary key in the WHERE so the plugin can fetch the old row.
"audit_logs does not exist" (or the custom table name)
Call auditor.AutoMigrate(ctx) once at startup, before any CRUD.
It's idempotent — safe to call repeatedly — and uses
IF NOT EXISTS everywhere.
audit.New returns an error
Most common causes:
DataAudit.Enabled: truewithout aUserFunc.- Neither
DataAudit.EnablednorAPIAudit.Enabledis true. - Invalid
DataAudit.TableorAPIAudit.Table. Identifiers must match^[A-Za-z_][A-Za-z0-9_]{0,62}$.
JSON fields stored as empty {} or missing
- Make sure struct fields are exported (capitalized).
- The ORM must surface the column in its update payload. For GORM,
make sure you're not hitting
.Select(...)that excludes the field.
Redaction not applied
- For API audit, confirm the call goes through
auditor.API().Record(ctx, ...)— a directINSERTbypasses redaction. - Case insensitivity is on by default for both headers and body
fields, but the match is against the key name. An entry like
"Authorization-Bearer"won't match"Authorization".
Performance degradation after enabling Go Audit
- Each UPDATE does one extra
SELECT(to capture old values) plus oneINSERT. SetDataAudit.SkipOldValues: trueon hot paths where "before" state isn't needed. - Ensure the default indexes exist. Missing indexes on
entity_type/entity_idortransaction_idshow up as slow reads, not slow writes. - If audit volume is very high, partition
audit_logsbycreated_atand rely onPurgeor partition-drop for retention.
transaction_id is empty
You need to stamp the context:
ctx = audit.WithTransactionID(ctx, audit.NewTransactionID())Every write on this ctx will carry the ID. Without it, records
still write — they just have an empty transaction_id.
You can also set DataEntry.TransactionID or APIEntry.TransactionID
explicitly on the entry itself for cases where the context isn't
convenient to thread through.