Query & Filters
Cross-table queries, retention, snapshot, and restore.
Auditor.QueryByTransaction(ctx, txID) → (*TransactionLog, error)
Fetches all data audit rows and API call rows sharing a
transaction_id.
trail, err := auditor.QueryByTransaction(ctx, txID)
// trail.DataLogs []AuditLog
// trail.APILogs []AuditAPILogAPI logs are only included if APIAudit.Enabled == true.
audit.TransactionLog
type TransactionLog struct {
TransactionID string
DataLogs []AuditLog
APILogs []AuditAPILog
}Use this in UIs that show a transaction timeline or in support tooling that reconstructs what happened during a single user action.
Pagination
Every filter type exposes Limit and Offset:
page1, _ := auditor.Query(ctx, audit.DataFilter{Limit: 25, Offset: 0})
page2, _ := auditor.Query(ctx, audit.DataFilter{Limit: 25, Offset: 25})For very large tables, prefer keyset pagination against created_at
(set the next page's DateTo to the previous page's last row's
CreatedAt) over deep offset pagination.
Sorting
All queries return results ordered by created_at DESC by default.
Transaction ID Helpers
func audit.NewTransactionID() string
func audit.WithTransactionID(ctx context.Context, txID string) context.Context
func audit.TransactionIDFromContext(ctx context.Context) stringNewTransactionID returns a YYYYMMDDTHHmmss-<32-char hex> string
(lexicographically sortable by time). TransactionIDFromContext
returns the empty string when no ID is set.
Auditor.Purge(ctx, before) → (PurgeResult, error)
Deletes rows older than before from audit_logs and
audit_api_logs (only the tables that are enabled).
cutoff := time.Now().AddDate(-1, 0, 0) // 1 year ago
result, err := auditor.Purge(ctx, cutoff)
// result.DataLogs: int64 rows deleted
// result.APILogs: int64 rows deletedReturns a PurgeResult:
type PurgeResult struct {
DataLogs int64
APILogs int64
}Run Purge from a daily cron to enforce retention policies.
Auditor.Snapshot(ctx, entityType, entityID, at) → (map[string]any, error)
Reconstructs the state of an entity at a given point in time by
replaying its audit history up to at.
state, err := auditor.Snapshot(ctx,
"orders", "42",
time.Date(2026, 3, 1, 12, 0, 0, 0, time.UTC),
)
// state is the map of column → value at that momentReturns nil (without error) if the entity did not exist or had been
deleted at that time. Requires non-empty entityType, entityID,
and a non-zero at time.
Useful for "what did this record look like when X happened?" questions during incident response.
Auditor.Restore(ctx, entityType, entityID, at) → (*RestoreResult, error)
Reconstructs the target state via Snapshot, then writes a restore
audit entry (old values = current state, new values = target state),
and returns the target values for the caller to apply via their ORM.
result, err := auditor.Restore(ctx, "orders", "42", oldTime)
if err != nil { return err }
// Apply the returned values via GORM
gormDB.Model(&Order{ID: 42}).Updates(result.Values)type RestoreResult struct {
EntityType string `json:"entity_type"`
EntityID string `json:"entity_id"`
Values map[string]any `json:"values,omitempty"`
WasDeleted bool `json:"was_deleted"`
}The ORM adapter will not record the application-level write as a
separate update — the restore audit entry already captures it. Your
caller is responsible for actually persisting the restored values.