Go
Guia completo de integração com o ImmutableLog em Go. Middleware automático para net/http, Gin e Fiber — ou cliente HTTP direto para workers, Kafka consumers e jobs agendados. Goroutines fire-and-forget garantem zero overhead na latência das respostas.
Middlewares
Integração automática via middleware — toda requisição HTTP é capturada sem modificar nenhum handler. Clique no framework para ver o código completo.
Middleware para a biblioteca padrão. Zero dependências — compatível com gorilla/mux, chi e qualquer router http.Handler.
func Middleware(opts Options) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
startedAt := time.Now()
wrapped := &responseWriter{ResponseWriter: w}
next.ServeHTTP(wrapped, r)
go emit(r, wrapped.Status(), time.Since(startedAt), opts)
})
}
}Ver documentação completa →
gin.HandlerFunc com c.Next(), c.Writer.Status() e c.Set() para eventos customizados. Suporte a route groups e abort de auth.
func Middleware(cfg Config) gin.HandlerFunc {
return func(c *gin.Context) {
startedAt := time.Now()
c.Next()
elapsed := time.Since(startedAt)
status := c.Writer.Status()
eventName, _ := c.Get("imtbl.eventName")
go emit(c.Request, status, elapsed, eventName, cfg)
}
}Ver documentação completa →
fiber.Handler com fasthttp. Copia o body antes de c.Next() para evitar buffer recycling. Captura erros Go retornados pela cadeia.
func New(cfg Config) fiber.Handler {
return func(c *fiber.Ctx) error {
body := make([]byte, len(c.Body()))
copy(body, c.Body())
startedAt := time.Now()
err := c.Next()
status := c.Response().StatusCode()
go emit(c, status, time.Since(startedAt), body, err, cfg)
return err
}
}Ver documentação completa →
Cliente HTTP direto
Use o Client para enviar eventos diretamente ao ImmutableLog sem depender de um framework web. Ideal para workers, consumidores Kafka, cron jobs e qualquer código Go que precise registrar eventos.
package immutablelog
import (
"bytes"
"context"
"crypto/sha256"
"encoding/json"
"fmt"
"net/http"
"os"
"time"
)
type Client struct {
APIKey string
ServiceName string
Env string
APIURL string
httpClient *http.Client
}
func NewClient(apiKey, serviceName, env string) *Client {
return &Client{
APIKey: apiKey,
ServiceName: serviceName,
Env: env,
APIURL: "https://api.immutablelog.com",
httpClient: &http.Client{Timeout: 5 * time.Second},
}
}
type SendOpts struct {
EventName string
Type string // "success" | "info" | "error"
Payload any // will be marshaled to JSON string
}
// Send sends a single event. Returns immediately — use SendAsync for fire-and-forget.
func (c *Client) Send(ctx context.Context, opts SendOpts) error {
payloadJSON, err := json.Marshal(opts.Payload)
if err != nil {
return fmt.Errorf("marshal payload: %w", err)
}
requestID := fmt.Sprintf("%d", time.Now().UnixNano())
body := map[string]any{
"payload": string(payloadJSON),
"meta": map[string]string{
"type": opts.Type,
"event_name": opts.EventName,
"service": c.ServiceName,
"env": c.Env,
"request_id": requestID,
},
}
bodyJSON, err := json.Marshal(body)
if err != nil {
return fmt.Errorf("marshal body: %w", err)
}
req, err := http.NewRequestWithContext(ctx, http.MethodPost,
c.APIURL+"/v1/events", bytes.NewBuffer(bodyJSON))
if err != nil {
return fmt.Errorf("create request: %w", err)
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Authorization", "Bearer "+c.APIKey)
req.Header.Set("Idempotency-Key", requestID)
resp, err := c.httpClient.Do(req)
if err != nil {
return fmt.Errorf("send event: %w", err)
}
defer resp.Body.Close()
return nil
}
// SendAsync sends the event in a goroutine. Never blocks the caller.
func (c *Client) SendAsync(opts SendOpts) {
go func() {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
_ = c.Send(ctx, opts)
}()
}
// Usage:
// client := immutablelog.NewClient(os.Getenv("IMTBL_API_KEY"), "my-service", "production")
//
// client.SendAsync(immutablelog.SendOpts{
// EventName: "user.created",
// Type: "success",
// Payload: map[string]any{"user_id": "usr_123", "plan": "pro"},
// })SendAsync() dispara uma goroutine e retorna imediatamente. Use Send() quando precisar do erro de retorno. Ambos respeitam o context.Context para cancelamento e timeout.
Retry com backoff exponencial
Para produção de alta disponibilidade, adicione retry com backoff exponencial. O ImmutableLog retorna 202 em sucesso — nunca faça retry em 429 (limite mensal atingido).
// SendWithRetry retries on transient errors (not on 429).
func (c *Client) SendWithRetry(ctx context.Context, opts SendOpts, maxAttempts int) error {
delay := 500 * time.Millisecond
for attempt := 0; attempt < maxAttempts; attempt++ {
err := c.Send(ctx, opts)
if err == nil {
return nil
}
// Do not retry on context cancellation
if ctx.Err() != nil {
return ctx.Err()
}
if attempt+1 < maxAttempts {
time.Sleep(delay)
delay *= 2 // exponential backoff: 500ms → 1s → 2s
}
}
return fmt.Errorf("failed after %d attempts", maxAttempts)
}Envio em lote (batch)
Em workers de alta frequência, envie múltiplos eventos em paralelo com sync.WaitGroup. Cada evento é enviado individualmente — o WaitGroup aguarda todos completarem.
// SendBatch sends multiple events concurrently using goroutines.
// Uses a WaitGroup to wait for all sends to complete.
func (c *Client) SendBatch(ctx context.Context, events []SendOpts) []error {
errs := make([]error, len(events))
var wg sync.WaitGroup
for i, evt := range events {
wg.Add(1)
go func(idx int, e SendOpts) {
defer wg.Done()
errs[idx] = c.Send(ctx, e)
}(i, evt)
}
wg.Wait()
return errs
}
// Usage:
ctx := context.Background()
errs := client.SendBatch(ctx, []immutablelog.SendOpts{
{EventName: "order.created", Type: "success", Payload: map[string]any{"order_id": "ord_1"}},
{EventName: "payment.captured", Type: "success", Payload: map[string]any{"amount": 9900}},
{EventName: "email.sent", Type: "info", Payload: map[string]any{"template": "welcome"}},
})Hash de dados sensíveis
Nunca envie dados pessoais brutos (e-mail, CPF, IP) para o ImmutableLog. Use SHA-256 para gerar um digest determinístico — rastreável sem expor o dado original.
import (
"crypto/sha256"
"fmt"
)
// Hash sensitive data before sending — never log raw PII.
func sha256Hex(data []byte) string {
h := sha256.Sum256(data)
return fmt.Sprintf("%x", h)
}
// Usage in event payload:
payload := map[string]any{
"user_id": "usr_123",
"email_hash": sha256Hex([]byte("user@example.com")),
"ip_hash": sha256Hex([]byte("192.168.1.1")),
}Esta documentação reflete o comportamento atual da API. Para dúvidas ou integrações avançadas, entre em contato com o time de suporte.
