Examples
Full Example
End-to-end e-commerce flow — order, payment, shipping — all correlated.
A single customer checkout touches several systems: the order table,
the payment gateway, and the shipping provider. This example shows
all three linked under one transaction_id.
Complete Code
package main
import (
"context"
"fmt"
"log"
"net/http"
"time"
"github.com/gopackx/go-audit"
auditgorm "github.com/gopackx/go-audit/adapters/gorm"
"gorm.io/driver/postgres"
"gorm.io/gorm"
)
type Order struct {
ID uint `gorm:"primaryKey"`
CustomerID string
Amount int
Status string
PaymentRef string
TrackingNumber string
}
func Checkout(
ctx context.Context,
gormDB *gorm.DB,
auditor audit.Auditor,
customerID string,
amount int,
) error {
ctx = audit.WithTransactionID(ctx, audit.NewTransactionID())
tx := gormDB.WithContext(ctx)
// 1. Create order (audit_logs: create)
order := Order{CustomerID: customerID, Amount: amount, Status: "pending"}
if err := tx.Create(&order).Error; err != nil {
return err
}
// 2. Call payment gateway (audit_api_logs: bca POST)
start := time.Now()
ref, err := bcaTransfer(ctx, amount)
_ = auditor.API().Record(ctx, audit.APIEntry{
Service: "bca",
Endpoint: "/v1/transfer",
Method: http.MethodPost,
StatusCode: 200,
DurationMs: int(time.Since(start).Milliseconds()),
})
if err != nil {
tx.Model(&order).Update("Status", "failed")
return err
}
// 3. Update order with payment ref (audit_logs: update)
tx.Model(&order).Updates(map[string]any{
"payment_ref": ref,
"status": "paid",
})
// 4. Call shipping provider (audit_api_logs: jne POST)
start = time.Now()
tracking, err := jneCreateShipment(ctx, order.ID)
_ = auditor.API().Record(ctx, audit.APIEntry{
Service: "jne",
Endpoint: "/v2/shipments",
Method: http.MethodPost,
StatusCode: 201,
DurationMs: int(time.Since(start).Milliseconds()),
})
if err != nil {
return err
}
// 5. Update order with tracking (audit_logs: update)
return tx.Model(&order).Updates(map[string]any{
"tracking_number": tracking,
"status": "shipped",
}).Error
}
func main() {
gormDB, err := gorm.Open(postgres.Open("..."), &gorm.Config{})
if err != nil {
log.Fatal(err)
}
sqlDB, err := gormDB.DB()
if err != nil {
log.Fatal(err)
}
auditor, err := audit.New(sqlDB, audit.Config{
Dialect: audit.PostgreSQL,
UserFunc: func(ctx context.Context) (string, string) { return "user-1", "user" },
DataAudit: audit.DataAuditConfig{Enabled: true},
APIAudit: audit.APIAuditConfig{Enabled: true},
})
if err != nil {
log.Fatal(err)
}
_ = gormDB.Use(auditgorm.Plugin(auditor))
_ = auditor.AutoMigrate(context.Background())
_ = Checkout(context.Background(), gormDB, auditor, "cust-42", 250000)
_ = fmt.Println
}Querying the Audit Trail
trail, _ := auditor.QueryByTransaction(ctx, txID)
for _, l := range trail.DataLogs {
fmt.Printf("[data] %s %s#%s\n", l.Action, l.EntityType, l.EntityID)
}
for _, a := range trail.APILogs {
fmt.Printf("[api] %s %s %s (%d ms)\n",
a.Method, a.Service, a.Endpoint, a.DurationMs)
}Output
[data] create orders#7
[data] update orders#7
[data] update orders#7
[api] POST bca /v1/transfer (842 ms)
[api] POST jne /v2/shipments (430 ms)This is the full timeline of one customer checkout — queryable whenever you need it.
Retention
Run a daily job that trims old audit rows:
cutoff := time.Now().AddDate(-1, 0, 0) // 1 year
result, _ := auditor.Purge(ctx, cutoff)
log.Printf("purged data=%d api=%d", result.DataLogs, result.APILogs)