go-auditGo Audit
Features

API Call Logging

Capture every outgoing or third-party API request with header and body redaction, smart truncation, and automatic correlation to the surrounding business transaction.

API call logging records outbound requests to services like payment gateways, shipping APIs, and identity providers. Each log entry captures the service, endpoint, method, headers, bodies, response, duration, and status.

Go Audit never originates HTTP calls itself. You call your third-party API as usual, then hand the request/response to auditor.API().Record(...).

Setup

Enable it on the auditor config:

auditor, err := audit.New(sqlDB, audit.Config{
    Dialect: audit.PostgreSQL,
    APIAudit: audit.APIAuditConfig{
        Enabled:          true,
        RedactHeaders:    []string{"Authorization", "X-API-Key"},
        RedactBodyFields: []string{"password", "card_number", "cvv"},
        MaxBodySize:      64 * 1024, // default is 4096 bytes
    },
})

Then run auditor.AutoMigrate(ctx) — it creates the audit_api_logs table alongside audit_logs.

Recording an API Call

Go
start := time.Now()
resp, err := httpClient.Do(req)
duration := time.Since(start)

reqHeaders := flatten(req.Header)
respHeaders := flatten(resp.Header)

_ = auditor.API().Record(ctx, audit.APIEntry{
    Service:         "bca",
    Endpoint:        "/v1/transfer",
    Method:          http.MethodPost,
    StatusCode:      resp.StatusCode,
    RequestHeaders:  reqHeaders,
    ResponseHeaders: respHeaders,
    RequestBody:     reqBody,
    ResponseBody:    respBody,
    DurationMs:      int(duration.Milliseconds()),
})
Go snippet

RequestHeaders and ResponseHeaders are both map[string]string and share the same RedactHeaders list. RequestBody and ResponseBody are any — structs, pointers, and maps all work because the auditor normalizes them through encoding/json before redaction.

Auto Redaction

Any header whose key matches RedactHeaders (case-insensitive) is replaced with "***REDACTED***". Any body field whose key matches RedactBodyFields (case-insensitive, at any depth in a JSON tree) is replaced the same way.

JSON
{
  "request_headers": {
    "Authorization": "***REDACTED***",
    "Content-Type": "application/json"
  },
  "response_headers": {
    "Set-Cookie": "***REDACTED***",
    "Content-Type": "application/json"
  },
  "request_body": {
    "card_number": "***REDACTED***",
    "amount": 100000
  }
}
JSON snippet

The redaction is symmetric — it applies to request and response headers (via RedactHeaders) and request and response bodies (via RedactBodyFields).

Body Truncation

Bodies larger than MaxBodySize (default 4096 bytes) are replaced with a truncation marker so you can distinguish truncated payloads from short ones:

JSON
{
  "_truncated": true,
  "original_size": 128512,
  "preview": "{\"items\":[{\"id\":1,\"name\":\"…"
}
JSON snippet

The preview is the first MaxBodySize bytes, rewound to the nearest valid UTF-8 rune boundary.

Linking to Data Changes

Pass a transaction ID through the context and Go Audit stamps it on both data audit rows and API call rows:

Go
txID := audit.NewTransactionID()
ctx = audit.WithTransactionID(ctx, txID)

// every data + API log on this ctx shares txID
Go snippet

Query the full story with QueryByTransaction.

On this page