Pular para o conteúdo principal
ImmutableLog logo
VoltarGo / Golang

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.

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.

go
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).

go
// 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.

go
// 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.

go
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.