go-auditGo Audit
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)

On this page